@samitouri / QOS-React-1 / commits / 14094f80cb

Allow `nonce` to be used on hoistable styles (#32461)

fixes https://github.com/facebook/react/issues/32449 This is my first time touching this code. There are multiple systems in place here and I wouldn't be surprised to learn that this has to be handled in some other areas too. I have found some other style-related code areas but I had no time yet to double-check them. cc @gnoff

Mateusz Burzyński committed May 29, 2025 at 17:17 UTC 14094f80cbf52cad4024211080d8491f3334ef61
11 files changed +398 -30
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+86 -20
@@ -135,6 +135,13 @@ const SentMarkShellTime /* */ = 0b001000000;
135 const NeedUpgradeToViewTransitions /* */ = 0b010000000;
136 const SentUpgradeToViewTransitions /* */ = 0b100000000;
137
138 +type NonceOption =
139 + | string
140 + | {
141 + script?: string,
142 + style?: string,
143 + };
144 +
145 // Per request, global state that is not contextual to the rendering subtree.
146 // This cannot be resumed and therefore should only contain things that are
147 // temporary working state or are never used in the prerender pass.
@@ -147,6 +154,8 @@ export type RenderState = {
154 // inline script streaming format, unused if using external runtime / data
155 startInlineScript: PrecomputedChunk,
156
157 + startInlineStyle: PrecomputedChunk,
158 +
159 // the preamble must always flush before resuming, so all these chunks must
160 // be null or empty when resuming.
161
@@ -209,6 +218,11 @@ export type RenderState = {
218 moduleScripts: Map<string, Resource>,
219 },
220
221 + nonce: {
222 + script: string | void,
223 + style: string | void,
224 + },
225 +
226 // Module-global-like reference for flushing/hoisting state of style resources
227 // We need to track whether the current request has flushed any style resources
228 // without sending an instruction to hoist them. we do that here
@@ -295,6 +309,8 @@ export type ResumableState = {
309 },
310 };
311
312 +let currentlyFlushingRenderState: RenderState | null = null;
313 +
314 const dataElementQuotedEnd = stringToPrecomputedChunk('"></template>');
315
316 const startInlineScript = stringToPrecomputedChunk('<script');
@@ -307,6 +323,8 @@ const scriptIntegirty = stringToPrecomputedChunk(' integrity="');
323 const scriptCrossOrigin = stringToPrecomputedChunk(' crossorigin="');
324 const endAsyncScript = stringToPrecomputedChunk(' async=""></script>');
325
326 +const startInlineStyle = stringToPrecomputedChunk('<style');
327 +
328 /**
329 * This escaping function is designed to work with with inline scripts where the entire
330 * contents are escaped. Because we know we are escaping the entire script we can avoid for instance
@@ -365,17 +383,32 @@ if (__DEV__) {
383 // is set, the server will send instructions via data attributes (instead of inline scripts)
384 export function createRenderState(
385 resumableState: ResumableState,
368 - nonce: string | void,
386 + nonce:
387 + | string
388 + | {
389 + script?: string,
390 + style?: string,
391 + }
392 + | void,
393 externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
394 importMap: ImportMap | void,
395 onHeaders: void | ((headers: HeadersDescriptor) => void),
396 maxHeadersLength: void | number,
397 ): RenderState {
398 + const nonceScript = typeof nonce === 'string' ? nonce : nonce && nonce.script;
399 const inlineScriptWithNonce =
375 - nonce === undefined
400 + nonceScript === undefined
401 ? startInlineScript
402 : stringToPrecomputedChunk(
378 - '<script nonce="' + escapeTextForBrowser(nonce) + '"',
403 + '<script nonce="' + escapeTextForBrowser(nonceScript) + '"',
404 + );
405 + const nonceStyle =
406 + typeof nonce === 'string' ? undefined : nonce && nonce.style;
407 + const inlineStyleWithNonce =
408 + nonceStyle === undefined
409 + ? startInlineStyle
410 + : stringToPrecomputedChunk(
411 + '<style nonce="' + escapeTextForBrowser(nonceStyle) + '"',
412 );
413 const idPrefix = resumableState.idPrefix;
414
@@ -403,7 +436,7 @@ export function createRenderState(
436 src: externalRuntimeConfig,
437 async: true,
438 integrity: undefined,
406 - nonce: nonce,
439 + nonce: nonceScript,
440 });
441 } else {
442 externalRuntimeScript = {
@@ -414,7 +447,7 @@ export function createRenderState(
447 src: externalRuntimeConfig.src,
448 async: true,
449 integrity: externalRuntimeConfig.integrity,
417 - nonce: nonce,
450 + nonce: nonceScript,
451 });
452 }
453 }
@@ -459,6 +492,7 @@ export function createRenderState(
492 segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
493 boundaryPrefix: stringToPrecomputedChunk(idPrefix + 'B:'),
494 startInlineScript: inlineScriptWithNonce,
495 + startInlineStyle: inlineStyleWithNonce,
496 preamble: createPreambleState(),
497
498 externalRuntimeScript: externalRuntimeScript,
@@ -500,7 +534,10 @@ export function createRenderState(
534 moduleScripts: new Map(),
535 },
536
503 - nonce,
537 + nonce: {
538 + script: nonceScript,
539 + style: nonceStyle,
540 + },
541 // like a module global for currently rendering boundary
542 hoistableState: null,
543 stylesToHoist: false,
@@ -539,10 +576,10 @@ export function createRenderState(
576 stringToChunk(escapeTextForBrowser(src)),
577 attributeEnd,
578 );
542 - if (nonce) {
579 + if (nonceScript) {
580 bootstrapChunks.push(
581 scriptNonce,
545 - stringToChunk(escapeTextForBrowser(nonce)),
582 + stringToChunk(escapeTextForBrowser(nonceScript)),
583 attributeEnd,
584 );
585 }
@@ -571,7 +608,7 @@ export function createRenderState(
608 const props: PreloadModuleProps = ({
609 rel: 'modulepreload',
610 fetchPriority: 'low',
574 - nonce,
611 + nonce: nonceScript,
612 }: any);
613 if (typeof scriptConfig === 'string') {
614 props.href = src = scriptConfig;
@@ -596,10 +633,10 @@ export function createRenderState(
633 stringToChunk(escapeTextForBrowser(src)),
634 attributeEnd,
635 );
599 - if (nonce) {
636 + if (nonceScript) {
637 bootstrapChunks.push(
638 scriptNonce,
602 - stringToChunk(escapeTextForBrowser(nonce)),
639 + stringToChunk(escapeTextForBrowser(nonceScript)),
640 attributeEnd,
641 );
642 }
@@ -627,7 +664,7 @@ export function createRenderState(
664
665 export function resumeRenderState(
666 resumableState: ResumableState,
630 - nonce: string | void,
667 + nonce: NonceOption | void,
668 ): RenderState {
669 return createRenderState(
670 resumableState,
@@ -3046,6 +3083,7 @@ function pushStyle(
3083 }
3084 const precedence = props.precedence;
3085 const href = props.href;
3086 + const nonce = props.nonce;
3087
3088 if (
3089 formatContext.insertionMode === SVG_MODE ||
@@ -3091,15 +3129,33 @@ function pushStyle(
3129 styleQueue = {
3130 precedence: stringToChunk(escapeTextForBrowser(precedence)),
3131 rules: ([]: Array<Chunk | PrecomputedChunk>),
3094 - hrefs: [stringToChunk(escapeTextForBrowser(href))],
3132 + hrefs: ([]: Array<Chunk | PrecomputedChunk>),
3133 sheets: (new Map(): Map<string, StylesheetResource>),
3134 };
3135 renderState.styles.set(precedence, styleQueue);
3098 - } else {
3099 - // We have seen this precedence before and need to track this href
3136 + }
3137 +
3138 + const nonceStyle = renderState.nonce.style;
3139 + if (!nonceStyle || nonceStyle === nonce) {
3140 + if (__DEV__) {
3141 + if (!nonceStyle && nonce) {
3142 + console.error(
3143 + 'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
3144 + precedence,
3145 + nonce,
3146 + );
3147 + }
3148 + }
3149 styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
3150 + pushStyleContents(styleQueue.rules, props);
3151 + } else if (__DEV__) {
3152 + console.error(
3153 + 'React encountered a style tag with `precedence` "%s" and `nonce` "%s". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "%s" that was included with this render.',
3154 + precedence,
3155 + nonce,
3156 + nonceStyle,
3157 + );
3158 }
3102 - pushStyleContents(styleQueue.rules, props);
3159 }
3160 if (styleQueue) {
3161 // We need to track whether this boundary should wait on this resource or not.
@@ -5148,7 +5204,7 @@ function escapeJSObjectForInstructionScripts(input: Object): string {
5204 }
5205
5206 const lateStyleTagResourceOpen1 = stringToPrecomputedChunk(
5151 - '<style media="not all" data-precedence="',
5207 + ' media="not all" data-precedence="',
5208 );
5209 const lateStyleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
5210 const lateStyleTagResourceOpen3 = stringToPrecomputedChunk('">');
@@ -5176,6 +5232,10 @@ function flushStyleTagsLateForBoundary(
5232 }
5233 let i = 0;
5234 if (hrefs.length) {
5235 + writeChunk(
5236 + this,
5237 + ((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
5238 + );
5239 writeChunk(this, lateStyleTagResourceOpen1);
5240 writeChunk(this, styleQueue.precedence);
5241 writeChunk(this, lateStyleTagResourceOpen2);
@@ -5225,7 +5285,9 @@ export function writeHoistablesForBoundary(
5285 destinationHasCapacity = true;
5286
5287 // Flush style tags for each precedence this boundary depends on
5288 + currentlyFlushingRenderState = renderState;
5289 hoistableState.styles.forEach(flushStyleTagsLateForBoundary, destination);
5290 + currentlyFlushingRenderState = null;
5291
5292 // Determine if this boundary has stylesheets that need to be awaited upon completion
5293 hoistableState.stylesheets.forEach(hasStylesToHoist);
@@ -5268,9 +5330,7 @@ function flushStyleInPreamble(
5330 stylesheet.state = PREAMBLE;
5331 }
5332
5271 -const styleTagResourceOpen1 = stringToPrecomputedChunk(
5272 - '<style data-precedence="',
5273 -);
5333 +const styleTagResourceOpen1 = stringToPrecomputedChunk(' data-precedence="');
5334 const styleTagResourceOpen2 = stringToPrecomputedChunk('" data-href="');
5335 const spaceSeparator = stringToPrecomputedChunk(' ');
5336 const styleTagResourceOpen3 = stringToPrecomputedChunk('">');
@@ -5292,6 +5352,10 @@ function flushStylesInPreamble(
5352 // order so even if there are no rules for style tags at this precedence we emit an empty style
5353 // tag with the data-precedence attribute
5354 if (!hasStylesheets || hrefs.length) {
5355 + writeChunk(
5356 + this,
5357 + ((currentlyFlushingRenderState: any): RenderState).startInlineStyle,
5358 + );
5359 writeChunk(this, styleTagResourceOpen1);
5360 writeChunk(this, styleQueue.precedence);
5361 let i = 0;
@@ -5466,7 +5530,9 @@ export function writePreambleStart(
5530 renderState.highImagePreloads.clear();
5531
5532 // Flush unblocked stylesheets by precedence
5533 + currentlyFlushingRenderState = renderState;
5534 renderState.styles.forEach(flushStylesInPreamble, destination);
5535 + currentlyFlushingRenderState = null;
5536
5537 const importMapChunks = renderState.importMapChunks;
5538 for (i = 0; i < importMapChunks.length; i++) {
packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js
+7
@@ -47,6 +47,7 @@ export type RenderState = {
47 segmentPrefix: PrecomputedChunk,
48 boundaryPrefix: PrecomputedChunk,
49 startInlineScript: PrecomputedChunk,
50 + startInlineStyle: PrecomputedChunk,
51 preamble: PreambleState,
52 externalRuntimeScript: null | any,
53 bootstrapChunks: Array<Chunk | PrecomputedChunk>,
@@ -76,6 +77,10 @@ export type RenderState = {
77 scripts: Map<string, Resource>,
78 moduleScripts: Map<string, Resource>,
79 },
80 + nonce: {
81 + script: string | void,
82 + style: string | void,
83 + },
84 stylesToHoist: boolean,
85 // This is an extra field for the legacy renderer
86 generateStaticMarkup: boolean,
@@ -99,6 +104,7 @@ export function createRenderState(
104 segmentPrefix: renderState.segmentPrefix,
105 boundaryPrefix: renderState.boundaryPrefix,
106 startInlineScript: renderState.startInlineScript,
107 + startInlineStyle: renderState.startInlineStyle,
108 preamble: renderState.preamble,
109 externalRuntimeScript: renderState.externalRuntimeScript,
110 bootstrapChunks: renderState.bootstrapChunks,
@@ -118,6 +124,7 @@ export function createRenderState(
124 scripts: renderState.scripts,
125 bulkPreloads: renderState.bulkPreloads,
126 preloads: renderState.preloads,
127 + nonce: renderState.nonce,
128 stylesToHoist: renderState.stylesToHoist,
129
130 // This is an extra field for the legacy renderer
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+168
@@ -10279,4 +10279,172 @@ describe('ReactDOMFizzServer', () => {
10279 </html>,
10280 );
10281 });
10282 +
10283 + it('can render styles with nonce', async () => {
10284 + CSPnonce = 'R4nd0m';
10285 + await act(() => {
10286 + const {pipe} = renderToPipeableStream(
10287 + <>
10288 + <style
10289 + href="foo"
10290 + precedence="default"
10291 + nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
10292 + <style
10293 + href="bar"
10294 + precedence="default"
10295 + nonce={CSPnonce}>{`.bar { background-color: blue; }`}</style>
10296 + </>,
10297 + {nonce: {style: CSPnonce}},
10298 + );
10299 + pipe(writable);
10300 + });
10301 + expect(document.querySelector('style').nonce).toBe(CSPnonce);
10302 + expect(getVisibleChildren(document)).toEqual(
10303 + <html>
10304 + <head />
10305 + <body>
10306 + <div id="container">
10307 + <style
10308 + data-precedence="default"
10309 + data-href="foo bar"
10310 + nonce={
10311 + CSPnonce
10312 + }>{`.foo { color: hotpink; }.bar { background-color: blue; }`}</style>
10313 + </div>
10314 + </body>
10315 + </html>,
10316 + );
10317 + });
10318 +
10319 + it("shouldn't render styles with mismatched nonce", async () => {
10320 + CSPnonce = 'R4nd0m';
10321 + await act(() => {
10322 + const {pipe} = renderToPipeableStream(
10323 + <>
10324 + <style
10325 + href="foo"
10326 + precedence="default"
10327 + nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
10328 + <style
10329 + href="bar"
10330 + precedence="default"
10331 + nonce={`${CSPnonce}${CSPnonce}`}>{`.bar { background-color: blue; }`}</style>
10332 + </>,
10333 + {nonce: {style: CSPnonce}},
10334 + );
10335 + pipe(writable);
10336 + });
10337 + assertConsoleErrorDev([
10338 + 'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0mR4nd0m". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "R4nd0m" that was included with this render.',
10339 + ]);
10340 + expect(getVisibleChildren(document)).toEqual(
10341 + <html>
10342 + <head />
10343 + <body>
10344 + <div id="container">
10345 + <style
10346 + data-precedence="default"
10347 + data-href="foo"
10348 + nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
10349 + </div>
10350 + </body>
10351 + </html>,
10352 + );
10353 + });
10354 +
10355 + it("should render styles without nonce when render call doesn't receive nonce", async () => {
10356 + await act(() => {
10357 + const {pipe} = renderToPipeableStream(
10358 + <>
10359 + <style
10360 + href="foo"
10361 + precedence="default"
10362 + nonce="R4nd0m">{`.foo { color: hotpink; }`}</style>
10363 + </>,
10364 + );
10365 + pipe(writable);
10366 + });
10367 + assertConsoleErrorDev([
10368 + 'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
10369 + ]);
10370 + expect(getVisibleChildren(document)).toEqual(
10371 + <html>
10372 + <head />
10373 + <body>
10374 + <div id="container">
10375 + <style
10376 + data-precedence="default"
10377 + data-href="foo">{`.foo { color: hotpink; }`}</style>
10378 + </div>
10379 + </body>
10380 + </html>,
10381 + );
10382 + });
10383 +
10384 + it('should render styles without nonce when render call receives a string nonce dedicated to scripts', async () => {
10385 + CSPnonce = 'R4nd0m';
10386 + await act(() => {
10387 + const {pipe} = renderToPipeableStream(
10388 + <>
10389 + <style
10390 + href="foo"
10391 + precedence="default"
10392 + nonce={CSPnonce}>{`.foo { color: hotpink; }`}</style>
10393 + </>,
10394 + {nonce: CSPnonce},
10395 + );
10396 + pipe(writable);
10397 + });
10398 + assertConsoleErrorDev([
10399 + 'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
10400 + ]);
10401 + expect(getVisibleChildren(document)).toEqual(
10402 + <html>
10403 + <head />
10404 + <body>
10405 + <div id="container">
10406 + <style
10407 + data-precedence="default"
10408 + data-href="foo">{`.foo { color: hotpink; }`}</style>
10409 + </div>
10410 + </body>
10411 + </html>,
10412 + );
10413 + });
10414 +
10415 + it('should allow for different script and style nonces', async () => {
10416 + CSPnonce = 'R4nd0m';
10417 + await act(() => {
10418 + const {pipe} = renderToPipeableStream(
10419 + <>
10420 + <style
10421 + href="foo"
10422 + precedence="default"
10423 + nonce="D1ff3r3nt">{`.foo { color: hotpink; }`}</style>
10424 + </>,
10425 + {
10426 + nonce: {script: CSPnonce, style: 'D1ff3r3nt'},
10427 + bootstrapScriptContent: 'function noop(){}',
10428 + },
10429 + );
10430 + pipe(writable);
10431 + });
10432 + const scripts = Array.from(container.getElementsByTagName('script')).filter(
10433 + node => node.getAttribute('nonce') === CSPnonce,
10434 + );
10435 + expect(scripts[scripts.length - 1].textContent).toBe('function noop(){}');
10436 + expect(getVisibleChildren(document)).toEqual(
10437 + <html>
10438 + <head />
10439 + <body>
10440 + <div id="container">
10441 + <style
10442 + data-precedence="default"
10443 + data-href="foo"
10444 + nonce="D1ff3r3nt">{`.foo { color: hotpink; }`}</style>
10445 + </div>
10446 + </body>
10447 + </html>,
10448 + );
10449 + });
10450 });
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+80
@@ -8595,6 +8595,86 @@ background-color: green;
8595 ' in style (at **)',
8596 ]);
8597 });
8598 +
8599 + it('can emit styles with nonce', async () => {
8600 + const nonce = 'R4nD0m';
8601 + const fooCss = '.foo { color: hotpink; }';
8602 + const barCss = '.bar { background-color: blue; }';
8603 + const bazCss = '.baz { border: 1px solid black; }';
8604 + await act(() => {
8605 + renderToPipeableStream(
8606 + <html>
8607 + <body>
8608 + <Suspense>
8609 + <BlockedOn value="first">
8610 + <div>first</div>
8611 + <style href="foo" precedence="default" nonce={nonce}>
8612 + {fooCss}
8613 + </style>
8614 + <style href="bar" precedence="default" nonce={nonce}>
8615 + {barCss}
8616 + </style>
8617 + <BlockedOn value="second">
8618 + <div>second</div>
8619 + <style href="baz" precedence="default" nonce={nonce}>
8620 + {bazCss}
8621 + </style>
8622 + </BlockedOn>
8623 + </BlockedOn>
8624 + </Suspense>
8625 + </body>
8626 + </html>,
8627 + {nonce: {style: nonce}},
8628 + ).pipe(writable);
8629 + });
8630 +
8631 + expect(getMeaningfulChildren(document)).toEqual(
8632 + <html>
8633 + <head />
8634 + <body />
8635 + </html>,
8636 + );
8637 +
8638 + await act(() => {
8639 + resolveText('first');
8640 + });
8641 +
8642 + expect(getMeaningfulChildren(document)).toEqual(
8643 + <html>
8644 + <head />
8645 + <body>
8646 + <style
8647 + data-href="foo bar"
8648 + data-precedence="default"
8649 + media="not all"
8650 + nonce={nonce}>
8651 + {`${fooCss}${barCss}`}
8652 + </style>
8653 + </body>
8654 + </html>,
8655 + );
8656 +
8657 + await act(() => {
8658 + resolveText('second');
8659 + });
8660 +
8661 + expect(getMeaningfulChildren(document)).toEqual(
8662 + <html>
8663 + <head>
8664 + <style data-href="foo bar" data-precedence="default" nonce={nonce}>
8665 + {`${fooCss}${barCss}`}
8666 + </style>
8667 + <style data-href="baz" data-precedence="default" nonce={nonce}>
8668 + {bazCss}
8669 + </style>
8670 + </head>
8671 + <body>
8672 + <div>first</div>
8673 + <div>second</div>
8674 + </body>
8675 + </html>,
8676 + );
8677 + });
8678 });
8679
8680 describe('Script Resources', () => {
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+9 -2
@@ -40,10 +40,17 @@ import {
40 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
41 ensureCorrectIsomorphicReactVersion();
42
43 +type NonceOption =
44 + | string
45 + | {
46 + script?: string,
47 + style?: string,
48 + };
49 +
50 type Options = {
51 identifierPrefix?: string,
52 namespaceURI?: string,
46 - nonce?: string,
53 + nonce?: NonceOption,
54 bootstrapScriptContent?: string,
55 bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
56 bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
@@ -59,7 +66,7 @@ type Options = {
66 };
67
68 type ResumeOptions = {
62 - nonce?: string,
69 + nonce?: NonceOption,
70 signal?: AbortSignal,
71 onError?: (error: mixed) => ?string,
72 onPostpone?: (reason: string) => void,
packages/react-dom/src/server/ReactDOMFizzServerBun.js
+6 -1
@@ -37,7 +37,12 @@ ensureCorrectIsomorphicReactVersion();
37 type Options = {
38 identifierPrefix?: string,
39 namespaceURI?: string,
40 - nonce?: string,
40 + nonce?:
41 + | string
42 + | {
43 + script?: string,
44 + style?: string,
45 + },
46 bootstrapScriptContent?: string,
47 bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
48 bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
packages/react-dom/src/server/ReactDOMFizzServerEdge.js
+9 -2
@@ -40,10 +40,17 @@ import {
40 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
41 ensureCorrectIsomorphicReactVersion();
42
43 +type NonceOption =
44 + | string
45 + | {
46 + script?: string,
47 + style?: string,
48 + };
49 +
50 type Options = {
51 identifierPrefix?: string,
52 namespaceURI?: string,
46 - nonce?: string,
53 + nonce?: NonceOption,
54 bootstrapScriptContent?: string,
55 bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
56 bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
@@ -59,7 +66,7 @@ type Options = {
66 };
67
68 type ResumeOptions = {
62 - nonce?: string,
69 + nonce?: NonceOption,
70 signal?: AbortSignal,
71 onError?: (error: mixed) => ?string,
72 onPostpone?: (reason: string) => void,
packages/react-dom/src/server/ReactDOMFizzServerNode.js
+9 -2
@@ -56,10 +56,17 @@ function createCancelHandler(request: Request, reason: string) {
56 };
57 }
58
59 +type NonceOption =
60 + | string
61 + | {
62 + script?: string,
63 + style?: string,
64 + };
65 +
66 type Options = {
67 identifierPrefix?: string,
68 namespaceURI?: string,
62 - nonce?: string,
69 + nonce?: NonceOption,
70 bootstrapScriptContent?: string,
71 bootstrapScripts?: Array<string | BootstrapScriptDescriptor>,
72 bootstrapModules?: Array<string | BootstrapScriptDescriptor>,
@@ -77,7 +84,7 @@ type Options = {
84 };
85
86 type ResumeOptions = {
80 - nonce?: string,
87 + nonce?: NonceOption,
88 onShellReady?: () => void,
89 onShellError?: (error: mixed) => void,
90 onAllReady?: () => void,
packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+8 -1
@@ -43,6 +43,13 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
43 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
44 ensureCorrectIsomorphicReactVersion();
45
46 +type NonceOption =
47 + | string
48 + | {
49 + script?: string,
50 + style?: string,
51 + };
52 +
53 type Options = {
54 identifierPrefix?: string,
55 namespaceURI?: string,
@@ -151,7 +158,7 @@ function prerender(
158 }
159
160 type ResumeOptions = {
154 - nonce?: string,
161 + nonce?: NonceOption,
162 signal?: AbortSignal,
163 onError?: (error: mixed) => ?string,
164 onPostpone?: (reason: string) => void,
packages/react-dom/src/server/ReactDOMFizzStaticEdge.js
+8 -1
@@ -43,6 +43,13 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
43 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
44 ensureCorrectIsomorphicReactVersion();
45
46 +type NonceOption =
47 + | string
48 + | {
49 + script?: string,
50 + style?: string,
51 + };
52 +
53 type Options = {
54 identifierPrefix?: string,
55 namespaceURI?: string,
@@ -150,7 +157,7 @@ function prerender(
157 }
158
159 type ResumeOptions = {
153 - nonce?: string,
160 + nonce?: NonceOption,
161 signal?: AbortSignal,
162 onError?: (error: mixed) => ?string,
163 onPostpone?: (reason: string) => void,
packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+8 -1
@@ -44,6 +44,13 @@ import {enablePostpone, enableHalt} from 'shared/ReactFeatureFlags';
44 import {ensureCorrectIsomorphicReactVersion} from '../shared/ensureCorrectIsomorphicReactVersion';
45 ensureCorrectIsomorphicReactVersion();
46
47 +type NonceOption =
48 + | string
49 + | {
50 + script?: string,
51 + style?: string,
52 + };
53 +
54 type Options = {
55 identifierPrefix?: string,
56 namespaceURI?: string,
@@ -151,7 +158,7 @@ function prerenderToNodeStream(
158 }
159
160 type ResumeOptions = {
154 - nonce?: string,
161 + nonce?: NonceOption,
162 signal?: AbortSignal,
163 onError?: (error: mixed, errorInfo: ErrorInfo) => ?string,
164 onPostpone?: (reason: string, postponeInfo: PostponeInfo) => void,