i18next-cli 1.10.1 → 1.10.2
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/CHANGELOG.md +8 -0
- package/README.md +3 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/core/ast-visitors.js +1 -0
- package/dist/cjs/extractor/core/key-finder.js +1 -1
- package/dist/cjs/extractor/core/translation-manager.js +1 -1
- package/dist/cjs/extractor/parsers/call-expression-handler.js +1 -0
- package/dist/cjs/extractor/parsers/expression-resolver.js +1 -0
- package/dist/cjs/extractor/parsers/jsx-handler.js +1 -0
- package/dist/cjs/extractor/parsers/scope-manager.js +1 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/ast-visitors.js +1 -0
- package/dist/esm/extractor/core/key-finder.js +1 -1
- package/dist/esm/extractor/core/translation-manager.js +1 -1
- package/dist/esm/extractor/parsers/call-expression-handler.js +1 -0
- package/dist/esm/extractor/parsers/expression-resolver.js +1 -0
- package/dist/esm/extractor/parsers/jsx-handler.js +1 -0
- package/dist/esm/extractor/parsers/scope-manager.js +1 -0
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/extractor/core/ast-visitors.ts +170 -0
- package/src/extractor/core/extractor.ts +1 -1
- package/src/extractor/core/key-finder.ts +2 -2
- package/src/extractor/core/translation-manager.ts +88 -8
- package/src/extractor/index.ts +1 -1
- package/src/extractor/parsers/call-expression-handler.ts +506 -0
- package/src/extractor/parsers/expression-resolver.ts +178 -0
- package/src/extractor/parsers/jsx-handler.ts +358 -0
- package/src/extractor/parsers/scope-manager.ts +327 -0
- package/src/extractor.ts +1 -1
- package/src/types.ts +82 -0
- package/types/extractor/core/ast-visitors.d.ts +75 -0
- package/types/extractor/core/ast-visitors.d.ts.map +1 -0
- package/types/extractor/core/extractor.d.ts +1 -1
- package/types/extractor/core/extractor.d.ts.map +1 -1
- package/types/extractor/core/key-finder.d.ts.map +1 -1
- package/types/extractor/core/translation-manager.d.ts.map +1 -1
- package/types/extractor/index.d.ts +1 -1
- package/types/extractor/index.d.ts.map +1 -1
- package/types/extractor/parsers/call-expression-handler.d.ts +74 -0
- package/types/extractor/parsers/call-expression-handler.d.ts.map +1 -0
- package/types/extractor/parsers/expression-resolver.d.ts +62 -0
- package/types/extractor/parsers/expression-resolver.d.ts.map +1 -0
- package/types/extractor/parsers/jsx-handler.d.ts +44 -0
- package/types/extractor/parsers/jsx-handler.d.ts.map +1 -0
- package/types/extractor/parsers/scope-manager.d.ts +99 -0
- package/types/extractor/parsers/scope-manager.d.ts.map +1 -0
- package/types/extractor.d.ts +1 -1
- package/types/extractor.d.ts.map +1 -1
- package/types/types.d.ts +77 -0
- package/types/types.d.ts.map +1 -1
- package/dist/cjs/extractor/parsers/ast-visitors.js +0 -1
- package/dist/esm/extractor/parsers/ast-visitors.js +0 -1
- package/src/extractor/parsers/ast-visitors.ts +0 -1510
- package/types/extractor/parsers/ast-visitors.d.ts +0 -352
- package/types/extractor/parsers/ast-visitors.d.ts.map +0 -1
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import type { VariableDeclarator, CallExpression } from '@swc/core'
|
|
2
|
+
import type { ScopeInfo, UseTranslationHookConfig, I18nextToolkitConfig } from '../../types'
|
|
3
|
+
import { getObjectPropValue } from './ast-utils'
|
|
4
|
+
|
|
5
|
+
export class ScopeManager {
|
|
6
|
+
private scopeStack: Array<Map<string, ScopeInfo>> = []
|
|
7
|
+
private config: Omit<I18nextToolkitConfig, 'plugins'>
|
|
8
|
+
private scope: Map<string, { defaultNs?: string; keyPrefix?: string }> = new Map()
|
|
9
|
+
|
|
10
|
+
constructor (config: Omit<I18nextToolkitConfig, 'plugins'>) {
|
|
11
|
+
this.config = config
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Enters a new variable scope by pushing a new scope map onto the stack.
|
|
16
|
+
* Used when entering functions to isolate variable declarations.
|
|
17
|
+
*/
|
|
18
|
+
enterScope (): void {
|
|
19
|
+
this.scopeStack.push(new Map())
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Exits the current variable scope by popping the top scope map.
|
|
24
|
+
* Used when leaving functions to clean up variable tracking.
|
|
25
|
+
*/
|
|
26
|
+
exitScope (): void {
|
|
27
|
+
this.scopeStack.pop()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Stores variable information in the current scope.
|
|
32
|
+
* Used to track translation functions and their configuration.
|
|
33
|
+
*
|
|
34
|
+
* @param name - Variable name to store
|
|
35
|
+
* @param info - Scope information about the variable
|
|
36
|
+
*/
|
|
37
|
+
setVarInScope (name: string, info: ScopeInfo): void {
|
|
38
|
+
if (this.scopeStack.length > 0) {
|
|
39
|
+
this.scopeStack[this.scopeStack.length - 1].set(name, info)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Retrieves variable information from the scope chain.
|
|
45
|
+
* Searches from innermost to outermost scope.
|
|
46
|
+
*
|
|
47
|
+
* @param name - Variable name to look up
|
|
48
|
+
* @returns Scope information if found, undefined otherwise
|
|
49
|
+
*/
|
|
50
|
+
getVarFromScope (name: string): ScopeInfo | undefined {
|
|
51
|
+
// First check the proper scope stack (this is the primary source of truth)
|
|
52
|
+
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
|
|
53
|
+
if (this.scopeStack[i].has(name)) {
|
|
54
|
+
const scopeInfo = this.scopeStack[i].get(name)
|
|
55
|
+
return scopeInfo
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Then check the legacy scope tracking for useTranslation calls (for comment parsing)
|
|
60
|
+
const legacyScope = this.scope.get(name)
|
|
61
|
+
if (legacyScope) {
|
|
62
|
+
return legacyScope
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Handles variable declarations that might define translation functions.
|
|
70
|
+
*
|
|
71
|
+
* Processes two patterns:
|
|
72
|
+
* 1. `const { t } = useTranslation(...)` - React i18next pattern
|
|
73
|
+
* 2. `const t = i18next.getFixedT(...)` - Core i18next pattern
|
|
74
|
+
*
|
|
75
|
+
* Extracts namespace and key prefix information for later use.
|
|
76
|
+
*
|
|
77
|
+
* @param node - Variable declarator node to process
|
|
78
|
+
*/
|
|
79
|
+
handleVariableDeclarator (node: VariableDeclarator): void {
|
|
80
|
+
const init = node.init
|
|
81
|
+
if (!init) return
|
|
82
|
+
|
|
83
|
+
// Determine the actual call expression, looking inside AwaitExpressions.
|
|
84
|
+
const callExpr =
|
|
85
|
+
init.type === 'AwaitExpression' && init.argument.type === 'CallExpression'
|
|
86
|
+
? init.argument
|
|
87
|
+
: init.type === 'CallExpression'
|
|
88
|
+
? init
|
|
89
|
+
: null
|
|
90
|
+
|
|
91
|
+
if (!callExpr) return
|
|
92
|
+
|
|
93
|
+
const callee = callExpr.callee
|
|
94
|
+
|
|
95
|
+
// Handle: const { t } = useTranslation(...)
|
|
96
|
+
if (callee.type === 'Identifier') {
|
|
97
|
+
const hookConfig = this.getUseTranslationConfig(callee.value)
|
|
98
|
+
if (hookConfig) {
|
|
99
|
+
this.handleUseTranslationDeclarator(node, callExpr, hookConfig)
|
|
100
|
+
|
|
101
|
+
// ALSO store in the legacy scope for comment parsing compatibility
|
|
102
|
+
this.handleUseTranslationForComments(node, callExpr, hookConfig)
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Handle: const t = i18next.getFixedT(...)
|
|
108
|
+
if (
|
|
109
|
+
callee.type === 'MemberExpression' &&
|
|
110
|
+
callee.property.type === 'Identifier' &&
|
|
111
|
+
callee.property.value === 'getFixedT'
|
|
112
|
+
) {
|
|
113
|
+
this.handleGetFixedTDeclarator(node, callExpr)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handles useTranslation calls for comment scope resolution.
|
|
119
|
+
* This is a separate method to store scope info in the legacy scope map
|
|
120
|
+
* that the comment parser can access.
|
|
121
|
+
*
|
|
122
|
+
* @param node - Variable declarator with useTranslation call
|
|
123
|
+
* @param callExpr - The CallExpression node representing the useTranslation invocation
|
|
124
|
+
* @param hookConfig - Configuration describing argument positions for namespace and keyPrefix
|
|
125
|
+
*/
|
|
126
|
+
private getUseTranslationConfig (name: string): UseTranslationHookConfig | undefined {
|
|
127
|
+
const useTranslationNames = this.config.extract.useTranslationNames || ['useTranslation']
|
|
128
|
+
|
|
129
|
+
for (const item of useTranslationNames) {
|
|
130
|
+
if (typeof item === 'string' && item === name) {
|
|
131
|
+
// Default behavior for simple string entries
|
|
132
|
+
return { name, nsArg: 0, keyPrefixArg: 1 }
|
|
133
|
+
}
|
|
134
|
+
if (typeof item === 'object' && item.name === name) {
|
|
135
|
+
// Custom configuration with specified or default argument positions
|
|
136
|
+
return {
|
|
137
|
+
name: item.name,
|
|
138
|
+
nsArg: item.nsArg ?? 0,
|
|
139
|
+
keyPrefixArg: item.keyPrefixArg ?? 1,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Processes useTranslation hook declarations to extract scope information.
|
|
148
|
+
*
|
|
149
|
+
* Handles various destructuring patterns:
|
|
150
|
+
* - `const [t] = useTranslation('ns')` - Array destructuring
|
|
151
|
+
* - `const { t } = useTranslation('ns')` - Object destructuring
|
|
152
|
+
* - `const { t: myT } = useTranslation('ns')` - Aliased destructuring
|
|
153
|
+
*
|
|
154
|
+
* Extracts namespace from the first argument and keyPrefix from options.
|
|
155
|
+
*
|
|
156
|
+
* @param node - Variable declarator with useTranslation call
|
|
157
|
+
* @param callExpr - The CallExpression node representing the useTranslation invocation
|
|
158
|
+
* @param hookConfig - Configuration describing argument positions for namespace and keyPrefix
|
|
159
|
+
*/
|
|
160
|
+
private handleUseTranslationForComments (node: VariableDeclarator, callExpr: CallExpression, hookConfig: UseTranslationHookConfig): void {
|
|
161
|
+
let variableName: string | undefined
|
|
162
|
+
|
|
163
|
+
// Handle simple assignment: let t = useTranslation()
|
|
164
|
+
if (node.id.type === 'Identifier') {
|
|
165
|
+
variableName = node.id.value
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Handle array destructuring: const [t, i18n] = useTranslation()
|
|
169
|
+
if (node.id.type === 'ArrayPattern') {
|
|
170
|
+
const firstElement = node.id.elements[0]
|
|
171
|
+
if (firstElement?.type === 'Identifier') {
|
|
172
|
+
variableName = firstElement.value
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Handle object destructuring: const { t } or { t: t1 } = useTranslation()
|
|
177
|
+
if (node.id.type === 'ObjectPattern') {
|
|
178
|
+
for (const prop of node.id.properties) {
|
|
179
|
+
if (prop.type === 'AssignmentPatternProperty' && prop.key.type === 'Identifier' && prop.key.value === 't') {
|
|
180
|
+
// This handles { t = defaultT }
|
|
181
|
+
variableName = 't'
|
|
182
|
+
break
|
|
183
|
+
}
|
|
184
|
+
if (prop.type === 'KeyValuePatternProperty' && prop.key.type === 'Identifier' && prop.key.value === 't' && prop.value.type === 'Identifier') {
|
|
185
|
+
// This handles { t: myT }
|
|
186
|
+
variableName = prop.value.value
|
|
187
|
+
break
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// If we couldn't find a `t` function being declared, exit
|
|
193
|
+
if (!variableName) return
|
|
194
|
+
|
|
195
|
+
// Extract namespace from useTranslation arguments
|
|
196
|
+
const nsArg = callExpr.arguments?.[hookConfig.nsArg]?.expression
|
|
197
|
+
const optionsArg = callExpr.arguments?.[hookConfig.keyPrefixArg]?.expression
|
|
198
|
+
|
|
199
|
+
let defaultNs: string | undefined
|
|
200
|
+
let keyPrefix: string | undefined
|
|
201
|
+
|
|
202
|
+
// Parse namespace argument
|
|
203
|
+
if (nsArg?.type === 'StringLiteral') {
|
|
204
|
+
defaultNs = nsArg.value
|
|
205
|
+
} else if (nsArg?.type === 'ArrayExpression' && nsArg.elements[0]?.expression.type === 'StringLiteral') {
|
|
206
|
+
defaultNs = nsArg.elements[0].expression.value
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Parse keyPrefix from options object
|
|
210
|
+
if (optionsArg?.type === 'ObjectExpression') {
|
|
211
|
+
const keyPrefixProp = optionsArg.properties.find(
|
|
212
|
+
prop => prop.type === 'KeyValueProperty' &&
|
|
213
|
+
prop.key.type === 'Identifier' &&
|
|
214
|
+
prop.key.value === 'keyPrefix'
|
|
215
|
+
)
|
|
216
|
+
if (keyPrefixProp?.type === 'KeyValueProperty' && keyPrefixProp.value.type === 'StringLiteral') {
|
|
217
|
+
keyPrefix = keyPrefixProp.value.value
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Store in the legacy scope map for comment parsing
|
|
222
|
+
if (defaultNs || keyPrefix) {
|
|
223
|
+
this.scope.set(variableName, { defaultNs, keyPrefix })
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Processes useTranslation hook declarations to extract scope information.
|
|
229
|
+
*
|
|
230
|
+
* Handles various destructuring patterns:
|
|
231
|
+
* - `const [t] = useTranslation('ns')` - Array destructuring
|
|
232
|
+
* - `const { t } = useTranslation('ns')` - Object destructuring
|
|
233
|
+
* - `const { t: myT } = useTranslation('ns')` - Aliased destructuring
|
|
234
|
+
*
|
|
235
|
+
* Extracts namespace from the first argument and keyPrefix from options.
|
|
236
|
+
*
|
|
237
|
+
* @param node - Variable declarator with useTranslation call
|
|
238
|
+
* @param callExpr - The CallExpression node representing the useTranslation invocation
|
|
239
|
+
* @param hookConfig - Configuration describing argument positions for namespace and keyPrefix
|
|
240
|
+
*/
|
|
241
|
+
private handleUseTranslationDeclarator (node: VariableDeclarator, callExpr: CallExpression, hookConfig: UseTranslationHookConfig): void {
|
|
242
|
+
let variableName: string | undefined
|
|
243
|
+
|
|
244
|
+
// Handle simple assignment: let t = useTranslation()
|
|
245
|
+
if (node.id.type === 'Identifier') {
|
|
246
|
+
variableName = node.id.value
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Handle array destructuring: const [t, i18n] = useTranslation()
|
|
250
|
+
if (node.id.type === 'ArrayPattern') {
|
|
251
|
+
const firstElement = node.id.elements[0]
|
|
252
|
+
if (firstElement?.type === 'Identifier') {
|
|
253
|
+
variableName = firstElement.value
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Handle object destructuring: const { t } or { t: t1 } = useTranslation()
|
|
258
|
+
if (node.id.type === 'ObjectPattern') {
|
|
259
|
+
for (const prop of node.id.properties) {
|
|
260
|
+
if (prop.type === 'AssignmentPatternProperty' && prop.key.type === 'Identifier' && prop.key.value === 't') {
|
|
261
|
+
// This handles { t = defaultT }
|
|
262
|
+
variableName = 't'
|
|
263
|
+
break
|
|
264
|
+
}
|
|
265
|
+
if (prop.type === 'KeyValuePatternProperty' && prop.key.type === 'Identifier' && prop.key.value === 't' && prop.value.type === 'Identifier') {
|
|
266
|
+
// This handles { t: myT }
|
|
267
|
+
variableName = prop.value.value
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// If we couldn't find a `t` function being declared, exit
|
|
274
|
+
if (!variableName) return
|
|
275
|
+
|
|
276
|
+
// Use the configured argument indices from hookConfig
|
|
277
|
+
const nsArg = callExpr.arguments?.[hookConfig.nsArg]?.expression
|
|
278
|
+
|
|
279
|
+
let defaultNs: string | undefined
|
|
280
|
+
if (nsArg?.type === 'StringLiteral') {
|
|
281
|
+
defaultNs = nsArg.value
|
|
282
|
+
} else if (nsArg?.type === 'ArrayExpression' && nsArg.elements[0]?.expression.type === 'StringLiteral') {
|
|
283
|
+
defaultNs = nsArg.elements[0].expression.value
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const optionsArg = callExpr.arguments?.[hookConfig.keyPrefixArg]?.expression
|
|
287
|
+
let keyPrefix: string | undefined
|
|
288
|
+
if (optionsArg?.type === 'ObjectExpression') {
|
|
289
|
+
const kp = getObjectPropValue(optionsArg, 'keyPrefix')
|
|
290
|
+
keyPrefix = typeof kp === 'string' ? kp : undefined
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Store the scope info for the declared variable
|
|
294
|
+
this.setVarInScope(variableName, { defaultNs, keyPrefix })
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Processes getFixedT function declarations to extract scope information.
|
|
299
|
+
*
|
|
300
|
+
* Handles the pattern: `const t = i18next.getFixedT(lng, ns, keyPrefix)`
|
|
301
|
+
* - Ignores the first argument (language)
|
|
302
|
+
* - Extracts namespace from the second argument
|
|
303
|
+
* - Extracts key prefix from the third argument
|
|
304
|
+
*
|
|
305
|
+
* @param node - Variable declarator with getFixedT call
|
|
306
|
+
* @param callExpr - The CallExpression node representing the getFixedT invocation
|
|
307
|
+
*/
|
|
308
|
+
private handleGetFixedTDeclarator (node: VariableDeclarator, callExpr: CallExpression): void {
|
|
309
|
+
// Ensure we are assigning to a simple variable, e.g., const t = ...
|
|
310
|
+
if (node.id.type !== 'Identifier' || !node.init || node.init.type !== 'CallExpression') return
|
|
311
|
+
|
|
312
|
+
const variableName = node.id.value
|
|
313
|
+
const args = callExpr.arguments
|
|
314
|
+
|
|
315
|
+
// getFixedT(lng, ns, keyPrefix)
|
|
316
|
+
// We ignore the first argument (lng) for key extraction.
|
|
317
|
+
const nsArg = args[1]?.expression
|
|
318
|
+
const keyPrefixArg = args[2]?.expression
|
|
319
|
+
|
|
320
|
+
const defaultNs = (nsArg?.type === 'StringLiteral') ? nsArg.value : undefined
|
|
321
|
+
const keyPrefix = (keyPrefixArg?.type === 'StringLiteral') ? keyPrefixArg.value : undefined
|
|
322
|
+
|
|
323
|
+
if (defaultNs || keyPrefix) {
|
|
324
|
+
this.setVarInScope(variableName, { defaultNs, keyPrefix })
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
package/src/extractor.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { runExtractor, extract } from './extractor/core/extractor'
|
|
3
3
|
import { findKeys } from './extractor/core/key-finder'
|
|
4
4
|
import { getTranslations } from './extractor/core/translation-manager'
|
|
5
|
-
import { ASTVisitors } from './extractor/
|
|
5
|
+
import { ASTVisitors } from './extractor/core/ast-visitors'
|
|
6
6
|
import type { PluginContext } from './types'
|
|
7
7
|
|
|
8
8
|
export {
|
package/src/types.ts
CHANGED
|
@@ -421,3 +421,85 @@ export interface ScopeInfo {
|
|
|
421
421
|
/** Key prefix to prepend to all translation keys in this scope */
|
|
422
422
|
keyPrefix?: string;
|
|
423
423
|
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Configuration for useTranslation hook patterns.
|
|
427
|
+
* Defines how to extract namespace and key prefix information from hook calls.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```typescript
|
|
431
|
+
* // For: const { t } = useTranslation('common', { keyPrefix: 'user' })
|
|
432
|
+
* const config: UseTranslationHookConfig = {
|
|
433
|
+
* name: 'useTranslation',
|
|
434
|
+
* nsArg: 0, // namespace is first argument
|
|
435
|
+
* keyPrefixArg: 1 // keyPrefix is in second argument (options object)
|
|
436
|
+
* }
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
export interface UseTranslationHookConfig {
|
|
440
|
+
/** The name of the hook function (e.g., 'useTranslation', 'getT') */
|
|
441
|
+
name: string;
|
|
442
|
+
/** Zero-based index of the argument containing the namespace */
|
|
443
|
+
nsArg: number;
|
|
444
|
+
/** Zero-based index of the argument containing options with keyPrefix */
|
|
445
|
+
keyPrefixArg: number;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Optional hooks for customizing AST visitor behavior during extraction.
|
|
450
|
+
* Allows plugins and external code to extend the visitor's capabilities.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```typescript
|
|
454
|
+
* const hooks: ASTVisitorHooks = {
|
|
455
|
+
* onBeforeVisitNode: (node) => {
|
|
456
|
+
* console.log(`Visiting ${node.type}`)
|
|
457
|
+
* },
|
|
458
|
+
*
|
|
459
|
+
* resolvePossibleKeyStringValues: (expression) => {
|
|
460
|
+
* // Custom logic to extract keys from complex expressions
|
|
461
|
+
* if (isCustomKeyExpression(expression)) {
|
|
462
|
+
* return ['custom.key.1', 'custom.key.2']
|
|
463
|
+
* }
|
|
464
|
+
* return []
|
|
465
|
+
* }
|
|
466
|
+
* }
|
|
467
|
+
* ```
|
|
468
|
+
*/
|
|
469
|
+
export interface ASTVisitorHooks {
|
|
470
|
+
/**
|
|
471
|
+
* Called before visiting each AST node during traversal.
|
|
472
|
+
* Useful for logging, debugging, or pre-processing nodes.
|
|
473
|
+
*
|
|
474
|
+
* @param node - The AST node about to be visited
|
|
475
|
+
*/
|
|
476
|
+
onBeforeVisitNode?: (node: Node) => void
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Called after visiting each AST node during traversal.
|
|
480
|
+
* Useful for cleanup, post-processing, or collecting statistics.
|
|
481
|
+
*
|
|
482
|
+
* @param node - The AST node that was just visited
|
|
483
|
+
*/
|
|
484
|
+
onAfterVisitNode?: (node: Node) => void
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Custom resolver for extracting context values from expressions.
|
|
488
|
+
* Supplements the built-in expression resolution with plugin-specific logic.
|
|
489
|
+
*
|
|
490
|
+
* @param expression - The expression to extract context from
|
|
491
|
+
* @param returnEmptyStrings - Whether to include empty strings in results
|
|
492
|
+
* @returns Array of possible context string values
|
|
493
|
+
*/
|
|
494
|
+
resolvePossibleContextStringValues?: (expression: Expression, returnEmptyStrings?: boolean) => string[]
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Custom resolver for extracting translation keys from expressions.
|
|
498
|
+
* Supplements the built-in expression resolution with plugin-specific logic.
|
|
499
|
+
*
|
|
500
|
+
* @param expression - The expression to extract keys from
|
|
501
|
+
* @param returnEmptyStrings - Whether to include empty strings in results
|
|
502
|
+
* @returns Array of possible translation key values
|
|
503
|
+
*/
|
|
504
|
+
resolvePossibleKeyStringValues?: (expression: Expression, returnEmptyStrings?: boolean) => string[]
|
|
505
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Module } from '@swc/core';
|
|
2
|
+
import type { PluginContext, I18nextToolkitConfig, Logger, ASTVisitorHooks, ScopeInfo } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* AST visitor class that traverses JavaScript/TypeScript syntax trees to extract translation keys.
|
|
5
|
+
*
|
|
6
|
+
* This class implements a manual recursive walker that:
|
|
7
|
+
* - Maintains scope information for tracking useTranslation and getFixedT calls
|
|
8
|
+
* - Extracts keys from t() function calls with various argument patterns
|
|
9
|
+
* - Handles JSX Trans components with complex children serialization
|
|
10
|
+
* - Supports both string literals and selector API for type-safe keys
|
|
11
|
+
* - Processes pluralization and context variants
|
|
12
|
+
* - Manages namespace resolution from multiple sources
|
|
13
|
+
*
|
|
14
|
+
* The visitor respects configuration options for separators, function names,
|
|
15
|
+
* component names, and other extraction settings.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const visitors = new ASTVisitors(config, pluginContext, logger)
|
|
20
|
+
* visitors.visit(parsedAST)
|
|
21
|
+
*
|
|
22
|
+
* // The pluginContext will now contain all extracted keys
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class ASTVisitors {
|
|
26
|
+
private readonly pluginContext;
|
|
27
|
+
private readonly config;
|
|
28
|
+
private readonly logger;
|
|
29
|
+
private hooks;
|
|
30
|
+
get objectKeys(): Set<string>;
|
|
31
|
+
private readonly scopeManager;
|
|
32
|
+
private readonly expressionResolver;
|
|
33
|
+
private readonly callExpressionHandler;
|
|
34
|
+
private readonly jsxHandler;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new AST visitor instance.
|
|
37
|
+
*
|
|
38
|
+
* @param config - Toolkit configuration with extraction settings
|
|
39
|
+
* @param pluginContext - Context for adding discovered translation keys
|
|
40
|
+
* @param logger - Logger for warnings and debug information
|
|
41
|
+
*/
|
|
42
|
+
constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, logger: Logger, hooks?: ASTVisitorHooks);
|
|
43
|
+
/**
|
|
44
|
+
* Main entry point for AST traversal.
|
|
45
|
+
* Creates a root scope and begins the recursive walk through the syntax tree.
|
|
46
|
+
*
|
|
47
|
+
* @param node - The root module node to traverse
|
|
48
|
+
*/
|
|
49
|
+
visit(node: Module): void;
|
|
50
|
+
/**
|
|
51
|
+
* Recursively walks through AST nodes, handling scoping and visiting logic.
|
|
52
|
+
*
|
|
53
|
+
* This is the core traversal method that:
|
|
54
|
+
* 1. Manages function scopes (enter/exit)
|
|
55
|
+
* 2. Dispatches to specific handlers based on node type
|
|
56
|
+
* 3. Recursively processes child nodes
|
|
57
|
+
* 4. Maintains proper scope cleanup
|
|
58
|
+
*
|
|
59
|
+
* @param node - The current AST node to process
|
|
60
|
+
*
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
private walk;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieves variable information from the scope chain.
|
|
66
|
+
* Searches from innermost to outermost scope.
|
|
67
|
+
*
|
|
68
|
+
* @param name - Variable name to look up
|
|
69
|
+
* @returns Scope information if found, undefined otherwise
|
|
70
|
+
*
|
|
71
|
+
* @private
|
|
72
|
+
*/
|
|
73
|
+
getVarFromScope(name: string): ScopeInfo | undefined;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=ast-visitors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAQ,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAM1G;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,KAAK,CAAiB;IAE9B,IAAW,UAAU,gBAEpB;IAED,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe;IAkBzB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IA2DZ;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;CAG7D"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger, I18nextToolkitConfig, Plugin, PluginContext } from '../../types';
|
|
2
|
-
import { ASTVisitors } from '
|
|
2
|
+
import { ASTVisitors } from './ast-visitors';
|
|
3
3
|
/**
|
|
4
4
|
* Main extractor function that runs the complete key extraction and file generation process.
|
|
5
5
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKtF,OAAO,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,EACE,WAAmB,EACnB,QAAgB,EACjB,GAAE;IACD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACf,EACN,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAyDlB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CAoCf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,sDAO1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key-finder.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/key-finder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"key-finder.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/key-finder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAmB,MAAM,aAAa,CAAA;AAM9F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAgE1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"translation-manager.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/translation-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AA6RnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAC/B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,EAAE,CAAC,CA8E9B"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './core/extractor';
|
|
2
2
|
export * from './core/key-finder';
|
|
3
3
|
export * from './core/translation-manager';
|
|
4
|
-
export * from './
|
|
4
|
+
export * from './core/ast-visitors';
|
|
5
5
|
export * from './parsers/comment-parser';
|
|
6
6
|
export * from './parsers/jsx-parser';
|
|
7
7
|
export * from './plugin-manager';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractor/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/extractor/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { CallExpression } from '@swc/core';
|
|
2
|
+
import type { PluginContext, I18nextToolkitConfig, Logger, ScopeInfo } from '../../types';
|
|
3
|
+
import { ExpressionResolver } from './expression-resolver';
|
|
4
|
+
export declare class CallExpressionHandler {
|
|
5
|
+
private pluginContext;
|
|
6
|
+
private config;
|
|
7
|
+
private logger;
|
|
8
|
+
private expressionResolver;
|
|
9
|
+
objectKeys: Set<string>;
|
|
10
|
+
constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, logger: Logger, expressionResolver: ExpressionResolver);
|
|
11
|
+
/**
|
|
12
|
+
* Processes function call expressions to extract translation keys.
|
|
13
|
+
*
|
|
14
|
+
* This is the core extraction method that handles:
|
|
15
|
+
* - Standard t() calls with string literals
|
|
16
|
+
* - Selector API calls with arrow functions: `t($ => $.path.to.key)`
|
|
17
|
+
* - Namespace resolution from multiple sources
|
|
18
|
+
* - Default value extraction from various argument patterns
|
|
19
|
+
* - Pluralization and context handling
|
|
20
|
+
* - Key prefix application from scope
|
|
21
|
+
*
|
|
22
|
+
* @param node - Call expression node to process
|
|
23
|
+
* @param getScopeInfo - Function to retrieve scope information for variables
|
|
24
|
+
*/
|
|
25
|
+
handleCallExpression(node: CallExpression, getScopeInfo: (name: string) => ScopeInfo | undefined): void;
|
|
26
|
+
/**
|
|
27
|
+
* Processed a call expression to extract keys from the specified argument.
|
|
28
|
+
*
|
|
29
|
+
* @param node - The call expression node
|
|
30
|
+
* @param argIndex - The index of the argument to process
|
|
31
|
+
* @returns An object containing the keys to process and a flag indicating if the selector API is used
|
|
32
|
+
*/
|
|
33
|
+
private handleCallExpressionArgument;
|
|
34
|
+
/**
|
|
35
|
+
* Extracts translation key from selector API arrow function.
|
|
36
|
+
*
|
|
37
|
+
* Processes selector expressions like:
|
|
38
|
+
* - `$ => $.path.to.key` → 'path.to.key'
|
|
39
|
+
* - `$ => $.app['title'].main` → 'app.title.main'
|
|
40
|
+
* - `$ => { return $.nested.key; }` → 'nested.key'
|
|
41
|
+
*
|
|
42
|
+
* Handles both dot notation and bracket notation, respecting
|
|
43
|
+
* the configured key separator or flat key structure.
|
|
44
|
+
*
|
|
45
|
+
* @param node - Arrow function expression from selector call
|
|
46
|
+
* @returns Extracted key path or null if not statically analyzable
|
|
47
|
+
*/
|
|
48
|
+
private extractKeyFromSelector;
|
|
49
|
+
/**
|
|
50
|
+
* Generates plural form keys based on the primary language's plural rules.
|
|
51
|
+
*
|
|
52
|
+
* Uses Intl.PluralRules to determine the correct plural categories
|
|
53
|
+
* for the configured primary language and generates suffixed keys
|
|
54
|
+
* for each category (e.g., 'item_one', 'item_other').
|
|
55
|
+
*
|
|
56
|
+
* @param key - Base key name for pluralization
|
|
57
|
+
* @param ns - Namespace for the keys
|
|
58
|
+
* @param options - object expression options
|
|
59
|
+
* @param isOrdinal - isOrdinal flag
|
|
60
|
+
*/
|
|
61
|
+
private handlePluralKeys;
|
|
62
|
+
/**
|
|
63
|
+
* Serializes a callee node (Identifier or MemberExpression) into a string.
|
|
64
|
+
*
|
|
65
|
+
* Produces a dotted name for simple callees that can be used for scope lookups
|
|
66
|
+
* or configuration matching.
|
|
67
|
+
*
|
|
68
|
+
* @param callee - The CallExpression callee node to serialize
|
|
69
|
+
* @returns A dotted string name for supported callees, or null when the callee
|
|
70
|
+
* is a computed/unsupported expression.
|
|
71
|
+
*/
|
|
72
|
+
private getFunctionName;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=call-expression-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-expression-handler.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/call-expression-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA6C,MAAM,WAAW,CAAA;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,SAAS,EAAE,MAAM,aAAa,CAAA;AACvG,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAG1D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,kBAAkB,CAAoB;IACvC,UAAU,cAAoB;gBAGnC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,kBAAkB;IAQxC;;;;;;;;;;;;;OAaG;IACH,oBAAoB,CAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,GAAG,IAAI;IAqMxG;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IA+HxB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|