Use underscore instead of « » for useId algorithm (#33422)

Alternative to #33421. The difference is that this also adds an
underscore between the "R" and the ID.

The reason we wanted to use special characters is because we use the
full spectrum of A-Z 0-9 in our ID generation so we can basically
collide with any common word (or anyone using a similar algorithm,
base64 or even base16). It's a little less likely that someone would put
`_R_` specifically unless you generate like two IDs separated by
underscore.


![9w2ogt](https://github.com/user-attachments/assets/21b2d2ac-1a3a-4657-ba0b-1616e49dfdee)
This commit is contained in:
Sebastian Markbåge
2025-06-03 11:30:17 -04:00
committed by GitHub
parent 2b4064eb9b
commit 1ae0a845bd
22 changed files with 85 additions and 96 deletions

View File

@@ -1957,8 +1957,8 @@ describe('ReactFlight', () => {
});
expect(ReactNoop).toMatchRenderedOutput(
<>
<div prop="«S1»" />
<div prop="«S2»" />
<div prop="_S_1_" />
<div prop="_S_2_" />
</>,
);
});
@@ -1981,8 +1981,8 @@ describe('ReactFlight', () => {
});
expect(ReactNoop).toMatchRenderedOutput(
<>
<div prop="«fooS" />
<div prop="«fooS" />
<div prop="_fooS_1_" />
<div prop="_fooS_2_" />
</>,
);
});
@@ -2021,8 +2021,8 @@ describe('ReactFlight', () => {
assertLog(['ClientDoubler']);
expect(ReactNoop).toMatchRenderedOutput(
<>
<div prop="«S1»">«S1»</div>
<div prop="«S1»">«S1»</div>
<div prop="_S_1_">_S_1_</div>
<div prop="_S_1_">_S_1_</div>
</>,
);
});

View File

