@bleedingdev/modern-js-runtime 3.2.0-ultramodern.82 → 3.2.0-ultramodern.84
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/index.js +3 -14
- package/dist/cjs/core/react/wrapper.js +9 -3
- package/dist/cjs/core/server/helmet.js +6 -0
- package/dist/cjs/core/server/stream/beforeTemplate.js +1 -12
- package/dist/cjs/core/server/stream/beforeTemplate.worker.js +1 -12
- package/dist/cjs/core/server/stream/createReadableStream.js +3 -0
- package/dist/cjs/core/server/string/index.js +8 -10
- package/dist/cjs/exports/head.js +196 -5
- package/dist/cjs/router/runtime/tanstack/routeTree.js +10 -3
- package/dist/cjs/ssr/serverRender/renderToString/entry.js +9 -8
- package/dist/esm/cli/ssr/index.mjs +3 -14
- package/dist/esm/core/react/wrapper.mjs +9 -3
- package/dist/esm/core/server/helmet.mjs +4 -1
- package/dist/esm/core/server/stream/beforeTemplate.mjs +2 -3
- package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -3
- package/dist/esm/core/server/stream/createReadableStream.mjs +3 -0
- package/dist/esm/core/server/string/index.mjs +9 -10
- package/dist/esm/exports/head.mjs +189 -4
- package/dist/esm/router/runtime/tanstack/routeTree.mjs +10 -3
- package/dist/esm/ssr/serverRender/renderToString/entry.mjs +9 -6
- package/dist/esm-node/cli/ssr/index.mjs +3 -14
- package/dist/esm-node/core/react/wrapper.mjs +9 -3
- package/dist/esm-node/core/server/helmet.mjs +4 -1
- package/dist/esm-node/core/server/stream/beforeTemplate.mjs +2 -3
- package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -3
- package/dist/esm-node/core/server/stream/createReadableStream.mjs +3 -0
- package/dist/esm-node/core/server/string/index.mjs +9 -10
- package/dist/esm-node/exports/head.mjs +189 -4
- package/dist/esm-node/router/runtime/tanstack/routeTree.mjs +10 -3
- package/dist/esm-node/ssr/serverRender/renderToString/entry.mjs +9 -6
- package/dist/types/core/context/runtime.d.ts +4 -0
- package/dist/types/core/server/helmet.d.ts +5 -3
- package/dist/types/exports/head.d.ts +10 -3
- package/package.json +10 -11
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { matchRoutes } from "@modern-js/runtime-utils/router";
|
|
2
|
-
import react_helmet from "react-helmet";
|
|
3
2
|
import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
|
|
4
3
|
import { CHUNK_CSS_PLACEHOLDER } from "../constants.mjs";
|
|
5
4
|
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
6
|
-
import { createReplaceHelemt } from "../helmet.mjs";
|
|
5
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
7
6
|
import { buildHtml } from "../shared.mjs";
|
|
8
7
|
import { safeReplace } from "../utils.mjs";
|
|
9
8
|
const checkIsInline = (chunk, enableInline)=>{
|
|
@@ -13,7 +12,7 @@ const checkIsInline = (chunk, enableInline)=>{
|
|
|
13
12
|
};
|
|
14
13
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
15
14
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
|
|
16
|
-
const helmetData =
|
|
15
|
+
const helmetData = getHelmetData(runtimeContext);
|
|
17
16
|
const callbacks = [
|
|
18
17
|
createReplaceHelemt(helmetData),
|
|
19
18
|
(template)=>injectCss(template, entryName, styledComponentsStyleTags)
|
|
@@ -35,11 +35,14 @@ const createReadableStreamFromElement = async (request, rootElement, options)=>{
|
|
|
35
35
|
if (extender.modifyRootElement) processedRootElement = extender.modifyRootElement(processedRootElement);
|
|
36
36
|
});
|
|
37
37
|
const chunkVec = [];
|
|
38
|
+
let hasStartedPipe = false;
|
|
38
39
|
return new Promise((resolve)=>{
|
|
39
40
|
const { pipe: reactStreamingPipe } = renderToPipeableStream(processedRootElement, {
|
|
40
41
|
nonce: config.nonce,
|
|
41
42
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX,
|
|
42
43
|
[onReady] () {
|
|
44
|
+
if (hasStartedPipe) return;
|
|
45
|
+
hasStartedPipe = true;
|
|
43
46
|
let styledComponentsStyleTags = '';
|
|
44
47
|
extenders.forEach((extender)=>{
|
|
45
48
|
if (extender.getStyleTags) styledComponentsStyleTags += extender.getStyleTags();
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { time } from "@modern-js/runtime-utils/time";
|
|
2
2
|
import { SSR_HYDRATION_ID_PREFIX } from "@modern-js/utils/universal/constants";
|
|
3
3
|
import server from "react-dom/server";
|
|
4
|
-
import react_helmet from "react-helmet";
|
|
5
4
|
import { RenderLevel } from "../../constants.mjs";
|
|
6
5
|
import { getGlobalInternalRuntimeContext } from "../../context/index.mjs";
|
|
7
6
|
import { wrapRuntimeContextProvider } from "../../react/wrapper.mjs";
|
|
8
7
|
import { CHUNK_CSS_PLACEHOLDER, CHUNK_JS_PLACEHOLDER, HTML_PLACEHOLDER, SSR_DATA_PLACEHOLDER } from "../constants.mjs";
|
|
9
|
-
import { createReplaceHelemt } from "../helmet.mjs";
|
|
8
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
10
9
|
import { buildHtml } from "../shared.mjs";
|
|
11
10
|
import { SSRErrors, SSRTimings } from "../tracer.mjs";
|
|
12
11
|
import { getSSRConfigByEntry, safeReplace } from "../utils.mjs";
|
|
13
12
|
import { LoadableCollector } from "./loadable.mjs";
|
|
14
13
|
import { SSRDataCollector } from "./ssrData.mjs";
|
|
15
14
|
const renderString = async (request, serverRoot, options)=>{
|
|
16
|
-
const { resource, runtimeContext, config, onError, onTiming } = options;
|
|
15
|
+
const { resource, runtimeContext: runtimeContext1, config, onError, onTiming } = options;
|
|
17
16
|
const tracer = {
|
|
18
17
|
onError,
|
|
19
18
|
onTiming
|
|
20
19
|
};
|
|
21
|
-
const routerContext =
|
|
20
|
+
const routerContext = runtimeContext1.routerContext;
|
|
22
21
|
const { htmlTemplate, entryName, loadableStats, routeManifest, moduleFederationCssAssets } = resource;
|
|
23
22
|
const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
|
|
24
23
|
const chunkSet = {
|
|
@@ -32,7 +31,7 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
32
31
|
stats: loadableStats,
|
|
33
32
|
nonce: config.nonce,
|
|
34
33
|
routeManifest,
|
|
35
|
-
runtimeContext,
|
|
34
|
+
runtimeContext: runtimeContext1,
|
|
36
35
|
template: htmlTemplate,
|
|
37
36
|
entryName,
|
|
38
37
|
moduleFederationCssAssets,
|
|
@@ -40,10 +39,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
40
39
|
config
|
|
41
40
|
}),
|
|
42
41
|
new SSRDataCollector({
|
|
43
|
-
runtimeContext,
|
|
42
|
+
runtimeContext: runtimeContext1,
|
|
44
43
|
request,
|
|
45
44
|
ssrConfig,
|
|
46
|
-
ssrContext:
|
|
45
|
+
ssrContext: runtimeContext1.ssrContext,
|
|
47
46
|
chunkSet,
|
|
48
47
|
routerContext,
|
|
49
48
|
nonce: config.nonce,
|
|
@@ -56,10 +55,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
56
55
|
chunkSet
|
|
57
56
|
});
|
|
58
57
|
for (const c of extraCollectors)if (c) collectors.unshift(c);
|
|
59
|
-
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(
|
|
58
|
+
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext1, {
|
|
60
59
|
ssr: true
|
|
61
60
|
}));
|
|
62
|
-
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors,
|
|
61
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext1.ssrContext?.htmlModifiers || [], tracer);
|
|
63
62
|
return html;
|
|
64
63
|
};
|
|
65
64
|
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, { onError, onTiming }) {
|
|
@@ -72,7 +71,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
72
71
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
73
72
|
});
|
|
74
73
|
chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
|
|
75
|
-
helmetData =
|
|
74
|
+
helmetData = getHelmetData(runtimeContext);
|
|
76
75
|
const cost = end();
|
|
77
76
|
onTiming(SSRTimings.RENDER_HTML, cost);
|
|
78
77
|
} catch (e) {
|
|
@@ -1,5 +1,190 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import react from "react";
|
|
3
|
+
import { Helmet, HelmetData, HelmetProvider } from "react-helmet-async";
|
|
4
|
+
import { InternalRuntimeContext } from "../core/context/index.mjs";
|
|
5
|
+
const ATTRIBUTE_NAME_MAP = {
|
|
6
|
+
charSet: 'charset',
|
|
7
|
+
className: 'class',
|
|
8
|
+
contentEditable: 'contenteditable',
|
|
9
|
+
httpEquiv: 'http-equiv',
|
|
10
|
+
itemProp: 'itemprop',
|
|
11
|
+
tabIndex: 'tabindex'
|
|
12
|
+
};
|
|
13
|
+
const escapeHtml = (value)=>String(value).replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>');
|
|
14
|
+
const toHtmlAttributeName = (name)=>ATTRIBUTE_NAME_MAP[name] ?? name;
|
|
15
|
+
const attributesToString = (attributes, includeHelmetAttribute = false)=>{
|
|
16
|
+
const pairs = [];
|
|
17
|
+
if (includeHelmetAttribute) pairs.push('data-rh="true"');
|
|
18
|
+
for (const [name, value] of Object.entries(attributes ?? {})){
|
|
19
|
+
if (false === value || null == value) continue;
|
|
20
|
+
const htmlName = toHtmlAttributeName(name);
|
|
21
|
+
if (true === value) pairs.push(htmlName);
|
|
22
|
+
else pairs.push(`${htmlName}="${escapeHtml(value)}"`);
|
|
23
|
+
}
|
|
24
|
+
return pairs.join(' ');
|
|
25
|
+
};
|
|
26
|
+
const createDatum = (tagName, tags)=>({
|
|
27
|
+
toComponent: ()=>[],
|
|
28
|
+
toString: ()=>tags.map((tag)=>{
|
|
29
|
+
const attrs = attributesToString(tag, true);
|
|
30
|
+
if ("script" === tagName && 'string' == typeof tag.innerHTML) return `<script ${attrs}>${tag.innerHTML}</script>`;
|
|
31
|
+
if ('style' === tagName && 'string' == typeof tag.cssText) return `<style ${attrs}>${tag.cssText}</style>`;
|
|
32
|
+
if ("noscript" === tagName && 'string' == typeof tag.innerHTML) return `<noscript ${attrs}>${tag.innerHTML}</noscript>`;
|
|
33
|
+
return `<${tagName} ${attrs}>`;
|
|
34
|
+
}).join('')
|
|
35
|
+
});
|
|
36
|
+
const createAttributeDatum = (attributes)=>({
|
|
37
|
+
toComponent: ()=>attributes,
|
|
38
|
+
toString: ()=>attributesToString(attributes)
|
|
39
|
+
});
|
|
40
|
+
const createTitleDatum = (title, attributes)=>({
|
|
41
|
+
toComponent: ()=>[],
|
|
42
|
+
toString: ()=>{
|
|
43
|
+
if (!title) return '';
|
|
44
|
+
const attrs = attributesToString(attributes, true);
|
|
45
|
+
return `<title ${attrs}>${escapeHtml(title)}</title>`;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const createEmptyHelmetState = ()=>({
|
|
49
|
+
base: createDatum('base', []),
|
|
50
|
+
bodyAttributes: createAttributeDatum({}),
|
|
51
|
+
htmlAttributes: createAttributeDatum({}),
|
|
52
|
+
link: createDatum('link', []),
|
|
53
|
+
meta: createDatum('meta', []),
|
|
54
|
+
noscript: createDatum("noscript", []),
|
|
55
|
+
priority: createDatum('meta', []),
|
|
56
|
+
script: createDatum("script", []),
|
|
57
|
+
style: createDatum('style', []),
|
|
58
|
+
title: createTitleDatum(void 0, {})
|
|
59
|
+
});
|
|
60
|
+
const mergeAttributes = (current, next)=>({
|
|
61
|
+
...current,
|
|
62
|
+
...next ?? {}
|
|
63
|
+
});
|
|
64
|
+
const collectChildren = (children, draft)=>{
|
|
65
|
+
react.Children.forEach(children, (child)=>{
|
|
66
|
+
if (!react.isValidElement(child)) return;
|
|
67
|
+
if (child.type === react.Fragment) return void collectChildren(child.props.children, draft);
|
|
68
|
+
if ('string' != typeof child.type) return;
|
|
69
|
+
const { children: nestedChildren, ...props } = child.props;
|
|
70
|
+
if ('title' === child.type) {
|
|
71
|
+
draft.title = react.Children.toArray(nestedChildren).join('');
|
|
72
|
+
draft.titleAttributes = mergeAttributes(draft.titleAttributes, props);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if ('html' === child.type || 'body' === child.type) return;
|
|
76
|
+
if ('base' === child.type || 'link' === child.type || 'meta' === child.type || "noscript" === child.type || "script" === child.type || 'style' === child.type) {
|
|
77
|
+
const tag = {
|
|
78
|
+
...props
|
|
79
|
+
};
|
|
80
|
+
if (("script" === child.type || 'style' === child.type || "noscript" === child.type) && 'string' == typeof nestedChildren) tag['style' === child.type ? 'cssText' : 'innerHTML'] = nestedChildren;
|
|
81
|
+
draft[child.type].push(tag);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const collectHelmetProps = (current, props)=>{
|
|
86
|
+
const baseState = current ?? createEmptyHelmetState();
|
|
87
|
+
const draft = {
|
|
88
|
+
base: [
|
|
89
|
+
...props.base ? [
|
|
90
|
+
props.base
|
|
91
|
+
] : []
|
|
92
|
+
],
|
|
93
|
+
bodyAttributes: props.bodyAttributes,
|
|
94
|
+
htmlAttributes: props.htmlAttributes,
|
|
95
|
+
link: [
|
|
96
|
+
...props.link ?? []
|
|
97
|
+
],
|
|
98
|
+
meta: [
|
|
99
|
+
...props.meta ?? []
|
|
100
|
+
],
|
|
101
|
+
noscript: [
|
|
102
|
+
...props.noscript ?? []
|
|
103
|
+
],
|
|
104
|
+
script: [
|
|
105
|
+
...props.script ?? []
|
|
106
|
+
],
|
|
107
|
+
style: [
|
|
108
|
+
...props.style ?? []
|
|
109
|
+
],
|
|
110
|
+
title: 'string' == typeof props.title ? props.title : Array.isArray(props.title) ? props.title.join('') : void 0,
|
|
111
|
+
titleAttributes: props.titleAttributes ?? {}
|
|
112
|
+
};
|
|
113
|
+
collectChildren(props.children, draft);
|
|
114
|
+
const title = draft.title && props.titleTemplate ? props.titleTemplate.replaceAll('%s', draft.title) : draft.title ?? props.defaultTitle;
|
|
115
|
+
return {
|
|
116
|
+
base: createDatum('base', [
|
|
117
|
+
...baseState.__baseTags ?? [],
|
|
118
|
+
...draft.base
|
|
119
|
+
]),
|
|
120
|
+
bodyAttributes: createAttributeDatum(mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes)),
|
|
121
|
+
htmlAttributes: createAttributeDatum(mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes)),
|
|
122
|
+
link: createDatum('link', [
|
|
123
|
+
...baseState.__linkTags ?? [],
|
|
124
|
+
...draft.link
|
|
125
|
+
]),
|
|
126
|
+
meta: createDatum('meta', [
|
|
127
|
+
...baseState.__metaTags ?? [],
|
|
128
|
+
...draft.meta
|
|
129
|
+
]),
|
|
130
|
+
noscript: createDatum("noscript", [
|
|
131
|
+
...baseState.__noscriptTags ?? [],
|
|
132
|
+
...draft.noscript
|
|
133
|
+
]),
|
|
134
|
+
priority: createDatum('meta', []),
|
|
135
|
+
script: createDatum("script", [
|
|
136
|
+
...baseState.__scriptTags ?? [],
|
|
137
|
+
...draft.script
|
|
138
|
+
]),
|
|
139
|
+
style: createDatum('style', [
|
|
140
|
+
...baseState.__styleTags ?? [],
|
|
141
|
+
...draft.style
|
|
142
|
+
]),
|
|
143
|
+
title: createTitleDatum(title ?? baseState.__title, mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)),
|
|
144
|
+
__baseTags: [
|
|
145
|
+
...baseState.__baseTags ?? [],
|
|
146
|
+
...draft.base
|
|
147
|
+
],
|
|
148
|
+
__bodyAttributes: mergeAttributes(baseState.__bodyAttributes ?? {}, draft.bodyAttributes),
|
|
149
|
+
__htmlAttributes: mergeAttributes(baseState.__htmlAttributes ?? {}, draft.htmlAttributes),
|
|
150
|
+
__linkTags: [
|
|
151
|
+
...baseState.__linkTags ?? [],
|
|
152
|
+
...draft.link
|
|
153
|
+
],
|
|
154
|
+
__metaTags: [
|
|
155
|
+
...baseState.__metaTags ?? [],
|
|
156
|
+
...draft.meta
|
|
157
|
+
],
|
|
158
|
+
__noscriptTags: [
|
|
159
|
+
...baseState.__noscriptTags ?? [],
|
|
160
|
+
...draft.noscript
|
|
161
|
+
],
|
|
162
|
+
__scriptTags: [
|
|
163
|
+
...baseState.__scriptTags ?? [],
|
|
164
|
+
...draft.script
|
|
165
|
+
],
|
|
166
|
+
__styleTags: [
|
|
167
|
+
...baseState.__styleTags ?? [],
|
|
168
|
+
...draft.style
|
|
169
|
+
],
|
|
170
|
+
__title: title ?? baseState.__title,
|
|
171
|
+
__titleAttributes: mergeAttributes(baseState.__titleAttributes ?? {}, draft.titleAttributes)
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
const head_Helmet = (props)=>{
|
|
175
|
+
const runtimeContext = react.useContext(InternalRuntimeContext);
|
|
176
|
+
if (runtimeContext && !runtimeContext.isBrowser) {
|
|
177
|
+
runtimeContext._helmetContext ??= {};
|
|
178
|
+
runtimeContext._helmetContext.helmet = collectHelmetProps(runtimeContext._helmetContext.helmet ?? void 0, props);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return react.createElement(Helmet, props);
|
|
182
|
+
};
|
|
183
|
+
const head = {
|
|
184
|
+
Helmet: head_Helmet,
|
|
185
|
+
HelmetData: HelmetData,
|
|
186
|
+
HelmetProvider: HelmetProvider
|
|
187
|
+
};
|
|
188
|
+
const exports_head = head;
|
|
189
|
+
export default exports_head;
|
|
190
|
+
export { HelmetData, HelmetProvider, head_Helmet as Helmet };
|
|
@@ -44,12 +44,19 @@ function isModernDeferredData(value) {
|
|
|
44
44
|
function normalizeModernLoaderResult(result) {
|
|
45
45
|
return isModernDeferredData(result) ? result.data : result;
|
|
46
46
|
}
|
|
47
|
-
function pickRouteModuleComponent(routeModule) {
|
|
47
|
+
function pickRouteModuleComponent(routeModule, seen = new Set()) {
|
|
48
48
|
if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
|
|
49
49
|
if (!routeModule || 'object' != typeof routeModule) return;
|
|
50
|
+
if (seen.has(routeModule)) return;
|
|
51
|
+
seen.add(routeModule);
|
|
50
52
|
const module = routeModule;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
+
for (const candidate of [
|
|
54
|
+
module.default,
|
|
55
|
+
module.Component
|
|
56
|
+
]){
|
|
57
|
+
const component = pickRouteModuleComponent(candidate, seen);
|
|
58
|
+
if (component) return component;
|
|
59
|
+
}
|
|
53
60
|
}
|
|
54
61
|
function createServerLazyImportComponent(lazyImport, fallbackComponent) {
|
|
55
62
|
if ("u" > typeof document) return fallbackComponent;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { sanitizeSSRPayload, serializeJson } from "@modern-js/runtime-utils/node";
|
|
2
2
|
import { time } from "@modern-js/runtime-utils/time";
|
|
3
3
|
import react from "react";
|
|
4
|
-
import
|
|
4
|
+
import { HelmetProvider } from "react-helmet-async";
|
|
5
|
+
import { getHelmetData, helmetReplace } from "../../../core/server/helmet.mjs";
|
|
5
6
|
import { serializeErrors } from "../../../router/runtime/utils.mjs";
|
|
6
7
|
import prefetch from "../../prefetch";
|
|
7
|
-
import helmet from "../helmet";
|
|
8
8
|
import { SSRErrors, SSRTimings } from "../tracker";
|
|
9
9
|
import { RenderLevel } from "../types.mjs";
|
|
10
10
|
import { ROUTER_DATA_JSON_ID, SSR_DATA_JSON_ID, attributesToString } from "../utils";
|
|
@@ -60,8 +60,8 @@ class Entry {
|
|
|
60
60
|
createReplaceSSRDataScript(ssrDataScripts),
|
|
61
61
|
...this.htmlModifiers
|
|
62
62
|
]);
|
|
63
|
-
const helmetData =
|
|
64
|
-
return helmetData ?
|
|
63
|
+
const helmetData = getHelmetData(context);
|
|
64
|
+
return helmetData ? helmetReplace(html, helmetData) : html;
|
|
65
65
|
}
|
|
66
66
|
async prefetch(context) {
|
|
67
67
|
let prefetchData;
|
|
@@ -82,11 +82,14 @@ class Entry {
|
|
|
82
82
|
const end = time();
|
|
83
83
|
const { ssrContext } = context;
|
|
84
84
|
try {
|
|
85
|
-
const
|
|
85
|
+
const helmetContext = context._helmetContext ??= {};
|
|
86
|
+
const App = react.createElement(HelmetProvider, {
|
|
87
|
+
context: helmetContext
|
|
88
|
+
}, react.createElement(this.App, {
|
|
86
89
|
context: Object.assign(context, {
|
|
87
90
|
ssr: true
|
|
88
91
|
})
|
|
89
|
-
});
|
|
92
|
+
}));
|
|
90
93
|
html = await createRender(App).addCollector(createStyledCollector(this.result)).addCollector(createLoadableCollector({
|
|
91
94
|
stats: ssrContext.loadableStats,
|
|
92
95
|
result: this.result,
|
|
@@ -94,23 +94,12 @@ const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
|
|
|
94
94
|
console.warn(warningMessage);
|
|
95
95
|
}
|
|
96
96
|
const isModuleFederationAppSSR = hasServerRendering && hasExplicitMfSsrFlag && !isCloudflareWorkerSSR;
|
|
97
|
-
const useModuleFederationNodeOutput = hasServerRendering && isModuleFederationAppSSR && isNodeEnvironmentTarget(config.output.target);
|
|
98
97
|
const ssrEnv = userConfig.deploy?.worker?.ssr || userConfig.server?.rsc ? 'edge' : 'node';
|
|
99
98
|
const appContext = modernAPI.getAppContext();
|
|
100
99
|
const { appDirectory, entrypoints } = appContext;
|
|
101
|
-
const serverBundlerChain = useModuleFederationNodeOutput ? (chain)=>{
|
|
102
|
-
chain.target('async-node');
|
|
103
|
-
chain.output.module(false);
|
|
104
|
-
chain.output.chunkFormat('commonjs');
|
|
105
|
-
chain.output.chunkLoading('async-node');
|
|
106
|
-
chain.output.library({
|
|
107
|
-
...chain.output.get('library') || {},
|
|
108
|
-
type: 'commonjs-module'
|
|
109
|
-
});
|
|
110
|
-
} : void 0;
|
|
111
100
|
const useLoadablePlugin = isUseSSRBundle(userConfig) && !isServerEnvironment && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
112
101
|
const outputConfig = {
|
|
113
|
-
module: isServerEnvironment &&
|
|
102
|
+
module: isServerEnvironment && (outputModule || 'workerSSR' === name && userConfig.deploy?.target === 'cloudflare')
|
|
114
103
|
};
|
|
115
104
|
const useLoadableComponents = isUseSSRBundle(userConfig) && checkUseStringSSR(userConfig, appDirectory, entrypoints);
|
|
116
105
|
return mergeEnvironmentConfig(config, {
|
|
@@ -123,13 +112,13 @@ const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
|
|
|
123
112
|
},
|
|
124
113
|
output: outputConfig,
|
|
125
114
|
tools: {
|
|
126
|
-
bundlerChain:
|
|
115
|
+
bundlerChain: useLoadablePlugin ? (chain)=>{
|
|
127
116
|
chain.plugin('loadable').use(loadable_bundler_plugin, [
|
|
128
117
|
{
|
|
129
118
|
filename: LOADABLE_STATS_FILE
|
|
130
119
|
}
|
|
131
120
|
]);
|
|
132
|
-
} : void 0
|
|
121
|
+
} : void 0,
|
|
133
122
|
swc: useLoadableComponents ? {
|
|
134
123
|
jsc: {
|
|
135
124
|
experimental: {
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { HelmetProvider } from "react-helmet-async";
|
|
3
4
|
import { InternalRuntimeContext, RuntimeContext } from "../context/index.mjs";
|
|
4
5
|
function wrapRuntimeContextProvider(App, contextValue) {
|
|
5
|
-
const { isBrowser, initialData, routes, routerFramework, context, routeManifest, routerRuntime, routerInstance, routerHydrationScript, routerMatchedRouteIds, routerServerSnapshot, routerContext, unstable_getBlockNavState, ssrContext, _internalContext, _internalRouterBaseName, ...rest } = contextValue;
|
|
6
|
+
const { isBrowser, initialData, routes, routerFramework, context, routeManifest, routerRuntime, routerInstance, routerHydrationScript, routerMatchedRouteIds, routerServerSnapshot, routerContext, unstable_getBlockNavState, ssrContext, _internalContext, _internalRouterBaseName, _helmetContext, ...rest } = contextValue;
|
|
7
|
+
const internalContextValue = contextValue;
|
|
8
|
+
internalContextValue._helmetContext ??= {};
|
|
6
9
|
const runtimeContextValue = {
|
|
7
10
|
isBrowser,
|
|
8
11
|
initialData,
|
|
@@ -12,10 +15,13 @@ function wrapRuntimeContextProvider(App, contextValue) {
|
|
|
12
15
|
...rest
|
|
13
16
|
};
|
|
14
17
|
return /*#__PURE__*/ jsx(InternalRuntimeContext.Provider, {
|
|
15
|
-
value:
|
|
18
|
+
value: internalContextValue,
|
|
16
19
|
children: /*#__PURE__*/ jsx(RuntimeContext.Provider, {
|
|
17
20
|
value: runtimeContextValue,
|
|
18
|
-
children:
|
|
21
|
+
children: /*#__PURE__*/ jsx(HelmetProvider, {
|
|
22
|
+
context: internalContextValue._helmetContext,
|
|
23
|
+
children: App
|
|
24
|
+
})
|
|
19
25
|
})
|
|
20
26
|
});
|
|
21
27
|
}
|
|
@@ -6,6 +6,9 @@ const RE_BODY_ATTR = /<body[^>]*>/;
|
|
|
6
6
|
const RE_LAST_IN_HEAD = /<\/head>/;
|
|
7
7
|
const RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/;
|
|
8
8
|
const TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/;
|
|
9
|
+
function getHelmetData(runtimeContext) {
|
|
10
|
+
return runtimeContext._helmetContext?.helmet ?? void 0;
|
|
11
|
+
}
|
|
9
12
|
function createReplaceHelemt(helmetData) {
|
|
10
13
|
return helmetData ? (template)=>helmetReplace(template, helmetData) : (tempalte)=>tempalte;
|
|
11
14
|
}
|
|
@@ -36,4 +39,4 @@ function helmetReplace(content, helmetData) {
|
|
|
36
39
|
].reduce((pre, cur)=>pre + (cur.length > 0 ? ` ${cur}${EOL}` : ''), '');
|
|
37
40
|
return safeReplace(result, RE_LAST_IN_HEAD, `${helmetStr}</head>`);
|
|
38
41
|
}
|
|
39
|
-
export { createReplaceHelemt, helmetReplace };
|
|
42
|
+
export { createReplaceHelemt, getHelmetData, helmetReplace };
|
|
@@ -1,10 +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
5
|
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
7
|
-
import { createReplaceHelemt } from "../helmet.mjs";
|
|
6
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
8
7
|
import { buildHtml } from "../shared.mjs";
|
|
9
8
|
import { checkIsNode, safeReplace } from "../utils.mjs";
|
|
10
9
|
import { fileURLToPath as __rspack_fileURLToPath } from "node:url";
|
|
@@ -23,7 +22,7 @@ const checkIsInline = (chunk, enableInline)=>{
|
|
|
23
22
|
};
|
|
24
23
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
25
24
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
|
|
26
|
-
const helmetData =
|
|
25
|
+
const helmetData = getHelmetData(runtimeContext);
|
|
27
26
|
const callbacks = [
|
|
28
27
|
createReplaceHelemt(helmetData),
|
|
29
28
|
(template)=>injectCss(template, entryName, styledComponentsStyleTags)
|
|
@@ -1,10 +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
5
|
import { createFederatedCssLinks } from "../federatedCss.mjs";
|
|
7
|
-
import { createReplaceHelemt } from "../helmet.mjs";
|
|
6
|
+
import { createReplaceHelemt, getHelmetData } from "../helmet.mjs";
|
|
8
7
|
import { buildHtml } from "../shared.mjs";
|
|
9
8
|
import { safeReplace } from "../utils.mjs";
|
|
10
9
|
const checkIsInline = (chunk, enableInline)=>{
|
|
@@ -14,7 +13,7 @@ const checkIsInline = (chunk, enableInline)=>{
|
|
|
14
13
|
};
|
|
15
14
|
async function buildShellBeforeTemplate(beforeAppTemplate, options) {
|
|
16
15
|
const { config, runtimeContext, styledComponentsStyleTags, entryName, moduleFederationCssAssets } = options;
|
|
17
|
-
const helmetData =
|
|
16
|
+
const helmetData = getHelmetData(runtimeContext);
|
|
18
17
|
const callbacks = [
|
|
19
18
|
createReplaceHelemt(helmetData),
|
|
20
19
|
(template)=>injectCss(template, entryName, styledComponentsStyleTags)
|
|
@@ -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();
|
|
@@ -2,24 +2,23 @@ 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";
|
|
14
13
|
import { LoadableCollector } from "./loadable.mjs";
|
|
15
14
|
import { SSRDataCollector } from "./ssrData.mjs";
|
|
16
15
|
const renderString = async (request, serverRoot, options)=>{
|
|
17
|
-
const { resource, runtimeContext, config, onError, onTiming } = options;
|
|
16
|
+
const { resource, runtimeContext: runtimeContext1, config, onError, onTiming } = options;
|
|
18
17
|
const tracer = {
|
|
19
18
|
onError,
|
|
20
19
|
onTiming
|
|
21
20
|
};
|
|
22
|
-
const routerContext =
|
|
21
|
+
const routerContext = runtimeContext1.routerContext;
|
|
23
22
|
const { htmlTemplate, entryName, loadableStats, routeManifest, moduleFederationCssAssets } = resource;
|
|
24
23
|
const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
|
|
25
24
|
const chunkSet = {
|
|
@@ -33,7 +32,7 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
33
32
|
stats: loadableStats,
|
|
34
33
|
nonce: config.nonce,
|
|
35
34
|
routeManifest,
|
|
36
|
-
runtimeContext,
|
|
35
|
+
runtimeContext: runtimeContext1,
|
|
37
36
|
template: htmlTemplate,
|
|
38
37
|
entryName,
|
|
39
38
|
moduleFederationCssAssets,
|
|
@@ -41,10 +40,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
41
40
|
config
|
|
42
41
|
}),
|
|
43
42
|
new SSRDataCollector({
|
|
44
|
-
runtimeContext,
|
|
43
|
+
runtimeContext: runtimeContext1,
|
|
45
44
|
request,
|
|
46
45
|
ssrConfig,
|
|
47
|
-
ssrContext:
|
|
46
|
+
ssrContext: runtimeContext1.ssrContext,
|
|
48
47
|
chunkSet,
|
|
49
48
|
routerContext,
|
|
50
49
|
nonce: config.nonce,
|
|
@@ -57,10 +56,10 @@ const renderString = async (request, serverRoot, options)=>{
|
|
|
57
56
|
chunkSet
|
|
58
57
|
});
|
|
59
58
|
for (const c of extraCollectors)if (c) collectors.unshift(c);
|
|
60
|
-
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(
|
|
59
|
+
const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext1, {
|
|
61
60
|
ssr: true
|
|
62
61
|
}));
|
|
63
|
-
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors,
|
|
62
|
+
const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext1.ssrContext?.htmlModifiers || [], tracer);
|
|
64
63
|
return html;
|
|
65
64
|
};
|
|
66
65
|
async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, { onError, onTiming }) {
|
|
@@ -73,7 +72,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
|
|
|
73
72
|
identifierPrefix: SSR_HYDRATION_ID_PREFIX
|
|
74
73
|
});
|
|
75
74
|
chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
|
|
76
|
-
helmetData =
|
|
75
|
+
helmetData = getHelmetData(runtimeContext);
|
|
77
76
|
const cost = end();
|
|
78
77
|
onTiming(SSRTimings.RENDER_HTML, cost);
|
|
79
78
|
} catch (e) {
|