Skip to content

Commit 2899034

Browse files
committed
bootstrap scripts now preload
1 parent 8ea96ef commit 2899034

19 files changed

+109
-14
lines changed

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export type ExternalRuntimeScript = {
201201
// if passed externalRuntimeConfig and the enableFizzExternalRuntime feature flag
202202
// is set, the server will send instructions via data attributes (instead of inline scripts)
203203
export function createResponseState(
204+
resources: Resources,
204205
identifierPrefix: string | void,
205206
nonce: string | void,
206207
bootstrapScriptContent: string | void,
@@ -266,6 +267,8 @@ export function createResponseState(
266267
const integrity =
267268
typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
268269

270+
preloadBootstrapScript(resources, src);
271+
269272
bootstrapChunks.push(
270273
startScriptSrc,
271274
stringToChunk(escapeTextForBrowser(src)),
@@ -5378,6 +5381,39 @@ function preinit(href: string, options: PreinitOptions): void {
53785381
}
53795382
}
53805383

5384+
// This function is only safe to call at Request start time since it assumes
5385+
// that each script has not already been preloaded. If we find a need to preload
5386+
// scripts at any other point in time we will need to check whether the preload
5387+
// already exists and not assume it
5388+
function preloadBootstrapScript(resources: Resources, src: string): void {
5389+
const key = getResourceKey('script', src);
5390+
if (__DEV__) {
5391+
if (resources.preloadsMap.has(key)) {
5392+
// This is coded as a React error because it should be impossible for a userspace preload to preempt this call
5393+
// If a userspace preload can preempt it then this assumption is broken and we need to reconsider this strategy
5394+
// rather than instruct the user to not preload their bootstrap scripts themselves
5395+
console.error(
5396+
'Internal React Error: React expected bootstrap script with src "%s" to not have been preloaded already. please file an issue',
5397+
src,
5398+
);
5399+
}
5400+
}
5401+
const props: PreloadProps = {
5402+
rel: 'preload',
5403+
href: src,
5404+
as: 'script',
5405+
};
5406+
const resource: PreloadResource = {
5407+
type: 'preload',
5408+
chunks: [],
5409+
state: NoState,
5410+
props,
5411+
};
5412+
resources.preloadsMap.set(key, resource);
5413+
resources.explicitScriptPreloads.add(resource);
5414+
pushLinkImpl(resource.chunks, props);
5415+
}
5416+
53815417
function internalPreinitScript(
53825418
resources: Resources,
53835419
src: string,

packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import type {
11+
Resources,
1112
BootstrapScriptDescriptor,
1213
ExternalRuntimeScript,
1314
FormatContext,
@@ -63,11 +64,13 @@ export type ResponseState = {
6364
};
6465

6566
export function createResponseState(
67+
resources: Resources,
6668
generateStaticMarkup: boolean,
6769
identifierPrefix: string | void,
6870
externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
6971
): ResponseState {
7072
const responseState = createResponseStateImpl(
73+
resources,
7174
identifierPrefix,
7275
undefined,
7376
undefined,

packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,10 @@ describe('ReactDOMFizzServer', () => {
601601
pipe(writable);
602602
});
603603

604-
expect(getVisibleChildren(container)).toEqual(<div>Loading...</div>);
604+
expect(getVisibleChildren(container)).toEqual([
605+
<link rel="preload" href="init.js" as="script" />,
606+
<div>Loading...</div>,
607+
]);
605608

606609
// check that there are 4 scripts with a matching nonce:
607610
// The runtime script, an inline bootstrap script, and two src scripts
@@ -614,7 +617,10 @@ describe('ReactDOMFizzServer', () => {
614617
await act(() => {
615618
resolve({default: Text});
616619
});
617-
expect(getVisibleChildren(container)).toEqual(<div>Hello</div>);
620+
expect(getVisibleChildren(container)).toEqual([
621+
<link rel="preload" href="init.js" as="script" />,
622+
<div>Hello</div>,
623+
]);
618624
} finally {
619625
CSPnonce = null;
620626
}
@@ -3662,7 +3668,11 @@ describe('ReactDOMFizzServer', () => {
36623668

36633669
expect(getVisibleChildren(document)).toEqual(
36643670
<html>
3665-
<head />
3671+
<head>
3672+
<link rel="preload" href="foo" as="script" />
3673+
<link rel="preload" href="bar" as="script" />
3674+
<link rel="preload" href="baz" as="script" />
3675+
</head>
36663676
<body>
36673677
<div>hello world</div>
36683678
</body>

packages/react-dom/src/__tests__/ReactDOMFizzServerBrowser-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('ReactDOMFizzServerBrowser', () => {
8484
);
8585
const result = await readResult(stream);
8686
expect(result).toMatchInlineSnapshot(
87-
`"<div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
87+
`"<link rel="preload" href="init.js" as="script"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
8888
);
8989
});
9090

@@ -500,7 +500,7 @@ describe('ReactDOMFizzServerBrowser', () => {
500500
);
501501
const result = await readResult(stream);
502502
expect(result).toMatchInlineSnapshot(
503-
`"<div>hello world</div><script nonce="${nonce}">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
503+
`"<link rel="preload" href="init.js" as="script"/><div>hello world</div><script nonce="${nonce}">INIT();</script><script src="init.js" nonce="${nonce}" async=""></script><script type="module" src="init.mjs" nonce="${nonce}" async=""></script>"`,
504504
);
505505
});
506506
});

packages/react-dom/src/__tests__/ReactDOMFizzServerNode-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('ReactDOMFizzServerNode', () => {
9898
pipe(writable);
9999
jest.runAllTimers();
100100
expect(output.result).toMatchInlineSnapshot(
101-
`"<div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
101+
`"<link rel="preload" href="init.js" as="script"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
102102
);
103103
});
104104

packages/react-dom/src/__tests__/ReactDOMFizzStaticBrowser-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ describe('ReactDOMFizzStaticBrowser', () => {
8484
});
8585
const prelude = await readContent(result.prelude);
8686
expect(prelude).toMatchInlineSnapshot(
87-
`"<div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
87+
`"<link rel="preload" href="init.js" as="script"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
8888
);
8989
});
9090

packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('ReactDOMFizzStaticNode', () => {
8686
);
8787
const prelude = await readContent(result.prelude);
8888
expect(prelude).toMatchInlineSnapshot(
89-
`"<div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
89+
`"<link rel="preload" href="init.js" as="script"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
9090
);
9191
});
9292

packages/react-dom/src/server/ReactDOMFizzServerBrowser.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from 'react-server/src/ReactFizzServer';
2121

2222
import {
23+
createResources,
2324
createResponseState,
2425
createRootFormatContext,
2526
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
@@ -79,9 +80,12 @@ function renderToReadableStream(
7980
allReady.catch(() => {});
8081
reject(error);
8182
}
83+
const resources = createResources();
8284
const request = createRequest(
8385
children,
86+
resources,
8487
createResponseState(
88+
resources,
8589
options ? options.identifierPrefix : undefined,
8690
options ? options.nonce : undefined,
8791
options ? options.bootstrapScriptContent : undefined,

packages/react-dom/src/server/ReactDOMFizzServerBun.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from 'react-server/src/ReactFizzServer';
2121

2222
import {
23+
createResources,
2324
createResponseState,
2425
createRootFormatContext,
2526
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
@@ -80,9 +81,12 @@ function renderToReadableStream(
8081
allReady.catch(() => {});
8182
reject(error);
8283
}
84+
const resources = createResources();
8385
const request = createRequest(
8486
children,
87+
resources,
8588
createResponseState(
89+
resources,
8690
options ? options.identifierPrefix : undefined,
8791
options ? options.nonce : undefined,
8892
options ? options.bootstrapScriptContent : undefined,

packages/react-dom/src/server/ReactDOMFizzServerEdge.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from 'react-server/src/ReactFizzServer';
2121

2222
import {
23+
createResources,
2324
createResponseState,
2425
createRootFormatContext,
2526
} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
@@ -79,9 +80,12 @@ function renderToReadableStream(
7980
allReady.catch(() => {});
8081
reject(error);
8182
}
83+
const resources = createResources();
8284
const request = createRequest(
8385
children,
86+
resources,
8487
createResponseState(
88+
resources,
8589
options ? options.identifierPrefix : undefined,
8690
options ? options.nonce : undefined,
8791
options ? options.bootstrapScriptContent : undefined,

0 commit comments

Comments
 (0)