@@ -1553,7 +1553,7 @@ describe('ReactHooksInspectionIntegration', () => {
expect(tree[0].id).toEqual(0);
expect(tree[0].isStateEditable).toEqual(false);
expect(tree[0].name).toEqual('Id');
expect(String(tree[0].value).startsWith('\u00ABr')).toBe(true);
expect(String(tree[0].value).startsWith('_r_')).toBe(true);
expect(normalizeSourceLoc(tree)[1]).toMatchInlineSnapshot(`
{

View File

@@ -1082,7 +1082,7 @@ export function makeId(
): string {
const idPrefix = resumableState.idPrefix;
let id = '\u00AB' + idPrefix + 'R' + treeId;
let id = '_' + idPrefix + 'R_' + treeId;
// Unless this is the first id at this level, append a number at the end
// that represents the position of this useId hook among all the useId
@@ -1091,7 +1091,7 @@ export function makeId(
id += 'H' + localId.toString(32);
}
return id + '\u00BB';
return id + '_';
}
function encodeHTMLTextNode(text: string): string {
@@ -5415,7 +5415,7 @@ function writeBlockingRenderInstruction(
): void {
if (enableFizzBlockingRender) {
const idPrefix = resumableState.idPrefix;
const shellId = '\u00AB' + idPrefix + 'R\u00BB';
const shellId = '_' + idPrefix + 'R_';
writeChunk(destination, blockingRenderChunkStart);
writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
writeChunk(destination, blockingRenderChunkEnd);
@@ -5433,7 +5433,7 @@ function writeCompletedShellIdAttribute(
}
resumableState.instructions |= SentCompletedShellId;
const idPrefix = resumableState.idPrefix;
const shellId = '\u00AB' + idPrefix + 'R\u00BB';
const shellId = '_' + idPrefix + 'R_';
writeChunk(destination, completedShellIdAttributeStart);
writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
writeChunk(destination, attributeEnd);
@@ -5448,7 +5448,7 @@ function pushCompletedShellIdAttribute(
}
resumableState.instructions |= SentCompletedShellId;
const idPrefix = resumableState.idPrefix;
const shellId = '\u00AB' + idPrefix + 'R\u00BB';
const shellId = '_' + idPrefix + 'R_';
target.push(
completedShellIdAttributeStart,
stringToChunk(escapeTextForBrowser(shellId)),

View File

@@ -8,7 +8,7 @@ export const clientRenderBoundary =
export const completeBoundary =
'$RB=[];$RV=function(c){$RT=performance.now();for(var a=0;a<c.length;a+=2){var b=c[a],h=c[a+1],e=b.parentNode;if(e){var f=b.previousSibling,g=0;do{if(b&&8===b.nodeType){var d=b.data;if("/$"===d||"/&"===d)if(0===g)break;else g--;else"$"!==d&&"$?"!==d&&"$~"!==d&&"$!"!==d&&"&"!==d||g++}d=b.nextSibling;e.removeChild(b);b=d}while(b);for(;h.firstChild;)e.insertBefore(h.firstChild,b);f.data="$";f._reactRetry&&f._reactRetry()}}c.length=0};$RC=function(c,a){if(a=document.getElementById(a))if(a.parentNode.removeChild(a),c=document.getElementById(c))c.previousSibling.data="$~",$RB.push(c,a),2===$RB.length&&setTimeout($RV.bind(null,$RB),("number"!==typeof $RT?0:$RT)+300-performance.now())};';
export const completeBoundaryUpgradeToViewTransitions =
'$RV=function(w,f){function h(a,d){var k=a.getAttribute(d);k&&(d=a.style,l.push(a,d.viewTransitionName,d.viewTransitionClass),"auto"!==k&&(d.viewTransitionClass=k),(a=a.getAttribute("vt-name"))||(a="\\u00abT"+F++ +"\\u00bb"),d.viewTransitionName=a,x=!0)}var x=!1,F=0,l=[];try{var e=document.__reactViewTransition;if(e){e.finished.finally($RV.bind(null,f));return}var m=new Map;for(e=1;e<f.length;e+=2)for(var g=f[e].querySelectorAll("[vt-share]"),c=0;c<g.length;c++){var b=g[c];m.set(b.getAttribute("vt-name"),b)}for(g=0;g<f.length;g+=2){var y=f[g],t=y.parentNode;if(t){var r=t.getBoundingClientRect();if(r.left||r.top||r.width||r.height){b=y;for(e=0;b;){if(8===b.nodeType){var p=b.data;if("/$"===p)if(0===e)break;else e--;else"$"!==p&&"$?"!==p&&"$~"!==p&&"$!"!==p||e++}else if(1===b.nodeType){c=b;var z=c.getAttribute("vt-name"),u=m.get(z);h(c,u?"vt-share":"vt-exit");u&&(h(u,"vt-share"),m.set(z,null));var A=c.querySelectorAll("[vt-share]");for(c=0;c<A.length;c++){var B=A[c],C=B.getAttribute("vt-name"),D=m.get(C);\nD&&(h(B,"vt-share"),h(D,"vt-share"),m.set(C,null))}}b=b.nextSibling}for(var q=f[g+1].firstElementChild;q;)null!==m.get(q.getAttribute("vt-name"))&&h(q,"vt-enter"),q=q.nextElementSibling;b=t;do for(var n=b.firstElementChild;n;){var E=n.getAttribute("vt-update");E&&"none"!==E&&!l.includes(n)&&h(n,"vt-update");n=n.nextElementSibling}while((b=b.parentNode)&&1===b.nodeType&&"none"!==b.getAttribute("vt-update"))}}}if(x){var v=document.__reactViewTransition=document.startViewTransition({update:function(){w(f,\ndocument.documentElement.clientHeight);return Promise.race([document.fonts.ready,new Promise(function(a){return setTimeout(a,500)})])},types:[]});v.ready.finally(function(){for(var a=l.length-3;0<=a;a-=3){var d=l[a],k=d.style;k.viewTransitionName=l[a+1];k.viewTransitionClass=l[a+1];""===d.getAttribute("style")&&d.removeAttribute("style")}});v.finished.finally(function(){document.__reactViewTransition===v&&(document.__reactViewTransition=null)});$RB=[];return}}catch(a){}w(f)}.bind(null,$RV);';
'$RV=function(w,f){function h(a,d){var k=a.getAttribute(d);k&&(d=a.style,l.push(a,d.viewTransitionName,d.viewTransitionClass),"auto"!==k&&(d.viewTransitionClass=k),(a=a.getAttribute("vt-name"))||(a="_T_"+F++ +"_"),d.viewTransitionName=a,x=!0)}var x=!1,F=0,l=[];try{var e=document.__reactViewTransition;if(e){e.finished.finally($RV.bind(null,f));return}var m=new Map;for(e=1;e<f.length;e+=2)for(var g=f[e].querySelectorAll("[vt-share]"),c=0;c<g.length;c++){var b=g[c];m.set(b.getAttribute("vt-name"),b)}for(g=0;g<f.length;g+=2){var y=f[g],t=y.parentNode;if(t){var r=t.getBoundingClientRect();if(r.left||r.top||r.width||r.height){b=y;for(e=0;b;){if(8===b.nodeType){var p=b.data;if("/$"===p)if(0===e)break;else e--;else"$"!==p&&"$?"!==p&&"$~"!==p&&"$!"!==p||e++}else if(1===b.nodeType){c=b;var z=c.getAttribute("vt-name"),u=m.get(z);h(c,u?"vt-share":"vt-exit");u&&(h(u,"vt-share"),m.set(z,null));var A=c.querySelectorAll("[vt-share]");for(c=0;c<A.length;c++){var B=A[c],C=B.getAttribute("vt-name"),D=m.get(C);\nD&&(h(B,"vt-share"),h(D,"vt-share"),m.set(C,null))}}b=b.nextSibling}for(var q=f[g+1].firstElementChild;q;)null!==m.get(q.getAttribute("vt-name"))&&h(q,"vt-enter"),q=q.nextElementSibling;b=t;do for(var n=b.firstElementChild;n;){var E=n.getAttribute("vt-update");E&&"none"!==E&&!l.includes(n)&&h(n,"vt-update");n=n.nextElementSibling}while((b=b.parentNode)&&1===b.nodeType&&"none"!==b.getAttribute("vt-update"))}}}if(x){var v=document.__reactViewTransition=document.startViewTransition({update:function(){w(f,\ndocument.documentElement.clientHeight);return Promise.race([document.fonts.ready,new Promise(function(a){return setTimeout(a,500)})])},types:[]});v.ready.finally(function(){for(var a=l.length-3;0<=a;a-=3){var d=l[a],k=d.style;k.viewTransitionName=l[a+1];k.viewTransitionClass=l[a+1];""===d.getAttribute("style")&&d.removeAttribute("style")}});v.finished.finally(function(){document.__reactViewTransition===v&&(document.__reactViewTransition=null)});$RB=[];return}}catch(a){}w(f)}.bind(null,$RV);';
export const completeBoundaryWithStyles =
'$RM=new Map;$RR=function(n,w,p){function u(q){this._p=null;q()}for(var r=new Map,t=document,h,b,e=t.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=e[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&$RM.set(b.getAttribute("href"),b),r.set(b.dataset.precedence,h=b));e=0;b=[];var l,a;for(k=!0;;){if(k){var f=p[e++];if(!f){k=!1;e=0;continue}var c=!1,m=0;var d=f[m++];if(a=$RM.get(d)){var g=a._p;c=!0}else{a=t.createElement("link");a.href=d;a.rel=\n"stylesheet";for(a.dataset.precedence=l=f[m++];g=f[m++];)a.setAttribute(g,f[m++]);g=a._p=new Promise(function(q,x){a.onload=u.bind(a,q);a.onerror=u.bind(a,x)});$RM.set(d,a)}d=a.getAttribute("media");!g||d&&!matchMedia(d).matches||b.push(g);if(c)continue}else{a=v[e++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=r.get(l)||h;c===h&&(h=a);r.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=t.head,c.insertBefore(a,c.firstChild))}if(p=document.getElementById(n))p.previousSibling.data=\n"$~";Promise.all(b).then($RC.bind(null,n,w),$RX.bind(null,n,"CSS failed to load"))};';
export const completeSegment =

View File

@@ -111,7 +111,7 @@ export function revealCompletedBoundariesWithViewTransitions(
// TODO: We don't have a prefix to pick from here but maybe we don't need it
// since it's only applicable temporarily during this specific animation.
const idPrefix = '';
name = '\u00AB' + idPrefix + 'T' + autoNameIdx++ + '\u00BB';
name = '_' + idPrefix + 'T_' + autoNameIdx++ + '_';
}
elementStyle['viewTransitionName'] = name;
shouldStartViewTransition = true;

View File

@@ -3587,7 +3587,7 @@ describe('ReactDOMFizzServer', () => {
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render">'
? '<link rel="expect" href="#_R_" blocking="render">'
: ''),
);
});
@@ -4197,7 +4197,7 @@ describe('ReactDOMFizzServer', () => {
renderOptions.unstable_externalRuntimeSrc,
).map(n => n.outerHTML),
).toEqual([
'<script src="foo" id="«R»" async=""></script>',
'<script src="foo" id="_R_" async=""></script>',
'<script src="bar" async=""></script>',
'<script src="baz" integrity="qux" async=""></script>',
'<script type="module" src="quux" async=""></script>',
@@ -4284,7 +4284,7 @@ describe('ReactDOMFizzServer', () => {
renderOptions.unstable_externalRuntimeSrc,
).map(n => n.outerHTML),
).toEqual([
'<script src="foo" id="«R»" async=""></script>',
'<script src="foo" id="_R_" async=""></script>',
'<script src="bar" async=""></script>',
'<script src="baz" crossorigin="" async=""></script>',
'<script src="qux" crossorigin="" async=""></script>',
@@ -4523,11 +4523,11 @@ describe('ReactDOMFizzServer', () => {
expect(document.documentElement.innerHTML).toEqual(
'<head><script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render">'
? '<link rel="expect" href="#_R_" blocking="render">'
: '') +
'</head><body><p>hello world!</p>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body>',
);
@@ -6519,11 +6519,11 @@ describe('ReactDOMFizzServer', () => {
? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
: '') +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render">'
? '<link rel="expect" href="#_R_" blocking="render">'
: '') +
'</head><body><script>try { foo() } catch (e) {} ;</script>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);

View File

@@ -73,7 +73,7 @@ describe('ReactDOMFizzServerBrowser', () => {
const result = await readResult(stream);
if (gate(flags => flags.enableFizzBlockingRender)) {
expect(result).toMatchInlineSnapshot(
`"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
`"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
);
} else {
expect(result).toMatchInlineSnapshot(
@@ -92,7 +92,7 @@ describe('ReactDOMFizzServerBrowser', () => {
);
const result = await readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="«R»">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="_R_">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
);
});
@@ -524,11 +524,11 @@ describe('ReactDOMFizzServerBrowser', () => {
expect(result).toEqual(
'<!DOCTYPE html><html><head>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'<title>foo</title></head><body>bar' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);
@@ -548,7 +548,7 @@ describe('ReactDOMFizzServerBrowser', () => {
expect(result).toMatchInlineSnapshot(
// TODO: remove interpolation because it prevents snapshot updates.
// eslint-disable-next-line jest/no-interpolation-in-snapshots
`"<link rel="preload" as="script" fetchPriority="low" nonce="R4nd0m" href="init.js"/><link rel="modulepreload" fetchPriority="low" nonce="R4nd0m" href="init.mjs"/><div>hello world</div><script nonce="${nonce}" id="«R»">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" nonce="R4nd0m" href="init.js"/><link rel="modulepreload" fetchPriority="low" nonce="R4nd0m" href="init.mjs"/><div>hello world</div><script nonce="${nonce}" id="_R_">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
);
});

