@bleedingdev/modern-js-runtime 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90
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/boundary-debugger/index.js +299 -0
- package/dist/cjs/cli/ssr/index.js +5 -15
- package/dist/cjs/cli/template.server.js +1 -0
- package/dist/cjs/core/react/wrapper.js +9 -3
- package/dist/cjs/core/server/federatedCss.js +47 -0
- package/dist/cjs/core/server/helmet.js +8 -2
- package/dist/cjs/core/server/stream/afterTemplate.js +9 -6
- package/dist/cjs/core/server/stream/beforeTemplate.js +13 -20
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +98 -0
- package/dist/cjs/core/server/stream/createReadableStream.js +7 -2
- package/dist/cjs/core/server/stream/createReadableStream.worker.js +4 -2
- package/dist/cjs/core/server/stream/shared.js +3 -1
- package/dist/cjs/core/server/string/index.js +6 -6
- package/dist/cjs/core/server/string/loadable.js +33 -7
- package/dist/cjs/exports/head.js +196 -5
- package/dist/cjs/router/cli/code/tanstackTypes.js +116 -51
- package/dist/cjs/router/cli/code/templates.js +1 -8
- package/dist/cjs/router/runtime/tanstack/plugin.js +4 -5
- package/dist/cjs/router/runtime/tanstack/plugin.node.js +2 -13
- package/dist/cjs/router/runtime/tanstack/routeTree.js +47 -4
- package/dist/cjs/rsc/server.worker.js +58 -0
- package/dist/cjs/ssr/serverRender/renderToString/entry.js +9 -8
- package/dist/esm/boundary-debugger/index.mjs +263 -0
- package/dist/esm/cli/ssr/index.mjs +5 -15
- package/dist/esm/cli/template.server.mjs +1 -0
- package/dist/esm/core/react/wrapper.mjs +9 -3
- package/dist/esm/core/server/federatedCss.mjs +13 -0
- package/dist/esm/core/server/helmet.mjs +5 -2
- package/dist/esm/core/server/stream/afterTemplate.mjs +10 -7
- package/dist/esm/core/server/stream/beforeTemplate.mjs +14 -11
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +64 -0
- package/dist/esm/core/server/stream/createReadableStream.mjs +7 -2
- package/dist/esm/core/server/stream/createReadableStream.worker.mjs +4 -2
- package/dist/esm/core/server/stream/shared.mjs +3 -1
- package/dist/esm/core/server/string/index.mjs +7 -6
- package/dist/esm/core/server/string/loadable.mjs +33 -7
- package/dist/esm/exports/head.mjs +189 -4
- package/dist/esm/router/cli/code/tanstackTypes.mjs +116 -51
- package/dist/esm/router/cli/code/templates.mjs +1 -8
- package/dist/esm/router/runtime/tanstack/plugin.mjs +8 -9
- package/dist/esm/router/runtime/tanstack/plugin.node.mjs +3 -14
- package/dist/esm/router/runtime/tanstack/routeTree.mjs +47 -4
- package/dist/esm/rsc/server.worker.mjs +1 -0
- package/dist/esm/ssr/serverRender/renderToString/entry.mjs +9 -6
- package/dist/esm-node/boundary-debugger/index.mjs +264 -0
- package/dist/esm-node/cli/ssr/index.mjs +5 -15
- package/dist/esm-node/cli/template.server.mjs +1 -0
- package/dist/esm-node/core/react/wrapper.mjs +9 -3
- package/dist/esm-node/core/server/federatedCss.mjs +14 -0
- package/dist/esm-node/core/server/helmet.mjs +5 -2
- package/dist/esm-node/core/server/stream/afterTemplate.mjs +10 -7
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +14 -11
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +65 -0
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +7 -2
- package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +4 -2
- package/dist/esm-node/core/server/stream/shared.mjs +3 -1
- package/dist/esm-node/core/server/string/index.mjs +7 -6
- package/dist/esm-node/core/server/string/loadable.mjs +33 -7
- package/dist/esm-node/exports/head.mjs +189 -4
- package/dist/esm-node/router/cli/code/tanstackTypes.mjs +116 -51
- package/dist/esm-node/router/cli/code/templates.mjs +1 -8
- package/dist/esm-node/router/runtime/tanstack/plugin.mjs +8 -9
- package/dist/esm-node/router/runtime/tanstack/plugin.node.mjs +3 -14
- package/dist/esm-node/router/runtime/tanstack/routeTree.mjs +47 -4
- package/dist/esm-node/rsc/server.worker.mjs +2 -0
- package/dist/esm-node/ssr/serverRender/renderToString/entry.mjs +9 -6
- package/dist/types/boundary-debugger/index.d.ts +28 -0
- package/dist/types/core/context/runtime.d.ts +4 -0
- package/dist/types/core/server/federatedCss.d.ts +5 -0
- package/dist/types/core/server/helmet.d.ts +5 -3
- package/dist/types/core/server/stream/beforeTemplate.d.ts +1 -0
- package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +10 -0
- package/dist/types/core/server/stream/shared.d.ts +8 -0
- package/dist/types/core/server/string/loadable.d.ts +4 -0
- package/dist/types/exports/head.d.ts +10 -3
- package/dist/types/rsc/server.worker.d.ts +1 -0
- package/package.json +25 -19
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { matchRoutes } from "@modern-js/runtime-utils/router";
|
|
3
|
-
import react_helmet from "react-helmet";
|
|
4
3
|
import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
5
4
|
import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
6
|
-
import {
|
|
5
|
+
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
6
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
7
7
|
import { buildHtml } from "../shared.mjs";
|
|
8
8
|
import { checkIsNode, safeReplace } from "../utils.mjs";
|
|
9
9
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
@@ -21,8 +21,8 @@ const checkIsInline = (chunk, enableInline)=>{
|
|
|
21
21
|
return Boolean(enableInline);
|
|
22
22
|
};
|
|
23
23
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
24
|
-
const { config, runtimeContext, styledComponentsStyleTags, entryName } = options;
|
|
25
|
-
const helmetData =
|
|
24
|
+
const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
|
|
25
|
+
const helmetData = getHelmetData(runtimeContext);
|
|
26
26
|
const callbacks = [
|
|
27
27
|
createReplaceHelemt(helmetData),
|
|
28
28
|
(template)=>injectCss(template, entryName, styledComponentsStyleTags)
|
|
@@ -31,33 +31,36 @@ async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
|
31
31
|
async function injectCss(template, entryName, styledComponentsStyleTags) {
|
|
32
32
|
let css = await getCssChunks();
|
|
33
33
|
if (styledComponentsStyleTags) css += styledComponentsStyleTags;
|
|
34
|
+
css += createFederatedCssLinks(moduleFederationCssAssets, {
|
|
35
|
+
template,
|
|
36
|
+
existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
|
|
37
|
+
});
|
|
34
38
|
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
35
39
|
async function getCssChunks() {
|
|
36
40
|
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
37
41
|
if (!routeManifest) return '';
|
|
38
42
|
const { routeAssets } = routeManifest;
|
|
39
|
-
let matchedRouteManifests;
|
|
43
|
+
let matchedRouteManifests = [];
|
|
40
44
|
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
41
45
|
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
42
|
-
else {
|
|
43
|
-
if (!routerContext || !routes) return '';
|
|
46
|
+
else if (routerContext && routes) {
|
|
44
47
|
const matches = matchRoutes(routes, routerContext.location, routerContext.basename);
|
|
45
48
|
matchedRouteManifests = matches?.map((match, index)=>{
|
|
46
49
|
if (!index) return;
|
|
47
50
|
const routeId = match.route.id;
|
|
48
51
|
if (routeId) return routeAssets[routeId];
|
|
49
|
-
}).filter(Boolean);
|
|
52
|
+
}).filter(Boolean) ?? [];
|
|
50
53
|
}
|
|
51
54
|
const asyncEntry = routeAssets[`async-${entryName}`];
|
|
52
|
-
if (asyncEntry) matchedRouteManifests
|
|
53
|
-
const cssChunks = matchedRouteManifests
|
|
55
|
+
if (asyncEntry) matchedRouteManifests.push(asyncEntry);
|
|
56
|
+
const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
|
|
54
57
|
const { referenceCssAssets = [] } = routeManifest;
|
|
55
58
|
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template.includes(asset));
|
|
56
59
|
return [
|
|
57
60
|
...chunks,
|
|
58
61
|
..._cssChunks
|
|
59
62
|
];
|
|
60
|
-
}, [])
|
|
63
|
+
}, []);
|
|
61
64
|
const { inlineStyles } = config;
|
|
62
65
|
const styles = await Promise.all(cssChunks.map(async (chunk)=>{
|
|
63
66
|
const link = `<link href="${chunk}" rel="stylesheet" />`;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { matchRoutes } from "@modern-js/runtime-utils/router";
|
|
3
|
+
import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
4
|
+
import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
5
|
+
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
6
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
7
|
+
import { buildHtml } from "../shared.mjs";
|
|
8
|
+
import { safeReplace } from "../utils.mjs";
|
|
9
|
+
const checkIsInline = (chunk, enableInline)=>{
|
|
10
|
+
if ('production' !== process.env.NODE_ENV) return false;
|
|
11
|
+
if (enableInline instanceof RegExp) return enableInline.test(chunk);
|
|
12
|
+
return Boolean(enableInline);
|
|
13
|
+
};
|
|
14
|
+
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
15
|
+
const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
|
|
16
|
+
const helmetData = getHelmetData(runtimeContext);
|
|
17
|
+
const callbacks = [
|
|
18
|
+
createReplaceHelemt(helmetData),
|
|
19
|
+
(template)=>injectCss(template, entryName, styledComponentsStyleTags)
|
|
20
|
+
];
|
|
21
|
+
return buildHtml(beforeAppTemplate, callbacks);
|
|
22
|
+
async function injectCss(template, entryName, styledComponentsStyleTags) {
|
|
23
|
+
let css = await getCssChunks();
|
|
24
|
+
if (styledComponentsStyleTags) css += styledComponentsStyleTags;
|
|
25
|
+
css += createFederatedCssLinks(moduleFederationCssAssets, {
|
|
26
|
+
template,
|
|
27
|
+
existingAssets: css.match(/href="([^"]+)"/g)?.map((item)=>item.replace(/^href="/, '').replace(/"$/, ''))
|
|
28
|
+
});
|
|
29
|
+
return safeReplace(template, CHUNK_CSS_PLACEHOLDER, css);
|
|
30
|
+
async function getCssChunks() {
|
|
31
|
+
const { routeManifest, routerContext, routes } = runtimeContext;
|
|
32
|
+
if (!routeManifest) return '';
|
|
33
|
+
const { routeAssets } = routeManifest;
|
|
34
|
+
let matchedRouteManifests = [];
|
|
35
|
+
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext);
|
|
36
|
+
if (matchedRouteIds?.length) matchedRouteManifests = matchedRouteIds.map((routeId)=>routeAssets[routeId]).filter(Boolean);
|
|
37
|
+
else if (routerContext && routes) {
|
|
38
|
+
const matches = matchRoutes(routes, routerContext.location, routerContext.basename);
|
|
39
|
+
matchedRouteManifests = matches?.map((match, index)=>{
|
|
40
|
+
if (!index) return;
|
|
41
|
+
const routeId = match.route.id;
|
|
42
|
+
if (routeId) return routeAssets[routeId];
|
|
43
|
+
}).filter(Boolean) ?? [];
|
|
44
|
+
}
|
|
45
|
+
const asyncEntry = routeAssets[`async-${entryName}`];
|
|
46
|
+
if (asyncEntry) matchedRouteManifests.push(asyncEntry);
|
|
47
|
+
const cssChunks = matchedRouteManifests.reduce((chunks, routeManifest)=>{
|
|
48
|
+
const { referenceCssAssets = [] } = routeManifest;
|
|
49
|
+
const _cssChunks = referenceCssAssets.filter((asset)=>asset?.endsWith('.css') && !template.includes(asset));
|
|
50
|
+
return [
|
|
51
|
+
...chunks,
|
|
52
|
+
..._cssChunks
|
|
53
|
+
];
|
|
54
|
+
}, []);
|
|
55
|
+
const { inlineStyles } = config;
|
|
56
|
+
const styles = cssChunks.map((chunk)=>{
|
|
57
|
+
const link = `<link href="${chunk}" rel="stylesheet" />`;
|
|
58
|
+
checkIsInline(chunk, inlineStyles);
|
|
59
|
+
return link;
|
|
60
|
+
});
|
|
61
|
+
return `${styles.join('')}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export { buildShellBeforeTemplate };
|
|
@@ -16,7 +16,7 @@ const defaultExtender = {
|
|
|
16
16
|
};
|
|
17
17
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
18
18
|
const { renderToPipeableStream } = await import("react-dom/server");
|
|
19
|
-
const { runtimeContext, htmlTemplate, config, ssrConfig, entryName } = options;
|
|
19
|
+
const { runtimeContext, htmlTemplate, config, ssrConfig, entryName, moduleFederationCssAssets } = options;
|
|
20
20
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
21
21
|
let renderLevel = RenderLevel.SERVER_RENDER;
|
|
22
22
|
const forceStream2String = Boolean(process.env.MODERN_JS_STREAM_TO_STRING);
|
|
@@ -36,11 +36,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
36
36
|
if (extender.modifyRootElement) processedRootElement = extender.modifyRootElement(processedRootElement);
|
|
37
37
|
});
|
|
38
38
|
const chunkVec = [];
|
|
39
|
+
let hasStartedPipe = false;
|
|
39
40
|
return new Promise((resolve)=>{
|
|
40
41
|
const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
|
|
41
42
|
nonce: config.nonce,
|
|
42
43
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
|
|
43
44
|
[onReady] () {
|
|
45
|
+
if (hasStartedPipe) return;
|
|
46
|
+
hasStartedPipe = true;
|
|
44
47
|
let styledComponentsStyleTags = '';
|
|
45
48
|
extenders.forEach((extender)=>{
|
|
46
49
|
if (extender.getStyleTags) styledComponentsStyleTags += extender.getStyleTags();
|
|
@@ -53,6 +56,7 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
53
56
|
runtimeContext,
|
|
54
57
|
config,
|
|
55
58
|
entryName,
|
|
59
|
+
moduleFederationCssAssets,
|
|
56
60
|
styledComponentsStyleTags
|
|
57
61
|
}).then(({ shellAfter, shellBefore })=>{
|
|
58
62
|
const pendingScripts = [];
|
|
@@ -110,7 +114,8 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
110
114
|
renderLevel,
|
|
111
115
|
runtimeContext,
|
|
112
116
|
entryName,
|
|
113
|
-
config
|
|
117
|
+
config,
|
|
118
|
+
moduleFederationCssAssets
|
|
114
119
|
}).then(({ shellAfter, shellBefore })=>{
|
|
115
120
|
const fallbackHtml = `${shellBefore}${shellAfter}`;
|
|
116
121
|
const readableStream = getReadableStreamFromString(fallbackHtml);
|
|
@@ -9,19 +9,21 @@ import { getTemplates } from "./template.mjs";
|
|
|
9
9
|
const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
10
10
|
let shellChunkStatus = ShellChunkStatus.START;
|
|
11
11
|
const chunkVec = [];
|
|
12
|
-
const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, rscRoot } = options;
|
|
12
|
+
const { htmlTemplate, runtimeContext, config, ssrConfig, entryName, moduleFederationCssAssets, rscManifest, rscRoot } = options;
|
|
13
13
|
const { shellBefore, shellAfter } = await getTemplates(htmlTemplate, {
|
|
14
14
|
renderLevel: RenderLevel.SERVER_RENDER,
|
|
15
15
|
runtimeContext,
|
|
16
16
|
ssrConfig,
|
|
17
17
|
request,
|
|
18
18
|
config,
|
|
19
|
-
entryName
|
|
19
|
+
entryName,
|
|
20
|
+
moduleFederationCssAssets
|
|
20
21
|
});
|
|
21
22
|
try {
|
|
22
23
|
const readableOriginal = await renderSSRStream(rootElement, {
|
|
23
24
|
request,
|
|
24
25
|
nonce: config.nonce,
|
|
26
|
+
rscManifest,
|
|
25
27
|
rscRoot: rscRoot,
|
|
26
28
|
routes: runtimeContext.routes,
|
|
27
29
|
onError (error) {
|
|
@@ -64,7 +64,7 @@ function createRenderStreaming(createReadableStreamPromise) {
|
|
|
64
64
|
const end = time();
|
|
65
65
|
const { runtimeContext, config, resource } = options;
|
|
66
66
|
const { onError, onTiming } = options;
|
|
67
|
-
const { htmlTemplate, entryName } = resource;
|
|
67
|
+
const { htmlTemplate, entryName, moduleFederationCssAssets } = resource;
|
|
68
68
|
const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
|
|
69
69
|
const StreamServerRootWrapper = ({ children })=>/*#__PURE__*/ jsxs(Fragment, {
|
|
70
70
|
children: [
|
|
@@ -84,9 +84,11 @@ function createRenderStreaming(createReadableStreamPromise) {
|
|
|
84
84
|
runtimeContext,
|
|
85
85
|
ssrConfig,
|
|
86
86
|
entryName,
|
|
87
|
+
moduleFederationCssAssets,
|
|
87
88
|
rscClientManifest: options.rscClientManifest,
|
|
88
89
|
rscSSRManifest: options.rscSSRManifest,
|
|
89
90
|
rscServerManifest: options.rscServerManifest,
|
|
91
|
+
rscManifest: options.rscManifest,
|
|
90
92
|
rscRoot: options.rscRoot,
|
|
91
93
|
onShellReady () {
|
|
92
94
|
const cost = end();
|
|
@@ -2,12 +2,11 @@ import "node:module";
|
|
|
2
2
|
import { time } from "@modern-js/runtime-utils/time";
|
|
3
3
|
import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
|
|
4
4
|
import server from "react-dom/server";
|
|
5
|
-
import react_helmet from "react-helmet";
|
|
6
5
|
import { RenderLevel } from "../../constants.mjs";
|
|
7
6
|
import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
8
7
|
import { wrapRuntimeContextProvider } from "../../react/wrapper.mjs";
|
|
9
8
|
import { CHUNK_CSS_PLACEHOLDER, CHUNK_JS_PLACEHOLDER, HTML_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
10
|
-
import { createReplaceHelemt } from "../helmet.mjs";
|
|
9
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
11
10
|
import { buildHtml } from "../shared.mjs";
|
|
12
11
|
import { SSRErrors, SSRTimings } from "../tracer.mjs";
|
|
13
12
|
import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
|
|
@@ -20,7 +19,7 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
20
19
|
onTiming
|
|
21
20
|
};
|
|
22
21
|
const routerContext = runtimeContext.routerContext;
|
|
23
|
-
const { htmlTemplate, entryName, loadableStats, routeManifest } = resource;
|
|
22
|
+
const { htmlTemplate, entryName, loadableStats, routeManifest, moduleFederationCssAssets } = resource;
|
|
24
23
|
const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
|
|
25
24
|
const chunkSet = {
|
|
26
25
|
renderLevel: RenderLevel.CLIENT_RENDER,
|
|
@@ -33,8 +32,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
33
32
|
stats: loadableStats,
|
|
34
33
|
nonce: config.nonce,
|
|
35
34
|
routeManifest,
|
|
35
|
+
runtimeContext,
|
|
36
36
|
template: htmlTemplate,
|
|
37
37
|
entryName,
|
|
38
|
+
moduleFederationCssAssets,
|
|
38
39
|
chunkSet,
|
|
39
40
|
config
|
|
40
41
|
}),
|
|
@@ -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 || [], tracer);
|
|
62
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
|
|
62
63
|
return html;
|
|
63
64
|
};
|
|
64
|
-
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, { onError, onTiming }) {
|
|
65
|
+
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
|
|
65
66
|
let html = '';
|
|
66
67
|
let helmetData;
|
|
67
68
|
const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
|
|
@@ -71,7 +72,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
71
72
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
72
73
|
});
|
|
73
74
|
chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
|
|
74
|
-
helmetData =
|
|
75
|
+
helmetData = getHelmetData(runtimeContext);
|
|
75
76
|
const cost = end();
|
|
76
77
|
onTiming(SSRTimings.RENDER_HTML, cost);
|
|
77
78
|
} catch (e) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { ChunkExtractor } from "@loadable/server";
|
|
3
|
+
import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
4
|
+
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
3
5
|
import { attributesToString, checkIsNode } from "../utils.mjs";
|
|
4
6
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
5
7
|
import { dirname as __rspack_dirname } from "node:path";
|
|
@@ -25,6 +27,20 @@ class LoadableCollector {
|
|
|
25
27
|
const { routeManifest, entryName } = this.options;
|
|
26
28
|
return routeManifest?.routeAssets?.[entryName]?.assets;
|
|
27
29
|
}
|
|
30
|
+
getMatchedRouteChunks() {
|
|
31
|
+
const { routeManifest, runtimeContext } = this.options;
|
|
32
|
+
const routeAssets = routeManifest?.routeAssets;
|
|
33
|
+
if (!routeAssets) return [];
|
|
34
|
+
const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext) ?? [];
|
|
35
|
+
return matchedRouteIds.flatMap((routeId)=>{
|
|
36
|
+
const routeAsset = routeAssets[routeId];
|
|
37
|
+
return (routeAsset?.assets ?? []).map((asset)=>({
|
|
38
|
+
filename: asset.replace(/^\//, ''),
|
|
39
|
+
path: asset,
|
|
40
|
+
url: asset
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
}
|
|
28
44
|
collect(comopnent) {
|
|
29
45
|
const { stats, entryName } = this.options;
|
|
30
46
|
if (!stats) return comopnent;
|
|
@@ -37,20 +53,21 @@ class LoadableCollector {
|
|
|
37
53
|
return this.extractor.collectChunks(comopnent);
|
|
38
54
|
}
|
|
39
55
|
async effect() {
|
|
40
|
-
if (!this.extractor) return;
|
|
41
56
|
const { extractor, options } = this;
|
|
42
57
|
const { entryName, config } = options;
|
|
43
58
|
const asyncChunks = [];
|
|
44
|
-
if (config.enableAsyncEntry) try {
|
|
59
|
+
if (extractor && config.enableAsyncEntry) try {
|
|
45
60
|
asyncChunks.push(...extractor.getChunkAssets([
|
|
46
61
|
`async-${entryName}`
|
|
47
62
|
]));
|
|
48
63
|
} catch (e) {}
|
|
49
|
-
const chunks = [].concat(asyncChunks).concat(extractor.getChunkAssets(extractor.chunks));
|
|
64
|
+
const chunks = [].concat(asyncChunks).concat(extractor ? extractor.getChunkAssets(extractor.chunks) : []).concat(this.getMatchedRouteChunks());
|
|
50
65
|
const scriptChunks = generateChunks(chunks, 'js');
|
|
51
66
|
const styleChunks = generateChunks(chunks, 'css');
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
if (extractor) {
|
|
68
|
+
this.emitLoadableScripts(extractor);
|
|
69
|
+
await this.emitScriptAssets(scriptChunks);
|
|
70
|
+
}
|
|
54
71
|
await this.emitStyleAssets(styleChunks);
|
|
55
72
|
}
|
|
56
73
|
emitLoadableScripts(extractor) {
|
|
@@ -85,19 +102,28 @@ class LoadableCollector {
|
|
|
85
102
|
chunkSet.jsChunk += scripts.filter((script)=>Boolean(script)).join('');
|
|
86
103
|
}
|
|
87
104
|
async emitStyleAssets(chunks) {
|
|
88
|
-
const { template, chunkSet, config,
|
|
105
|
+
const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
|
|
89
106
|
const { inlineStyles } = config;
|
|
90
107
|
const atrributes = attributesToString(this.generateAttributes());
|
|
91
108
|
const linkRegExp = /<link .*?href="([^"]+)".*?>/g;
|
|
92
109
|
const matchs = template.matchAll(linkRegExp);
|
|
93
110
|
const existedLinks = [];
|
|
94
111
|
for (const match of matchs)existedLinks.push(match[1]);
|
|
95
|
-
const
|
|
112
|
+
const emittedChunks = chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path));
|
|
113
|
+
const css = await Promise.all(emittedChunks.map(async (chunk)=>{
|
|
96
114
|
const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
|
|
97
115
|
if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
|
|
98
116
|
return link;
|
|
99
117
|
}));
|
|
100
118
|
chunkSet.cssChunk += css.filter((css)=>Boolean(css)).join('');
|
|
119
|
+
chunkSet.cssChunk += createFederatedCssLinks(moduleFederationCssAssets, {
|
|
120
|
+
template,
|
|
121
|
+
attributes: this.generateAttributes(),
|
|
122
|
+
existingAssets: [
|
|
123
|
+
...existedLinks,
|
|
124
|
+
...emittedChunks.map((chunk)=>chunk.url)
|
|
125
|
+
]
|
|
126
|
+
});
|
|
101
127
|
}
|
|
102
128
|
generateAttributes(extraAtr = {}) {
|
|
103
129
|
const { config } = this.options;
|
|
@@ -1,6 +1,191 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
"use client";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import react from "react";
|
|
4
|
+
import { Helmet, HelmetData, HelmetProvider } from "react-helmet-async";
|
|
5
|
+
import { InternalRuntimeContext } from "../core/context/index.mjs";
|
|
6
|
+
const ATTRIBUTE_NAME_MAP = {
|
|
7
|
+
charSet: 'charset',
|
|
8
|
+
className: 'class',
|
|
9
|
+
contentEditable: 'contenteditable',
|
|
10
|
+
httpEquiv: 'http-equiv',
|
|
11
|
+
itemProp: 'itemprop',
|
|
12
|
+
tabIndex: 'tabindex'
|
|
13
|
+
};
|
|
14
|
+
const escapeHtml = (value)=>String(value).replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>');
|
|
15
|
+
const toHtmlAttributeName = (name)=>ATTRIBUTE_NAME_MAP[name] ?? name;
|
|
16
|
+
const attributesToString = (attributes, includeHelmetAttribute = false)=>{
|
|
17
|
+
const pairs = [];
|
|
18
|
+
if (includeHelmetAttribute) pairs.push('data-rh="true"');
|
|
19
|
+
for (const [name, value] of Object.entries(attributes ?? {})){
|
|
20
|
+
if (false === value || null == value) continue;
|
|
21
|
+
const htmlName = toHtmlAttributeName(name);
|
|
22
|
+
if (true === value) pairs.push(htmlName);
|
|
23
|
+
else pairs.push(`${htmlName}="${escapeHtml(value)}"`);
|
|
24
|
+
}
|
|
25
|
+
return pairs.join(' ');
|
|
26
|
+
};
|
|
27
|
+
const createDatum = (tagName, tags)=>({
|
|
28
|
+
toComponent: ()=>[],
|
|
29
|
+
toString: ()=>tags.map((tag)=>{
|
|
30
|
+
const attrs = attributesToString(tag, true);
|
|
31
|
+
if ("script" === tagName && 'string' == typeof tag.innerHTML) return `<script ${attrs}>${tag.innerHTML}</script>`;
|
|
32
|
+
if ('style' === tagName && 'string' == typeof tag.cssText) return `<style ${attrs}>${tag.cssText}</style>`;
|
|
33
|
+
if ("noscript" === tagName && 'string' == typeof tag.innerHTML) return `<noscript ${attrs}>${tag.innerHTML}</noscript>`;
|
|
34
|
+
return `<${tagName} ${attrs}>`;
|
|
35
|
+
}).join('')
|
|
36
|
+
});
|
|
37
|
+
const createAttributeDatum = (attributes)=>({
|
|
38
|
+
toComponent: ()=>attributes,
|
|
39
|
+
toString: ()=>attributesToString(attributes)
|
|
40
|
+
});
|
|
41
|
+
const createTitleDatum = (title, attributes)=>({
|
|
42
|
+
toComponent: ()=>[],
|
|
43
|
+
toString: ()=>{
|
|
44
|
+
if (!title) return '';
|
|
45
|
+
const attrs = attributesToString(attributes, true);
|
|
46
|
+
return `<title ${attrs}>${escapeHtml(title)}</title>`;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const createEmptyHelmetState = ()=>({
|
|
50
|
+
base: createDatum('base', []),
|
|
51
|
+
bodyAttributes: createAttributeDatum({}),
|
|
52
|
+
htmlAttributes: createAttributeDatum({}),
|
|
53
|
+
link: createDatum('link', []),
|
|
54
|
+
meta: createDatum('meta', []),
|
|
55
|
+
noscript: createDatum("noscript", []),
|
|
56
|
+
priority: createDatum('meta', []),
|
|
57
|
+
script: createDatum("script", []),
|
|
58
|
+
style: createDatum('style', []),
|
|
59
|
+
title: createTitleDatum(void 0, {})
|
|
60
|
+
});
|
|
61
|
+
const mergeAttributes = (current, next)=>({
|
|
62
|
+
...current,
|
|
63
|
+
...next ?? {}
|
|
64
|
+
});
|
|
65
|
+
const collectChildren = (children, draft)=>{
|
|
66
|
+
react.Children.forEach(children, (child)=>{
|
|
67
|
+
if (!react.isValidElement(child)) return;
|
|
68
|
+
if (child.type === react.Fragment) return void collectChildren(child.props.children, draft);
|
|
69
|
+
if ('string' != typeof child.type) return;
|
|
70
|
+
const { children: nestedChildren, ...props } = child.props;
|
|
71
|
+
if ('title' === child.type) {
|
|
72
|
+
draft.title = react.Children.toArray(nestedChildren).join('');
|
|
73
|
+
draft.titleAttributes = mergeAttributes(draft.titleAttributes, props);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if ('html' === child.type || 'body' === child.type) return;
|
|
77
|
+
if ('base' === child.type || 'link' === child.type || 'meta' === child.type || "noscript" === child.type || "script" === child.type || 'style' === child.type) {
|
|
78
|
+
const tag = {
|
|
79
|
+
...props
|
|
80
|
+
};
|
|
81
|
+
if (("script" === child.type || 'style' === child.type || "noscript" === child.type) && 'string' == typeof nestedChildren) tag['style' === child.type ? 'cssText' : 'innerHTML'] = nestedChildren;
|
|
82
|
+
draft[child.type].push(tag);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const collectHelmetProps = (current, props)=>{
|
|
87
|
+
const baseState = current ?? createEmptyHelmetState();
|
|
88
|
+
const draft = {
|
|
89
|
+
base: [
|
|
90
|
+
...props.base ? [
|
|
91
|
+
props.base
|
|
92
|
+
] : []
|
|
93
|
+
],
|
|
94
|
+
bodyAttributes: props.bodyAttributes,
|
|
95
|
+
htmlAttributes: props.htmlAttributes,
|
|
96
|
+
link: [
|
|
97
|
+
...props.link ?? []
|
|
98
|
+
],
|
|
99
|
+
meta: [
|
|
100
|
+
...props.meta ?? []
|
|
101
|
+
],
|
|
102
|
+
noscript: [
|
|
103
|
+
...props.noscript ?? []
|
|
104
|
+
],
|
|
105
|
+
script: [
|
|
106
|
+
...props.script ?? []
|
|
107
|
+
],
|
|
108
|
+
style: [
|
|
109
|
+
...props.style ?? []
|
|
110
|
+
],
|
|
111
|
+
title: 'string' == typeof props.title ? props.title : Array.isArray(props.title) ? props.title.join('') : void 0,
|
|
112
|
+
titleAttributes: props.titleAttributes ?? {}
|
|
113
|
+
};
|
|
114
|
+
collectChildren(props.children, draft);
|
|
115
|
+
const title = draft.title && props.titleTemplate ? props.titleTemplate.replaceAll('%s', draft.title) : draft.title ?? props.defaultTitle;
|
|
116
|
+
return {
|
|
117
|
+
base: createDatum('base', [
|
|
118
|
+
...baseState.__baseTags ?? [],
|
|
119
|
+
...draft.base
|
|
120
|
+
]),
|
|
121
|
+
bodyAttributes: createAttributeDatum(mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes)),
|
|
122
|
+
htmlAttributes: createAttributeDatum(mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes)),
|
|
123
|
+
link: createDatum('link', [
|
|
124
|
+
...baseState.__linkTags ?? [],
|
|
125
|
+
...draft.link
|
|
126
|
+
]),
|
|
127
|
+
meta: createDatum('meta', [
|
|
128
|
+
...baseState.__metaTags ?? [],
|
|
129
|
+
...draft.meta
|
|
130
|
+
]),
|
|
131
|
+
noscript: createDatum("noscript", [
|
|
132
|
+
...baseState.__noscriptTags ?? [],
|
|
133
|
+
...draft.noscript
|
|
134
|
+
]),
|
|
135
|
+
priority: createDatum('meta', []),
|
|
136
|
+
script: createDatum("script", [
|
|
137
|
+
...baseState.__scriptTags ?? [],
|
|
138
|
+
...draft.script
|
|
139
|
+
]),
|
|
140
|
+
style: createDatum('style', [
|
|
141
|
+
...baseState.__styleTags ?? [],
|
|
142
|
+
...draft.style
|
|
143
|
+
]),
|
|
144
|
+
title: createTitleDatum(title ?? baseState.__title, mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)),
|
|
145
|
+
__baseTags: [
|
|
146
|
+
...baseState.__baseTags ?? [],
|
|
147
|
+
...draft.base
|
|
148
|
+
],
|
|
149
|
+
__bodyAttributes: mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes),
|
|
150
|
+
__htmlAttributes: mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes),
|
|
151
|
+
__linkTags: [
|
|
152
|
+
...baseState.__linkTags ?? [],
|
|
153
|
+
...draft.link
|
|
154
|
+
],
|
|
155
|
+
__metaTags: [
|
|
156
|
+
...baseState.__metaTags ?? [],
|
|
157
|
+
...draft.meta
|
|
158
|
+
],
|
|
159
|
+
__noscriptTags: [
|
|
160
|
+
...baseState.__noscriptTags ?? [],
|
|
161
|
+
...draft.noscript
|
|
162
|
+
],
|
|
163
|
+
__scriptTags: [
|
|
164
|
+
...baseState.__scriptTags ?? [],
|
|
165
|
+
...draft.script
|
|
166
|
+
],
|
|
167
|
+
__styleTags: [
|
|
168
|
+
...baseState.__styleTags ?? [],
|
|
169
|
+
...draft.style
|
|
170
|
+
],
|
|
171
|
+
__title: title ?? baseState.__title,
|
|
172
|
+
__titleAttributes: mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
const head_Helmet = (props)=>{
|
|
176
|
+
const runtimeContext = react.useContext(InternalRuntimeContext);
|
|
177
|
+
if (runtimeContext && !runtimeContext.isBrowser) {
|
|
178
|
+
runtimeContext._helmetContext ??= {};
|
|
179
|
+
runtimeContext._helmetContext.helmet = collectHelmetProps(runtimeContext._helmetContext.helmet ?? void 0, props);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return react.createElement(Helmet, props);
|
|
183
|
+
};
|
|
184
|
+
const head = {
|
|
185
|
+
Helmet: head_Helmet,
|
|
186
|
+
HelmetData: HelmetData,
|
|
187
|
+
HelmetProvider: HelmetProvider
|
|
188
|
+
};
|
|
189
|
+
const exports_head = head;
|
|
190
|
+
export default exports_head;
|
|
191
|
+
export { HelmetData, HelmetProvider, head_Helmet as Helmet };
|