@bleedingdev/modern-js-runtime 3.8.2-ultramodern.9 → 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/index.js +1 -1
- 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/index.mjs +1 -1
- 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/index.mjs +1 -1
- 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 +11 -10
- package/rstest.config.mts +11 -4
|
@@ -3,247 +3,18 @@ import react from "react";
|
|
|
3
3
|
import { Helmet, HelmetData, HelmetProvider } from "react-helmet-async";
|
|
4
4
|
import { InternalRuntimeContext } from "../core/context/index.mjs";
|
|
5
5
|
import { ensureHelmetContext } from "../core/context/helmetContext.mjs";
|
|
6
|
-
|
|
7
|
-
charSet: 'charset',
|
|
8
|
-
className: 'class',
|
|
9
|
-
contentEditable: 'contenteditable',
|
|
10
|
-
httpEquiv: 'http-equiv',
|
|
11
|
-
hrefLang: 'hreflang',
|
|
12
|
-
itemProp: 'itemprop',
|
|
13
|
-
tabIndex: 'tabindex'
|
|
14
|
-
};
|
|
15
|
-
const CONTENT_PROPERTIES = new Set([
|
|
16
|
-
'innerHTML',
|
|
17
|
-
'cssText'
|
|
18
|
-
]);
|
|
19
|
-
const escapeHtml = (value)=>String(value).replaceAll('&', '&').replaceAll('"', '"').replaceAll('<', '<').replaceAll('>', '>');
|
|
20
|
-
const toHtmlAttributeName = (name)=>ATTRIBUTE_NAME_MAP[name] ?? name;
|
|
21
|
-
const attributesToString = (attributes, includeHelmetAttribute = false)=>{
|
|
22
|
-
const pairs = [];
|
|
23
|
-
if (includeHelmetAttribute) pairs.push('data-rh="true"');
|
|
24
|
-
for (const [name, value] of Object.entries(attributes ?? {})){
|
|
25
|
-
if (false === value || null == value) continue;
|
|
26
|
-
if (CONTENT_PROPERTIES.has(name)) continue;
|
|
27
|
-
const htmlName = toHtmlAttributeName(name);
|
|
28
|
-
if (true === value) pairs.push(htmlName);
|
|
29
|
-
else pairs.push(`${htmlName}="${escapeHtml(value)}"`);
|
|
30
|
-
}
|
|
31
|
-
return pairs.join(' ');
|
|
32
|
-
};
|
|
33
|
-
const createDatum = (tagName, tags)=>({
|
|
34
|
-
toComponent: ()=>[],
|
|
35
|
-
toString: ()=>tags.map((tag)=>{
|
|
36
|
-
const attrs = attributesToString(tag, true);
|
|
37
|
-
if ("script" === tagName && 'string' == typeof tag.innerHTML) return `<script ${attrs}>${tag.innerHTML}</script>`;
|
|
38
|
-
if ('style' === tagName && 'string' == typeof tag.cssText) return `<style ${attrs}>${tag.cssText}</style>`;
|
|
39
|
-
if ("noscript" === tagName && 'string' == typeof tag.innerHTML) return `<noscript ${attrs}>${tag.innerHTML}</noscript>`;
|
|
40
|
-
return `<${tagName} ${attrs}>`;
|
|
41
|
-
}).join('')
|
|
42
|
-
});
|
|
43
|
-
const createAttributeDatum = (attributes)=>({
|
|
44
|
-
toComponent: ()=>attributes,
|
|
45
|
-
toString: ()=>attributesToString(attributes)
|
|
46
|
-
});
|
|
47
|
-
const createTitleDatum = (title, attributes)=>({
|
|
48
|
-
toComponent: ()=>[],
|
|
49
|
-
toString: ()=>{
|
|
50
|
-
if (void 0 === title || '' === title) return '';
|
|
51
|
-
const attrs = attributesToString(attributes, true);
|
|
52
|
-
return `<title ${attrs}>${escapeHtml(title)}</title>`;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
const normalizeHelmetTitle = (title)=>{
|
|
56
|
-
if ('string' == typeof title) return title;
|
|
57
|
-
if (Array.isArray(title)) return title.map((part)=>String(part)).join('');
|
|
58
|
-
};
|
|
59
|
-
const mergeAttributes = (current, next)=>({
|
|
60
|
-
...current,
|
|
61
|
-
...next ?? {}
|
|
62
|
-
});
|
|
63
|
-
const collectChildren = (children, draft)=>{
|
|
64
|
-
react.Children.forEach(children, (child)=>{
|
|
65
|
-
if (!react.isValidElement(child)) return;
|
|
66
|
-
if (child.type === react.Fragment) return void collectChildren(child.props.children, draft);
|
|
67
|
-
if ('string' != typeof child.type) return;
|
|
68
|
-
const { children: nestedChildren, ...props } = child.props;
|
|
69
|
-
if ('title' === child.type) {
|
|
70
|
-
draft.title = react.Children.toArray(nestedChildren).join('');
|
|
71
|
-
draft.titleAttributes = mergeAttributes(draft.titleAttributes, props);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if ('html' === child.type) {
|
|
75
|
-
draft.htmlAttributes = mergeAttributes(draft.htmlAttributes ?? {}, props);
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if ('body' === child.type) {
|
|
79
|
-
draft.bodyAttributes = mergeAttributes(draft.bodyAttributes ?? {}, props);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if ('base' === child.type || 'link' === child.type || 'meta' === child.type || "noscript" === child.type || "script" === child.type || 'style' === child.type) {
|
|
83
|
-
const tag = {
|
|
84
|
-
...props
|
|
85
|
-
};
|
|
86
|
-
if (("script" === child.type || 'style' === child.type || "noscript" === child.type) && 'string' == typeof nestedChildren) tag['style' === child.type ? 'cssText' : 'innerHTML'] = nestedChildren;
|
|
87
|
-
draft[child.type].push(tag);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
const createHelmetRecord = (props)=>{
|
|
92
|
-
const draft = {
|
|
93
|
-
base: [
|
|
94
|
-
...void 0 !== props.base ? [
|
|
95
|
-
props.base
|
|
96
|
-
] : []
|
|
97
|
-
],
|
|
98
|
-
bodyAttributes: props.bodyAttributes,
|
|
99
|
-
htmlAttributes: props.htmlAttributes,
|
|
100
|
-
link: [
|
|
101
|
-
...props.link ?? []
|
|
102
|
-
],
|
|
103
|
-
meta: [
|
|
104
|
-
...props.meta ?? []
|
|
105
|
-
],
|
|
106
|
-
noscript: [
|
|
107
|
-
...props.noscript ?? []
|
|
108
|
-
],
|
|
109
|
-
script: [
|
|
110
|
-
...props.script ?? []
|
|
111
|
-
],
|
|
112
|
-
style: [
|
|
113
|
-
...props.style ?? []
|
|
114
|
-
],
|
|
115
|
-
title: normalizeHelmetTitle(props.title),
|
|
116
|
-
titleAttributes: props.titleAttributes ?? {}
|
|
117
|
-
};
|
|
118
|
-
collectChildren(props.children, draft);
|
|
119
|
-
return {
|
|
120
|
-
...draft,
|
|
121
|
-
titleTemplate: props.titleTemplate,
|
|
122
|
-
defaultTitle: props.defaultTitle
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
const getInnermostDefined = (records, pick)=>{
|
|
126
|
-
for(let i = records.length - 1; i >= 0; i -= 1){
|
|
127
|
-
const value = pick(records[i]);
|
|
128
|
-
if (void 0 !== value) return value;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
const deriveTitle = (records)=>{
|
|
132
|
-
const innermostTitle = getInnermostDefined(records, (record)=>record.title);
|
|
133
|
-
const innermostTemplate = getInnermostDefined(records, (record)=>record.titleTemplate);
|
|
134
|
-
if (innermostTemplate && innermostTitle) return innermostTemplate.replaceAll('%s', innermostTitle);
|
|
135
|
-
return innermostTitle || getInnermostDefined(records, (record)=>record.defaultTitle);
|
|
136
|
-
};
|
|
137
|
-
const mergeRecordAttributes = (records, pick)=>records.reduce((merged, record)=>mergeAttributes(merged, pick(record)), {});
|
|
138
|
-
const deriveBaseTags = (records)=>{
|
|
139
|
-
for(let i = records.length - 1; i >= 0; i -= 1){
|
|
140
|
-
const tags = records[i].base;
|
|
141
|
-
for(let j = tags.length - 1; j >= 0; j -= 1)if (tags[j].href) return [
|
|
142
|
-
tags[j]
|
|
143
|
-
];
|
|
144
|
-
}
|
|
145
|
-
return [];
|
|
146
|
-
};
|
|
147
|
-
const TAG_PRIMARY_ATTRIBUTES = {
|
|
148
|
-
link: [
|
|
149
|
-
'rel',
|
|
150
|
-
'href'
|
|
151
|
-
],
|
|
152
|
-
meta: [
|
|
153
|
-
'name',
|
|
154
|
-
'charset',
|
|
155
|
-
'http-equiv',
|
|
156
|
-
'property',
|
|
157
|
-
'itemprop'
|
|
158
|
-
],
|
|
159
|
-
noscript: [
|
|
160
|
-
'innerHTML'
|
|
161
|
-
],
|
|
162
|
-
script: [
|
|
163
|
-
'src',
|
|
164
|
-
'innerHTML'
|
|
165
|
-
],
|
|
166
|
-
style: [
|
|
167
|
-
'cssText'
|
|
168
|
-
]
|
|
169
|
-
};
|
|
170
|
-
const getPrimaryAttribute = (tagName, tag)=>{
|
|
171
|
-
const candidates = TAG_PRIMARY_ATTRIBUTES[tagName];
|
|
172
|
-
let selectedKey;
|
|
173
|
-
let selectedValue;
|
|
174
|
-
for (const [key, value] of Object.entries(tag)){
|
|
175
|
-
const normalizedKey = CONTENT_PROPERTIES.has(key) ? key : toHtmlAttributeName(key).toLowerCase();
|
|
176
|
-
if (candidates.includes(normalizedKey)) {
|
|
177
|
-
if ('rel' !== selectedKey || 'canonical' !== String(selectedValue).toLowerCase()) {
|
|
178
|
-
if ('rel' !== normalizedKey || 'stylesheet' !== String(value).toLowerCase()) {
|
|
179
|
-
selectedKey = normalizedKey;
|
|
180
|
-
selectedValue = value;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
if (void 0 === selectedKey || !selectedValue) return;
|
|
186
|
-
return {
|
|
187
|
-
key: selectedKey,
|
|
188
|
-
value: String(selectedValue).toLowerCase()
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
|
-
const dedupeTags = (tagName, records)=>{
|
|
192
|
-
const approvedValues = new Map();
|
|
193
|
-
const approved = [];
|
|
194
|
-
for(let i = records.length - 1; i >= 0; i -= 1){
|
|
195
|
-
const instanceValues = new Map();
|
|
196
|
-
const kept = [];
|
|
197
|
-
for (const tag of records[i][tagName]){
|
|
198
|
-
const primary = getPrimaryAttribute(tagName, tag);
|
|
199
|
-
if (void 0 === primary) continue;
|
|
200
|
-
if (approvedValues.get(primary.key)?.has(primary.value)) continue;
|
|
201
|
-
let seen = instanceValues.get(primary.key);
|
|
202
|
-
if (void 0 === seen) {
|
|
203
|
-
seen = new Set();
|
|
204
|
-
instanceValues.set(primary.key, seen);
|
|
205
|
-
}
|
|
206
|
-
seen.add(primary.value);
|
|
207
|
-
kept.push(tag);
|
|
208
|
-
}
|
|
209
|
-
approved.unshift(...kept);
|
|
210
|
-
for (const [key, values] of instanceValues){
|
|
211
|
-
const target = approvedValues.get(key);
|
|
212
|
-
if (void 0 === target) approvedValues.set(key, values);
|
|
213
|
-
else for (const value of values)target.add(value);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return approved;
|
|
217
|
-
};
|
|
218
|
-
const deriveHelmetServerState = (records)=>({
|
|
219
|
-
base: createDatum('base', deriveBaseTags(records)),
|
|
220
|
-
bodyAttributes: createAttributeDatum(mergeRecordAttributes(records, (record)=>record.bodyAttributes)),
|
|
221
|
-
htmlAttributes: createAttributeDatum(mergeRecordAttributes(records, (record)=>record.htmlAttributes)),
|
|
222
|
-
link: createDatum('link', dedupeTags('link', records)),
|
|
223
|
-
meta: createDatum('meta', dedupeTags('meta', records)),
|
|
224
|
-
noscript: createDatum("noscript", dedupeTags("noscript", records)),
|
|
225
|
-
priority: createDatum('meta', []),
|
|
226
|
-
script: createDatum("script", dedupeTags("script", records)),
|
|
227
|
-
style: createDatum('style', dedupeTags('style', records)),
|
|
228
|
-
title: createTitleDatum(deriveTitle(records), mergeRecordAttributes(records, (record)=>record.titleAttributes))
|
|
229
|
-
});
|
|
230
|
-
const serverHelmetRecords = new WeakMap();
|
|
6
|
+
import * as __rspack_external__modern_js_runtime_extensions_ce611631 from "@modern-js/runtime-extensions";
|
|
231
7
|
const collectServerHelmet = (runtimeContext, props)=>{
|
|
232
8
|
const helmetContext = ensureHelmetContext(runtimeContext);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
records.push(createHelmetRecord(props));
|
|
239
|
-
helmetContext.helmet = deriveHelmetServerState(records);
|
|
9
|
+
const createRecord = ()=>__rspack_external__modern_js_runtime_extensions_ce611631.createHelmetRecord(react, props);
|
|
10
|
+
const deriveState = (records)=>__rspack_external__modern_js_runtime_extensions_ce611631.deriveHelmetServerState(react, records);
|
|
11
|
+
const marker = __rspack_external__modern_js_runtime_extensions_ce611631.collectHeadState(runtimeContext, createRecord, deriveState, helmetContext);
|
|
12
|
+
if (void 0 !== marker) return marker;
|
|
13
|
+
helmetContext.helmet = __rspack_external__modern_js_runtime_extensions_ce611631.collectImmediateHelmetState(react, helmetContext, createRecord());
|
|
240
14
|
};
|
|
241
15
|
const head_Helmet = (props)=>{
|
|
242
16
|
const runtimeContext = react.useContext(InternalRuntimeContext);
|
|
243
|
-
if (null !== runtimeContext && false === runtimeContext.isBrowser)
|
|
244
|
-
collectServerHelmet(runtimeContext, props);
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
17
|
+
if (null !== runtimeContext && false === runtimeContext.isBrowser) return __rspack_external__modern_js_runtime_extensions_ce611631.renderHeadMarker(react, collectServerHelmet(runtimeContext, props));
|
|
247
18
|
return react.createElement(Helmet, props);
|
|
248
19
|
};
|
|
249
20
|
const head = {
|
|
@@ -253,4 +24,4 @@ const head = {
|
|
|
253
24
|
};
|
|
254
25
|
const exports_head = head;
|
|
255
26
|
export default exports_head;
|
|
256
|
-
export {
|
|
27
|
+
export { head_Helmet as Helmet };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { selectResolvedSurface } from "@modern-js/utils/universal";
|
|
1
2
|
import { createDiscoveryError, deliveryUnitResolutionKey } from "./surface-resolution-types.mjs";
|
|
2
3
|
const LAST_KNOWN_GOOD_PROVIDER_NAME = 'last-known-good';
|
|
3
4
|
function createInMemoryLkgStorage() {
|
|
@@ -31,16 +32,38 @@ function staleRecordResult(ref, env, ageMs, maxStaleMs) {
|
|
|
31
32
|
})
|
|
32
33
|
};
|
|
33
34
|
}
|
|
35
|
+
function resolutionAnswersRef(resolved, ref) {
|
|
36
|
+
const selected = selectResolvedSurface(resolved, ref);
|
|
37
|
+
return selected.ok && selected.surface.servedMajor === ref.major;
|
|
38
|
+
}
|
|
34
39
|
function createLastKnownGoodProvider(options) {
|
|
35
40
|
const storage = options.storage ?? createInMemoryLkgStorage();
|
|
36
41
|
const now = options.now ?? (()=>Math.floor(performance.timeOrigin + performance.now()));
|
|
37
42
|
const maxStaleMs = options.freshness?.maxStaleMs;
|
|
43
|
+
let nextResolutionGeneration = 0;
|
|
44
|
+
const latestCacheableGenerationByKey = new Map();
|
|
45
|
+
const writeTailByKey = new Map();
|
|
46
|
+
async function cacheCompatibleResolution(key, generation, record) {
|
|
47
|
+
latestCacheableGenerationByKey.set(key, Math.max(latestCacheableGenerationByKey.get(key) ?? 0, generation));
|
|
48
|
+
const previous = writeTailByKey.get(key) ?? Promise.resolve();
|
|
49
|
+
const write = previous.catch(()=>void 0).then(async ()=>{
|
|
50
|
+
if (latestCacheableGenerationByKey.get(key) !== generation) return;
|
|
51
|
+
await storage.write(key, record);
|
|
52
|
+
});
|
|
53
|
+
writeTailByKey.set(key, write);
|
|
54
|
+
try {
|
|
55
|
+
await write;
|
|
56
|
+
} finally{
|
|
57
|
+
if (writeTailByKey.get(key) === write) writeTailByKey.delete(key);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
38
60
|
async function serveFromCache(ref, env, fallback) {
|
|
39
61
|
const key = deliveryUnitResolutionKey(ref.unitId, env);
|
|
40
62
|
const cached = await storage.read(key);
|
|
41
63
|
if (!cached) return fallback();
|
|
42
64
|
if (cached.major !== ref.major) return fallback();
|
|
43
|
-
if (
|
|
65
|
+
if (!resolutionAnswersRef(cached.resolved, ref)) return fallback();
|
|
66
|
+
if ('compatible' !== cached.resolved.compatibility.status) return fallback();
|
|
44
67
|
const ageMs = now() - cached.storedAt;
|
|
45
68
|
if (void 0 !== maxStaleMs && ageMs > maxStaleMs) return staleRecordResult(ref, env, ageMs, maxStaleMs);
|
|
46
69
|
return {
|
|
@@ -52,6 +75,7 @@ function createLastKnownGoodProvider(options) {
|
|
|
52
75
|
name: LAST_KNOWN_GOOD_PROVIDER_NAME,
|
|
53
76
|
async resolve (ref, env) {
|
|
54
77
|
const key = deliveryUnitResolutionKey(ref.unitId, env);
|
|
78
|
+
const generation = ++nextResolutionGeneration;
|
|
55
79
|
let resolution;
|
|
56
80
|
try {
|
|
57
81
|
resolution = await options.provider.resolve(ref, env);
|
|
@@ -68,7 +92,16 @@ function createLastKnownGoodProvider(options) {
|
|
|
68
92
|
const failure = resolution;
|
|
69
93
|
return serveFromCache(ref, env, ()=>failure);
|
|
70
94
|
}
|
|
71
|
-
if (
|
|
95
|
+
if (!resolutionAnswersRef(resolution.unit, ref)) return serveFromCache(ref, env, ()=>({
|
|
96
|
+
ok: false,
|
|
97
|
+
error: createDiscoveryError('identity-mismatch', ref, `Provider ${options.provider.name} returned a delivery unit that does not answer the requested surface.`, {
|
|
98
|
+
env,
|
|
99
|
+
provider: options.provider.name,
|
|
100
|
+
recordUnitId: resolution.unit.unitId,
|
|
101
|
+
availableSurfaces: resolution.unit.surfaces.map((surface)=>surface.surfaceId)
|
|
102
|
+
})
|
|
103
|
+
}));
|
|
104
|
+
if ('compatible' === resolution.unit.compatibility.status) await cacheCompatibleResolution(key, generation, {
|
|
72
105
|
resolved: resolution.unit,
|
|
73
106
|
storedAt: now(),
|
|
74
107
|
major: ref.major
|
|
@@ -1,38 +1,11 @@
|
|
|
1
1
|
import node_path from "node:path";
|
|
2
|
-
import { setTimeout as promises_setTimeout } from "node:timers/promises";
|
|
3
2
|
import { fs } from "@modern-js/utils";
|
|
4
3
|
import { cloneDeep } from "@modern-js/utils/lodash";
|
|
5
|
-
const lockPollIntervalMs = 25;
|
|
6
|
-
const staleLockAgeMs = 120000;
|
|
7
4
|
const pendingUpdates = new Map();
|
|
8
5
|
let tempFileCounter = 0;
|
|
9
|
-
async function acquireSpecLock(specPath) {
|
|
10
|
-
const lockDir = `${specPath}.lock`;
|
|
11
|
-
await fs.ensureDir(node_path.dirname(specPath));
|
|
12
|
-
while(true){
|
|
13
|
-
try {
|
|
14
|
-
await fs.mkdir(lockDir);
|
|
15
|
-
return async ()=>{
|
|
16
|
-
await fs.remove(lockDir);
|
|
17
|
-
};
|
|
18
|
-
} catch (error) {
|
|
19
|
-
if ('EEXIST' !== error.code) throw error;
|
|
20
|
-
}
|
|
21
|
-
try {
|
|
22
|
-
const stat = await fs.stat(lockDir);
|
|
23
|
-
if (performance.timeOrigin + performance.now() - stat.mtimeMs > staleLockAgeMs) {
|
|
24
|
-
await fs.remove(lockDir);
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if ('ENOENT' !== error.code) throw error;
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
await promises_setTimeout(lockPollIntervalMs);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
6
|
async function writeJSONAtomically(filePath, value) {
|
|
35
7
|
const directory = node_path.dirname(filePath);
|
|
8
|
+
await fs.ensureDir(directory);
|
|
36
9
|
const tempPath = node_path.join(directory, `.${node_path.basename(filePath)}.${process.pid}.${tempFileCounter += 1}.tmp`);
|
|
37
10
|
try {
|
|
38
11
|
await fs.writeFile(tempPath, `${JSON.stringify(value)}\n`);
|
|
@@ -47,16 +20,12 @@ async function updateNestedRoutesSpec(specPath, nextRoutes) {
|
|
|
47
20
|
const nextRoutesSnapshot = cloneDeep(nextRoutes);
|
|
48
21
|
const previousUpdate = pendingUpdates.get(resolvedSpecPath) ?? Promise.resolve();
|
|
49
22
|
const currentUpdate = previousUpdate.catch(()=>void 0).then(async ()=>{
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
} finally{
|
|
58
|
-
await releaseLock();
|
|
59
|
-
}
|
|
23
|
+
const existingRoutes = await fs.pathExists(resolvedSpecPath) ? await fs.readJSON(resolvedSpecPath) : {};
|
|
24
|
+
const mergedRoutes = {
|
|
25
|
+
...existingRoutes,
|
|
26
|
+
...nextRoutesSnapshot
|
|
27
|
+
};
|
|
28
|
+
await writeJSONAtomically(resolvedSpecPath, mergedRoutes);
|
|
60
29
|
});
|
|
61
30
|
pendingUpdates.set(resolvedSpecPath, currentUpdate);
|
|
62
31
|
try {
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { getNavigationWarmupCacheKey } from "@modern-js/runtime-extensions";
|
|
3
4
|
import { Link as router_Link, NavLink, matchRoutes, useHref, useMatches, useResolvedPath } from "@modern-js/runtime-utils/router";
|
|
4
5
|
import react, { useContext, useMemo } from "react";
|
|
5
6
|
import { InternalRuntimeContext } from "../../core/context/index.mjs";
|
|
7
|
+
import { __webpack_require__ } from "../../rslib-runtime.mjs";
|
|
8
|
+
const getWebpackChunkLoader = ()=>__webpack_chunk_load__;
|
|
9
|
+
const getWebpackPublicPath = ()=>{
|
|
10
|
+
try {
|
|
11
|
+
return __webpack_require__.p || '';
|
|
12
|
+
} catch {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
};
|
|
6
16
|
function composeEventHandlers(theirHandler, ourHandler) {
|
|
7
17
|
return (event)=>{
|
|
8
18
|
theirHandler?.(event);
|
|
@@ -41,8 +51,7 @@ const runNextWarmup = ()=>{
|
|
|
41
51
|
const task = warmupQueue.shift();
|
|
42
52
|
if (task.cancelled) continue;
|
|
43
53
|
activeWarmups += 1;
|
|
44
|
-
task.run().catch((
|
|
45
|
-
console.error(error);
|
|
54
|
+
task.run().catch(()=>{
|
|
46
55
|
warmupCache.delete(task.key);
|
|
47
56
|
}).finally(()=>{
|
|
48
57
|
activeWarmups -= 1;
|
|
@@ -64,6 +73,7 @@ const scheduleWarmup = (key, run)=>{
|
|
|
64
73
|
runNextWarmup();
|
|
65
74
|
return ()=>{
|
|
66
75
|
task.cancelled = true;
|
|
76
|
+
if (warmupQueue.includes(task)) warmupCache.delete(task.key);
|
|
67
77
|
};
|
|
68
78
|
};
|
|
69
79
|
const setRef = (ref, value)=>{
|
|
@@ -147,16 +157,17 @@ function usePrefetchBehavior(prefetch, preload, theirElementProps) {
|
|
|
147
157
|
setViewportElement
|
|
148
158
|
];
|
|
149
159
|
}
|
|
150
|
-
async function loadRouteModule(route, routeAssets) {
|
|
160
|
+
async function loadRouteModule(route, routeAssets, chunkLoader) {
|
|
151
161
|
const routeId = route.id;
|
|
152
162
|
if (!routeId) return;
|
|
153
163
|
if (!routeAssets[routeId]) return;
|
|
154
164
|
const { chunkIds } = routeAssets[routeId];
|
|
155
165
|
if (!chunkIds) return;
|
|
156
166
|
try {
|
|
157
|
-
await Promise.all(chunkIds.map((chunkId)=>
|
|
167
|
+
await Promise.all(chunkIds.map((chunkId)=>chunkLoader(chunkId)));
|
|
158
168
|
} catch (error) {
|
|
159
169
|
console.error(error);
|
|
170
|
+
throw error;
|
|
160
171
|
}
|
|
161
172
|
}
|
|
162
173
|
const getRequestUrl = (pathname, routeId)=>{
|
|
@@ -189,22 +200,31 @@ const PrefetchPageLinks = ({ path, includeData })=>{
|
|
|
189
200
|
pathname,
|
|
190
201
|
routes
|
|
191
202
|
]);
|
|
203
|
+
const chunkLoader = getWebpackChunkLoader();
|
|
204
|
+
const routeAssetGeneration = JSON.stringify([
|
|
205
|
+
getWebpackPublicPath(),
|
|
206
|
+
matches?.map(({ route: { id } })=>[
|
|
207
|
+
id,
|
|
208
|
+
routeAssets?.[id]?.chunkIds
|
|
209
|
+
])
|
|
210
|
+
]);
|
|
192
211
|
react.useEffect(()=>{
|
|
193
|
-
if (!allowNetworkWarmup || !Array.isArray(matches) || !routeAssets || !
|
|
212
|
+
if (!allowNetworkWarmup || !Array.isArray(matches) || !routeAssets || !chunkLoader) return;
|
|
194
213
|
const cancellations = matches.map((match)=>{
|
|
195
214
|
const routeId = match.route.id;
|
|
196
215
|
const routeAsset = routeId ? routeAssets[routeId] : void 0;
|
|
197
216
|
const chunkIds = routeAsset?.chunkIds;
|
|
198
217
|
if (!routeId || !Array.isArray(chunkIds) || 0 === chunkIds.length) return ()=>{};
|
|
199
|
-
return scheduleWarmup(`route-module:${routeId}:${chunkIds.join(',')}
|
|
218
|
+
return scheduleWarmup(getNavigationWarmupCacheKey(context, chunkLoader, getWebpackPublicPath(), `route-module:${routeId}:${chunkIds.join(',')}`), ()=>loadRouteModule(match.route, routeAssets, chunkLoader));
|
|
200
219
|
});
|
|
201
220
|
return ()=>{
|
|
202
221
|
cancellations.forEach((cancel)=>cancel());
|
|
203
222
|
};
|
|
204
223
|
}, [
|
|
205
224
|
allowNetworkWarmup,
|
|
206
|
-
|
|
207
|
-
|
|
225
|
+
chunkLoader,
|
|
226
|
+
context,
|
|
227
|
+
routeAssetGeneration
|
|
208
228
|
]);
|
|
209
229
|
if (!allowNetworkWarmup || !includeData || !window._SSR_DATA) return null;
|
|
210
230
|
return /*#__PURE__*/ jsx(PrefetchDataLinks, {
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
const handleRSCRedirect = (headers, basename, status)=>{
|
|
2
2
|
const newHeaders = new Headers(headers);
|
|
3
3
|
let redirectUrl = headers.get('Location');
|
|
4
|
-
if (
|
|
4
|
+
if (!redirectUrl || ![
|
|
5
|
+
301,
|
|
6
|
+
302,
|
|
7
|
+
303,
|
|
8
|
+
307,
|
|
9
|
+
308
|
|
10
|
+
].includes(status) || !URL.canParse(redirectUrl, 'http://localhost')) return new Response(null, {
|
|
11
|
+
status,
|
|
12
|
+
headers: newHeaders
|
|
13
|
+
});
|
|
14
|
+
if ('/' !== basename && redirectUrl.startsWith(basename)) redirectUrl = redirectUrl.slice(basename.length) || '/';
|
|
5
15
|
newHeaders.set('X-Modernjs-Redirect', redirectUrl);
|
|
6
16
|
newHeaders.set('X-Modernjs-BaseUrl', basename);
|
|
7
17
|
newHeaders.delete('Location');
|
|
@@ -176,12 +176,14 @@ function serializeRouteErrorResponse(error) {
|
|
|
176
176
|
status: error.status,
|
|
177
177
|
statusText: 'Internal Server Error',
|
|
178
178
|
data: 'Unexpected Server Error',
|
|
179
|
+
internal: error.internal,
|
|
179
180
|
__type: 'RouteErrorResponse'
|
|
180
181
|
};
|
|
181
182
|
return {
|
|
182
183
|
status: error.status,
|
|
183
184
|
statusText: error.statusText,
|
|
184
185
|
data: error.data,
|
|
186
|
+
internal: error.internal,
|
|
185
187
|
__type: 'RouteErrorResponse'
|
|
186
188
|
};
|
|
187
189
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var __webpack_require__ = {};
|
|
2
|
+
(()=>{
|
|
3
|
+
var scriptUrl;
|
|
4
|
+
if ("string" == typeof import.meta.url) scriptUrl = import.meta.url;
|
|
5
|
+
if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");
|
|
6
|
+
scriptUrl = scriptUrl.replace(/^blob:/, "").replace(/#.*$/, "").replace(/\?.*$/, "").replace(/\/[^\/]+$/, "/");
|
|
7
|
+
__webpack_require__.p = scriptUrl;
|
|
8
|
+
})();
|
|
9
|
+
export { __webpack_require__ };
|
|
@@ -109,7 +109,7 @@ const ssrBuilderPlugin = (modernAPI, outputModule, exportLoadablePath)=>({
|
|
|
109
109
|
'process.env.MODERN_ENABLE_HYDRATION': JSON.stringify(isUseSSRBundle(userConfig)),
|
|
110
110
|
'process.env.MODERN_ENABLE_RSC': JSON.stringify(Boolean(userConfig.server?.rsc)),
|
|
111
111
|
__MODERN_ENABLE_RSC__: JSON.stringify(Boolean(userConfig.server?.rsc)),
|
|
112
|
-
'process.env.MODERN_MF_APP_SSR': JSON.stringify(isModuleFederationAppSSR)
|
|
112
|
+
'process.env.MODERN_MF_APP_SSR': JSON.stringify(String(isModuleFederationAppSSR))
|
|
113
113
|
}
|
|
114
114
|
},
|
|
115
115
|
output: outputConfig,
|
|
@@ -13,7 +13,7 @@ class LoadablePlugin {
|
|
|
13
13
|
const chunkLoadingGlobal = this.opts.chunkLoadingGlobal ?? compiler.options.output.chunkLoadingGlobal ?? DEFAULT_CHUNK_LOADING_GLOBAL;
|
|
14
14
|
compiler.options.output.chunkLoadingGlobal = chunkLoadingGlobal;
|
|
15
15
|
new compiler.webpack.DefinePlugin({
|
|
16
|
-
|
|
16
|
+
__MODERN_CHUNK_LOADING_GLOBAL__: JSON.stringify(chunkLoadingGlobal)
|
|
17
17
|
}).apply(compiler);
|
|
18
18
|
if (this.opts.outputAsset || this.opts.writeToDisk) compiler.hooks.make.tap(LoadablePlugin.name, (compilation)=>{
|
|
19
19
|
compilation.hooks.processAssets.tap({
|
|
@@ -6,18 +6,8 @@ import { hydrateRoot } from "react-dom/client";
|
|
|
6
6
|
import { RenderLevel } from "../constants.mjs";
|
|
7
7
|
import { wrapRuntimeContextProvider } from "../react/wrapper.mjs";
|
|
8
8
|
import { WithCallback } from "./withCallback.mjs";
|
|
9
|
-
const runtimeProcess = Reflect.get(globalThis, 'process');
|
|
10
|
-
const runtimeEnvironment = null !== runtimeProcess && 'object' == typeof runtimeProcess ? Reflect.get(runtimeProcess, 'env') : void 0;
|
|
11
|
-
const readRuntimeEnvironment = (name)=>{
|
|
12
|
-
if (null === runtimeEnvironment || 'object' != typeof runtimeEnvironment) return;
|
|
13
|
-
const value = Reflect.get(runtimeEnvironment, name);
|
|
14
|
-
return 'string' == typeof value ? value : void 0;
|
|
15
|
-
};
|
|
16
9
|
const loadableReadyOptions = {
|
|
17
|
-
chunkLoadingGlobal:
|
|
18
|
-
const configured = readRuntimeEnvironment('MODERN_CHUNK_LOADING_GLOBAL');
|
|
19
|
-
return void 0 === configured || '' === configured ? '__LOADABLE_LOADED_CHUNKS__' : configured;
|
|
20
|
-
})()
|
|
10
|
+
chunkLoadingGlobal: 'string' == typeof __MODERN_CHUNK_LOADING_GLOBAL__ && '' !== __MODERN_CHUNK_LOADING_GLOBAL__ ? __MODERN_CHUNK_LOADING_GLOBAL__ : '__LOADABLE_LOADED_CHUNKS__'
|
|
21
11
|
};
|
|
22
12
|
function hydrateWithReact(App, rootElement) {
|
|
23
13
|
return Promise.resolve(hydrateRoot(rootElement, App, {
|
|
@@ -5,8 +5,13 @@ function getStore(context) {
|
|
|
5
5
|
}
|
|
6
6
|
function ensureStore(context) {
|
|
7
7
|
const target = context;
|
|
8
|
-
target[EXTENSIONS_SLOT]
|
|
9
|
-
|
|
8
|
+
const store = target[EXTENSIONS_SLOT] ?? new Map();
|
|
9
|
+
Object.defineProperty(target, EXTENSIONS_SLOT, {
|
|
10
|
+
configurable: true,
|
|
11
|
+
enumerable: true,
|
|
12
|
+
value: store
|
|
13
|
+
});
|
|
14
|
+
return store;
|
|
10
15
|
}
|
|
11
16
|
function stripRuntimeContextExtensions(context) {
|
|
12
17
|
delete context[EXTENSIONS_SLOT];
|
|
@@ -4,8 +4,19 @@ const ROUTE_MANIFEST = '_MODERNJS_ROUTE_MANIFEST';
|
|
|
4
4
|
const InternalRuntimeContext = createContext({});
|
|
5
5
|
const getInitialContext = (isBrowser = true, routeManifest)=>{
|
|
6
6
|
const requestContext = {
|
|
7
|
-
request: {
|
|
8
|
-
|
|
7
|
+
request: {
|
|
8
|
+
params: {},
|
|
9
|
+
pathname: '',
|
|
10
|
+
query: {},
|
|
11
|
+
headers: {},
|
|
12
|
+
host: '',
|
|
13
|
+
url: ''
|
|
14
|
+
},
|
|
15
|
+
response: {
|
|
16
|
+
setHeader () {},
|
|
17
|
+
status () {},
|
|
18
|
+
locals: {}
|
|
19
|
+
}
|
|
9
20
|
};
|
|
10
21
|
return {
|
|
11
22
|
isBrowser,
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { HelmetProvider } from "react-helmet-async";
|
|
4
|
-
import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc } from "../context/index.mjs";
|
|
4
|
+
import { InternalRuntimeContext, RuntimeContext, getGlobalEnableRsc, getInitialContext } from "../context/index.mjs";
|
|
5
5
|
import { stripRuntimeContextExtensions } from "../context/extensions.mjs";
|
|
6
6
|
import { ensureHelmetContext } from "../context/helmetContext.mjs";
|
|
7
7
|
function createRscSafeRequestContext(ssrContext) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const requestContext = getInitialContext().requestContext;
|
|
9
|
+
Object.defineProperty(requestContext.response, 'setHeader', {
|
|
10
|
+
enumerable: false
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(requestContext.response, 'status', {
|
|
13
|
+
enumerable: false
|
|
14
|
+
});
|
|
15
|
+
if (void 0 === ssrContext) return requestContext;
|
|
14
16
|
const { request, response } = ssrContext;
|
|
15
17
|
return {
|
|
16
18
|
request: {
|
|
@@ -30,9 +32,9 @@ function createRscSafeRequestContext(ssrContext) {
|
|
|
30
32
|
host: request.host,
|
|
31
33
|
referer: request.referer
|
|
32
34
|
},
|
|
33
|
-
response: {
|
|
35
|
+
response: Object.assign(requestContext.response, {
|
|
34
36
|
locals: response.locals ?? {}
|
|
35
|
-
}
|
|
37
|
+
})
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
function wrapRuntimeContextProvider(App, contextValue) {
|