View File

@@ -73,7 +73,7 @@ describe('ReactDOMFizzServerEdge', () => {
if (gate(flags => flags.enableFizzBlockingRender)) {
expect(result).toMatchInlineSnapshot(
`"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body><main>hello</main><template id="«R»"></template></body></html>"`,
`"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body><main>hello</main><template id="_R_"></template></body></html>"`,
);
} else {
expect(result).toMatchInlineSnapshot(

View File

@@ -95,7 +95,7 @@ describe('ReactDOMFizzServerNode', () => {
// with Float, we emit empty heads if they are elided when rendering <html>
if (gate(flags => flags.enableFizzBlockingRender)) {
expect(output.result).toMatchInlineSnapshot(
`"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
`"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
);
} else {
expect(output.result).toMatchInlineSnapshot(
@@ -118,7 +118,7 @@ describe('ReactDOMFizzServerNode', () => {
pipe(writable);
});
expect(output.result).toMatchInlineSnapshot(
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="«R»">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="_R_">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
);
});

View File

@@ -186,7 +186,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
const prelude = await readContent(result.prelude);
if (gate(flags => flags.enableFizzBlockingRender)) {
expect(prelude).toMatchInlineSnapshot(
`"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
`"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
);
} else {
expect(prelude).toMatchInlineSnapshot(
@@ -205,7 +205,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
);
const prelude = await readContent(result.prelude);
expect(prelude).toMatchInlineSnapshot(
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="«R»">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="_R_">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
);
});
@@ -1434,12 +1434,12 @@ describe('ReactDOMFizzStaticBrowser', () => {
'<!DOCTYPE html><html lang="en"><head>' +
'<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'</head>' +
'<body>Hello' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);
@@ -1487,8 +1487,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
expect(await readContent(content)).toBe(
'<!DOCTYPE html><html lang="en"><head>' +
'<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
'<link rel="expect" href="#«R»" blocking="render"/></head>' +
'<body>Hello<template id="«R»"></template></body></html>',
'<link rel="expect" href="#_R_" blocking="render"/></head>' +
'<body>Hello<template id="_R_"></template></body></html>',
);
});
@@ -1539,8 +1539,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
expect(await readContent(content)).toBe(
'<!DOCTYPE html><html><head>' +
'<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
'<link rel="expect" href="#«R»" blocking="render"/></head>' +
'<body><div>Hello</div><template id="«R»"></template></body></html>',
'<link rel="expect" href="#_R_" blocking="render"/></head>' +
'<body><div>Hello</div><template id="_R_"></template></body></html>',
);
});
@@ -1622,8 +1622,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
let result = decoder.decode(value, {stream: true});
expect(result).toBe(
'<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head>' +
'<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="«R»">requestAnimationFrame(function(){$RT=performance.now()});</script>',
'<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
'<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>',
);
await 1;
@@ -1647,8 +1647,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
const slice = result.slice(0, instructionIndex + '$RX'.length);
expect(slice).toBe(
'<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head>' +
'<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="«R»">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
'<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
'<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
'<div hidden id="S:1">world<!-- --></div><script>$RX',
);
});

