Add support for 'crossorigin' attribute on bootstrapScripts and bootstrapModules (#26844)
base build ci job failing but this change is unrelated and I think it is just flake with the builds host application
Henrique Limas committed
Jun 13, 2023 at 09:12 UTC
90229eb925e2330667fb7c023a5c4317c7ae8363
2 files changed
+89
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+26
@@ -163,6 +163,7 @@ const startScriptSrc = stringToPrecomputedChunk('<script src="');
163
const startModuleSrc = stringToPrecomputedChunk('<script type="module" src="');
164
const scriptNonce = stringToPrecomputedChunk('" nonce="');
165
const scriptIntegirty = stringToPrecomputedChunk('" integrity="');
166
+const scriptCrossOrigin = stringToPrecomputedChunk('" crossorigin="');
167
const endAsyncScript = stringToPrecomputedChunk('" async=""></script>');
168
169
/**
@@ -192,6 +193,7 @@ const scriptReplacer = (
193
export type BootstrapScriptDescriptor = {
194
src: string,
195
integrity?: string,
196
+ crossOrigin?: string,
197
};
198
export type ExternalRuntimeScript = {
199
src: string,
@@ -266,6 +268,12 @@ export function createResponseState(
268
typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
269
const integrity =
270
typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
271
+ const crossOrigin =
272
+ typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null
273
+ ? undefined
274
+ : scriptConfig.crossOrigin === 'use-credentials'
275
+ ? 'use-credentials'
276
+ : '';
277
278
preloadBootstrapScript(resources, src, nonce, integrity);
279
@@ -285,6 +293,12 @@ export function createResponseState(
293
stringToChunk(escapeTextForBrowser(integrity)),
294
);
295
}
296
+ if (typeof crossOrigin === 'string') {
297
+ bootstrapChunks.push(
298
+ scriptCrossOrigin,
299
+ stringToChunk(escapeTextForBrowser(crossOrigin)),
300
+ );
301
+ }
302
bootstrapChunks.push(endAsyncScript);
303
}
304
}
@@ -295,6 +309,12 @@ export function createResponseState(
309
typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
310
const integrity =
311
typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
312
+ const crossOrigin =
313
+ typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null
314
+ ? undefined
315
+ : scriptConfig.crossOrigin === 'use-credentials'
316
+ ? 'use-credentials'
317
+ : '';
318
319
preloadBootstrapModule(resources, src, nonce, integrity);
320
@@ -315,6 +335,12 @@ export function createResponseState(
335
stringToChunk(escapeTextForBrowser(integrity)),
336
);
337
}
338
+ if (typeof crossOrigin === 'string') {
339
+ bootstrapChunks.push(
340
+ scriptCrossOrigin,
341
+ stringToChunk(escapeTextForBrowser(crossOrigin)),
342
+ );
343
+ }
344
bootstrapChunks.push(endAsyncScript);
345
}
346
}
packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js
+63
@@ -3824,6 +3824,69 @@ describe('ReactDOMFizzServer', () => {
3824
]);
3825
});
3826
3827
+ it('accepts a crossOrigin property for bootstrapScripts and bootstrapModules', async () => {
3828
+ await act(() => {
3829
+ const {pipe} = renderToPipeableStream(
3830
+ <html>
3831
+ <head />
3832
+ <body>
3833
+ <div>hello world</div>
3834
+ </body>
3835
+ </html>,
3836
+ {
3837
+ bootstrapScripts: [
3838
+ 'foo',
3839
+ {
3840
+ src: 'bar',
3841
+ },
3842
+ {
3843
+ src: 'baz',
3844
+ crossOrigin: '',
3845
+ },
3846
+ {
3847
+ src: 'qux',
3848
+ crossOrigin: 'defaults-to-empty',
3849
+ },
3850
+ ],
3851
+ bootstrapModules: [
3852
+ 'quux',
3853
+ {
3854
+ src: 'corge',
3855
+ },
3856
+ {
3857
+ src: 'grault',
3858
+ crossOrigin: 'use-credentials',
3859
+ },
3860
+ ],
3861
+ },
3862
+ );
3863
+ pipe(writable);
3864
+ });
3865
+
3866
+ expect(getVisibleChildren(document)).toEqual(
3867
+ <html>
3868
+ <head />
3869
+ <body>
3870
+ <div>hello world</div>
3871
+ </body>
3872
+ </html>,
3873
+ );
3874
+ expect(
3875
+ stripExternalRuntimeInNodes(
3876
+ document.getElementsByTagName('script'),
3877
+ renderOptions.unstable_externalRuntimeSrc,
3878
+ ).map(n => n.outerHTML),
3879
+ ).toEqual([
3880
+ '<script src="foo" async=""></script>',
3881
+ '<script src="bar" async=""></script>',
3882
+ '<script src="baz" crossorigin="" async=""></script>',
3883
+ '<script src="qux" crossorigin="" async=""></script>',
3884
+ '<script type="module" src="quux" async=""></script>',
3885
+ '<script type="module" src="corge" async=""></script>',
3886
+ '<script type="module" src="grault" crossorigin="use-credentials" async=""></script>',
3887
+ ]);
3888
+ });
3889
+
3890
describe('bootstrapScriptContent escaping', () => {
3891
it('the "S" in "</?[Ss]cript" strings are replaced with unicode escaped lowercase s or S depending on case, preserving case sensitivity of nearby characters', async () => {
3892
window.__test_outlet = '';