@tanstack/router-plugin 1.121.0-alpha.26 → 1.121.0-alpha.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/cjs/core/code-splitter/compilers.cjs +66 -40
  2. package/dist/cjs/core/code-splitter/compilers.cjs.map +1 -1
  3. package/dist/cjs/core/code-splitter/compilers.d.cts +3 -3
  4. package/dist/cjs/core/code-splitter/framework-options.cjs.map +1 -1
  5. package/dist/cjs/core/code-splitter/path-ids.cjs.map +1 -1
  6. package/dist/cjs/core/config.cjs +11 -1
  7. package/dist/cjs/core/config.cjs.map +1 -1
  8. package/dist/cjs/core/config.d.cts +49 -4
  9. package/dist/cjs/core/route-autoimport-plugin.cjs +52 -45
  10. package/dist/cjs/core/route-autoimport-plugin.cjs.map +1 -1
  11. package/dist/cjs/core/route-hmr-statement.cjs.map +1 -1
  12. package/dist/cjs/core/router-code-splitter-plugin.cjs +84 -64
  13. package/dist/cjs/core/router-code-splitter-plugin.cjs.map +1 -1
  14. package/dist/cjs/core/router-composed-plugin.cjs +12 -3
  15. package/dist/cjs/core/router-composed-plugin.cjs.map +1 -1
  16. package/dist/cjs/core/router-generator-plugin.cjs +27 -25
  17. package/dist/cjs/core/router-generator-plugin.cjs.map +1 -1
  18. package/dist/cjs/core/router-hmr-plugin.cjs +38 -29
  19. package/dist/cjs/core/router-hmr-plugin.cjs.map +1 -1
  20. package/dist/cjs/core/router-hmr-plugin.d.cts +1 -6
  21. package/dist/cjs/core/utils.cjs +0 -7
  22. package/dist/cjs/core/utils.cjs.map +1 -1
  23. package/dist/cjs/core/utils.d.cts +0 -1
  24. package/dist/cjs/esbuild.cjs.map +1 -1
  25. package/dist/cjs/esbuild.d.cts +20 -0
  26. package/dist/cjs/index.cjs +4 -0
  27. package/dist/cjs/index.cjs.map +1 -1
  28. package/dist/cjs/index.d.cts +1 -0
  29. package/dist/cjs/rspack.cjs.map +1 -1
  30. package/dist/cjs/rspack.d.cts +20 -0
  31. package/dist/cjs/vite.cjs.map +1 -1
  32. package/dist/cjs/vite.d.cts +25 -0
  33. package/dist/cjs/webpack.cjs.map +1 -1
  34. package/dist/cjs/webpack.d.cts +20 -0
  35. package/dist/esm/core/code-splitter/compilers.d.ts +3 -3
  36. package/dist/esm/core/code-splitter/compilers.js +66 -40
  37. package/dist/esm/core/code-splitter/compilers.js.map +1 -1
  38. package/dist/esm/core/code-splitter/framework-options.js.map +1 -1
  39. package/dist/esm/core/code-splitter/path-ids.js.map +1 -1
  40. package/dist/esm/core/config.d.ts +49 -4
  41. package/dist/esm/core/config.js +11 -1
  42. package/dist/esm/core/config.js.map +1 -1
  43. package/dist/esm/core/route-autoimport-plugin.js +53 -46
  44. package/dist/esm/core/route-autoimport-plugin.js.map +1 -1
  45. package/dist/esm/core/route-hmr-statement.js.map +1 -1
  46. package/dist/esm/core/router-code-splitter-plugin.js +86 -66
  47. package/dist/esm/core/router-code-splitter-plugin.js.map +1 -1
  48. package/dist/esm/core/router-composed-plugin.js +11 -2
  49. package/dist/esm/core/router-composed-plugin.js.map +1 -1
  50. package/dist/esm/core/router-generator-plugin.js +28 -26
  51. package/dist/esm/core/router-generator-plugin.js.map +1 -1
  52. package/dist/esm/core/router-hmr-plugin.d.ts +1 -6
  53. package/dist/esm/core/router-hmr-plugin.js +39 -30
  54. package/dist/esm/core/router-hmr-plugin.js.map +1 -1
  55. package/dist/esm/core/utils.d.ts +0 -1
  56. package/dist/esm/core/utils.js +1 -8
  57. package/dist/esm/core/utils.js.map +1 -1
  58. package/dist/esm/esbuild.d.ts +20 -0
  59. package/dist/esm/esbuild.js.map +1 -1
  60. package/dist/esm/index.d.ts +1 -0
  61. package/dist/esm/index.js +4 -0
  62. package/dist/esm/index.js.map +1 -1
  63. package/dist/esm/rspack.d.ts +20 -0
  64. package/dist/esm/vite.d.ts +25 -0
  65. package/dist/esm/vite.js.map +1 -1
  66. package/dist/esm/webpack.d.ts +20 -0
  67. package/package.json +15 -15
  68. package/src/core/code-splitter/compilers.ts +95 -60
  69. package/src/core/config.ts +25 -0
  70. package/src/core/route-autoimport-plugin.ts +59 -53
  71. package/src/core/router-code-splitter-plugin.ts +95 -80
  72. package/src/core/router-composed-plugin.ts +12 -2
  73. package/src/core/router-generator-plugin.ts +32 -32
  74. package/src/core/router-hmr-plugin.ts +42 -37
  75. package/src/core/utils.ts +0 -15
  76. package/src/global.d.ts +7 -0
  77. package/src/index.ts +5 -0
