@tanstack/router-plugin 1.168.6 → 1.168.7
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.
- package/dist/cjs/core/code-splitter/compilers.cjs +32 -233
- package/dist/cjs/core/code-splitter/compilers.cjs.map +1 -1
- package/dist/cjs/core/code-splitter/compilers.d.cts +2 -57
- package/dist/cjs/core/code-splitter/plugins.d.cts +1 -0
- package/dist/cjs/core/config.cjs +1 -1
- package/dist/cjs/core/config.cjs.map +1 -1
- package/dist/cjs/core/config.d.cts +44 -163
- package/dist/cjs/core/router-code-splitter-plugin.cjs +5 -6
- package/dist/cjs/core/router-code-splitter-plugin.cjs.map +1 -1
- package/dist/cjs/esbuild.d.cts +26 -26
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/vite.d.cts +26 -26
- package/dist/esm/core/code-splitter/compilers.d.ts +2 -57
- package/dist/esm/core/code-splitter/compilers.js +15 -216
- package/dist/esm/core/code-splitter/compilers.js.map +1 -1
- package/dist/esm/core/code-splitter/plugins.d.ts +1 -0
- package/dist/esm/core/config.d.ts +44 -163
- package/dist/esm/core/config.js +1 -1
- package/dist/esm/core/config.js.map +1 -1
- package/dist/esm/core/router-code-splitter-plugin.js +5 -6
- package/dist/esm/core/router-code-splitter-plugin.js.map +1 -1
- package/dist/esm/esbuild.d.ts +26 -26
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/vite.d.ts +26 -26
- package/package.json +7 -7
- package/src/core/code-splitter/compilers.ts +51 -411
- package/src/core/code-splitter/plugins.ts +3 -0
- package/src/core/config.ts +12 -1
- package/src/core/router-code-splitter-plugin.ts +11 -9
- package/src/index.ts +5 -0
- package/dist/cjs/core/code-splitter/path-ids.cjs +0 -32
- package/dist/cjs/core/code-splitter/path-ids.cjs.map +0 -1
- package/dist/cjs/core/code-splitter/path-ids.d.cts +0 -2
- package/dist/esm/core/code-splitter/path-ids.d.ts +0 -2
- package/dist/esm/core/code-splitter/path-ids.js +0 -31
- package/dist/esm/core/code-splitter/path-ids.js.map +0 -1
- package/src/core/code-splitter/path-ids.ts +0 -39
package/dist/esm/vite.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ declare const tanStackRouterCodeSplitter: (options?: RouterPluginOptions, router
|
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
33
33
|
declare const tanstackRouter: (options?: Partial<{
|
|
34
|
-
target: "
|
|
34
|
+
target: "vue" | "react" | "solid";
|
|
35
35
|
routeFileIgnorePrefix: string;
|
|
36
36
|
routesDirectory: string;
|
|
37
37
|
quoteStyle: "single" | "double";
|
|
@@ -52,21 +52,11 @@ declare const tanstackRouter: (options?: Partial<{
|
|
|
52
52
|
enableRouteTreeFormatting: boolean;
|
|
53
53
|
tmpDir: string;
|
|
54
54
|
importRoutesUsingAbsolutePaths: boolean;
|
|
55
|
-
enableRouteGeneration?: boolean | undefined;
|
|
56
|
-
codeSplittingOptions?: CodeSplittingOptions | undefined;
|
|
57
|
-
plugin?: {
|
|
58
|
-
vite?: {
|
|
59
|
-
environmentName?: string | undefined;
|
|
60
|
-
} | undefined;
|
|
61
|
-
hmr?: {
|
|
62
|
-
style?: "vite" | "webpack" | undefined;
|
|
63
|
-
} | undefined;
|
|
64
|
-
} | undefined;
|
|
65
55
|
virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
|
|
66
56
|
routeFilePrefix?: string | undefined;
|
|
67
57
|
routeFileIgnorePattern?: string | undefined;
|
|
68
|
-
pathParamsAllowedCharacters?: ("
|
|
69
|
-
routeTreeFileFooter?: string[] | ((
|
|
58
|
+
pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
|
|
59
|
+
routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
|
|
70
60
|
autoCodeSplitting?: boolean | undefined;
|
|
71
61
|
customScaffolding?: {
|
|
72
62
|
routeTemplate?: string | undefined;
|
|
@@ -76,12 +66,22 @@ declare const tanstackRouter: (options?: Partial<{
|
|
|
76
66
|
enableCodeSplitting?: boolean | undefined;
|
|
77
67
|
} | undefined;
|
|
78
68
|
plugins?: import('@tanstack/router-generator').GeneratorPlugin[] | undefined;
|
|
69
|
+
enableRouteGeneration?: boolean | undefined;
|
|
70
|
+
codeSplittingOptions?: CodeSplittingOptions | undefined;
|
|
71
|
+
plugin?: {
|
|
72
|
+
hmr?: {
|
|
73
|
+
style?: "vite" | "webpack" | undefined;
|
|
74
|
+
} | undefined;
|
|
75
|
+
vite?: {
|
|
76
|
+
environmentName?: string | undefined;
|
|
77
|
+
} | undefined;
|
|
78
|
+
} | undefined;
|
|
79
79
|
} | (() => Config)> | undefined) => import('vite').Plugin<any> | import('vite').Plugin<any>[];
|
|
80
80
|
/**
|
|
81
81
|
* @deprecated Use `tanstackRouter` instead.
|
|
82
82
|
*/
|
|
83
83
|
declare const TanStackRouterVite: (options?: Partial<{
|
|
84
|
-
target: "
|
|
84
|
+
target: "vue" | "react" | "solid";
|
|
85
85
|
routeFileIgnorePrefix: string;
|
|
86
86
|
routesDirectory: string;
|
|
87
87
|
quoteStyle: "single" | "double";
|
|
@@ -102,21 +102,11 @@ declare const TanStackRouterVite: (options?: Partial<{
|
|
|
102
102
|
enableRouteTreeFormatting: boolean;
|
|
103
103
|
tmpDir: string;
|
|
104
104
|
importRoutesUsingAbsolutePaths: boolean;
|
|
105
|
-
enableRouteGeneration?: boolean | undefined;
|
|
106
|
-
codeSplittingOptions?: CodeSplittingOptions | undefined;
|
|
107
|
-
plugin?: {
|
|
108
|
-
vite?: {
|
|
109
|
-
environmentName?: string | undefined;
|
|
110
|
-
} | undefined;
|
|
111
|
-
hmr?: {
|
|
112
|
-
style?: "vite" | "webpack" | undefined;
|
|
113
|
-
} | undefined;
|
|
114
|
-
} | undefined;
|
|
115
105
|
virtualRouteConfig?: string | import('@tanstack/virtual-file-routes').VirtualRootRoute | undefined;
|
|
116
106
|
routeFilePrefix?: string | undefined;
|
|
117
107
|
routeFileIgnorePattern?: string | undefined;
|
|
118
|
-
pathParamsAllowedCharacters?: ("
|
|
119
|
-
routeTreeFileFooter?: string[] | ((
|
|
108
|
+
pathParamsAllowedCharacters?: (":" | "$" | ";" | "@" | "&" | "=" | "+" | ",")[] | undefined;
|
|
109
|
+
routeTreeFileFooter?: string[] | (() => Array<string>) | undefined;
|
|
120
110
|
autoCodeSplitting?: boolean | undefined;
|
|
121
111
|
customScaffolding?: {
|
|
122
112
|
routeTemplate?: string | undefined;
|
|
@@ -126,6 +116,16 @@ declare const TanStackRouterVite: (options?: Partial<{
|
|
|
126
116
|
enableCodeSplitting?: boolean | undefined;
|
|
127
117
|
} | undefined;
|
|
128
118
|
plugins?: import('@tanstack/router-generator').GeneratorPlugin[] | undefined;
|
|
119
|
+
enableRouteGeneration?: boolean | undefined;
|
|
120
|
+
codeSplittingOptions?: CodeSplittingOptions | undefined;
|
|
121
|
+
plugin?: {
|
|
122
|
+
hmr?: {
|
|
123
|
+
style?: "vite" | "webpack" | undefined;
|
|
124
|
+
} | undefined;
|
|
125
|
+
vite?: {
|
|
126
|
+
environmentName?: string | undefined;
|
|
127
|
+
} | undefined;
|
|
128
|
+
} | undefined;
|
|
129
129
|
} | (() => Config)> | undefined) => import('vite').Plugin<any> | import('vite').Plugin<any>[];
|
|
130
130
|
export default tanstackRouter;
|
|
131
131
|
export { configSchema, getConfig, tanStackRouterCodeSplitter, tanstackRouterGenerator, TanStackRouterVite, tanstackRouter, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/router-plugin",
|
|
3
|
-
"version": "1.168.
|
|
3
|
+
"version": "1.168.7",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -106,12 +106,12 @@
|
|
|
106
106
|
"@babel/template": "^7.27.2",
|
|
107
107
|
"@babel/traverse": "^7.28.5",
|
|
108
108
|
"@babel/types": "^7.28.5",
|
|
109
|
-
"chokidar": "^
|
|
109
|
+
"chokidar": "^5.0.0",
|
|
110
110
|
"unplugin": "^3.0.0",
|
|
111
|
-
"zod": "^
|
|
112
|
-
"@tanstack/router-core": "1.171.
|
|
113
|
-
"@tanstack/router-generator": "1.167.
|
|
114
|
-
"@tanstack/router-utils": "1.162.
|
|
111
|
+
"zod": "^4.4.3",
|
|
112
|
+
"@tanstack/router-core": "1.171.3",
|
|
113
|
+
"@tanstack/router-generator": "1.167.6",
|
|
114
|
+
"@tanstack/router-utils": "1.162.1",
|
|
115
115
|
"@tanstack/virtual-file-routes": "1.162.0"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0",
|
|
126
126
|
"vite-plugin-solid": "^2.11.10 || ^3.0.0-0",
|
|
127
127
|
"webpack": ">=5.92.0",
|
|
128
|
-
"@tanstack/react-router": "^1.170.
|
|
128
|
+
"@tanstack/react-router": "^1.170.5"
|
|
129
129
|
},
|
|
130
130
|
"peerDependenciesMeta": {
|
|
131
131
|
"@rsbuild/core": {
|
|
@@ -2,15 +2,27 @@ import * as t from '@babel/types'
|
|
|
2
2
|
import * as babel from '@babel/core'
|
|
3
3
|
import * as template from '@babel/template'
|
|
4
4
|
import {
|
|
5
|
+
buildDeclarationMap,
|
|
6
|
+
buildDependencyGraph,
|
|
7
|
+
collectIdentifiersFromPattern,
|
|
8
|
+
collectLocalBindingsFromStatement,
|
|
9
|
+
collectModuleLevelRefsFromNode,
|
|
10
|
+
createIdentifier,
|
|
5
11
|
deadCodeElimination,
|
|
12
|
+
expandDestructuredDeclarations,
|
|
13
|
+
expandSharedDestructuredDeclarators,
|
|
14
|
+
expandTransitively,
|
|
6
15
|
findReferencedIdentifiers,
|
|
7
16
|
generateFromAst,
|
|
8
17
|
parseAst,
|
|
18
|
+
removeBindingsTransitivelyDependingOn,
|
|
19
|
+
retainModuleLevelDeclarations,
|
|
20
|
+
stripUnreferencedTopLevelExpressionStatements,
|
|
21
|
+
unwrapExportedDeclarations,
|
|
9
22
|
} from '@tanstack/router-utils'
|
|
10
23
|
import { tsrShared, tsrSplit } from '../constants'
|
|
11
24
|
import { createRouteHmrStatement } from '../hmr'
|
|
12
25
|
import { getObjectPropertyKeyName } from '../utils'
|
|
13
|
-
import { createIdentifier } from './path-ids'
|
|
14
26
|
import { getFrameworkOptions } from './framework-options'
|
|
15
27
|
import type {
|
|
16
28
|
CompileCodeSplitReferenceRouteOptions,
|
|
@@ -20,6 +32,25 @@ import type { GeneratorResult, ParseAstOptions } from '@tanstack/router-utils'
|
|
|
20
32
|
import type { CodeSplitGroupings, SplitRouteIdentNodes } from '../constants'
|
|
21
33
|
import type { SplitNodeMeta } from './types'
|
|
22
34
|
|
|
35
|
+
export {
|
|
36
|
+
buildDeclarationMap,
|
|
37
|
+
buildDependencyGraph,
|
|
38
|
+
collectIdentifiersFromNode,
|
|
39
|
+
collectLocalBindingsFromStatement,
|
|
40
|
+
collectModuleLevelRefsFromNode,
|
|
41
|
+
expandDestructuredDeclarations,
|
|
42
|
+
expandSharedDestructuredDeclarators,
|
|
43
|
+
expandTransitively,
|
|
44
|
+
removeBindingsTransitivelyDependingOn,
|
|
45
|
+
} from '@tanstack/router-utils'
|
|
46
|
+
|
|
47
|
+
export function removeBindingsDependingOnRoute(
|
|
48
|
+
bindings: Set<string>,
|
|
49
|
+
dependencyGraph: Map<string, Set<string>>,
|
|
50
|
+
) {
|
|
51
|
+
removeBindingsTransitivelyDependingOn(bindings, dependencyGraph, ['Route'])
|
|
52
|
+
}
|
|
53
|
+
|
|
23
54
|
const SPLIT_NODES_CONFIG = new Map<SplitRouteIdentNodes, SplitNodeMeta>([
|
|
24
55
|
[
|
|
25
56
|
'loader',
|
|
@@ -108,124 +139,6 @@ const allCreateRouteFns = [
|
|
|
108
139
|
...unsplittableCreateRouteFns,
|
|
109
140
|
]
|
|
110
141
|
|
|
111
|
-
/**
|
|
112
|
-
* Recursively walk an AST node and collect referenced identifier-like names.
|
|
113
|
-
* Much cheaper than babel.traverse — no path/scope overhead.
|
|
114
|
-
*
|
|
115
|
-
* Notes:
|
|
116
|
-
* - Uses @babel/types `isReferenced` to avoid collecting non-references like
|
|
117
|
-
* object keys, member expression properties, or binding identifiers.
|
|
118
|
-
* - Also handles JSX identifiers for component references.
|
|
119
|
-
*/
|
|
120
|
-
export function collectIdentifiersFromNode(node: t.Node): Set<string> {
|
|
121
|
-
const ids = new Set<string>()
|
|
122
|
-
|
|
123
|
-
;(function walk(
|
|
124
|
-
n: t.Node | null | undefined,
|
|
125
|
-
parent?: t.Node,
|
|
126
|
-
grandparent?: t.Node,
|
|
127
|
-
parentKey?: string,
|
|
128
|
-
) {
|
|
129
|
-
if (!n) return
|
|
130
|
-
|
|
131
|
-
if (t.isIdentifier(n)) {
|
|
132
|
-
// When we don't have parent info (node passed in isolation), treat as referenced.
|
|
133
|
-
if (!parent || t.isReferenced(n, parent, grandparent)) {
|
|
134
|
-
ids.add(n.name)
|
|
135
|
-
}
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (t.isJSXIdentifier(n)) {
|
|
140
|
-
// Skip attribute names: <div data-testid="x" />
|
|
141
|
-
if (parent && t.isJSXAttribute(parent) && parentKey === 'name') {
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Skip member properties: <Foo.Bar /> should count Foo, not Bar
|
|
146
|
-
if (
|
|
147
|
-
parent &&
|
|
148
|
-
t.isJSXMemberExpression(parent) &&
|
|
149
|
-
parentKey === 'property'
|
|
150
|
-
) {
|
|
151
|
-
return
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Intrinsic elements (lowercase) are not identifiers
|
|
155
|
-
const first = n.name[0]
|
|
156
|
-
if (first && first === first.toLowerCase()) {
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
ids.add(n.name)
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (const key of t.VISITOR_KEYS[n.type] || []) {
|
|
165
|
-
const child = (n as any)[key]
|
|
166
|
-
if (Array.isArray(child)) {
|
|
167
|
-
for (const c of child) {
|
|
168
|
-
if (c && typeof c.type === 'string') {
|
|
169
|
-
walk(c, n, parent, key)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
} else if (child && typeof child.type === 'string') {
|
|
173
|
-
walk(child, n, parent, key)
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
})(node)
|
|
177
|
-
|
|
178
|
-
return ids
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Build a map from binding name → declaration AST node for all
|
|
183
|
-
* locally-declared module-level bindings. Built once, O(1) lookup.
|
|
184
|
-
*/
|
|
185
|
-
export function buildDeclarationMap(ast: t.File): Map<string, t.Node> {
|
|
186
|
-
const map = new Map<string, t.Node>()
|
|
187
|
-
for (const stmt of ast.program.body) {
|
|
188
|
-
const decl =
|
|
189
|
-
t.isExportNamedDeclaration(stmt) && stmt.declaration
|
|
190
|
-
? stmt.declaration
|
|
191
|
-
: stmt
|
|
192
|
-
|
|
193
|
-
if (t.isVariableDeclaration(decl)) {
|
|
194
|
-
for (const declarator of decl.declarations) {
|
|
195
|
-
for (const name of collectIdentifiersFromPattern(declarator.id)) {
|
|
196
|
-
map.set(name, declarator)
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
} else if (t.isFunctionDeclaration(decl) && decl.id) {
|
|
200
|
-
map.set(decl.id.name, decl)
|
|
201
|
-
} else if (t.isClassDeclaration(decl) && decl.id) {
|
|
202
|
-
map.set(decl.id.name, decl)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return map
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Build a dependency graph: for each local binding, the set of other local
|
|
210
|
-
* bindings its declaration references. Built once via simple node walking.
|
|
211
|
-
*/
|
|
212
|
-
export function buildDependencyGraph(
|
|
213
|
-
declMap: Map<string, t.Node>,
|
|
214
|
-
localBindings: Set<string>,
|
|
215
|
-
): Map<string, Set<string>> {
|
|
216
|
-
const graph = new Map<string, Set<string>>()
|
|
217
|
-
for (const [name, declNode] of declMap) {
|
|
218
|
-
if (!localBindings.has(name)) continue
|
|
219
|
-
const allIds = collectIdentifiersFromNode(declNode)
|
|
220
|
-
const deps = new Set<string>()
|
|
221
|
-
for (const id of allIds) {
|
|
222
|
-
if (id !== name && localBindings.has(id)) deps.add(id)
|
|
223
|
-
}
|
|
224
|
-
graph.set(name, deps)
|
|
225
|
-
}
|
|
226
|
-
return graph
|
|
227
|
-
}
|
|
228
|
-
|
|
229
142
|
/**
|
|
230
143
|
* Computes module-level bindings that are shared between split and non-split
|
|
231
144
|
* route properties. These bindings need to be extracted into a shared virtual
|
|
@@ -381,199 +294,11 @@ export function computeSharedBindings(opts: {
|
|
|
381
294
|
// Remove shared bindings that transitively depend on `Route`.
|
|
382
295
|
// The Route singleton must stay in the reference file; extracting a
|
|
383
296
|
// binding that references it would duplicate Route in the shared module.
|
|
384
|
-
|
|
297
|
+
removeBindingsTransitivelyDependingOn(shared, fullDepGraph, ['Route'])
|
|
385
298
|
|
|
386
299
|
return shared
|
|
387
300
|
}
|
|
388
301
|
|
|
389
|
-
/**
|
|
390
|
-
* If bindings from the same destructured declarator are referenced by
|
|
391
|
-
* different groups, mark all bindings from that declarator as shared.
|
|
392
|
-
*/
|
|
393
|
-
export function expandSharedDestructuredDeclarators(
|
|
394
|
-
ast: t.File,
|
|
395
|
-
refsByGroup: Map<string, Set<number>>,
|
|
396
|
-
shared: Set<string>,
|
|
397
|
-
) {
|
|
398
|
-
for (const stmt of ast.program.body) {
|
|
399
|
-
const decl =
|
|
400
|
-
t.isExportNamedDeclaration(stmt) && stmt.declaration
|
|
401
|
-
? stmt.declaration
|
|
402
|
-
: stmt
|
|
403
|
-
|
|
404
|
-
if (!t.isVariableDeclaration(decl)) continue
|
|
405
|
-
|
|
406
|
-
for (const declarator of decl.declarations) {
|
|
407
|
-
if (!t.isObjectPattern(declarator.id) && !t.isArrayPattern(declarator.id))
|
|
408
|
-
continue
|
|
409
|
-
|
|
410
|
-
const names = collectIdentifiersFromPattern(declarator.id)
|
|
411
|
-
|
|
412
|
-
const usedGroups = new Set<number>()
|
|
413
|
-
for (const name of names) {
|
|
414
|
-
const groups = refsByGroup.get(name)
|
|
415
|
-
if (!groups) continue
|
|
416
|
-
for (const g of groups) usedGroups.add(g)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (usedGroups.size >= 2) {
|
|
420
|
-
for (const name of names) {
|
|
421
|
-
shared.add(name)
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Collect locally-declared module-level binding names from a statement.
|
|
430
|
-
* Pure node inspection, no traversal.
|
|
431
|
-
*/
|
|
432
|
-
export function collectLocalBindingsFromStatement(
|
|
433
|
-
node: t.Statement | t.ModuleDeclaration,
|
|
434
|
-
bindings: Set<string>,
|
|
435
|
-
) {
|
|
436
|
-
const decl =
|
|
437
|
-
t.isExportNamedDeclaration(node) && node.declaration
|
|
438
|
-
? node.declaration
|
|
439
|
-
: node
|
|
440
|
-
|
|
441
|
-
if (t.isVariableDeclaration(decl)) {
|
|
442
|
-
for (const declarator of decl.declarations) {
|
|
443
|
-
for (const name of collectIdentifiersFromPattern(declarator.id)) {
|
|
444
|
-
bindings.add(name)
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
} else if (t.isFunctionDeclaration(decl) && decl.id) {
|
|
448
|
-
bindings.add(decl.id.name)
|
|
449
|
-
} else if (t.isClassDeclaration(decl) && decl.id) {
|
|
450
|
-
bindings.add(decl.id.name)
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Collect direct module-level binding names referenced from a given AST node.
|
|
456
|
-
* Uses a simple recursive walk instead of babel.traverse.
|
|
457
|
-
*/
|
|
458
|
-
export function collectModuleLevelRefsFromNode(
|
|
459
|
-
node: t.Node,
|
|
460
|
-
localModuleLevelBindings: Set<string>,
|
|
461
|
-
): Set<string> {
|
|
462
|
-
const allIds = collectIdentifiersFromNode(node)
|
|
463
|
-
const refs = new Set<string>()
|
|
464
|
-
for (const name of allIds) {
|
|
465
|
-
if (localModuleLevelBindings.has(name)) refs.add(name)
|
|
466
|
-
}
|
|
467
|
-
return refs
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Expand the shared set transitively using a prebuilt dependency graph.
|
|
472
|
-
* No AST traversals — pure graph BFS.
|
|
473
|
-
*/
|
|
474
|
-
export function expandTransitively(
|
|
475
|
-
shared: Set<string>,
|
|
476
|
-
depGraph: Map<string, Set<string>>,
|
|
477
|
-
) {
|
|
478
|
-
const queue = [...shared]
|
|
479
|
-
const visited = new Set<string>()
|
|
480
|
-
|
|
481
|
-
while (queue.length > 0) {
|
|
482
|
-
const name = queue.pop()!
|
|
483
|
-
if (visited.has(name)) continue
|
|
484
|
-
visited.add(name)
|
|
485
|
-
|
|
486
|
-
const deps = depGraph.get(name)
|
|
487
|
-
if (!deps) continue
|
|
488
|
-
|
|
489
|
-
for (const dep of deps) {
|
|
490
|
-
if (!shared.has(dep)) {
|
|
491
|
-
shared.add(dep)
|
|
492
|
-
queue.push(dep)
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Remove any bindings from `shared` that transitively depend on `Route`.
|
|
500
|
-
* The Route singleton must remain in the reference file; if a shared binding
|
|
501
|
-
* references it (directly or transitively), extracting that binding would
|
|
502
|
-
* duplicate Route in the shared module.
|
|
503
|
-
*
|
|
504
|
-
* Uses `depGraph` which must include `Route` as a node so the dependency
|
|
505
|
-
* chain is visible.
|
|
506
|
-
*/
|
|
507
|
-
export function removeBindingsDependingOnRoute(
|
|
508
|
-
shared: Set<string>,
|
|
509
|
-
depGraph: Map<string, Set<string>>,
|
|
510
|
-
) {
|
|
511
|
-
const reverseGraph = new Map<string, Set<string>>()
|
|
512
|
-
for (const [name, deps] of depGraph) {
|
|
513
|
-
for (const dep of deps) {
|
|
514
|
-
let parents = reverseGraph.get(dep)
|
|
515
|
-
if (!parents) {
|
|
516
|
-
parents = new Set<string>()
|
|
517
|
-
reverseGraph.set(dep, parents)
|
|
518
|
-
}
|
|
519
|
-
parents.add(name)
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Walk backwards from Route to find all bindings that can reach it.
|
|
524
|
-
const visited = new Set<string>()
|
|
525
|
-
const queue = ['Route']
|
|
526
|
-
while (queue.length > 0) {
|
|
527
|
-
const cur = queue.pop()!
|
|
528
|
-
if (visited.has(cur)) continue
|
|
529
|
-
visited.add(cur)
|
|
530
|
-
|
|
531
|
-
const parents = reverseGraph.get(cur)
|
|
532
|
-
if (!parents) continue
|
|
533
|
-
for (const parent of parents) {
|
|
534
|
-
if (!visited.has(parent)) queue.push(parent)
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
for (const name of [...shared]) {
|
|
539
|
-
if (visited.has(name)) {
|
|
540
|
-
shared.delete(name)
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* If any binding from a destructured declaration is shared,
|
|
547
|
-
* ensure all bindings from that same declaration are also shared.
|
|
548
|
-
* Pure node inspection of program.body, no traversal.
|
|
549
|
-
*/
|
|
550
|
-
export function expandDestructuredDeclarations(
|
|
551
|
-
ast: t.File,
|
|
552
|
-
shared: Set<string>,
|
|
553
|
-
) {
|
|
554
|
-
for (const stmt of ast.program.body) {
|
|
555
|
-
const decl =
|
|
556
|
-
t.isExportNamedDeclaration(stmt) && stmt.declaration
|
|
557
|
-
? stmt.declaration
|
|
558
|
-
: stmt
|
|
559
|
-
|
|
560
|
-
if (!t.isVariableDeclaration(decl)) continue
|
|
561
|
-
|
|
562
|
-
for (const declarator of decl.declarations) {
|
|
563
|
-
if (!t.isObjectPattern(declarator.id) && !t.isArrayPattern(declarator.id))
|
|
564
|
-
continue
|
|
565
|
-
|
|
566
|
-
const names = collectIdentifiersFromPattern(declarator.id)
|
|
567
|
-
const hasShared = names.some((n) => shared.has(n))
|
|
568
|
-
if (hasShared) {
|
|
569
|
-
for (const n of names) {
|
|
570
|
-
shared.add(n)
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
302
|
/**
|
|
578
303
|
* Find which shared bindings are user-exported in the original source.
|
|
579
304
|
* These need to be re-exported from the shared module.
|
|
@@ -740,6 +465,21 @@ export function compileCodeSplitReferenceRoute(
|
|
|
740
465
|
if (t.isObjectExpression(routeOptions)) {
|
|
741
466
|
const insertionPath = path.getStatementParent() ?? path
|
|
742
467
|
|
|
468
|
+
opts.compilerPlugins?.forEach((plugin) => {
|
|
469
|
+
const pluginResult = plugin.onRouteOptions?.({
|
|
470
|
+
programPath,
|
|
471
|
+
callExpressionPath: path,
|
|
472
|
+
insertionPath,
|
|
473
|
+
routeOptions,
|
|
474
|
+
createRouteFn,
|
|
475
|
+
opts: opts as CompileCodeSplitReferenceRouteOptions,
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
if (pluginResult?.modified) {
|
|
479
|
+
modified = true
|
|
480
|
+
}
|
|
481
|
+
})
|
|
482
|
+
|
|
743
483
|
if (opts.deleteNodes && opts.deleteNodes.size > 0) {
|
|
744
484
|
routeOptions.properties = routeOptions.properties.filter(
|
|
745
485
|
(prop) => {
|
|
@@ -1525,22 +1265,7 @@ export function compileCodeSplitVirtualRoute(
|
|
|
1525
1265
|
})
|
|
1526
1266
|
|
|
1527
1267
|
deadCodeElimination(ast, refIdents)
|
|
1528
|
-
|
|
1529
|
-
// Strip top-level expression statements that reference no locally-bound names.
|
|
1530
|
-
// DCE only removes unused declarations; bare side-effect statements like
|
|
1531
|
-
// `console.log(...)` survive even when the virtual file has no exports.
|
|
1532
|
-
{
|
|
1533
|
-
const locallyBound = new Set<string>()
|
|
1534
|
-
for (const stmt of ast.program.body) {
|
|
1535
|
-
collectLocalBindingsFromStatement(stmt, locallyBound)
|
|
1536
|
-
}
|
|
1537
|
-
ast.program.body = ast.program.body.filter((stmt) => {
|
|
1538
|
-
if (!t.isExpressionStatement(stmt)) return true
|
|
1539
|
-
const refs = collectIdentifiersFromNode(stmt)
|
|
1540
|
-
// Keep if it references at least one locally-bound identifier
|
|
1541
|
-
return [...refs].some((name) => locallyBound.has(name))
|
|
1542
|
-
})
|
|
1543
|
-
}
|
|
1268
|
+
stripUnreferencedTopLevelExpressionStatements(ast)
|
|
1544
1269
|
|
|
1545
1270
|
// If the body is empty after DCE, strip directive prologues too.
|
|
1546
1271
|
// A file containing only `'use client'` with no real code is useless.
|
|
@@ -1595,49 +1320,8 @@ export function compileCodeSplitSharedRoute(
|
|
|
1595
1320
|
keepBindings.delete('Route')
|
|
1596
1321
|
expandTransitively(keepBindings, depGraph)
|
|
1597
1322
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
// - Declarations of bindings in keepBindings
|
|
1601
|
-
ast.program.body = ast.program.body.filter((stmt) => {
|
|
1602
|
-
// Always keep imports — DCE will remove unused ones
|
|
1603
|
-
if (t.isImportDeclaration(stmt)) return true
|
|
1604
|
-
|
|
1605
|
-
const decl =
|
|
1606
|
-
t.isExportNamedDeclaration(stmt) && stmt.declaration
|
|
1607
|
-
? stmt.declaration
|
|
1608
|
-
: stmt
|
|
1609
|
-
|
|
1610
|
-
if (t.isVariableDeclaration(decl)) {
|
|
1611
|
-
// Keep declarators where at least one binding is in keepBindings
|
|
1612
|
-
decl.declarations = decl.declarations.filter((declarator) => {
|
|
1613
|
-
const names = collectIdentifiersFromPattern(declarator.id)
|
|
1614
|
-
return names.some((n) => keepBindings.has(n))
|
|
1615
|
-
})
|
|
1616
|
-
if (decl.declarations.length === 0) return false
|
|
1617
|
-
|
|
1618
|
-
// Strip the `export` wrapper — shared module controls its own exports
|
|
1619
|
-
if (t.isExportNamedDeclaration(stmt) && stmt.declaration) {
|
|
1620
|
-
return true // keep for now, we'll convert below
|
|
1621
|
-
}
|
|
1622
|
-
return true
|
|
1623
|
-
} else if (t.isFunctionDeclaration(decl) && decl.id) {
|
|
1624
|
-
return keepBindings.has(decl.id.name)
|
|
1625
|
-
} else if (t.isClassDeclaration(decl) && decl.id) {
|
|
1626
|
-
return keepBindings.has(decl.id.name)
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
// Remove everything else (expression statements, other exports, etc.)
|
|
1630
|
-
return false
|
|
1631
|
-
})
|
|
1632
|
-
|
|
1633
|
-
// Convert `export const/function/class` to plain declarations
|
|
1634
|
-
// (we'll add our own export statement at the end)
|
|
1635
|
-
ast.program.body = ast.program.body.map((stmt) => {
|
|
1636
|
-
if (t.isExportNamedDeclaration(stmt) && stmt.declaration) {
|
|
1637
|
-
return stmt.declaration
|
|
1638
|
-
}
|
|
1639
|
-
return stmt
|
|
1640
|
-
})
|
|
1323
|
+
retainModuleLevelDeclarations(ast, keepBindings)
|
|
1324
|
+
unwrapExportedDeclarations(ast)
|
|
1641
1325
|
|
|
1642
1326
|
// Export all shared bindings (sorted for deterministic output)
|
|
1643
1327
|
const exportNames = [...opts.sharedBindings].sort((a, b) =>
|
|
@@ -1837,50 +1521,6 @@ function getImportSpecifierAndPathFromLocalName(
|
|
|
1837
1521
|
return { specifier, path }
|
|
1838
1522
|
}
|
|
1839
1523
|
|
|
1840
|
-
/**
|
|
1841
|
-
* Recursively collects all identifier names from a destructuring pattern
|
|
1842
|
-
* (ObjectPattern, ArrayPattern, AssignmentPattern, RestElement).
|
|
1843
|
-
*/
|
|
1844
|
-
function collectIdentifiersFromPattern(
|
|
1845
|
-
node: t.LVal | t.Node | null | undefined,
|
|
1846
|
-
): Array<string> {
|
|
1847
|
-
if (!node) {
|
|
1848
|
-
return []
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
if (t.isIdentifier(node)) {
|
|
1852
|
-
return [node.name]
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
if (t.isAssignmentPattern(node)) {
|
|
1856
|
-
return collectIdentifiersFromPattern(node.left)
|
|
1857
|
-
}
|
|
1858
|
-
|
|
1859
|
-
if (t.isRestElement(node)) {
|
|
1860
|
-
return collectIdentifiersFromPattern(node.argument)
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
if (t.isObjectPattern(node)) {
|
|
1864
|
-
return node.properties.flatMap((prop) => {
|
|
1865
|
-
if (t.isObjectProperty(prop)) {
|
|
1866
|
-
return collectIdentifiersFromPattern(prop.value as t.LVal)
|
|
1867
|
-
}
|
|
1868
|
-
if (t.isRestElement(prop)) {
|
|
1869
|
-
return collectIdentifiersFromPattern(prop.argument)
|
|
1870
|
-
}
|
|
1871
|
-
return []
|
|
1872
|
-
})
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
if (t.isArrayPattern(node)) {
|
|
1876
|
-
return node.elements.flatMap((element) =>
|
|
1877
|
-
collectIdentifiersFromPattern(element),
|
|
1878
|
-
)
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
return []
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
1524
|
// Reusable function to get literal value or resolve variable to literal
|
|
1885
1525
|
function resolveIdentifier(path: any, node: any): t.Node | undefined {
|
|
1886
1526
|
if (t.isIdentifier(node)) {
|
|
@@ -43,6 +43,9 @@ export type ReferenceRouteCompilerPluginResult = {
|
|
|
43
43
|
export type ReferenceRouteCompilerPlugin = {
|
|
44
44
|
name: string
|
|
45
45
|
getStableRouteOptionKeys?: () => Array<string>
|
|
46
|
+
onRouteOptions?: (
|
|
47
|
+
ctx: ReferenceRouteCompilerPluginContext,
|
|
48
|
+
) => void | ReferenceRouteCompilerPluginResult
|
|
46
49
|
onAddHmr?: (
|
|
47
50
|
ctx: ReferenceRouteCompilerPluginContext,
|
|
48
51
|
) => void | ReferenceRouteCompilerPluginResult
|
package/src/core/config.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
RouteIds,
|
|
10
10
|
} from '@tanstack/router-core'
|
|
11
11
|
import type { CodeSplitGroupings } from './constants'
|
|
12
|
+
import type { ReferenceRouteCompilerPlugin } from './code-splitter/plugins'
|
|
12
13
|
|
|
13
14
|
export const splitGroupingsSchema = z
|
|
14
15
|
.array(
|
|
@@ -70,6 +71,12 @@ export type CodeSplittingOptions = {
|
|
|
70
71
|
* @default true
|
|
71
72
|
*/
|
|
72
73
|
addHmr?: boolean
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Internal compiler plugins used by framework integrations.
|
|
77
|
+
* @internal
|
|
78
|
+
*/
|
|
79
|
+
compilerPlugins?: Array<ReferenceRouteCompilerPlugin>
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
export type HmrStyle = 'vite' | 'webpack'
|
|
@@ -86,7 +93,11 @@ export type HmrOptions = {
|
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
const codeSplittingOptionsSchema = z.object({
|
|
89
|
-
splitBehavior: z
|
|
96
|
+
splitBehavior: z
|
|
97
|
+
.custom<
|
|
98
|
+
CodeSplittingOptions['splitBehavior']
|
|
99
|
+
>((value) => typeof value === 'function')
|
|
100
|
+
.optional(),
|
|
90
101
|
defaultBehavior: splitGroupingsSchema.optional(),
|
|
91
102
|
deleteNodes: z.array(z.string()).optional(),
|
|
92
103
|
addHmr: z.boolean().optional().default(true),
|