@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.
Files changed (84) hide show
  1. package/dist/cjs/cli/ssr/index.js +1 -1
  2. package/dist/cjs/cli/ssr/loadable-bundler-plugin.js +1 -1
  3. package/dist/cjs/core/browser/hydrate.js +1 -11
  4. package/dist/cjs/core/context/extensions.js +7 -2
  5. package/dist/cjs/core/context/runtime.js +13 -2
  6. package/dist/cjs/core/react/wrapper.js +10 -8
  7. package/dist/cjs/core/server/federatedCss.js +7 -4
  8. package/dist/cjs/core/server/helmet.js +2 -0
  9. package/dist/cjs/core/server/requestResponse.js +35 -11
  10. package/dist/cjs/core/server/routerCleanup.js +10 -1
  11. package/dist/cjs/core/server/scriptOrder.js +5 -2
  12. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +1 -1
  13. package/dist/cjs/core/server/stream/createReadableStream.js +18 -5
  14. package/dist/cjs/core/server/stream/createReadableStream.worker.js +10 -2
  15. package/dist/cjs/core/server/stream/deferredScript.js +2 -1
  16. package/dist/cjs/core/server/string/index.js +4 -0
  17. package/dist/cjs/core/server/string/loadable.js +11 -2
  18. package/dist/cjs/core/server/utils.js +12 -5
  19. package/dist/cjs/exports/head.js +7 -242
  20. package/dist/cjs/module-federation/last-known-good.js +35 -2
  21. package/dist/cjs/router/cli/nestedRoutesSpec.js +7 -38
  22. package/dist/cjs/router/runtime/PrefetchLink.js +55 -8
  23. package/dist/cjs/router/runtime/redirect.js +11 -1
  24. package/dist/cjs/router/runtime/utils.js +2 -0
  25. package/dist/esm/cli/ssr/index.mjs +1 -1
  26. package/dist/esm/cli/ssr/loadable-bundler-plugin.mjs +1 -1
  27. package/dist/esm/core/browser/hydrate.mjs +1 -11
  28. package/dist/esm/core/context/extensions.mjs +7 -2
  29. package/dist/esm/core/context/runtime.mjs +13 -2
  30. package/dist/esm/core/react/wrapper.mjs +11 -9
  31. package/dist/esm/core/server/federatedCss.mjs +7 -4
  32. package/dist/esm/core/server/helmet.mjs +2 -0
  33. package/dist/esm/core/server/requestResponse.mjs +35 -11
  34. package/dist/esm/core/server/routerCleanup.mjs +10 -1
  35. package/dist/esm/core/server/scriptOrder.mjs +6 -3
  36. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +2 -2
  37. package/dist/esm/core/server/stream/createReadableStream.mjs +16 -3
  38. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +10 -2
  39. package/dist/esm/core/server/stream/deferredScript.mjs +2 -1
  40. package/dist/esm/core/server/string/index.mjs +4 -0
  41. package/dist/esm/core/server/string/loadable.mjs +11 -2
  42. package/dist/esm/core/server/utils.mjs +8 -4
  43. package/dist/esm/exports/head.mjs +8 -237
  44. package/dist/esm/module-federation/last-known-good.mjs +35 -2
  45. package/dist/esm/router/cli/nestedRoutesSpec.mjs +7 -38
  46. package/dist/esm/router/runtime/PrefetchLink.mjs +28 -8
  47. package/dist/esm/router/runtime/redirect.mjs +11 -1
  48. package/dist/esm/router/runtime/utils.mjs +2 -0
  49. package/dist/esm/rslib-runtime.mjs +9 -0
  50. package/dist/esm-node/cli/ssr/index.mjs +1 -1
  51. package/dist/esm-node/cli/ssr/loadable-bundler-plugin.mjs +1 -1
  52. package/dist/esm-node/core/browser/hydrate.mjs +1 -11
  53. package/dist/esm-node/core/context/extensions.mjs +7 -2
  54. package/dist/esm-node/core/context/runtime.mjs +13 -2
  55. package/dist/esm-node/core/react/wrapper.mjs +11 -9
  56. package/dist/esm-node/core/server/federatedCss.mjs +7 -4
  57. package/dist/esm-node/core/server/helmet.mjs +2 -0
  58. package/dist/esm-node/core/server/requestResponse.mjs +35 -11
  59. package/dist/esm-node/core/server/routerCleanup.mjs +10 -1
  60. package/dist/esm-node/core/server/scriptOrder.mjs +6 -3
  61. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +2 -2
  62. package/dist/esm-node/core/server/stream/createReadableStream.mjs +16 -3
  63. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +10 -2
  64. package/dist/esm-node/core/server/stream/deferredScript.mjs +2 -1
  65. package/dist/esm-node/core/server/string/index.mjs +4 -0
  66. package/dist/esm-node/core/server/string/loadable.mjs +11 -2
  67. package/dist/esm-node/core/server/utils.mjs +8 -4
  68. package/dist/esm-node/exports/head.mjs +8 -237
  69. package/dist/esm-node/module-federation/last-known-good.mjs +35 -2
  70. package/dist/esm-node/router/cli/nestedRoutesSpec.mjs +7 -38
  71. package/dist/esm-node/router/runtime/PrefetchLink.mjs +28 -8
  72. package/dist/esm-node/router/runtime/redirect.mjs +11 -1
  73. package/dist/esm-node/router/runtime/utils.mjs +2 -0
  74. package/dist/esm-node/rslib-runtime.mjs +10 -0
  75. package/dist/types/core/context/extensions.d.ts +2 -4
  76. package/dist/types/core/context/public.d.ts +4 -8
  77. package/dist/types/core/server/requestResponse.d.ts +1 -1
  78. package/dist/types/core/server/routerCleanup.d.ts +2 -0
  79. package/dist/types/core/server/string/loadable.d.ts +1 -0
  80. package/dist/types/core/server/utils.d.ts +1 -0
  81. package/dist/types/exports/head.d.ts +1 -2
  82. package/dist/types/module-federation/last-known-good.d.ts +0 -20
  83. package/package.json +11 -10
  84. 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
- const ATTRIBUTE_NAME_MAP = {
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('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
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
- let records = serverHelmetRecords.get(helmetContext);
234
- if (void 0 === records) {
235
- records = [];
236
- serverHelmetRecords.set(helmetContext, records);
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 { HelmetData, HelmetProvider, head_Helmet as Helmet };
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 ('incompatible' === cached.resolved.compatibility.status) return fallback();
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 ('incompatible' !== resolution.unit.compatibility.status) await storage.write(key, {
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 releaseLock = await acquireSpecLock(resolvedSpecPath);
51
- try {
52
- const existingRoutes = await fs.pathExists(resolvedSpecPath) ? await fs.readJSON(resolvedSpecPath) : {};
53
- await writeJSONAtomically(resolvedSpecPath, {
54
- ...existingRoutes,
55
- ...nextRoutesSnapshot
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((error)=>{
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)=>__webpack_chunk_load__?.(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 || !__webpack_chunk_load__) return;
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(',')}`, ()=>loadRouteModule(match.route, routeAssets));
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
- matches,
207
- routeAssets
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 ('/' !== basename) redirectUrl = redirectUrl.replace(basename, '');
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
- 'process.env.MODERN_CHUNK_LOADING_GLOBAL': JSON.stringify(chunkLoadingGlobal)
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] ??= new Map();
9
- return target[EXTENSIONS_SLOT];
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
- response: {}
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
- if (void 0 === ssrContext) return {
9
- request: {},
10
- response: {
11
- locals: {}
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) {