View File

@@ -65,7 +65,7 @@ describe('ReactDOMFizzStaticNode', () => {
const prelude = await readContent(result.prelude);
if (gate(flags => flags.enableFizzBlockingRender)) {
expect(prelude).toMatchInlineSnapshot(
`"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
`"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
);
} else {
expect(prelude).toMatchInlineSnapshot(
@@ -86,7 +86,7 @@ describe('ReactDOMFizzStaticNode', () => {
);
const prelude = await readContent(result.prelude);
expect(prelude).toMatchInlineSnapshot(
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="«R»">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="_R_">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
);
});

View File

@@ -303,7 +303,7 @@ describe('ReactDOMFizzViewTransition', () => {
expect(getVisibleChildren(container)).toEqual(
<div>
<div vt-name="«R0»" vt-update="auto" vt-share="auto">
<div vt-name="_R_0_" vt-update="auto" vt-share="auto">
<span vt-update="auto">Loading</span>
</div>
</div>,
@@ -321,7 +321,7 @@ describe('ReactDOMFizzViewTransition', () => {
expect(getVisibleChildren(container)).toEqual(
<div>
<div vt-name="«R0»" vt-update="auto" vt-share="auto">
<div vt-name="_R_0_" vt-update="auto" vt-share="auto">
<span vt-update="auto">Content</span>
</div>
</div>,

View File

@@ -707,12 +707,12 @@ describe('ReactDOMFloat', () => {
? '<script src="react-dom/unstable_server-external-runtime" async=""></script>'
: '') +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'<title>foo</title></head>' +
'<body>bar' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
'</body></html>',
]);

View File

@@ -36,11 +36,11 @@ describe('ReactDOMFloat', () => {
expect(result).toEqual(
'<html><head><meta charSet="utf-8"/>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'<title>title</title><script src="foo"></script></head>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</html>',
);

View File

@@ -96,7 +96,7 @@ describe('useId', () => {
}
function normalizeTreeIdForTesting(id) {
const result = id.match(/\u00AB(R|r)([a-z0-9]*)(H([0-9]*))?\u00BB/);
const result = id.match(/_(R|r)_([a-z0-9]*)(H([0-9]*))?_/);
if (result === undefined) {
throw new Error('Invalid id format');
}
@@ -285,7 +285,7 @@ describe('useId', () => {
// 'R:' prefix, and the first character after that, which may not correspond
// to a complete set of 5 bits.
//
// Example: «Rclalalalalalalala...:
// Example: _Rclalalalalalalala...:
//
// We can use this pattern to test large ids that exceed the bitwise
// safe range (32 bits). The algorithm should theoretically support ids
@@ -320,8 +320,8 @@ describe('useId', () => {
// Confirm that every id matches the expected pattern
for (let i = 0; i < divs.length; i++) {
// Example: «Rclalalalalalalala...:
expect(divs[i].id).toMatch(/^\u00ABR.(((al)*a?)((la)*l?))*\u00BB$/);
// Example: _Rclalalalalalalala...:
expect(divs[i].id).toMatch(/^_R_.(((al)*a?)((la)*l?))*_$/);
}
});
@@ -345,7 +345,7 @@ describe('useId', () => {
<div
id="container"
>
«R0», «R0H1», «R0H2»
_R_0_, _R_0H1_, _R_0H2_
</div>
`);
});
@@ -370,7 +370,7 @@ describe('useId', () => {
<div
id="container"
>
«R0»
_R_0_
</div>
`);
});
@@ -608,10 +608,10 @@ describe('useId', () => {
id="container"
>
<div>
«custom-prefix-R
_custom-prefix-R_1_
</div>
<div>
«custom-prefix-R
_custom-prefix-R_2_
</div>
</div>
`);
@@ -625,13 +625,13 @@ describe('useId', () => {
id="container"
>
<div>
«custom-prefix-R
_custom-prefix-R_1_
</div>
<div>
«custom-prefix-R
_custom-prefix-R_2_
</div>
<div>
«custom-prefix-r
_custom-prefix-r_0_
</div>
</div>
`);
@@ -672,11 +672,11 @@ describe('useId', () => {
id="container"
>
<div>
«R0»
_R_0_
<!-- -->
<div>
«R7»
_R_7_
</div>
</div>
</div>
@@ -690,11 +690,11 @@ describe('useId', () => {
id="container"
>
<div>
«R0»
_R_0_
<!-- -->
<div>
«R7»
_R_7_
</div>
</div>
</div>

View File

@@ -80,7 +80,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
'Hello world' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
@@ -90,7 +90,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
'Hello moon' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
@@ -120,7 +120,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
'Hello world' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
@@ -135,12 +135,12 @@ describe('rendering React components at document', () => {
expect(testDocument.body).toBe(originalBody);
expect(originalBody.innerHTML).toBe(
gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '',
);
expect(originalHead.innerHTML).toBe(
gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render">'
? '<link rel="expect" href="#_R_" blocking="render">'
: '',
);
});
@@ -183,7 +183,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
'Hello world' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
@@ -193,7 +193,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') + 'Goodbye world',
);
});
@@ -227,7 +227,7 @@ describe('rendering React components at document', () => {
expect(testDocument.body.innerHTML).toBe(
'Hello world' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
});
@@ -366,7 +366,7 @@ describe('rendering React components at document', () => {
? 'Hello world'
: 'Goodbye world' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: ''),
);
});

View File

@@ -3446,7 +3446,7 @@ function mountId(): string {
const treeId = getTreeId();
// Use a captial R prefix for server-generated ids.
id = '\u00AB' + identifierPrefix + 'R' + treeId;
id = '_' + identifierPrefix + 'R_' + treeId;
// Unless this is the first id at this level, append a number at the end
// that represents the position of this useId hook among all the useId
@@ -3456,16 +3456,11 @@ function mountId(): string {
id += 'H' + localId.toString(32);
}
id += '\u00BB';
id += '_';
} else {
// Use a lowercase r prefix for client-generated ids.
const globalClientId = globalClientIdCounter++;
id =
'\u00AB' +
identifierPrefix +
'r' +
globalClientId.toString(32) +
'\u00BB';
id = '_' + identifierPrefix + 'r_' + globalClientId.toString(32) + '_';
}
hook.memoizedState = id;

View File

@@ -41,7 +41,7 @@ export function getViewTransitionName(
const identifierPrefix = root.identifierPrefix;
const globalClientId = globalClientIdCounter++;
const name =
'\u00AB' + identifierPrefix + 't' + globalClientId.toString(32) + '\u00BB';
'_' + identifierPrefix + 't_' + globalClientId.toString(32) + '_';
instance.autoName = name;
return name;
}

View File

@@ -59,7 +59,7 @@ describe('ReactDOMServerFB', () => {
});
const result = readResult(stream);
expect(result).toMatchInlineSnapshot(
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="«R»">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script id="_R_">INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
);
});

View File

@@ -1909,12 +1909,12 @@ describe('ReactFlightDOM', () => {
'<!DOCTYPE html><html><head><link rel="preload" href="before1" as="style"/>' +
'<link rel="preload" href="after1" as="style"/>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'</head>' +
'<body><p>hello world</p>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);
@@ -1922,12 +1922,12 @@ describe('ReactFlightDOM', () => {
'<!DOCTYPE html><html><head><link rel="preload" href="before2" as="style"/>' +
'<link rel="preload" href="after2" as="style"/>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'</head>' +
'<body><p>hello world</p>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);

View File

@@ -1888,12 +1888,12 @@ describe('ReactFlightDOMBrowser', () => {
expect(content).toEqual(
'<!DOCTYPE html><html><head>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<link rel="expect" href="#«R»" blocking="render"/>'
? '<link rel="expect" href="#_R_" blocking="render"/>'
: '') +
'</head>' +
'<body><p>hello world</p>' +
(gate(flags => flags.enableFizzBlockingRender)
? '<template id="«R»"></template>'
? '<template id="_R_"></template>'
: '') +
'</body></html>',
);

View File

@@ -120,13 +120,7 @@ function useId(): string {
}
const id = currentRequest.identifierCount++;
// use 'S' for Flight components to distinguish from 'R' and 'r' in Fizz/Client
return (
'\u00AB' +
currentRequest.identifierPrefix +
'S' +
id.toString(32) +
'\u00BB'
);
return '_' + currentRequest.identifierPrefix + 'S_' + id.toString(32) + '_';
}
function use<T>(usable: Usable<T>): T {