@@ -12,7 +12,7 @@ import { createIdentifier } from './path-ids'
12
12
  import { getFrameworkOptions } from './framework-options'
13
13
  import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils'
14
14
  import type { CodeSplitGroupings, SplitRouteIdentNodes } from '../constants'
15
- import type { Config } from '../config'
15
+ import type { Config, DeletableNodes } from '../config'
16
16
 
17
17
  type SplitNodeMeta = {
18
18
  routeIdent: SplitRouteIdentNodes
@@ -92,13 +92,24 @@ function removeSplitSearchParamFromFilename(filename: string) {
92
92
  return bareFilename!
93
93
  }
94
94
 
95
+ const splittableCreateRouteFns = ['createFileRoute']
96
+ const unsplittableCreateRouteFns = [
97
+ 'createRootRoute',
98
+ 'createRootRouteWithContext',
99
+ ]
100
+ const allCreateRouteFns = [
101
+ ...splittableCreateRouteFns,
102
+ ...unsplittableCreateRouteFns,
103
+ ]
104
+
95
105
  export function compileCodeSplitReferenceRoute(
96
106
  opts: ParseAstOptions & {
97
- runtimeEnv: 'dev' | 'prod'
98
107
  codeSplitGroupings: CodeSplitGroupings
108
+ deleteNodes?: Set<DeletableNodes>
99
109
  targetFramework: Config['target']
100
110
  filename: string
101
111
  id: string
112
+ addHmr?: boolean
102
113
  },
103
114
  ): GeneratorResult {
104
115
  const ast = parseAst(opts)
@@ -116,6 +127,8 @@ export function compileCodeSplitReferenceRoute(
116
127
  const LAZY_ROUTE_COMPONENT_IDENT = frameworkOptions.idents.lazyRouteComponent
117
128
  const LAZY_FN_IDENT = frameworkOptions.idents.lazyFn
118
129
 
130
+ let createRouteFn: string
131
+
119
132
  babel.traverse(ast, {
120
133
  Program: {
121
134
  enter(programPath) {
@@ -136,29 +149,48 @@ export function compileCodeSplitReferenceRoute(
136
149
  return
137
150
  }
138
151
 
139
- if (
140
- !(
141
- path.node.callee.name === 'createRoute' ||
142
- path.node.callee.name === 'createFileRoute'
143
- )
144
- ) {
152
+ if (!allCreateRouteFns.includes(path.node.callee.name)) {
145
153
  return
146
154
  }
147
155
 
156
+ createRouteFn = path.node.callee.name
157
+
148
158
  function babelHandleReference(routeOptions: t.Node | undefined) {
149
159
  const hasImportedOrDefinedIdentifier = (name: string) => {
150
160
  return programPath.scope.hasBinding(name)
151
161
  }
152
162
 
153
163
  if (t.isObjectExpression(routeOptions)) {
164
+ if (opts.deleteNodes && opts.deleteNodes.size > 0) {
165
+ routeOptions.properties = routeOptions.properties.filter(
166
+ (prop) => {
167
+ if (t.isObjectProperty(prop)) {
168
+ if (t.isIdentifier(prop.key)) {
169
+ if (opts.deleteNodes?.has(prop.key.name as any)) {
170
+ return false
171
+ }
172
+ }
173
+ }
174
+ return true
175
+ },
176
+ )
177
+ }
178
+ if (!splittableCreateRouteFns.includes(createRouteFn)) {
179
+ // we can't split this route but we still add HMR handling if enabled
180
+ if (opts.addHmr) {
181
+ programPath.pushContainer('body', routeHmrStatement)
182
+ }
183
+ // exit traversal so this route is not split
184
+ return programPath.stop()
185
+ }
154
186
  routeOptions.properties.forEach((prop) => {
155
187
  if (t.isObjectProperty(prop)) {
156
188
  if (t.isIdentifier(prop.key)) {
189
+ const key = prop.key.name
190
+
157
191
  // If the user has not specified a split grouping for this key
158
192
  // then we should not split it
159
- const codeSplitGroupingByKey = findIndexForSplitNode(
160
- prop.key.name,
161
- )
193
+ const codeSplitGroupingByKey = findIndexForSplitNode(key)
162
194
  if (codeSplitGroupingByKey === -1) {
163
195
  return
164
196
  }
@@ -168,7 +200,6 @@ export function compileCodeSplitReferenceRoute(
168
200
  ),
169
201
  ]
170
202
 
171
- const key = prop.key.name
172
203
  // find key in nodeSplitConfig
173
204
  const isNodeConfigAvailable = SPLIT_NODES_CONFIG.has(
174
205
  key as any,
@@ -178,6 +209,16 @@ export function compileCodeSplitReferenceRoute(
178
209
  return
179
210
  }
180
211
 
212
+ // Exit early if the value is undefined
213
+ // Since we don't need to run an import just to get the value of `undefined`
214
+ // This is useful for cases like: `createFileRoute('/')({ component: undefined })`
215
+ if (
216
+ t.isIdentifier(prop.value) &&
217
+ prop.value.name === 'undefined'
218
+ ) {
219
+ return
220
+ }
221
+
181
222
  const splitNodeMeta = SPLIT_NODES_CONFIG.get(key as any)!
182
223
 
183
224
  // We need to extract the existing search params from the filename, if any
@@ -222,7 +263,6 @@ export function compileCodeSplitReferenceRoute(
222
263
  // Prepend the import statement to the program along with the importer function
223
264
  // Check to see if lazyRouteComponent is already imported before attempting
224
265
  // to import it again
225
-
226
266
  if (
227
267
  !hasImportedOrDefinedIdentifier(
228
268
  LAZY_ROUTE_COMPONENT_IDENT,
@@ -249,19 +289,12 @@ export function compileCodeSplitReferenceRoute(
249
289
  ])
250
290
  }
251
291
 
252
- // If it's a component, we need to pass the function to check the Route.ssr value
253
- if (key === 'component') {
254
- prop.value = template.expression(
255
- `${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}', () => Route.ssr)`,
256
- )()
257
- } else {
258
- prop.value = template.expression(
259
- `${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')`,
260
- )()
261
- }
292
+ prop.value = template.expression(
293
+ `${LAZY_ROUTE_COMPONENT_IDENT}(${splitNodeMeta.localImporterIdent}, '${splitNodeMeta.exporterIdent}')`,
294
+ )()
262
295
 
263
296
  // add HMR handling
264
- if (opts.runtimeEnv !== 'prod') {
297
+ if (opts.addHmr) {
265
298
  programPath.pushContainer('body', routeHmrStatement)
266
299
  }
267
300
  }
@@ -416,12 +449,7 @@ export function compileCodeSplitVirtualRoute(
416
449
  return
417
450
  }
418
451
 
419
- if (
420
- !(
421
- path.node.callee.name === 'createRoute' ||
422
- path.node.callee.name === 'createFileRoute'
423
- )
424
- ) {
452
+ if (!splittableCreateRouteFns.includes(path.node.callee.name)) {
425
453
  return
426
454
  }
427
455
 
@@ -442,6 +470,14 @@ export function compileCodeSplitVirtualRoute(
442
470
 
443
471
  const value = prop.value
444
472
 
473
+ // If the value for the `key` is `undefined`, then we don't need to include it
474
+ // in the split file, so we can just return, since it will kept in-place in the
475
+ // reference file
476
+ // This is useful for cases like: `createFileRoute('/')({ component: undefined })`
477
+ if (t.isIdentifier(value) && value.name === 'undefined') {
478
+ return
479
+ }
480
+
445
481
  let isExported = false
446
482
  if (t.isIdentifier(value)) {
447
483
  isExported = hasExport(ast, value)
@@ -706,12 +742,9 @@ export function compileCodeSplitVirtualRoute(
706
742
  */
707
743
  export function detectCodeSplitGroupingsFromRoute(opts: ParseAstOptions): {
708
744
  groupings: CodeSplitGroupings | undefined
709
- routeId: string
710
745
  } {
711
746
  const ast = parseAst(opts)
712
747
 
713
- let routeId = ''
714
-
715
748
  let codeSplitGroupings: CodeSplitGroupings | undefined = undefined
716
749
 
717
750
  babel.traverse(ast, {
@@ -732,26 +765,11 @@ export function detectCodeSplitGroupingsFromRoute(opts: ParseAstOptions): {
732
765
  return
733
766
  }
734
767
 
735
- if (t.isCallExpression(path.parentPath.node)) {
736
- // Extract out the routeId
737
- if (t.isCallExpression(path.parentPath.node.callee)) {
738
- const callee = path.parentPath.node.callee
739
-
740
- if (t.isIdentifier(callee.callee)) {
741
- const firstArg = callee.arguments[0]
742
- if (t.isStringLiteral(firstArg)) {
743
- routeId = firstArg.value
744
- }
745
- }
746
- }
747
-
748
- // Extracting the codeSplitGroupings
749
- const options = resolveIdentifier(
750
- path,
751
- path.parentPath.node.arguments[0],
752
- )
753
- if (t.isObjectExpression(options)) {
754
- options.properties.forEach((prop) => {
768
+ function babelHandleSplittingGroups(
769
+ routeOptions: t.Node | undefined,
770
+ ) {
771
+ if (t.isObjectExpression(routeOptions)) {
772
+ routeOptions.properties.forEach((prop) => {
755
773
  if (t.isObjectProperty(prop)) {
756
774
  if (t.isIdentifier(prop.key)) {
757
775
  if (prop.key.name === 'codeSplitGroupings') {
@@ -786,13 +804,32 @@ export function detectCodeSplitGroupingsFromRoute(opts: ParseAstOptions): {
786
804
  })
787
805
  }
788
806
  }
807
+
808
+ // Extracting the codeSplitGroupings
809
+ if (t.isCallExpression(path.parentPath.node)) {
810
+ // createFileRoute('/')({ ... })
811
+ const options = resolveIdentifier(
812
+ path,
813
+ path.parentPath.node.arguments[0],
814
+ )
815
+
816
+ babelHandleSplittingGroups(options)
817
+ } else if (t.isVariableDeclarator(path.parentPath.node)) {
818
+ // createFileRoute({ ... })
819
+ const caller = resolveIdentifier(path, path.parentPath.node.init)
820
+
821
+ if (t.isCallExpression(caller)) {
822
+ const options = resolveIdentifier(path, caller.arguments[0])
823
+ babelHandleSplittingGroups(options)
824
+ }
825
+ }
789
826
  },
790
827
  })
791
828
  },
792
829
  },
793
830
  })
794
831
 
795
- return { groupings: codeSplitGroupings, routeId }
832
+ return { groupings: codeSplitGroupings }
796
833
  }
797
834
 
798
835
  function getImportSpecifierAndPathFromLocalName(
@@ -849,12 +886,10 @@ function resolveIdentifier(path: any, node: any): t.Node | undefined {
849
886
  return node
850
887
  }
851
888
 
852
- function removeIdentifierLiteral(path: any, node: any) {
853
- if (t.isIdentifier(node)) {
854
- const binding = path.scope.getBinding(node.name)
855
- if (binding) {
856
- binding.path.remove()
857
- }
889
+ function removeIdentifierLiteral(path: babel.NodePath, node: t.Identifier) {
890
+ const binding = path.scope.getBinding(node.name)
891
+ if (binding) {
892
+ binding.path.remove()
858
893
  }
859
894
  }
860
895
 
@@ -55,12 +55,28 @@ export type CodeSplittingOptions = {
55
55
  * @default [['component'],['pendingComponent'],['errorComponent'],['notFoundComponent']]
56
56
  */
57
57
  defaultBehavior?: CodeSplitGroupings
58
+
59
+ /**
60
+ * The nodes that shall be deleted from the route.
61
+ * @default undefined
62
+ */
63
+ deleteNodes?: Array<DeletableNodes>
64
+
65
+ /**
66
+ * @default true
67
+ */
68
+ addHmr?: boolean
58
69
  }
59
70
 
71
+ const DELETABLE_NODES = ['ssr'] as const
72
+ export const deletableNodesSchema = z.enum(DELETABLE_NODES)
60
73
  const codeSplittingOptionsSchema = z.object({
61
74
  splitBehavior: z.function().optional(),
62
75
  defaultBehavior: splitGroupingsSchema.optional(),
76
+ deleteNodes: z.array(deletableNodesSchema).optional(),
77
+ addHmr: z.boolean().optional().default(true),
63
78
  })
79
+ export type DeletableNodes = (typeof DELETABLE_NODES)[number]
64
80
 
65
81
  export const configSchema = generatorConfigSchema.extend({
66
82
  enableRouteGeneration: z.boolean().optional(),
@@ -69,6 +85,15 @@ export const configSchema = generatorConfigSchema.extend({
69
85
  return codeSplittingOptionsSchema.parse(v)
70
86
  })
71
87
  .optional(),
88
+ plugin: z
89
+ .object({
90
+ vite: z
91
+ .object({
92
+ environmentName: z.string().optional(),
93
+ })
94
+ .optional(),
95
+ })
96
+ .optional(),
72
97
  })
73
98
 
74
99
  export const getConfig = (inlineConfig: Partial<Config>, root: string) => {
@@ -2,7 +2,7 @@ import { generateFromAst, logDiff, parseAst } from '@tanstack/router-utils'
2
2
  import babel from '@babel/core'
3
3
  import * as template from '@babel/template'
4
4
  import { getConfig } from './config'
5
- import { debug, fileIsInRoutesDirectory } from './utils'
5
+ import { debug } from './utils'
6
6
  import type { Config } from './config'
7
7
  import type { UnpluginFactory } from 'unplugin'
8
8
 
@@ -16,70 +16,76 @@ export const unpluginRouteAutoImportFactory: UnpluginFactory<
16
16
  let userConfig = options as Config
17
17
 
18
18
  return {
19
- name: 'router-autoimport-plugin',
19
+ name: 'tanstack-router:autoimport',
20
20
  enforce: 'pre',
21
21
 
22
- transform(code, id) {
23
- let routeType: 'createFileRoute' | 'createLazyFileRoute'
24
- if (code.includes('export const Route = createFileRoute(')) {
25
- routeType = 'createFileRoute'
26
- } else if (code.includes('export const Route = createLazyFileRoute(')) {
27
- routeType = 'createLazyFileRoute'
28
- } else {
29
- return null
30
- }
22
+ transform: {
23
+ filter: {
24
+ // this is necessary for webpack / rspack to avoid matching .html files
25
+ id: /\.(m|c)?(j|t)sx?$/,
26
+ code: /createFileRoute\(|createLazyFileRoute\(/,
27
+ },
28
+ handler(code, id) {
29
+ if (!globalThis.TSR_ROUTES_BY_ID_MAP?.has(id)) {
30
+ return null
31
+ }
32
+ let routeType: 'createFileRoute' | 'createLazyFileRoute'
33
+ if (code.includes('createFileRoute(')) {
34
+ routeType = 'createFileRoute'
35
+ } else if (code.includes('createLazyFileRoute(')) {
36
+ routeType = 'createLazyFileRoute'
37
+ } else {
38
+ return null
39
+ }
31
40
 
32
- const routerImportPath = `@tanstack/${userConfig.target}-router`
41
+ const routerImportPath = `@tanstack/${userConfig.target}-router`
33
42
 
34
- const ast = parseAst({ code })
43
+ const ast = parseAst({ code })
35
44
 
36
- let isCreateRouteFunctionImported = false as boolean
45
+ let isCreateRouteFunctionImported = false as boolean
37
46
 
38
- babel.traverse(ast, {
39
- Program: {
40
- enter(programPath) {
41
- programPath.traverse({
42
- ImportDeclaration(path) {
43
- const importedSpecifiers = path.node.specifiers.map(
44
- (specifier) => specifier.local.name,
45
- )
46
- if (
47
- importedSpecifiers.includes(routeType) &&
48
- path.node.source.value === routerImportPath
49
- ) {
50
- isCreateRouteFunctionImported = true
51
- }
52
- },
53
- })
47
+ babel.traverse(ast, {
48
+ Program: {
49
+ enter(programPath) {
50
+ programPath.traverse({
51
+ ImportDeclaration(path) {
52
+ const importedSpecifiers = path.node.specifiers.map(
53
+ (specifier) => specifier.local.name,
54
+ )
55
+ if (
56
+ importedSpecifiers.includes(routeType) &&
57
+ path.node.source.value === routerImportPath
58
+ ) {
59
+ isCreateRouteFunctionImported = true
60
+ }
61
+ },
62
+ })
63
+ },
54
64
  },
55
- },
56
- })
65
+ })
57
66
 
58
- if (!isCreateRouteFunctionImported) {
59
- if (debug) console.info('Adding autoimports to route ', id)
67
+ if (!isCreateRouteFunctionImported) {
68
+ if (debug) console.info('Adding autoimports to route ', id)
60
69
 
61
- const autoImportStatement = template.statement(
62
- `import { ${routeType} } from '${routerImportPath}'`,
63
- )()
64
- ast.program.body.unshift(autoImportStatement)
70
+ const autoImportStatement = template.statement(
71
+ `import { ${routeType} } from '${routerImportPath}'`,
72
+ )()
73
+ ast.program.body.unshift(autoImportStatement)
65
74
 
66
- const result = generateFromAst(ast, {
67
- sourceMaps: true,
68
- filename: id,
69
- sourceFileName: id,
70
- })
71
- if (debug) {
72
- logDiff(code, result.code)
73
- console.log('Output:\n', result.code + '\n\n')
75
+ const result = generateFromAst(ast, {
76
+ sourceMaps: true,
77
+ filename: id,
78
+ sourceFileName: id,
79
+ })
80
+ if (debug) {
81
+ logDiff(code, result.code)
82
+ console.log('Output:\n', result.code + '\n\n')
83
+ }
84
+ return result
74
85
  }
75
- return result
76
- }
77
-
78
- return null
79
- },
80
86
 
81
- transformInclude(id) {
82
- return fileIsInRoutesDirectory(id, userConfig.routesDirectory)
87
+ return null
88
+ },
83
89
  },
84
90
 
85
91
  vite: {