@bleedingdev/modern-js-plugin-tanstack 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 (48) hide show
  1. package/dist/cjs/cli/index.js +12 -0
  2. package/dist/cjs/cli/routeSplitting.js +83 -0
  3. package/dist/cjs/cli/tanstackTypes.js +146 -58
  4. package/dist/cjs/runtime/index.js +38 -6
  5. package/dist/cjs/runtime/plugin.js +6 -5
  6. package/dist/cjs/runtime/plugin.node.js +27 -10
  7. package/dist/cjs/runtime/plugin.worker.js +49 -0
  8. package/dist/cjs/runtime/routeTree.js +55 -4
  9. package/dist/cjs/runtime/types.js +27 -1
  10. package/dist/esm/cli/index.mjs +4 -1
  11. package/dist/esm/cli/routeSplitting.mjs +43 -0
  12. package/dist/esm/cli/tanstackTypes.mjs +146 -58
  13. package/dist/esm/runtime/index.mjs +2 -1
  14. package/dist/esm/runtime/plugin.mjs +10 -9
  15. package/dist/esm/runtime/plugin.node.mjs +28 -11
  16. package/dist/esm/runtime/plugin.worker.mjs +1 -0
  17. package/dist/esm/runtime/routeTree.mjs +55 -4
  18. package/dist/esm/runtime/types.mjs +7 -0
  19. package/dist/esm-node/cli/index.mjs +4 -1
  20. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  21. package/dist/esm-node/cli/tanstackTypes.mjs +146 -58
  22. package/dist/esm-node/runtime/index.mjs +2 -1
  23. package/dist/esm-node/runtime/plugin.mjs +10 -9
  24. package/dist/esm-node/runtime/plugin.node.mjs +28 -11
  25. package/dist/esm-node/runtime/plugin.worker.mjs +2 -0
  26. package/dist/esm-node/runtime/routeTree.mjs +55 -4
  27. package/dist/esm-node/runtime/types.mjs +7 -0
  28. package/dist/types/cli/index.d.ts +4 -0
  29. package/dist/types/cli/routeSplitting.d.ts +29 -0
  30. package/dist/types/runtime/index.d.ts +3 -1
  31. package/dist/types/runtime/plugin.d.ts +1 -1
  32. package/dist/types/runtime/plugin.node.d.ts +1 -1
  33. package/dist/types/runtime/plugin.worker.d.ts +1 -0
  34. package/dist/types/runtime/types.d.ts +7 -0
  35. package/package.json +14 -14
  36. package/src/cli/index.ts +17 -0
  37. package/src/cli/routeSplitting.ts +81 -0
  38. package/src/cli/tanstackTypes.ts +216 -67
  39. package/src/runtime/index.tsx +13 -1
  40. package/src/runtime/plugin.node.tsx +54 -7
  41. package/src/runtime/plugin.tsx +8 -5
  42. package/src/runtime/plugin.worker.tsx +4 -0
  43. package/src/runtime/routeTree.ts +125 -8
  44. package/src/runtime/types.ts +13 -0
  45. package/tests/router/cli.test.ts +239 -0
  46. package/tests/router/fastDefaults.test.ts +25 -0
  47. package/tests/router/routeTree.test.ts +193 -1
  48. package/tests/router/tanstackTypes.test.ts +184 -0
@@ -0,0 +1,44 @@
1
+ import "node:module";
2
+ const TANSTACK_START_ROUTE_FACTORY_CALLS = [
3
+ 'createFileRoute',
4
+ 'createRootRoute',
5
+ 'createRootRouteWithContext'
6
+ ];
7
+ const TANSTACK_START_ROUTE_FACTORY_REGEX = /\b(createFileRoute|createRootRoute|createRootRouteWithContext)\s*(?:<|\()/;
8
+ function isTanstackStartRouteModuleSource(source) {
9
+ return TANSTACK_START_ROUTE_FACTORY_REGEX.test(source);
10
+ }
11
+ function resolveTanstackRouteCodeSplittingEnabled(option) {
12
+ if ('boolean' == typeof option) return option;
13
+ return option?.enabled ?? true;
14
+ }
15
+ function createTanstackRsbuildRouteSplittingProfile(opts) {
16
+ return {
17
+ defaultConfig: {
18
+ output: {
19
+ splitRouteChunks: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting)
20
+ }
21
+ },
22
+ modernRouteChunks: {
23
+ enabled: resolveTanstackRouteCodeSplittingEnabled(opts.routeCodeSplitting),
24
+ owner: 'modern'
25
+ },
26
+ builderChunkSplit: {
27
+ owner: 'modern-rsbuild',
28
+ preserved: true
29
+ },
30
+ tanstackStartRspackSplitter: {
31
+ compatible: false,
32
+ reason: 'TanStack Start Rsbuild route splitting is tied to TanStack file-route factory modules; Modern generates TanStack route trees from Modern route metadata and owns route chunking through output.splitRouteChunks.',
33
+ clientDeleteNodes: [
34
+ 'ssr',
35
+ 'server',
36
+ 'headers'
37
+ ],
38
+ routeFactoryCalls: [
39
+ ...TANSTACK_START_ROUTE_FACTORY_CALLS
40
+ ]
41
+ }
42
+ };
43
+ }
44
+ export { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled };
@@ -56,6 +56,14 @@ function pickModernLoaderModule(route) {
56
56
  inline
57
57
  };
