i18next-cli 1.49.0 → 1.49.1

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/cli.js CHANGED
@@ -31,7 +31,7 @@ const program = new commander.Command();
31
31
  program
32
32
  .name('i18next-cli')
33
33
  .description('A unified, high-performance i18next CLI.')
34
- .version('1.49.0'); // This string is replaced with the actual version at build time by rollup
34
+ .version('1.49.1'); // This string is replaced with the actual version at build time by rollup
35
35
  // new: global config override option
36
36
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
37
37
  program
@@ -301,6 +301,18 @@ class ASTVisitors {
301
301
  }
302
302
  this.hooks.onAfterVisitNode?.(node);
303
303
  // --- END VISIT LOGIC ---
304
+ // Detect array iteration calls (.map / .forEach / .flatMap etc.) on a known
305
+ // as-const array so the callback parameter is bound to the array values while
306
+ // the callback body is walked. We inject the binding BEFORE generic recursion
307
+ // and remove it AFTER, so the whole subtree sees the correct value.
308
+ let arrayCallbackCleanup;
309
+ if (node.type === 'CallExpression') {
310
+ const info = this.tryGetArrayIterationCallbackInfo(node);
311
+ if (info) {
312
+ this.expressionResolver.setTemporaryVariable(info.paramName, info.values);
313
+ arrayCallbackCleanup = () => this.expressionResolver.deleteTemporaryVariable(info.paramName);
314
+ }
315
+ }
304
316
  // --- RECURSION ---
305
317
  // Recurse into the children of the current node
