@samitouri / QOS-React / commits / 1ae0a845bd

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)

Sebastian Markbåge committed Jun 3, 2025 at 11:30 UTC 1ae0a845bde5b95dfc319cadf366cb7b3fb1ca92
22 files changed +85 -96
packages/react-client/src/__tests__/ReactFlight-test.js
+6 -6
@@ -1957,8 +1957,8 @@ describe('ReactFlight', () => {
1957 });
1958 expect(ReactNoop).toMatchRenderedOutput(
1959 <>
1960 - <div prop="«S1»" />
1961 - <div prop="«S2»" />
1960 + <div prop="_S_1_" />
1961 + <div prop="_S_2_" />
1962 </>,
1963 );
1964 });
@@ -1981,8 +1981,8 @@ describe('ReactFlight', () => {
1981 });
1982 expect(ReactNoop).toMatchRenderedOutput(
1983 <>
1984 - <div prop="«fooS1»" />
1985 - <div prop="«fooS2»" />
1984 + <div prop="_fooS_1_" />
1985 + <div prop="_fooS_2_" />
1986 </>,
1987 );
1988 });
@@ -2021,8 +2021,8 @@ describe('ReactFlight', () => {
2021 assertLog(['ClientDoubler']);
2022 expect(ReactNoop).toMatchRenderedOutput(
2023 <>
2024 - <div prop="«S1»">«S1»</div>
2025 - <div prop="«S1»">«S1»</div>
2024 + <div prop="_S_1_">_S_1_</div>
2025 + <div prop="_S_1_">_S_1_</div>
2026 </>,
2027 );
2028 });
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+1 -1
@@ -1553,7 +1553,7 @@ describe('ReactHooksInspectionIntegration', () => {
1553 expect(tree[0].id).toEqual(0);
1554 expect(tree[0].isStateEditable).toEqual(false);
1555 expect(tree[0].name).toEqual('Id');
1556 - expect(String(tree[0].value).startsWith('\u00ABr')).toBe(true);
1556 + expect(String(tree[0].value).startsWith('_r_')).toBe(true);
1557
1558 expect(normalizeSourceLoc(tree)[1]).toMatchInlineSnapshot(`
1559 {
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+5 -5
@@ -1082,7 +1082,7 @@ export function makeId(
1082 ): string {
1083 const idPrefix = resumableState.idPrefix;
1084
1085 - let id = '\u00AB' + idPrefix + 'R' + treeId;
1085 + let id = '_' + idPrefix + 'R_' + treeId;
1086
1087 // Unless this is the first id at this level, append a number at the end
1088 // that represents the position of this useId hook among all the useId
@@ -1091,7 +1091,7 @@ export function makeId(
1091 id += 'H' + localId.toString(32);
1092 }
1093
1094 - return id + '\u00BB';
1094 + return id + '_';
1095 }
1096
1097 function encodeHTMLTextNode(text: string): string {
@@ -5415,7 +5415,7 @@ function writeBlockingRenderInstruction(
5415 ): void {
5416 if (enableFizzBlockingRender) {
5417 const idPrefix = resumableState.idPrefix;
5418 - const shellId = '\u00AB' + idPrefix + 'R\u00BB';
5418 + const shellId = '_' + idPrefix + 'R_';
5419 writeChunk(destination, blockingRenderChunkStart);
5420 writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
5421 writeChunk(destination, blockingRenderChunkEnd);
@@ -5433,7 +5433,7 @@ function writeCompletedShellIdAttribute(
5433 }
5434 resumableState.instructions |= SentCompletedShellId;
5435 const idPrefix = resumableState.idPrefix;
5436 - const shellId = '\u00AB' + idPrefix + 'R\u00BB';
5436 + const shellId = '_' + idPrefix + 'R_';
5437 writeChunk(destination, completedShellIdAttributeStart);
5438 writeChunk(destination, stringToChunk(escapeTextForBrowser(shellId)));
5439 writeChunk(destination, attributeEnd);
@@ -5448,7 +5448,7 @@ function pushCompletedShellIdAttribute(
5448 }
5449 resumableState.instructions |= SentCompletedShellId;
5450 const idPrefix = resumableState.idPrefix;
5451 - const shellId = '\u00AB' + idPrefix + 'R\u00BB';
5451 + const shellId = '_' + idPrefix + 'R_';
5452 target.push(
5453 completedShellIdAttributeStart,
5454 stringToChunk(escapeTextForBrowser(shellId)),
packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js
+1 -1
@@ -8,7 +8,7 @@ export const clientRenderBoundary =
8 export const completeBoundary =
9 '$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())};';
10 export const completeBoundaryUpgradeToViewTransitions =
11 - '$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);';
11 + '$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);';
12 export const completeBoundaryWithStyles =
13 '$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"))};';
14 export const completeSegment =
packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetShared.js
+1 -1
@@ -111,7 +111,7 @@ export function revealCompletedBoundariesWithViewTransitions(
111 // TODO: We don't have a prefix to pick from here but maybe we don't need it
112 // since it's only applicable temporarily during this specific animation.
113 const idPrefix = '';
114 - name = '\u00AB' + idPrefix + 'T' + autoNameIdx++ + '\u00BB';
114 + name = '_' + idPrefix + 'T_' + autoNameIdx++ + '_';
115 }
116 elementStyle['viewTransitionName'] = name;
117 shouldStartViewTransition = true;
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+7 -7
@@ -3587,7 +3587,7 @@ describe('ReactDOMFizzServer', () => {
3587 ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
3588 : '') +
3589 (gate(flags => flags.enableFizzBlockingRender)
3590 - ? '<link rel="expect" href="#«R»" blocking="render">'
3590 + ? '<link rel="expect" href="#_R_" blocking="render">'
3591 : ''),
3592 );
3593 });
@@ -4197,7 +4197,7 @@ describe('ReactDOMFizzServer', () => {
4197 renderOptions.unstable_externalRuntimeSrc,
4198 ).map(n => n.outerHTML),
4199 ).toEqual([
4200 - '<script src="foo" id="«R»" async=""></script>',
4200 + '<script src="foo" id="_R_" async=""></script>',
4201 '<script src="bar" async=""></script>',
4202 '<script src="baz" integrity="qux" async=""></script>',
4203 '<script type="module" src="quux" async=""></script>',
@@ -4284,7 +4284,7 @@ describe('ReactDOMFizzServer', () => {
4284 renderOptions.unstable_externalRuntimeSrc,
4285 ).map(n => n.outerHTML),
4286 ).toEqual([
4287 - '<script src="foo" id="«R»" async=""></script>',
4287 + '<script src="foo" id="_R_" async=""></script>',
4288 '<script src="bar" async=""></script>',
4289 '<script src="baz" crossorigin="" async=""></script>',
4290 '<script src="qux" crossorigin="" async=""></script>',
@@ -4523,11 +4523,11 @@ describe('ReactDOMFizzServer', () => {
4523 expect(document.documentElement.innerHTML).toEqual(
4524 '<head><script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>' +
4525 (gate(flags => flags.enableFizzBlockingRender)
4526 - ? '<link rel="expect" href="#«R»" blocking="render">'
4526 + ? '<link rel="expect" href="#_R_" blocking="render">'
4527 : '') +
4528 '</head><body><p>hello world!</p>' +
4529 (gate(flags => flags.enableFizzBlockingRender)
4530 - ? '<template id="«R»"></template>'
4530 + ? '<template id="_R_"></template>'
4531 : '') +
4532 '</body>',
4533 );
@@ -6519,11 +6519,11 @@ describe('ReactDOMFizzServer', () => {
6519 ? '<script src="react-dom-bindings/src/server/ReactDOMServerExternalRuntime.js" async=""></script>'
6520 : '') +
6521 (gate(flags => flags.enableFizzBlockingRender)
6522 - ? '<link rel="expect" href="#«R»" blocking="render">'
6522 + ? '<link rel="expect" href="#_R_" blocking="render">'
6523 : '') +
6524 '</head><body><script>try { foo() } catch (e) {} ;</script>' +
6525 (gate(flags => flags.enableFizzBlockingRender)
6526 - ? '<template id="«R»"></template>'
6526 + ? '<template id="_R_"></template>'
6527 : '') +
6528 '</body></html>',
6529 );
packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js
+5 -5
@@ -73,7 +73,7 @@ describe('ReactDOMFizzServerBrowser', () => {
73 const result = await readResult(stream);
74 if (gate(flags => flags.enableFizzBlockingRender)) {
75 expect(result).toMatchInlineSnapshot(
76 - `"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
76 + `"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
77 );
78 } else {
79 expect(result).toMatchInlineSnapshot(
@@ -92,7 +92,7 @@ describe('ReactDOMFizzServerBrowser', () => {
92 );
93 const result = await readResult(stream);
94 expect(result).toMatchInlineSnapshot(
95 - `"<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>"`,
95 + `"<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>"`,
96 );
97 });
98
@@ -524,11 +524,11 @@ describe('ReactDOMFizzServerBrowser', () => {
524 expect(result).toEqual(
525 '<!DOCTYPE html><html><head>' +
526 (gate(flags => flags.enableFizzBlockingRender)
527 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
527 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
528 : '') +
529 '<title>foo</title></head><body>bar' +
530 (gate(flags => flags.enableFizzBlockingRender)
531 - ? '<template id="«R»"></template>'
531 + ? '<template id="_R_"></template>'
532 : '') +
533 '</body></html>',
534 );
@@ -548,7 +548,7 @@ describe('ReactDOMFizzServerBrowser', () => {
548 expect(result).toMatchInlineSnapshot(
549 // TODO: remove interpolation because it prevents snapshot updates.
550 // eslint-disable-next-line jest/no-interpolation-in-snapshots
551 - `"<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>"`,
551 + `"<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>"`,
552 );
553 });
554
packages/react-dom/src/__tests__/ReactDOMFizzServerEdge-test.js
+1 -1
@@ -73,7 +73,7 @@ describe('ReactDOMFizzServerEdge', () => {
73
74 if (gate(flags => flags.enableFizzBlockingRender)) {
75 expect(result).toMatchInlineSnapshot(
76 - `"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body><main>hello</main><template id="«R»"></template></body></html>"`,
76 + `"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body><main>hello</main><template id="_R_"></template></body></html>"`,
77 );
78 } else {
79 expect(result).toMatchInlineSnapshot(
packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js
+2 -2
@@ -95,7 +95,7 @@ describe('ReactDOMFizzServerNode', () => {
95 // with Float, we emit empty heads if they are elided when rendering <html>
96 if (gate(flags => flags.enableFizzBlockingRender)) {
97 expect(output.result).toMatchInlineSnapshot(
98 - `"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
98 + `"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
99 );
100 } else {
101 expect(output.result).toMatchInlineSnapshot(
@@ -118,7 +118,7 @@ describe('ReactDOMFizzServerNode', () => {
118 pipe(writable);
119 });
120 expect(output.result).toMatchInlineSnapshot(
121 - `"<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>"`,
121 + `"<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>"`,
122 );
123 });
124
packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js
+12 -12
@@ -186,7 +186,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
186 const prelude = await readContent(result.prelude);
187 if (gate(flags => flags.enableFizzBlockingRender)) {
188 expect(prelude).toMatchInlineSnapshot(
189 - `"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
189 + `"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
190 );
191 } else {
192 expect(prelude).toMatchInlineSnapshot(
@@ -205,7 +205,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
205 );
206 const prelude = await readContent(result.prelude);
207 expect(prelude).toMatchInlineSnapshot(
208 - `"<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>"`,
208 + `"<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>"`,
209 );
210 });
211
@@ -1434,12 +1434,12 @@ describe('ReactDOMFizzStaticBrowser', () => {
1434 '<!DOCTYPE html><html lang="en"><head>' +
1435 '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1436 (gate(flags => flags.enableFizzBlockingRender)
1437 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
1437 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
1438 : '') +
1439 '</head>' +
1440 '<body>Hello' +
1441 (gate(flags => flags.enableFizzBlockingRender)
1442 - ? '<template id="«R»"></template>'
1442 + ? '<template id="_R_"></template>'
1443 : '') +
1444 '</body></html>',
1445 );
@@ -1487,8 +1487,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
1487 expect(await readContent(content)).toBe(
1488 '<!DOCTYPE html><html lang="en"><head>' +
1489 '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1490 - '<link rel="expect" href="#«R»" blocking="render"/></head>' +
1491 - '<body>Hello<template id="«R»"></template></body></html>',
1490 + '<link rel="expect" href="#_R_" blocking="render"/></head>' +
1491 + '<body>Hello<template id="_R_"></template></body></html>',
1492 );
1493 });
1494
@@ -1539,8 +1539,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
1539 expect(await readContent(content)).toBe(
1540 '<!DOCTYPE html><html><head>' +
1541 '<link rel="stylesheet" href="my-style" data-precedence="high"/>' +
1542 - '<link rel="expect" href="#«R»" blocking="render"/></head>' +
1543 - '<body><div>Hello</div><template id="«R»"></template></body></html>',
1542 + '<link rel="expect" href="#_R_" blocking="render"/></head>' +
1543 + '<body><div>Hello</div><template id="_R_"></template></body></html>',
1544 );
1545 });
1546
@@ -1622,8 +1622,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
1622 let result = decoder.decode(value, {stream: true});
1623
1624 expect(result).toBe(
1625 - '<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head>' +
1626 - '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="«R»">requestAnimationFrame(function(){$RT=performance.now()});</script>',
1625 + '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1626 + '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>',
1627 );
1628
1629 await 1;
@@ -1647,8 +1647,8 @@ describe('ReactDOMFizzStaticBrowser', () => {
1647 const slice = result.slice(0, instructionIndex + '$RX'.length);
1648
1649 expect(slice).toBe(
1650 - '<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head>' +
1651 - '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="«R»">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
1650 + '<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head>' +
1651 + '<body>hello<!--$?--><template id="B:1"></template><!--/$--><script id="_R_">requestAnimationFrame(function(){$RT=performance.now()});</script>' +
1652 '<div hidden id="S:1">world<!-- --></div><script>$RX',
1653 );
1654 });
packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js
+2 -2
@@ -65,7 +65,7 @@ describe('ReactDOMFizzStaticNode', () => {
65 const prelude = await readContent(result.prelude);
66 if (gate(flags => flags.enableFizzBlockingRender)) {
67 expect(prelude).toMatchInlineSnapshot(
68 - `"<!DOCTYPE html><html><head><link rel="expect" href="#«R»" blocking="render"/></head><body>hello world<template id="«R»"></template></body></html>"`,
68 + `"<!DOCTYPE html><html><head><link rel="expect" href="#_R_" blocking="render"/></head><body>hello world<template id="_R_"></template></body></html>"`,
69 );
70 } else {
71 expect(prelude).toMatchInlineSnapshot(
@@ -86,7 +86,7 @@ describe('ReactDOMFizzStaticNode', () => {
86 );
87 const prelude = await readContent(result.prelude);
88 expect(prelude).toMatchInlineSnapshot(
89 - `"<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>"`,
89 + `"<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>"`,
90 );
91 });
92
packages/react-dom/src/__tests__/ReactDOMFizzViewTransition-test.js
+2 -2
@@ -303,7 +303,7 @@ describe('ReactDOMFizzViewTransition', () => {
303
304 expect(getVisibleChildren(container)).toEqual(
305 <div>
306 - <div vt-name="«R0»" vt-update="auto" vt-share="auto">
306 + <div vt-name="_R_0_" vt-update="auto" vt-share="auto">
307 <span vt-update="auto">Loading</span>
308 </div>
309 </div>,
@@ -321,7 +321,7 @@ describe('ReactDOMFizzViewTransition', () => {
321
322 expect(getVisibleChildren(container)).toEqual(
323 <div>
324 - <div vt-name="«R0»" vt-update="auto" vt-share="auto">
324 + <div vt-name="_R_0_" vt-update="auto" vt-share="auto">
325 <span vt-update="auto">Content</span>
326 </div>
327 </div>,
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+2 -2
@@ -707,12 +707,12 @@ describe('ReactDOMFloat', () => {
707 ? '<script src="react-dom/unstable_server-external-runtime" async=""></script>'
708 : '') +
709 (gate(flags => flags.enableFizzBlockingRender)
710 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
710 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
711 : '') +
712 '<title>foo</title></head>' +
713 '<body>bar' +
714 (gate(flags => flags.enableFizzBlockingRender)
715 - ? '<template id="«R»"></template>'
715 + ? '<template id="_R_"></template>'
716 : ''),
717 '</body></html>',
718 ]);
packages/react-dom/src/__tests__/ReactDOMLegacyFloat-test.js
+2 -2
@@ -36,11 +36,11 @@ describe('ReactDOMFloat', () => {
36 expect(result).toEqual(
37 '<html><head><meta charSet="utf-8"/>' +
38 (gate(flags => flags.enableFizzBlockingRender)
39 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
39 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
40 : '') +
41 '<title>title</title><script src="foo"></script></head>' +
42 (gate(flags => flags.enableFizzBlockingRender)
43 - ? '<template id="«R»"></template>'
43 + ? '<template id="_R_"></template>'
44 : '') +
45 '</html>',
46 );
packages/react-dom/src/__tests__/ReactDOMUseId-test.js
+15 -15
@@ -96,7 +96,7 @@ describe('useId', () => {
96 }
97
98 function normalizeTreeIdForTesting(id) {
99 - const result = id.match(/\u00AB(R|r)([a-z0-9]*)(H([0-9]*))?\u00BB/);
99 + const result = id.match(/_(R|r)_([a-z0-9]*)(H([0-9]*))?_/);
100 if (result === undefined) {
101 throw new Error('Invalid id format');
102 }
@@ -285,7 +285,7 @@ describe('useId', () => {
285 // 'R:' prefix, and the first character after that, which may not correspond
286 // to a complete set of 5 bits.
287 //
288 - // Example: «Rclalalalalalalala...:
288 + // Example: _Rclalalalalalalala...:
289 //
290 // We can use this pattern to test large ids that exceed the bitwise
291 // safe range (32 bits). The algorithm should theoretically support ids
@@ -320,8 +320,8 @@ describe('useId', () => {
320
321 // Confirm that every id matches the expected pattern
322 for (let i = 0; i < divs.length; i++) {
323 - // Example: «Rclalalalalalalala...:
324 - expect(divs[i].id).toMatch(/^\u00ABR.(((al)*a?)((la)*l?))*\u00BB$/);
323 + // Example: _Rclalalalalalalala...:
324 + expect(divs[i].id).toMatch(/^_R_.(((al)*a?)((la)*l?))*_$/);
325 }
326 });
327
@@ -345,7 +345,7 @@ describe('useId', () => {
345 <div
346 id="container"
347 >
348 - «R0», «R0H1», «R0H2»
348 + _R_0_, _R_0H1_, _R_0H2_
349 </div>
350 `);
351 });
@@ -370,7 +370,7 @@ describe('useId', () => {
370 <div
371 id="container"
372 >
373 - «R0»
373 + _R_0_
374 </div>
375 `);
376 });
@@ -608,10 +608,10 @@ describe('useId', () => {
608 id="container"
609 >
610 <div>
611 - «custom-prefix-R1»
611 + _custom-prefix-R_1_
612 </div>
613 <div>
614 - «custom-prefix-R2»
614 + _custom-prefix-R_2_
615 </div>
616 </div>
617 `);
@@ -625,13 +625,13 @@ describe('useId', () => {
625 id="container"
626 >
627 <div>
628 - «custom-prefix-R1»
628 + _custom-prefix-R_1_
629 </div>
630 <div>
631 - «custom-prefix-R2»
631 + _custom-prefix-R_2_
632 </div>
633 <div>
634 - «custom-prefix-r0»
634 + _custom-prefix-r_0_
635 </div>
636 </div>
637 `);
@@ -672,11 +672,11 @@ describe('useId', () => {
672 id="container"
673 >
674 <div>
675 - «R0»
675 + _R_0_
676 <!-- -->
677
678 <div>
679 - «R7»
679 + _R_7_
680 </div>
681 </div>
682 </div>
@@ -690,11 +690,11 @@ describe('useId', () => {
690 id="container"
691 >
692 <div>
693 - «R0»
693 + _R_0_
694 <!-- -->
695
696 <div>
697 - «R7»
697 + _R_7_
698 </div>
699 </div>
700 </div>
packages/react-dom/src/__tests__/ReactRenderDocument-test.js
+9 -9
@@ -80,7 +80,7 @@ describe('rendering React components at document', () => {
80 expect(testDocument.body.innerHTML).toBe(
81 'Hello world' +
82 (gate(flags => flags.enableFizzBlockingRender)
83 - ? '<template id="«R»"></template>'
83 + ? '<template id="_R_"></template>'
84 : ''),
85 );
86
@@ -90,7 +90,7 @@ describe('rendering React components at document', () => {
90 expect(testDocument.body.innerHTML).toBe(
91 'Hello moon' +
92 (gate(flags => flags.enableFizzBlockingRender)
93 - ? '<template id="«R»"></template>'
93 + ? '<template id="_R_"></template>'
94 : ''),
95 );
96
@@ -120,7 +120,7 @@ describe('rendering React components at document', () => {
120 expect(testDocument.body.innerHTML).toBe(
121 'Hello world' +
122 (gate(flags => flags.enableFizzBlockingRender)
123 - ? '<template id="«R»"></template>'
123 + ? '<template id="_R_"></template>'
124 : ''),
125 );
126
@@ -135,12 +135,12 @@ describe('rendering React components at document', () => {
135 expect(testDocument.body).toBe(originalBody);
136 expect(originalBody.innerHTML).toBe(
137 gate(flags => flags.enableFizzBlockingRender)
138 - ? '<template id="«R»"></template>'
138 + ? '<template id="_R_"></template>'
139 : '',
140 );
141 expect(originalHead.innerHTML).toBe(
142 gate(flags => flags.enableFizzBlockingRender)
143 - ? '<link rel="expect" href="#«R»" blocking="render">'
143 + ? '<link rel="expect" href="#_R_" blocking="render">'
144 : '',
145 );
146 });
@@ -183,7 +183,7 @@ describe('rendering React components at document', () => {
183 expect(testDocument.body.innerHTML).toBe(
184 'Hello world' +
185 (gate(flags => flags.enableFizzBlockingRender)
186 - ? '<template id="«R»"></template>'
186 + ? '<template id="_R_"></template>'
187 : ''),
188 );
189
@@ -193,7 +193,7 @@ describe('rendering React components at document', () => {
193
194 expect(testDocument.body.innerHTML).toBe(
195 (gate(flags => flags.enableFizzBlockingRender)
196 - ? '<template id="«R»"></template>'
196 + ? '<template id="_R_"></template>'
197 : '') + 'Goodbye world',
198 );
199 });
@@ -227,7 +227,7 @@ describe('rendering React components at document', () => {
227 expect(testDocument.body.innerHTML).toBe(
228 'Hello world' +
229 (gate(flags => flags.enableFizzBlockingRender)
230 - ? '<template id="«R»"></template>'
230 + ? '<template id="_R_"></template>'
231 : ''),
232 );
233 });
@@ -366,7 +366,7 @@ describe('rendering React components at document', () => {
366 ? 'Hello world'
367 : 'Goodbye world' +
368 (gate(flags => flags.enableFizzBlockingRender)
369 - ? '<template id="«R»"></template>'
369 + ? '<template id="_R_"></template>'
370 : ''),
371 );
372 });
packages/react-reconciler/src/ReactFiberHooks.js
+3 -8
@@ -3446,7 +3446,7 @@ function mountId(): string {
3446 const treeId = getTreeId();
3447
3448 // Use a captial R prefix for server-generated ids.
3449 - id = '\u00AB' + identifierPrefix + 'R' + treeId;
3449 + id = '_' + identifierPrefix + 'R_' + treeId;
3450
3451 // Unless this is the first id at this level, append a number at the end
3452 // that represents the position of this useId hook among all the useId
@@ -3456,16 +3456,11 @@ function mountId(): string {
3456 id += 'H' + localId.toString(32);
3457 }
3458
3459 - id += '\u00BB';
3459 + id += '_';
3460 } else {
3461 // Use a lowercase r prefix for client-generated ids.
3462 const globalClientId = globalClientIdCounter++;
3463 - id =
3464 - '\u00AB' +
3465 - identifierPrefix +
3466 - 'r' +
3467 - globalClientId.toString(32) +
3468 - '\u00BB';
3463 + id = '_' + identifierPrefix + 'r_' + globalClientId.toString(32) + '_';
3464 }
3465
3466 hook.memoizedState = id;
packages/react-reconciler/src/ReactFiberViewTransitionComponent.js
+1 -1
@@ -41,7 +41,7 @@ export function getViewTransitionName(
41 const identifierPrefix = root.identifierPrefix;
42 const globalClientId = globalClientIdCounter++;
43 const name =
44 - '\u00AB' + identifierPrefix + 't' + globalClientId.toString(32) + '\u00BB';
44 + '_' + identifierPrefix + 't_' + globalClientId.toString(32) + '_';
45 instance.autoName = name;
46 return name;
47 }
packages/react-server-dom-fb/src/__tests__/ReactDOMServerFB-test.internal.js
+1 -1
@@ -59,7 +59,7 @@ describe('ReactDOMServerFB', () => {
59 });
60 const result = readResult(stream);
61 expect(result).toMatchInlineSnapshot(
62 - `"<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>"`,
62 + `"<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>"`,
63 );
64 });
65
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOM-test.js
+4 -4
@@ -1909,12 +1909,12 @@ describe('ReactFlightDOM', () => {
1909 '<!DOCTYPE html><html><head><link rel="preload" href="before1" as="style"/>' +
1910 '<link rel="preload" href="after1" as="style"/>' +
1911 (gate(flags => flags.enableFizzBlockingRender)
1912 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
1912 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
1913 : '') +
1914 '</head>' +
1915 '<body><p>hello world</p>' +
1916 (gate(flags => flags.enableFizzBlockingRender)
1917 - ? '<template id="«R»"></template>'
1917 + ? '<template id="_R_"></template>'
1918 : '') +
1919 '</body></html>',
1920 );
@@ -1922,12 +1922,12 @@ describe('ReactFlightDOM', () => {
1922 '<!DOCTYPE html><html><head><link rel="preload" href="before2" as="style"/>' +
1923 '<link rel="preload" href="after2" as="style"/>' +
1924 (gate(flags => flags.enableFizzBlockingRender)
1925 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
1925 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
1926 : '') +
1927 '</head>' +
1928 '<body><p>hello world</p>' +
1929 (gate(flags => flags.enableFizzBlockingRender)
1930 - ? '<template id="«R»"></template>'
1930 + ? '<template id="_R_"></template>'
1931 : '') +
1932 '</body></html>',
1933 );
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js
+2 -2
@@ -1888,12 +1888,12 @@ describe('ReactFlightDOMBrowser', () => {
1888 expect(content).toEqual(
1889 '<!DOCTYPE html><html><head>' +
1890 (gate(flags => flags.enableFizzBlockingRender)
1891 - ? '<link rel="expect" href="#«R»" blocking="render"/>'
1891 + ? '<link rel="expect" href="#_R_" blocking="render"/>'
1892 : '') +
1893 '</head>' +
1894 '<body><p>hello world</p>' +
1895 (gate(flags => flags.enableFizzBlockingRender)
1896 - ? '<template id="«R»"></template>'
1896 + ? '<template id="_R_"></template>'
1897 : '') +
1898 '</body></html>',
1899 );
packages/react-server/src/ReactFlightHooks.js
+1 -7
@@ -120,13 +120,7 @@ function useId(): string {
120 }
121 const id = currentRequest.identifierCount++;
122 // use 'S' for Flight components to distinguish from 'R' and 'r' in Fizz/Client
123 - return (
124 - '\u00AB' +
125 - currentRequest.identifierPrefix +
126 - 'S' +
127 - id.toString(32) +
128 - '\u00BB'
129 - );
123 + return '_' + currentRequest.identifierPrefix + 'S_' + id.toString(32) + '_';
124 }
125
126 function use<T>(usable: Usable<T>): T {