i18next-cli 1.49.3 → 1.49.5
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 +1 -1
- package/dist/cjs/extractor/core/ast-visitors.js +119 -25
- package/dist/cjs/extractor/core/extractor.js +75 -1
- package/dist/cjs/extractor/core/key-finder.js +10 -1
- package/dist/cjs/extractor/parsers/expression-resolver.js +59 -0
- package/dist/cjs/types-generator.js +3 -3
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/core/ast-visitors.js +119 -25
- package/dist/esm/extractor/core/extractor.js +75 -2
- package/dist/esm/extractor/core/key-finder.js +11 -2
- package/dist/esm/extractor/parsers/expression-resolver.js +59 -0
- package/dist/esm/types-generator.js +3 -3
- package/package.json +31 -31
- package/types/extractor/core/ast-visitors.d.ts +29 -0
- package/types/extractor/core/ast-visitors.d.ts.map +1 -1
- package/types/extractor/core/extractor.d.ts +19 -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/parsers/expression-resolver.d.ts +6 -0
- package/types/extractor/parsers/expression-resolver.d.ts.map +1 -1
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.
|
|
34
|
+
.version('1.49.5'); // 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
|
|
@@ -64,6 +64,65 @@ class ASTVisitors {
|
|
|
64
64
|
this.callExpressionHandler = new callExpressionHandler.CallExpressionHandler(config, pluginContext, logger, this.expressionResolver, () => this.getCurrentFile(), () => this.getCurrentCode(), (name) => this.scopeManager.resolveSimpleStringIdentifier(name));
|
|
65
65
|
this.jsxHandler = new jsxHandler.JSXHandler(config, pluginContext, this.expressionResolver, () => this.getCurrentFile(), () => this.getCurrentCode());
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Lightweight pre-scan pass: populates the shared constant / type-alias / array tables
|
|
69
|
+
* (`sharedConstants` in ScopeManager; `sharedVariableTable` and `sharedTypeAliasTable`
|
|
70
|
+
* in ExpressionResolver) WITHOUT performing any key extraction.
|
|
71
|
+
*
|
|
72
|
+
* Callers should invoke this for ALL source files before calling `visit()` for any file,
|
|
73
|
+
* so that cross-file identifier references — e.g. `useTranslation(NS_CALENDAR)` where
|
|
74
|
+
* `NS_CALENDAR` is exported from a separate constants file — are already resolved when
|
|
75
|
+
* the hook call is encountered during the extraction pass.
|
|
76
|
+
*
|
|
77
|
+
* The per-file tables (variableTable, typeAliasTable) are reset on each call so that
|
|
78
|
+
* local bindings from one file do not bleed into another; the shared tables accumulate
|
|
79
|
+
* across all calls and are intentionally NOT cleared here.
|
|
80
|
+
*/
|
|
81
|
+
preScanForConstants(node) {
|
|
82
|
+
this.expressionResolver.resetFileSymbols();
|
|
83
|
+
this._walkForConstants(node);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Recursive walker used exclusively by `preScanForConstants`.
|
|
87
|
+
* Dispatches only to constant-capturing handlers; never extracts translation keys.
|
|
88
|
+
*/
|
|
89
|
+
_walkForConstants(node) {
|
|
90
|
+
if (!node || typeof node !== 'object')
|
|
91
|
+
return;
|
|
92
|
+
switch (node.type) {
|
|
93
|
+
case 'VariableDeclarator':
|
|
94
|
+
// String constants → ScopeManager.sharedConstants
|
|
95
|
+
// as-const arrays/objects → ExpressionResolver.sharedVariableTable
|
|
96
|
+
this.scopeManager.handleVariableDeclarator(node);
|
|
97
|
+
this.expressionResolver.captureVariableDeclarator(node);
|
|
98
|
+
break;
|
|
99
|
+
case 'TsTypeAliasDeclaration':
|
|
100
|
+
case 'TSTypeAliasDeclaration':
|
|
101
|
+
case 'TsTypeAliasDecl':
|
|
102
|
+
// Type aliases → ExpressionResolver.sharedTypeAliasTable
|
|
103
|
+
this.expressionResolver.captureTypeAliasDeclaration(node);
|
|
104
|
+
break;
|
|
105
|
+
case 'FunctionDeclaration':
|
|
106
|
+
case 'FnDecl':
|
|
107
|
+
// Return-type annotations for t(fn()) patterns
|
|
108
|
+
this.expressionResolver.captureFunctionDeclaration(node);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
for (const key in node) {
|
|
112
|
+
if (key === 'span')
|
|
113
|
+
continue;
|
|
114
|
+
const child = node[key];
|
|
115
|
+
if (Array.isArray(child)) {
|
|
116
|
+
for (const item of child) {
|
|
117
|
+
if (item && typeof item === 'object')
|
|
118
|
+
this._walkForConstants(item);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (child && typeof child === 'object') {
|
|
122
|
+
this._walkForConstants(child);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
67
126
|
/**
|
|
68
127
|
* Main entry point for AST traversal.
|
|
69
128
|
* Creates a root scope and begins the recursive walk through the syntax tree.
|
|
@@ -456,6 +515,10 @@ class ASTVisitors {
|
|
|
456
515
|
* If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
|
|
457
516
|
* string-array constant, returns the callback's first parameter name and the
|
|
458
517
|
* array values so the caller can inject a temporary variable binding.
|
|
518
|
+
*
|
|
519
|
+
* Also handles:
|
|
520
|
+
* `Object.keys(MAP).map/forEach(k => ...)` → param bound to MAP's keys
|
|
521
|
+
* `Object.values(MAP).map/forEach(v => ...)` → param bound to MAP's values
|
|
459
522
|
*/
|
|
460
523
|
tryGetArrayIterationCallbackInfo(node) {
|
|
461
524
|
try {
|
|
@@ -469,36 +532,67 @@ class ASTVisitors {
|
|
|
469
532
|
return undefined;
|
|
470
533
|
// The object must be an identifier whose value is a known string array
|
|
471
534
|
const obj = callee.object;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
535
|
+
// ── Case 1: KNOWN_ARRAY.map(x => ...) ─────────────────────────────────
|
|
536
|
+
if (obj?.type === 'Identifier') {
|
|
537
|
+
const values = this.expressionResolver.getVariableValues(obj.value);
|
|
538
|
+
if (values && values.length > 0) {
|
|
539
|
+
return this.extractCallbackParam(node, values);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// ── Case 2: Object.keys(MAP).map(k => ...) ────────────────────────────
|
|
543
|
+
// Object.values(MAP).map(v => ...)
|
|
544
|
+
// callee.object is a CallExpression: Object.keys(...) / Object.values(...)
|
|
545
|
+
if (obj?.type === 'CallExpression') {
|
|
546
|
+
const innerCallee = obj.callee;
|
|
547
|
+
// Must be `Object.keys` or `Object.values`
|
|
548
|
+
if (innerCallee?.type === 'MemberExpression' &&
|
|
549
|
+
innerCallee.object?.type === 'Identifier' &&
|
|
550
|
+
innerCallee.object.value === 'Object' &&
|
|
551
|
+
innerCallee.property?.type === 'Identifier' &&
|
|
552
|
+
(innerCallee.property.value === 'keys' || innerCallee.property.value === 'values')) {
|
|
553
|
+
const isKeys = innerCallee.property.value === 'keys';
|
|
554
|
+
// The single argument to Object.keys/values must be a known identifier
|
|
555
|
+
const mapArg = obj.arguments?.[0]?.expression;
|
|
556
|
+
if (mapArg?.type === 'Identifier') {
|
|
557
|
+
const mapEntry = this.expressionResolver.getObjectMap(mapArg.value);
|
|
558
|
+
if (mapEntry) {
|
|
559
|
+
const values = isKeys ? Object.keys(mapEntry) : Object.values(mapEntry);
|
|
560
|
+
if (values.length > 0) {
|
|
561
|
+
return this.extractCallbackParam(node, values);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return undefined;
|
|
497
568
|
}
|
|
498
569
|
catch {
|
|
499
570
|
return undefined;
|
|
500
571
|
}
|
|
501
572
|
}
|
|
573
|
+
/**
|
|
574
|
+
* Extracts the first callback parameter identifier from an iteration call node
|
|
575
|
+
* and pairs it with the provided values array.
|
|
576
|
+
*/
|
|
577
|
+
extractCallbackParam(node, values) {
|
|
578
|
+
const callbackArg = node.arguments?.[0]?.expression;
|
|
579
|
+
if (!callbackArg)
|
|
580
|
+
return undefined;
|
|
581
|
+
const params = callbackArg.params ?? callbackArg.parameters ?? [];
|
|
582
|
+
const firstParam = params[0];
|
|
583
|
+
if (!firstParam)
|
|
584
|
+
return undefined;
|
|
585
|
+
const ident = firstParam.type === 'Identifier'
|
|
586
|
+
? firstParam
|
|
587
|
+
: firstParam.type === 'Param' && firstParam.pat?.type === 'Identifier'
|
|
588
|
+
? firstParam.pat
|
|
589
|
+
: firstParam.type === 'AssignmentPattern' && firstParam.left?.type === 'Identifier'
|
|
590
|
+
? firstParam.left
|
|
591
|
+
: null;
|
|
592
|
+
if (!ident)
|
|
593
|
+
return undefined;
|
|
594
|
+
return { paramName: ident.value, values };
|
|
595
|
+
}
|
|
502
596
|
/**
|
|
503
597
|
* Retrieves variable information from the scope chain.
|
|
504
598
|
* Searches from innermost to outermost scope.
|
|
@@ -236,7 +236,80 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
/**
|
|
239
|
-
*
|
|
239
|
+
* Lightweight pre-scan pass for a single file.
|
|
240
|
+
*
|
|
241
|
+
* Parses the file and calls `astVisitors.preScanForConstants()` to populate
|
|
242
|
+
* cross-file shared constant / type-alias / array tables WITHOUT extracting
|
|
243
|
+
* any translation keys or running plugin hooks.
|
|
244
|
+
*
|
|
245
|
+
* Intended to be called for ALL files in a first pass before `processFile` is
|
|
246
|
+
* called for any file, ensuring that exported identifier references such as
|
|
247
|
+
* `NS_CALENDAR` from `@core/translations/ns` are resolved in all files
|
|
248
|
+
* regardless of processing order.
|
|
249
|
+
*
|
|
250
|
+
* @param file - Absolute or CWD-relative path of the source file to pre-scan
|
|
251
|
+
* @param astVisitors - Shared visitor instance (holds the shared constant tables)
|
|
252
|
+
* @param config - Extractor configuration (without plugins)
|
|
253
|
+
* @param logger - Logger for warnings/errors
|
|
254
|
+
* @param fileErrors - Optional array to collect per-file error messages
|
|
255
|
+
*/
|
|
256
|
+
async function preScanFile(file, astVisitors, config, logger$1 = new logger.ConsoleLogger(), fileErrors) {
|
|
257
|
+
try {
|
|
258
|
+
const code = await promises.readFile(file, 'utf-8');
|
|
259
|
+
const fileExt = node_path.extname(file).toLowerCase();
|
|
260
|
+
const isTypeScriptFile = fileExt === '.ts' || fileExt === '.tsx' || fileExt === '.mts' || fileExt === '.cts';
|
|
261
|
+
const isTSX = fileExt === '.tsx';
|
|
262
|
+
const isJSX = fileExt === '.jsx';
|
|
263
|
+
let ast;
|
|
264
|
+
try {
|
|
265
|
+
ast = await core.parse(code, {
|
|
266
|
+
syntax: isTypeScriptFile ? 'typescript' : 'ecmascript',
|
|
267
|
+
tsx: isTSX,
|
|
268
|
+
jsx: isJSX,
|
|
269
|
+
decorators: true,
|
|
270
|
+
dynamicImport: true,
|
|
271
|
+
comments: true,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
if (fileExt === '.ts' && !isTSX) {
|
|
276
|
+
try {
|
|
277
|
+
ast = await core.parse(code, { syntax: 'typescript', tsx: true, decorators: true, dynamicImport: true, comments: true });
|
|
278
|
+
}
|
|
279
|
+
catch (err2) {
|
|
280
|
+
throw new validation.ExtractorError('Failed to pre-scan file', file, err2);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else if (fileExt === '.js' && !isJSX) {
|
|
284
|
+
try {
|
|
285
|
+
ast = await core.parse(code, { syntax: 'ecmascript', jsx: true, decorators: true, dynamicImport: true, comments: true });
|
|
286
|
+
}
|
|
287
|
+
catch (err2) {
|
|
288
|
+
throw new validation.ExtractorError('Failed to pre-scan file', file, err2);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
throw new validation.ExtractorError('Failed to pre-scan file', file, err);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const firstTokenIdx = astUtils.findFirstTokenIndex(code);
|
|
296
|
+
astUtils.normalizeASTSpans(ast, ast.span.start - firstTokenIdx);
|
|
297
|
+
astVisitors.setCurrentFile(file, code);
|
|
298
|
+
astVisitors.preScanForConstants(ast);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
if (error instanceof pluginManager.ConflictError)
|
|
302
|
+
throw error;
|
|
303
|
+
logger$1.warn(`${node_util.styleText('yellow', 'Skipping file in constants pre-scan due to error:')} ${file}`);
|
|
304
|
+
const err = error;
|
|
305
|
+
const msg = typeof err?.message === 'string' && err.message.trim().length > 0
|
|
306
|
+
? err.message
|
|
307
|
+
: (typeof err === 'string' ? err : '') || err?.toString?.() || 'Unknown error';
|
|
308
|
+
if (fileErrors)
|
|
309
|
+
fileErrors.push(`${file}: ${msg}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
240
313
|
* Used primarily for testing and programmatic access.
|
|
241
314
|
*
|
|
242
315
|
* @param config - The i18next toolkit configuration object
|
|
@@ -287,5 +360,6 @@ async function printLocizeFunnel(logger$1, force) {
|
|
|
287
360
|
}
|
|
288
361
|
|
|
289
362
|
exports.extract = extract;
|
|
363
|
+
exports.preScanFile = preScanFile;
|
|
290
364
|
exports.processFile = processFile;
|
|
291
365
|
exports.runExtractor = runExtractor;
|
|
@@ -86,7 +86,16 @@ async function findKeys(config, logger$1 = new logger.ConsoleLogger(), fileError
|
|
|
86
86
|
pluginContext.getVarFromScope = astVisitors$1.getVarFromScope.bind(astVisitors$1);
|
|
87
87
|
// 5. Initialize plugins
|
|
88
88
|
await pluginManager.initializePlugins(plugins);
|
|
89
|
-
// 6.
|
|
89
|
+
// 6. Pre-scan all files to populate cross-file shared constant tables BEFORE any
|
|
90
|
+
// key extraction begins. This ensures that identifier-based namespace references
|
|
91
|
+
// such as `useTranslation(NS_CALENDAR)` or `t('key', { ns: NS_SETTINGS })` resolve
|
|
92
|
+
// correctly even when the defining constants file is processed after the file that
|
|
93
|
+
// uses the identifier.
|
|
94
|
+
for (const file of sourceFiles) {
|
|
95
|
+
await extractor.preScanFile(file, astVisitors$1, otherConfig, logger$1, fileErrors);
|
|
96
|
+
}
|
|
97
|
+
// 7. Extraction pass: all shared tables are now fully populated, so every
|
|
98
|
+
// identifier reference can be resolved regardless of file order.
|
|
90
99
|
for (const file of sourceFiles) {
|
|
91
100
|
await extractor.processFile(file, plugins, astVisitors$1, pluginContext, otherConfig, logger$1, fileErrors);
|
|
92
101
|
}
|
|
@@ -272,6 +272,20 @@ class ExpressionResolver {
|
|
|
272
272
|
return v;
|
|
273
273
|
return this.sharedVariableTable.get(name);
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Return the as-const object map stored for a variable name.
|
|
277
|
+
* Returns undefined if the name is not a known object map.
|
|
278
|
+
* Checks per-file variableTable first, then sharedEnumTable (for enums).
|
|
279
|
+
*/
|
|
280
|
+
getObjectMap(name) {
|
|
281
|
+
const v = this.variableTable.get(name);
|
|
282
|
+
if (v && !Array.isArray(v) && typeof v === 'object')
|
|
283
|
+
return v;
|
|
284
|
+
const ev = this.sharedEnumTable.get(name);
|
|
285
|
+
if (ev)
|
|
286
|
+
return ev;
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
275
289
|
/**
|
|
276
290
|
* Capture a TypeScript enum declaration so members can be resolved later.
|
|
277
291
|
* Accepts SWC node shapes like `TsEnumDeclaration` / `TSEnumDeclaration`.
|
|
@@ -557,14 +571,25 @@ class ExpressionResolver {
|
|
|
557
571
|
// pattern 2:
|
|
558
572
|
// Resolve a named type alias reference: `declare const x: ChangeType`
|
|
559
573
|
// where `type ChangeType = 'all' | 'next' | 'this'` was captured earlier.
|
|
574
|
+
// Also handles `declare const d: SomeEnum` where SomeEnum is a TS enum with string values.
|
|
560
575
|
if (type.type === 'TsTypeReference') {
|
|
561
576
|
const typeName = type.typeName?.type === 'Identifier'
|
|
562
577
|
? type.typeName.value
|
|
563
578
|
: undefined;
|
|
564
579
|
if (typeName) {
|
|
580
|
+
// 1. Check type alias table first (exact match for string-literal unions)
|
|
565
581
|
const aliasVals = this.typeAliasTable.get(typeName) ?? this.sharedTypeAliasTable.get(typeName);
|
|
566
582
|
if (aliasVals && aliasVals.length > 0)
|
|
567
583
|
return aliasVals;
|
|
584
|
+
// 2. Fall back to enum: `declare const d: Direction` where Direction is a string enum.
|
|
585
|
+
// sharedEnumTable maps enum-name → { MemberName: value }.
|
|
586
|
+
// A variable typed as the enum can take any of the enum's string values.
|
|
587
|
+
const enumMap = this.sharedEnumTable.get(typeName);
|
|
588
|
+
if (enumMap) {
|
|
589
|
+
const enumVals = Object.values(enumMap);
|
|
590
|
+
if (enumVals.length > 0)
|
|
591
|
+
return enumVals;
|
|
592
|
+
}
|
|
568
593
|
}
|
|
569
594
|
}
|
|
570
595
|
// `(typeof ACCESS_OPTIONS)[number]` — resolve through the shared array variable table.
|
|
@@ -594,6 +619,40 @@ class ExpressionResolver {
|
|
|
594
619
|
}
|
|
595
620
|
catch { }
|
|
596
621
|
}
|
|
622
|
+
// `keyof typeof MAP` — resolve to the keys of a known as-const object map.
|
|
623
|
+
// SWC emits: TsTypeOperator {
|
|
624
|
+
// operator: 'keyof',
|
|
625
|
+
// typeAnnotation: TsTypeQuery { exprName: Identifier }
|
|
626
|
+
// }
|
|
627
|
+
// This is the type of a variable that iterates over map keys:
|
|
628
|
+
// declare const k: keyof typeof LABELS; t(LABELS[k])
|
|
629
|
+
// Object.keys(MAP).forEach(k => t(MAP[k]))
|
|
630
|
+
if (type.type === 'TsTypeOperator') {
|
|
631
|
+
try {
|
|
632
|
+
const op = type.operator;
|
|
633
|
+
if (op === 'keyof') {
|
|
634
|
+
let inner = type.typeAnnotation;
|
|
635
|
+
while (inner?.type === 'TsParenthesizedType')
|
|
636
|
+
inner = inner.typeAnnotation;
|
|
637
|
+
if (inner?.type === 'TsTypeQuery' || inner?.type === 'TSTypeQuery') {
|
|
638
|
+
const exprName = inner.exprName ?? inner.expr ?? inner.entityName;
|
|
639
|
+
const varName = exprName?.value ?? exprName?.name;
|
|
640
|
+
if (varName) {
|
|
641
|
+
// Look up in variableTable (local) or sharedVariableTable (cross-file) for object maps
|
|
642
|
+
const v = this.variableTable.get(varName) ?? this.sharedVariableTable.get(varName);
|
|
643
|
+
if (v && !Array.isArray(v) && typeof v === 'object') {
|
|
644
|
+
return Object.keys(v);
|
|
645
|
+
}
|
|
646
|
+
// Also check sharedEnumTable (enum keys)
|
|
647
|
+
const ev = this.sharedEnumTable.get(varName);
|
|
648
|
+
if (ev)
|
|
649
|
+
return Object.keys(ev);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
catch { }
|
|
655
|
+
}
|
|
597
656
|
// We can't statically determine the value of other expressions (e.g., variables, function calls)
|
|
598
657
|
return [];
|
|
599
658
|
}
|
|
@@ -27,10 +27,10 @@ async function loadFile(file) {
|
|
|
27
27
|
type: 'commonjs'
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
-
const exports = {};
|
|
31
|
-
const module = { exports };
|
|
30
|
+
const exports$1 = {};
|
|
31
|
+
const module = { exports: exports$1 };
|
|
32
32
|
const context = vm.createContext({
|
|
33
|
-
exports,
|
|
33
|
+
exports: exports$1,
|
|
34
34
|
module,
|
|
35
35
|
require: (id) => require(id),
|
|
36
36
|
console,
|
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.
|
|
32
|
+
.version('1.49.5'); // 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
|
|
@@ -62,6 +62,65 @@ class ASTVisitors {
|
|
|
62
62
|
this.callExpressionHandler = new CallExpressionHandler(config, pluginContext, logger, this.expressionResolver, () => this.getCurrentFile(), () => this.getCurrentCode(), (name) => this.scopeManager.resolveSimpleStringIdentifier(name));
|
|
63
63
|
this.jsxHandler = new JSXHandler(config, pluginContext, this.expressionResolver, () => this.getCurrentFile(), () => this.getCurrentCode());
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Lightweight pre-scan pass: populates the shared constant / type-alias / array tables
|
|
67
|
+
* (`sharedConstants` in ScopeManager; `sharedVariableTable` and `sharedTypeAliasTable`
|
|
68
|
+
* in ExpressionResolver) WITHOUT performing any key extraction.
|
|
69
|
+
*
|
|
70
|
+
* Callers should invoke this for ALL source files before calling `visit()` for any file,
|
|
71
|
+
* so that cross-file identifier references — e.g. `useTranslation(NS_CALENDAR)` where
|
|
72
|
+
* `NS_CALENDAR` is exported from a separate constants file — are already resolved when
|
|
73
|
+
* the hook call is encountered during the extraction pass.
|
|
74
|
+
*
|
|
75
|
+
* The per-file tables (variableTable, typeAliasTable) are reset on each call so that
|
|
76
|
+
* local bindings from one file do not bleed into another; the shared tables accumulate
|
|
77
|
+
* across all calls and are intentionally NOT cleared here.
|
|
78
|
+
*/
|
|
79
|
+
preScanForConstants(node) {
|
|
80
|
+
this.expressionResolver.resetFileSymbols();
|
|
81
|
+
this._walkForConstants(node);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Recursive walker used exclusively by `preScanForConstants`.
|
|
85
|
+
* Dispatches only to constant-capturing handlers; never extracts translation keys.
|
|
86
|
+
*/
|
|
87
|
+
_walkForConstants(node) {
|
|
88
|
+
if (!node || typeof node !== 'object')
|
|
89
|
+
return;
|
|
90
|
+
switch (node.type) {
|
|
91
|
+
case 'VariableDeclarator':
|
|
92
|
+
// String constants → ScopeManager.sharedConstants
|
|
93
|
+
// as-const arrays/objects → ExpressionResolver.sharedVariableTable
|
|
94
|
+
this.scopeManager.handleVariableDeclarator(node);
|
|
95
|
+
this.expressionResolver.captureVariableDeclarator(node);
|
|
96
|
+
break;
|
|
97
|
+
case 'TsTypeAliasDeclaration':
|
|
98
|
+
case 'TSTypeAliasDeclaration':
|
|
99
|
+
case 'TsTypeAliasDecl':
|
|
100
|
+
// Type aliases → ExpressionResolver.sharedTypeAliasTable
|
|
101
|
+
this.expressionResolver.captureTypeAliasDeclaration(node);
|
|
102
|
+
break;
|
|
103
|
+
case 'FunctionDeclaration':
|
|
104
|
+
case 'FnDecl':
|
|
105
|
+
// Return-type annotations for t(fn()) patterns
|
|
106
|
+
this.expressionResolver.captureFunctionDeclaration(node);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
for (const key in node) {
|
|
110
|
+
if (key === 'span')
|
|
111
|
+
continue;
|
|
112
|
+
const child = node[key];
|
|
113
|
+
if (Array.isArray(child)) {
|
|
114
|
+
for (const item of child) {
|
|
115
|
+
if (item && typeof item === 'object')
|
|
116
|
+
this._walkForConstants(item);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (child && typeof child === 'object') {
|
|
120
|
+
this._walkForConstants(child);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
65
124
|
/**
|
|
66
125
|
* Main entry point for AST traversal.
|
|
67
126
|
* Creates a root scope and begins the recursive walk through the syntax tree.
|
|
@@ -454,6 +513,10 @@ class ASTVisitors {
|
|
|
454
513
|
* If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
|
|
455
514
|
* string-array constant, returns the callback's first parameter name and the
|
|
456
515
|
* array values so the caller can inject a temporary variable binding.
|
|
516
|
+
*
|
|
517
|
+
* Also handles:
|
|
518
|
+
* `Object.keys(MAP).map/forEach(k => ...)` → param bound to MAP's keys
|
|
519
|
+
* `Object.values(MAP).map/forEach(v => ...)` → param bound to MAP's values
|
|
457
520
|
*/
|
|
458
521
|
tryGetArrayIterationCallbackInfo(node) {
|
|
459
522
|
try {
|
|
@@ -467,36 +530,67 @@ class ASTVisitors {
|
|
|
467
530
|
return undefined;
|
|
468
531
|
// The object must be an identifier whose value is a known string array
|
|
469
532
|
const obj = callee.object;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
533
|
+
// ── Case 1: KNOWN_ARRAY.map(x => ...) ─────────────────────────────────
|
|
534
|
+
if (obj?.type === 'Identifier') {
|
|
535
|
+
const values = this.expressionResolver.getVariableValues(obj.value);
|
|
536
|
+
if (values && values.length > 0) {
|
|
537
|
+
return this.extractCallbackParam(node, values);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// ── Case 2: Object.keys(MAP).map(k => ...) ────────────────────────────
|
|
541
|
+
// Object.values(MAP).map(v => ...)
|
|
542
|
+
// callee.object is a CallExpression: Object.keys(...) / Object.values(...)
|
|
543
|
+
if (obj?.type === 'CallExpression') {
|
|
544
|
+
const innerCallee = obj.callee;
|
|
545
|
+
// Must be `Object.keys` or `Object.values`
|
|
546
|
+
if (innerCallee?.type === 'MemberExpression' &&
|
|
547
|
+
innerCallee.object?.type === 'Identifier' &&
|
|
548
|
+
innerCallee.object.value === 'Object' &&
|
|
549
|
+
innerCallee.property?.type === 'Identifier' &&
|
|
550
|
+
(innerCallee.property.value === 'keys' || innerCallee.property.value === 'values')) {
|
|
551
|
+
const isKeys = innerCallee.property.value === 'keys';
|
|
552
|
+
// The single argument to Object.keys/values must be a known identifier
|
|
553
|
+
const mapArg = obj.arguments?.[0]?.expression;
|
|
554
|
+
if (mapArg?.type === 'Identifier') {
|
|
555
|
+
const mapEntry = this.expressionResolver.getObjectMap(mapArg.value);
|
|
556
|
+
if (mapEntry) {
|
|
557
|
+
const values = isKeys ? Object.keys(mapEntry) : Object.values(mapEntry);
|
|
558
|
+
if (values.length > 0) {
|
|
559
|
+
return this.extractCallbackParam(node, values);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return undefined;
|
|
495
566
|
}
|
|
496
567
|
catch {
|
|
497
568
|
return undefined;
|
|
498
569
|
}
|
|
499
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* Extracts the first callback parameter identifier from an iteration call node
|
|
573
|
+
* and pairs it with the provided values array.
|
|
574
|
+
*/
|
|
575
|
+
extractCallbackParam(node, values) {
|
|
576
|
+
const callbackArg = node.arguments?.[0]?.expression;
|
|
577
|
+
if (!callbackArg)
|
|
578
|
+
return undefined;
|
|
579
|
+
const params = callbackArg.params ?? callbackArg.parameters ?? [];
|
|
580
|
+
const firstParam = params[0];
|
|
581
|
+
if (!firstParam)
|
|
582
|
+
return undefined;
|
|
583
|
+
const ident = firstParam.type === 'Identifier'
|
|
584
|
+
? firstParam
|
|
585
|
+
: firstParam.type === 'Param' && firstParam.pat?.type === 'Identifier'
|
|
586
|
+
? firstParam.pat
|
|
587
|
+
: firstParam.type === 'AssignmentPattern' && firstParam.left?.type === 'Identifier'
|
|
588
|
+
? firstParam.left
|
|
589
|
+
: null;
|
|
590
|
+
if (!ident)
|
|
591
|
+
return undefined;
|
|
592
|
+
return { paramName: ident.value, values };
|
|
593
|
+
}
|
|
500
594
|
/**
|
|
501
595
|
* Retrieves variable information from the scope chain.
|
|
502
596
|
* Searches from innermost to outermost scope.
|
|
@@ -234,7 +234,80 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
/**
|
|
237
|
-
*
|
|
237
|
+
* Lightweight pre-scan pass for a single file.
|
|
238
|
+
*
|
|
239
|
+
* Parses the file and calls `astVisitors.preScanForConstants()` to populate
|
|
240
|
+
* cross-file shared constant / type-alias / array tables WITHOUT extracting
|
|
241
|
+
* any translation keys or running plugin hooks.
|
|
242
|
+
*
|
|
243
|
+
* Intended to be called for ALL files in a first pass before `processFile` is
|
|
244
|
+
* called for any file, ensuring that exported identifier references such as
|
|
245
|
+
* `NS_CALENDAR` from `@core/translations/ns` are resolved in all files
|
|
246
|
+
* regardless of processing order.
|
|
247
|
+
*
|
|
248
|
+
* @param file - Absolute or CWD-relative path of the source file to pre-scan
|
|
249
|
+
* @param astVisitors - Shared visitor instance (holds the shared constant tables)
|
|
250
|
+
* @param config - Extractor configuration (without plugins)
|
|
251
|
+
* @param logger - Logger for warnings/errors
|
|
252
|
+
* @param fileErrors - Optional array to collect per-file error messages
|
|
253
|
+
*/
|
|
254
|
+
async function preScanFile(file, astVisitors, config, logger = new ConsoleLogger(), fileErrors) {
|
|
255
|
+
try {
|
|
256
|
+
const code = await readFile(file, 'utf-8');
|
|
257
|
+
const fileExt = extname(file).toLowerCase();
|
|
258
|
+
const isTypeScriptFile = fileExt === '.ts' || fileExt === '.tsx' || fileExt === '.mts' || fileExt === '.cts';
|
|
259
|
+
const isTSX = fileExt === '.tsx';
|
|
260
|
+
const isJSX = fileExt === '.jsx';
|
|
261
|
+
let ast;
|
|
262
|
+
try {
|
|
263
|
+
ast = await parse(code, {
|
|
264
|
+
syntax: isTypeScriptFile ? 'typescript' : 'ecmascript',
|
|
265
|
+
tsx: isTSX,
|
|
266
|
+
jsx: isJSX,
|
|
267
|
+
decorators: true,
|
|
268
|
+
dynamicImport: true,
|
|
269
|
+
comments: true,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
if (fileExt === '.ts' && !isTSX) {
|
|
274
|
+
try {
|
|
275
|
+
ast = await parse(code, { syntax: 'typescript', tsx: true, decorators: true, dynamicImport: true, comments: true });
|
|
276
|
+
}
|
|
277
|
+
catch (err2) {
|
|
278
|
+
throw new ExtractorError('Failed to pre-scan file', file, err2);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (fileExt === '.js' && !isJSX) {
|
|
282
|
+
try {
|
|
283
|
+
ast = await parse(code, { syntax: 'ecmascript', jsx: true, decorators: true, dynamicImport: true, comments: true });
|
|
284
|
+
}
|
|
285
|
+
catch (err2) {
|
|
286
|
+
throw new ExtractorError('Failed to pre-scan file', file, err2);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
throw new ExtractorError('Failed to pre-scan file', file, err);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const firstTokenIdx = findFirstTokenIndex(code);
|
|
294
|
+
normalizeASTSpans(ast, ast.span.start - firstTokenIdx);
|
|
295
|
+
astVisitors.setCurrentFile(file, code);
|
|
296
|
+
astVisitors.preScanForConstants(ast);
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
if (error instanceof ConflictError)
|
|
300
|
+
throw error;
|
|
301
|
+
logger.warn(`${styleText('yellow', 'Skipping file in constants pre-scan due to error:')} ${file}`);
|
|
302
|
+
const err = error;
|
|
303
|
+
const msg = typeof err?.message === 'string' && err.message.trim().length > 0
|
|
304
|
+
? err.message
|
|
305
|
+
: (typeof err === 'string' ? err : '') || err?.toString?.() || 'Unknown error';
|
|
306
|
+
if (fileErrors)
|
|
307
|
+
fileErrors.push(`${file}: ${msg}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
238
311
|
* Used primarily for testing and programmatic access.
|
|
239
312
|
*
|
|
240
313
|
* @param config - The i18next toolkit configuration object
|
|
@@ -284,4 +357,4 @@ async function printLocizeFunnel(logger, force) {
|
|
|
284
357
|
return recordFunnelShown('extract');
|
|
285
358
|
}
|
|
286
359
|
|
|
287
|
-
export { extract, processFile, runExtractor };
|
|
360
|
+
export { extract, preScanFile, processFile, runExtractor };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { glob } from 'glob';
|
|
2
|
-
import { processFile } from './extractor.js';
|
|
2
|
+
import { preScanFile, processFile } from './extractor.js';
|
|
3
3
|
import { ConsoleLogger } from '../../utils/logger.js';
|
|
4
4
|
import { createPluginContext, initializePlugins } from '../plugin-manager.js';
|
|
5
5
|
import { ASTVisitors } from './ast-visitors.js';
|
|
@@ -84,7 +84,16 @@ async function findKeys(config, logger = new ConsoleLogger(), fileErrors) {
|
|
|
84
84
|
pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors);
|
|
85
85
|
// 5. Initialize plugins
|
|
86
86
|
await initializePlugins(plugins);
|
|
87
|
-
// 6.
|
|
87
|
+
// 6. Pre-scan all files to populate cross-file shared constant tables BEFORE any
|
|
88
|
+
// key extraction begins. This ensures that identifier-based namespace references
|
|
89
|
+
// such as `useTranslation(NS_CALENDAR)` or `t('key', { ns: NS_SETTINGS })` resolve
|
|
90
|
+
// correctly even when the defining constants file is processed after the file that
|
|
91
|
+
// uses the identifier.
|
|
92
|
+
for (const file of sourceFiles) {
|
|
93
|
+
await preScanFile(file, astVisitors, otherConfig, logger, fileErrors);
|
|
94
|
+
}
|
|
95
|
+
// 7. Extraction pass: all shared tables are now fully populated, so every
|
|
96
|
+
// identifier reference can be resolved regardless of file order.
|
|
88
97
|
for (const file of sourceFiles) {
|
|
89
98
|
await processFile(file, plugins, astVisitors, pluginContext, otherConfig, logger, fileErrors);
|
|
90
99
|
}
|
|
@@ -270,6 +270,20 @@ class ExpressionResolver {
|
|
|
270
270
|
return v;
|
|
271
271
|
return this.sharedVariableTable.get(name);
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Return the as-const object map stored for a variable name.
|
|
275
|
+
* Returns undefined if the name is not a known object map.
|
|
276
|
+
* Checks per-file variableTable first, then sharedEnumTable (for enums).
|
|
277
|
+
*/
|
|
278
|
+
getObjectMap(name) {
|
|
279
|
+
const v = this.variableTable.get(name);
|
|
280
|
+
if (v && !Array.isArray(v) && typeof v === 'object')
|
|
281
|
+
return v;
|
|
282
|
+
const ev = this.sharedEnumTable.get(name);
|
|
283
|
+
if (ev)
|
|
284
|
+
return ev;
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
273
287
|
/**
|
|
274
288
|
* Capture a TypeScript enum declaration so members can be resolved later.
|
|
275
289
|
* Accepts SWC node shapes like `TsEnumDeclaration` / `TSEnumDeclaration`.
|
|
@@ -555,14 +569,25 @@ class ExpressionResolver {
|
|
|
555
569
|
// pattern 2:
|
|
556
570
|
// Resolve a named type alias reference: `declare const x: ChangeType`
|
|
557
571
|
// where `type ChangeType = 'all' | 'next' | 'this'` was captured earlier.
|
|
572
|
+
// Also handles `declare const d: SomeEnum` where SomeEnum is a TS enum with string values.
|
|
558
573
|
if (type.type === 'TsTypeReference') {
|
|
559
574
|
const typeName = type.typeName?.type === 'Identifier'
|
|
560
575
|
? type.typeName.value
|
|
561
576
|
: undefined;
|
|
562
577
|
if (typeName) {
|
|
578
|
+
// 1. Check type alias table first (exact match for string-literal unions)
|
|
563
579
|
const aliasVals = this.typeAliasTable.get(typeName) ?? this.sharedTypeAliasTable.get(typeName);
|
|
564
580
|
if (aliasVals && aliasVals.length > 0)
|
|
565
581
|
return aliasVals;
|
|
582
|
+
// 2. Fall back to enum: `declare const d: Direction` where Direction is a string enum.
|
|
583
|
+
// sharedEnumTable maps enum-name → { MemberName: value }.
|
|
584
|
+
// A variable typed as the enum can take any of the enum's string values.
|
|
585
|
+
const enumMap = this.sharedEnumTable.get(typeName);
|
|
586
|
+
if (enumMap) {
|
|
587
|
+
const enumVals = Object.values(enumMap);
|
|
588
|
+
if (enumVals.length > 0)
|
|
589
|
+
return enumVals;
|
|
590
|
+
}
|
|
566
591
|
}
|
|
567
592
|
}
|
|
568
593
|
// `(typeof ACCESS_OPTIONS)[number]` — resolve through the shared array variable table.
|
|
@@ -592,6 +617,40 @@ class ExpressionResolver {
|
|
|
592
617
|
}
|
|
593
618
|
catch { }
|
|
594
619
|
}
|
|
620
|
+
// `keyof typeof MAP` — resolve to the keys of a known as-const object map.
|
|
621
|
+
// SWC emits: TsTypeOperator {
|
|
622
|
+
// operator: 'keyof',
|
|
623
|
+
// typeAnnotation: TsTypeQuery { exprName: Identifier }
|
|
624
|
+
// }
|
|
625
|
+
// This is the type of a variable that iterates over map keys:
|
|
626
|
+
// declare const k: keyof typeof LABELS; t(LABELS[k])
|
|
627
|
+
// Object.keys(MAP).forEach(k => t(MAP[k]))
|
|
628
|
+
if (type.type === 'TsTypeOperator') {
|
|
629
|
+
try {
|
|
630
|
+
const op = type.operator;
|
|
631
|
+
if (op === 'keyof') {
|
|
632
|
+
let inner = type.typeAnnotation;
|
|
633
|
+
while (inner?.type === 'TsParenthesizedType')
|
|
634
|
+
inner = inner.typeAnnotation;
|
|
635
|
+
if (inner?.type === 'TsTypeQuery' || inner?.type === 'TSTypeQuery') {
|
|
636
|
+
const exprName = inner.exprName ?? inner.expr ?? inner.entityName;
|
|
637
|
+
const varName = exprName?.value ?? exprName?.name;
|
|
638
|
+
if (varName) {
|
|
639
|
+
// Look up in variableTable (local) or sharedVariableTable (cross-file) for object maps
|
|
640
|
+
const v = this.variableTable.get(varName) ?? this.sharedVariableTable.get(varName);
|
|
641
|
+
if (v && !Array.isArray(v) && typeof v === 'object') {
|
|
642
|
+
return Object.keys(v);
|
|
643
|
+
}
|
|
644
|
+
// Also check sharedEnumTable (enum keys)
|
|
645
|
+
const ev = this.sharedEnumTable.get(varName);
|
|
646
|
+
if (ev)
|
|
647
|
+
return Object.keys(ev);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
catch { }
|
|
653
|
+
}
|
|
595
654
|
// We can't statically determine the value of other expressions (e.g., variables, function calls)
|
|
596
655
|
return [];
|
|
597
656
|
}
|
|
@@ -25,10 +25,10 @@ async function loadFile(file) {
|
|
|
25
25
|
type: 'commonjs'
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
|
-
const exports = {};
|
|
29
|
-
const module = { exports };
|
|
28
|
+
const exports$1 = {};
|
|
29
|
+
const module = { exports: exports$1 };
|
|
30
30
|
const context = vm.createContext({
|
|
31
|
-
exports,
|
|
31
|
+
exports: exports$1,
|
|
32
32
|
module,
|
|
33
33
|
require: (id) => require(id),
|
|
34
34
|
console,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18next-cli",
|
|
3
|
-
"version": "1.49.
|
|
3
|
+
"version": "1.49.5",
|
|
4
4
|
"description": "A unified, high-performance i18next CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -52,39 +52,39 @@
|
|
|
52
52
|
"url": "https://github.com/i18next/i18next-cli/issues"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@rollup/plugin-replace": "6.0.3",
|
|
56
|
-
"@rollup/plugin-terser": "0.
|
|
57
|
-
"@types/inquirer": "9.0.9",
|
|
58
|
-
"@types/node": "25.
|
|
59
|
-
"@types/react": "19.2.14",
|
|
60
|
-
"@typescript-eslint/parser": "^8.
|
|
61
|
-
"@vitest/coverage-v8": "4.0.18",
|
|
62
|
-
"eslint": "9.39.2",
|
|
55
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
56
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
57
|
+
"@types/inquirer": "^9.0.9",
|
|
58
|
+
"@types/node": "^25.4.0",
|
|
59
|
+
"@types/react": "^19.2.14",
|
|
60
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
61
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
62
|
+
"eslint": "^9.39.2",
|
|
63
63
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
64
|
-
"eslint-plugin-import": "2.32.0",
|
|
65
|
-
"memfs": "4.56.11",
|
|
66
|
-
"neostandard": "0.13.0",
|
|
67
|
-
"rollup-plugin-typescript2": "0.36.0",
|
|
68
|
-
"typescript": "5.9.3",
|
|
69
|
-
"unplugin-swc": "1.5.9",
|
|
70
|
-
"vitest": "4.0.18"
|
|
64
|
+
"eslint-plugin-import": "^2.32.0",
|
|
65
|
+
"memfs": "^4.56.11",
|
|
66
|
+
"neostandard": "^0.13.0",
|
|
67
|
+
"rollup-plugin-typescript2": "^0.36.0",
|
|
68
|
+
"typescript": "^5.9.3",
|
|
69
|
+
"unplugin-swc": "^1.5.9",
|
|
70
|
+
"vitest": "^4.0.18"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@croct/json5-parser": "0.2.2",
|
|
74
|
-
"@swc/core": "1.15.18",
|
|
75
|
-
"chokidar": "5.0.0",
|
|
76
|
-
"commander": "14.0.3",
|
|
77
|
-
"execa": "9.6.1",
|
|
78
|
-
"glob": "13.0.6",
|
|
79
|
-
"i18next-resources-for-ts": "2.0.
|
|
80
|
-
"inquirer": "13.3.0",
|
|
81
|
-
"jiti": "2.6.1",
|
|
82
|
-
"jsonc-parser": "3.3.1",
|
|
83
|
-
"magic-string": "0.30.21",
|
|
84
|
-
"minimatch": "10.2.4",
|
|
85
|
-
"ora": "9.3.0",
|
|
73
|
+
"@croct/json5-parser": "^0.2.2",
|
|
74
|
+
"@swc/core": "^1.15.18",
|
|
75
|
+
"chokidar": "^5.0.0",
|
|
76
|
+
"commander": "^14.0.3",
|
|
77
|
+
"execa": "^9.6.1",
|
|
78
|
+
"glob": "^13.0.6",
|
|
79
|
+
"i18next-resources-for-ts": "^2.0.1",
|
|
80
|
+
"inquirer": "^13.3.0",
|
|
81
|
+
"jiti": "^2.6.1",
|
|
82
|
+
"jsonc-parser": "^3.3.1",
|
|
83
|
+
"magic-string": "^0.30.21",
|
|
84
|
+
"minimatch": "^10.2.4",
|
|
85
|
+
"ora": "^9.3.0",
|
|
86
86
|
"react": "^19.2.4",
|
|
87
|
-
"react-i18next": "^16.5.
|
|
88
|
-
"yaml": "2.8.2"
|
|
87
|
+
"react-i18next": "^16.5.6",
|
|
88
|
+
"yaml": "^2.8.2"
|
|
89
89
|
}
|
|
90
90
|
}
|
|
@@ -44,6 +44,26 @@ export declare class ASTVisitors {
|
|
|
44
44
|
* @param logger - Logger for warnings and debug information
|
|
45
45
|
*/
|
|
46
46
|
constructor(config: Omit<I18nextToolkitConfig, 'plugins'>, pluginContext: PluginContext, logger: Logger, hooks?: ASTVisitorHooks, expressionResolver?: ExpressionResolver);
|
|
47
|
+
/**
|
|
48
|
+
* Lightweight pre-scan pass: populates the shared constant / type-alias / array tables
|
|
49
|
+
* (`sharedConstants` in ScopeManager; `sharedVariableTable` and `sharedTypeAliasTable`
|
|
50
|
+
* in ExpressionResolver) WITHOUT performing any key extraction.
|
|
51
|
+
*
|
|
52
|
+
* Callers should invoke this for ALL source files before calling `visit()` for any file,
|
|
53
|
+
* so that cross-file identifier references — e.g. `useTranslation(NS_CALENDAR)` where
|
|
54
|
+
* `NS_CALENDAR` is exported from a separate constants file — are already resolved when
|
|
55
|
+
* the hook call is encountered during the extraction pass.
|
|
56
|
+
*
|
|
57
|
+
* The per-file tables (variableTable, typeAliasTable) are reset on each call so that
|
|
58
|
+
* local bindings from one file do not bleed into another; the shared tables accumulate
|
|
59
|
+
* across all calls and are intentionally NOT cleared here.
|
|
60
|
+
*/
|
|
61
|
+
preScanForConstants(node: Module): void;
|
|
62
|
+
/**
|
|
63
|
+
* Recursive walker used exclusively by `preScanForConstants`.
|
|
64
|
+
* Dispatches only to constant-capturing handlers; never extracts translation keys.
|
|
65
|
+
*/
|
|
66
|
+
private _walkForConstants;
|
|
47
67
|
/**
|
|
48
68
|
* Main entry point for AST traversal.
|
|
49
69
|
* Creates a root scope and begins the recursive walk through the syntax tree.
|
|
@@ -69,8 +89,17 @@ export declare class ASTVisitors {
|
|
|
69
89
|
* If `node` is a call like `ARRAY.map(param => ...)` where ARRAY is a known
|
|
70
90
|
* string-array constant, returns the callback's first parameter name and the
|
|
71
91
|
* array values so the caller can inject a temporary variable binding.
|
|
92
|
+
*
|
|
93
|
+
* Also handles:
|
|
94
|
+
* `Object.keys(MAP).map/forEach(k => ...)` → param bound to MAP's keys
|
|
95
|
+
* `Object.values(MAP).map/forEach(v => ...)` → param bound to MAP's values
|
|
72
96
|
*/
|
|
73
97
|
private tryGetArrayIterationCallbackInfo;
|
|
98
|
+
/**
|
|
99
|
+
* Extracts the first callback parameter identifier from an iteration call node
|
|
100
|
+
* and pairs it with the provided values array.
|
|
101
|
+
*/
|
|
102
|
+
private extractCallbackParam;
|
|
74
103
|
/**
|
|
75
104
|
* Retrieves variable information from the scope chain.
|
|
76
105
|
* 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,gBAAgB,CAAA;AAC7G,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAItE;;;;;;;;;;;;;;;;;;;;;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
|
|
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,gBAAgB,CAAA;AAC7G,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AAItE;;;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;OAaG;IACI,mBAAmB,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAK/C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAoCzB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAUjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAgWZ;;;;;;;;OAQG;IACH,OAAO,CAAC,gCAAgC;IAqDxC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;;;;;;;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"}
|
|
@@ -59,7 +59,25 @@ export declare function runExtractor(config: I18nextToolkitConfig, options?: {
|
|
|
59
59
|
*/
|
|
60
60
|
export declare function processFile(file: string, plugins: Plugin[], astVisitors: ASTVisitors, pluginContext: PluginContext, config: Omit<I18nextToolkitConfig, 'plugins'>, logger?: Logger, fileErrors?: string[]): Promise<void>;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Lightweight pre-scan pass for a single file.
|
|
63
|
+
*
|
|
64
|
+
* Parses the file and calls `astVisitors.preScanForConstants()` to populate
|
|
65
|
+
* cross-file shared constant / type-alias / array tables WITHOUT extracting
|
|
66
|
+
* any translation keys or running plugin hooks.
|
|
67
|
+
*
|
|
68
|
+
* Intended to be called for ALL files in a first pass before `processFile` is
|
|
69
|
+
* called for any file, ensuring that exported identifier references such as
|
|
70
|
+
* `NS_CALENDAR` from `@core/translations/ns` are resolved in all files
|
|
71
|
+
* regardless of processing order.
|
|
72
|
+
*
|
|
73
|
+
* @param file - Absolute or CWD-relative path of the source file to pre-scan
|
|
74
|
+
* @param astVisitors - Shared visitor instance (holds the shared constant tables)
|
|
75
|
+
* @param config - Extractor configuration (without plugins)
|
|
76
|
+
* @param logger - Logger for warnings/errors
|
|
77
|
+
* @param fileErrors - Optional array to collect per-file error messages
|
|
78
|
+
*/
|
|
79
|
+
export declare function preScanFile(file: string, astVisitors: ASTVisitors, config: Omit<I18nextToolkitConfig, 'plugins'>, logger?: Logger, fileErrors?: string[]): Promise<void>;
|
|
80
|
+
/**
|
|
63
81
|
* Used primarily for testing and programmatic access.
|
|
64
82
|
*
|
|
65
83
|
* @param config - The i18next toolkit configuration object
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAO5G,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAK/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CACX,GACL,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CA6E1D;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,EACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAkHf;AAED
|
|
1
|
+
{"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/extractor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAO5G,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAK/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CACX,GACL,OAAO,CAAC;IAAE,cAAc,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CA6E1D;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,EACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAkHf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,GAAE,MAA4B,EACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,CAkDf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAO1K"}
|
|
@@ -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,EAAmB,MAAM,gBAAgB,CAAA;AAOjG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,EACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,
|
|
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,gBAAgB,CAAA;AAOjG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,EACpC,UAAU,CAAC,EAAE,MAAM,EAAE,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CAuF1E"}
|
|
@@ -64,6 +64,12 @@ export declare class ExpressionResolver {
|
|
|
64
64
|
* Returns undefined if the name is not a known string array.
|
|
65
65
|
*/
|
|
66
66
|
getVariableValues(name: string): string[] | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Return the as-const object map stored for a variable name.
|
|
69
|
+
* Returns undefined if the name is not a known object map.
|
|
70
|
+
* Checks per-file variableTable first, then sharedEnumTable (for enums).
|
|
71
|
+
*/
|
|
72
|
+
getObjectMap(name: string): Record<string, string> | undefined;
|
|
67
73
|
/**
|
|
68
74
|
* Capture a TypeScript enum declaration so members can be resolved later.
|
|
69
75
|
* 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,gBAAgB,CAAA;AAErD,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;
|
|
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,gBAAgB,CAAA;AAErD,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;;;;OAIG;IACI,YAAY,CAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAQtE;;;;;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;IAiH3C;;;;;;OAMG;IACH,OAAO,CAAC,6CAA6C;IAyBrD;;;;;;OAMG;IACH,OAAO,CAAC,kDAAkD;CAwB3D"}
|