@bleedingdev/modern-js-runtime 3.2.0-ultramodern.9 → 3.2.0-ultramodern.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/cjs/boundary-debugger/index.js +299 -0
  2. package/dist/cjs/cli/ssr/index.js +5 -15
  3. package/dist/cjs/cli/template.server.js +1 -0
  4. package/dist/cjs/core/react/wrapper.js +9 -3
  5. package/dist/cjs/core/server/federatedCss.js +47 -0
  6. package/dist/cjs/core/server/helmet.js +8 -2
  7. package/dist/cjs/core/server/stream/afterTemplate.js +9 -6
  8. package/dist/cjs/core/server/stream/beforeTemplate.js +13 -20
  9. package/dist/cjs/core/server/stream/beforeTemplate.worker.js +98 -0
  10. package/dist/cjs/core/server/stream/createReadableStream.js +7 -2
  11. package/dist/cjs/core/server/stream/createReadableStream.worker.js +4 -2
  12. package/dist/cjs/core/server/stream/shared.js +3 -1
  13. package/dist/cjs/core/server/string/index.js +6 -6
  14. package/dist/cjs/core/server/string/loadable.js +33 -7
  15. package/dist/cjs/exports/head.js +196 -5
  16. package/dist/cjs/router/cli/code/tanstackTypes.js +116 -51
  17. package/dist/cjs/router/cli/code/templates.js +1 -8
  18. package/dist/cjs/router/runtime/tanstack/plugin.js +4 -5
  19. package/dist/cjs/router/runtime/tanstack/plugin.node.js +2 -13
  20. package/dist/cjs/router/runtime/tanstack/routeTree.js +47 -4
  21. package/dist/cjs/rsc/server.worker.js +58 -0
  22. package/dist/cjs/ssr/serverRender/renderToString/entry.js +9 -8
  23. package/dist/esm/boundary-debugger/index.mjs +263 -0
  24. package/dist/esm/cli/ssr/index.mjs +5 -15
  25. package/dist/esm/cli/template.server.mjs +1 -0
  26. package/dist/esm/core/react/wrapper.mjs +9 -3
  27. package/dist/esm/core/server/federatedCss.mjs +13 -0
  28. package/dist/esm/core/server/helmet.mjs +5 -2
  29. package/dist/esm/core/server/stream/afterTemplate.mjs +10 -7
  30. package/dist/esm/core/server/stream/beforeTemplate.mjs +14 -11
  31. package/dist/esm/core/server/stream/beforeTemplate.worker.mjs +64 -0
  32. package/dist/esm/core/server/stream/createReadableStream.mjs +7 -2
  33. package/dist/esm/core/server/stream/createReadableStream.worker.mjs +4 -2
  34. package/dist/esm/core/server/stream/shared.mjs +3 -1
  35. package/dist/esm/core/server/string/index.mjs +7 -6
  36. package/dist/esm/core/server/string/loadable.mjs +33 -7
  37. package/dist/esm/exports/head.mjs +189 -4
  38. package/dist/esm/router/cli/code/tanstackTypes.mjs +116 -51
  39. package/dist/esm/router/cli/code/templates.mjs +1 -8
  40. package/dist/esm/router/runtime/tanstack/plugin.mjs +8 -9
  41. package/dist/esm/router/runtime/tanstack/plugin.node.mjs +3 -14
  42. package/dist/esm/router/runtime/tanstack/routeTree.mjs +47 -4
  43. package/dist/esm/rsc/server.worker.mjs +1 -0
  44. package/dist/esm/ssr/serverRender/renderToString/entry.mjs +9 -6
  45. package/dist/esm-node/boundary-debugger/index.mjs +264 -0
  46. package/dist/esm-node/cli/ssr/index.mjs +5 -15
  47. package/dist/esm-node/cli/template.server.mjs +1 -0
  48. package/dist/esm-node/core/react/wrapper.mjs +9 -3
  49. package/dist/esm-node/core/server/federatedCss.mjs +14 -0
  50. package/dist/esm-node/core/server/helmet.mjs +5 -2
  51. package/dist/esm-node/core/server/stream/afterTemplate.mjs +10 -7
  52. package/dist/esm-node/core/server/stream/beforeTemplate.mjs +14 -11
  53. package/dist/esm-node/core/server/stream/beforeTemplate.worker.mjs +65 -0
  54. package/dist/esm-node/core/server/stream/createReadableStream.mjs +7 -2
  55. package/dist/esm-node/core/server/stream/createReadableStream.worker.mjs +4 -2
  56. package/dist/esm-node/core/server/stream/shared.mjs +3 -1
  57. package/dist/esm-node/core/server/string/index.mjs +7 -6
  58. package/dist/esm-node/core/server/string/loadable.mjs +33 -7
  59. package/dist/esm-node/exports/head.mjs +189 -4
  60. package/dist/esm-node/router/cli/code/tanstackTypes.mjs +116 -51
  61. package/dist/esm-node/router/cli/code/templates.mjs +1 -8
  62. package/dist/esm-node/router/runtime/tanstack/plugin.mjs +8 -9
  63. package/dist/esm-node/router/runtime/tanstack/plugin.node.mjs +3 -14
  64. package/dist/esm-node/router/runtime/tanstack/routeTree.mjs +47 -4
  65. package/dist/esm-node/rsc/server.worker.mjs +2 -0
  66. package/dist/esm-node/ssr/serverRender/renderToString/entry.mjs +9 -6
  67. package/dist/types/boundary-debugger/index.d.ts +28 -0
  68. package/dist/types/core/context/runtime.d.ts +4 -0
  69. package/dist/types/core/server/federatedCss.d.ts +5 -0
  70. package/dist/types/core/server/helmet.d.ts +5 -3
  71. package/dist/types/core/server/stream/beforeTemplate.d.ts +1 -0
  72. package/dist/types/core/server/stream/beforeTemplate.worker.d.ts +10 -0
  73. package/dist/types/core/server/stream/shared.d.ts +8 -0
  74. package/dist/types/core/server/string/loadable.d.ts +4 -0
  75. package/dist/types/exports/head.d.ts +10 -3
  76. package/dist/types/rsc/server.worker.d.ts +1 -0
  77. package/package.json +25 -19
