15
PreloadModuleOptions,
16
PreinitOptions,
17
PreinitModuleOptions,
18
+ ImportMap,
19
} from 'react-dom/src/shared/ReactDOMTypes';
20
21
import {
140
// Hoistable chunks
141
charsetChunks: Array<Chunk | PrecomputedChunk>,
142
preconnectChunks: Array<Chunk | PrecomputedChunk>,
143
+ importMapChunks: Array<Chunk | PrecomputedChunk>,
144
preloadChunks: Array<Chunk | PrecomputedChunk>,
145
hoistableChunks: Array<Chunk | PrecomputedChunk>,
146
207
const endAsyncScript = stringToPrecomputedChunk('" async=""></script>');
208
209
/**
208
- * This escaping function is designed to work with bootstrapScriptContent only.
210
+ * This escaping function is designed to work with bootstrapScriptContent and importMap only.
211
* because we know we are escaping the entire script. We can avoid for instance
212
* escaping html comment string sequences that are valid javascript as well because
213
* if there are no sebsequent <script sequences the html parser will never enter
216
* While untrusted script content should be made safe before using this api it will
217
* ensure that the script cannot be early terminated or never terminated state
218
*/
217
-function escapeBootstrapScriptContent(scriptText: string) {
219
+function escapeBootstrapAndImportMapScriptContent(scriptText: string) {
220
if (__DEV__) {
221
checkHtmlStringCoercion(scriptText);
222
}
239
src: string,
240
chunks: Array<Chunk | PrecomputedChunk>,
241
};
242
+
243
+const importMapScriptStart = stringToPrecomputedChunk(
244
+ '<script type="importmap">',
245
+);
246
+const importMapScriptEnd = stringToPrecomputedChunk('</script>');
247
+
248
// Allows us to keep track of what we've already written so we can refer back to it.
249
// if passed externalRuntimeConfig and the enableFizzExternalRuntime feature flag
250
// is set, the server will send instructions via data attributes (instead of inline scripts)
251
export function createRenderState(
252
resumableState: ResumableState,
253
nonce: string | void,
254
+ importMap: ImportMap | void,
255
): RenderState {
256
const inlineScriptWithNonce =
257
nonce === undefined
260
'<script nonce="' + escapeTextForBrowser(nonce) + '">',
261
);
262
const idPrefix = resumableState.idPrefix;
263
+ const importMapChunks: Array<Chunk | PrecomputedChunk> = [];
264
+ if (importMap !== undefined) {
265
+ const map = importMap;
266
+ importMapChunks.push(importMapScriptStart);
267
+ importMapChunks.push(
268
+ stringToChunk(
269
+ escapeBootstrapAndImportMapScriptContent(JSON.stringify(map)),
270
+ ),
271
+ );
272
+ importMapChunks.push(importMapScriptEnd);
273
+ }
274
return {
275
placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'),
276
segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
280
headChunks: null,
281
charsetChunks: [],
282
preconnectChunks: [],
283
+ importMapChunks,
284
preloadChunks: [],
285
hoistableChunks: [],
286
nonce,
311
);
312
bootstrapChunks.push(
313
inlineScriptWithNonce,
293
- stringToChunk(escapeBootstrapScriptContent(bootstrapScriptContent)),
314
+ stringToChunk(
315
+ escapeBootstrapAndImportMapScriptContent(bootstrapScriptContent),
316
+ ),
317
endInlineScript,
318
);
319
}
4365
// Flush unblocked stylesheets by precedence
4366
resumableState.precedences.forEach(flushAllStylesInPreamble, destination);
4367
4368
+ const importMapChunks = renderState.importMapChunks;
4369
+ for (i = 0; i < importMapChunks.length; i++) {
4370
+ writeChunk(destination, importMapChunks[i]);
4371
+ }
4372
+ importMapChunks.length = 0;
4373
+
4374
resumableState.bootstrapScripts.forEach(flushResourceInPreamble, destination);
4375
4376
resumableState.scripts.forEach(flushResourceInPreamble, destination);
4444
// but we want to kick off preloading as soon as possible
4445
resumableState.precedences.forEach(preloadLateStyles, destination);
4446
4447
+ // We only hoist importmaps that are configured through createResponse and that will
4448
+ // always flush in the preamble. Generally we don't expect people to render them as
4449
+ // tags when using React but if you do they are going to be treated like regular inline
4450
+ // scripts and flush after other hoistables which is problematic
4451
+
4452
// bootstrap scripts should flush above script priority but these can only flush in the preamble
4453
// so we elide the code here for performance
4454