58
58
  }
59
+ function pickRouteSearchContractModules(route) {
60
+ const validateSearchPath = route.validateSearch;
61
+ const loaderDepsPath = route.loaderDeps;
62
+ return {
63
+ validateSearchPath: 'string' == typeof validateSearchPath ? validateSearchPath : null,
64
+ loaderDepsPath: 'string' == typeof loaderDepsPath ? loaderDepsPath : null
65
+ };
66
+ }
59
67
  function isPathlessLayout(route) {
60
68
  return 'nested' === route.type && 'boolean' != typeof route.index && void 0 === route.path;
61
69
  }
@@ -89,8 +97,21 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
89
97
  const imports = [];
90
98
  const statements = [];
91
99
  const loaderImportMap = new Map();
100
+ const searchContractImportMap = new Map();
101
+ const usedRouteVarNames = new Set();
92
102
  let loaderIndex = 0;
103
+ let validateSearchIndex = 0;
104
+ let loaderDepsIndex = 0;
93
105
  let routeIndex = 0;
106
+ const resolveRouteModuleNoExt = async (aliasedNoExtPath)=>{
107
+ const prefix = `${appContext.internalSrcAlias}/`;
108
+ let absNoExt;
109
+ if (aliasedNoExtPath.startsWith(prefix)) {
110
+ const rel = aliasedNoExtPath.slice(prefix.length);
111
+ absNoExt = path.join(appContext.srcDirectory, rel);
112
+ } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
113
+ return resolveFileNoExt(absNoExt);
114
+ };
94
115
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
95
116
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
96
117
  const existing = loaderImportMap.get(key);
@@ -98,13 +119,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
98
119
  loaderName: existing,
99
120
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
100
121
  };
101
- const prefix = `${appContext.internalSrcAlias}/`;
102
- let absNoExt;
103
- if (aliasedNoExtPath.startsWith(prefix)) {
104
- const rel = aliasedNoExtPath.slice(prefix.length);
105
- absNoExt = path.join(appContext.srcDirectory, rel);
106
- } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
107
- const resolvedNoExt = await resolveFileNoExt(absNoExt);
122
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
108
123
  if (!resolvedNoExt) return null;
109
124
  const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
110
125
  const importName = `loader_${loaderIndex++}`;
@@ -122,10 +137,29 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
122
137
  actionName
123
138
  };
124
139
  };
140
+ const getImportNameForSearchContract = async (aliasedNoExtPath, exportName)=>{
141
+ const key = `${exportName}:${aliasedNoExtPath}`;
142
+ const existing = searchContractImportMap.get(key);
143
+ if (existing) return existing;
144
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
145
+ if (!resolvedNoExt) return null;
146
+ const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
147
+ const importName = 'validateSearch' === exportName ? `validateSearch_${validateSearchIndex++}` : `loaderDeps_${loaderDepsIndex++}`;
148
+ imports.push(`import { ${exportName} as ${importName} } from ${quote(relImport)};`);
149
+ searchContractImportMap.set(key, importName);
150
+ return importName;
151
+ };
152
+ const reserveRouteVarName = (preferred)=>{
153
+ let candidate = preferred;
154
+ let suffix = 1;
155
+ while(usedRouteVarNames.has(candidate))candidate = `${preferred}_${suffix++}`;
156
+ usedRouteVarNames.add(candidate);
157
+ return candidate;
158
+ };
125
159
  const createRouteVarName = (route)=>{
126
160
  const id = route.id;
127
161
  const base = id ? makeLegalIdentifier(id) : `r_${routeIndex++}`;
128
- return `route_${base}`;
162
+ return reserveRouteVarName(`route_${base}`);
129
163
  };