@@ -1,12 +1,11 @@
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";
@@ -19,7 +18,7 @@ const renderString = async (request, serverRoot, options)=>{
19
18
  onTiming
20
19
  };
21
20
  const routerContext = runtimeContext.routerContext;
22
- const { htmlTemplate, entryName, loadableStats, routeManifest } = resource;
21
+ const { htmlTemplate, entryName, loadableStats, routeManifest, moduleFederationCssAssets } = resource;
23
22
  const ssrConfig = getSSRConfigByEntry(entryName, config.ssr, config.ssrByEntries);
24
23
  const chunkSet = {
25
24
  renderLevel: RenderLevel.CLIENT_RENDER,
@@ -32,8 +31,10 @@ const renderString = async (request, serverRoot, options)=>{
32
31
  stats: loadableStats,
33
32
  nonce: config.nonce,
34
33
  routeManifest,
34
+ runtimeContext,
35
35
  template: htmlTemplate,
36
36
  entryName,
37
+ moduleFederationCssAssets,
37
38
  chunkSet,
38
39
  config
39
40
  }),
@@ -57,10 +58,10 @@ const renderString = async (request, serverRoot, options)=>{
57
58
  const rootElement = wrapRuntimeContextProvider(serverRoot, Object.assign(runtimeContext, {
58
59
  ssr: true
59
60
  }));
60
- const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], tracer);
61
+ const html = await generateHtml(rootElement, htmlTemplate, chunkSet, collectors, runtimeContext.ssrContext?.htmlModifiers || [], runtimeContext, tracer);
61
62
  return html;
62
63
  };
63
- async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, { onError, onTiming }) {
64
+ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifiers, runtimeContext, { onError, onTiming }) {
64
65
  let html = '';
65
66
  let helmetData;
66
67
  const finalApp = collectors.reduce((pre, creator)=>creator.collect?.(pre) || pre, App);
@@ -70,7 +71,7 @@ async function generateHtml(App, htmlTemplate, chunkSet, collectors, htmlModifie
70
71
  identifierPrefix: SSR_HYDRATION_ID_PREFIX
71
72
  });
72
73
  chunkSet.renderLevel = RenderLevel.SERVER_RENDER;
73
- helmetData = react_helmet.renderStatic();
74
+ helmetData = getHelmetData(runtimeContext);
74
75
  const cost = end();
75
76
  onTiming(SSRTimings.RENDER_HTML, cost);
