@tanstack/router-generator 1.166.29 → 1.166.31
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/config.cjs +0 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +0 -3
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +5 -4
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +2 -2
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/generator.cjs +1 -44
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/index.d.cts +1 -2
- package/dist/cjs/template.cjs +15 -12
- package/dist/cjs/template.cjs.map +1 -1
- package/dist/cjs/transform/transform.cjs +283 -266
- package/dist/cjs/transform/transform.cjs.map +1 -1
- package/dist/cjs/transform/transform.d.cts +1 -3
- package/dist/cjs/transform/types.d.cts +1 -7
- package/dist/cjs/utils.cjs +0 -16
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +0 -24
- package/dist/esm/config.d.ts +0 -3
- package/dist/esm/config.js +0 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.js +6 -5
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
- package/dist/esm/filesystem/virtual/getRouteNodes.js +3 -3
- package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -1
- package/dist/esm/generator.js +2 -45
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/index.d.ts +1 -2
- package/dist/esm/index.js +1 -2
- package/dist/esm/template.js +15 -12
- package/dist/esm/template.js.map +1 -1
- package/dist/esm/transform/transform.d.ts +1 -3
- package/dist/esm/transform/transform.js +280 -265
- package/dist/esm/transform/transform.js.map +1 -1
- package/dist/esm/transform/types.d.ts +1 -7
- package/dist/esm/utils.d.ts +0 -24
- package/dist/esm/utils.js +1 -15
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/config.ts +0 -1
- package/src/filesystem/physical/getRouteNodes.ts +10 -4
- package/src/filesystem/virtual/getRouteNodes.ts +7 -2
- package/src/generator.ts +0 -66
- package/src/index.ts +1 -7
- package/src/template.ts +16 -36
- package/src/transform/transform.ts +633 -446
- package/src/transform/types.ts +1 -8
- package/src/utils.ts +5 -43
- package/dist/cjs/transform/utils.cjs +0 -34
- package/dist/cjs/transform/utils.cjs.map +0 -1
- package/dist/cjs/transform/utils.d.cts +0 -2
- package/dist/esm/transform/utils.d.ts +0 -2
- package/dist/esm/transform/utils.js +0 -34
- package/dist/esm/transform/utils.js.map +0 -1
- package/src/transform/utils.ts +0 -42
|
@@ -1,529 +1,716 @@
|
|
|
1
|
+
import MagicString from 'magic-string'
|
|
2
|
+
import * as t from '@babel/types'
|
|
1
3
|
import { parseAst } from '@tanstack/router-utils'
|
|
2
|
-
import { parse, print, types, visit } from 'recast'
|
|
3
|
-
import { SourceMapConsumer } from 'source-map'
|
|
4
|
-
import { mergeImportDeclarations } from '../utils'
|
|
5
|
-
import { ensureStringArgument } from './utils'
|
|
6
|
-
import type { ImportDeclaration } from '../types'
|
|
7
|
-
import type { RawSourceMap } from 'source-map'
|
|
8
4
|
import type { TransformOptions, TransformResult } from './types'
|
|
9
5
|
|
|
10
|
-
const
|
|
6
|
+
const routeConstructors = ['createFileRoute', 'createLazyFileRoute'] as const
|
|
11
7
|
|
|
12
|
-
|
|
8
|
+
type RouteConstructorName = (typeof routeConstructors)[number]
|
|
9
|
+
type SupportedRouteId = t.StringLiteral | t.TemplateLiteral
|
|
10
|
+
type NamedImport = {
|
|
11
|
+
imported: string
|
|
12
|
+
local: string
|
|
13
|
+
importKind?: 'type' | 'typeof' | 'value'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type ParsedImportDeclaration = {
|
|
17
|
+
declaration: t.ImportDeclaration
|
|
18
|
+
defaultImport?: string
|
|
19
|
+
namespace?: string
|
|
20
|
+
named: Array<NamedImport>
|
|
21
|
+
moduleName: string
|
|
22
|
+
quote: '"' | "'"
|
|
23
|
+
semicolon: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type RouteImportAnalysis =
|
|
27
|
+
| { kind: 'ok' }
|
|
28
|
+
| {
|
|
29
|
+
kind: 'rename'
|
|
30
|
+
imported: t.Identifier
|
|
31
|
+
local: t.Identifier
|
|
32
|
+
next: RouteConstructorName
|
|
33
|
+
}
|
|
34
|
+
| { kind: 'normalize' }
|
|
35
|
+
|
|
36
|
+
type RouteCall = {
|
|
37
|
+
callee: t.Identifier & { name: RouteConstructorName }
|
|
38
|
+
routeIdArg: SupportedRouteId
|
|
39
|
+
optionsArg: t.CallExpression['arguments'][number] | undefined
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type RouteCallAnalysis = {
|
|
43
|
+
calls: Array<RouteCall>
|
|
44
|
+
hasUnsupportedRouteId: boolean
|
|
45
|
+
hasMalformedRouteCall: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function transform({
|
|
13
49
|
ctx,
|
|
14
50
|
source,
|
|
15
51
|
node,
|
|
16
|
-
}: TransformOptions):
|
|
17
|
-
let
|
|
18
|
-
|
|
52
|
+
}: TransformOptions): TransformResult {
|
|
53
|
+
let ast: ReturnType<typeof parseAst>
|
|
54
|
+
|
|
19
55
|
try {
|
|
20
|
-
ast =
|
|
21
|
-
|
|
22
|
-
parser: {
|
|
23
|
-
parse(code: string) {
|
|
24
|
-
return parseAst({
|
|
25
|
-
code,
|
|
26
|
-
// we need to instruct babel to produce tokens,
|
|
27
|
-
// otherwise recast will try to generate the tokens via its own parser and will fail
|
|
28
|
-
tokens: true,
|
|
29
|
-
})
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
})
|
|
33
|
-
} catch (e) {
|
|
34
|
-
console.error('Error parsing code', ctx.routeId, source, e)
|
|
56
|
+
ast = parseAst({ code: source })
|
|
57
|
+
} catch (error) {
|
|
35
58
|
return {
|
|
36
59
|
result: 'error',
|
|
37
|
-
error
|
|
60
|
+
error,
|
|
38
61
|
}
|
|
39
62
|
}
|
|
40
63
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let identifier: types.namedTypes.Identifier | undefined
|
|
60
|
-
// `const Route = createFileRoute({ ... })`
|
|
61
|
-
if (callExpression.callee.type === 'Identifier') {
|
|
62
|
-
identifier = callExpression.callee
|
|
63
|
-
if (ctx.verboseFileRoutes) {
|
|
64
|
-
// we need to add the string literal via another CallExpression
|
|
65
|
-
callExpression.callee = b.callExpression(identifier, [
|
|
66
|
-
b.stringLiteral(ctx.routeId),
|
|
67
|
-
])
|
|
68
|
-
appliedChanges = true
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// `const Route = createFileRoute('/path')({ ... })`
|
|
72
|
-
else if (
|
|
73
|
-
callExpression.callee.type === 'CallExpression' &&
|
|
74
|
-
callExpression.callee.callee.type === 'Identifier'
|
|
75
|
-
) {
|
|
76
|
-
identifier = callExpression.callee.callee
|
|
77
|
-
if (!ctx.verboseFileRoutes) {
|
|
78
|
-
// we need to remove the route id
|
|
79
|
-
callExpression.callee = identifier
|
|
80
|
-
appliedChanges = true
|
|
81
|
-
} else {
|
|
82
|
-
// check if the route id is correct
|
|
83
|
-
appliedChanges = ensureStringArgument(
|
|
84
|
-
callExpression.callee,
|
|
85
|
-
ctx.routeId,
|
|
86
|
-
ctx.preferredQuote,
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (identifier === undefined) {
|
|
91
|
-
throw new Error(
|
|
92
|
-
`expected identifier to be present in ${ctx.routeId} for export "Route"`,
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
if (identifier.name === 'createFileRoute' && ctx.lazy) {
|
|
96
|
-
identifier.name = 'createLazyFileRoute'
|
|
97
|
-
appliedChanges = true
|
|
98
|
-
} else if (identifier.name === 'createLazyFileRoute' && !ctx.lazy) {
|
|
99
|
-
identifier.name = 'createFileRoute'
|
|
100
|
-
appliedChanges = true
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
throw new Error(
|
|
104
|
-
`expected "Route" export to be initialized by a CallExpression`,
|
|
105
|
-
)
|
|
106
|
-
}
|
|
107
|
-
routeExportHandled = true
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const program: types.namedTypes.Program = ast.program
|
|
111
|
-
// first pass: find Route export
|
|
112
|
-
for (const n of program.body) {
|
|
113
|
-
if (n.type === 'ExportNamedDeclaration') {
|
|
114
|
-
// direct export of a variable declaration, e.g. `export const Route = createFileRoute('/path')`
|
|
115
|
-
if (n.declaration?.type === 'VariableDeclaration') {
|
|
116
|
-
const decl = n.declaration.declarations[0]
|
|
117
|
-
if (
|
|
118
|
-
decl &&
|
|
119
|
-
decl.type === 'VariableDeclarator' &&
|
|
120
|
-
decl.id.type === 'Identifier'
|
|
121
|
-
) {
|
|
122
|
-
if (decl.id.name === 'Route') {
|
|
123
|
-
onExportFound(decl)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// this is an export without a declaration, e.g. `export { Route }`
|
|
128
|
-
else if (n.declaration === null && n.specifiers) {
|
|
129
|
-
for (const spec of n.specifiers) {
|
|
130
|
-
if (typeof spec.exported.name === 'string') {
|
|
131
|
-
if (spec.exported.name === 'Route') {
|
|
132
|
-
const variableName = spec.local?.name || spec.exported.name
|
|
133
|
-
// find the matching variable declaration by iterating over the top-level declarations
|
|
134
|
-
for (const decl of program.body) {
|
|
135
|
-
if (
|
|
136
|
-
decl.type === 'VariableDeclaration' &&
|
|
137
|
-
decl.declarations[0]
|
|
138
|
-
) {
|
|
139
|
-
const variable = decl.declarations[0]
|
|
140
|
-
if (
|
|
141
|
-
variable.type === 'VariableDeclarator' &&
|
|
142
|
-
variable.id.type === 'Identifier' &&
|
|
143
|
-
variable.id.name === variableName
|
|
144
|
-
) {
|
|
145
|
-
onExportFound(variable)
|
|
146
|
-
break
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
64
|
+
const exportedRouteNames = getExportedRouteNames(ast.program.body)
|
|
65
|
+
|
|
66
|
+
if (exportedRouteNames.size === 0) {
|
|
67
|
+
return { result: 'no-route-export' }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
calls: routeCalls,
|
|
72
|
+
hasUnsupportedRouteId,
|
|
73
|
+
hasMalformedRouteCall,
|
|
74
|
+
} = findExportedRouteCalls(ast.program.body, exportedRouteNames)
|
|
75
|
+
|
|
76
|
+
if (routeCalls.length === 0 && hasMalformedRouteCall) {
|
|
77
|
+
return {
|
|
78
|
+
result: 'error',
|
|
79
|
+
error: new Error(
|
|
80
|
+
`expected Route export in ${ctx.routeId} to use createFileRoute('/path')({...}) or createLazyFileRoute('/path')({...})`,
|
|
81
|
+
),
|
|
154
82
|
}
|
|
155
|
-
|
|
156
|
-
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (routeCalls.length === 0 && hasUnsupportedRouteId) {
|
|
86
|
+
return {
|
|
87
|
+
result: 'error',
|
|
88
|
+
error: new Error(
|
|
89
|
+
`expected route id to be a string literal or plain template literal in ${ctx.routeId}`,
|
|
90
|
+
),
|
|
157
91
|
}
|
|
158
92
|
}
|
|
159
93
|
|
|
160
|
-
if (
|
|
94
|
+
if (routeCalls.length === 0) {
|
|
95
|
+
return { result: 'not-modified' }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (routeCalls.length > 1) {
|
|
161
99
|
return {
|
|
162
|
-
result: '
|
|
100
|
+
result: 'error',
|
|
101
|
+
error: new Error(
|
|
102
|
+
`expected exactly one createFileRoute/createLazyFileRoute call in ${ctx.routeId}`,
|
|
103
|
+
),
|
|
163
104
|
}
|
|
164
105
|
}
|
|
165
106
|
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
107
|
+
const routeCall = routeCalls[0]!
|
|
108
|
+
const routeIdQuote = getRouteIdQuote(source, routeCall.routeIdArg)
|
|
109
|
+
|
|
110
|
+
const createFileRouteProps = getCreateFileRouteProps(routeCall.optionsArg)
|
|
111
|
+
if (createFileRouteProps) {
|
|
112
|
+
node.createFileRouteProps = createFileRouteProps
|
|
172
113
|
}
|
|
173
114
|
|
|
115
|
+
const expectedCallee = getExpectedRouteConstructor(ctx.lazy)
|
|
116
|
+
const expectedRouteId = `${routeIdQuote}${ctx.routeId}${routeIdQuote}`
|
|
117
|
+
const currentRouteId = source.slice(
|
|
118
|
+
routeCall.routeIdArg.start!,
|
|
119
|
+
routeCall.routeIdArg.end!,
|
|
120
|
+
)
|
|
174
121
|
const targetModule = `@tanstack/${ctx.target}-router`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
122
|
+
const imports = parseTargetImports(ast.program.body, source, targetModule)
|
|
123
|
+
|
|
124
|
+
const s = new MagicString(source)
|
|
125
|
+
let modified = false
|
|
126
|
+
|
|
127
|
+
if (routeCall.callee.name !== expectedCallee) {
|
|
128
|
+
s.update(routeCall.callee.start!, routeCall.callee.end!, expectedCallee)
|
|
129
|
+
modified = true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (currentRouteId !== expectedRouteId) {
|
|
133
|
+
s.update(
|
|
134
|
+
routeCall.routeIdArg.start!,
|
|
135
|
+
routeCall.routeIdArg.end!,
|
|
136
|
+
expectedRouteId,
|
|
137
|
+
)
|
|
138
|
+
modified = true
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
updateRouteImports({
|
|
143
|
+
imports,
|
|
144
|
+
source,
|
|
145
|
+
s,
|
|
146
|
+
targetModule,
|
|
147
|
+
required: expectedCallee,
|
|
148
|
+
lineEnding: getLineEnding(source),
|
|
149
|
+
})
|
|
150
|
+
) {
|
|
151
|
+
modified = true
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!modified) {
|
|
155
|
+
return { result: 'not-modified' }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
result: 'modified',
|
|
160
|
+
output: s.toString(),
|
|
213
161
|
}
|
|
162
|
+
}
|
|
214
163
|
|
|
215
|
-
|
|
216
|
-
|
|
164
|
+
function getExportedRouteNames(body: Array<t.Statement>) {
|
|
165
|
+
const exportedRouteNames = new Set<string>()
|
|
217
166
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
167
|
+
for (const statement of body) {
|
|
168
|
+
if (!t.isExportNamedDeclaration(statement) || statement.source) {
|
|
169
|
+
continue
|
|
170
|
+
}
|
|
222
171
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
(i: ImportDeclaration) => {
|
|
228
|
-
if (i.source === opts.source) {
|
|
229
|
-
const importKind = i.importKind || 'value'
|
|
230
|
-
const expectedImportKind = opts.importKind || 'value'
|
|
231
|
-
return expectedImportKind === importKind
|
|
172
|
+
if (t.isVariableDeclaration(statement.declaration)) {
|
|
173
|
+
for (const declarator of statement.declaration.declarations) {
|
|
174
|
+
if (t.isIdentifier(declarator.id) && declarator.id.name === 'Route') {
|
|
175
|
+
exportedRouteNames.add('Route')
|
|
232
176
|
}
|
|
233
|
-
return false
|
|
234
177
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const bannedImports = imports.banned.filter(filterImport)[0]
|
|
243
|
-
if (!requiredImports && !bannedImports) {
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
for (const specifier of statement.specifiers) {
|
|
181
|
+
if (
|
|
182
|
+
!t.isExportSpecifier(specifier) ||
|
|
183
|
+
getExportedName(specifier.exported) !== 'Route'
|
|
184
|
+
) {
|
|
244
185
|
continue
|
|
245
186
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
if (!requiredImports && !bannedImports) {
|
|
251
|
-
break
|
|
252
|
-
}
|
|
253
|
-
if (
|
|
254
|
-
spec.type === 'ImportSpecifier' &&
|
|
255
|
-
typeof spec.imported.name === 'string'
|
|
256
|
-
) {
|
|
257
|
-
if (requiredImports) {
|
|
258
|
-
const requiredImportIndex = requiredImports.specifiers.findIndex(
|
|
259
|
-
(imp) => imp.imported === spec.imported.name,
|
|
260
|
-
)
|
|
261
|
-
if (requiredImportIndex !== -1) {
|
|
262
|
-
// import is already present, remove it from requiredImports
|
|
263
|
-
requiredImports.specifiers.splice(requiredImportIndex, 1)
|
|
264
|
-
if (requiredImports.specifiers.length === 0) {
|
|
265
|
-
imports.required = imports.required.splice(
|
|
266
|
-
imports.required.indexOf(requiredImports),
|
|
267
|
-
1,
|
|
268
|
-
)
|
|
269
|
-
requiredImports = undefined
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
// add the import statement to the candidates
|
|
273
|
-
importStatementCandidates.push(n)
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
if (bannedImports) {
|
|
277
|
-
const bannedImportIndex = bannedImports.specifiers.findIndex(
|
|
278
|
-
(imp) => imp.imported === spec.imported.name,
|
|
279
|
-
)
|
|
280
|
-
if (bannedImportIndex !== -1) {
|
|
281
|
-
importSpecifiersToRemove.push(spec)
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (importSpecifiersToRemove.length > 0) {
|
|
287
|
-
appliedChanges = true
|
|
288
|
-
n.specifiers = n.specifiers.filter(
|
|
289
|
-
(spec) => !importSpecifiersToRemove.includes(spec),
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
// mark the import statement as to be deleted if it is now empty
|
|
293
|
-
if (n.specifiers.length === 0) {
|
|
294
|
-
importDeclarationsToRemove.push(n)
|
|
295
|
-
}
|
|
296
|
-
}
|
|
187
|
+
|
|
188
|
+
const localName = getLocalBindingName(specifier.local)
|
|
189
|
+
if (localName) {
|
|
190
|
+
exportedRouteNames.add(localName)
|
|
297
191
|
}
|
|
298
192
|
}
|
|
299
193
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (importStatement.specifiers === undefined) {
|
|
317
|
-
importStatement.specifiers = []
|
|
318
|
-
}
|
|
319
|
-
const importSpecifiersToAdd = requiredImport.specifiers.map((spec) =>
|
|
320
|
-
b.importSpecifier(
|
|
321
|
-
b.identifier(spec.imported),
|
|
322
|
-
b.identifier(spec.imported),
|
|
323
|
-
),
|
|
324
|
-
)
|
|
325
|
-
importStatement.specifiers = [
|
|
326
|
-
...importStatement.specifiers,
|
|
327
|
-
...importSpecifiersToAdd,
|
|
328
|
-
]
|
|
329
|
-
return
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const importStatement = b.importDeclaration(
|
|
333
|
-
requiredImport.specifiers.map((spec) =>
|
|
334
|
-
b.importSpecifier(
|
|
335
|
-
b.identifier(spec.imported),
|
|
336
|
-
spec.local ? b.identifier(spec.local) : null,
|
|
337
|
-
),
|
|
338
|
-
),
|
|
339
|
-
b.stringLiteral(requiredImport.source),
|
|
340
|
-
)
|
|
341
|
-
program.body.unshift(importStatement)
|
|
194
|
+
|
|
195
|
+
return exportedRouteNames
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function findExportedRouteCalls(
|
|
199
|
+
body: Array<t.Statement>,
|
|
200
|
+
exportedRouteNames: Set<string>,
|
|
201
|
+
): RouteCallAnalysis {
|
|
202
|
+
const calls: Array<RouteCall> = []
|
|
203
|
+
let hasUnsupportedRouteId = false
|
|
204
|
+
let hasMalformedRouteCall = false
|
|
205
|
+
|
|
206
|
+
for (const statement of body) {
|
|
207
|
+
const declaration = getVariableDeclaration(statement)
|
|
208
|
+
if (!declaration) {
|
|
209
|
+
continue
|
|
342
210
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
211
|
+
|
|
212
|
+
for (const declarator of declaration.declarations) {
|
|
213
|
+
if (
|
|
214
|
+
!t.isIdentifier(declarator.id) ||
|
|
215
|
+
!exportedRouteNames.has(declarator.id.name)
|
|
216
|
+
) {
|
|
217
|
+
continue
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const init = getRouteConstructorInit(declarator.init)
|
|
221
|
+
if (!init) {
|
|
222
|
+
if (isDirectRouteConstructorCall(declarator.init)) {
|
|
223
|
+
hasMalformedRouteCall = true
|
|
352
224
|
}
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const routeIdArg = init.innerCall.arguments[0]
|
|
229
|
+
if (isSupportedRouteId(routeIdArg)) {
|
|
230
|
+
calls.push({
|
|
231
|
+
callee: init.callee,
|
|
232
|
+
routeIdArg,
|
|
233
|
+
optionsArg: init.outerCall.arguments[0],
|
|
234
|
+
})
|
|
235
|
+
} else {
|
|
236
|
+
hasUnsupportedRouteId = true
|
|
353
237
|
}
|
|
354
238
|
}
|
|
355
239
|
}
|
|
356
240
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
241
|
+
return { calls, hasUnsupportedRouteId, hasMalformedRouteCall }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function getVariableDeclaration(statement: t.Statement) {
|
|
245
|
+
const declaration = t.isExportNamedDeclaration(statement)
|
|
246
|
+
? statement.declaration
|
|
247
|
+
: statement
|
|
248
|
+
|
|
249
|
+
return t.isVariableDeclaration(declaration) ? declaration : null
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function getExportedName(node: t.Identifier | t.StringLiteral) {
|
|
253
|
+
return t.isIdentifier(node) ? node.name : node.value
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function getLocalBindingName(node: t.Identifier | t.StringLiteral) {
|
|
257
|
+
return t.isIdentifier(node) ? node.name : null
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function getRouteConstructorInit(expression: t.Expression | null | undefined) {
|
|
261
|
+
if (!expression || !t.isCallExpression(expression)) {
|
|
262
|
+
return null
|
|
361
263
|
}
|
|
362
264
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
sourceMapName: 'output.map',
|
|
366
|
-
})
|
|
367
|
-
let transformedCode = printResult.code
|
|
368
|
-
if (printResult.map) {
|
|
369
|
-
const fixedOutput = await fixTransformedOutputText({
|
|
370
|
-
originalCode: source,
|
|
371
|
-
transformedCode,
|
|
372
|
-
sourceMap: printResult.map as RawSourceMap,
|
|
373
|
-
preferredQuote,
|
|
374
|
-
})
|
|
375
|
-
transformedCode = fixedOutput
|
|
265
|
+
if (!t.isCallExpression(expression.callee)) {
|
|
266
|
+
return null
|
|
376
267
|
}
|
|
268
|
+
|
|
269
|
+
const innerCall = expression.callee
|
|
270
|
+
|
|
271
|
+
if (
|
|
272
|
+
!t.isIdentifier(innerCall.callee) ||
|
|
273
|
+
!isRouteConstructor(innerCall.callee)
|
|
274
|
+
) {
|
|
275
|
+
return null
|
|
276
|
+
}
|
|
277
|
+
|
|
377
278
|
return {
|
|
378
|
-
|
|
379
|
-
|
|
279
|
+
callee: innerCall.callee,
|
|
280
|
+
outerCall: expression,
|
|
281
|
+
innerCall,
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function isDirectRouteConstructorCall(
|
|
286
|
+
expression: t.Expression | null | undefined,
|
|
287
|
+
) {
|
|
288
|
+
return (
|
|
289
|
+
!!expression &&
|
|
290
|
+
t.isCallExpression(expression) &&
|
|
291
|
+
t.isIdentifier(expression.callee) &&
|
|
292
|
+
isRouteConstructor(expression.callee)
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function isRouteConstructor(callee: t.Identifier): callee is t.Identifier & {
|
|
297
|
+
name: RouteConstructorName
|
|
298
|
+
} {
|
|
299
|
+
return routeConstructors.includes(callee.name as RouteConstructorName)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isSupportedRouteId(
|
|
303
|
+
arg: t.CallExpression['arguments'][number] | undefined,
|
|
304
|
+
): arg is SupportedRouteId {
|
|
305
|
+
return (
|
|
306
|
+
!!arg &&
|
|
307
|
+
(t.isStringLiteral(arg) ||
|
|
308
|
+
(t.isTemplateLiteral(arg) && arg.expressions.length === 0))
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function getRouteIdQuote(
|
|
313
|
+
source: string,
|
|
314
|
+
arg: SupportedRouteId,
|
|
315
|
+
): '"' | "'" | '`' {
|
|
316
|
+
const raw = source.slice(arg.start!, arg.end!)
|
|
317
|
+
|
|
318
|
+
if (raw.startsWith("'")) return "'"
|
|
319
|
+
if (raw.startsWith('"')) return '"'
|
|
320
|
+
return '`'
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getCreateFileRouteProps(
|
|
324
|
+
arg: t.CallExpression['arguments'][number] | undefined,
|
|
325
|
+
) {
|
|
326
|
+
if (!arg || !t.isObjectExpression(arg)) {
|
|
327
|
+
return undefined
|
|
380
328
|
}
|
|
329
|
+
|
|
330
|
+
const props = new Set<string>()
|
|
331
|
+
|
|
332
|
+
for (const property of arg.properties) {
|
|
333
|
+
if (!t.isObjectProperty(property) || property.computed) {
|
|
334
|
+
continue
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (t.isIdentifier(property.key)) {
|
|
338
|
+
props.add(property.key.name)
|
|
339
|
+
continue
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (t.isStringLiteral(property.key)) {
|
|
343
|
+
props.add(property.key.value)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return props
|
|
381
348
|
}
|
|
382
349
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
350
|
+
function parseTargetImports(
|
|
351
|
+
body: Array<t.Statement>,
|
|
352
|
+
source: string,
|
|
353
|
+
targetModule: string,
|
|
354
|
+
) {
|
|
355
|
+
const imports: Array<ParsedImportDeclaration> = []
|
|
356
|
+
|
|
357
|
+
for (const statement of body) {
|
|
358
|
+
if (
|
|
359
|
+
!t.isImportDeclaration(statement) ||
|
|
360
|
+
statement.importKind === 'type' ||
|
|
361
|
+
statement.source.value !== targetModule
|
|
362
|
+
) {
|
|
363
|
+
continue
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const rawSource = source.slice(
|
|
367
|
+
statement.source.start!,
|
|
368
|
+
statement.source.end!,
|
|
369
|
+
)
|
|
370
|
+
const importStatement = source.slice(statement.start!, statement.end!)
|
|
371
|
+
|
|
372
|
+
imports.push({
|
|
373
|
+
declaration: statement,
|
|
374
|
+
defaultImport: statement.specifiers.find((specifier) =>
|
|
375
|
+
t.isImportDefaultSpecifier(specifier),
|
|
376
|
+
)?.local.name,
|
|
377
|
+
namespace: statement.specifiers.find((specifier) =>
|
|
378
|
+
t.isImportNamespaceSpecifier(specifier),
|
|
379
|
+
)?.local.name,
|
|
380
|
+
named: statement.specifiers
|
|
381
|
+
.filter((specifier): specifier is t.ImportSpecifier =>
|
|
382
|
+
t.isImportSpecifier(specifier),
|
|
383
|
+
)
|
|
384
|
+
.map((specifier) => ({
|
|
385
|
+
imported: t.isIdentifier(specifier.imported)
|
|
386
|
+
? specifier.imported.name
|
|
387
|
+
: specifier.imported.value,
|
|
388
|
+
local: specifier.local.name,
|
|
389
|
+
importKind: specifier.importKind ?? undefined,
|
|
390
|
+
})),
|
|
391
|
+
moduleName: statement.source.value,
|
|
392
|
+
quote: rawSource[0] as '"' | "'",
|
|
393
|
+
semicolon: importStatement.trimEnd().endsWith(';'),
|
|
394
|
+
})
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return imports
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function updateRouteImports({
|
|
401
|
+
imports,
|
|
402
|
+
source,
|
|
403
|
+
s,
|
|
404
|
+
targetModule,
|
|
405
|
+
required,
|
|
406
|
+
lineEnding,
|
|
388
407
|
}: {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
408
|
+
imports: Array<ParsedImportDeclaration>
|
|
409
|
+
source: string
|
|
410
|
+
s: MagicString
|
|
411
|
+
targetModule: string
|
|
412
|
+
required: RouteConstructorName
|
|
413
|
+
lineEnding: '\r\n' | '\n' | '\r'
|
|
393
414
|
}) {
|
|
394
|
-
const
|
|
395
|
-
|
|
415
|
+
const analysis = analyzeRouteImports(imports, required)
|
|
416
|
+
|
|
417
|
+
if (analysis.kind === 'ok') {
|
|
418
|
+
return false
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (analysis.kind === 'rename') {
|
|
422
|
+
s.update(analysis.imported.start!, analysis.imported.end!, analysis.next)
|
|
423
|
+
s.update(analysis.local.start!, analysis.local.end!, analysis.next)
|
|
424
|
+
return true
|
|
425
|
+
}
|
|
396
426
|
|
|
397
|
-
|
|
427
|
+
return normalizeRouteImports({
|
|
428
|
+
imports,
|
|
429
|
+
source,
|
|
430
|
+
s,
|
|
431
|
+
targetModule,
|
|
432
|
+
required,
|
|
433
|
+
lineEnding,
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function analyzeRouteImports(
|
|
438
|
+
imports: Array<ParsedImportDeclaration>,
|
|
439
|
+
required: RouteConstructorName,
|
|
440
|
+
): RouteImportAnalysis {
|
|
441
|
+
const opposite = getOtherRouteConstructor(required)
|
|
442
|
+
let requiredCount = 0
|
|
443
|
+
let oppositeCount = 0
|
|
444
|
+
let renameCandidate:
|
|
445
|
+
| {
|
|
446
|
+
imported: t.Identifier
|
|
447
|
+
local: t.Identifier
|
|
448
|
+
next: RouteConstructorName
|
|
449
|
+
}
|
|
450
|
+
| undefined
|
|
398
451
|
|
|
399
|
-
const
|
|
452
|
+
for (const declaration of imports) {
|
|
453
|
+
for (const specifier of declaration.declaration.specifiers) {
|
|
454
|
+
if (!t.isImportSpecifier(specifier)) {
|
|
455
|
+
continue
|
|
456
|
+
}
|
|
400
457
|
|
|
401
|
-
|
|
402
|
-
|
|
458
|
+
const imported = specifier.imported
|
|
459
|
+
if (!t.isIdentifier(imported)) {
|
|
460
|
+
return { kind: 'normalize' }
|
|
461
|
+
}
|
|
403
462
|
|
|
404
|
-
|
|
463
|
+
if (!isRouteConstructorName(imported.name)) {
|
|
464
|
+
continue
|
|
465
|
+
}
|
|
405
466
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
line: transformedLineNum,
|
|
409
|
-
column: col,
|
|
410
|
-
})
|
|
411
|
-
if (mapped.line != null && mapped.line > 0) {
|
|
412
|
-
origLineText = originalLines[mapped.line - 1]
|
|
413
|
-
break
|
|
467
|
+
if (specifier.local.name !== imported.name) {
|
|
468
|
+
return { kind: 'normalize' }
|
|
414
469
|
}
|
|
415
|
-
}
|
|
416
470
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
471
|
+
if (imported.name === required) {
|
|
472
|
+
requiredCount++
|
|
473
|
+
continue
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (imported.name === opposite) {
|
|
477
|
+
oppositeCount++
|
|
478
|
+
renameCandidate = {
|
|
479
|
+
imported,
|
|
480
|
+
local: specifier.local,
|
|
481
|
+
next: required,
|
|
482
|
+
}
|
|
420
483
|
}
|
|
421
|
-
return fixLine(line, {
|
|
422
|
-
originalLine: origLineText,
|
|
423
|
-
useOriginalSemicolon: true,
|
|
424
|
-
useOriginalQuotes: true,
|
|
425
|
-
fallbackQuote: preferredQuote,
|
|
426
|
-
})
|
|
427
|
-
} else {
|
|
428
|
-
return fixLine(line, {
|
|
429
|
-
originalLine: null,
|
|
430
|
-
useOriginalSemicolon: false,
|
|
431
|
-
useOriginalQuotes: false,
|
|
432
|
-
fallbackQuote: preferredQuote,
|
|
433
|
-
fallbackSemicolon: defaultUsesSemicolons,
|
|
434
|
-
})
|
|
435
484
|
}
|
|
436
|
-
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (requiredCount === 1 && oppositeCount === 0) {
|
|
488
|
+
return { kind: 'ok' }
|
|
489
|
+
}
|
|
437
490
|
|
|
438
|
-
|
|
491
|
+
if (requiredCount === 0 && oppositeCount === 1 && renameCandidate) {
|
|
492
|
+
return {
|
|
493
|
+
kind: 'rename',
|
|
494
|
+
...renameCandidate,
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return { kind: 'normalize' }
|
|
439
499
|
}
|
|
440
500
|
|
|
441
|
-
function
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
501
|
+
function normalizeRouteImports({
|
|
502
|
+
imports,
|
|
503
|
+
source,
|
|
504
|
+
s,
|
|
505
|
+
targetModule,
|
|
506
|
+
required,
|
|
507
|
+
lineEnding,
|
|
508
|
+
}: {
|
|
509
|
+
imports: Array<ParsedImportDeclaration>
|
|
510
|
+
source: string
|
|
511
|
+
s: MagicString
|
|
512
|
+
targetModule: string
|
|
513
|
+
required: RouteConstructorName
|
|
514
|
+
lineEnding: '\r\n' | '\n' | '\r'
|
|
515
|
+
}) {
|
|
516
|
+
const owner =
|
|
517
|
+
imports.find((declaration) =>
|
|
518
|
+
hasNamedImport(declaration.named, required),
|
|
519
|
+
) ?? imports.find((declaration) => !declaration.namespace)
|
|
520
|
+
|
|
521
|
+
let modified = false
|
|
522
|
+
|
|
523
|
+
for (const declaration of imports) {
|
|
524
|
+
const named = normalizeNamedImports({
|
|
525
|
+
named: declaration.named,
|
|
526
|
+
required,
|
|
527
|
+
isOwner: declaration === owner,
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
if (sameNamedImports(declaration.named, named)) {
|
|
531
|
+
continue
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const replacement = renderImportDeclaration({
|
|
535
|
+
...declaration,
|
|
536
|
+
named,
|
|
537
|
+
})
|
|
458
538
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
539
|
+
if (replacement === null) {
|
|
540
|
+
s.remove(
|
|
541
|
+
declaration.declaration.start!,
|
|
542
|
+
getRemovalEnd(source, declaration.declaration.end!),
|
|
543
|
+
)
|
|
544
|
+
modified = true
|
|
545
|
+
continue
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
s.update(
|
|
549
|
+
declaration.declaration.start!,
|
|
550
|
+
declaration.declaration.end!,
|
|
551
|
+
replacement,
|
|
552
|
+
)
|
|
553
|
+
modified = true
|
|
463
554
|
}
|
|
464
555
|
|
|
465
|
-
if (
|
|
466
|
-
const
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (!fallbackSemicolon && hasSemicolon) result = result.replace(/;\s*$/, '')
|
|
473
|
-
if (fallbackSemicolon && !hasSemicolon && result.trim()) result += ';'
|
|
556
|
+
if (!owner) {
|
|
557
|
+
const quote = imports[0]?.quote ?? "'"
|
|
558
|
+
const semicolon = imports[0]?.semicolon ?? false
|
|
559
|
+
s.prepend(
|
|
560
|
+
`import { ${required} } from ${quote}${targetModule}${quote}${semicolon ? ';' : ''}${lineEnding}`,
|
|
561
|
+
)
|
|
562
|
+
modified = true
|
|
474
563
|
}
|
|
475
564
|
|
|
476
|
-
return
|
|
565
|
+
return modified
|
|
477
566
|
}
|
|
478
567
|
|
|
479
|
-
function
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
568
|
+
function normalizeNamedImports({
|
|
569
|
+
named,
|
|
570
|
+
required,
|
|
571
|
+
isOwner,
|
|
572
|
+
}: {
|
|
573
|
+
named: Array<NamedImport>
|
|
574
|
+
required: RouteConstructorName
|
|
575
|
+
isOwner: boolean
|
|
576
|
+
}) {
|
|
577
|
+
const banned = getOtherRouteConstructor(required)
|
|
578
|
+
const nextNamed: Array<NamedImport> = []
|
|
579
|
+
const seen = new Set<string>()
|
|
580
|
+
|
|
581
|
+
for (const specifier of named) {
|
|
582
|
+
if (specifier.imported === banned) {
|
|
583
|
+
continue
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (
|
|
587
|
+
specifier.local === required &&
|
|
588
|
+
(specifier.imported !== required || !isOwner)
|
|
589
|
+
) {
|
|
590
|
+
continue
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const key = `${specifier.importKind ?? 'value'}:${specifier.imported}:${specifier.local}`
|
|
594
|
+
if (seen.has(key)) {
|
|
595
|
+
continue
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
seen.add(key)
|
|
599
|
+
nextNamed.push(specifier)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (isOwner && !hasNamedImport(nextNamed, required)) {
|
|
603
|
+
nextNamed.push({ imported: required, local: required })
|
|
483
604
|
}
|
|
484
|
-
|
|
605
|
+
|
|
606
|
+
return nextNamed
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function getExpectedRouteConstructor(lazy: boolean): RouteConstructorName {
|
|
610
|
+
return lazy ? 'createLazyFileRoute' : 'createFileRoute'
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function getOtherRouteConstructor(
|
|
614
|
+
constructor: RouteConstructorName,
|
|
615
|
+
): RouteConstructorName {
|
|
616
|
+
return constructor === 'createFileRoute'
|
|
617
|
+
? 'createLazyFileRoute'
|
|
618
|
+
: 'createFileRoute'
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function hasNamedImport(
|
|
622
|
+
named: Array<NamedImport>,
|
|
623
|
+
required: RouteConstructorName,
|
|
624
|
+
) {
|
|
625
|
+
return named.some(
|
|
626
|
+
(specifier) =>
|
|
627
|
+
specifier.imported === required &&
|
|
628
|
+
specifier.local === required &&
|
|
629
|
+
specifier.importKind !== 'type',
|
|
630
|
+
)
|
|
485
631
|
}
|
|
486
632
|
|
|
487
|
-
function
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
633
|
+
function sameNamedImports(left: Array<NamedImport>, right: Array<NamedImport>) {
|
|
634
|
+
return (
|
|
635
|
+
left.length === right.length &&
|
|
636
|
+
left.every(
|
|
637
|
+
(specifier, index) =>
|
|
638
|
+
specifier.imported === right[index]!.imported &&
|
|
639
|
+
specifier.local === right[index]!.local &&
|
|
640
|
+
specifier.importKind === right[index]!.importKind,
|
|
641
|
+
)
|
|
495
642
|
)
|
|
496
643
|
}
|
|
497
644
|
|
|
498
|
-
function
|
|
499
|
-
|
|
500
|
-
return match ? match[1] : null
|
|
645
|
+
function isRouteConstructorName(value: string): value is RouteConstructorName {
|
|
646
|
+
return routeConstructors.includes(value as RouteConstructorName)
|
|
501
647
|
}
|
|
502
648
|
|
|
503
|
-
function
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
649
|
+
function renderImportDeclaration(importDeclaration: {
|
|
650
|
+
defaultImport?: string
|
|
651
|
+
namespace?: string
|
|
652
|
+
named: Array<NamedImport>
|
|
653
|
+
moduleName: string
|
|
654
|
+
quote: '"' | "'"
|
|
655
|
+
semicolon: boolean
|
|
656
|
+
}) {
|
|
657
|
+
const parts: Array<string> = []
|
|
658
|
+
|
|
659
|
+
if (importDeclaration.defaultImport) {
|
|
660
|
+
parts.push(importDeclaration.defaultImport)
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (importDeclaration.namespace) {
|
|
664
|
+
parts.push(`* as ${importDeclaration.namespace}`)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (importDeclaration.named.length > 0) {
|
|
668
|
+
parts.push(
|
|
669
|
+
`{ ${importDeclaration.named
|
|
670
|
+
.map(
|
|
671
|
+
(specifier) =>
|
|
672
|
+
`${specifier.importKind === 'type' ? 'type ' : ''}${specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`}`,
|
|
673
|
+
)
|
|
674
|
+
.join(', ')} }`,
|
|
675
|
+
)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (parts.length === 0) {
|
|
679
|
+
return null
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return `import ${parts.join(', ')} from ${importDeclaration.quote}${importDeclaration.moduleName}${importDeclaration.quote}${importDeclaration.semicolon ? ';' : ''}`
|
|
508
683
|
}
|
|
509
684
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
685
|
+
function getLineEnding(source: string): '\r\n' | '\n' | '\r' {
|
|
686
|
+
if (source.includes('\r\n')) {
|
|
687
|
+
return '\r\n'
|
|
688
|
+
}
|
|
513
689
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
const raw = path.node.extra?.raw
|
|
518
|
-
if (raw?.startsWith("'")) single++
|
|
519
|
-
else if (raw?.startsWith('"')) double++
|
|
520
|
-
}
|
|
521
|
-
return false
|
|
522
|
-
},
|
|
523
|
-
})
|
|
690
|
+
if (source.includes('\n')) {
|
|
691
|
+
return '\n'
|
|
692
|
+
}
|
|
524
693
|
|
|
525
|
-
if (
|
|
526
|
-
return
|
|
694
|
+
if (source.includes('\r')) {
|
|
695
|
+
return '\r'
|
|
527
696
|
}
|
|
528
|
-
|
|
697
|
+
|
|
698
|
+
return '\n'
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function getRemovalEnd(source: string, end: number) {
|
|
702
|
+
let pos = end
|
|
703
|
+
while (pos < source.length && (source[pos] === ' ' || source[pos] === '\t')) {
|
|
704
|
+
pos++
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (source[pos] === '\r' && source[pos + 1] === '\n') {
|
|
708
|
+
return pos + 2
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (source[pos] === '\n' || source[pos] === '\r') {
|
|
712
|
+
return pos + 1
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return end
|
|
529
716
|
}
|