@bleedingdev/modern-js-plugin-tanstack 3.2.0-ultramodern.89 → 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 (43) 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 +45 -7
  4. package/dist/cjs/runtime/index.js +12 -0
  5. package/dist/cjs/runtime/plugin.js +2 -0
  6. package/dist/cjs/runtime/plugin.node.js +2 -0
  7. package/dist/cjs/runtime/routeTree.js +8 -0
  8. package/dist/cjs/runtime/types.js +27 -1
  9. package/dist/esm/cli/index.mjs +4 -1
  10. package/dist/esm/cli/routeSplitting.mjs +43 -0
  11. package/dist/esm/cli/tanstackTypes.mjs +45 -7
  12. package/dist/esm/runtime/index.mjs +1 -0
  13. package/dist/esm/runtime/plugin.mjs +2 -0
  14. package/dist/esm/runtime/plugin.node.mjs +2 -0
  15. package/dist/esm/runtime/routeTree.mjs +8 -0
  16. package/dist/esm/runtime/types.mjs +7 -0
  17. package/dist/esm-node/cli/index.mjs +4 -1
  18. package/dist/esm-node/cli/routeSplitting.mjs +44 -0
  19. package/dist/esm-node/cli/tanstackTypes.mjs +45 -7
  20. package/dist/esm-node/runtime/index.mjs +1 -0
  21. package/dist/esm-node/runtime/plugin.mjs +2 -0
  22. package/dist/esm-node/runtime/plugin.node.mjs +2 -0
  23. package/dist/esm-node/runtime/routeTree.mjs +8 -0
  24. package/dist/esm-node/runtime/types.mjs +7 -0
  25. package/dist/types/cli/index.d.ts +4 -0
  26. package/dist/types/cli/routeSplitting.d.ts +29 -0
  27. package/dist/types/runtime/index.d.ts +2 -0
  28. package/dist/types/runtime/plugin.d.ts +1 -1
  29. package/dist/types/runtime/plugin.node.d.ts +1 -1
  30. package/dist/types/runtime/types.d.ts +7 -0
  31. package/package.json +10 -10
  32. package/src/cli/index.ts +17 -0
  33. package/src/cli/routeSplitting.ts +81 -0
  34. package/src/cli/tanstackTypes.ts +102 -13
  35. package/src/runtime/index.tsx +5 -0
  36. package/src/runtime/plugin.node.tsx +6 -1
  37. package/src/runtime/plugin.tsx +5 -1
  38. package/src/runtime/routeTree.ts +12 -0
  39. package/src/runtime/types.ts +13 -0
  40. package/tests/router/cli.test.ts +239 -0
  41. package/tests/router/fastDefaults.test.ts +25 -0
  42. package/tests/router/routeTree.test.ts +87 -0
  43. package/tests/router/tanstackTypes.test.ts +120 -0
@@ -12,6 +12,7 @@ import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreate
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?.();
@@ -177,6 +178,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
177
178
  };
178
179
  hooks.onBeforeCreateRouter.call(routerLifecycleContext);