76
77
  } catch (e) {
@@ -1,4 +1,6 @@
1
1
  import { ChunkExtractor } from "@loadable/server";
2
+ import { getRouterMatchedRouteIds } from "../../../router/runtime/lifecycle.mjs";
3
+ import { createFederatedCssLinks } from "../federatedCss.mjs";
2
4
  import { attributesToString, checkIsNode } from "../utils.mjs";
3
5
  const extname = (uri)=>{
4
6
  if ('string' != typeof uri || !uri.includes('.')) return '';
@@ -21,6 +23,20 @@ class LoadableCollector {
21
23
  const { routeManifest, entryName } = this.options;
22
24
  return routeManifest?.routeAssets?.[entryName]?.assets;
23
25
  }
26
+ getMatchedRouteChunks() {
27
+ const { routeManifest, runtimeContext } = this.options;
28
+ const routeAssets = routeManifest?.routeAssets;
29
+ if (!routeAssets) return [];
30
+ const matchedRouteIds = getRouterMatchedRouteIds(runtimeContext) ?? [];
31
+ return matchedRouteIds.flatMap((routeId)=>{
32
+ const routeAsset = routeAssets[routeId];
33
+ return (routeAsset?.assets ?? []).map((asset)=>({
34
+ filename: asset.replace(/^\//, ''),
35
+ path: asset,
36
+ url: asset
37
+ }));
38
+ });
39
+ }
24
40
  collect(comopnent) {
25
41
  const { stats, entryName } = this.options;
26
42
  if (!stats) return comopnent;
@@ -33,20 +49,21 @@ class LoadableCollector {
33
49
  return this.extractor.collectChunks(comopnent);
34
50
  }
35
51
  async effect() {
36
- if (!this.extractor) return;
37
52
  const { extractor, options } = this;
38
53
  const { entryName, config } = options;
39
54
  const asyncChunks = [];
40
- if (config.enableAsyncEntry) try {
55
+ if (extractor && config.enableAsyncEntry) try {
41
56
  asyncChunks.push(...extractor.getChunkAssets([
42
57
  `async-${entryName}`
43
58
  ]));
44
59
  } catch (e) {}
45
- const chunks = [].concat(asyncChunks).concat(extractor.getChunkAssets(extractor.chunks));
60
+ const chunks = [].concat(asyncChunks).concat(extractor ? extractor.getChunkAssets(extractor.chunks) : []).concat(this.getMatchedRouteChunks());
46
61
  const scriptChunks = generateChunks(chunks, 'js');
47
62
  const styleChunks = generateChunks(chunks, 'css');
48
- this.emitLoadableScripts(extractor);
49
- await this.emitScriptAssets(scriptChunks);
63
+ if (extractor) {
64
+ this.emitLoadableScripts(extractor);
65
+ await this.emitScriptAssets(scriptChunks);
66
+ }
50
67
  await this.emitStyleAssets(styleChunks);
51
68
  }
52
69
  emitLoadableScripts(extractor) {
@@ -81,19 +98,28 @@ class LoadableCollector {
81
98
  chunkSet.jsChunk += scripts.filter((script)=>Boolean(script)).join('');
82
99
  }
83
100
  async emitStyleAssets(chunks) {
84
- const { template, chunkSet, config, entryName } = this.options;
101
+ const { template, chunkSet, config, moduleFederationCssAssets } = this.options;
85
102
  const { inlineStyles } = config;
86
103
  const atrributes = attributesToString(this.generateAttributes());
87
104
  const linkRegExp = /<link .*?href="([^"]+)".*?>/g;
88
105
  const matchs = template.matchAll(linkRegExp);
89
106
  const existedLinks = [];
90
107
  for (const match of matchs)existedLinks.push(match[1]);
91
- const css = await Promise.all(chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path)).map(async (chunk)=>{
108
+ const emittedChunks = chunks.filter((chunk)=>!existedLinks.includes(chunk.url) && !this.existsAssets?.includes(chunk.path));
109
+ const css = await Promise.all(emittedChunks.map(async (chunk)=>{
92
110
  const link = `<link${atrributes} href="${chunk.url}" rel="stylesheet" />`;
93
111
  if (checkIsNode() && checkIsInline(chunk, inlineStyles)) return readAsset(chunk).then((content)=>`<style>${content}</style>`).catch((_)=>link);
94
112
  return link;
95
113
  }));
96
114
  chunkSet.cssChunk += css.filter((css)=>Boolean(css)).join('');
115
+ chunkSet.cssChunk += createFederatedCssLinks(moduleFederationCssAssets, {
116
+ template,
117
+ attributes: this.generateAttributes(),
118
+ existingAssets: [
119
+ ...existedLinks,
120
+ ...emittedChunks.map((chunk)=>chunk.url)
121
+ ]
122
+ });
97
123
  }
98
124
  generateAttributes(extraAtr = {}) {
99
125
  const { config } = this.options;
@@ -1,5 +1,190 @@
1
1
  "use client";
2
- import react_helmet, { Helmet } from "react-helmet";
3
- const head = react_helmet;
4
- export default head;
5
- export { Helmet };
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('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
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 };
@@ -77,9 +77,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
77
77
  const topLevel = rootModern ? rootModern.children || [] : routes;
78
78
  const imports = [];
79
79
  const statements = [];
80
+ const componentImportMap = new Map();
80
81
  const loaderImportMap = new Map();
82
+ const usedRouteVarNames = new Set();
83
+ let componentIndex = 0;
81
84
  let loaderIndex = 0;
82
85
  let routeIndex = 0;
86
+ const getImportNameForComponent = (componentPath)=>{
87
+ if ('string' != typeof componentPath || 0 === componentPath.length) return null;
88
+ const existing = componentImportMap.get(componentPath);
89
+ if (existing) return existing;
90
+ const componentName = `component_${componentIndex++}`;
91
+ imports.push(`import ${componentName} from ${quote(componentPath)};`);
92
+ componentImportMap.set(componentPath, componentName);
93
+ return componentName;
94
+ };
83
95
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
84
96
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
85
97
  const existing = loaderImportMap.get(key);
@@ -111,10 +123,17 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
111
123
  actionName
112
124
  };
113
125
  };
126
+ const reserveRouteVarName = (preferred)=>{
127
+ let candidate = preferred;
128
+ let suffix = 1;
129
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
130
+ usedRouteVarNames.add(candidate);
131
+ return candidate;
132
+ };
114
133
  const createRouteVarName = (route)=>{
115
134
  const id = route.id;
116
135
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
117
- return `route_${base}`;
136
+ return reserveRouteVarName(`route_${base}`);
118
137
  };
119
138
  const buildRoute = async (opts)=>{
120
139
  const { parentVar, route } = opts;
@@ -129,6 +148,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
129
148
  const routeOpts = [
130
149
  `getParentRoute: () => ${parentVar},`
131
150
  ];
151
+ const componentName = getImportNameForComponent(route._component);
152
+ if (componentName) routeOpts.push(`component: ${componentName},`);
132
153
  if (isPathlessLayout(route)) {
133
154
  const id = route.id;
134
155
  routeOpts.push(`id: ${quote(id || 'pathless')},`);
@@ -143,14 +164,16 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
143
164
  actionName
144
165
  });
145
166
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
146
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
147
167
  const children = route.children;
168
+ const hasChildren = Boolean(children && children.length > 0);
169
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
170
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
148
171
  if (children && children.length > 0) {
149
172
  const childVars = await Promise.all(children.map((child)=>buildRoute({
150
- parentVar: varName,
173
+ parentVar: routeCtorVarName,
151
174
  route: child
152
175
  })));
153
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
176
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
154
177
  }
155
178
  return varName;
156
179
  };
@@ -164,6 +187,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
164
187
  route
165
188
  })));