306
318
  for (const key in node) {
@@ -308,49 +320,112 @@ class ASTVisitors {
308
320
  continue;
309
321
  const child = node[key];
310
322
  if (Array.isArray(child)) {
311
- // Pre-scan array children to register VariableDeclarator-based scopes
312
- // (e.g., `const { t } = useTranslation(...)`) before walking the rest
313
- // of the items. This ensures that functions/arrow-functions defined
314
- // earlier in the same block that reference t will resolve to the
315
- // correct scope even if the `useTranslation` declarator appears later.
323
+ // Pre-scan array children in THREE passes:
324
+ // Pass 1 variables WITH init (arrays, objects, strings, fns) + enums
325
+ // Pass 2 type aliases + functions (may depend on pass-1 arrays)
326
+ // Pass 3 `declare const x: Type` (no init; depends on pass-2 type aliases)
327
+ // This ordering ensures e.g.:
328
+ // const OPTS = ['a','b'] as const → pass 1
329
+ // type T = (typeof OPTS)[number] → pass 2 (resolves OPTS)
330
+ // declare const v: T → pass 3 (resolves T)
331
+ // ── Pass 1: variables with init ──────────────────────────────────────
316
332
  for (const item of child) {
317
333
  if (!item || typeof item !== 'object')
318
334
  continue;
319
- // Direct declarator present in arrays (rare)
320
- if (item.type === 'VariableDeclarator') {
335
+ // Direct declarator (rare)
336
+ if (item.type === 'VariableDeclarator' && item.init) {
321
337
  this.scopeManager.handleVariableDeclarator(item);
322
338
  this.expressionResolver.captureVariableDeclarator(item);
323
339
  continue;
324
340
  }
325
- // enum declarations can appear as ExportDeclaration.declaration earlier; be permissive
326
- if (item && item.id && Array.isArray(item.members)) {
341
+ // enum declarations
342
+ if (item.id && Array.isArray(item.members)) {
327
343
  this.expressionResolver.captureEnumDeclaration(item);
328
- // continue to allow further traversal
329
344
  }
330
- // pre-scan type alias declarations and function declarations
345
+ // Bare VariableDeclaration only declarators that have an init
346
+ if (item.type === 'VariableDeclaration' && Array.isArray(item.declarations)) {
347
+ for (const decl of item.declarations) {
348
+ if (decl?.type === 'VariableDeclarator' && decl.init) {
349
+ this.scopeManager.handleVariableDeclarator(decl);
350
+ this.expressionResolver.captureVariableDeclarator(decl);
351
+ }
352
+ }
353
+ }
354
+ // ExportDeclaration wrapping VariableDeclaration — only inited declarators
355
+ if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
356
+ const inner = item.declaration;
357
+ if (inner.type === 'VariableDeclaration' && Array.isArray(inner.declarations)) {
358
+ for (const vd of inner.declarations) {
359
+ if (vd?.type === 'VariableDeclarator' && vd.init) {
360
+ this.scopeManager.handleVariableDeclarator(vd);
361
+ this.expressionResolver.captureVariableDeclarator(vd);
362
+ }
363
+ }
364
+ }
365
+ }
366
+ }
367
+ // ── Pass 2: type aliases + functions ─────────────────────────────────
368
+ for (const item of child) {
369
+ if (!item || typeof item !== 'object')
370
+ continue;
331
371
  if (item.type === 'TsTypeAliasDeclaration' || item.type === 'TSTypeAliasDeclaration' || item.type === 'TsTypeAliasDecl') {
332
372
  this.expressionResolver.captureTypeAliasDeclaration(item);
333
373
  }
334
374
  if (item.type === 'FunctionDeclaration' || item.type === 'FnDecl') {
335
375
  this.expressionResolver.captureFunctionDeclaration(item);
336
376
  }
337
- // Also handle ExportDeclaration wrapping either of the above
338
377
  if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
339
- const decl = item.declaration;
340
- if (decl.type === 'TsTypeAliasDeclaration' || decl.type === 'TSTypeAliasDeclaration' || decl.type === 'TsTypeAliasDecl') {
341
- this.expressionResolver.captureTypeAliasDeclaration(decl);
378
+ const inner = item.declaration;
379
+ if (inner.type === 'TsTypeAliasDeclaration' || inner.type === 'TSTypeAliasDeclaration' || inner.type === 'TsTypeAliasDecl') {
380
+ this.expressionResolver.captureTypeAliasDeclaration(inner);
342
381
  }
343
- if (decl.type === 'FunctionDeclaration' || decl.type === 'FnDecl') {
344
- this.expressionResolver.captureFunctionDeclaration(decl);
382
+ if (inner.type === 'FunctionDeclaration' || inner.type === 'FnDecl') {
383
+ this.expressionResolver.captureFunctionDeclaration(inner);
345
384
  }
346
385
  }
347
- // Common case: VariableDeclaration which contains .declarations (VariableDeclarator[])
386
+ }
387
+ // ── Pass 3: `declare const x: Type` — no init, depends on type aliases ─
388
+ // Also re-processes ArrayPattern destructuring (e.g. useState<T>) whose
389
+ // type argument resolution failed in Pass 1 because typeAliasTable was empty.
390
+ for (const item of child) {
391
+ if (!item || typeof item !== 'object')
392
+ continue;
393
+ // Direct declarator with no init
394
+ if (item.type === 'VariableDeclarator' && !item.init) {
395
+ this.scopeManager.handleVariableDeclarator(item);
396
+ this.expressionResolver.captureVariableDeclarator(item);
397
+ continue;
398
+ }
399
+ // ArrayPattern destructuring with init — re-run now that type aliases are populated
400
+ if (item.type === 'VariableDeclarator' && item.init && item.id?.type === 'ArrayPattern') {
401
+ this.expressionResolver.captureVariableDeclarator(item);
402
+ continue;
403
+ }
404
+ // VariableDeclaration — process no-init declarators and re-process ArrayPattern ones
348
405
  if (item.type === 'VariableDeclaration' && Array.isArray(item.declarations)) {
349
406
  for (const decl of item.declarations) {
350
- if (decl && typeof decl === 'object' && decl.type === 'VariableDeclarator') {
407
+ if (!decl.init) {
351
408
  this.scopeManager.handleVariableDeclarator(decl);
352
409
  this.expressionResolver.captureVariableDeclarator(decl);
353
410
  }
411
+ else if (decl.id?.type === 'ArrayPattern') {
412
+ this.expressionResolver.captureVariableDeclarator(decl);
413
+ }
414
+ }
415
+ }
416
+ // ExportDeclaration wrapping — same logic
417
+ if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
418
+ const inner = item.declaration;
419
+ if (inner.type === 'VariableDeclaration' && Array.isArray(inner.declarations)) {
420
+ for (const vd of inner.declarations) {
421
+ if (!vd.init) {
422
+ this.scopeManager.handleVariableDeclarator(vd);
423
+ this.expressionResolver.captureVariableDeclarator(vd);
424
+ }
425
+ else if (vd.id?.type === 'ArrayPattern') {
426
+ this.expressionResolver.captureVariableDeclarator(vd);
427
+ }
428
+ }
354
429
  }
355
430
  }
356
431
  }
@@ -370,11 +445,60 @@ class ASTVisitors {
370
445
  }
371
446
  }
372
447
  // --- END RECURSION ---
448
+ // Remove temporary callback param binding if one was injected for this node
449
+ arrayCallbackCleanup?.();
373
450
  // LEAVE SCOPE for functions
374
451
  if (isNewScope) {
375
452
  this.scopeManager.exitScope();
376
453
  }
377
454
  }
455
+ /**
456
+ * If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
457
+ * string-array constant, returns the callback's first parameter name and the
458
+ * array values so the caller can inject a temporary variable binding.
459
+ */
460
+ tryGetArrayIterationCallbackInfo(node) {
461
+ try {
462
+ const callee = node.callee;
463
+ if (callee?.type !== 'MemberExpression')
464
+ return undefined;
465
+ const prop = callee.property;
466
+ if (prop?.type !== 'Identifier')
467
+ return undefined;
468
+ if (!['map', 'forEach', 'flatMap', 'filter', 'find', 'some', 'every'].includes(prop.value))
469
+ return undefined;
470
+ // The object must be an identifier whose value is a known string array
471
+ const obj = callee.object;
472
+ if (obj?.type !== 'Identifier')
473
+ return undefined;
474
+ const values = this.expressionResolver.getVariableValues(obj.value);
475
+ if (!values || values.length === 0)
476
+ return undefined;
477
+ // First argument must be a callback with at least one parameter
478
+ const callbackArg = node.arguments?.[0]?.expression;
479
+ if (!callbackArg)
480
+ return undefined;
481
+ // Normalise param across SWC shapes: ArrowFunctionExpression / FunctionExpression
482
+ const params = callbackArg.params ?? callbackArg.parameters ?? [];
483
+ const firstParam = params[0];
484
+ if (!firstParam)
485
+ return undefined;
486
+ // SWC wraps params in `Param { pat: Identifier }` or exposes them directly
487
+ const ident = firstParam.type === 'Identifier'
488
+ ? firstParam
489
+ : firstParam.type === 'Param' && firstParam.pat?.type === 'Identifier'
490
+ ? firstParam.pat
491
+ : firstParam.type === 'AssignmentPattern' && firstParam.left?.type === 'Identifier'
492
+ ? firstParam.left
493
+ : null;
494
+ if (!ident)
495
+ return undefined;
496
+ return { paramName: ident.value, values };
497
+ }
498
+ catch {
499
+ return undefined;
500
+ }
501
+ }
378
502
  /**
379
503
  * Retrieves variable information from the scope chain.
380
504
  * Searches from innermost to outermost scope.
@@ -12,6 +12,15 @@ class ExpressionResolver {
12
12
  // Per-file table for type aliases: Maps typeName -> string[]
13
13
  // e.g. `type ChangeType = 'all' | 'next' | 'this'` -> { ChangeType: ['all', 'next', 'this'] }
14
14
  typeAliasTable = new Map();
15
+ // Shared (cross-file) table for string-array constants (e.g. `as const` arrays).
16
+ // Persists across resetFileSymbols() so exported arrays are visible to importers.
17
+ sharedVariableTable = new Map();
18
+ // Shared (cross-file) table for type aliases — populated alongside typeAliasTable.
19
+ // Persists across resetFileSymbols() so exported type aliases are visible to importers.
20
+ sharedTypeAliasTable = new Map();
21
+ // Temporary per-scope variable overrides, used to inject .map() / .forEach()
22
+ // callback parameters while the callback body is being walked.
23
+ temporaryVariables = new Map();
15
24
  constructor(hooks) {
16
25
  this.hooks = hooks;
17
26
  }
@@ -21,6 +30,7 @@ class ExpressionResolver {
21
30
  resetFileSymbols() {
22
31
  this.variableTable.clear();
23
32
  this.typeAliasTable.clear();
33
+ this.temporaryVariables.clear();
24
34
  }
25
35
  /**
26
36
  * Capture a VariableDeclarator node to record simple statically analyzable
@@ -36,6 +46,41 @@ class ExpressionResolver {
36
46
  try {
37
47
  if (!node || !node.id)
38
48
  return;
49
+ // ── ArrayPattern id: `const [x, y] = fn<T>(...)` ────────────────────────
50
+ // Handles `const [state] = useState<'a'|'b'>('a')` or similar generic calls
51
+ // where the type argument is a finite string-literal union.
52
+ if (node.id.type === 'ArrayPattern' && node.init) {
53
+ const init = node.init;
54
+ // Unwrap await / as-expressions
55
+ let callExpr = init;
56
+ while (callExpr?.type === 'AwaitExpression')
57
+ callExpr = callExpr.argument;
58
+ while (callExpr?.type === 'TsConstAssertion' ||
59
+ callExpr?.type === 'TsAsExpression' ||
60
+ callExpr?.type === 'TsSatisfiesExpression')
61
+ callExpr = callExpr.expression;
62
+ if (callExpr?.type === 'CallExpression') {
63
+ const typeArgs = callExpr.typeArguments?.params ??
64
+ callExpr.typeParameters?.params ??
65
+ [];
66
+ if (typeArgs.length > 0) {
67
+ const vals = this.resolvePossibleStringValuesFromType(typeArgs[0]);
68
+ if (vals.length > 0) {
69
+ // Bind each array-pattern element: first element is the state variable
70
+ for (const el of node.id.elements) {
71
+ if (!el)
72
+ continue;
73
+ const ident = el.type === 'Identifier' ? el : (el.type === 'AssignmentPattern' && el.left?.type === 'Identifier' ? el.left : null);
74
+ if (ident) {
75
+ this.variableTable.set(ident.value, vals);
76
+ }
77
+ break; // only bind the first element (the state value, not the setter)
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return;
83
+ }
39
84
  // only handle simple identifier bindings like `const x = ...`
40
85
  if (node.id.type !== 'Identifier')
41
86
  return;
@@ -86,6 +131,24 @@ class ExpressionResolver {
86
131
  return;
87
132
  }
88
133
  }
134
+ // ArrayExpression -> list of string values
135
+ // Handles `const OPTS = ['a', 'b', 'c'] as const`
136
+ if (unwrappedInit.type === 'ArrayExpression' && Array.isArray(unwrappedInit.elements)) {
137
+ const vals = [];
138
+ for (const elem of unwrappedInit.elements) {
139
+ if (!elem || !elem.expression)
140
+ continue;
141
+ const resolved = this.resolvePossibleStringValuesFromExpression(elem.expression);
142
+ if (resolved.length === 1)
143
+ vals.push(resolved[0]);
144
+ }
145
+ if (vals.length > 0) {
146
+ this.variableTable.set(name, vals);
147
+ // Also share so importing files can see this array
148
+ this.sharedVariableTable.set(name, vals);
149
+ return;
150
+ }
151
+ }
89
152
  // For other initializers, try to resolve to one-or-more strings
90
153
  const vals = this.resolvePossibleStringValuesFromExpression(init);
91
154
  if (vals.length > 0) {
@@ -129,6 +192,8 @@ class ExpressionResolver {
129
192
  const vals = this.resolvePossibleStringValuesFromType(tsType);
130
193
  if (vals.length > 0) {
131
194
  this.typeAliasTable.set(name, vals);
195
+ // Also share so importing files can resolve this alias by name
196
+ this.sharedTypeAliasTable.set(name, vals);
132
197
  }
133
198
  }
134
199
  catch {
@@ -180,6 +245,33 @@ class ExpressionResolver {
180
245
  return raw.typeAnnotation;
181
246
  return raw;
182
247
  }
248
+ /**
249
+ * Temporarily bind a variable name to a set of string values.
250
+ * Used by ast-visitors to inject .map()/.forEach() callback parameters.
251
+ * Call deleteTemporaryVariable() after walking the callback body.
252
+ */
253
+ setTemporaryVariable(name, values) {
254
+ this.temporaryVariables.set(name, values);
255
+ }
256
+ /**
257
+ * Remove a previously-injected temporary variable binding.
258
+ */
259
+ deleteTemporaryVariable(name) {
260
+ this.temporaryVariables.delete(name);
261
+ }
262
+ /**
263
+ * Return the array values stored for a variable name, checking all tables.
264
+ * Returns undefined if the name is not a known string array.
265
+ */
266
+ getVariableValues(name) {
267
+ const tmp = this.temporaryVariables.get(name);
268
+ if (tmp)
269
+ return tmp;
270
+ const v = this.variableTable.get(name);
271
+ if (Array.isArray(v))
272
+ return v;
273
+ return this.sharedVariableTable.get(name);
274
+ }
183
275
  /**
184
276
  * Capture a TypeScript enum declaration so members can be resolved later.
185
277
  * Accepts SWC node shapes like `TsEnumDeclaration` / `TSEnumDeclaration`.
@@ -421,9 +513,18 @@ class ExpressionResolver {
421
513
  }
422
514
  // Identifier resolution via captured per-file variable table only
423
515
  if (expression.type === 'Identifier') {
516
+ // Check temporary (callback param) overrides first
517
+ const tmp = this.temporaryVariables.get(expression.value);
518
+ if (tmp)
519
+ return tmp;
424
520
  const v = this.variableTable.get(expression.value);
425
- if (!v)
521
+ if (!v) {
522
+ // Fall back to shared cross-file array table
523
+ const sv = this.sharedVariableTable.get(expression.value);
524
+ if (sv)
525
+ return sv;
426
526
  return [];
527
+ }
427
528
  if (Array.isArray(v))
428
529
  return v;
429
530
  // object map - cannot be used directly as key, so return empty
@@ -433,6 +534,11 @@ class ExpressionResolver {
433
534
  return [];
434
535
  }
435
536
  resolvePossibleStringValuesFromType(type, returnEmptyStrings = false) {
537
+ // Unwrap TsParenthesizedType — SWC explicitly emits these for grouped types like
538
+ // `(typeof X)[number]` where `(typeof X)` becomes TsParenthesizedType { typeAnnotation: TsTypeQuery }
539
+ if (type.type === 'TsParenthesizedType') {
540
+ return this.resolvePossibleStringValuesFromType(type.typeAnnotation, returnEmptyStrings);
541
+ }
436
542
  if (type.type === 'TsUnionType') {
437
543
  return type.types.flatMap((t) => this.resolvePossibleStringValuesFromType(t, returnEmptyStrings));
438
544
  }
@@ -456,11 +562,38 @@ class ExpressionResolver {
456
562
  ? type.typeName.value
457
563
  : undefined;
458
564
  if (typeName) {
459
- const aliasVals = this.typeAliasTable.get(typeName);
565
+ const aliasVals = this.typeAliasTable.get(typeName) ?? this.sharedTypeAliasTable.get(typeName);
460
566
  if (aliasVals && aliasVals.length > 0)
461
567
  return aliasVals;
462
568
  }
463
569
  }
570
+ // `(typeof ACCESS_OPTIONS)[number]` — resolve through the shared array variable table.
571
+ // SWC emits: TsIndexedAccessType {
572
+ // objectType: TsParenthesizedType { typeAnnotation: TsTypeQuery { exprName: Identifier } }
573
+ // indexType: TsKeywordType
574
+ // }
575
+ // The parens around `typeof X` produce a TsParenthesizedType wrapper that we must unwrap.
576
+ if (type.type === 'TsIndexedAccessType') {
577
+ try {
578
+ let objType = type.objectType;
579
+ // Unwrap TsParenthesizedType wrapper (SWC preserves explicit parens in type positions)
580
+ while (objType?.type === 'TsParenthesizedType') {
581
+ objType = objType.typeAnnotation;
582
+ }
583
+ if (objType?.type === 'TsTypeQuery' || objType?.type === 'TSTypeQuery') {
584
+ // SWC: TsTypeQuery.exprName is TsEntityName (Identifier | TsQualifiedName)
585
+ const exprName = objType.exprName ?? objType.expr ?? objType.entityName;
586
+ // access .value (Identifier) or fall back to .name for alternate SWC builds
587
+ const varName = exprName?.value ?? exprName?.name;
588
+ if (varName) {
589
+ const vals = this.getVariableValues(varName);
590
+ if (vals && vals.length > 0)
591
+ return vals;
592
+ }
593
+ }
594
+ }
595
+ catch { }
596
+ }
464
597
  // We can't statically determine the value of other expressions (e.g., variables, function calls)
465
598
  return [];
466
599
  }
package/dist/esm/cli.js CHANGED
@@ -29,7 +29,7 @@ const program = new Command();
29
29
  program
30
30
  .name('i18next-cli')
31
31
  .description('A unified, high-performance i18next CLI.')
32
- .version('1.49.0'); // This string is replaced with the actual version at build time by rollup
32
+ .version('1.49.1'); // This string is replaced with the actual version at build time by rollup
33
33
  // new: global config override option
34
34
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
35
35
  program
@@ -299,6 +299,18 @@ class ASTVisitors {
299
299
  }
300
300
  this.hooks.onAfterVisitNode?.(node);
301
301
  // --- END VISIT LOGIC ---
302
+ // Detect array iteration calls (.map / .forEach / .flatMap etc.) on a known
303
+ // as-const array so the callback parameter is bound to the array values while
304
+ // the callback body is walked. We inject the binding BEFORE generic recursion
305
+ // and remove it AFTER, so the whole subtree sees the correct value.
306
+ let arrayCallbackCleanup;
307
+ if (node.type === 'CallExpression') {
308
+ const info = this.tryGetArrayIterationCallbackInfo(node);
309
+ if (info) {
310
+ this.expressionResolver.setTemporaryVariable(info.paramName, info.values);
311
+ arrayCallbackCleanup = () => this.expressionResolver.deleteTemporaryVariable(info.paramName);
312
+ }
313
+ }
302
314
  // --- RECURSION ---
303
315
  // Recurse into the children of the current node
304
316
  for (const key in node) {
@@ -306,49 +318,112 @@ class ASTVisitors {
306
318
  continue;
307
319
  const child = node[key];
308
320
  if (Array.isArray(child)) {
309
- // Pre-scan array children to register VariableDeclarator-based scopes
310
- // (e.g., `const { t } = useTranslation(...)`) before walking the rest
311
- // of the items. This ensures that functions/arrow-functions defined
312
- // earlier in the same block that reference t will resolve to the
313
- // correct scope even if the `useTranslation` declarator appears later.
321
+ // Pre-scan array children in THREE passes:
322
+ // Pass 1 variables WITH init (arrays, objects, strings, fns) + enums
323
+ // Pass 2 type aliases + functions (may depend on pass-1 arrays)
324
+ // Pass 3 `declare const x: Type` (no init; depends on pass-2 type aliases)
325
+ // This ordering ensures e.g.:
326
+ // const OPTS = ['a','b'] as const → pass 1
327
+ // type T = (typeof OPTS)[number] → pass 2 (resolves OPTS)
328
+ // declare const v: T → pass 3 (resolves T)
329
+ // ── Pass 1: variables with init ──────────────────────────────────────
314
330
  for (const item of child) {
315
331
  if (!item || typeof item !== 'object')
316
332
  continue;
317
- // Direct declarator present in arrays (rare)
318
- if (item.type === 'VariableDeclarator') {
333
+ // Direct declarator (rare)
334
+ if (item.type === 'VariableDeclarator' && item.init) {
319
335
  this.scopeManager.handleVariableDeclarator(item);
320
336
  this.expressionResolver.captureVariableDeclarator(item);
321
337
  continue;
322
338
  }
323
- // enum declarations can appear as ExportDeclaration.declaration earlier; be permissive
324
- if (item && item.id && Array.isArray(item.members)) {
339
+ // enum declarations
340
+ if (item.id && Array.isArray(item.members)) {
325
341
  this.expressionResolver.captureEnumDeclaration(item);
326
- // continue to allow further traversal
327
342
  }
328
- // pre-scan type alias declarations and function declarations
343
+ // Bare VariableDeclaration only declarators that have an init
344
+ if (item.type === 'VariableDeclaration' && Array.isArray(item.declarations)) {
345
+ for (const decl of item.declarations) {
346
+ if (decl?.type === 'VariableDeclarator' && decl.init) {
347
+ this.scopeManager.handleVariableDeclarator(decl);
348
+ this.expressionResolver.captureVariableDeclarator(decl);
349
+ }
350
+ }
351
+ }
352
+ // ExportDeclaration wrapping VariableDeclaration — only inited declarators
353
+ if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
354
+ const inner = item.declaration;
355
+ if (inner.type === 'VariableDeclaration' && Array.isArray(inner.declarations)) {
356
+ for (const vd of inner.declarations) {
357
+ if (vd?.type === 'VariableDeclarator' && vd.init) {
358
+ this.scopeManager.handleVariableDeclarator(vd);
359
+ this.expressionResolver.captureVariableDeclarator(vd);
360
+ }
361
+ }
362
+ }
363
+ }
364
+ }
365
+ // ── Pass 2: type aliases + functions ─────────────────────────────────
366
+ for (const item of child) {
367
+ if (!item || typeof item !== 'object')
368
+ continue;
329
369
  if (item.type === 'TsTypeAliasDeclaration' || item.type === 'TSTypeAliasDeclaration' || item.type === 'TsTypeAliasDecl') {
330
370
  this.expressionResolver.captureTypeAliasDeclaration(item);
331
371
  }
332
372
  if (item.type === 'FunctionDeclaration' || item.type === 'FnDecl') {
333
373
  this.expressionResolver.captureFunctionDeclaration(item);
334
374
  }
335
- // Also handle ExportDeclaration wrapping either of the above
336
375
  if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
337
- const decl = item.declaration;
338
- if (decl.type === 'TsTypeAliasDeclaration' || decl.type === 'TSTypeAliasDeclaration' || decl.type === 'TsTypeAliasDecl') {
339
- this.expressionResolver.captureTypeAliasDeclaration(decl);
376
+ const inner = item.declaration;
377
+ if (inner.type === 'TsTypeAliasDeclaration' || inner.type === 'TSTypeAliasDeclaration' || inner.type === 'TsTypeAliasDecl') {
378
+ this.expressionResolver.captureTypeAliasDeclaration(inner);
340
379
  }
341
- if (decl.type === 'FunctionDeclaration' || decl.type === 'FnDecl') {
342
- this.expressionResolver.captureFunctionDeclaration(decl);
380
+ if (inner.type === 'FunctionDeclaration' || inner.type === 'FnDecl') {
381
+ this.expressionResolver.captureFunctionDeclaration(inner);
343
382
  }
344
383
  }
345
- // Common case: VariableDeclaration which contains .declarations (VariableDeclarator[])
384
+ }
385
+ // ── Pass 3: `declare const x: Type` — no init, depends on type aliases ─
386
+ // Also re-processes ArrayPattern destructuring (e.g. useState<T>) whose
387
+ // type argument resolution failed in Pass 1 because typeAliasTable was empty.
388
+ for (const item of child) {
389
+ if (!item || typeof item !== 'object')
390
+ continue;
391
+ // Direct declarator with no init
392
+ if (item.type === 'VariableDeclarator' && !item.init) {
393
+ this.scopeManager.handleVariableDeclarator(item);
394
+ this.expressionResolver.captureVariableDeclarator(item);
395
+ continue;
396
+ }
397
+ // ArrayPattern destructuring with init — re-run now that type aliases are populated
398
+ if (item.type === 'VariableDeclarator' && item.init && item.id?.type === 'ArrayPattern') {
399
+ this.expressionResolver.captureVariableDeclarator(item);
400
+ continue;
401
+ }
402
+ // VariableDeclaration — process no-init declarators and re-process ArrayPattern ones
346
403
  if (item.type === 'VariableDeclaration' && Array.isArray(item.declarations)) {
347
404
  for (const decl of item.declarations) {
348
- if (decl && typeof decl === 'object' && decl.type === 'VariableDeclarator') {
405
+ if (!decl.init) {
349
406
  this.scopeManager.handleVariableDeclarator(decl);
350
407
  this.expressionResolver.captureVariableDeclarator(decl);
351
408
  }
409
+ else if (decl.id?.type === 'ArrayPattern') {
410
+ this.expressionResolver.captureVariableDeclarator(decl);
411
+ }
412
+ }
413
+ }
414
+ // ExportDeclaration wrapping — same logic
415
+ if ((item.type === 'ExportDeclaration' || item.type === 'ExportNamedDeclaration') && item.declaration) {
416
+ const inner = item.declaration;
417
+ if (inner.type === 'VariableDeclaration' && Array.isArray(inner.declarations)) {
418
+ for (const vd of inner.declarations) {
419
+ if (!vd.init) {
420
+ this.scopeManager.handleVariableDeclarator(vd);
421
+ this.expressionResolver.captureVariableDeclarator(vd);
422
+ }
423
+ else if (vd.id?.type === 'ArrayPattern') {
424
+ this.expressionResolver.captureVariableDeclarator(vd);
425
+ }
426
+ }
352
427
  }
353
428
  }
354
429
  }
@@ -368,11 +443,60 @@ class ASTVisitors {
368
443
  }
369
444
  }
370
445
  // --- END RECURSION ---
446
+ // Remove temporary callback param binding if one was injected for this node
447
+ arrayCallbackCleanup?.();
371
448
  // LEAVE SCOPE for functions
372
449
  if (isNewScope) {
373
450
  this.scopeManager.exitScope();
374
451
  }
375
452
  }
453
+ /**
454
+ * If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
455
+ * string-array constant, returns the callback's first parameter name and the
456
+ * array values so the caller can inject a temporary variable binding.
457
+ */
458
+ tryGetArrayIterationCallbackInfo(node) {
459
+ try {
460
+ const callee = node.callee;
461
+ if (callee?.type !== 'MemberExpression')
462
+ return undefined;
463
+ const prop = callee.property;
464
+ if (prop?.type !== 'Identifier')
465
+ return undefined;
466
+ if (!['map', 'forEach', 'flatMap', 'filter', 'find', 'some', 'every'].includes(prop.value))
467
+ return undefined;
468
+ // The object must be an identifier whose value is a known string array
469
+ const obj = callee.object;
470
+ if (obj?.type !== 'Identifier')
471
+ return undefined;
472
+ const values = this.expressionResolver.getVariableValues(obj.value);
473
+ if (!values || values.length === 0)
474
+ return undefined;
475
+ // First argument must be a callback with at least one parameter
476
+ const callbackArg = node.arguments?.[0]?.expression;
477
+ if (!callbackArg)
478
+ return undefined;
479
+ // Normalise param across SWC shapes: ArrowFunctionExpression / FunctionExpression
480
+ const params = callbackArg.params ?? callbackArg.parameters ?? [];
481
+ const firstParam = params[0];
482
+ if (!firstParam)
483
+ return undefined;
484
+ // SWC wraps params in `Param { pat: Identifier }` or exposes them directly
485
+ const ident = firstParam.type === 'Identifier'
486
+ ? firstParam
487
+ : firstParam.type === 'Param' && firstParam.pat?.type === 'Identifier'
488
+ ? firstParam.pat
489
+ : firstParam.type === 'AssignmentPattern' && firstParam.left?.type === 'Identifier'
490
+ ? firstParam.left
491
+ : null;
492
+ if (!ident)
493
+ return undefined;
494
+ return { paramName: ident.value, values };
495
+ }
496
+ catch {
497
+ return undefined;
498
+ }
499
+ }
376
500
  /**
377
501
  * Retrieves variable information from the scope chain.
378
502
  * Searches from innermost to outermost scope.
@@ -10,6 +10,15 @@ class ExpressionResolver {
10
10
  // Per-file table for type aliases: Maps typeName -> string[]
11
11
  // e.g. `type ChangeType = 'all' | 'next' | 'this'` -> { ChangeType: ['all', 'next', 'this'] }
12
12
  typeAliasTable = new Map();
13
+ // Shared (cross-file) table for string-array constants (e.g. `as const` arrays).
14
+ // Persists across resetFileSymbols() so exported arrays are visible to importers.
15
+ sharedVariableTable = new Map();
16
+ // Shared (cross-file) table for type aliases — populated alongside typeAliasTable.
17
+ // Persists across resetFileSymbols() so exported type aliases are visible to importers.
18
+ sharedTypeAliasTable = new Map();
19
+ // Temporary per-scope variable overrides, used to inject .map() / .forEach()
20
+ // callback parameters while the callback body is being walked.
21
+ temporaryVariables = new Map();
13
22
  constructor(hooks) {
14
23
  this.hooks = hooks;
15
24
  }
@@ -19,6 +28,7 @@ class ExpressionResolver {
19
28
  resetFileSymbols() {
20
29
  this.variableTable.clear();
21
30
  this.typeAliasTable.clear();
31
+ this.temporaryVariables.clear();
22
32
  }
23
33
  /**
24
34
  * Capture a VariableDeclarator node to record simple statically analyzable
@@ -34,6 +44,41 @@ class ExpressionResolver {
34
44
  try {
35
45
  if (!node || !node.id)
36
46
  return;
47
+ // ── ArrayPattern id: `const [x, y] = fn<T>(...)` ────────────────────────
48
+ // Handles `const [state] = useState<'a'|'b'>('a')` or similar generic calls
49
+ // where the type argument is a finite string-literal union.
50
+ if (node.id.type === 'ArrayPattern' && node.init) {
51
+ const init = node.init;
52
+ // Unwrap await / as-expressions
53
+ let callExpr = init;
54
+ while (callExpr?.type === 'AwaitExpression')
55
+ callExpr = callExpr.argument;
56
+ while (callExpr?.type === 'TsConstAssertion' ||
57
+ callExpr?.type === 'TsAsExpression' ||
58
+ callExpr?.type === 'TsSatisfiesExpression')
59
+ callExpr = callExpr.expression;
60
+ if (callExpr?.type === 'CallExpression') {
61
+ const typeArgs = callExpr.typeArguments?.params ??
62
+ callExpr.typeParameters?.params ??
63
+ [];
64
+ if (typeArgs.length > 0) {
65
+ const vals = this.resolvePossibleStringValuesFromType(typeArgs[0]);
66
+ if (vals.length > 0) {
67
+ // Bind each array-pattern element: first element is the state variable
68
+ for (const el of node.id.elements) {
69
+ if (!el)
70
+ continue;
71
+ const ident = el.type === 'Identifier' ? el : (el.type === 'AssignmentPattern' && el.left?.type === 'Identifier' ? el.left : null);
72
+ if (ident) {
73
+ this.variableTable.set(ident.value, vals);
74
+ }
75
+ break; // only bind the first element (the state value, not the setter)
76
+ }
77
+ }
78
+ }
79
+ }
80
+ return;
81
+ }
37
82
  // only handle simple identifier bindings like `const x = ...`
38
83
  if (node.id.type !== 'Identifier')
39
84
  return;
@@ -84,6 +129,24 @@ class ExpressionResolver {
84
129
  return;
85
130
  }
86
131
  }
132
+ // ArrayExpression -> list of string values
133
+ // Handles `const OPTS = ['a', 'b', 'c'] as const`
134
+ if (unwrappedInit.type === 'ArrayExpression' && Array.isArray(unwrappedInit.elements)) {
135
+ const vals = [];
136
+ for (const elem of unwrappedInit.elements) {
137
+ if (!elem || !elem.expression)
138
+ continue;
139
+ const resolved = this.resolvePossibleStringValuesFromExpression(elem.expression);
140
+ if (resolved.length === 1)
141
+ vals.push(resolved[0]);
142
+ }
143
+ if (vals.length > 0) {
144
+ this.variableTable.set(name, vals);
145
+ // Also share so importing files can see this array
146
+ this.sharedVariableTable.set(name, vals);
147
+ return;
148
+ }
149
+ }
87
150
  // For other initializers, try to resolve to one-or-more strings
88
151
  const vals = this.resolvePossibleStringValuesFromExpression(init);
89
152
  if (vals.length > 0) {
@@ -127,6 +190,8 @@ class ExpressionResolver {
127
190
  const vals = this.resolvePossibleStringValuesFromType(tsType);
128
191
  if (vals.length > 0) {
129
192
  this.typeAliasTable.set(name, vals);
193
+ // Also share so importing files can resolve this alias by name
194
+ this.sharedTypeAliasTable.set(name, vals);
130
195
  }
131
196
  }
132
197
  catch {
@@ -178,6 +243,33 @@ class ExpressionResolver {
178
243
  return raw.typeAnnotation;
179
244
  return raw;
180
245
  }
246
+ /**
247
+ * Temporarily bind a variable name to a set of string values.
248
+ * Used by ast-visitors to inject .map()/.forEach() callback parameters.
249
+ * Call deleteTemporaryVariable() after walking the callback body.
250
+ */
251
+ setTemporaryVariable(name, values) {
252
+ this.temporaryVariables.set(name, values);
253
+ }
254
+ /**
255
+ * Remove a previously-injected temporary variable binding.
256
+ */
257
+ deleteTemporaryVariable(name) {
258
+ this.temporaryVariables.delete(name);
259
+ }
260
+ /**
261
+ * Return the array values stored for a variable name, checking all tables.
262
+ * Returns undefined if the name is not a known string array.
263
+ */
264
+ getVariableValues(name) {
265
+ const tmp = this.temporaryVariables.get(name);
266
+ if (tmp)
267
+ return tmp;
268
+ const v = this.variableTable.get(name);
269
+ if (Array.isArray(v))
270
+ return v;
271
+ return this.sharedVariableTable.get(name);
272
+ }
181
273
  /**
182
274
  * Capture a TypeScript enum declaration so members can be resolved later.
183
275
  * Accepts SWC node shapes like `TsEnumDeclaration` / `TSEnumDeclaration`.
@@ -419,9 +511,18 @@ class ExpressionResolver {
419
511
  }
420
512
  // Identifier resolution via captured per-file variable table only
421
513
  if (expression.type === 'Identifier') {
514
+ // Check temporary (callback param) overrides first
515
+ const tmp = this.temporaryVariables.get(expression.value);
516
+ if (tmp)
517
+ return tmp;
422
518
  const v = this.variableTable.get(expression.value);
423
- if (!v)
519
+ if (!v) {
520
+ // Fall back to shared cross-file array table
521
+ const sv = this.sharedVariableTable.get(expression.value);
522
+ if (sv)
523
+ return sv;
424
524
  return [];
525
+ }
425
526
  if (Array.isArray(v))
426
527
  return v;
427
528
  // object map - cannot be used directly as key, so return empty
@@ -431,6 +532,11 @@ class ExpressionResolver {
431
532
  return [];
432
533
  }
433
534
  resolvePossibleStringValuesFromType(type, returnEmptyStrings = false) {
535
+ // Unwrap TsParenthesizedType — SWC explicitly emits these for grouped types like
536
+ // `(typeof X)[number]` where `(typeof X)` becomes TsParenthesizedType { typeAnnotation: TsTypeQuery }
537
+ if (type.type === 'TsParenthesizedType') {
538
+ return this.resolvePossibleStringValuesFromType(type.typeAnnotation, returnEmptyStrings);
539
+ }
434
540
  if (type.type === 'TsUnionType') {
435
541
  return type.types.flatMap((t) => this.resolvePossibleStringValuesFromType(t, returnEmptyStrings));
436
542
  }
@@ -454,11 +560,38 @@ class ExpressionResolver {
454
560
  ? type.typeName.value
455
561
  : undefined;
456
562
  if (typeName) {
457
- const aliasVals = this.typeAliasTable.get(typeName);
563
+ const aliasVals = this.typeAliasTable.get(typeName) ?? this.sharedTypeAliasTable.get(typeName);
458
564
  if (aliasVals && aliasVals.length > 0)
459
565
  return aliasVals;
460
566
  }
461
567
  }
568
+ // `(typeof ACCESS_OPTIONS)[number]` — resolve through the shared array variable table.
569
+ // SWC emits: TsIndexedAccessType {
570
+ // objectType: TsParenthesizedType { typeAnnotation: TsTypeQuery { exprName: Identifier } }
571
+ // indexType: TsKeywordType
572
+ // }
573
+ // The parens around `typeof X` produce a TsParenthesizedType wrapper that we must unwrap.
574
+ if (type.type === 'TsIndexedAccessType') {
575
+ try {
576
+ let objType = type.objectType;
577
+ // Unwrap TsParenthesizedType wrapper (SWC preserves explicit parens in type positions)
578
+ while (objType?.type === 'TsParenthesizedType') {
579
+ objType = objType.typeAnnotation;
580
+ }
581
+ if (objType?.type === 'TsTypeQuery' || objType?.type === 'TSTypeQuery') {
582
+ // SWC: TsTypeQuery.exprName is TsEntityName (Identifier | TsQualifiedName)
583
+ const exprName = objType.exprName ?? objType.expr ?? objType.entityName;
584
+ // access .value (Identifier) or fall back to .name for alternate SWC builds
585
+ const varName = exprName?.value ?? exprName?.name;
586
+ if (varName) {
587
+ const vals = this.getVariableValues(varName);
588
+ if (vals && vals.length > 0)
589
+ return vals;
590
+ }
591
+ }
592
+ }
593
+ catch { }
594
+ }
462
595
  // We can't statically determine the value of other expressions (e.g., variables, function calls)
463
596
  return [];
464
597
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.49.0",
3
+ "version": "1.49.1",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -65,6 +65,12 @@ export declare class ASTVisitors {
65
65
  * @private
66
66
  */
67
67
  private walk;
68
+ /**
69
+ * If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
70
+ * string-array constant, returns the callback's first parameter name and the
71
+ * array values so the caller can inject a temporary variable binding.
72
+ */
73
+ private tryGetArrayIterationCallbackInfo;
68
74
  /**
69
75
  * Retrieves variable information from the scope chain.
70
76
  * Searches from innermost to outermost scope.
@@ -1 +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;AAC1G,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAInE;;;;;;;;;;;;;;;;;;;;;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,SAAgB,YAAY,EAAE,YAAY,CAAA;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAEhC;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAiCzC;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAUjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAgRZ;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI5D;;OAEG;IACI,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxD;;;;;;OAMG;IACI,cAAc,IAAK,MAAM;IAIhC;;OAEG;IACI,cAAc,IAAK,MAAM;CAGjC"}
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;AAC1G,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAInE;;;;;;;;;;;;;;;;;;;;;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,SAAgB,YAAY,EAAE,YAAY,CAAA;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAuB;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IACvC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAa;IAEhC;;;;;;OAMG;gBAED,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,eAAe,EACvB,kBAAkB,CAAC,EAAE,kBAAkB;IAiCzC;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAUjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAgWZ;;;;OAIG;IACH,OAAO,CAAC,gCAAgC;IAwCxC;;;;;;;;OAQG;IACI,eAAe,CAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAI5D;;OAEG;IACI,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxD;;;;;;OAMG;IACI,cAAc,IAAK,MAAM;IAIhC;;OAEG;IACI,cAAc,IAAK,MAAM;CAGjC"}
@@ -5,6 +5,9 @@ export declare class ExpressionResolver {
5
5
  private variableTable;
6
6
  private sharedEnumTable;
7
7
  private typeAliasTable;
8
+ private sharedVariableTable;
9
+ private sharedTypeAliasTable;
10
+ private temporaryVariables;
8
11
  constructor(hooks: ASTVisitorHooks);
9
12
  /**
10
13
  * Clear per-file captured variables. Enums / shared maps are kept.
@@ -46,6 +49,21 @@ export declare class ExpressionResolver {
46
49
  * SWC may wrap it in a `TsTypeAnnotation` node — this unwraps it.
47
50
  */
48
51
  private extractTypeAnnotation;
52
+ /**
53
+ * Temporarily bind a variable name to a set of string values.
54
+ * Used by ast-visitors to inject .map()/.forEach() callback parameters.
55
+ * Call deleteTemporaryVariable() after walking the callback body.
56
+ */
57
+ setTemporaryVariable(name: string, values: string[]): void;
58
+ /**
59
+ * Remove a previously-injected temporary variable binding.
60
+ */
61
+ deleteTemporaryVariable(name: string): void;
62
+ /**
63
+ * Return the array values stored for a variable name, checking all tables.
64
+ * Returns undefined if the name is not a known string array.
65
+ */
66
+ getVariableValues(name: string): string[] | undefined;
49
67
  /**
50
68
  * Capture a TypeScript enum declaration so members can be resolved later.
51
69
  * Accepts SWC node shapes like `TsEnumDeclaration` / `TSEnumDeclaration`.
@@ -1 +1 @@
1
- {"version":3,"file":"expression-resolver.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/expression-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,WAAW,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAiB;IAK9B,OAAO,CAAC,aAAa,CAA4D;IAGjF,OAAO,CAAC,eAAe,CAAiD;IAIxE,OAAO,CAAC,cAAc,CAAmC;gBAE5C,KAAK,EAAE,eAAe;IAInC;;OAEG;IACI,gBAAgB,IAAK,IAAI;IAKhC;;;;;;;;;OASG;IACH,yBAAyB,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAiF3C;;;;;;;OAOG;IACH,2BAA2B,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAgB7C;;;;;;;;;OASG;IACH,0BAA0B,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAoB5C;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;OAKG;IACH,sBAAsB,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAwBxC;;;;;;;OAOG;IACH,kCAAkC,CAAE,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE;IAKrE;;;;;;;OAOG;IACH,8BAA8B,CAAE,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE;IAKjE;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,yCAAyC;IAwLjD,OAAO,CAAC,mCAAmC;IAsC3C;;;;;;OAMG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;OAMG;IACH,OAAO,CAAC,kDAAkD;CAwB3D"}
1
+ {"version":3,"file":"expression-resolver.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/expression-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAkD,MAAM,WAAW,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAiB;IAK9B,OAAO,CAAC,aAAa,CAA4D;IAGjF,OAAO,CAAC,eAAe,CAAiD;IAIxE,OAAO,CAAC,cAAc,CAAmC;IAIzD,OAAO,CAAC,mBAAmB,CAAmC;IAI9D,OAAO,CAAC,oBAAoB,CAAmC;IAI/D,OAAO,CAAC,kBAAkB,CAAmC;gBAEhD,KAAK,EAAE,eAAe;IAInC;;OAEG;IACI,gBAAgB,IAAK,IAAI;IAMhC;;;;;;;;;OASG;IACH,yBAAyB,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAwI3C;;;;;;;OAOG;IACH,2BAA2B,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAkB7C;;;;;;;;;OASG;IACH,0BAA0B,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAoB5C;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;OAIG;IACI,oBAAoB,CAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IAIlE;;OAEG;IACI,uBAAuB,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAInD;;;OAGG;IACI,iBAAiB,CAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAQ7D;;;;;OAKG;IACH,sBAAsB,CAAE,IAAI,EAAE,GAAG,GAAG,IAAI;IAwBxC;;;;;;;OAOG;IACH,kCAAkC,CAAE,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE;IAKrE;;;;;;;OAOG;IACH,8BAA8B,CAAE,UAAU,EAAE,UAAU,GAAG,MAAM,EAAE;IAKjE;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,yCAAyC;IAgMjD,OAAO,CAAC,mCAAmC;IAsE3C;;;;;;OAMG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;OAMG;IACH,OAAO,CAAC,kDAAkD;CAwB3D"}