@bleedingdev/modern-js-runtime 3.2.0-ultramodern.96 → 3.2.0-ultramodern.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/server/scriptOrder.js +59 -0
- package/dist/cjs/core/server/stream/afterTemplate.js +6 -1
- package/dist/cjs/core/server/string/index.js +12 -5
- package/dist/cjs/core/server/string/loadable.js +42 -4
- package/dist/cjs/router/runtime/tanstack/hydrationBoundary.js +43 -0
- package/dist/cjs/router/runtime/tanstack/plugin.js +4 -2
- package/dist/cjs/router/runtime/tanstack/plugin.node.js +3 -2
- package/dist/esm/core/server/scriptOrder.mjs +25 -0
- package/dist/esm/core/server/stream/afterTemplate.mjs +6 -1
- package/dist/esm/core/server/string/index.mjs +12 -5
- package/dist/esm/core/server/string/loadable.mjs +38 -3
- package/dist/esm/router/runtime/tanstack/hydrationBoundary.mjs +9 -0
- package/dist/esm/router/runtime/tanstack/plugin.mjs +4 -2
- package/dist/esm/router/runtime/tanstack/plugin.node.mjs +3 -2
- package/dist/esm-node/core/server/scriptOrder.mjs +26 -0
- package/dist/esm-node/core/server/stream/afterTemplate.mjs +6 -1
- package/dist/esm-node/core/server/string/index.mjs +12 -5
- package/dist/esm-node/core/server/string/loadable.mjs +38 -3
- package/dist/esm-node/router/runtime/tanstack/hydrationBoundary.mjs +10 -0
- package/dist/esm-node/router/runtime/tanstack/plugin.mjs +4 -2
- package/dist/esm-node/router/runtime/tanstack/plugin.node.mjs +3 -2
- package/dist/types/core/server/scriptOrder.d.ts +1 -0
- package/dist/types/core/server/string/loadable.d.ts +7 -0
- package/dist/types/router/runtime/tanstack/hydrationBoundary.d.ts +2 -0
- package/package.json +8 -8
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
injectBeforeHydrationEntryScript: ()=>injectBeforeHydrationEntryScript
|
|
28
|
+
});
|
|
29
|
+
function getScriptTags(template) {
|
|
30
|
+
const scriptRegExp = /<script\b[^>]*\bsrc=(["'])(.*?)\1[^>]*><\/script>/g;
|
|
31
|
+
return Array.from(template.matchAll(scriptRegExp)).map((match)=>({
|
|
32
|
+
index: match.index ?? 0,
|
|
33
|
+
tag: match[0],
|
|
34
|
+
src: match[2]
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
function getAssetBasename(src) {
|
|
38
|
+
const withoutQuery = src.split(/[?#]/)[0];
|
|
39
|
+
return withoutQuery.split('/').pop() || withoutQuery;
|
|
40
|
+
}
|
|
41
|
+
function isEntryScript(src, entryName, asyncEntry) {
|
|
42
|
+
const basename = getAssetBasename(src);
|
|
43
|
+
const prefix = asyncEntry ? `async-${entryName}` : entryName;
|
|
44
|
+
return basename === `${prefix}.js` || basename.startsWith(`${prefix}.`) || basename.startsWith(`${prefix}-`);
|
|
45
|
+
}
|
|
46
|
+
function injectBeforeHydrationEntryScript(template, scripts, entryName = 'index') {
|
|
47
|
+
if (!scripts) return template;
|
|
48
|
+
const scriptTags = getScriptTags(template);
|
|
49
|
+
const target = scriptTags.find((match)=>isEntryScript(match.src, entryName, false)) ?? scriptTags.find((match)=>isEntryScript(match.src, entryName, true));
|
|
50
|
+
if (!target) return template;
|
|
51
|
+
return `${template.slice(0, target.index)}${scripts}${template.slice(target.index)}`;
|
|
52
|
+
}
|
|
53
|
+
exports.injectBeforeHydrationEntryScript = __webpack_exports__.injectBeforeHydrationEntryScript;
|
|
54
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
55
|
+
"injectBeforeHydrationEntryScript"
|
|
56
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
57
|
+
Object.defineProperty(exports, '__esModule', {
|
|
58
|
+
value: true
|
|
59
|
+
});
|
|
@@ -30,6 +30,7 @@ const node_namespaceObject = require("@modern-js/runtime-utils/node");
|
|
|
30
30
|
const lifecycle_js_namespaceObject = require("../../../router/runtime/lifecycle.js");
|
|
31
31
|
const external_constants_js_namespaceObject = require("../../constants.js");
|
|
32
32
|
const external_constants_js_namespaceObject_1 = require("../constants.js");
|
|
33
|
+
const external_scriptOrder_js_namespaceObject = require("../scriptOrder.js");
|
|
33
34
|
const external_shared_js_namespaceObject = require("../shared.js");
|
|
34
35
|
const external_utils_js_namespaceObject = require("../utils.js");
|
|
35
36
|
function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
@@ -58,7 +59,11 @@ function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
|
58
59
|
const jsAssets = Array.from(new Set(assetEntries.flatMap((entry)=>(entry.assets ?? []).filter((asset)=>asset.endsWith('.js')))));
|
|
59
60
|
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
|
|
60
61
|
const jsChunkStr = jsAssets.filter((asset)=>!template.includes(asset)).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
|
|
61
|
-
if (jsChunkStr)
|
|
62
|
+
if (jsChunkStr) {
|
|
63
|
+
const withoutPlaceholder = (0, external_utils_js_namespaceObject.safeReplace)(template, '<!--<?- chunksMap.js ?>-->', '');
|
|
64
|
+
const withEarlyScripts = (0, external_scriptOrder_js_namespaceObject.injectBeforeHydrationEntryScript)(withoutPlaceholder, jsChunkStr, entryName);
|
|
65
|
+
return withEarlyScripts !== withoutPlaceholder ? withEarlyScripts : (0, external_utils_js_namespaceObject.safeReplace)(template, '<!--<?- chunksMap.js ?>-->', jsChunkStr);
|
|
66
|
+
}
|
|
62
67
|
return template;
|
|
63
68
|
}
|
|
64
69
|
return (0, external_shared_js_namespaceObject.buildHtml)(afterAppTemplate, callbacks);
|
|
@@ -44,6 +44,7 @@ const index_js_namespaceObject = require("../../context/index.js");
|
|
|
44
44
|
const wrapper_js_namespaceObject = require("../../react/wrapper.js");
|
|
45
45
|
const external_constants_js_namespaceObject_1 = require("../constants.js");
|
|
46
46
|
const external_helmet_js_namespaceObject = require("../helmet.js");
|
|
47
|
+
const external_scriptOrder_js_namespaceObject = require("../scriptOrder.js");
|
|
47
48
|
const external_shared_js_namespaceObject = require("../shared.js");
|
|
48
49
|
const external_tracer_js_namespaceObject = require("../tracer.js");
|
|
49
50
|
const external_utils_js_namespaceObject = require("../utils.js");
|
|
@@ -96,10 +97,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
96
97
|
const rootElement = (0, wrapper_js_namespaceObject.wrapRuntimeContextProvider)(serverRoot, Object.assign(runtimeContext, {
|
|
97
98
|
ssr: true
|
|
98
99
|
}));
|
|
99
|
-
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
|
|
100
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, entryName, tracer);
|
|
100
101
|
return html;
|
|
101
102
|
};
|
|
102
|
-
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
|
|
103
|
+
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, entryName, { onError, onTiming }) {
|
|
103
104
|
let html = '';
|
|
104
105
|
let helmetData;
|
|
105
106
|
const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
|
|
@@ -120,7 +121,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
120
121
|
const { ssrScripts, cssChunk, jsChunk } = chunkSet;
|
|
121
122
|
const finalHtml = await (0, external_shared_js_namespaceObject.buildHtml)(htmlTemplate, [
|
|
122
123
|
createReplaceHtml(html),
|
|
123
|
-
createReplaceChunkJs(jsChunk),
|
|
124
|
+
createReplaceChunkJs(jsChunk, entryName),
|
|
124
125
|
createReplaceChunkCss(cssChunk),
|
|
125
126
|
createReplaceSSRDataScript(ssrScripts),
|
|
126
127
|
(0, external_helmet_js_namespaceObject.createReplaceHelemt)(helmetData),
|
|
@@ -134,8 +135,14 @@ function createReplaceHtml(html) {
|
|
|
134
135
|
function createReplaceSSRDataScript(data) {
|
|
135
136
|
return (template)=>(0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject_1.SSR_DATA_PLACEHOLDER, data);
|
|
136
137
|
}
|
|
137
|
-
function createReplaceChunkJs(js) {
|
|
138
|
-
return (template)=>
|
|
138
|
+
function createReplaceChunkJs(js, entryName) {
|
|
139
|
+
return (template)=>{
|
|
140
|
+
if (!js) return (0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject_1.CHUNK_JS_PLACEHOLDER, '');
|
|
141
|
+
const withoutPlaceholder = (0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject_1.CHUNK_JS_PLACEHOLDER, '');
|
|
142
|
+
const withEarlyScripts = (0, external_scriptOrder_js_namespaceObject.injectBeforeHydrationEntryScript)(withoutPlaceholder, js, entryName);
|
|
143
|
+
if (withEarlyScripts !== withoutPlaceholder) return withEarlyScripts;
|
|
144
|
+
return (0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject_1.CHUNK_JS_PLACEHOLDER, js);
|
|
145
|
+
};
|
|
139
146
|
}
|
|
140
147
|
function createReplaceChunkCss(css) {
|
|
141
148
|
return (template)=>(0, external_utils_js_namespaceObject.safeReplace)(template, external_constants_js_namespaceObject_1.CHUNK_CSS_PLACEHOLDER, css);
|
|
@@ -24,7 +24,8 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
LoadableCollector: ()=>LoadableCollector
|
|
27
|
+
LoadableCollector: ()=>LoadableCollector,
|
|
28
|
+
orderHydrationScriptChunks: ()=>orderHydrationScriptChunks
|
|
28
29
|
});
|
|
29
30
|
const server_namespaceObject = require("@loadable/server");
|
|
30
31
|
const lifecycle_js_namespaceObject = require("../../../router/runtime/lifecycle.js");
|
|
@@ -35,6 +36,33 @@ const extname = (uri)=>{
|
|
|
35
36
|
return `.${uri?.split('.').pop()}` || '';
|
|
36
37
|
};
|
|
37
38
|
const generateChunks = (chunks, ext)=>chunks.filter((chunk)=>Boolean(chunk.url)).filter((chunk)=>extname(chunk.url).slice(1) === ext);
|
|
39
|
+
const dedupeChunksByUrl = (chunks)=>{
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
return chunks.filter((chunk)=>{
|
|
42
|
+
if (!chunk.url || seen.has(chunk.url)) return false;
|
|
43
|
+
seen.add(chunk.url);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const isAsyncEntryScriptChunk = (chunk, entryName)=>{
|
|
48
|
+
if (!chunk.url?.endsWith('.js')) return false;
|
|
49
|
+
const asyncEntryName = `async-${entryName}`;
|
|
50
|
+
const filename = chunk.filename || chunk.url;
|
|
51
|
+
const basename = filename.split('/').pop() || filename;
|
|
52
|
+
return basename === `${asyncEntryName}.js` || basename.startsWith(`${asyncEntryName}.`) || basename.startsWith(`${asyncEntryName}-`);
|
|
53
|
+
};
|
|
54
|
+
const orderHydrationScriptChunks = ({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName })=>{
|
|
55
|
+
const asyncEntryScriptChunks = [];
|
|
56
|
+
const asyncEntryDependencyChunks = [];
|
|
57
|
+
for (const chunk of asyncEntryChunks)if (isAsyncEntryScriptChunk(chunk, entryName)) asyncEntryScriptChunks.push(chunk);
|
|
58
|
+
else asyncEntryDependencyChunks.push(chunk);
|
|
59
|
+
return dedupeChunksByUrl([
|
|
60
|
+
...asyncEntryDependencyChunks,
|
|
61
|
+
...collectedChunks,
|
|
62
|
+
...matchedRouteChunks,
|
|
63
|
+
...asyncEntryScriptChunks
|
|
64
|
+
]);
|
|
65
|
+
};
|
|
38
66
|
const checkIsInline = (chunk, enableInline)=>{
|
|
39
67
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
40
68
|
if (enableInline instanceof RegExp) return enableInline.test(chunk.url);
|
|
@@ -85,8 +113,16 @@ class LoadableCollector {
|
|
|
85
113
|
`async-${entryName}`
|
|
86
114
|
]));
|
|
87
115
|
} catch (e) {}
|
|
88
|
-
const
|
|
89
|
-
const
|
|
116
|
+
const collectedChunks = extractor ? extractor.getChunkAssets(extractor.chunks) : [];
|
|
117
|
+
const matchedRouteChunks = this.getMatchedRouteChunks();
|
|
118
|
+
const orderedScriptChunks = orderHydrationScriptChunks({
|
|
119
|
+
asyncEntryChunks: asyncChunks,
|
|
120
|
+
collectedChunks,
|
|
121
|
+
matchedRouteChunks,
|
|
122
|
+
entryName
|
|
123
|
+
});
|
|
124
|
+
const chunks = [].concat(asyncChunks).concat(collectedChunks).concat(matchedRouteChunks);
|
|
125
|
+
const scriptChunks = generateChunks(orderedScriptChunks, 'js');
|
|
90
126
|
const styleChunks = generateChunks(chunks, 'css');
|
|
91
127
|
if (extractor) {
|
|
92
128
|
this.emitLoadableScripts(extractor);
|
|
@@ -164,8 +200,10 @@ class LoadableCollector {
|
|
|
164
200
|
}
|
|
165
201
|
}
|
|
166
202
|
exports.LoadableCollector = __webpack_exports__.LoadableCollector;
|
|
203
|
+
exports.orderHydrationScriptChunks = __webpack_exports__.orderHydrationScriptChunks;
|
|
167
204
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
168
|
-
"LoadableCollector"
|
|
205
|
+
"LoadableCollector",
|
|
206
|
+
"orderHydrationScriptChunks"
|
|
169
207
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
170
208
|
Object.defineProperty(exports, '__esModule', {
|
|
171
209
|
value: true
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
wrapTanstackSsrHydrationBoundary: ()=>wrapTanstackSsrHydrationBoundary
|
|
28
|
+
});
|
|
29
|
+
const jsx_runtime_namespaceObject = require("react/jsx-runtime");
|
|
30
|
+
const external_react_namespaceObject = require("react");
|
|
31
|
+
function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
|
|
32
|
+
return shouldWrap ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_react_namespaceObject.Suspense, {
|
|
33
|
+
fallback: null,
|
|
34
|
+
children: routerContent
|
|
35
|
+
}) : routerContent;
|
|
36
|
+
}
|
|
37
|
+
exports.wrapTanstackSsrHydrationBoundary = __webpack_exports__.wrapTanstackSsrHydrationBoundary;
|
|
38
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
39
|
+
"wrapTanstackSsrHydrationBoundary"
|
|
40
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
41
|
+
Object.defineProperty(exports, '__esModule', {
|
|
42
|
+
value: true
|
|
43
|
+
});
|
|
@@ -37,6 +37,7 @@ const external_hooks_js_namespaceObject = require("../hooks.js");
|
|
|
37
37
|
const external_lifecycle_js_namespaceObject = require("../lifecycle.js");
|
|
38
38
|
const external_utils_js_namespaceObject = require("../utils.js");
|
|
39
39
|
const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
|
|
40
|
+
const external_hydrationBoundary_js_namespaceObject = require("./hydrationBoundary.js");
|
|
40
41
|
const external_prefetchLink_js_namespaceObject = require("./prefetchLink.js");
|
|
41
42
|
const external_routeTree_js_namespaceObject = require("./routeTree.js");
|
|
42
43
|
const client_js_namespaceObject = require("./rsc/client.js");
|
|
@@ -215,6 +216,7 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
215
216
|
}) : /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(react_router_namespaceObject.RouterProvider, {
|
|
216
217
|
router: router
|
|
217
218
|
});
|
|
219
|
+
const HydratableRouterContent = (0, external_hydrationBoundary_js_namespaceObject.wrapTanstackSsrHydrationBoundary)(RouterContent, hasSSRBootstrap);
|
|
218
220
|
if (hasSSRBootstrap) hooks.onAfterHydrateRouter.call({
|
|
219
221
|
...lifecycleContext,
|
|
220
222
|
phase: 'hydrate',
|
|
@@ -222,8 +224,8 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
222
224
|
runtimeContext: runtimeState
|
|
223
225
|
});
|
|
224
226
|
return App ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(App, {
|
|
225
|
-
children:
|
|
226
|
-
}) :
|
|
227
|
+
children: HydratableRouterContent
|
|
228
|
+
}) : HydratableRouterContent;
|
|
227
229
|
};
|
|
228
230
|
return RouterWrapper;
|
|
229
231
|
});
|
|
@@ -38,6 +38,7 @@ const index_js_namespaceObject = require("../../../core/context/index.js");
|
|
|
38
38
|
const external_lifecycle_js_namespaceObject = require("../lifecycle.js");
|
|
39
39
|
const external_utils_js_namespaceObject = require("../utils.js");
|
|
40
40
|
const external_basepathRewrite_js_namespaceObject = require("./basepathRewrite.js");
|
|
41
|
+
const external_hydrationBoundary_js_namespaceObject = require("./hydrationBoundary.js");
|
|
41
42
|
const external_routeTree_js_namespaceObject = require("./routeTree.js");
|
|
42
43
|
function isPreloadableRouteComponent(component) {
|
|
43
44
|
if (!component || 'function' != typeof component) return false;
|
|
@@ -247,9 +248,9 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
247
248
|
if (!router) return App ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(App, {
|
|
248
249
|
...props
|
|
249
250
|
}) : null;
|
|
250
|
-
const routerWrapper = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(react_router_namespaceObject.RouterProvider, {
|
|
251
|
+
const routerWrapper = (0, external_hydrationBoundary_js_namespaceObject.wrapTanstackSsrHydrationBoundary)(/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(react_router_namespaceObject.RouterProvider, {
|
|
251
252
|
router: router
|
|
252
|
-
});
|
|
253
|
+
}), true);
|
|
253
254
|
return App ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(App, {
|
|
254
255
|
children: routerWrapper
|
|
255
256
|
}) : routerWrapper;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
function getScriptTags(template) {
|
|
2
|
+
const scriptRegExp = /<script\b[^>]*\bsrc=(["'])(.*?)\1[^>]*><\/script>/g;
|
|
3
|
+
return Array.from(template.matchAll(scriptRegExp)).map((match)=>({
|
|
4
|
+
index: match.index ?? 0,
|
|
5
|
+
tag: match[0],
|
|
6
|
+
src: match[2]
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
function getAssetBasename(src) {
|
|
10
|
+
const withoutQuery = src.split(/[?#]/)[0];
|
|
11
|
+
return withoutQuery.split('/').pop() || withoutQuery;
|
|
12
|
+
}
|
|
13
|
+
function isEntryScript(src, entryName, asyncEntry) {
|
|
14
|
+
const basename = getAssetBasename(src);
|
|
15
|
+
const prefix = asyncEntry ? `async-${entryName}` : entryName;
|
|
16
|
+
return basename === `${prefix}.js` || basename.startsWith(`${prefix}.`) || basename.startsWith(`${prefix}-`);
|
|
17
|
+
}
|
|
18
|
+
function injectBeforeHydrationEntryScript(template, scripts, entryName = 'index') {
|
|
19
|
+
if (!scripts) return template;
|
|
20
|
+
const scriptTags = getScriptTags(template);
|
|
21
|
+
const target = scriptTags.find((match)=>isEntryScript(match.src, entryName, false)) ?? scriptTags.find((match)=>isEntryScript(match.src, entryName, true));
|
|
22
|
+
if (!target) return template;
|
|
23
|
+
return `${template.slice(0, target.index)}${scripts}${template.slice(target.index)}`;
|
|
24
|
+
}
|
|
25
|
+
export { injectBeforeHydrationEntryScript };
|
|
@@ -2,6 +2,7 @@ import { serializeJson } from "@modern-js/runtime-utils/node";
|
|
|
2
2
|
import { getRouterHydrationScripts, getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
3
3
|
import { SSR_DATA_JSON_ID } from "../../constants.mjs";
|
|
4
4
|
import { SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
5
|
+
import { injectBeforeHydrationEntryScript } from "../scriptOrder.mjs";
|
|
5
6
|
import { buildHtml } from "../shared.mjs";
|
|
6
7
|
import { attributesToString, safeReplace } from "../utils.mjs";
|
|
7
8
|
function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
@@ -30,7 +31,11 @@ function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
|
30
31
|
const jsAssets = Array.from(new Set(assetEntries.flatMap((entry)=>(entry.assets ?? []).filter((asset)=>asset.endsWith('.js')))));
|
|
31
32
|
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
|
|
32
33
|
const jsChunkStr = jsAssets.filter((asset)=>!template.includes(asset)).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
|
|
33
|
-
if (jsChunkStr)
|
|
34
|
+
if (jsChunkStr) {
|
|
35
|
+
const withoutPlaceholder = safeReplace(template, '<!--<?- chunksMap.js ?>-->', '');
|
|
36
|
+
const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, jsChunkStr, entryName);
|
|
37
|
+
return withEarlyScripts !== withoutPlaceholder ? withEarlyScripts : safeReplace(template, '<!--<?- chunksMap.js ?>-->', jsChunkStr);
|
|
38
|
+
}
|
|
34
39
|
return template;
|
|
35
40
|
}
|
|
36
41
|
return buildHtml(afterAppTemplate, callbacks);
|
|
@@ -6,6 +6,7 @@ import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
|
6
6
|
import { wrapRuntimeContextProvider } from "../../react/wrapper.mjs";
|
|
7
7
|
import { CHUNK_CSS_PLACEHOLDER, CHUNK_JS_PLACEHOLDER, HTML_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
8
8
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
9
|
+
import { injectBeforeHydrationEntryScript } from "../scriptOrder.mjs";
|
|
9
10
|
import { buildHtml } from "../shared.mjs";
|
|
10
11
|
import { SSRErrors, SSRTimings } from "../tracer.mjs";
|
|
11
12
|
import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
|
|
@@ -58,10 +59,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
58
59
|
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext, {
|
|
59
60
|
ssr: true
|
|
60
61
|
}));
|
|
61
|
-
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
|
|
62
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, entryName, tracer);
|
|
62
63
|
return html;
|
|
63
64
|
};
|
|
64
|
-
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
|
|
65
|
+
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, entryName, { onError, onTiming }) {
|
|
65
66
|
let html = '';
|
|
66
67
|
let helmetData;
|
|
67
68
|
const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
|
|
@@ -82,7 +83,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
82
83
|
const { ssrScripts, cssChunk, jsChunk } = chunkSet;
|
|
83
84
|
const finalHtml = await buildHtml(htmlTemplate, [
|
|
84
85
|
createReplaceHtml(html),
|
|
85
|
-
createReplaceChunkJs(jsChunk),
|
|
86
|
+
createReplaceChunkJs(jsChunk, entryName),
|
|
86
87
|
createReplaceChunkCss(cssChunk),
|
|
87
88
|
createReplaceSSRDataScript(ssrScripts),
|
|
88
89
|
createReplaceHelemt(helmetData),
|
|
@@ -96,8 +97,14 @@ function createReplaceHtml(html) {
|
|
|
96
97
|
function createReplaceSSRDataScript(data) {
|
|
97
98
|
return (template)=>safeReplace(template, SSR_DATA_PLACEHOLDER, data);
|
|
98
99
|
}
|
|
99
|
-
function createReplaceChunkJs(js) {
|
|
100
|
-
return (template)=>
|
|
100
|
+
function createReplaceChunkJs(js, entryName) {
|
|
101
|
+
return (template)=>{
|
|
102
|
+
if (!js) return safeReplace(template, CHUNK_JS_PLACEHOLDER, '');
|
|
103
|
+
const withoutPlaceholder = safeReplace(template, CHUNK_JS_PLACEHOLDER, '');
|
|
104
|
+
const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, js, entryName);
|
|
105
|
+
if (withEarlyScripts !== withoutPlaceholder) return withEarlyScripts;
|
|
106
|
+
return safeReplace(template, CHUNK_JS_PLACEHOLDER, js);
|
|
107
|
+
};
|
|
101
108
|
}
|
|
102
109
|
function createReplaceChunkCss(css) {
|
|
103
110
|
return (template)=>safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
@@ -7,6 +7,33 @@ const extname = (uri)=>{
|
|
|
7
7
|
return `.${uri?.split('.').pop()}` || '';
|
|
8
8
|
};
|
|
9
9
|
const generateChunks = (chunks, ext)=>chunks.filter((chunk)=>Boolean(chunk.url)).filter((chunk)=>extname(chunk.url).slice(1) === ext);
|
|
10
|
+
const dedupeChunksByUrl = (chunks)=>{
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
return chunks.filter((chunk)=>{
|
|
13
|
+
if (!chunk.url || seen.has(chunk.url)) return false;
|
|
14
|
+
seen.add(chunk.url);
|
|
15
|
+
return true;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
const isAsyncEntryScriptChunk = (chunk, entryName)=>{
|
|
19
|
+
if (!chunk.url?.endsWith('.js')) return false;
|
|
20
|
+
const asyncEntryName = `async-${entryName}`;
|
|
21
|
+
const filename = chunk.filename || chunk.url;
|
|
22
|
+
const basename = filename.split('/').pop() || filename;
|
|
23
|
+
return basename === `${asyncEntryName}.js` || basename.startsWith(`${asyncEntryName}.`) || basename.startsWith(`${asyncEntryName}-`);
|
|
24
|
+
};
|
|
25
|
+
const orderHydrationScriptChunks = ({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName })=>{
|
|
26
|
+
const asyncEntryScriptChunks = [];
|
|
27
|
+
const asyncEntryDependencyChunks = [];
|
|
28
|
+
for (const chunk of asyncEntryChunks)if (isAsyncEntryScriptChunk(chunk, entryName)) asyncEntryScriptChunks.push(chunk);
|
|
29
|
+
else asyncEntryDependencyChunks.push(chunk);
|
|
30
|
+
return dedupeChunksByUrl([
|
|
31
|
+
...asyncEntryDependencyChunks,
|
|
32
|
+
...collectedChunks,
|
|
33
|
+
...matchedRouteChunks,
|
|
34
|
+
...asyncEntryScriptChunks
|
|
35
|
+
]);
|
|
36
|
+
};
|
|
10
37
|
const checkIsInline = (chunk, enableInline)=>{
|
|
11
38
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
12
39
|
if (enableInline instanceof RegExp) return enableInline.test(chunk.url);
|
|
@@ -57,8 +84,16 @@ class LoadableCollector {
|
|
|
57
84
|
`async-${entryName}`
|
|
58
85
|
]));
|
|
59
86
|
} catch (e) {}
|
|
60
|
-
const
|
|
61
|
-
const
|
|
87
|
+
const collectedChunks = extractor ? extractor.getChunkAssets(extractor.chunks) : [];
|
|
88
|
+
const matchedRouteChunks = this.getMatchedRouteChunks();
|
|
89
|
+
const orderedScriptChunks = orderHydrationScriptChunks({
|
|
90
|
+
asyncEntryChunks: asyncChunks,
|
|
91
|
+
collectedChunks,
|
|
92
|
+
matchedRouteChunks,
|
|
93
|
+
entryName
|
|
94
|
+
});
|
|
95
|
+
const chunks = [].concat(asyncChunks).concat(collectedChunks).concat(matchedRouteChunks);
|
|
96
|
+
const scriptChunks = generateChunks(orderedScriptChunks, 'js');
|
|
62
97
|
const styleChunks = generateChunks(chunks, 'css');
|
|
63
98
|
if (extractor) {
|
|
64
99
|
this.emitLoadableScripts(extractor);
|
|
@@ -135,4 +170,4 @@ class LoadableCollector {
|
|
|
135
170
|
this.options = options;
|
|
136
171
|
}
|
|
137
172
|
}
|
|
138
|
-
export { LoadableCollector };
|
|
173
|
+
export { LoadableCollector, orderHydrationScriptChunks };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense } from "react";
|
|
3
|
+
function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
|
|
4
|
+
return shouldWrap ? /*#__PURE__*/ jsx(Suspense, {
|
|
5
|
+
fallback: null,
|
|
6
|
+
children: routerContent
|
|
7
|
+
}) : routerContent;
|
|
8
|
+
}
|
|
9
|
+
export { wrapTanstackSsrHydrationBoundary };
|
|
@@ -9,6 +9,7 @@ import { onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBefo
|
|
|
9
9
|
import { applyRouterRuntimeState } from "../lifecycle.mjs";
|
|
10
10
|
import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
|
|
11
11
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
12
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
12
13
|
import { Link } from "./prefetchLink.mjs";
|
|
13
14
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
14
15
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
@@ -187,6 +188,7 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
187
188
|
}) : /*#__PURE__*/ jsx(RouterProvider, {
|
|
188
189
|
router: router
|
|
189
190
|
});
|
|
191
|
+
const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(RouterContent, hasSSRBootstrap);
|
|
190
192
|
if (hasSSRBootstrap) hooks.onAfterHydrateRouter.call({
|
|
191
193
|
...lifecycleContext,
|
|
192
194
|
phase: 'hydrate',
|
|
@@ -194,8 +196,8 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
194
196
|
runtimeContext: runtimeState
|
|
195
197
|
});
|
|
196
198
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
197
|
-
children:
|
|
198
|
-
}) :
|
|
199
|
+
children: HydratableRouterContent
|
|
200
|
+
}) : HydratableRouterContent;
|
|
199
201
|
};
|
|
200
202
|
return RouterWrapper;
|
|
201
203
|
});
|
|
@@ -10,6 +10,7 @@ import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlob
|
|
|
10
10
|
import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "../lifecycle.mjs";
|
|
11
11
|
import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
|
|
12
12
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
13
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
13
14
|
import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
|
|
14
15
|
function isPreloadableRouteComponent(component) {
|
|
15
16
|
if (!component || 'function' != typeof component) return false;
|
|
@@ -219,9 +220,9 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
219
220
|
if (!router) return App ? /*#__PURE__*/ jsx(App, {
|
|
220
221
|
...props
|
|
221
222
|
}) : null;
|
|
222
|
-
const routerWrapper = /*#__PURE__*/ jsx(RouterProvider, {
|
|
223
|
+
const routerWrapper = wrapTanstackSsrHydrationBoundary(/*#__PURE__*/ jsx(RouterProvider, {
|
|
223
224
|
router: router
|
|
224
|
-
});
|
|
225
|
+
}), true);
|
|
225
226
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
226
227
|
children: routerWrapper
|
|
227
228
|
}) : routerWrapper;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
function getScriptTags(template) {
|
|
3
|
+
const scriptRegExp = /<script\b[^>]*\bsrc=(["'])(.*?)\1[^>]*><\/script>/g;
|
|
4
|
+
return Array.from(template.matchAll(scriptRegExp)).map((match)=>({
|
|
5
|
+
index: match.index ?? 0,
|
|
6
|
+
tag: match[0],
|
|
7
|
+
src: match[2]
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
function getAssetBasename(src) {
|
|
11
|
+
const withoutQuery = src.split(/[?#]/)[0];
|
|
12
|
+
return withoutQuery.split('/').pop() || withoutQuery;
|
|
13
|
+
}
|
|
14
|
+
function isEntryScript(src, entryName, asyncEntry) {
|
|
15
|
+
const basename = getAssetBasename(src);
|
|
16
|
+
const prefix = asyncEntry ? `async-${entryName}` : entryName;
|
|
17
|
+
return basename === `${prefix}.js` || basename.startsWith(`${prefix}.`) || basename.startsWith(`${prefix}-`);
|
|
18
|
+
}
|
|
19
|
+
function injectBeforeHydrationEntryScript(template, scripts, entryName = 'index') {
|
|
20
|
+
if (!scripts) return template;
|
|
21
|
+
const scriptTags = getScriptTags(template);
|
|
22
|
+
const target = scriptTags.find((match)=>isEntryScript(match.src, entryName, false)) ?? scriptTags.find((match)=>isEntryScript(match.src, entryName, true));
|
|
23
|
+
if (!target) return template;
|
|
24
|
+
return `${template.slice(0, target.index)}${scripts}${template.slice(target.index)}`;
|
|
25
|
+
}
|
|
26
|
+
export { injectBeforeHydrationEntryScript };
|
|
@@ -3,6 +3,7 @@ import { serializeJson } from "@modern-js/runtime-utils/node";
|
|
|
3
3
|
import { getRouterHydrationScripts, getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
4
4
|
import { SSR_DATA_JSON_ID } from "../../constants.mjs";
|
|
5
5
|
import { SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
6
|
+
import { injectBeforeHydrationEntryScript } from "../scriptOrder.mjs";
|
|
6
7
|
import { buildHtml } from "../shared.mjs";
|
|
7
8
|
import { attributesToString, safeReplace } from "../utils.mjs";
|
|
8
9
|
function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
@@ -31,7 +32,11 @@ function buildShellAfterTemplate(afterAppTemplate, options) {
|
|
|
31
32
|
const jsAssets = Array.from(new Set(assetEntries.flatMap((entry)=>(entry.assets ?? []).filter((asset)=>asset.endsWith('.js')))));
|
|
32
33
|
const nonceAttr = nonce ? ` nonce="${nonce}"` : '';
|
|
33
34
|
const jsChunkStr = jsAssets.filter((asset)=>!template.includes(asset)).map((asset)=>`<script src=${asset}${nonceAttr}></script>`).join(' ');
|
|
34
|
-
if (jsChunkStr)
|
|
35
|
+
if (jsChunkStr) {
|
|
36
|
+
const withoutPlaceholder = safeReplace(template, '<!--<?- chunksMap.js ?>-->', '');
|
|
37
|
+
const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, jsChunkStr, entryName);
|
|
38
|
+
return withEarlyScripts !== withoutPlaceholder ? withEarlyScripts : safeReplace(template, '<!--<?- chunksMap.js ?>-->', jsChunkStr);
|
|
39
|
+
}
|
|
35
40
|
return template;
|
|
36
41
|
}
|
|
37
42
|
return buildHtml(afterAppTemplate, callbacks);
|
|
@@ -7,6 +7,7 @@ import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
|
7
7
|
import { wrapRuntimeContextProvider } from "../../react/wrapper.mjs";
|
|
8
8
|
import { CHUNK_CSS_PLACEHOLDER, CHUNK_JS_PLACEHOLDER, HTML_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
9
9
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
10
|
+
import { injectBeforeHydrationEntryScript } from "../scriptOrder.mjs";
|
|
10
11
|
import { buildHtml } from "../shared.mjs";
|
|
11
12
|
import { SSRErrors, SSRTimings } from "../tracer.mjs";
|
|
12
13
|
import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
|
|
@@ -59,10 +60,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
59
60
|
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext, {
|
|
60
61
|
ssr: true
|
|
61
62
|
}));
|
|
62
|
-
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
|
|
63
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, entryName, tracer);
|
|
63
64
|
return html;
|
|
64
65
|
};
|
|
65
|
-
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
|
|
66
|
+
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, entryName, { onError, onTiming }) {
|
|
66
67
|
let html = '';
|
|
67
68
|
let helmetData;
|
|
68
69
|
const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
|
|
@@ -83,7 +84,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
83
84
|
const { ssrScripts, cssChunk, jsChunk } = chunkSet;
|
|
84
85
|
const finalHtml = await buildHtml(htmlTemplate, [
|
|
85
86
|
createReplaceHtml(html),
|
|
86
|
-
createReplaceChunkJs(jsChunk),
|
|
87
|
+
createReplaceChunkJs(jsChunk, entryName),
|
|
87
88
|
createReplaceChunkCss(cssChunk),
|
|
88
89
|
createReplaceSSRDataScript(ssrScripts),
|
|
89
90
|
createReplaceHelemt(helmetData),
|
|
@@ -97,8 +98,14 @@ function createReplaceHtml(html) {
|
|
|
97
98
|
function createReplaceSSRDataScript(data) {
|
|
98
99
|
return (template)=>safeReplace(template, SSR_DATA_PLACEHOLDER, data);
|
|
99
100
|
}
|
|
100
|
-
function createReplaceChunkJs(js) {
|
|
101
|
-
return (template)=>
|
|
101
|
+
function createReplaceChunkJs(js, entryName) {
|
|
102
|
+
return (template)=>{
|
|
103
|
+
if (!js) return safeReplace(template, CHUNK_JS_PLACEHOLDER, '');
|
|
104
|
+
const withoutPlaceholder = safeReplace(template, CHUNK_JS_PLACEHOLDER, '');
|
|
105
|
+
const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, js, entryName);
|
|
106
|
+
if (withEarlyScripts !== withoutPlaceholder) return withEarlyScripts;
|
|
107
|
+
return safeReplace(template, CHUNK_JS_PLACEHOLDER, js);
|
|
108
|
+
};
|
|
102
109
|
}
|
|
103
110
|
function createReplaceChunkCss(css) {
|
|
104
111
|
return (template)=>safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
@@ -11,6 +11,33 @@ const extname = (uri)=>{
|
|
|
11
11
|
return `.${uri?.split('.').pop()}` || '';
|
|
12
12
|
};
|
|
13
13
|
const generateChunks = (chunks, ext)=>chunks.filter((chunk)=>Boolean(chunk.url)).filter((chunk)=>extname(chunk.url).slice(1) === ext);
|
|
14
|
+
const dedupeChunksByUrl = (chunks)=>{
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
return chunks.filter((chunk)=>{
|
|
17
|
+
if (!chunk.url || seen.has(chunk.url)) return false;
|
|
18
|
+
seen.add(chunk.url);
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const isAsyncEntryScriptChunk = (chunk, entryName)=>{
|
|
23
|
+
if (!chunk.url?.endsWith('.js')) return false;
|
|
24
|
+
const asyncEntryName = `async-${entryName}`;
|
|
25
|
+
const filename = chunk.filename || chunk.url;
|
|
26
|
+
const basename = filename.split('/').pop() || filename;
|
|
27
|
+
return basename === `${asyncEntryName}.js` || basename.startsWith(`${asyncEntryName}.`) || basename.startsWith(`${asyncEntryName}-`);
|
|
28
|
+
};
|
|
29
|
+
const orderHydrationScriptChunks = ({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName })=>{
|
|
30
|
+
const asyncEntryScriptChunks = [];
|
|
31
|
+
const asyncEntryDependencyChunks = [];
|
|
32
|
+
for (const chunk of asyncEntryChunks)if (isAsyncEntryScriptChunk(chunk, entryName)) asyncEntryScriptChunks.push(chunk);
|
|
33
|
+
else asyncEntryDependencyChunks.push(chunk);
|
|
34
|
+
return dedupeChunksByUrl([
|
|
35
|
+
...asyncEntryDependencyChunks,
|
|
36
|
+
...collectedChunks,
|
|
37
|
+
...matchedRouteChunks,
|
|
38
|
+
...asyncEntryScriptChunks
|
|
39
|
+
]);
|
|
40
|
+
};
|
|
14
41
|
const checkIsInline = (chunk, enableInline)=>{
|
|
15
42
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
16
43
|
if (enableInline instanceof RegExp) return enableInline.test(chunk.url);
|
|
@@ -61,8 +88,16 @@ class LoadableCollector {
|
|
|
61
88
|
`async-${entryName}`
|
|
62
89
|
]));
|
|
63
90
|
} catch (e) {}
|
|
64
|
-
const
|
|
65
|
-
const
|
|
91
|
+
const collectedChunks = extractor ? extractor.getChunkAssets(extractor.chunks) : [];
|
|
92
|
+
const matchedRouteChunks = this.getMatchedRouteChunks();
|
|
93
|
+
const orderedScriptChunks = orderHydrationScriptChunks({
|
|
94
|
+
asyncEntryChunks: asyncChunks,
|
|
95
|
+
collectedChunks,
|
|
96
|
+
matchedRouteChunks,
|
|
97
|
+
entryName
|
|
98
|
+
});
|
|
99
|
+
const chunks = [].concat(asyncChunks).concat(collectedChunks).concat(matchedRouteChunks);
|
|
100
|
+
const scriptChunks = generateChunks(orderedScriptChunks, 'js');
|
|
66
101
|
const styleChunks = generateChunks(chunks, 'css');
|
|
67
102
|
if (extractor) {
|
|
68
103
|
this.emitLoadableScripts(extractor);
|
|
@@ -139,4 +174,4 @@ class LoadableCollector {
|
|
|
139
174
|
this.options = options;
|
|
140
175
|
}
|
|
141
176
|
}
|
|
142
|
-
export { LoadableCollector };
|
|
177
|
+
export { LoadableCollector, orderHydrationScriptChunks };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Suspense } from "react";
|
|
4
|
+
function wrapTanstackSsrHydrationBoundary(routerContent, shouldWrap) {
|
|
5
|
+
return shouldWrap ? /*#__PURE__*/ jsx(Suspense, {
|
|
6
|
+
fallback: null,
|
|
7
|
+
children: routerContent
|
|
8
|
+
}) : routerContent;
|
|
9
|
+
}
|
|
10
|
+
export { wrapTanstackSsrHydrationBoundary };
|
|
@@ -10,6 +10,7 @@ import { onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBefo
|
|
|
10
10
|
import { applyRouterRuntimeState } from "../lifecycle.mjs";
|
|
11
11
|
import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
|
|
12
12
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
13
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
13
14
|
import { Link } from "./prefetchLink.mjs";
|
|
14
15
|
import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
|
|
15
16
|
import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
|
|
@@ -188,6 +189,7 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
188
189
|
}) : /*#__PURE__*/ jsx(RouterProvider, {
|
|
189
190
|
router: router
|
|
190
191
|
});
|
|
192
|
+
const HydratableRouterContent = wrapTanstackSsrHydrationBoundary(RouterContent, hasSSRBootstrap);
|
|
191
193
|
if (hasSSRBootstrap) hooks.onAfterHydrateRouter.call({
|
|
192
194
|
...lifecycleContext,
|
|
193
195
|
phase: 'hydrate',
|
|
@@ -195,8 +197,8 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
195
197
|
runtimeContext: runtimeState
|
|
196
198
|
});
|
|
197
199
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
198
|
-
children:
|
|
199
|
-
}) :
|
|
200
|
+
children: HydratableRouterContent
|
|
201
|
+
}) : HydratableRouterContent;
|
|
200
202
|
};
|
|
201
203
|
return RouterWrapper;
|
|
202
204
|
});
|
|
@@ -11,6 +11,7 @@ import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlob
|
|
|
11
11
|
import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "../lifecycle.mjs";
|
|
12
12
|
import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
|
|
13
13
|
import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
|
|
14
|
+
import { wrapTanstackSsrHydrationBoundary } from "./hydrationBoundary.mjs";
|
|
14
15
|
import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
|
|
15
16
|
function isPreloadableRouteComponent(component) {
|
|
16
17
|
if (!component || 'function' != typeof component) return false;
|
|
@@ -220,9 +221,9 @@ const tanstackRouterPlugin = (userConfig = {})=>({
|
|
|
220
221
|
if (!router) return App ? /*#__PURE__*/ jsx(App, {
|
|
221
222
|
...props
|
|
222
223
|
}) : null;
|
|
223
|
-
const routerWrapper = /*#__PURE__*/ jsx(RouterProvider, {
|
|
224
|
+
const routerWrapper = wrapTanstackSsrHydrationBoundary(/*#__PURE__*/ jsx(RouterProvider, {
|
|
224
225
|
router: router
|
|
225
|
-
});
|
|
226
|
+
}), true);
|
|
226
227
|
return App ? /*#__PURE__*/ jsx(App, {
|
|
227
228
|
children: routerWrapper
|
|
228
229
|
}) : routerWrapper;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function injectBeforeHydrationEntryScript(template: string, scripts: string, entryName?: string): string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Chunk } from '@loadable/server';
|
|
1
2
|
import type { ReactElement } from 'react';
|
|
2
3
|
import type { TInternalRuntimeContext } from '../../context';
|
|
3
4
|
import type { ChunkSet, Collector } from './types';
|
|
@@ -7,6 +8,12 @@ declare module '@loadable/server' {
|
|
|
7
8
|
getChunkAssets: (chunks: string[]) => Chunk[];
|
|
8
9
|
}
|
|
9
10
|
}
|
|
11
|
+
export declare const orderHydrationScriptChunks: ({ asyncEntryChunks, collectedChunks, matchedRouteChunks, entryName, }: {
|
|
12
|
+
asyncEntryChunks: Chunk[];
|
|
13
|
+
collectedChunks: Chunk[];
|
|
14
|
+
matchedRouteChunks: Chunk[];
|
|
15
|
+
entryName: string;
|
|
16
|
+
}) => Chunk[];
|
|
10
17
|
export interface LoadableCollectorOptions {
|
|
11
18
|
nonce?: string;
|
|
12
19
|
stats?: Record<string, any>;
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.2.0-ultramodern.
|
|
20
|
+
"version": "3.2.0-ultramodern.98",
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
@@ -233,12 +233,12 @@
|
|
|
233
233
|
"isbot": "5.1.40",
|
|
234
234
|
"react-helmet-async": "3.0.0",
|
|
235
235
|
"react-is": "^19.2.6",
|
|
236
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.
|
|
237
|
-
"@modern-js/
|
|
238
|
-
"@modern-js/render": "npm:@bleedingdev/modern-js-render@3.2.0-ultramodern.
|
|
239
|
-
"@modern-js/
|
|
240
|
-
"@modern-js/
|
|
241
|
-
"@modern-js/
|
|
236
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.98",
|
|
237
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.98",
|
|
238
|
+
"@modern-js/render": "npm:@bleedingdev/modern-js-render@3.2.0-ultramodern.98",
|
|
239
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.98",
|
|
240
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.98",
|
|
241
|
+
"@modern-js/plugin-data-loader": "npm:@bleedingdev/modern-js-plugin-data-loader@3.2.0-ultramodern.98"
|
|
242
242
|
},
|
|
243
243
|
"peerDependencies": {
|
|
244
244
|
"react": "^19.2.6",
|
|
@@ -258,7 +258,7 @@
|
|
|
258
258
|
"react": "^19.2.6",
|
|
259
259
|
"react-dom": "^19.2.6",
|
|
260
260
|
"@scripts/rstest-config": "2.66.0",
|
|
261
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.
|
|
261
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.98"
|
|
262
262
|
},
|
|
263
263
|
"sideEffects": false,
|
|
264
264
|
"publishConfig": {
|