Skip to content

Commit 24f4565

Browse files
committed
preloads bootstrap modules
1 parent 2899034 commit 24f4565

File tree

7 files changed

+58
-14
lines changed

7 files changed

+58
-14
lines changed

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

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ export function createResponseState(
296296
const integrity =
297297
typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
298298

299+
preloadBootstrapModule(resources, src);
300+
299301
bootstrapChunks.push(
300302
startModuleSrc,
301303
stringToChunk(escapeTextForBrowser(src)),
@@ -1886,7 +1888,7 @@ function pushLink(
18861888
}
18871889
}
18881890
pushLinkImpl(resource.chunks, resource.props);
1889-
resources.usedStylesheets.add(resource);
1891+
resources.usedStylesheets.set(key, resource);
18901892
return pushLinkImpl(target, props);
18911893
} else {
18921894
// This stylesheet refers to a Resource and we create a new one if necessary
@@ -4158,8 +4160,7 @@ export function writePreamble(
41584160
// Flush unblocked stylesheets by precedence
41594161
resources.precedences.forEach(flushAllStylesInPreamble, destination);
41604162

4161-
resources.usedStylesheets.forEach(resource => {
4162-
const key = getResourceKey(resource.props.as, resource.props.href);
4163+
resources.usedStylesheets.forEach((resource, key) => {
41634164
if (resources.stylesMap.has(key)) {
41644165
// The underlying stylesheet is represented both as a used stylesheet
41654166
// (a regular component we will attempt to preload) and as a StylesheetResource.
@@ -4254,8 +4255,7 @@ export function writeHoistables(
42544255
// but we want to kick off preloading as soon as possible
42554256
resources.precedences.forEach(preloadLateStyles, destination);
42564257

4257-
resources.usedStylesheets.forEach(resource => {
4258-
const key = getResourceKey(resource.props.as, resource.props.href);
4258+
resources.usedStylesheets.forEach((resource, key) => {
42594259
if (resources.stylesMap.has(key)) {
42604260
// The underlying stylesheet is represented both as a used stylesheet
42614261
// (a regular component we will attempt to preload) and as a StylesheetResource.
@@ -4770,12 +4770,18 @@ type PreconnectProps = {
47704770
};
47714771
type PreconnectResource = TResource<'preconnect', null>;
47724772

4773-
type PreloadProps = {
4773+
type PreloadAsProps = {
47744774
rel: 'preload',
47754775
as: string,
47764776
href: string,
47774777
[string]: mixed,
47784778
};
4779+
type PreloadModuleProps = {
4780+
rel: 'modulepreload',
4781+
href: string,
4782+
[string]: mixed,
4783+
};
4784+
type PreloadProps = PreloadAsProps | PreloadModuleProps;
47794785
type PreloadResource = TResource<'preload', PreloadProps>;
47804786

47814787
type StylesheetProps = {
@@ -4820,7 +4826,7 @@ export type Resources = {
48204826
// usedImagePreloads: Set<PreloadResource>,
48214827
precedences: Map<string, Set<StyleResource>>,
48224828
stylePrecedences: Map<string, StyleTagResource>,
4823-
usedStylesheets: Set<PreloadResource>,
4829+
usedStylesheets: Map<string, PreloadResource>,
48244830
scripts: Set<ScriptResource>,
48254831
usedScripts: Set<PreloadResource>,
48264832
explicitStylesheetPreloads: Set<PreloadResource>,
@@ -4848,7 +4854,7 @@ export function createResources(): Resources {
48484854
// usedImagePreloads: new Set(),
48494855
precedences: new Map(),
48504856
stylePrecedences: new Map(),
4851-
usedStylesheets: new Set(),
4857+
usedStylesheets: new Map(),
48524858
scripts: new Set(),
48534859
usedScripts: new Set(),
48544860
explicitStylesheetPreloads: new Set(),
@@ -5414,6 +5420,39 @@ function preloadBootstrapScript(resources: Resources, src: string): void {
54145420
pushLinkImpl(resource.chunks, props);
54155421
}
54165422

5423+
// This function is only safe to call at Request start time since it assumes
5424+
// that each module has not already been preloaded. If we find a need to preload
5425+
// scripts at any other point in time we will need to check whether the preload
5426+
// already exists and not assume it
5427+
function preloadBootstrapModule(resources: Resources, src: string): void {
5428+
const key = getResourceKey('script', src);
5429+
if (__DEV__) {
5430+
if (resources.preloadsMap.has(key)) {
5431+
// This is coded as a React error because it should be impossible for a userspace preload to preempt this call
5432+
// If a userspace preload can preempt it then this assumption is broken and we need to reconsider this strategy
5433+
// rather than instruct the user to not preload their bootstrap scripts themselves
5434+
console.error(
5435+
'Internal React Error: React expected bootstrap module with src "%s" to not have been preloaded already. please file an issue',
5436+
src,
5437+
);
5438+
}
5439+
}
5440+
const props: PreloadModuleProps = {
5441+
rel: 'modulepreload',
5442+
href: src,
5443+
};
5444+
const resource: PreloadResource = {
5445+
type: 'preload',
5446+
chunks: [],
5447+
state: NoState,
5448+
props,
5449+
};
5450+
resources.preloadsMap.set(key, resource);
5451+
resources.explicitScriptPreloads.add(resource);
5452+
pushLinkImpl(resource.chunks, props);
5453+
return;
5454+
}
5455+
54175456
function internalPreinitScript(
54185457
resources: Resources,
54195458
src: string,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,7 @@ describe('ReactDOMFizzServer', () => {
603603

604604
expect(getVisibleChildren(container)).toEqual([
605605
<link rel="preload" href="init.js" as="script" />,
606+
<link rel="modulepreload" href="init.mjs" />,
606607
<div>Loading...</div>,
607608
]);
608609

@@ -619,6 +620,7 @@ describe('ReactDOMFizzServer', () => {
619620
});
620621
expect(getVisibleChildren(container)).toEqual([
621622
<link rel="preload" href="init.js" as="script" />,
623+
<link rel="modulepreload" href="init.mjs" />,
622624
<div>Hello</div>,
623625
]);
624626
} finally {
@@ -3672,6 +3674,9 @@ describe('ReactDOMFizzServer', () => {
36723674
<link rel="preload" href="foo" as="script" />
36733675
<link rel="preload" href="bar" as="script" />
36743676
<link rel="preload" href="baz" as="script" />
3677+
<link rel="modulepreload" href="quux" />
3678+
<link rel="modulepreload" href="corge" />
3679+
<link rel="modulepreload" href="grault" />
36753680
</head>
36763681
<body>
36773682
<div>hello world</div>

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-
`"<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>"`,
87+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><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-
`"<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>"`,
503+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><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-
`"<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>"`,
101+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><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-
`"<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>"`,
87+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><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-
`"<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>"`,
89+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><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-server-dom-relay/src/__tests__/ReactDOMServerFB-test.internal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('ReactDOMServerFB', () => {
5959
});
6060
const result = readResult(stream);
6161
expect(result).toMatchInlineSnapshot(
62-
`"<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>"`,
62+
`"<link rel="preload" href="init.js" as="script"/><link rel="modulepreload" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
6363
);
6464
});
6565

0 commit comments

Comments
 (0)