one 1.25.16 → 1.26.0
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/native-transforms.cjs +28 -0
- package/dist/cjs/native-transforms.native.js +31 -0
- package/dist/cjs/native-transforms.native.js.map +1 -0
- package/dist/cjs/router/useViteRoutes.cjs +3 -2
- package/dist/cjs/router/useViteRoutes.native.js +3 -2
- package/dist/cjs/router/useViteRoutes.native.js.map +1 -1
- package/dist/cjs/router/useViteRoutes.test.cjs +101 -1
- package/dist/cjs/router/useViteRoutes.test.native.js +109 -1
- package/dist/cjs/router/useViteRoutes.test.native.js.map +1 -1
- package/dist/cjs/vercel/build/generate/createApiServerlessFunction.cjs +45 -44
- package/dist/cjs/vercel/build/generate/createApiServerlessFunction.native.js +64 -39
- package/dist/cjs/vercel/build/generate/createApiServerlessFunction.native.js.map +1 -1
- package/dist/cjs/vite/one.cjs +10 -2
- package/dist/cjs/vite/one.native.js +13 -4
- package/dist/cjs/vite/one.native.js.map +1 -1
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.cjs +597 -92
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.native.js +1358 -113
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.native.js.map +1 -1
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.cjs +185 -0
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.native.js +185 -0
- package/dist/cjs/vite/plugins/clientTreeShakePlugin.test.native.js.map +1 -1
- package/dist/cjs/vite/plugins/useDomPlugin.cjs +14 -37
- package/dist/cjs/vite/plugins/useDomPlugin.native.js +14 -36
- package/dist/cjs/vite/plugins/useDomPlugin.native.js.map +1 -1
- package/dist/esm/native-transforms.mjs +3 -0
- package/dist/esm/native-transforms.mjs.map +1 -0
- package/dist/esm/native-transforms.native.js +3 -0
- package/dist/esm/native-transforms.native.js.map +1 -0
- package/dist/esm/router/useViteRoutes.mjs +3 -2
- package/dist/esm/router/useViteRoutes.mjs.map +1 -1
- package/dist/esm/router/useViteRoutes.native.js +3 -2
- package/dist/esm/router/useViteRoutes.native.js.map +1 -1
- package/dist/esm/router/useViteRoutes.test.mjs +77 -1
- package/dist/esm/router/useViteRoutes.test.mjs.map +1 -1
- package/dist/esm/router/useViteRoutes.test.native.js +85 -1
- package/dist/esm/router/useViteRoutes.test.native.js.map +1 -1
- package/dist/esm/vercel/build/generate/createApiServerlessFunction.mjs +45 -44
- package/dist/esm/vercel/build/generate/createApiServerlessFunction.mjs.map +1 -1
- package/dist/esm/vercel/build/generate/createApiServerlessFunction.native.js +64 -39
- package/dist/esm/vercel/build/generate/createApiServerlessFunction.native.js.map +1 -1
- package/dist/esm/vite/one.mjs +10 -2
- package/dist/esm/vite/one.mjs.map +1 -1
- package/dist/esm/vite/one.native.js +13 -4
- package/dist/esm/vite/one.native.js.map +1 -1
- package/dist/esm/vite/plugins/clientTreeShakePlugin.mjs +597 -92
- package/dist/esm/vite/plugins/clientTreeShakePlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/clientTreeShakePlugin.native.js +1358 -113
- package/dist/esm/vite/plugins/clientTreeShakePlugin.native.js.map +1 -1
- package/dist/esm/vite/plugins/clientTreeShakePlugin.test.mjs +185 -0
- package/dist/esm/vite/plugins/clientTreeShakePlugin.test.mjs.map +1 -1
- package/dist/esm/vite/plugins/clientTreeShakePlugin.test.native.js +185 -0
- package/dist/esm/vite/plugins/clientTreeShakePlugin.test.native.js.map +1 -1
- package/dist/esm/vite/plugins/useDomPlugin.mjs +13 -25
- package/dist/esm/vite/plugins/useDomPlugin.mjs.map +1 -1
- package/dist/esm/vite/plugins/useDomPlugin.native.js +14 -25
- package/dist/esm/vite/plugins/useDomPlugin.native.js.map +1 -1
- package/package.json +15 -10
- package/src/createApp.tsx +0 -1
- package/src/headless.tsx +0 -1
- package/src/native-transforms.ts +11 -0
- package/src/router/useViteRoutes.test.ts +91 -0
- package/src/router/useViteRoutes.tsx +7 -4
- package/src/vercel/build/generate/createApiServerlessFunction.ts +65 -112
- package/src/vite/one.ts +22 -2
- package/src/vite/plugins/clientTreeShakePlugin.test.ts +201 -0
- package/src/vite/plugins/clientTreeShakePlugin.ts +732 -152
- package/src/vite/plugins/useDomPlugin.ts +20 -32
- package/src/vite/types.ts +60 -5
- package/types/native-transforms.d.ts +11 -0
- package/types/native-transforms.d.ts.map +1 -0
- package/types/router/useViteRoutes.d.ts.map +1 -1
- package/types/vercel/build/generate/createApiServerlessFunction.d.ts.map +1 -1
- package/types/vite/one.d.ts.map +1 -1
- package/types/vite/plugins/clientTreeShakePlugin.d.ts +1 -9
- package/types/vite/plugins/clientTreeShakePlugin.d.ts.map +1 -1
- package/types/vite/plugins/useDomPlugin.d.ts.map +1 -1
- package/types/vite/types.d.ts +57 -5
- package/types/vite/types.d.ts.map +1 -1
|
@@ -1,56 +1,9 @@
|
|
|
1
1
|
import { extname, relative } from 'node:path'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import BabelTraverse from '@babel/traverse'
|
|
5
|
-
import type * as t from '@babel/types'
|
|
6
|
-
import {
|
|
7
|
-
deadCodeElimination,
|
|
8
|
-
findReferencedIdentifiers,
|
|
9
|
-
} from 'babel-dead-code-elimination'
|
|
2
|
+
import MagicString from 'magic-string'
|
|
3
|
+
import { parseSync } from 'oxc-parser'
|
|
10
4
|
import type { Plugin } from 'vite'
|
|
11
5
|
import { EMPTY_LOADER_STRING, makeLoaderRouteIdStub } from '../constants'
|
|
12
6
|
|
|
13
|
-
const traverse = (BabelTraverse['default'] || BabelTraverse) as typeof BabelTraverse
|
|
14
|
-
const generate = (BabelGenerate['default'] ||
|
|
15
|
-
BabelGenerate) as any as typeof BabelGenerate
|
|
16
|
-
|
|
17
|
-
// Collect type-only imports before dead code elimination runs
|
|
18
|
-
// These should never be removed since TypeScript erases them at compile time
|
|
19
|
-
function collectTypeImports(ast: t.File): t.ImportDeclaration[] {
|
|
20
|
-
const typeImports: t.ImportDeclaration[] = []
|
|
21
|
-
traverse(ast, {
|
|
22
|
-
ImportDeclaration(path) {
|
|
23
|
-
// Check if the entire import is type-only: `import type { X } from '...'`
|
|
24
|
-
if (path.node.importKind === 'type') {
|
|
25
|
-
typeImports.push(path.node)
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
return typeImports
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Restore type-only imports that may have been removed by dead code elimination
|
|
33
|
-
function restoreTypeImports(ast: t.File, typeImports: t.ImportDeclaration[]) {
|
|
34
|
-
if (typeImports.length === 0) return
|
|
35
|
-
|
|
36
|
-
// Get existing import sources to avoid duplicates
|
|
37
|
-
const existingSources = new Set<string>()
|
|
38
|
-
traverse(ast, {
|
|
39
|
-
ImportDeclaration(path) {
|
|
40
|
-
if (path.node.importKind === 'type') {
|
|
41
|
-
existingSources.add(path.node.source.value)
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
// Add back any type imports that were removed
|
|
47
|
-
for (const typeImport of typeImports) {
|
|
48
|
-
if (!existingSources.has(typeImport.source.value)) {
|
|
49
|
-
ast.program.body.unshift(typeImport)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
7
|
export const clientTreeShakePlugin = (opts?: {
|
|
55
8
|
// 'rolldown' when used in the native rolldown DevEngine (no vite environment context)
|
|
56
9
|
runtime?: 'vite' | 'rolldown'
|
|
@@ -94,6 +47,327 @@ export const clientTreeShakePlugin = (opts?: {
|
|
|
94
47
|
} satisfies Plugin
|
|
95
48
|
}
|
|
96
49
|
|
|
50
|
+
function extractPatternBindings(node: any, names: Set<string>): void {
|
|
51
|
+
if (!node) return
|
|
52
|
+
switch (node.type) {
|
|
53
|
+
case 'Identifier':
|
|
54
|
+
names.add(node.name)
|
|
55
|
+
break
|
|
56
|
+
case 'ObjectPattern':
|
|
57
|
+
for (const prop of node.properties) {
|
|
58
|
+
if (prop.type === 'Property') {
|
|
59
|
+
extractPatternBindings(prop.value, names)
|
|
60
|
+
} else if (prop.type === 'RestElement') {
|
|
61
|
+
extractPatternBindings(prop.argument, names)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
break
|
|
65
|
+
case 'ArrayPattern':
|
|
66
|
+
for (const element of node.elements) {
|
|
67
|
+
if (element) {
|
|
68
|
+
extractPatternBindings(element, names)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
break
|
|
72
|
+
case 'AssignmentPattern':
|
|
73
|
+
extractPatternBindings(node.left, names)
|
|
74
|
+
break
|
|
75
|
+
case 'RestElement':
|
|
76
|
+
extractPatternBindings(node.argument, names)
|
|
77
|
+
break
|
|
78
|
+
case 'TSParameterProperty':
|
|
79
|
+
extractPatternBindings(node.parameter, names)
|
|
80
|
+
break
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getReferencedIdentifiers(
|
|
85
|
+
rootNode: any,
|
|
86
|
+
candidateNames: Set<string>
|
|
87
|
+
): Set<string> {
|
|
88
|
+
const referenced = new Set<string>()
|
|
89
|
+
const scopeStack: Set<string>[] = []
|
|
90
|
+
|
|
91
|
+
function isShadowed(name: string): boolean {
|
|
92
|
+
for (let i = scopeStack.length - 1; i >= 0; i--) {
|
|
93
|
+
if (scopeStack[i]!.has(name)) return true
|
|
94
|
+
}
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function checkIdentifier(name: string): void {
|
|
99
|
+
if (!isShadowed(name) && candidateNames.has(name)) {
|
|
100
|
+
referenced.add(name)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function walk(node: any): void {
|
|
105
|
+
if (!node || typeof node !== 'object') return
|
|
106
|
+
|
|
107
|
+
// Skip TypeScript type declarations and annotations
|
|
108
|
+
if (
|
|
109
|
+
node.type === 'TSTypeAnnotation' ||
|
|
110
|
+
node.type === 'TSTypeReference' ||
|
|
111
|
+
node.type === 'TSTypeAliasDeclaration' ||
|
|
112
|
+
node.type === 'TSInterfaceDeclaration' ||
|
|
113
|
+
node.type === 'TSTypeParameterDeclaration' ||
|
|
114
|
+
node.type === 'TSTypeParameterInstantiation'
|
|
115
|
+
) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (
|
|
120
|
+
node.type === 'TSAsExpression' ||
|
|
121
|
+
node.type === 'TSTypeAssertion' ||
|
|
122
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
123
|
+
node.type === 'TSNonNullExpression'
|
|
124
|
+
) {
|
|
125
|
+
walk(node.expression)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function walkPatternExpressions(pat: any): void {
|
|
130
|
+
if (!pat || typeof pat !== 'object') return
|
|
131
|
+
switch (pat.type) {
|
|
132
|
+
case 'AssignmentPattern':
|
|
133
|
+
walk(pat.right)
|
|
134
|
+
walkPatternExpressions(pat.left)
|
|
135
|
+
break
|
|
136
|
+
case 'ObjectPattern':
|
|
137
|
+
for (const prop of pat.properties) {
|
|
138
|
+
if (prop.type === 'Property') {
|
|
139
|
+
if (prop.computed) {
|
|
140
|
+
walk(prop.key)
|
|
141
|
+
}
|
|
142
|
+
walkPatternExpressions(prop.value)
|
|
143
|
+
} else if (prop.type === 'RestElement') {
|
|
144
|
+
walkPatternExpressions(prop.argument)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
break
|
|
148
|
+
case 'ArrayPattern':
|
|
149
|
+
for (const element of pat.elements) {
|
|
150
|
+
if (element) {
|
|
151
|
+
walkPatternExpressions(element)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
break
|
|
155
|
+
case 'RestElement':
|
|
156
|
+
walkPatternExpressions(pat.argument)
|
|
157
|
+
break
|
|
158
|
+
case 'TSParameterProperty':
|
|
159
|
+
walkPatternExpressions(pat.parameter)
|
|
160
|
+
break
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Function scopes
|
|
165
|
+
if (
|
|
166
|
+
node.type === 'FunctionDeclaration' ||
|
|
167
|
+
node.type === 'FunctionExpression' ||
|
|
168
|
+
node.type === 'ArrowFunctionExpression'
|
|
169
|
+
) {
|
|
170
|
+
const scope = new Set<string>()
|
|
171
|
+
if (node.type === 'FunctionExpression' && node.id) {
|
|
172
|
+
scope.add(node.id.name)
|
|
173
|
+
}
|
|
174
|
+
if (node.params) {
|
|
175
|
+
for (const param of node.params) {
|
|
176
|
+
walkPatternExpressions(param)
|
|
177
|
+
extractPatternBindings(param, scope)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Add var declarations hoisted in function
|
|
181
|
+
function addVars(n: any): void {
|
|
182
|
+
if (!n || typeof n !== 'object') return
|
|
183
|
+
if (
|
|
184
|
+
n.type === 'FunctionDeclaration' ||
|
|
185
|
+
n.type === 'FunctionExpression' ||
|
|
186
|
+
n.type === 'ArrowFunctionExpression'
|
|
187
|
+
) {
|
|
188
|
+
return // do not cross into inner functions
|
|
189
|
+
}
|
|
190
|
+
if (n.type === 'VariableDeclaration' && n.kind === 'var') {
|
|
191
|
+
for (const d of n.declarations) {
|
|
192
|
+
extractPatternBindings(d.id, scope)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
for (const k of Object.keys(n)) {
|
|
196
|
+
if (k === 'parent') continue
|
|
197
|
+
const val = n[k]
|
|
198
|
+
if (Array.isArray(val)) {
|
|
199
|
+
for (const item of val) addVars(item)
|
|
200
|
+
} else if (val && typeof val === 'object') {
|
|
201
|
+
addVars(val)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
addVars(node.body)
|
|
206
|
+
|
|
207
|
+
scopeStack.push(scope)
|
|
208
|
+
walk(node.body)
|
|
209
|
+
scopeStack.pop()
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Block scopes
|
|
214
|
+
if (node.type === 'BlockStatement') {
|
|
215
|
+
const scope = new Set<string>()
|
|
216
|
+
for (const stmt of node.body) {
|
|
217
|
+
if (
|
|
218
|
+
stmt.type === 'VariableDeclaration' &&
|
|
219
|
+
(stmt.kind === 'let' || stmt.kind === 'const')
|
|
220
|
+
) {
|
|
221
|
+
for (const decl of stmt.declarations) {
|
|
222
|
+
extractPatternBindings(decl.id, scope)
|
|
223
|
+
}
|
|
224
|
+
} else if (stmt.type === 'FunctionDeclaration' && stmt.id) {
|
|
225
|
+
scope.add(stmt.id.name)
|
|
226
|
+
} else if (stmt.type === 'ClassDeclaration' && stmt.id) {
|
|
227
|
+
scope.add(stmt.id.name)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
scopeStack.push(scope)
|
|
231
|
+
for (const stmt of node.body) {
|
|
232
|
+
walk(stmt)
|
|
233
|
+
}
|
|
234
|
+
scopeStack.pop()
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// CatchClause
|
|
239
|
+
if (node.type === 'CatchClause') {
|
|
240
|
+
const scope = new Set<string>()
|
|
241
|
+
if (node.param) {
|
|
242
|
+
extractPatternBindings(node.param, scope)
|
|
243
|
+
}
|
|
244
|
+
scopeStack.push(scope)
|
|
245
|
+
walk(node.body)
|
|
246
|
+
scopeStack.pop()
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Loop scopes
|
|
251
|
+
if (
|
|
252
|
+
node.type === 'ForStatement' ||
|
|
253
|
+
node.type === 'ForInStatement' ||
|
|
254
|
+
node.type === 'ForOfStatement'
|
|
255
|
+
) {
|
|
256
|
+
const scope = new Set<string>()
|
|
257
|
+
const initOrLeft = node.init || node.left
|
|
258
|
+
if (
|
|
259
|
+
initOrLeft &&
|
|
260
|
+
initOrLeft.type === 'VariableDeclaration' &&
|
|
261
|
+
initOrLeft.kind !== 'var'
|
|
262
|
+
) {
|
|
263
|
+
for (const decl of initOrLeft.declarations) {
|
|
264
|
+
extractPatternBindings(decl.id, scope)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
scopeStack.push(scope)
|
|
268
|
+
if (node.init) walk(node.init)
|
|
269
|
+
if (node.left) walk(node.left)
|
|
270
|
+
if (node.right) walk(node.right)
|
|
271
|
+
if (node.test) walk(node.test)
|
|
272
|
+
if (node.update) walk(node.update)
|
|
273
|
+
walk(node.body)
|
|
274
|
+
scopeStack.pop()
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Identifiers
|
|
279
|
+
if (node.type === 'Identifier') {
|
|
280
|
+
checkIdentifier(node.name)
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// JSX
|
|
285
|
+
if (node.type === 'JSXOpeningElement') {
|
|
286
|
+
if (node.name) {
|
|
287
|
+
if (node.name.type === 'JSXIdentifier') {
|
|
288
|
+
if (node.name.name && /^[A-Z]/.test(node.name.name)) {
|
|
289
|
+
checkIdentifier(node.name.name)
|
|
290
|
+
}
|
|
291
|
+
} else if (node.name.type === 'JSXMemberExpression') {
|
|
292
|
+
let rootObj = node.name.object
|
|
293
|
+
while (rootObj && rootObj.type === 'JSXMemberExpression') {
|
|
294
|
+
rootObj = rootObj.object
|
|
295
|
+
}
|
|
296
|
+
if (rootObj && rootObj.type === 'JSXIdentifier') {
|
|
297
|
+
checkIdentifier(rootObj.name)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (node.attributes) {
|
|
302
|
+
for (const attr of node.attributes) {
|
|
303
|
+
walk(attr)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (node.type === 'JSXAttribute') {
|
|
310
|
+
if (node.value) walk(node.value)
|
|
311
|
+
return
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (node.type === 'JSXClosingElement') {
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Property in ObjectExpression
|
|
319
|
+
if (node.type === 'Property') {
|
|
320
|
+
if (node.computed) {
|
|
321
|
+
walk(node.key)
|
|
322
|
+
}
|
|
323
|
+
walk(node.value)
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// MemberExpression
|
|
328
|
+
if (node.type === 'MemberExpression') {
|
|
329
|
+
walk(node.object)
|
|
330
|
+
if (node.computed) {
|
|
331
|
+
walk(node.property)
|
|
332
|
+
}
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// VariableDeclarator
|
|
337
|
+
if (node.type === 'VariableDeclarator') {
|
|
338
|
+
walkPatternExpressions(node.id)
|
|
339
|
+
if (node.init) {
|
|
340
|
+
walk(node.init)
|
|
341
|
+
}
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// ExportSpecifier
|
|
346
|
+
if (node.type === 'ExportSpecifier') {
|
|
347
|
+
if (node.local && node.local.type === 'Identifier') {
|
|
348
|
+
checkIdentifier(node.local.name)
|
|
349
|
+
}
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Generic recursive walk
|
|
354
|
+
for (const key of Object.keys(node)) {
|
|
355
|
+
if (key === 'parent') continue
|
|
356
|
+
const child = node[key]
|
|
357
|
+
if (Array.isArray(child)) {
|
|
358
|
+
for (const item of child) {
|
|
359
|
+
if (item && typeof item === 'object') walk(item)
|
|
360
|
+
}
|
|
361
|
+
} else if (child && typeof child === 'object') {
|
|
362
|
+
walk(child)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
walk(rootNode)
|
|
368
|
+
return referenced
|
|
369
|
+
}
|
|
370
|
+
|
|
97
371
|
export async function transformTreeShakeClient(
|
|
98
372
|
code: string,
|
|
99
373
|
id: string,
|
|
@@ -104,16 +378,29 @@ export async function transformTreeShakeClient(
|
|
|
104
378
|
return
|
|
105
379
|
}
|
|
106
380
|
|
|
107
|
-
|
|
381
|
+
const isProd = process.env.NODE_ENV === 'production'
|
|
382
|
+
const filename = (id || 'file.tsx').split('?')[0]!.split('#')[0]!
|
|
383
|
+
|
|
384
|
+
let parseResult: any
|
|
108
385
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
386
|
+
parseResult = parseSync(filename, code)
|
|
387
|
+
if (parseResult.errors && parseResult.errors.length > 0) {
|
|
388
|
+
const errorMessage = parseResult.errors.map((e: any) => e.message).join('\n')
|
|
389
|
+
if (isProd) {
|
|
390
|
+
throw new Error(
|
|
391
|
+
`[one] Failed to parse ${id} with server exports:\n${errorMessage}`
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
console.warn(
|
|
395
|
+
`[one] Skipping tree shaking for ${id} due to syntax error:`,
|
|
396
|
+
errorMessage
|
|
397
|
+
)
|
|
398
|
+
return
|
|
399
|
+
}
|
|
114
400
|
} catch (error) {
|
|
115
|
-
|
|
116
|
-
|
|
401
|
+
if (isProd) {
|
|
402
|
+
throw error instanceof Error ? error : new Error(String(error))
|
|
403
|
+
}
|
|
117
404
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
118
405
|
console.warn(
|
|
119
406
|
`[one] Skipping tree shaking for ${id} due to syntax error:`,
|
|
@@ -122,125 +409,418 @@ export async function transformTreeShakeClient(
|
|
|
122
409
|
return
|
|
123
410
|
}
|
|
124
411
|
|
|
125
|
-
|
|
412
|
+
const ast = parseResult.program
|
|
413
|
+
|
|
126
414
|
try {
|
|
127
|
-
|
|
415
|
+
return doTreeShakeClient(code, id, root, ast, routerRoot)
|
|
128
416
|
} catch (error) {
|
|
129
|
-
|
|
417
|
+
if (isProd) {
|
|
418
|
+
throw error instanceof Error ? error : new Error(String(error))
|
|
419
|
+
}
|
|
130
420
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
131
421
|
console.warn(
|
|
132
|
-
`[one] Skipping tree shaking for ${id} due to
|
|
422
|
+
`[one] Skipping tree shaking for ${id} due to analysis error:`,
|
|
133
423
|
errorMessage
|
|
134
424
|
)
|
|
135
425
|
return
|
|
136
426
|
}
|
|
427
|
+
}
|
|
137
428
|
|
|
138
|
-
|
|
429
|
+
function doTreeShakeClient(
|
|
430
|
+
code: string,
|
|
431
|
+
id: string,
|
|
432
|
+
root: string | undefined,
|
|
433
|
+
ast: any,
|
|
434
|
+
routerRoot: string
|
|
435
|
+
) {
|
|
436
|
+
const removed: Record<string, boolean> = {
|
|
139
437
|
loader: false,
|
|
140
438
|
generateStaticParams: false,
|
|
141
439
|
}
|
|
142
440
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
441
|
+
const serverExportStmts = new Map<
|
|
442
|
+
any,
|
|
443
|
+
{ names: Set<string>; replaceAll: boolean; declaratorsToRemove: Set<any> }
|
|
444
|
+
>()
|
|
445
|
+
|
|
446
|
+
for (const stmt of ast.body) {
|
|
447
|
+
if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration) {
|
|
448
|
+
if (stmt.declaration.type === 'FunctionDeclaration') {
|
|
449
|
+
const fnName = stmt.declaration.id?.name
|
|
450
|
+
if (fnName === 'loader' || fnName === 'generateStaticParams') {
|
|
451
|
+
removed[fnName] = true
|
|
452
|
+
serverExportStmts.set(stmt, {
|
|
453
|
+
names: new Set([fnName]),
|
|
454
|
+
replaceAll: true,
|
|
455
|
+
declaratorsToRemove: new Set(),
|
|
456
|
+
})
|
|
457
|
+
}
|
|
458
|
+
} else if (stmt.declaration.type === 'VariableDeclaration') {
|
|
459
|
+
const found: any[] = []
|
|
460
|
+
for (const d of stmt.declaration.declarations) {
|
|
461
|
+
if (
|
|
462
|
+
d.id?.type === 'Identifier' &&
|
|
463
|
+
(d.id.name === 'loader' || d.id.name === 'generateStaticParams')
|
|
464
|
+
) {
|
|
465
|
+
found.push(d)
|
|
466
|
+
removed[d.id.name] = true
|
|
158
467
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
declarator.id.type === 'Identifier' &&
|
|
166
|
-
(declarator.id.name === 'loader' ||
|
|
167
|
-
declarator.id.name === 'generateStaticParams')
|
|
168
|
-
) {
|
|
169
|
-
const declaration = path.get('declaration.declarations.' + index)
|
|
170
|
-
if (!Array.isArray(declaration) && declaration) {
|
|
171
|
-
;(declaration as any).remove()
|
|
172
|
-
removed[declarator.id.name] = true
|
|
173
|
-
}
|
|
174
|
-
}
|
|
468
|
+
}
|
|
469
|
+
if (found.length > 0) {
|
|
470
|
+
serverExportStmts.set(stmt, {
|
|
471
|
+
names: new Set(found.map((d) => d.id.name)),
|
|
472
|
+
replaceAll: found.length === stmt.declaration.declarations.length,
|
|
473
|
+
declaratorsToRemove: new Set(found),
|
|
175
474
|
})
|
|
176
475
|
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
`[one] Skipping tree shaking for ${id} due to traversal error:`,
|
|
184
|
-
errorMessage
|
|
185
|
-
)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const removedFunctions = Object.keys(removed).filter((k) => removed[k])
|
|
481
|
+
if (removedFunctions.length === 0) {
|
|
186
482
|
return
|
|
187
483
|
}
|
|
188
484
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
485
|
+
// Collect all module bindings
|
|
486
|
+
const moduleBindings = new Map<
|
|
487
|
+
string,
|
|
488
|
+
{
|
|
489
|
+
kind: 'import' | 'function' | 'variable' | 'class' | 'enum'
|
|
490
|
+
stmt: any
|
|
491
|
+
node: any
|
|
492
|
+
isType?: boolean
|
|
493
|
+
}
|
|
494
|
+
>()
|
|
495
|
+
|
|
496
|
+
for (const stmt of ast.body) {
|
|
497
|
+
if (stmt.type === 'ImportDeclaration') {
|
|
498
|
+
const isDeclType = stmt.importKind === 'type'
|
|
499
|
+
for (const sp of stmt.specifiers) {
|
|
500
|
+
const isType = isDeclType || sp.importKind === 'type'
|
|
501
|
+
moduleBindings.set(sp.local.name, {
|
|
502
|
+
kind: 'import',
|
|
503
|
+
stmt,
|
|
504
|
+
node: sp,
|
|
505
|
+
isType,
|
|
506
|
+
})
|
|
507
|
+
}
|
|
508
|
+
} else if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration) {
|
|
509
|
+
const decl = stmt.declaration
|
|
510
|
+
if (decl.type === 'FunctionDeclaration' && decl.id) {
|
|
511
|
+
moduleBindings.set(decl.id.name, { kind: 'function', stmt, node: decl })
|
|
512
|
+
} else if (decl.type === 'VariableDeclaration') {
|
|
513
|
+
for (const d of decl.declarations) {
|
|
514
|
+
const names = new Set<string>()
|
|
515
|
+
extractPatternBindings(d.id, names)
|
|
516
|
+
for (const name of names) {
|
|
517
|
+
moduleBindings.set(name, { kind: 'variable', stmt, node: d })
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
} else if (decl.type === 'ClassDeclaration' && decl.id) {
|
|
521
|
+
moduleBindings.set(decl.id.name, { kind: 'class', stmt, node: decl })
|
|
522
|
+
}
|
|
523
|
+
} else if (stmt.type === 'FunctionDeclaration' && stmt.id) {
|
|
524
|
+
moduleBindings.set(stmt.id.name, { kind: 'function', stmt, node: stmt })
|
|
525
|
+
} else if (stmt.type === 'VariableDeclaration') {
|
|
526
|
+
for (const d of stmt.declarations) {
|
|
527
|
+
const names = new Set<string>()
|
|
528
|
+
extractPatternBindings(d.id, names)
|
|
529
|
+
for (const name of names) {
|
|
530
|
+
moduleBindings.set(name, { kind: 'variable', stmt, node: d })
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
} else if (stmt.type === 'ClassDeclaration' && stmt.id) {
|
|
534
|
+
moduleBindings.set(stmt.id.name, { kind: 'class', stmt, node: stmt })
|
|
535
|
+
} else if (stmt.type === 'TSEnumDeclaration' && stmt.id) {
|
|
536
|
+
moduleBindings.set(stmt.id.name, { kind: 'enum', stmt, node: stmt })
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const allBindingNames = new Set(moduleBindings.keys())
|
|
541
|
+
const topLevelDeps = new Map<string, Set<string>>()
|
|
542
|
+
const clientRootDeps = new Set<string>()
|
|
543
|
+
|
|
544
|
+
// Compute dependencies of all module declarations
|
|
545
|
+
for (const [name, info] of moduleBindings) {
|
|
546
|
+
if (info.kind === 'import') {
|
|
547
|
+
topLevelDeps.set(name, new Set())
|
|
548
|
+
} else if (info.kind === 'function') {
|
|
549
|
+
topLevelDeps.set(name, getReferencedIdentifiers(info.node, allBindingNames))
|
|
550
|
+
} else if (info.kind === 'variable') {
|
|
551
|
+
const deps = getReferencedIdentifiers(info.node, allBindingNames)
|
|
552
|
+
deps.delete(name)
|
|
553
|
+
topLevelDeps.set(name, deps)
|
|
554
|
+
} else if (info.kind === 'class') {
|
|
555
|
+
topLevelDeps.set(name, getReferencedIdentifiers(info.node, allBindingNames))
|
|
556
|
+
} else if (info.kind === 'enum') {
|
|
557
|
+
topLevelDeps.set(name, getReferencedIdentifiers(info.node, allBindingNames))
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// 1. BFS for server needed (transitive closure of loader and generateStaticParams)
|
|
562
|
+
const serverNeeded = new Set<string>()
|
|
563
|
+
const serverQueue: string[] = []
|
|
564
|
+
for (const fn of removedFunctions) {
|
|
565
|
+
const deps = topLevelDeps.get(fn)
|
|
566
|
+
if (deps) {
|
|
567
|
+
for (const dep of deps) serverQueue.push(dep)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
while (serverQueue.length > 0) {
|
|
571
|
+
const name = serverQueue.shift()!
|
|
572
|
+
if (serverNeeded.has(name)) continue
|
|
573
|
+
serverNeeded.add(name)
|
|
574
|
+
const deps = topLevelDeps.get(name)
|
|
575
|
+
if (deps) {
|
|
576
|
+
for (const dep of deps) {
|
|
577
|
+
if (!serverNeeded.has(dep)) serverQueue.push(dep)
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 2. Compute client root dependencies from all retained statements
|
|
583
|
+
for (const stmt of ast.body) {
|
|
584
|
+
if (stmt.type === 'ImportDeclaration') {
|
|
585
|
+
continue
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (serverExportStmts.has(stmt)) {
|
|
589
|
+
const info = serverExportStmts.get(stmt)!
|
|
590
|
+
if (!info.replaceAll && stmt.declaration?.declarations) {
|
|
591
|
+
for (const d of stmt.declaration.declarations) {
|
|
592
|
+
if (!info.declaratorsToRemove.has(d)) {
|
|
593
|
+
const deps = getReferencedIdentifiers(d, allBindingNames)
|
|
594
|
+
for (const dep of deps) clientRootDeps.add(dep)
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
continue
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
602
|
+
const allInServer = stmt.declarations.every((d: any) => {
|
|
603
|
+
const names = new Set<string>()
|
|
604
|
+
extractPatternBindings(d.id, names)
|
|
605
|
+
return names.size > 0 && Array.from(names).every((n) => serverNeeded.has(n))
|
|
606
|
+
})
|
|
607
|
+
if (!allInServer) {
|
|
608
|
+
// Retained on client!
|
|
609
|
+
for (const d of stmt.declarations) {
|
|
610
|
+
const names = new Set<string>()
|
|
611
|
+
extractPatternBindings(d.id, names)
|
|
612
|
+
const inServer =
|
|
613
|
+
names.size > 0 && Array.from(names).every((n) => serverNeeded.has(n))
|
|
614
|
+
if (!inServer) {
|
|
615
|
+
const deps = getReferencedIdentifiers(d, allBindingNames)
|
|
616
|
+
for (const dep of deps) clientRootDeps.add(dep)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
continue
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (
|
|
624
|
+
(stmt.type === 'FunctionDeclaration' ||
|
|
625
|
+
stmt.type === 'ClassDeclaration' ||
|
|
626
|
+
stmt.type === 'TSEnumDeclaration') &&
|
|
627
|
+
stmt.id &&
|
|
628
|
+
serverNeeded.has(stmt.id.name)
|
|
629
|
+
) {
|
|
630
|
+
// Local server declaration, do not treat as client root
|
|
631
|
+
continue
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// All other top-level statements (ExportDefaultDeclaration, ExportNamedDeclaration,
|
|
635
|
+
// ExpressionStatement, IfStatement, ForStatement, WhileStatement, TryStatement, BlockStatement, etc.)
|
|
636
|
+
const deps = getReferencedIdentifiers(stmt, allBindingNames)
|
|
637
|
+
for (const dep of deps) clientRootDeps.add(dep)
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// 3. BFS for client needed
|
|
641
|
+
const clientNeeded = new Set<string>()
|
|
642
|
+
const clientQueue = Array.from(clientRootDeps)
|
|
643
|
+
while (clientQueue.length > 0) {
|
|
644
|
+
const name = clientQueue.shift()!
|
|
645
|
+
if (clientNeeded.has(name)) continue
|
|
646
|
+
clientNeeded.add(name)
|
|
647
|
+
// Do NOT expand server stub exports into server dependencies
|
|
648
|
+
if (name === 'loader' || name === 'generateStaticParams') continue
|
|
649
|
+
const deps = topLevelDeps.get(name)
|
|
650
|
+
if (deps) {
|
|
651
|
+
for (const dep of deps) {
|
|
652
|
+
if (!clientNeeded.has(dep)) clientQueue.push(dep)
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// 4. Exclusive server bindings
|
|
658
|
+
const exclusiveServerBindings = new Set<string>()
|
|
659
|
+
for (const name of serverNeeded) {
|
|
660
|
+
if (!clientNeeded.has(name)) {
|
|
661
|
+
exclusiveServerBindings.add(name)
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Use MagicString to apply modifications
|
|
666
|
+
const s = new MagicString(code)
|
|
667
|
+
|
|
668
|
+
// 1. Replace server export declarations with stubs
|
|
669
|
+
for (const [stmt, info] of serverExportStmts) {
|
|
670
|
+
const stubs: string[] = []
|
|
671
|
+
if (info.names.has('loader')) {
|
|
672
|
+
if (root) {
|
|
673
|
+
// compute routeId relative to the router root so it matches the
|
|
674
|
+
// route contextKey format buildPage looks the stub up by.
|
|
675
|
+
// contextKeys are like "./_layout.tsx", "./matches-test/page1+ssg.tsx"
|
|
676
|
+
const fromRoot = relative(root, id).replace(/\\/g, '/')
|
|
677
|
+
const prefix = routerRoot.replace(/^\.\//, '').replace(/\/$/, '') + '/'
|
|
678
|
+
const routeId =
|
|
679
|
+
'./' + (fromRoot.startsWith(prefix) ? fromRoot.slice(prefix.length) : fromRoot)
|
|
680
|
+
stubs.push(makeLoaderRouteIdStub(routeId))
|
|
681
|
+
} else {
|
|
682
|
+
stubs.push(EMPTY_LOADER_STRING)
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (info.names.has('generateStaticParams')) {
|
|
686
|
+
stubs.push('export function generateStaticParams() {};')
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
if (info.replaceAll) {
|
|
690
|
+
s.overwrite(stmt.start, stmt.end, stubs.join('\n'))
|
|
691
|
+
} else {
|
|
692
|
+
// Remove only the server declarators
|
|
693
|
+
const decl = stmt.declaration
|
|
694
|
+
for (let i = decl.declarations.length - 1; i >= 0; i--) {
|
|
695
|
+
const d = decl.declarations[i]
|
|
696
|
+
if (info.declaratorsToRemove.has(d)) {
|
|
697
|
+
if (i === 0) {
|
|
698
|
+
const next = decl.declarations[1]
|
|
699
|
+
s.remove(d.start, next.start)
|
|
700
|
+
} else {
|
|
701
|
+
const prev = decl.declarations[i - 1]
|
|
702
|
+
s.remove(prev.end, d.end)
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
s.appendRight(stmt.end, '\n' + stubs.join('\n'))
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// 2. Remove exclusive local declarations (functions, variables, classes)
|
|
711
|
+
for (const stmt of ast.body) {
|
|
712
|
+
if (serverExportStmts.has(stmt) || stmt.type === 'ImportDeclaration') continue
|
|
713
|
+
|
|
714
|
+
if (
|
|
715
|
+
(stmt.type === 'FunctionDeclaration' ||
|
|
716
|
+
stmt.type === 'ClassDeclaration' ||
|
|
717
|
+
stmt.type === 'TSEnumDeclaration') &&
|
|
718
|
+
stmt.id &&
|
|
719
|
+
exclusiveServerBindings.has(stmt.id.name)
|
|
720
|
+
) {
|
|
721
|
+
let end = stmt.end
|
|
722
|
+
if (code[end] === ';') end++
|
|
723
|
+
if (code[end] === '\r' && code[end + 1] === '\n') end += 2
|
|
724
|
+
else if (code[end] === '\n') end += 1
|
|
725
|
+
s.remove(stmt.start, end)
|
|
726
|
+
} else if (stmt.type === 'VariableDeclaration') {
|
|
727
|
+
const allExclusive = stmt.declarations.every((d: any) => {
|
|
728
|
+
const names = new Set<string>()
|
|
729
|
+
extractPatternBindings(d.id, names)
|
|
730
|
+
return Array.from(names).every((n) => exclusiveServerBindings.has(n))
|
|
731
|
+
})
|
|
732
|
+
if (allExclusive) {
|
|
733
|
+
let end = stmt.end
|
|
734
|
+
if (code[end] === ';') end++
|
|
735
|
+
if (code[end] === '\r' && code[end + 1] === '\n') end += 2
|
|
736
|
+
else if (code[end] === '\n') end += 1
|
|
737
|
+
s.remove(stmt.start, end)
|
|
738
|
+
} else {
|
|
739
|
+
// Check if any individual declarators should be removed
|
|
740
|
+
for (let i = stmt.declarations.length - 1; i >= 0; i--) {
|
|
741
|
+
const d = stmt.declarations[i]
|
|
742
|
+
const names = new Set<string>()
|
|
743
|
+
extractPatternBindings(d.id, names)
|
|
744
|
+
if (Array.from(names).every((n) => exclusiveServerBindings.has(n))) {
|
|
745
|
+
if (i === 0) {
|
|
746
|
+
const next = stmt.declarations[1]
|
|
747
|
+
s.remove(d.start, next.start)
|
|
748
|
+
} else {
|
|
749
|
+
const prev = stmt.declarations[i - 1]
|
|
750
|
+
s.remove(prev.end, d.end)
|
|
222
751
|
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
} else if (
|
|
756
|
+
stmt.type === 'ClassDeclaration' &&
|
|
757
|
+
stmt.id &&
|
|
758
|
+
exclusiveServerBindings.has(stmt.id.name)
|
|
759
|
+
) {
|
|
760
|
+
let end = stmt.end
|
|
761
|
+
if (code[end] === ';') end++
|
|
762
|
+
if (code[end] === '\r' && code[end + 1] === '\n') end += 2
|
|
763
|
+
else if (code[end] === '\n') end += 1
|
|
764
|
+
s.remove(stmt.start, end)
|
|
765
|
+
}
|
|
766
|
+
}
|
|
223
767
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
768
|
+
// 3. Remove unused import specifiers / declarations
|
|
769
|
+
for (const stmt of ast.body) {
|
|
770
|
+
if (stmt.type !== 'ImportDeclaration') continue
|
|
771
|
+
if (stmt.importKind === 'type') continue // Preserve type-only imports
|
|
772
|
+
if (!stmt.specifiers || stmt.specifiers.length === 0) continue // Preserve side-effect imports
|
|
227
773
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
)
|
|
774
|
+
const kept = stmt.specifiers.filter((sp: any) => {
|
|
775
|
+
if (sp.importKind === 'type') return true
|
|
776
|
+
return !exclusiveServerBindings.has(sp.local.name)
|
|
777
|
+
})
|
|
778
|
+
|
|
779
|
+
if (kept.length === 0) {
|
|
780
|
+
let end = stmt.end
|
|
781
|
+
if (code[end] === ';') end++
|
|
782
|
+
if (code[end] === '\r' && code[end + 1] === '\n') end += 2
|
|
783
|
+
else if (code[end] === '\n') end += 1
|
|
784
|
+
s.remove(stmt.start, end)
|
|
785
|
+
} else if (kept.length < stmt.specifiers.length) {
|
|
786
|
+
const declCode = code.slice(stmt.start, stmt.source.start)
|
|
787
|
+
const prefixMatch = declCode.match(/^import\s+/)
|
|
788
|
+
const prefixEnd = stmt.start + (prefixMatch ? prefixMatch[0].length : 7)
|
|
789
|
+
|
|
790
|
+
let pos = stmt.source.start - 1
|
|
791
|
+
while (pos >= 0 && /\s/.test(code[pos]!)) pos--
|
|
792
|
+
const fromStart = pos - 3 // 'from' keyword start
|
|
793
|
+
|
|
794
|
+
const defaultSpec = kept.find((sp: any) => sp.type === 'ImportDefaultSpecifier')
|
|
795
|
+
const namespaceSpec = kept.find((sp: any) => sp.type === 'ImportNamespaceSpecifier')
|
|
796
|
+
const namedSpecs = kept.filter((sp: any) => sp.type === 'ImportSpecifier')
|
|
231
797
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
798
|
+
const parts: string[] = []
|
|
799
|
+
if (defaultSpec) parts.push(defaultSpec.local.name)
|
|
800
|
+
if (namespaceSpec) parts.push('* as ' + namespaceSpec.local.name)
|
|
801
|
+
if (namedSpecs.length > 0) {
|
|
802
|
+
const namedStr = namedSpecs
|
|
803
|
+
.map((sp: any) => {
|
|
804
|
+
const typePrefix = sp.importKind === 'type' ? 'type ' : ''
|
|
805
|
+
const importedName = sp.imported.raw || sp.imported.name
|
|
806
|
+
if (importedName === sp.local.name) {
|
|
807
|
+
return typePrefix + sp.local.name
|
|
808
|
+
}
|
|
809
|
+
return typePrefix + importedName + ' as ' + sp.local.name
|
|
810
|
+
})
|
|
811
|
+
.join(', ')
|
|
812
|
+
parts.push('{ ' + namedStr + ' }')
|
|
235
813
|
}
|
|
236
|
-
|
|
237
|
-
// If code generation fails, skip transformation
|
|
238
|
-
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
239
|
-
console.warn(
|
|
240
|
-
`[one] Skipping tree shaking for ${id} due to code generation error:`,
|
|
241
|
-
errorMessage
|
|
242
|
-
)
|
|
243
|
-
return
|
|
814
|
+
s.overwrite(prefixEnd, fromStart, parts.join(', ') + ' ')
|
|
244
815
|
}
|
|
245
816
|
}
|
|
817
|
+
|
|
818
|
+
console.info(
|
|
819
|
+
` 🧹 [one] ${relative(process.cwd(), id)} removed ${removedFunctions.length} server-only exports`
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
return {
|
|
823
|
+
code: s.toString(),
|
|
824
|
+
map: s.generateMap({ hires: 'boundary', source: id, includeContent: true }),
|
|
825
|
+
}
|
|
246
826
|
}
|