diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index 5314564242ed9..b74492102158d 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -163,6 +163,7 @@ const startScriptSrc = stringToPrecomputedChunk(''); /** @@ -192,6 +193,7 @@ const scriptReplacer = ( export type BootstrapScriptDescriptor = { src: string, integrity?: string, + crossOrigin?: string, }; export type ExternalRuntimeScript = { src: string, @@ -265,6 +267,12 @@ export function createResponseState( typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src; const integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity; + const crossOrigin = + typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null + ? undefined + : scriptConfig.crossOrigin === 'use-credentials' + ? 'use-credentials' + : ''; bootstrapChunks.push( startScriptSrc, @@ -282,6 +290,12 @@ export function createResponseState( stringToChunk(escapeTextForBrowser(integrity)), ); } + if (typeof crossOrigin === 'string') { + bootstrapChunks.push( + scriptCrossOrigin, + stringToChunk(escapeTextForBrowser(crossOrigin)), + ); + } bootstrapChunks.push(endAsyncScript); } } @@ -292,6 +306,12 @@ export function createResponseState( typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src; const integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity; + const crossOrigin = + typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null + ? undefined + : scriptConfig.crossOrigin === 'use-credentials' + ? 'use-credentials' + : ''; bootstrapChunks.push( startModuleSrc, @@ -310,6 +330,12 @@ export function createResponseState( stringToChunk(escapeTextForBrowser(integrity)), ); } + if (typeof crossOrigin === 'string') { + bootstrapChunks.push( + scriptCrossOrigin, + stringToChunk(escapeTextForBrowser(crossOrigin)), + ); + } bootstrapChunks.push(endAsyncScript); } } diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 2bf48eae39b18..be4a2353a1077 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -3777,6 +3777,69 @@ describe('ReactDOMFizzServer', () => { ]); }); + it('accepts a crossOrigin property for bootstrapScripts and bootstrapModules', async () => { + await act(() => { + const {pipe} = renderToPipeableStream( + + + +
hello world
+ + , + { + bootstrapScripts: [ + 'foo', + { + src: 'bar', + }, + { + src: 'baz', + crossOrigin: '', + }, + { + src: 'qux', + crossOrigin: 'defaults-to-empty', + }, + ], + bootstrapModules: [ + 'quux', + { + src: 'corge', + }, + { + src: 'grault', + crossOrigin: 'use-credentials', + }, + ], + }, + ); + pipe(writable); + }); + + expect(getVisibleChildren(document)).toEqual( + + + +
hello world
+ + , + ); + expect( + stripExternalRuntimeInNodes( + document.getElementsByTagName('script'), + renderOptions.unstable_externalRuntimeSrc, + ).map(n => n.outerHTML), + ).toEqual([ + '', + '', + '', + '', + '', + '', + '', + ]); + }); + describe('bootstrapScriptContent escaping', () => { it('the "S" in " { window.__test_outlet = '';