130
164
  const buildRoute = async (opts)=>{
131
165
  const { parentVar, route } = opts;
@@ -135,6 +169,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
135
169
  const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
136
170
  const loaderName = loaderImports?.loaderName || null;
137
171
  const actionName = loaderImports?.actionName || null;
172
+ const searchContractInfo = pickRouteSearchContractModules(route);
173
+ const validateSearchName = searchContractInfo.validateSearchPath ? await getImportNameForSearchContract(searchContractInfo.validateSearchPath, 'validateSearch') : null;
174
+ const loaderDepsName = searchContractInfo.loaderDepsPath ? await getImportNameForSearchContract(searchContractInfo.loaderDepsPath, 'loaderDeps') : null;
138
175
  const rawPath = route.path;
139
176
  const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
140
177
  const routeOpts = [
@@ -148,20 +185,24 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
148
185
  routeOpts.push(`path: ${quote(p)},`);
149
186
  }
150
187
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
188
+ if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
189
+ if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
151
190
  const staticDataSnippet = createRouteStaticDataSnippet({
152
191
  modernRouteId: route.id,
153
192
  loaderName,
154
193
  actionName
155
194
  });
156
195
  if (staticDataSnippet) routeOpts.push(staticDataSnippet);
157
- statements.push(`const ${varName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
158
196
  const children = route.children;
197
+ const hasChildren = Boolean(children && children.length > 0);
198
+ const routeCtorVarName = hasChildren ? reserveRouteVarName(`${varName}__base`) : varName;
199
+ statements.push(`const ${routeCtorVarName} = createRoute({\n ${routeOpts.join('\n ')}\n});`);
159
200
  if (children && children.length > 0) {
160
201
  const childVars = await Promise.all(children.map((child)=>buildRoute({
161
- parentVar: varName,
202
+ parentVar: routeCtorVarName,
162
203
  route: child
163
204
  })));
164
- statements.push(`${varName}.addChildren([${childVars.join(', ')}]);`);
205
+ statements.push(`const ${varName} = ${routeCtorVarName}.addChildren([${childVars.join(', ')}]);`);
165
206
  }
166
207
  return varName;
167
208
  };
@@ -170,17 +211,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
170
211
  const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
171
212
  const rootLoaderName = rootLoaderImports?.loaderName || null;
172
213
  const rootActionName = rootLoaderImports?.actionName || null;
214
+ const rootSearchContractInfo = rootModern ? pickRouteSearchContractModules(rootModern) : null;
215
+ const rootValidateSearchName = rootSearchContractInfo?.validateSearchPath ? await getImportNameForSearchContract(rootSearchContractInfo.validateSearchPath, 'validateSearch') : null;
216
+ const rootLoaderDepsName = rootSearchContractInfo?.loaderDepsPath ? await getImportNameForSearchContract(rootSearchContractInfo.loaderDepsPath, 'loaderDeps') : null;
173
217
  const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
174
218
  parentVar: 'rootRoute',
175
219
  route
176
220
  })));
177
221
  const rootOpts = [];
178
222
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
223
+ if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
224
+ if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
179
225
  const routerGenTs = `/* eslint-disable */
180
226
  // This file is auto-generated by Modern.js. Do not edit manually.
181
227
 
182
228
  import {
183
229
  createMemoryHistory,
230
+ modernTanstackRouterFastDefaults,
184
231
  createRootRouteWithContext,
185
232
  createRoute,
186
233
  createRouter,
@@ -208,7 +255,7 @@ function isRedirectResponse(res: Response) {
208
255
  }
209
256
 
210
257
  function throwTanstackRedirect(location: string) {
211
- const target = location || '/';
258
+ const target = location.length > 0 ? location : '/';
212
259
  try {
213
260
  void new URL(target);
214
261
  throw redirect({ href: target });
@@ -234,21 +281,87 @@ function createRouteStaticData(opts: {
234
281
  modernRouteAction?: unknown;
235
282
  modernRouteLoader?: unknown;
236
283
  }) {
237
- const staticData: Record<string, unknown> = {};
284
+ const staticData: {
285
+ modernRouteId?: string;
286
+ modernRouteAction?: unknown;
287
+ modernRouteLoader?: unknown;
288
+ } = {};
238
289
 
239
- if (opts.modernRouteId) {
290
+ if (typeof opts.modernRouteId === 'string' && opts.modernRouteId.length > 0) {
240
291
  staticData.modernRouteId = opts.modernRouteId;
241
292
  }
242
293
 
243
- if (opts.modernRouteLoader) {
294
+ if (typeof opts.modernRouteLoader !== 'undefined') {
244
295
  staticData.modernRouteLoader = opts.modernRouteLoader;
245
296
  }
246
297
 
247
- if (opts.modernRouteAction) {
298
+ if (typeof opts.modernRouteAction !== 'undefined') {
248
299
  staticData.modernRouteAction = opts.modernRouteAction;
249
300
  }
250
301
 
251
- return Object.keys(staticData).length > 0 ? staticData : undefined;
302
+ return staticData;
303
+ }
304
+
305
+ function getLoaderSignal(ctx: any): AbortSignal {
306
+ const abortSignal = ctx?.abortController?.signal;
307
+ if (abortSignal instanceof AbortSignal) {
308
+ return abortSignal;
309
+ }
310
+ if (ctx?.signal instanceof AbortSignal) {
311
+ return ctx.signal;
312
+ }
313
+ return new AbortController().signal;
314
+ }
315
+
316
+ function getLoaderHref(ctx: any): string {
317
+ if (typeof ctx?.location === 'string') {
318
+ return ctx.location;
319
+ }
320
+
321
+ const publicHref = ctx?.location?.publicHref;
322
+ if (typeof publicHref === 'string') {
323
+ return publicHref;
324
+ }
325
+
326
+ const href = ctx?.location?.href;
327
+ if (typeof href === 'string') {
328
+ return href;
329
+ }
330
+
331
+ const urlHref = ctx?.location?.url?.href;
332
+ return typeof urlHref === 'string' ? urlHref : '';
333
+ }
334
+
335
+ function getLoaderParams(ctx: any): Record<string, string> {
336
+ return typeof ctx?.params === 'object' && ctx.params !== null ? ctx.params : {};
337
+ }
338
+
339
+ function handleModernLoaderResult<LoaderResult>(result: LoaderResult): LoaderResult {
340
+ if (isResponse(result)) {
341
+ if (isRedirectResponse(result)) {
342
+ const location = result.headers.get('Location') ?? '/';
343
+ throwTanstackRedirect(location);
344
+ }
345
+ if (result.status === 404) {
346
+ throw notFound();
347
+ }
348
+ }
349
+
350
+ return result;
351
+ }
352
+
353
+ function handleModernLoaderError(err: unknown): never {
354
+ if (isResponse(err)) {
355
+ if (isRedirectResponse(err)) {
356
+ const location = err.headers.get('Location') ?? '/';
357
+ throwTanstackRedirect(location);
358
+ }
359
+ if (err.status === 404) {
360
+ throw notFound();
361
+ }
362
+ }
363
+
364
+ throw err;
252
365
  }
253
366
 
254
367
  function modernLoaderToTanstack<TLoader extends (args: any) => any>(
@@ -257,57 +370,31 @@ function modernLoaderToTanstack<TLoader extends (args: any) => any>(
257
370
  ) {
258
371
  type LoaderResult = Awaited<ReturnType<TLoader>>;
259
372
 
260
- return async (ctx: any): Promise<LoaderResult> => {
373
+ return (ctx: any): Promise<LoaderResult> => {
261
374
  try {
262
- const signal: AbortSignal =
263
- ctx?.abortController?.signal ||
264
- ctx?.signal ||
265
- new AbortController().signal;
375
+ const signal = getLoaderSignal(ctx);
266
376
  const baseRequest: Request | undefined =
267
377
  ctx?.context?.request instanceof Request ? ctx.context.request : undefined;
268
378
 
269
- const href =
270
- typeof ctx?.location === 'string'
271
- ? ctx.location
272
- : ctx?.location?.publicHref ||
273
- ctx?.location?.href ||
274
- ctx?.location?.url?.href ||
275
- '';
379
+ const href = getLoaderHref(ctx);
276
380
 
277
- const request = baseRequest
381
+ const request = baseRequest !== undefined
278
382
  ? new Request(baseRequest, { signal })
279
383
  : new Request(href, { signal });
280
384
 
281
- const params = mapParamsForModernLoader(ctx?.params || {}, opts.hasSplat);
385
+ const params = mapParamsForModernLoader(getLoaderParams(ctx), opts.hasSplat);
282
386
 
283
- const result = await (modernLoader as any)({
284
- request,
285
- params,
286
- context: ctx?.context?.requestContext,
287
- });
288
-
289
- if (isResponse(result)) {
290
- if (isRedirectResponse(result)) {
291
- const location = result.headers.get('Location') || '/';
292
- throwTanstackRedirect(location);
293
- }
294
- if (result.status === 404) {
295
- throw notFound();
296
- }
297
- }
298
-
299
- return result as LoaderResult;
387
+ return Promise.resolve(
388
+ (modernLoader as any)({
389
+ request,
390
+ params,
391
+ context: ctx?.context?.requestContext,
392
+ }),
393
+ )
394
+ .then((result: LoaderResult) => handleModernLoaderResult(result))
395
+ .catch(handleModernLoaderError);
300
396
  } catch (err) {
301
- if (isResponse(err)) {
302
- if (isRedirectResponse(err)) {
303
- const location = err.headers.get('Location') || '/';
304
- throwTanstackRedirect(location);
305
- }
306
- if (err.status === 404) {
307
- throw notFound();
308
- }
309
- }
310
- throw err;
397
+ handleModernLoaderError(err);
311
398
  }
312
399
  };
313
400
  }
@@ -328,6 +415,7 @@ ${statements.join('\n\n')}
328
415
  export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
329
416
 
330
417
  export const router = createRouter({
418
+ ...modernTanstackRouterFastDefaults,
331
419
  routeTree,
332
420
  history: createMemoryHistory({
333
421
  initialEntries: ['/'],
@@ -1,7 +1,8 @@
1
1
  import "node:module";
2
2
  export * from "@tanstack/react-router";
3
- export { useMatch } from "@tanstack/react-router";
3
+ export { Outlet, useLocation, useMatch, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
4
4
  export { Form, RouteActionResponseError, useFetcher } from "./dataMutation.mjs";
5
5
  export { tanstackRouterPlugin as default, tanstackRouterPlugin } from "./plugin.mjs";
6
6
  export { Link, NavLink } from "./prefetchLink.mjs";
7
7
  export { CompositeComponent } from "./rsc/client.mjs";
8
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults } from "./types.mjs";
@@ -5,13 +5,15 @@ import { merge } from "@modern-js/runtime-utils/merge";
5
5
  import { normalizePathname } from "@modern-js/runtime-utils/url";
6
6
  import { RouterProvider, createBrowserHistory, createHashHistory, createRouter, useLocation, useMatches, useNavigate, useRouter } from "@tanstack/react-router";
7
7
  import { RouterClient } from "@tanstack/react-router/ssr/client";
8
+ import { useContext, useMemo } from "react";
8
9
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
9
10
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
10
11
  import { applyRouterRuntimeState } from "./lifecycle.mjs";
12
+ import { Link } from "./prefetchLink.mjs";
11
13
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
12
14
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
15
+ import { getModernTanstackRouterFastDefaults } from "./types.mjs";
13
16
  import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
14
- import * as __rspack_external_react from "react";
15
17
  const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-subscribe');
16
18
  const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-state');
17
19
  function normalizeBase(b) {
@@ -74,6 +76,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
74
76
  }
75
77
  }
76
78
  context.router = {
79
+ Link: Link,
77
80
  useMatches: useMatches,
78
81
  useLocation: useLocation,
79
82
  useNavigate: useNavigate,
@@ -108,10 +111,10 @@ const tanstackRouterPlugin = (userConfig = {})=>{
108
111
  let cachedRouter = null;
109
112
  let cachedRouterBasepath = null;
110
113
  const RouterWrapper = ()=>{
111
- const runtimeContext = (0, __rspack_external_react.useContext)(InternalRuntimeContext);
114
+ const runtimeContext = useContext(InternalRuntimeContext);
112
115
  const baseUrl = selectBasePath(location.pathname).replace(/^\/*/, '/');
113
116
  const _basename = '/' === baseUrl ? urlJoin(baseUrl, runtimeContext._internalRouterBaseName || basename || '') : baseUrl;
114
- const routeTree = (0, __rspack_external_react.useMemo)(()=>{
117
+ const routeTree = useMemo(()=>{
115
118
  if (cachedRouteTree) return cachedRouteTree;
116
119
  const routeObjects = getRouteObjects();
117
120
  if (!routeObjects.length) return null;
@@ -121,7 +124,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
121
124
  return cachedRouteTree;
122
125
  }, []);
123
126
  if (!routeTree) return App ? /*#__PURE__*/ jsx(App, {}) : null;
124
- const router = (0, __rspack_external_react.useMemo)(()=>{
127
+ const router = useMemo(()=>{
125
128
  const lifecycleContext = {
126
129
  framework: 'tanstack',
127
130
  phase: 'client-create',
@@ -143,6 +146,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
143
146
  const rewrite = createModernBasepathRewrite(_basename);
144
147
  const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
145
148
  cachedRouter = createRouter({
149
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
146
150
  routeTree,
147
151
  basepath: '/',
148
152
  rewrite,
@@ -186,11 +190,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
186
190
  router,
187
191
  runtimeContext: runtimeState
188
192
  });
189
- const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(__rspack_external_react.Suspense, {
190
- fallback: null,
191
- children: /*#__PURE__*/ jsx(RouterClient, {
192
- router: router
193
- })
193
+ const RouterContent = hasSSRBootstrap ? /*#__PURE__*/ jsx(RouterClient, {
194
+ router: router
194
195
  }) : /*#__PURE__*/ jsx(RouterProvider, {
195
196
  router: router
196
197
  });
@@ -6,24 +6,41 @@ import { createRequestContext, storage } from "@modern-js/runtime-utils/node";
6
6
  import { time } from "@modern-js/runtime-utils/time";
7
7
  import { LOADER_REPORTER_NAME } from "@modern-js/utils/universal/constants";
8
8
  import { RouterProvider, createMemoryHistory, createRouter } from "@tanstack/react-router";
9
- import { attachRouterServerSsrUtils } from "@tanstack/react-router/ssr/server";
10
- import { Suspense, useContext } from "react";
9
+ import { attachRouterServerSsrUtils } from "@tanstack/router-core/ssr/server";
10
+ import { useContext } from "react";
11
11
  import { createModernBasepathRewrite } from "./basepathRewrite.mjs";
12
12
  import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreateRouter, onBeforeCreateRoutes, onBeforeHydrateRouter } from "./hooks.mjs";
13
13
  import { applyRouterServerPrepareResult, createRouterServerSnapshot } from "./lifecycle.mjs";
14
14
  import { createRouteTreeFromRouteObjects, getModernRouteIdsFromMatches } from "./routeTree.mjs";
15
15
  import { createTanstackRscServerPayload, handleTanstackRscRedirect } from "./rsc/payloadRouter.mjs";
16
+ import { getModernTanstackRouterFastDefaults } from "./types.mjs";
16
17
  import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
17
18
  const setTanstackRscServerPayload = (payload)=>{
18
19
  const storageContext = storage.useContext?.();
19
20
  if (storageContext) storageContext.serverPayload = payload;
20
21
  };
22
+ function isPromiseLike(value) {
23
+ return Boolean(value && 'function' == typeof value.then);
24
+ }
21
25
  function isPreloadableRouteComponent(component) {
22
26
  if (!component || 'function' != typeof component) return false;
23
27
  const preloadable = component;
24
28
  return 'function' == typeof preloadable.load || 'function' == typeof preloadable.preload;
25
29
  }
30
+ function isReactLazyRouteComponent(component) {
31
+ return Boolean(component) && 'object' == typeof component && 'function' == typeof component._init && '_payload' in component;
32
+ }
33
+ async function preloadReactLazyRouteComponent(component) {
34
+ try {
35
+ component._init?.(component._payload);
36
+ } catch (thrown) {
37
+ if (!isPromiseLike(thrown)) throw thrown;
38
+ await thrown;
39
+ component._init?.(component._payload);
40
+ }
41
+ }
26
42
  async function preloadRouteComponent(component) {
43
+ if (isReactLazyRouteComponent(component)) return void await preloadReactLazyRouteComponent(component);
27
44
  if (!isPreloadableRouteComponent(component)) return;
28
45
  if ('function' == typeof component.load) return void await component.load({});
29
46
  await component.preload?.({});
@@ -162,6 +179,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
162
179
  };
163
180
  hooks.onBeforeCreateRouter.call(routerLifecycleContext);
164
181
  const tanstackRouter = createRouter({
182
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
165
183
  routeTree,
166
184
  history,
167
185
  basepath: '/',
@@ -207,10 +225,12 @@ const tanstackRouterPlugin = (userConfig = {})=>{
207
225
  await preloadMatchedRouteComponents(serverRouter);
208
226
  context.ssrContext?.response.status(tanstackRouter.state.statusCode);
209
227
  await serverRouter.serverSsr?.dehydrate?.();
210
- await waitForRouterSerialization(serverRouter);
211
- if (isRSCNavigation) setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
212
- omitClientLoaderData: true
213
- }));
228
+ if (isRSCNavigation) {
229
+ await waitForRouterSerialization(serverRouter);
230
+ setTanstackRscServerPayload(createTanstackRscServerPayload(serverRouter, {
231
+ omitClientLoaderData: true
232
+ }));
233
+ }
214
234
  const ssrScriptTags = serverRouter.serverSsr?.takeBufferedScripts?.();
215
235
  const hydrationScripts = routerManagedTagsToHtml(ssrScriptTags);
216
236
  const matchedRouteIds = getModernRouteIdsFromMatches(serverRouter);
@@ -248,11 +268,8 @@ const tanstackRouterPlugin = (userConfig = {})=>{
248
268
  if (!router) return App ? /*#__PURE__*/ jsx(App, {
249
269
  ...props
250
270
  }) : null;
251
- const routerWrapper = /*#__PURE__*/ jsx(Suspense, {
252
- fallback: null,
253
- children: /*#__PURE__*/ jsx(RouterProvider, {
254
- router: router
255
- })
271
+ const routerWrapper = /*#__PURE__*/ jsx(RouterProvider, {
272
+ router: router
256
273
  });
257
274
  return App ? /*#__PURE__*/ jsx(App, {
258
275
  children: routerWrapper
@@ -0,0 +1,2 @@
1
+ import "node:module";
2
+ export { default, tanstackRouterPlugin } from "./plugin.node.mjs";
@@ -1,5 +1,6 @@
1
1
  import "node:module";
2
2
  import { createRootRoute, createRoute, notFound, redirect } from "@tanstack/react-router";
3
+ import { createElement } from "react";
3
4
  import { DefaultNotFound } from "./DefaultNotFound.mjs";
4
5
  import { isTanstackRscPayloadNavigationEnabled, loadTanstackRscRouteData } from "./rsc/payloadRouter.mjs";
5
6
  function createTanstackRoute(options) {
@@ -55,6 +56,40 @@ function normalizeModernLoaderResponse(result) {
55
56
  }
56
57
  return normalizeModernLoaderResult(result);
57
58
  }
59
+ function pickRouteModuleComponent(routeModule, seen = new Set()) {
60
+ if ('function' == typeof routeModule || routeModule && 'object' == typeof routeModule && '$$typeof' in routeModule) return routeModule;
61
+ if (!routeModule || 'object' != typeof routeModule) return;
62
+ if (seen.has(routeModule)) return;
63
+ seen.add(routeModule);
64
+ const module = routeModule;
65
+ for (const candidate of [
66
+ module.default,
67
+ module.Component
68
+ ]){
69
+ const component = pickRouteModuleComponent(candidate, seen);
70
+ if (component) return component;
71
+ }
72
+ }
73
+ function createServerLazyImportComponent(lazyImport, fallbackComponent) {
74
+ if ("u" > typeof document) return fallbackComponent;
75
+ let resolvedComponent;
76
+ let pendingLoad;
77
+ const load = async ()=>{
78
+ if (resolvedComponent) return resolvedComponent;
79
+ const routeModule = await lazyImport();
80
+ const component = pickRouteModuleComponent(routeModule);
81
+ if (component) resolvedComponent = component;
82
+ return resolvedComponent;
83
+ };
84
+ const Component = (props)=>{
85
+ if (resolvedComponent) return createElement(resolvedComponent, props);
86
+ pendingLoad ||= load();
87
+ throw pendingLoad;
88
+ };
89
+ Component.load = load;
90
+ Component.preload = load;
91
+ return Component;
92
+ }
58
93
  function isAbsoluteUrl(value) {
59
94
  try {
60
95
  new URL(value);
@@ -127,7 +162,7 @@ function wrapModernLoader(modernRoute, modernLoader, revalidationState, options
127
162
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
128
163
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
129
164
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
130
- const request = baseRequest ? new Request(baseRequest, {
165
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
131
166
  signal
132
167
  }) : createModernRequest(href, signal);
133
168
  const params = mapParamsForModernLoader({
@@ -192,7 +227,7 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
192
227
  const signal = ctx?.abortController?.signal || ctx?.signal || new AbortController().signal;
193
228
  const baseRequest = ctx?.context?.request instanceof Request ? ctx.context.request : void 0;
194
229
  const href = 'string' == typeof ctx?.location ? ctx.location : ctx?.location?.publicHref || ctx?.location?.href || ctx?.location?.url?.href || '';
195
- const request = baseRequest ? new Request(baseRequest, {
230
+ const request = void 0 !== baseRequest ? new Request(baseRequest, {
196
231
  signal
197
232
  }) : createModernRequest(href, signal);
198
233
  const params = mapParamsForRouteObjectLoader({
@@ -229,10 +264,18 @@ function wrapRouteObjectLoader(route, revalidationState, options = {}) {
229
264
  }
230
265
  function toRouteComponent(routeObject) {
231
266
  const route = routeObject;
267
+ const lazyImport = 'function' == typeof route.lazyImport ? route.lazyImport : void 0;
268
+ const fallbackComponent = route.Component ? route.Component : route.element ? ()=>route.element : void 0;
269
+ if (lazyImport && fallbackComponent) return createServerLazyImportComponent(lazyImport, fallbackComponent);
232
270
  if (route.Component) return route.Component;
233
271
  const element = route.element;
234
272
  if (element) return ()=>element;
235
273
  }
274
+ function toModernRouteComponent(route) {
275
+ const component = route.component || void 0;
276
+ if ('function' == typeof route.lazyImport && component) return createServerLazyImportComponent(route.lazyImport, component);
277
+ return component;
278
+ }
236
279
  function toErrorComponent(routeObject) {
237
280
  const route = routeObject;
238
281
  if (route.ErrorBoundary) return route.ErrorBoundary;
@@ -275,6 +318,8 @@ function createRouteFromRouteObject(opts) {
275
318
  component: toRouteComponent(routeObject),
276
319
  pendingComponent: toPendingComponent(routeObject),
277
320
  errorComponent: toErrorComponent(routeObject),
321
+ validateSearch: modernRouteObject.validateSearch,
322
+ loaderDeps: modernRouteObject.loaderDeps,
278
323
  wrapInSuspense: true,
279
324
  staticData: createRouteStaticData({
280
325
  modernRouteId: routeObject.id,
@@ -313,7 +358,7 @@ function createRouteFromModernRoute(opts) {
313
358
  const stableFallbackId = modernId || route._component || route.filename || route.data || ('function' == typeof route.loader ? route.id : void 0);
314
359
  const pendingComponent = route.loading || route.pendingComponent;
315
360
  const errorComponent = route.error || route.errorComponent;
316
- const component = route.component;
361
+ const component = toModernRouteComponent(route);
317
362
  const modernLoader = route.loader;
318
363
  const modernAction = route.action;
319
364
  const modernShouldRevalidate = route.shouldRevalidate;
@@ -325,6 +370,8 @@ function createRouteFromModernRoute(opts) {
325
370
  component: component || void 0,
326
371
  pendingComponent: pendingComponent || void 0,
327
372
  errorComponent: errorComponent || void 0,
373
+ validateSearch: route.validateSearch,
374
+ loaderDeps: route.loaderDeps,
328
375
  wrapInSuspense: true,
329
376
  staticData: createRouteStaticData({
330
377
  modernRouteId: modernId,
@@ -360,7 +407,7 @@ function createRouteFromModernRoute(opts) {
360
407
  }
361
408
  function createRouteTreeFromModernRoutes(routes, options = {}) {
362
409
  const rootModern = routes.find((r)=>r && 'nested' === r.type && r.isRoot);
363
- const rootComponent = rootModern?.component;
410
+ const rootComponent = rootModern ? toModernRouteComponent(rootModern) : void 0;
364
411
  const pendingComponent = rootModern?.loading;
365
412
  const errorComponent = rootModern?.error;
366
413
  const rootLoader = rootModern?.loader;
@@ -373,6 +420,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
373
420
  component: rootComponent || void 0,
374
421
  pendingComponent: pendingComponent || void 0,
375
422
  errorComponent: errorComponent || void 0,
423
+ validateSearch: rootModern?.validateSearch,
424
+ loaderDeps: rootModern?.loaderDeps,
376
425
  wrapInSuspense: true,
377
426
  notFoundComponent: DefaultNotFound,
378
427
  staticData: createRouteStaticData({
@@ -412,6 +461,8 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
412
461
  component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
413
462
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
414
463
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
464
+ validateSearch: rootLikeRoute?.validateSearch,
465
+ loaderDeps: rootLikeRoute?.loaderDeps,
415
466
  wrapInSuspense: true,
416
467
  notFoundComponent: DefaultNotFound,
417
468
  staticData: createRouteStaticData({