166
189
  const rootOpts = [];
190
+ const rootComponentName = getImportNameForComponent(rootModern?._component);
191
+ if (rootComponentName) rootOpts.push(`component: ${rootComponentName},`);
167
192
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
168
193
  const routerGenTs = `/* eslint-disable */
169
194
  // This file is auto-generated by Modern.js. Do not edit manually.
@@ -197,7 +222,7 @@ function isRedirectResponse(res: Response) {
197
222
  }
198
223
 
199
224
  function throwTanstackRedirect(location: string) {
200
- const target = location || '/';
225
+ const target = location.length > 0 ? location : '/';
201
226
  try {
202
227
  void new URL(target);
203
228
  throw redirect({ href: target });
@@ -223,21 +248,87 @@ function createRouteStaticData(opts: {
223
248
  modernRouteAction?: unknown;
224
249
  modernRouteLoader?: unknown;
225
250
  }) {
226
- const staticData: Record<string, unknown> = {};
251
+ const staticData: {
252
+ modernRouteId?: string;
253
+ modernRouteAction?: unknown;
254
+ modernRouteLoader?: unknown;
255
+ } = {};
227
256
 
228
- if (opts.modernRouteId) {
257
+ if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
229
258
  staticData.modernRouteId = opts.modernRouteId;
230
259
  }
231
260
 
232
- if (opts.modernRouteLoader) {
261
+ if (typeof opts.modernRouteLoader !== 'undefined') {
233
262
  staticData.modernRouteLoader = opts.modernRouteLoader;
234
263
  }
235
264
 
236
- if (opts.modernRouteAction) {
265
+ if (typeof opts.modernRouteAction !== 'undefined') {
237
266
  staticData.modernRouteAction = opts.modernRouteAction;
238
267
  }
239
268
 
240
- return Object.keys(staticData).length > 0 ? staticData : undefined;
269
+ return staticData;
270
+ }
271
+
272
+ function getLoaderSignal(ctx: any): AbortSignal {
273
+ const abortSignal = ctx?.abortController?.signal;
274
+ if (abortSignal instanceof AbortSignal) {
275
+ return abortSignal;
276
+ }
277
+ if (ctx?.signal instanceof AbortSignal) {
278
+ return ctx.signal;
279
+ }
280
+ return new AbortController().signal;
281
+ }
282
+
283
+ function getLoaderHref(ctx: any): string {
284
+ if (typeof ctx?.location === 'string') {
285
+ return ctx.location;
286
+ }
287
+
288
+ const publicHref = ctx?.location?.publicHref;
289
+ if (typeof publicHref === 'string') {
290
+ return publicHref;
291
+ }
292
+
293
+ const href = ctx?.location?.href;
294
+ if (typeof href === 'string') {
295
+ return href;
296
+ }
297
+
298
+ const urlHref = ctx?.location?.url?.href;
299
+ return typeof urlHref === 'string' ? urlHref : '';
300
+ }
301
+
302
+ function getLoaderParams(ctx: any): Record<string, string> {
303
+ return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
304
+ }
305
+
306
+ function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
307
+ if (isResponse(result)) {
308
+ if (isRedirectResponse(result)) {
309
+ const location = result.headers.get('Location') ?? '/';
310
+ throwTanstackRedirect(location);
311
+ }
312
+ if (result.status === 404) {
313
+ throw notFound();
314
+ }
315
+ }
316
+
317
+ return result;
318
+ }
319
+
320
+ function handleModernLoaderError(err: unknown): never {
321
+ if (isResponse(err)) {
322
+ if (isRedirectResponse(err)) {
323
+ const location = err.headers.get('Location') ?? '/';
324
+ throwTanstackRedirect(location);
325
+ }
326
+ if (err.status === 404) {
327
+ throw notFound();
328
+ }
329
+ }
330
+
331
+ throw err;
241
332
  }
242
333
 
243
334
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -246,57 +337,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
246
337
  ) {
247
338
  type LoaderResult = Awaited<ReturnType<TLoader>>;
248
339
 
249
- return async (ctx: any): Promise<LoaderResult> => {
340
+ return (ctx: any): Promise<LoaderResult> => {
250
341
  try {
251
- const signal: AbortSignal =
252
- ctx?.abortController?.signal ||
253
- ctx?.signal ||
254
- new AbortController().signal;
342
+ const signal = getLoaderSignal(ctx);
255
343
  const baseRequest: Request | undefined =
256
344
  ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
257
345
 
258
- const href =
259
- typeof ctx?.location === 'string'
260
- ? ctx.location
261
- : ctx?.location?.publicHref ||
262
- ctx?.location?.href ||
263
- ctx?.location?.url?.href ||
264
- '';
346
+ const href = getLoaderHref(ctx);
265
347
 
266
- const request = baseRequest
348
+ const request = baseRequest !== undefined
267
349
  ? new Request(baseRequest, { signal })
268
350
  : new Request(href, { signal });
269
351
 
270
- const params = mapParamsForModernLoader(ctx?.params || {}, opts.hasSplat);
271
-
272
- const result = await (modernLoader as any)({
273
- request,
274
- params,
275
- context: ctx?.context?.requestContext,
276
- });
277
-
278
- if (isResponse(result)) {
279
- if (isRedirectResponse(result)) {
280
- const location = result.headers.get('Location') || '/';
281
- throwTanstackRedirect(location);
282
- }
283
- if (result.status === 404) {
284
- throw notFound();
285
- }
286
- }
352
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
287
353
 
288
- return result as LoaderResult;
354
+ return Promise.resolve(
355
+ (modernLoader as any)({
356
+ request,
357
+ params,
358
+ context: ctx?.context?.requestContext,
359
+ }),
360
+ )
361
+ .then((result: LoaderResult) => handleModernLoaderResult(result))
362
+ .catch(handleModernLoaderError);
289
363
  } catch (err) {
290
- if (isResponse(err)) {
291
- if (isRedirectResponse(err)) {
292
- const location = err.headers.get('Location') || '/';
293
- throwTanstackRedirect(location);
294
- }
295
- if (err.status === 404) {
296
- throw notFound();
297
- }
298
- }
299
- throw err;
364
+ handleModernLoaderError(err);
300
365
  }
301
366
  };
302
367
  }
@@ -170,16 +170,9 @@ const fileSystemRoutes = async ({ metaName, routes, ssrMode, nestedRoutesEntry,
170
170
  webpackChunkName: true
171
171
  });
172
172
  component = 'string' === ssrMode ? `loadable(${lazyImport})` : `lazy(${lazyImport})`;
173
- } else if ('string' === ssrMode) {
173
+ } else {
174
174
  components.push(route._component);
175
175
  component = `component_${components.length - 1}`;
176
- } else {
177
- lazyImport = createLazyImport({
178
- componentPath: route._component,
179
- routeId: route.id,
180
- eager: true
181
- });
182
- component = `lazy(${lazyImport})`;
183
176
  }
184
177
  } else if (route._component) if (splitRouteChunks) {
185
178
  lazyImport = `() => import('${route._component}')`;
@@ -3,14 +3,15 @@ import { merge } from "@modern-js/runtime-utils/merge";
3
3
  import { normalizePathname } from "@modern-js/runtime-utils/url";
4
4
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
5
5
  import { RouterClient } from "@tanstack/react-router/ssr/client";
6
+ import { useContext, useMemo } from "react";
6
7
  import { InternalRuntimeContext, getGlobalEnableRsc, getGlobalLayoutApp, getGlobalRoutes } from "../../../core/context/index.mjs";
7
8
  import { onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeHydrateRouter } from "../hooks.mjs";
8
9
  import { applyRouterRuntimeState } from "../lifecycle.mjs";
9
10
  import { createRouteObjectsFromConfig, urlJoin } from "../utils.mjs";
10
11
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
12
+ import { Link } from "./prefetchLink.mjs";
11
13
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
12
14
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
13
- import * as __rspack_external_react from "react";
14
15
  const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-runtime:tanstack-blocking-subscribe');
15
16
  const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-runtime:tanstack-blocking-state');
16
17
  function normalizeBase(b) {
@@ -70,6 +71,7 @@ const tanstackRouterPlugin = (userConfig = {})=>({
70
71
  }
71
72
  }
72
73
  context.router = {
74
+ Link: Link,
73
75
  useMatches: useMatches,
74
76
  useLocation: useLocation,
75
77
  useNavigate: useNavigate,
@@ -104,10 +106,10 @@ const tanstackRouterPlugin = (userConfig = {})=>({
104
106
  let cachedRouter = null;
105
107
  let cachedRouterBasepath = null;
106
108
  const RouterWrapper = ()=>{
107
- const runtimeContext = (0, __rspack_external_react.useContext)(InternalRuntimeContext);
109
+ const runtimeContext = useContext(InternalRuntimeContext);
108
110
  const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
109
111
  const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
110
- const routeTree = (0, __rspack_external_react.useMemo)(()=>{
112
+ const routeTree = useMemo(()=>{
111
113
  if (cachedRouteTree) return cachedRouteTree;
112
114
  const routeObjects = getRouteObjects();
113
115
  if (!routeObjects.length) return null;
@@ -115,7 +117,7 @@ const tanstackRouterPlugin = (userConfig = {})=>({
115
117
  return cachedRouteTree;
116
118
  }, []);
117
119
  if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
118
- const router = (0, __rspack_external_react.useMemo)(()=>{
120
+ const router = useMemo(()=>{
119
121
  const lifecycleContext = {
120
122
  framework: 'tanstack',
121
123
  phase: 'client-create',
@@ -180,11 +182,8 @@ const tanstackRouterPlugin = (userConfig = {})=>({
180
182
  router,
181
183
  runtimeContext: runtimeState
182
184
  });
183
- const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(__rspack_external_react.Suspense, {
184
- fallback: null,
185
- children: /*#__PURE__*/ jsx(RouterClient, {
186
- router: router
187
- })
185
+ const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(RouterClient, {
186
+ router: router
188
187
  }) : /*#__PURE__*/ jsx(RouterProvider, {
189
188
  router: router
190
189
  });