179
180
  const tanstackRouter = createRouter({
181
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
180
182
  routeTree,
181
183
  history,
182
184
  basepath: '/',
@@ -317,6 +317,8 @@ function createRouteFromRouteObject(opts) {
317
317
  component: toRouteComponent(routeObject),
318
318
  pendingComponent: toPendingComponent(routeObject),
319
319
  errorComponent: toErrorComponent(routeObject),
320
+ validateSearch: modernRouteObject.validateSearch,
321
+ loaderDeps: modernRouteObject.loaderDeps,
320
322
  wrapInSuspense: true,
321
323
  staticData: createRouteStaticData({
322
324
  modernRouteId: routeObject.id,
@@ -367,6 +369,8 @@ function createRouteFromModernRoute(opts) {
367
369
  component: component || void 0,
368
370
  pendingComponent: pendingComponent || void 0,
369
371
  errorComponent: errorComponent || void 0,
372
+ validateSearch: route.validateSearch,
373
+ loaderDeps: route.loaderDeps,
370
374
  wrapInSuspense: true,
371
375
  staticData: createRouteStaticData({
372
376
  modernRouteId: modernId,
@@ -415,6 +419,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
415
419
  component: rootComponent || void 0,
416
420
  pendingComponent: pendingComponent || void 0,
417
421
  errorComponent: errorComponent || void 0,
422
+ validateSearch: rootModern?.validateSearch,
423
+ loaderDeps: rootModern?.loaderDeps,
418
424
  wrapInSuspense: true,
419
425
  notFoundComponent: DefaultNotFound,
420
426
  staticData: createRouteStaticData({
@@ -454,6 +460,8 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
454
460
  component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
455
461
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
456
462
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
463
+ validateSearch: rootLikeRoute?.validateSearch,
464
+ loaderDeps: rootLikeRoute?.loaderDeps,
457
465
  wrapInSuspense: true,
458
466
  notFoundComponent: DefaultNotFound,
459
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 };
@@ -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,9 +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();
92
101
  const usedRouteVarNames = new Set();
93
102
  let loaderIndex = 0;
103
+ let validateSearchIndex = 0;
104
+ let loaderDepsIndex = 0;
94
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
+ };
95
115
  const getImportNamesForLoader = async (aliasedNoExtPath, inline, hasAction)=>{
96
116
  const key = `${inline ? 'inline' : 'default'}:${hasAction ? 'action' : 'loader'}:${aliasedNoExtPath}`;
97
117
  const existing = loaderImportMap.get(key);
@@ -99,13 +119,7 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
99
119
  loaderName: existing,
100
120
  actionName: hasAction ? existing.replace(/^loader_/, 'action_') : null
101
121
  };
102
- const prefix = `${appContext.internalSrcAlias}/`;
103
- let absNoExt;
104
- if (aliasedNoExtPath.startsWith(prefix)) {
105
- const rel = aliasedNoExtPath.slice(prefix.length);
106
- absNoExt = path.join(appContext.srcDirectory, rel);
107
- } else absNoExt = path.isAbsolute(aliasedNoExtPath) ? aliasedNoExtPath : path.join(appContext.srcDirectory, aliasedNoExtPath);
108
- const resolvedNoExt = await resolveFileNoExt(absNoExt);
122
+ const resolvedNoExt = await resolveRouteModuleNoExt(aliasedNoExtPath);
109
123
  if (!resolvedNoExt) return null;
110
124
  const relImport = normalizeRelativeImport(path.relative(outDir, resolvedNoExt));
111
125
  const importName = `loader_${loaderIndex++}`;
@@ -123,6 +137,18 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
123
137
  actionName
124
138
  };
125
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
+ };
126
152
  const reserveRouteVarName = (preferred)=>{
127
153
  let candidate = preferred;
128
154
  let suffix = 1;
@@ -143,6 +169,9 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
143
169
  const loaderImports = loaderInfo ? await getImportNamesForLoader(loaderInfo.loaderPath, loaderInfo.inline, Boolean(loaderInfo.inline && routeAction === loaderInfo.loaderPath)) : null;
144
170
  const loaderName = loaderImports?.loaderName || null;
145
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;
146
175
  const rawPath = route.path;
147
176
  const hasSplat = 'string' == typeof rawPath && rawPath.includes('*');
148
177
  const routeOpts = [
@@ -156,6 +185,8 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
156
185
  routeOpts.push(`path: ${quote(p)},`);
157
186
  }
158
187
  if (loaderName) routeOpts.push(`loader: modernLoaderToTanstack({ hasSplat: ${hasSplat} }, ${loaderName}),`);
188
+ if (validateSearchName) routeOpts.push(`validateSearch: ${validateSearchName},`);
189
+ if (loaderDepsName) routeOpts.push(`loaderDeps: ${loaderDepsName},`);
159
190
  const staticDataSnippet = createRouteStaticDataSnippet({
160
191
  modernRouteId: route.id,
161
192
  loaderName,
@@ -180,17 +211,23 @@ async function generateTanstackRouterTypesSourceForEntry(opts) {
180
211
  const rootLoaderImports = rootLoaderInfo?.loaderPath ? await getImportNamesForLoader(rootLoaderInfo.loaderPath, rootLoaderInfo.inline, Boolean(rootLoaderInfo.inline && rootAction === rootLoaderInfo.loaderPath)) : null;
181
212
  const rootLoaderName = rootLoaderImports?.loaderName || null;
182
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;
183
217
  const topLevelVars = await Promise.all(topLevel.map((route)=>buildRoute({
184
218
  parentVar: 'rootRoute',
185
219
  route
186
220
  })));
187
221
  const rootOpts = [];
188
222
  if (rootLoaderName) rootOpts.push(`loader: modernLoaderToTanstack({ hasSplat: false }, ${rootLoaderName}),`);
223
+ if (rootValidateSearchName) rootOpts.push(`validateSearch: ${rootValidateSearchName},`);
224
+ if (rootLoaderDepsName) rootOpts.push(`loaderDeps: ${rootLoaderDepsName},`);
189
225
  const routerGenTs = `/* eslint-disable */
190
226
  // This file is auto-generated by Modern.js. Do not edit manually.
191
227
 
192
228
  import {
193
229
  createMemoryHistory,
230
+ modernTanstackRouterFastDefaults,
194
231
  createRootRouteWithContext,
195
232
  createRoute,
196
233
  createRouter,
@@ -378,6 +415,7 @@ ${statements.join('\n\n')}
378
415
  export const routeTree = rootRoute.addChildren([${topLevelVars.join(', ')}]);
379
416
 
380
417
  export const router = createRouter({
418
+ ...modernTanstackRouterFastDefaults,
381
419
  routeTree,
382
420
  history: createMemoryHistory({
383
421
  initialEntries: ['/'],
@@ -5,3 +5,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";
@@ -12,6 +12,7 @@ import { applyRouterRuntimeState } from "./lifecycle.mjs";
12
12
  import { Link } from "./prefetchLink.mjs";
13
13
  import { createRouteTreeFromRouteObjects } from "./routeTree.mjs";
14
14
  import { getTanstackRscSerializationAdapters } from "./rsc/client.mjs";
15
+ import { getModernTanstackRouterFastDefaults } from "./types.mjs";
15
16
  import { createRouteObjectsFromConfig, urlJoin } from "./utils.mjs";
16
17
  const BLOCKING_SUBSCRIBE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-subscribe');
17
18
  const BLOCKING_STATE_SYMBOL = Symbol.for('@modern-js/plugin-tanstack:blocking-state');
@@ -145,6 +146,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
145
146
  const rewrite = createModernBasepathRewrite(_basename);
146
147
  const serializationAdapters = getGlobalEnableRsc() ? getTanstackRscSerializationAdapters() : void 0;
147
148
  cachedRouter = createRouter({
149
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
148
150
  routeTree,
149
151
  basepath: '/',
150
152
  rewrite,
@@ -13,6 +13,7 @@ import { modifyRoutes, onAfterCreateRouter, onAfterHydrateRouter, onBeforeCreate
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?.();
@@ -178,6 +179,7 @@ const tanstackRouterPlugin = (userConfig = {})=>{
178
179
  };
179
180
  hooks.onBeforeCreateRouter.call(routerLifecycleContext);
180
181
  const tanstackRouter = createRouter({
182
+ ...getModernTanstackRouterFastDefaults(mergedConfig),
181
183
  routeTree,
182
184
  history,
183
185
  basepath: '/',
@@ -318,6 +318,8 @@ function createRouteFromRouteObject(opts) {
318
318
  component: toRouteComponent(routeObject),
319
319
  pendingComponent: toPendingComponent(routeObject),
320
320
  errorComponent: toErrorComponent(routeObject),
321
+ validateSearch: modernRouteObject.validateSearch,
322
+ loaderDeps: modernRouteObject.loaderDeps,
321
323
  wrapInSuspense: true,
322
324
  staticData: createRouteStaticData({
323
325
  modernRouteId: routeObject.id,
@@ -368,6 +370,8 @@ function createRouteFromModernRoute(opts) {
368
370
  component: component || void 0,
369
371
  pendingComponent: pendingComponent || void 0,
370
372
  errorComponent: errorComponent || void 0,
373
+ validateSearch: route.validateSearch,
374
+ loaderDeps: route.loaderDeps,
371
375
  wrapInSuspense: true,
372
376
  staticData: createRouteStaticData({
373
377
  modernRouteId: modernId,
@@ -416,6 +420,8 @@ function createRouteTreeFromModernRoutes(routes, options = {}) {
416
420
  component: rootComponent || void 0,
417
421
  pendingComponent: pendingComponent || void 0,
418
422
  errorComponent: errorComponent || void 0,
423
+ validateSearch: rootModern?.validateSearch,
424
+ loaderDeps: rootModern?.loaderDeps,
419
425
  wrapInSuspense: true,
420
426
  notFoundComponent: DefaultNotFound,
421
427
  staticData: createRouteStaticData({
@@ -455,6 +461,8 @@ function createRouteTreeFromRouteObjects(routes, options = {}) {
455
461
  component: rootLikeRoute ? toRouteComponent(rootLikeRoute) : void 0,
456
462
  pendingComponent: rootLikeRoute ? toPendingComponent(rootLikeRoute) : void 0,
457
463
  errorComponent: rootLikeRoute ? toErrorComponent(rootLikeRoute) : void 0,
464
+ validateSearch: rootLikeRoute?.validateSearch,
465
+ loaderDeps: rootLikeRoute?.loaderDeps,
458
466
  wrapInSuspense: true,
459
467
  notFoundComponent: DefaultNotFound,
460
468
  staticData: createRouteStaticData({
@@ -1 +1,8 @@
1
1
  import "node:module";
2
+ const modernTanstackRouterFastDefaults = {
3
+ defaultStructuralSharing: true
4
+ };
5
+ const getModernTanstackRouterFastDefaults = (config = {})=>({
6
+ defaultStructuralSharing: config.defaultStructuralSharing ?? modernTanstackRouterFastDefaults.defaultStructuralSharing
7
+ });
8
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults };
@@ -1,9 +1,13 @@
1
1
  import type { AppTools, AppToolsContext, CliPlugin } from '@modern-js/app-tools';
2
2
  import type { NestedRouteForCli, PageRoute } from '@modern-js/types';
3
+ import { type TanstackRouteCodeSplittingOption } from './routeSplitting';
4
+ export type { TanstackRouteCodeSplittingOption, TanstackRsbuildRouteSplittingProfile, } from './routeSplitting';
5
+ export { createTanstackRsbuildRouteSplittingProfile, isTanstackStartRouteModuleSource, resolveTanstackRouteCodeSplittingEnabled, } from './routeSplitting';
3
6
  export { generateTanstackRouterTypesSourceForEntry, isTanstackRouterFrameworkEnabled, } from './tanstackTypes';
4
7
  export type TanstackRouterPluginOptions = {
5
8
  routesDir?: string;
6
9
  generatedDirName?: string;
10
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
7
11
  };
8
12
  export declare function writeTanstackRegisterFile(opts: {
9
13
  entries: string[];
@@ -0,0 +1,29 @@
1
+ export type TanstackRouteCodeSplittingOption = boolean | {
2
+ enabled?: boolean;
3
+ };
4
+ export type TanstackRsbuildRouteSplittingProfile = {
5
+ defaultConfig: {
6
+ output: {
7
+ splitRouteChunks: boolean;
8
+ };
9
+ };
10
+ modernRouteChunks: {
11
+ enabled: boolean;
12
+ owner: 'modern';
13
+ };
14
+ builderChunkSplit: {
15
+ owner: 'modern-rsbuild';
16
+ preserved: true;
17
+ };
18
+ tanstackStartRspackSplitter: {
19
+ compatible: boolean;
20
+ reason: string;
21
+ clientDeleteNodes: string[];
22
+ routeFactoryCalls: string[];
23
+ };
24
+ };
25
+ export declare function isTanstackStartRouteModuleSource(source: string): boolean;
26
+ export declare function resolveTanstackRouteCodeSplittingEnabled(option?: TanstackRouteCodeSplittingOption): boolean;
27
+ export declare function createTanstackRsbuildRouteSplittingProfile(opts: {
28
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
29
+ }): TanstackRsbuildRouteSplittingProfile;
@@ -7,3 +7,5 @@ export type { LinkProps, NavLinkProps, PrefetchBehavior, } from './prefetchLink'
7
7
  export { Link, NavLink } from './prefetchLink';
8
8
  export type { AnyCompositeComponent, AnyRenderableServerComponent, CompositeComponentProps, } from './rsc/client';
9
9
  export { CompositeComponent } from './rsc/client';
10
+ export type { RouterConfig } from './types';
11
+ export { getModernTanstackRouterFastDefaults, modernTanstackRouterFastDefaults, } from './types';
@@ -2,7 +2,7 @@ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
2
  import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
3
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
4
  import { type RouterExtendsHooks } from './hooks';
5
- import type { RouterConfig } from './types';
5
+ import { type RouterConfig } from './types';
6
6
  type TanstackRouterRuntimeConfig = {
7
7
  plugins?: TanstackRouterRuntimePlugin[];
8
8
  router?: Partial<RouterConfig>;
@@ -2,7 +2,7 @@ import type { Plugin, RuntimePluginExtends } from '@modern-js/plugin';
2
2
  import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
3
3
  import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
4
4
  import { type RouterExtendsHooks } from './hooks';
5
- import type { RouterConfig } from './types';
5
+ import { type RouterConfig } from './types';
6
6
  type TanstackRouterRuntimeConfig = {
7
7
  plugins?: TanstackRouterRuntimePlugin[];
8
8
  router?: Partial<RouterConfig>;
@@ -18,8 +18,15 @@ export type RouterConfig = {
18
18
  future?: Partial<{
19
19
  v7_startTransition: boolean;
20
20
  }>;
21
+ defaultStructuralSharing?: boolean;
21
22
  unstable_reloadOnURLMismatch?: boolean;
22
23
  };
24
+ export declare const modernTanstackRouterFastDefaults: {
25
+ readonly defaultStructuralSharing: true;
26
+ };
27
+ export declare const getModernTanstackRouterFastDefaults: (config?: Partial<Pick<RouterConfig, 'defaultStructuralSharing'>>) => {
28
+ defaultStructuralSharing: boolean;
29
+ };
23
30
  export interface RouterRouteMatchSnapshot {
24
31
  routeId: string;
25
32
  assetRouteId?: string;
package/package.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "modern.js",
19
19
  "tanstack-router"
20
20
  ],
21
- "version": "3.2.0-ultramodern.89",
21
+ "version": "3.2.0-ultramodern.90",
22
22
  "engines": {
23
23
  "node": ">=20"
24
24
  },
@@ -86,15 +86,15 @@
86
86
  },
87
87
  "dependencies": {
88
88
  "@swc/helpers": "^0.5.23",
89
- "@tanstack/react-router": "1.170.8",
90
- "@tanstack/router-core": "1.171.6",
91
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.89",
92
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.89",
93
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.89",
94
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.89"
89
+ "@tanstack/react-router": "1.170.11",
90
+ "@tanstack/router-core": "1.171.9",
91
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.90",
92
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.90",
93
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.90",
94
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.90"
95
95
  },
96
96
  "peerDependencies": {
97
- "@modern-js/runtime": "3.2.0-ultramodern.89",
97
+ "@modern-js/runtime": "3.2.0-ultramodern.90",
98
98
  "react": "^19.2.6",
99
99
  "react-dom": "^19.2.6"
100
100
  },
@@ -109,8 +109,8 @@
109
109
  "@typescript/native-preview": "7.0.0-dev.20260527.2",
110
110
  "react": "^19.2.6",
111
111
  "react-dom": "^19.2.6",
112
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.89",
113
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.89",
112
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.90",
113
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.90",
114
114
  "@scripts/rstest-config": "2.66.0"
115
115
  },
116
116
  "sideEffects": false,
package/src/cli/index.ts CHANGED
@@ -18,11 +18,24 @@ import {
18
18
  fs,
19
19
  NESTED_ROUTE_SPEC_FILE,
20
20
  } from '@modern-js/utils';
21
+ import {
22
+ createTanstackRsbuildRouteSplittingProfile,
23
+ type TanstackRouteCodeSplittingOption,
24
+ } from './routeSplitting';
21
25
  import {
22
26
  generateTanstackRouterTypesSourceForEntry,
23
27
  isTanstackRouterFrameworkEnabled,
24
28
  } from './tanstackTypes';
25
29
 
30
+ export type {
31
+ TanstackRouteCodeSplittingOption,
32
+ TanstackRsbuildRouteSplittingProfile,
33
+ } from './routeSplitting';
34
+ export {
35
+ createTanstackRsbuildRouteSplittingProfile,
36
+ isTanstackStartRouteModuleSource,
37
+ resolveTanstackRouteCodeSplittingEnabled,
38
+ } from './routeSplitting';
26
39
  export {
27
40
  generateTanstackRouterTypesSourceForEntry,
28
41
  isTanstackRouterFrameworkEnabled,
@@ -35,6 +48,7 @@ const ENTRYPOINTS_KEY = '@modern-js/plugin-tanstack';
35
48
  export type TanstackRouterPluginOptions = {
36
49
  routesDir?: string;
37
50
  generatedDirName?: string;
51
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
38
52
  };
39
53
 
40
54
  type RuntimeRouterCliHelpers = {
@@ -224,6 +238,8 @@ export function tanstackRouterPlugin(
224
238
  const routesDir = options.routesDir || DEFAULT_ROUTES_DIR;
225
239
  const generatedDirName =
226
240
  options.generatedDirName || DEFAULT_GENERATED_DIR_NAME;
241
+ const routeSplittingProfile =
242
+ createTanstackRsbuildRouteSplittingProfile(options);
227
243
 
228
244
  return {
229
245
  name: '@modern-js/plugin-tanstack',
@@ -265,6 +281,7 @@ export function tanstackRouterPlugin(
265
281
  }));
266
282
 
267
283
  api.config(() => ({
284
+ ...routeSplittingProfile.defaultConfig,
268
285
  source: {
269
286
  include: [
270
287
  /[\\/]node_modules[\\/]@tanstack[\\/]react-router[\\/]/,
@@ -0,0 +1,81 @@
1
+ export type TanstackRouteCodeSplittingOption =
2
+ | boolean
3
+ | {
4
+ enabled?: boolean;
5
+ };
6
+
7
+ export type TanstackRsbuildRouteSplittingProfile = {
8
+ defaultConfig: {
9
+ output: {
10
+ splitRouteChunks: boolean;
11
+ };
12
+ };
13
+ modernRouteChunks: {
14
+ enabled: boolean;
15
+ owner: 'modern';
16
+ };
17
+ builderChunkSplit: {
18
+ owner: 'modern-rsbuild';
19
+ preserved: true;
20
+ };
21
+ tanstackStartRspackSplitter: {
22
+ compatible: boolean;
23
+ reason: string;
24
+ clientDeleteNodes: string[];
25
+ routeFactoryCalls: string[];
26
+ };
27
+ };
28
+
29
+ const TANSTACK_START_ROUTE_FACTORY_CALLS = [
30
+ 'createFileRoute',
31
+ 'createRootRoute',
32
+ 'createRootRouteWithContext',
33
+ ] as const;
34
+
35
+ const TANSTACK_START_ROUTE_FACTORY_REGEX =
36
+ /\b(createFileRoute|createRootRoute|createRootRouteWithContext)\s*(?:<|\()/;
37
+
38
+ export function isTanstackStartRouteModuleSource(source: string) {
39
+ return TANSTACK_START_ROUTE_FACTORY_REGEX.test(source);
40
+ }
41
+
42
+ export function resolveTanstackRouteCodeSplittingEnabled(
43
+ option?: TanstackRouteCodeSplittingOption,
44
+ ) {
45
+ if (typeof option === 'boolean') {
46
+ return option;
47
+ }
48
+
49
+ return option?.enabled ?? true;
50
+ }
51
+
52
+ export function createTanstackRsbuildRouteSplittingProfile(opts: {
53
+ routeCodeSplitting?: TanstackRouteCodeSplittingOption;
54
+ }): TanstackRsbuildRouteSplittingProfile {
55
+ return {
56
+ defaultConfig: {
57
+ output: {
58
+ splitRouteChunks: resolveTanstackRouteCodeSplittingEnabled(
59
+ opts.routeCodeSplitting,
60
+ ),
61
+ },
62
+ },
63
+ modernRouteChunks: {
64
+ enabled: resolveTanstackRouteCodeSplittingEnabled(
65
+ opts.routeCodeSplitting,
66
+ ),
67
+ owner: 'modern',
68
+ },
69
+ builderChunkSplit: {
70
+ owner: 'modern-rsbuild',
71
+ preserved: true,
72
+ },
73
+ tanstackStartRspackSplitter: {
74
+ compatible: false,
75
+ reason:
76
+ '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.',
77
+ clientDeleteNodes: ['ssr', 'server', 'headers'],
78
+ routeFactoryCalls: [...TANSTACK_START_ROUTE_FACTORY_CALLS],
79
+ },
80
+ };
81
+ }