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