@bleedingdev/modern-js-runtime 3.8.3-ultramodern.1 → 3.8.3-ultramodern.2
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/cli/ssr/loadable-bundler-plugin.js +1 -1
- package/dist/cjs/core/browser/hydrate.js +1 -11
- package/dist/cjs/core/context/extensions.js +7 -2
- package/dist/cjs/core/context/runtime.js +13 -2
- package/dist/cjs/core/react/wrapper.js +10 -8
- package/dist/cjs/core/server/federatedCss.js +7 -4
- package/dist/cjs/core/server/helmet.js +2 -0
- package/dist/cjs/core/server/requestResponse.js +35 -11
- package/dist/cjs/core/server/routerCleanup.js +10 -1
- package/dist/cjs/core/server/scriptOrder.js +5 -2
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +1 -1
- package/dist/cjs/core/server/stream/createReadableStream.js +18 -5
- package/dist/cjs/core/server/stream/createReadableStream.worker.js +10 -2
- package/dist/cjs/core/server/stream/deferredScript.js +2 -1
- package/dist/cjs/core/server/string/index.js +4 -0
- package/dist/cjs/core/server/string/loadable.js +11 -2
- package/dist/cjs/core/server/utils.js +12 -5
- package/dist/cjs/exports/head.js +7 -242
- package/dist/cjs/module-federation/last-known-good.js +35 -2
- package/dist/cjs/router/cli/nestedRoutesSpec.js +7 -38
- package/dist/cjs/router/runtime/PrefetchLink.js +55 -8
- package/dist/cjs/router/runtime/redirect.js +11 -1
- package/dist/cjs/router/runtime/utils.js +2 -0
- package/dist/esm/cli/ssr/loadable-bundler-plugin.mjs +1 -1
- package/dist/esm/core/browser/hydrate.mjs +1 -11
- package/dist/esm/core/context/extensions.mjs +7 -2
- package/dist/esm/core/context/runtime.mjs +13 -2
- package/dist/esm/core/react/wrapper.mjs +11 -9
- package/dist/esm/core/server/federatedCss.mjs +7 -4
- package/dist/esm/core/server/helmet.mjs +2 -0
- package/dist/esm/core/server/requestResponse.mjs +35 -11
- package/dist/esm/core/server/routerCleanup.mjs +10 -1
- package/dist/esm/core/server/scriptOrder.mjs +6 -3
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm/core/server/stream/createReadableStream.mjs +16 -3
- package/dist/esm/core/server/stream/createReadableStream.worker.mjs +10 -2
- package/dist/esm/core/server/stream/deferredScript.mjs +2 -1
- package/dist/esm/core/server/string/index.mjs +4 -0
- package/dist/esm/core/server/string/loadable.mjs +11 -2
- package/dist/esm/core/server/utils.mjs +8 -4
- package/dist/esm/exports/head.mjs +8 -237
- package/dist/esm/module-federation/last-known-good.mjs +35 -2
- package/dist/esm/router/cli/nestedRoutesSpec.mjs +7 -38
- package/dist/esm/router/runtime/PrefetchLink.mjs +28 -8
- package/dist/esm/router/runtime/redirect.mjs +11 -1
- package/dist/esm/router/runtime/utils.mjs +2 -0
- package/dist/esm/rslib-runtime.mjs +9 -0
- package/dist/esm-node/cli/ssr/loadable-bundler-plugin.mjs +1 -1
- package/dist/esm-node/core/browser/hydrate.mjs +1 -11
- package/dist/esm-node/core/context/extensions.mjs +7 -2
- package/dist/esm-node/core/context/runtime.mjs +13 -2
- package/dist/esm-node/core/react/wrapper.mjs +11 -9
- package/dist/esm-node/core/server/federatedCss.mjs +7 -4
- package/dist/esm-node/core/server/helmet.mjs +2 -0
- package/dist/esm-node/core/server/requestResponse.mjs +35 -11
- package/dist/esm-node/core/server/routerCleanup.mjs +10 -1
- package/dist/esm-node/core/server/scriptOrder.mjs +6 -3
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +16 -3
- package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +10 -2
- package/dist/esm-node/core/server/stream/deferredScript.mjs +2 -1
- package/dist/esm-node/core/server/string/index.mjs +4 -0
- package/dist/esm-node/core/server/string/loadable.mjs +11 -2
- package/dist/esm-node/core/server/utils.mjs +8 -4
- package/dist/esm-node/exports/head.mjs +8 -237
- package/dist/esm-node/module-federation/last-known-good.mjs +35 -2
- package/dist/esm-node/router/cli/nestedRoutesSpec.mjs +7 -38
- package/dist/esm-node/router/runtime/PrefetchLink.mjs +28 -8
- package/dist/esm-node/router/runtime/redirect.mjs +11 -1
- package/dist/esm-node/router/runtime/utils.mjs +2 -0
- package/dist/esm-node/rslib-runtime.mjs +10 -0
- package/dist/types/core/context/extensions.d.ts +2 -4
- package/dist/types/core/context/public.d.ts +4 -8
- package/dist/types/core/server/requestResponse.d.ts +1 -1
- package/dist/types/core/server/routerCleanup.d.ts +2 -0
- package/dist/types/core/server/string/loadable.d.ts +1 -0
- package/dist/types/core/server/utils.d.ts +1 -0
- package/dist/types/exports/head.d.ts +1 -2
- package/dist/types/module-federation/last-known-good.d.ts +0 -20
- package/package.json +10 -9
- package/rstest.config.mts +11 -4
|
@@ -20,6 +20,7 @@ function helmetReplace(content, helmetData) {
|
|
|
20
20
|
const htmlAttributes = helmetData.htmlAttributes.toString();
|
|
21
21
|
if (htmlAttributes) result = safeReplace(result, RE_HTML_ATTR, `<html ${htmlAttributes}>`);
|
|
22
22
|
const base = helmetData.base.toString();
|
|
23
|
+
const priority = helmetData.priority.toString();
|
|
23
24
|
const link = helmetData.link.toString();
|
|
24
25
|
const meta = helmetData.meta.toString();
|
|
25
26
|
const noscript = helmetData.noscript.toString();
|
|
@@ -31,6 +32,7 @@ function helmetReplace(content, helmetData) {
|
|
|
31
32
|
if (shouldReplaceTitle) result = safeReplace(result, RE_TITLE, title);
|
|
32
33
|
const helmetStr = [
|
|
33
34
|
base,
|
|
35
|
+
priority,
|
|
34
36
|
link,
|
|
35
37
|
meta,
|
|
36
38
|
noscript,
|
|
@@ -2,9 +2,15 @@ import "node:module";
|
|
|
2
2
|
import { getRouterServerSnapshot } from "../../router/runtime/lifecycle.mjs";
|
|
3
3
|
import { handleRSCRedirect } from "../../router/runtime/redirect.mjs";
|
|
4
4
|
import { SSRErrors } from "./tracer.mjs";
|
|
5
|
-
const isRedirectStatus = (status)=>status
|
|
5
|
+
const isRedirectStatus = (status)=>301 === status || 302 === status || 303 === status || 307 === status || 308 === status;
|
|
6
6
|
const isNullBodyStatus = (status)=>204 === status || 205 === status || 304 === status;
|
|
7
|
+
const getRedirectLocation = (headers)=>{
|
|
8
|
+
const location = headers.get('Location');
|
|
9
|
+
return location && URL.canParse(location, 'http://localhost') ? location : void 0;
|
|
10
|
+
};
|
|
7
11
|
const processRedirect = (headers, status, ctx)=>{
|
|
12
|
+
headers.delete('content-length');
|
|
13
|
+
headers.delete('transfer-encoding');
|
|
8
14
|
if (ctx.enableRsc && ctx.isRSCNavigation) return handleRSCRedirect(headers, ctx.basename, status);
|
|
9
15
|
return new Response(null, {
|
|
10
16
|
status,
|
|
@@ -21,20 +27,38 @@ const applyRouterSnapshotResult = (context, onError)=>{
|
|
|
21
27
|
const createLoaderRedirectResponse = (beforeRenderResult, redirectCtx)=>{
|
|
22
28
|
if (void 0 === beforeRenderResult || !isRedirectStatus(beforeRenderResult.status)) return;
|
|
23
29
|
if (beforeRenderResult.headers.has('X-Modernjs-Redirect')) return beforeRenderResult;
|
|
24
|
-
const redirectUrl = beforeRenderResult.headers
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}), beforeRenderResult.status, redirectCtx);
|
|
30
|
+
const redirectUrl = getRedirectLocation(beforeRenderResult.headers);
|
|
31
|
+
if (!redirectUrl) return;
|
|
32
|
+
return processRedirect(new Headers(beforeRenderResult.headers), beforeRenderResult.status, redirectCtx);
|
|
28
33
|
};
|
|
29
|
-
const finalizeRenderResponse = (response, responseProxy, redirectCtx, routerCleanup)=>{
|
|
30
|
-
|
|
34
|
+
const finalizeRenderResponse = async (response, responseProxy, redirectCtx, routerCleanup)=>{
|
|
35
|
+
const proxyHeaders = new Headers(responseProxy.headers);
|
|
36
|
+
if (-1 !== responseProxy.status && isRedirectStatus(responseProxy.status) && void 0 !== getRedirectLocation(proxyHeaders)) {
|
|
37
|
+
await routerCleanup.discardBody(response);
|
|
38
|
+
return processRedirect(proxyHeaders, responseProxy.status, redirectCtx);
|
|
39
|
+
}
|
|
40
|
+
const headers = new Headers(response.headers);
|
|
41
|
+
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
42
|
+
headers.set(key, value);
|
|
43
|
+
});
|
|
44
|
+
if (-1 !== responseProxy.status) {
|
|
45
|
+
if (isNullBodyStatus(responseProxy.status)) {
|
|
46
|
+
await routerCleanup.discardBody(response);
|
|
47
|
+
headers.delete('content-length');
|
|
48
|
+
headers.delete('transfer-encoding');
|
|
49
|
+
return new Response(null, {
|
|
50
|
+
status: responseProxy.status,
|
|
51
|
+
headers
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return routerCleanup.deferUntilBodyDone(new Response(response.body, {
|
|
55
|
+
status: responseProxy.status,
|
|
56
|
+
headers
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
31
59
|
Object.entries(responseProxy.headers).forEach(([key, value])=>{
|
|
32
60
|
response.headers.set(key, value);
|
|
33
61
|
});
|
|
34
|
-
if (-1 !== responseProxy.status) return routerCleanup.deferUntilBodyDone(new Response(isNullBodyStatus(responseProxy.status) ? null : response.body, {
|
|
35
|
-
status: responseProxy.status,
|
|
36
|
-
headers: response.headers
|
|
37
|
-
}));
|
|
38
62
|
return routerCleanup.deferUntilBodyDone(response);
|
|
39
63
|
};
|
|
40
64
|
export { applyRouterSnapshotResult, createLoaderRedirectResponse, finalizeRenderResponse };
|
|
@@ -63,12 +63,21 @@ function createRouterCleanup(runtimeContext, onError) {
|
|
|
63
63
|
headers: response.headers
|
|
64
64
|
});
|
|
65
65
|
};
|
|
66
|
+
const discardBody = async (response)=>{
|
|
67
|
+
const { body } = response;
|
|
68
|
+
if (!body) return void await run();
|
|
69
|
+
deferred = true;
|
|
70
|
+
if (body.locked) throw new TypeError('Cannot discard a locked response body before router cleanup');
|
|
71
|
+
await body.cancel('Response body discarded during finalization');
|
|
72
|
+
await run();
|
|
73
|
+
};
|
|
66
74
|
return {
|
|
67
75
|
get deferred () {
|
|
68
76
|
return deferred;
|
|
69
77
|
},
|
|
70
78
|
run,
|
|
71
|
-
deferUntilBodyDone
|
|
79
|
+
deferUntilBodyDone,
|
|
80
|
+
discardBody
|
|
72
81
|
};
|
|
73
82
|
}
|
|
74
83
|
export { ROUTER_CLEANUP_ERROR, createRouterCleanup, finishWithRouterCleanup, runWithRouterCleanupOnError };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { getRouterMatchedRouteIds } from "../../router/runtime/lifecycle.mjs";
|
|
3
3
|
import { CHUNK_JS_PLACEHOLDER } from "./constants.mjs";
|
|
4
|
-
import { safeReplace } from "./utils.mjs";
|
|
4
|
+
import { attributesToString, safeReplace } from "./utils.mjs";
|
|
5
5
|
function getScriptTags(template) {
|
|
6
6
|
const scriptRegExp = /<script\b[^>]*\bsrc=(["'])(.*?)\1[^>]*><\/script>/g;
|
|
7
7
|
return Array.from(template.matchAll(scriptRegExp)).map((match)=>({
|
|
@@ -67,6 +67,7 @@ function injectBeforeHydrationEntryScript(template, scripts, entryName = 'index'
|
|
|
67
67
|
return `${template.slice(0, target.index)}${scripts}${template.slice(target.index)}`;
|
|
68
68
|
}
|
|
69
69
|
function replaceChunkJsPlaceholder(template, scripts, entryName, placeholder = CHUNK_JS_PLACEHOLDER) {
|
|
70
|
+
if (!template.includes(placeholder)) return template;
|
|
70
71
|
if ('' === scripts) return safeReplace(template, placeholder, '');
|
|
71
72
|
const withoutPlaceholder = safeReplace(template, placeholder, '');
|
|
72
73
|
const withEarlyScripts = injectBeforeHydrationEntryScript(withoutPlaceholder, scripts, entryName);
|
|
@@ -82,7 +83,9 @@ function createRouteHydrationScriptTags(runtimeContext, entryName, options = {})
|
|
|
82
83
|
...getMatchedRouteAssets(runtimeContext),
|
|
83
84
|
...routeAssets[`async-${entryName}`]?.assets ?? []
|
|
84
85
|
].filter((asset)=>asset.endsWith('.js'))));
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
return jsAssets.filter((asset)=>!existingScriptSrcs.has(asset)).map((asset)=>`<script${attributesToString({
|
|
87
|
+
src: asset,
|
|
88
|
+
nonce: nonce || void 0
|
|
89
|
+
})}></script>`).join(' ');
|
|
87
90
|
}
|
|
88
91
|
export { createRouteHydrationScriptTags, getMatchedRouteChunks, injectBeforeHydrationEntryScript, orderHydrationScriptChunks, replaceChunkJsPlaceholder };
|
|
@@ -4,7 +4,7 @@ import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
|
4
4
|
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
5
5
|
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
6
6
|
import { buildHtml } from "../shared.mjs";
|
|
7
|
-
import { safeReplace } from "../utils.mjs";
|
|
7
|
+
import { hasStylesheetLink, safeReplace } from "../utils.mjs";
|
|
8
8
|
const checkIsInline = (chunk, enableInline)=>{
|
|
9
9
|
if ('production' !== process.env.NODE_ENV) return false;
|
|
10
10
|
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
@@ -37,7 +37,7 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
37
37
|
if (asyncEntry) matchedRouteManifests.push(asyncEntry);
|
|
38
38
|
const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
|
|
39
39
|
const { referenceCssAssets = [] } = routeManifest;
|
|
40
|
-
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template
|
|
40
|
+
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !hasStylesheetLink(template, asset));
|
|
41
41
|
return [
|
|
42
42
|
...chunks,
|
|
43
43
|
..._cssChunks
|
|
@@ -6,9 +6,11 @@ import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
|
|
|
6
6
|
import { RenderLevel } from "../../constants.mjs";
|
|
7
7
|
import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
8
8
|
import { getMonitors } from "../../context/monitors/index.mjs";
|
|
9
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
9
10
|
import { enqueueFromEntries } from "./deferredScript.mjs";
|
|
10
11
|
import { ShellChunkStatus, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
|
|
11
12
|
import { getTemplates } from "./template.mjs";
|
|
13
|
+
import * as __rspack_external__modern_js_runtime_extensions_node_f016b1ee from "@modern-js/runtime-extensions/node";
|
|
12
14
|
const defaultExtender = {
|
|
13
15
|
modifyRootElement: (rootElement)=>rootElement,
|
|
14
16
|
getStyleTags: ()=>'',
|
|
@@ -37,6 +39,8 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
37
39
|
});
|
|
38
40
|
const chunkVec = [];
|
|
39
41
|
let hasStartedPipe = false;
|
|
42
|
+
__rspack_external__modern_js_runtime_extensions_node_f016b1ee.beginHeadRender(runtimeContext);
|
|
43
|
+
const reportError = __rspack_external__modern_js_runtime_extensions_node_f016b1ee.createOnceErrorReporter(options.onError);
|
|
40
44
|
return new Promise((resolve)=>{
|
|
41
45
|
const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
|
|
42
46
|
nonce: config.nonce,
|
|
@@ -70,8 +74,10 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
70
74
|
if (-1 !== markerIndex) {
|
|
71
75
|
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
72
76
|
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
77
|
+
__rspack_external__modern_js_runtime_extensions_node_f016b1ee.publishHeadRender(runtimeContext);
|
|
78
|
+
const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
|
|
73
79
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
74
|
-
this.push(`${
|
|
80
|
+
this.push(`${completedShellBefore}${beforeMark}${shellAfter}`);
|
|
75
81
|
if (afterMark) this.push(afterMark);
|
|
76
82
|
if (pendingScripts.length > 0) for (const s of pendingScripts)this.push(s);
|
|
77
83
|
}
|
|
@@ -89,8 +95,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
89
95
|
extenders.forEach((extender)=>{
|
|
90
96
|
if (extender.processStream) processedStream = extender.processStream(processedStream);
|
|
91
97
|
});
|
|
98
|
+
__rspack_external__modern_js_runtime_extensions_node_f016b1ee.pipeNodeHeadStream({
|
|
99
|
+
source: processedStream,
|
|
100
|
+
destination: body,
|
|
101
|
+
context: runtimeContext,
|
|
102
|
+
terminalMarker: ESCAPED_SHELL_STREAM_END_MARK,
|
|
103
|
+
onError: reportError
|
|
104
|
+
});
|
|
92
105
|
reactStreamingPipe(passThrough);
|
|
93
|
-
processedStream.pipe(body);
|
|
94
106
|
try {
|
|
95
107
|
const storageContext = storage.useContext?.();
|
|
96
108
|
const activeDeferreds = storageContext?.activeDeferreds;
|
|
@@ -109,6 +121,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
109
121
|
});
|
|
110
122
|
},
|
|
111
123
|
onShellError (error) {
|
|
124
|
+
__rspack_external__modern_js_runtime_extensions_node_f016b1ee.abortHeadRender(runtimeContext);
|
|
112
125
|
renderLevel = RenderLevel.CLIENT_RENDER;
|
|
113
126
|
getTemplates(htmlTemplate, {
|
|
114
127
|
request,
|
|
@@ -127,7 +140,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
127
140
|
},
|
|
128
141
|
onError (error) {
|
|
129
142
|
renderLevel = RenderLevel.CLIENT_RENDER;
|
|
130
|
-
|
|
143
|
+
reportError(error);
|
|
131
144
|
}
|
|
132
145
|
});
|
|
133
146
|
});
|
|
@@ -3,13 +3,16 @@ import { renderSSRStream } from "@modern-js/render/ssr";
|
|
|
3
3
|
import { storage } from "@modern-js/runtime-utils/node";
|
|
4
4
|
import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common.mjs";
|
|
5
5
|
import { RenderLevel } from "../../constants.mjs";
|
|
6
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
6
7
|
import { enqueueFromEntries } from "./deferredScript.mjs";
|
|
7
8
|
import { ShellChunkStatus, encodeForWebStream, getReadableStreamFromString, resolveStreamingMode } from "./shared.mjs";
|
|
8
9
|
import { getTemplates } from "./template.mjs";
|
|
10
|
+
import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
|
|
9
11
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
10
12
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
11
13
|
const chunkVec = [];
|
|
12
14
|
const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, moduleFederationCssAssets, rscManifest, rscRoot } = options;
|
|
15
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.beginHeadRender(runtimeContext);
|
|
13
16
|
const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
|
|
14
17
|
renderLevel: RenderLevel.SERVER_RENDER,
|
|
15
18
|
runtimeContext,
|
|
@@ -37,7 +40,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
37
40
|
const forceStreamToString = Boolean("u" > typeof process && process.env?.MODERN_JS_STREAM_TO_STRING);
|
|
38
41
|
const { waitForAllReady } = resolveStreamingMode(request, forceStreamToString);
|
|
39
42
|
if (waitForAllReady) await readableOriginal.allReady;
|
|
40
|
-
const reader = readableOriginal.getReader();
|
|
43
|
+
const reader = __rspack_external__modern_js_runtime_extensions_ce611631.createConservingWebShellStream(readableOriginal, runtimeContext, ESCAPED_SHELL_STREAM_END_MARK, options.onError).getReader();
|
|
41
44
|
const stream = new ReadableStream({
|
|
42
45
|
start (controller) {
|
|
43
46
|
const pendingScripts = [];
|
|
@@ -82,14 +85,17 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
82
85
|
if (-1 !== markerIndex) {
|
|
83
86
|
const beforeMark = concatedChunk.slice(0, markerIndex);
|
|
84
87
|
const afterMark = concatedChunk.slice(markerIndex + ESCAPED_SHELL_STREAM_END_MARK.length);
|
|
88
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.publishHeadRender(runtimeContext);
|
|
89
|
+
const completedShellBefore = createReplaceHelemt(getHelmetData(runtimeContext))(shellBefore);
|
|
85
90
|
shellChunkStatus = ShellChunkStatus.FINISH;
|
|
86
|
-
safeEnqueue(encodeForWebStream(`${
|
|
91
|
+
safeEnqueue(encodeForWebStream(`${completedShellBefore}${beforeMark}${shellAfter}`));
|
|
87
92
|
if (afterMark.length > 0) safeEnqueue(encodeForWebStream(afterMark));
|
|
88
93
|
flushPendingScripts();
|
|
89
94
|
}
|
|
90
95
|
} else safeEnqueue(value);
|
|
91
96
|
if (!isClosed) push();
|
|
92
97
|
} catch (error) {
|
|
98
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
|
|
93
99
|
if (!isClosed) {
|
|
94
100
|
isClosed = true;
|
|
95
101
|
try {
|
|
@@ -101,11 +107,13 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
101
107
|
push();
|
|
102
108
|
},
|
|
103
109
|
cancel (reason) {
|
|
110
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
|
|
104
111
|
reader.cancel(reason).catch(()=>{});
|
|
105
112
|
}
|
|
106
113
|
});
|
|
107
114
|
return stream;
|
|
108
115
|
} catch (e) {
|
|
116
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
|
|
109
117
|
const fallbackHtml = `${shellBefore}${shellAfter}`;
|
|
110
118
|
const stream = getReadableStreamFromString(fallbackHtml);
|
|
111
119
|
return stream;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { runRouterDataFnStr } from "../../../router/runtime/constants.mjs";
|
|
3
|
+
import { shouldRedactServerError } from "../utils.mjs";
|
|
3
4
|
const HTML_ESCAPE_MAP = {
|
|
4
5
|
'&': '&',
|
|
5
6
|
'<': '<',
|
|
@@ -23,7 +24,7 @@ function isPromiseLike(value) {
|
|
|
23
24
|
return !!value && 'function' == typeof value.then;
|
|
24
25
|
}
|
|
25
26
|
function toErrorInfo(error) {
|
|
26
|
-
if (
|
|
27
|
+
if (shouldRedactServerError()) return {
|
|
27
28
|
message: 'Unexpected Server Error'
|
|
28
29
|
};
|
|
29
30
|
if (error && 'object' == typeof error) {
|
|
@@ -13,6 +13,7 @@ import { SSRErrors, SSRTimings } from "../tracer.mjs";
|
|
|
13
13
|
import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
|
|
14
14
|
import { LoadableCollector } from "./loadable.mjs";
|
|
15
15
|
import { SSRDataCollector } from "./ssrData.mjs";
|
|
16
|
+
import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
|
|
16
17
|
const renderString = async (request, serverRoot, options)=>{
|
|
17
18
|
const { resource, runtimeContext, config, onError, onTiming } = options;
|
|
18
19
|
const tracer = {
|
|
@@ -67,16 +68,19 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
67
68
|
let html = '';
|
|
68
69
|
let helmetData;
|
|
69
70
|
const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
|
|
71
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.beginHeadRender(runtimeContext);
|
|
70
72
|
try {
|
|
71
73
|
const end = time();
|
|
72
74
|
html = server.renderToString(finalApp, {
|
|
73
75
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
74
76
|
});
|
|
77
|
+
html = __rspack_external__modern_js_runtime_extensions_ce611631.completeHeadRender(runtimeContext, html);
|
|
75
78
|
chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
|
|
76
79
|
helmetData = getHelmetData(runtimeContext);
|
|
77
80
|
const cost = end();
|
|
78
81
|
onTiming(SSRTimings.RENDER_HTML, cost);
|
|
79
82
|
} catch (e) {
|
|
83
|
+
__rspack_external__modern_js_runtime_extensions_ce611631.abortHeadRender(runtimeContext);
|
|
80
84
|
chunkSet.renderLevel = RenderLevel.CLIENT_RENDER;
|
|
81
85
|
onError(e, SSRErrors.RENDER_HTML);
|
|
82
86
|
}
|
|
@@ -32,6 +32,10 @@ const readAsset = async (chunk)=>{
|
|
|
32
32
|
return fs.readFile(filepath, 'utf-8');
|
|
33
33
|
};
|
|
34
34
|
class LoadableCollector {
|
|
35
|
+
get existsAssets() {
|
|
36
|
+
const { routeManifest, entryName } = this.options;
|
|
37
|
+
return routeManifest?.routeAssets?.[entryName]?.assets;
|
|
38
|
+
}
|
|
35
39
|
collect(comopnent) {
|
|
36
40
|
const { stats, entryName } = this.options;
|
|
37
41
|
if (!stats) return comopnent;
|
|
@@ -93,7 +97,7 @@ class LoadableCollector {
|
|
|
93
97
|
const matchs = template.matchAll(jsScriptRegExp);
|
|
94
98
|
const existedScript = [];
|
|
95
99
|
for (const match of matchs)existedScript.push(match[1]);
|
|
96
|
-
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url)).map(async (chunk)=>{
|
|
100
|
+
const scripts = await Promise.all(chunks.filter((chunk)=>!existedScript.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
|
|
97
101
|
const script = `<script${attributes} src="${chunk.url}"></script>`;
|
|
98
102
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<script>${content}</script>`).catch((_)=>script);
|
|
99
103
|
return script;
|
|
@@ -104,7 +108,12 @@ class LoadableCollector {
|
|
|
104
108
|
const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
|
|
105
109
|
const { inlineStyles } = config;
|
|
106
110
|
const atrributes = attributesToString(this.generateAttributes());
|
|
107
|
-
const
|
|
111
|
+
const emittedAssetUrls = new Set();
|
|
112
|
+
const emittedChunks = chunks.filter((chunk)=>{
|
|
113
|
+
const shouldEmit = !hasStylesheetLink(template, chunk.url) && !this.existsAssets?.includes(chunk.path) && !emittedAssetUrls.has(chunk.url);
|
|
114
|
+
if (shouldEmit) emittedAssetUrls.add(chunk.url);
|
|
115
|
+
return shouldEmit;
|
|
116
|
+
});
|
|
108
117
|
const css = await Promise.all(emittedChunks.map(async (chunk)=>{
|
|
109
118
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
110
119
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
+
import { escapeHtmlAttribute, isSafeHtmlAttributeName } from "@modern-js/runtime-extensions";
|
|
2
3
|
import { isRouteErrorResponse } from "../../router/runtime/routerHelper.mjs";
|
|
3
4
|
function attributesToString(attributes) {
|
|
4
|
-
return Object.entries(attributes).reduce((str, [key, value])=>void 0
|
|
5
|
+
return Object.entries(attributes).reduce((str, [key, value])=>void 0 !== value && isSafeHtmlAttributeName(key) ? `${str} ${key}="${escapeHtmlAttribute(value)}"` : str, '');
|
|
5
6
|
}
|
|
6
7
|
function safeReplace(source, searchValue, replaceValue) {
|
|
7
8
|
return source.replace(searchValue, ()=>replaceValue);
|
|
@@ -39,12 +40,14 @@ function serializeRouteErrorResponse(error) {
|
|
|
39
40
|
status: error.status,
|
|
40
41
|
statusText: 'Internal Server Error',
|
|
41
42
|
data: 'Unexpected Server Error',
|
|
43
|
+
internal: error.internal,
|
|
42
44
|
__type: 'RouteErrorResponse'
|
|
43
45
|
};
|
|
44
46
|
return {
|
|
45
47
|
status: error.status,
|
|
46
48
|
statusText: error.statusText,
|
|
47
49
|
data: error.data,
|
|
50
|
+
internal: error.internal,
|
|
48
51
|
__type: 'RouteErrorResponse'
|
|
49
52
|
};
|
|
50
53
|
}
|
|
@@ -66,8 +69,9 @@ const getLinkAttributes = (linkTag)=>{
|
|
|
66
69
|
let match;
|
|
67
70
|
while(match = attributeRegExp.exec(linkTag)){
|
|
68
71
|
const [, name, doubleQuotedValue, singleQuotedValue, unquotedValue] = match;
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
const normalizedName = name.toLowerCase();
|
|
73
|
+
if ('link' === normalizedName || attributes.has(normalizedName)) continue;
|
|
74
|
+
attributes.set(normalizedName, doubleQuotedValue ?? singleQuotedValue ?? unquotedValue ?? '');
|
|
71
75
|
}
|
|
72
76
|
return attributes;
|
|
73
77
|
};
|
|
@@ -80,4 +84,4 @@ const hasStylesheetLink = (template, href)=>{
|
|
|
80
84
|
return linkHref === href && rel?.split(/\s+/).some((relToken)=>'stylesheet' === relToken.toLowerCase());
|
|
81
85
|
});
|
|
82
86
|
};
|
|
83
|
-
export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors };
|
|
87
|
+
export { attributesToString, checkIsNode, getSSRConfigByEntry, getSSRMode, hasStylesheetLink, safeReplace, serializeErrors, shouldRedactServerError };
|