i18next-cli 1.43.0 → 1.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -8
- package/dist/cjs/cli.js +9 -6
- package/dist/cjs/extractor/core/extractor.js +10 -4
- package/dist/cjs/extractor/core/key-finder.js +2 -2
- package/dist/cjs/linter.js +88 -4
- package/dist/esm/cli.js +9 -6
- package/dist/esm/extractor/core/extractor.js +10 -4
- package/dist/esm/extractor/core/key-finder.js +2 -2
- package/dist/esm/linter.js +88 -4
- package/package.json +1 -1
- package/types/cli.d.ts.map +1 -1
- package/types/extractor/core/extractor.d.ts +5 -2
- package/types/extractor/core/extractor.d.ts.map +1 -1
- package/types/extractor/core/key-finder.d.ts +1 -1
- package/types/extractor/core/key-finder.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/linter.d.ts +12 -16
- package/types/linter.d.ts.map +1 -1
- package/types/types.d.ts +65 -5
- package/types/types.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ A unified, high-performance i18next CLI toolchain, powered by SWC.
|
|
|
40
40
|
- **Translation Status**: Get a high-level overview or a detailed, key-by-key report of your project's translation completeness.
|
|
41
41
|
- **Plugin System**: Extensible architecture for custom extraction patterns and file types (e.g., HTML, Handlebars).
|
|
42
42
|
- **Legacy Migration**: Automatic migration from `i18next-parser` configurations.
|
|
43
|
-
- **Cloud Integration**: Seamless integration with the [
|
|
43
|
+
- **Cloud Integration**: Seamless integration with the [Locize](https://locize.com) translation management platform.
|
|
44
44
|
|
|
45
45
|
## Installation
|
|
46
46
|
|
|
@@ -573,7 +573,7 @@ export default defineConfig({
|
|
|
573
573
|
|
|
574
574
|
### Plugin System
|
|
575
575
|
|
|
576
|
-
Create custom plugins to extend the capabilities of `i18next-cli`. The plugin system provides
|
|
576
|
+
Create custom plugins to extend the capabilities of `i18next-cli`. The plugin system provides hooks for both extraction and linting, with a single unified `plugins` array.
|
|
577
577
|
|
|
578
578
|
**Available Hooks:**
|
|
579
579
|
|
|
@@ -585,6 +585,63 @@ Create custom plugins to extend the capabilities of `i18next-cli`. The plugin sy
|
|
|
585
585
|
- `onEnd`: Runs after all JS/TS files have been parsed but *before* the final keys are compared with existing translation files. This is the ideal hook for parsing non-JavaScript files (like `.html`, `.vue`, or `.svelte`) and adding their keys to the collection.
|
|
586
586
|
- `afterSync`: Runs after the extractor has compared the found keys with your translation files and generated the final results. This is perfect for post-processing tasks, like generating a report of newly added keys.
|
|
587
587
|
|
|
588
|
+
**Lint Plugin Hooks:**
|
|
589
|
+
|
|
590
|
+
- `lintSetup(context)`: Runs once before linting starts. Receives `LintPluginContext` with `config` and `logger`.
|
|
591
|
+
- `lintExtensions`: Optional extension hint (for example `['.vue']`). Used as a skip hint/optimization.
|
|
592
|
+
- `lintOnLoad(code, filePath)`: Runs before lint parsing for each file.
|
|
593
|
+
- Return `string` to replace source code for linting.
|
|
594
|
+
- Return `undefined` to pass through unchanged.
|
|
595
|
+
- Return `null` to skip linting the file entirely.
|
|
596
|
+
- `lintOnResult(filePath, issues)`: Runs after each file is linted. Return a new issues array to filter/augment results, or `undefined` to keep as-is.
|
|
597
|
+
|
|
598
|
+
### Lint Plugin API
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
import type {
|
|
602
|
+
Plugin,
|
|
603
|
+
LinterPlugin,
|
|
604
|
+
LintPluginContext,
|
|
605
|
+
LintIssue,
|
|
606
|
+
} from 'i18next-cli';
|
|
607
|
+
|
|
608
|
+
// You can type your plugin as Plugin (full surface) or LinterPlugin (lint-focused)
|
|
609
|
+
export const vueLintPlugin = (): LinterPlugin => ({
|
|
610
|
+
name: 'vue-lint-plugin',
|
|
611
|
+
lintExtensions: ['.vue'],
|
|
612
|
+
lintSetup: async (context: LintPluginContext) => {
|
|
613
|
+
context.logger.info('vue lint plugin initialized');
|
|
614
|
+
},
|
|
615
|
+
lintOnLoad: async (code, filePath) => {
|
|
616
|
+
if (!filePath.endsWith('.vue')) return undefined;
|
|
617
|
+
// preprocess SFC/template to lintable JS/TS/JSX text
|
|
618
|
+
return code;
|
|
619
|
+
},
|
|
620
|
+
lintOnResult: async (_filePath, issues: LintIssue[]) => {
|
|
621
|
+
// Example: keep only interpolation issues
|
|
622
|
+
return issues.filter(issue => issue.type === 'interpolation');
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**Config usage (same plugins list for extract + lint):**
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { defineConfig } from 'i18next-cli';
|
|
631
|
+
import { vueLintPlugin } from './plugins/vue-lint-plugin.mjs';
|
|
632
|
+
|
|
633
|
+
export default defineConfig({
|
|
634
|
+
locales: ['en', 'de'],
|
|
635
|
+
extract: {
|
|
636
|
+
input: ['src/**/*.{ts,tsx,js,jsx,vue}'],
|
|
637
|
+
output: 'locales/{{language}}/{{namespace}}.json'
|
|
638
|
+
},
|
|
639
|
+
plugins: [
|
|
640
|
+
vueLintPlugin()
|
|
641
|
+
]
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
588
645
|
**Basic Plugin Example:**
|
|
589
646
|
|
|
590
647
|
```typescript
|
|
@@ -1081,8 +1138,8 @@ const config: I18nextToolkitConfig = {
|
|
|
1081
1138
|
};
|
|
1082
1139
|
|
|
1083
1140
|
// Run the complete extraction process
|
|
1084
|
-
const
|
|
1085
|
-
console.log('Files updated:',
|
|
1141
|
+
const { anyFileUpdated, hasErrors } = await runExtractor(config);
|
|
1142
|
+
console.log('Files updated:', anyFileUpdated);
|
|
1086
1143
|
|
|
1087
1144
|
// Check translation status programmatically
|
|
1088
1145
|
await runStatus(config);
|
|
@@ -1190,12 +1247,14 @@ This programmatic API gives you the same power as the CLI but with full control
|
|
|
1190
1247
|
|
|
1191
1248
|
---
|
|
1192
1249
|
|
|
1193
|
-
**From the creators of i18next: localization as a service - locize.com**
|
|
1250
|
+
**From the creators of i18next: localization as a service - [Locize](https://www.locize.com)**
|
|
1251
|
+
|
|
1252
|
+
A translation management system built around the i18next ecosystem - [Locize](https://locize.com).
|
|
1194
1253
|
|
|
1195
|
-
|
|
1254
|
+
**Now with a [Free plan](https://locize.com/pricing) for small projects!** Perfect for hobbyists or getting started.
|
|
1196
1255
|
|
|
1197
|
-

|
|
1198
1257
|
|
|
1199
|
-
With using [
|
|
1258
|
+
With using [Locize](https://www.locize.com/?utm_source=i18next_cli_readme&utm_medium=github) you directly support the future of i18next.
|
|
1200
1259
|
|
|
1201
1260
|
---
|
package/dist/cjs/cli.js
CHANGED
|
@@ -28,7 +28,7 @@ const program = new commander.Command();
|
|
|
28
28
|
program
|
|
29
29
|
.name('i18next-cli')
|
|
30
30
|
.description('A unified, high-performance i18next CLI.')
|
|
31
|
-
.version('1.
|
|
31
|
+
.version('1.45.0'); // This string is replaced with the actual version at build time by rollup
|
|
32
32
|
// new: global config override option
|
|
33
33
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
34
34
|
program
|
|
@@ -47,22 +47,25 @@ program
|
|
|
47
47
|
const runExtract = async () => {
|
|
48
48
|
// --sync-all implies sync-primary behavior
|
|
49
49
|
const syncPrimary = !!options.syncPrimary || !!options.syncAll;
|
|
50
|
-
const
|
|
50
|
+
const { anyFileUpdated, hasErrors } = await extractor.runExtractor(config$1, {
|
|
51
51
|
isWatchMode: !!options.watch,
|
|
52
52
|
isDryRun: !!options.dryRun,
|
|
53
53
|
syncPrimaryWithDefaults: syncPrimary,
|
|
54
54
|
syncAll: !!options.syncAll,
|
|
55
55
|
quiet: !!options.quiet
|
|
56
56
|
});
|
|
57
|
-
if (options.ci && !
|
|
57
|
+
if (options.ci && !anyFileUpdated) {
|
|
58
58
|
console.log('✅ No files were updated.');
|
|
59
|
-
process.exit(0);
|
|
59
|
+
process.exit(hasErrors ? 1 : 0);
|
|
60
60
|
}
|
|
61
|
-
else if (options.ci &&
|
|
61
|
+
else if (options.ci && anyFileUpdated) {
|
|
62
62
|
console.error('❌ Some files were updated. This should not happen in CI mode.');
|
|
63
63
|
process.exit(1);
|
|
64
64
|
}
|
|
65
|
-
|
|
65
|
+
if (hasErrors && !options.watch) {
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return anyFileUpdated;
|
|
66
69
|
};
|
|
67
70
|
// Run the extractor once initially
|
|
68
71
|
await runExtract();
|
|
@@ -52,7 +52,8 @@ async function runExtractor(config, options = {}) {
|
|
|
52
52
|
// Only pass logger to spinner if explicitly provided
|
|
53
53
|
const spinner = wrapOra.createSpinnerLike('Running i18next key extractor...\n', { quiet: !!options.quiet, logger: options.logger });
|
|
54
54
|
try {
|
|
55
|
-
const
|
|
55
|
+
const fileErrors = [];
|
|
56
|
+
const { allKeys, objectKeys } = await keyFinder.findKeys(config, internalLogger, fileErrors);
|
|
56
57
|
spinner.text = `Found ${allKeys.size} unique keys. Updating translation files...`;
|
|
57
58
|
const results = await translationManager.getTranslations(allKeys, objectKeys, config, {
|
|
58
59
|
syncPrimaryWithDefaults: options.syncPrimaryWithDefaults,
|
|
@@ -82,11 +83,14 @@ async function runExtractor(config, options = {}) {
|
|
|
82
83
|
await plugin.afterSync?.(results, config);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
+
const completionMessage = fileErrors.length > 0
|
|
87
|
+
? node_util.styleText('bold', `Extraction complete, but ignored ${fileErrors.length} file${fileErrors.length === 1 ? '' : 's'}!`)
|
|
88
|
+
: node_util.styleText('bold', 'Extraction complete!');
|
|
89
|
+
spinner.succeed(completionMessage);
|
|
86
90
|
// Show the funnel message only if files were actually changed.
|
|
87
91
|
if (anyFileUpdated)
|
|
88
92
|
await printLocizeFunnel(options.logger);
|
|
89
|
-
return anyFileUpdated;
|
|
93
|
+
return { anyFileUpdated, hasErrors: fileErrors.length > 0 };
|
|
90
94
|
}
|
|
91
95
|
catch (error) {
|
|
92
96
|
spinner.fail(node_util.styleText('red', 'Extraction failed.'));
|
|
@@ -114,7 +118,7 @@ async function runExtractor(config, options = {}) {
|
|
|
114
118
|
*
|
|
115
119
|
* @internal
|
|
116
120
|
*/
|
|
117
|
-
async function processFile(file, plugins, astVisitors, pluginContext, config, logger$1 = new logger.ConsoleLogger()) {
|
|
121
|
+
async function processFile(file, plugins, astVisitors, pluginContext, config, logger$1 = new logger.ConsoleLogger(), fileErrors) {
|
|
118
122
|
try {
|
|
119
123
|
let code = await promises.readFile(file, 'utf-8');
|
|
120
124
|
// Run onLoad hooks from plugins with error handling
|
|
@@ -215,6 +219,8 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
215
219
|
if ((!err?.message || String(err.message).trim() === '') && err?.stack) {
|
|
216
220
|
logger$1.warn(` ${String(err.stack)}`);
|
|
217
221
|
}
|
|
222
|
+
// Record the failure so callers can exit non-zero even though we continue extraction
|
|
223
|
+
fileErrors?.push(file);
|
|
218
224
|
}
|
|
219
225
|
}
|
|
220
226
|
/**
|
|
@@ -35,7 +35,7 @@ var expressionResolver = require('../parsers/expression-resolver.js');
|
|
|
35
35
|
* console.log(`Found ${keys.size} unique translation keys`)
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
|
-
async function findKeys(config, logger$1 = new logger.ConsoleLogger()) {
|
|
38
|
+
async function findKeys(config, logger$1 = new logger.ConsoleLogger(), fileErrors) {
|
|
39
39
|
const { plugins: pluginsOrUndefined, ...otherConfig } = config;
|
|
40
40
|
const plugins = pluginsOrUndefined || [];
|
|
41
41
|
const sourceFiles = await processSourceFiles(config);
|
|
@@ -88,7 +88,7 @@ async function findKeys(config, logger$1 = new logger.ConsoleLogger()) {
|
|
|
88
88
|
await pluginManager.initializePlugins(plugins);
|
|
89
89
|
// 6. Process each file
|
|
90
90
|
for (const file of sourceFiles) {
|
|
91
|
-
await extractor.processFile(file, plugins, astVisitors$1, pluginContext, otherConfig, logger$1);
|
|
91
|
+
await extractor.processFile(file, plugins, astVisitors$1, pluginContext, otherConfig, logger$1, fileErrors);
|
|
92
92
|
}
|
|
93
93
|
// 7. Run onEnd hooks
|
|
94
94
|
for (const plugin of plugins) {
|
package/dist/cjs/linter.js
CHANGED
|
@@ -254,9 +254,11 @@ const defaultIgnoredAttributes = ['className', 'key', 'id', 'style', 'href', 'i1
|
|
|
254
254
|
const defaultIgnoredTags = ['script', 'style', 'code'];
|
|
255
255
|
class Linter extends node_events.EventEmitter {
|
|
256
256
|
config;
|
|
257
|
-
|
|
257
|
+
logger;
|
|
258
|
+
constructor(config, logger$1 = new logger.ConsoleLogger()) {
|
|
258
259
|
super({ captureRejections: true });
|
|
259
260
|
this.config = config;
|
|
261
|
+
this.logger = logger$1;
|
|
260
262
|
}
|
|
261
263
|
wrapError(error) {
|
|
262
264
|
const prefix = 'Linter failed to run: ';
|
|
@@ -288,10 +290,19 @@ class Linter extends node_events.EventEmitter {
|
|
|
288
290
|
// Load translation values once so the interpolation linter can resolve lookup keys
|
|
289
291
|
// to their translated strings (fixes: key != value interpolation not detected)
|
|
290
292
|
const translationValues = await loadPrimaryTranslationValues(config);
|
|
293
|
+
const plugins = config.plugins || [];
|
|
294
|
+
const lintPluginContext = this.createLintPluginContext(config);
|
|
295
|
+
await this.initializeLintPlugins(plugins, lintPluginContext);
|
|
291
296
|
let totalIssues = 0;
|
|
292
297
|
const issuesByFile = new Map();
|
|
293
298
|
for (const file of sourceFiles) {
|
|
294
|
-
const
|
|
299
|
+
const sourceCode = await promises.readFile(file, 'utf-8');
|
|
300
|
+
const lintPrepared = await this.runLintOnLoadPipeline(sourceCode, file, plugins);
|
|
301
|
+
if (lintPrepared === null) {
|
|
302
|
+
this.emit('progress', { message: `Skipped ${file} by plugin` });
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const code = lintPrepared;
|
|
295
306
|
// Determine parser options from file extension so .ts is not parsed as TSX
|
|
296
307
|
const fileExt = node_path.extname(file).toLowerCase();
|
|
297
308
|
const isTypeScriptFile = fileExt === '.ts' || fileExt === '.tsx' || fileExt === '.mts' || fileExt === '.cts';
|
|
@@ -349,7 +360,8 @@ class Linter extends node_events.EventEmitter {
|
|
|
349
360
|
const hardcodedStrings = findHardcodedStrings(ast, code, config);
|
|
350
361
|
// Collect interpolation parameter issues
|
|
351
362
|
const interpolationIssues = lintInterpolationParams(ast, code, config, translationValues);
|
|
352
|
-
|
|
363
|
+
let allIssues = [...hardcodedStrings, ...interpolationIssues];
|
|
364
|
+
allIssues = await this.runLintOnResultPipeline(file, allIssues, plugins);
|
|
353
365
|
if (allIssues.length > 0) {
|
|
354
366
|
totalIssues += allIssues.length;
|
|
355
367
|
issuesByFile.set(file, allIssues);
|
|
@@ -366,6 +378,78 @@ class Linter extends node_events.EventEmitter {
|
|
|
366
378
|
throw wrappedError;
|
|
367
379
|
}
|
|
368
380
|
}
|
|
381
|
+
createLintPluginContext(config) {
|
|
382
|
+
return {
|
|
383
|
+
config,
|
|
384
|
+
logger: this.logger,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async initializeLintPlugins(plugins, context) {
|
|
388
|
+
for (const plugin of plugins) {
|
|
389
|
+
try {
|
|
390
|
+
await plugin.lintSetup?.(context);
|
|
391
|
+
}
|
|
392
|
+
catch (err) {
|
|
393
|
+
const wrapped = this.wrapError(err);
|
|
394
|
+
this.emit('error', wrapped);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
normalizeExtension(ext) {
|
|
399
|
+
const trimmed = ext.trim().toLowerCase();
|
|
400
|
+
if (!trimmed)
|
|
401
|
+
return '';
|
|
402
|
+
return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
|
|
403
|
+
}
|
|
404
|
+
shouldRunLintPluginForFile(plugin, filePath) {
|
|
405
|
+
const hints = plugin.lintExtensions;
|
|
406
|
+
if (!hints || hints.length === 0)
|
|
407
|
+
return true;
|
|
408
|
+
const fileExt = this.normalizeExtension(node_path.extname(filePath));
|
|
409
|
+
if (!fileExt)
|
|
410
|
+
return false;
|
|
411
|
+
const normalizedHints = hints.map(hint => this.normalizeExtension(hint)).filter(Boolean);
|
|
412
|
+
if (normalizedHints.length === 0)
|
|
413
|
+
return true;
|
|
414
|
+
return normalizedHints.includes(fileExt);
|
|
415
|
+
}
|
|
416
|
+
async runLintOnLoadPipeline(initialCode, filePath, plugins) {
|
|
417
|
+
let code = initialCode;
|
|
418
|
+
for (const plugin of plugins) {
|
|
419
|
+
if (!this.shouldRunLintPluginForFile(plugin, filePath))
|
|
420
|
+
continue;
|
|
421
|
+
try {
|
|
422
|
+
const result = await plugin.lintOnLoad?.(code, filePath);
|
|
423
|
+
if (result === null)
|
|
424
|
+
return null;
|
|
425
|
+
if (typeof result === 'string')
|
|
426
|
+
code = result;
|
|
427
|
+
}
|
|
428
|
+
catch (err) {
|
|
429
|
+
const wrapped = this.wrapError(err);
|
|
430
|
+
this.emit('error', wrapped);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return code;
|
|
434
|
+
}
|
|
435
|
+
async runLintOnResultPipeline(filePath, initialIssues, plugins) {
|
|
436
|
+
let issues = initialIssues;
|
|
437
|
+
for (const plugin of plugins) {
|
|
438
|
+
if (!this.shouldRunLintPluginForFile(plugin, filePath))
|
|
439
|
+
continue;
|
|
440
|
+
try {
|
|
441
|
+
const result = await plugin.lintOnResult?.(filePath, issues);
|
|
442
|
+
if (Array.isArray(result)) {
|
|
443
|
+
issues = result;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
const wrapped = this.wrapError(err);
|
|
448
|
+
this.emit('error', wrapped);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return issues;
|
|
452
|
+
}
|
|
369
453
|
}
|
|
370
454
|
/**
|
|
371
455
|
* Runs the i18next linter to detect hardcoded strings and other potential issues.
|
|
@@ -401,7 +485,7 @@ async function runLinter(config) {
|
|
|
401
485
|
}
|
|
402
486
|
async function runLinterCli(config, options = {}) {
|
|
403
487
|
const internalLogger = options.logger ?? new logger.ConsoleLogger();
|
|
404
|
-
const linter = new Linter(config);
|
|
488
|
+
const linter = new Linter(config, internalLogger);
|
|
405
489
|
const spinner = wrapOra.createSpinnerLike('', { quiet: !!options.quiet, logger: options.logger });
|
|
406
490
|
linter.on('progress', (event) => {
|
|
407
491
|
spinner.text = event.message;
|
package/dist/esm/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ const program = new Command();
|
|
|
26
26
|
program
|
|
27
27
|
.name('i18next-cli')
|
|
28
28
|
.description('A unified, high-performance i18next CLI.')
|
|
29
|
-
.version('1.
|
|
29
|
+
.version('1.45.0'); // This string is replaced with the actual version at build time by rollup
|
|
30
30
|
// new: global config override option
|
|
31
31
|
program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)');
|
|
32
32
|
program
|
|
@@ -45,22 +45,25 @@ program
|
|
|
45
45
|
const runExtract = async () => {
|
|
46
46
|
// --sync-all implies sync-primary behavior
|
|
47
47
|
const syncPrimary = !!options.syncPrimary || !!options.syncAll;
|
|
48
|
-
const
|
|
48
|
+
const { anyFileUpdated, hasErrors } = await runExtractor(config, {
|
|
49
49
|
isWatchMode: !!options.watch,
|
|
50
50
|
isDryRun: !!options.dryRun,
|
|
51
51
|
syncPrimaryWithDefaults: syncPrimary,
|
|
52
52
|
syncAll: !!options.syncAll,
|
|
53
53
|
quiet: !!options.quiet
|
|
54
54
|
});
|
|
55
|
-
if (options.ci && !
|
|
55
|
+
if (options.ci && !anyFileUpdated) {
|
|
56
56
|
console.log('✅ No files were updated.');
|
|
57
|
-
process.exit(0);
|
|
57
|
+
process.exit(hasErrors ? 1 : 0);
|
|
58
58
|
}
|
|
59
|
-
else if (options.ci &&
|
|
59
|
+
else if (options.ci && anyFileUpdated) {
|
|
60
60
|
console.error('❌ Some files were updated. This should not happen in CI mode.');
|
|
61
61
|
process.exit(1);
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
if (hasErrors && !options.watch) {
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
return anyFileUpdated;
|
|
64
67
|
};
|
|
65
68
|
// Run the extractor once initially
|
|
66
69
|
await runExtract();
|
|
@@ -50,7 +50,8 @@ async function runExtractor(config, options = {}) {
|
|
|
50
50
|
// Only pass logger to spinner if explicitly provided
|
|
51
51
|
const spinner = createSpinnerLike('Running i18next key extractor...\n', { quiet: !!options.quiet, logger: options.logger });
|
|
52
52
|
try {
|
|
53
|
-
const
|
|
53
|
+
const fileErrors = [];
|
|
54
|
+
const { allKeys, objectKeys } = await findKeys(config, internalLogger, fileErrors);
|
|
54
55
|
spinner.text = `Found ${allKeys.size} unique keys. Updating translation files...`;
|
|
55
56
|
const results = await getTranslations(allKeys, objectKeys, config, {
|
|
56
57
|
syncPrimaryWithDefaults: options.syncPrimaryWithDefaults,
|
|
@@ -80,11 +81,14 @@ async function runExtractor(config, options = {}) {
|
|
|
80
81
|
await plugin.afterSync?.(results, config);
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
|
-
|
|
84
|
+
const completionMessage = fileErrors.length > 0
|
|
85
|
+
? styleText('bold', `Extraction complete, but ignored ${fileErrors.length} file${fileErrors.length === 1 ? '' : 's'}!`)
|
|
86
|
+
: styleText('bold', 'Extraction complete!');
|
|
87
|
+
spinner.succeed(completionMessage);
|
|
84
88
|
// Show the funnel message only if files were actually changed.
|
|
85
89
|
if (anyFileUpdated)
|
|
86
90
|
await printLocizeFunnel(options.logger);
|
|
87
|
-
return anyFileUpdated;
|
|
91
|
+
return { anyFileUpdated, hasErrors: fileErrors.length > 0 };
|
|
88
92
|
}
|
|
89
93
|
catch (error) {
|
|
90
94
|
spinner.fail(styleText('red', 'Extraction failed.'));
|
|
@@ -112,7 +116,7 @@ async function runExtractor(config, options = {}) {
|
|
|
112
116
|
*
|
|
113
117
|
* @internal
|
|
114
118
|
*/
|
|
115
|
-
async function processFile(file, plugins, astVisitors, pluginContext, config, logger = new ConsoleLogger()) {
|
|
119
|
+
async function processFile(file, plugins, astVisitors, pluginContext, config, logger = new ConsoleLogger(), fileErrors) {
|
|
116
120
|
try {
|
|
117
121
|
let code = await readFile(file, 'utf-8');
|
|
118
122
|
// Run onLoad hooks from plugins with error handling
|
|
@@ -213,6 +217,8 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
|
|
|
213
217
|
if ((!err?.message || String(err.message).trim() === '') && err?.stack) {
|
|
214
218
|
logger.warn(` ${String(err.stack)}`);
|
|
215
219
|
}
|
|
220
|
+
// Record the failure so callers can exit non-zero even though we continue extraction
|
|
221
|
+
fileErrors?.push(file);
|
|
216
222
|
}
|
|
217
223
|
}
|
|
218
224
|
/**
|
|
@@ -33,7 +33,7 @@ import { ExpressionResolver } from '../parsers/expression-resolver.js';
|
|
|
33
33
|
* console.log(`Found ${keys.size} unique translation keys`)
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
async function findKeys(config, logger = new ConsoleLogger()) {
|
|
36
|
+
async function findKeys(config, logger = new ConsoleLogger(), fileErrors) {
|
|
37
37
|
const { plugins: pluginsOrUndefined, ...otherConfig } = config;
|
|
38
38
|
const plugins = pluginsOrUndefined || [];
|
|
39
39
|
const sourceFiles = await processSourceFiles(config);
|
|
@@ -86,7 +86,7 @@ async function findKeys(config, logger = new ConsoleLogger()) {
|
|
|
86
86
|
await initializePlugins(plugins);
|
|
87
87
|
// 6. Process each file
|
|
88
88
|
for (const file of sourceFiles) {
|
|
89
|
-
await processFile(file, plugins, astVisitors, pluginContext, otherConfig, logger);
|
|
89
|
+
await processFile(file, plugins, astVisitors, pluginContext, otherConfig, logger, fileErrors);
|
|
90
90
|
}
|
|
91
91
|
// 7. Run onEnd hooks
|
|
92
92
|
for (const plugin of plugins) {
|
package/dist/esm/linter.js
CHANGED
|
@@ -252,9 +252,11 @@ const defaultIgnoredAttributes = ['className', 'key', 'id', 'style', 'href', 'i1
|
|
|
252
252
|
const defaultIgnoredTags = ['script', 'style', 'code'];
|
|
253
253
|
class Linter extends EventEmitter {
|
|
254
254
|
config;
|
|
255
|
-
|
|
255
|
+
logger;
|
|
256
|
+
constructor(config, logger = new ConsoleLogger()) {
|
|
256
257
|
super({ captureRejections: true });
|
|
257
258
|
this.config = config;
|
|
259
|
+
this.logger = logger;
|
|
258
260
|
}
|
|
259
261
|
wrapError(error) {
|
|
260
262
|
const prefix = 'Linter failed to run: ';
|
|
@@ -286,10 +288,19 @@ class Linter extends EventEmitter {
|
|
|
286
288
|
// Load translation values once so the interpolation linter can resolve lookup keys
|
|
287
289
|
// to their translated strings (fixes: key != value interpolation not detected)
|
|
288
290
|
const translationValues = await loadPrimaryTranslationValues(config);
|
|
291
|
+
const plugins = config.plugins || [];
|
|
292
|
+
const lintPluginContext = this.createLintPluginContext(config);
|
|
293
|
+
await this.initializeLintPlugins(plugins, lintPluginContext);
|
|
289
294
|
let totalIssues = 0;
|
|
290
295
|
const issuesByFile = new Map();
|
|
291
296
|
for (const file of sourceFiles) {
|
|
292
|
-
const
|
|
297
|
+
const sourceCode = await readFile(file, 'utf-8');
|
|
298
|
+
const lintPrepared = await this.runLintOnLoadPipeline(sourceCode, file, plugins);
|
|
299
|
+
if (lintPrepared === null) {
|
|
300
|
+
this.emit('progress', { message: `Skipped ${file} by plugin` });
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const code = lintPrepared;
|
|
293
304
|
// Determine parser options from file extension so .ts is not parsed as TSX
|
|
294
305
|
const fileExt = extname(file).toLowerCase();
|
|
295
306
|
const isTypeScriptFile = fileExt === '.ts' || fileExt === '.tsx' || fileExt === '.mts' || fileExt === '.cts';
|
|
@@ -347,7 +358,8 @@ class Linter extends EventEmitter {
|
|
|
347
358
|
const hardcodedStrings = findHardcodedStrings(ast, code, config);
|
|
348
359
|
// Collect interpolation parameter issues
|
|
349
360
|
const interpolationIssues = lintInterpolationParams(ast, code, config, translationValues);
|
|
350
|
-
|
|
361
|
+
let allIssues = [...hardcodedStrings, ...interpolationIssues];
|
|
362
|
+
allIssues = await this.runLintOnResultPipeline(file, allIssues, plugins);
|
|
351
363
|
if (allIssues.length > 0) {
|
|
352
364
|
totalIssues += allIssues.length;
|
|
353
365
|
issuesByFile.set(file, allIssues);
|
|
@@ -364,6 +376,78 @@ class Linter extends EventEmitter {
|
|
|
364
376
|
throw wrappedError;
|
|
365
377
|
}
|
|
366
378
|
}
|
|
379
|
+
createLintPluginContext(config) {
|
|
380
|
+
return {
|
|
381
|
+
config,
|
|
382
|
+
logger: this.logger,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
async initializeLintPlugins(plugins, context) {
|
|
386
|
+
for (const plugin of plugins) {
|
|
387
|
+
try {
|
|
388
|
+
await plugin.lintSetup?.(context);
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
const wrapped = this.wrapError(err);
|
|
392
|
+
this.emit('error', wrapped);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
normalizeExtension(ext) {
|
|
397
|
+
const trimmed = ext.trim().toLowerCase();
|
|
398
|
+
if (!trimmed)
|
|
399
|
+
return '';
|
|
400
|
+
return trimmed.startsWith('.') ? trimmed : `.${trimmed}`;
|
|
401
|
+
}
|
|
402
|
+
shouldRunLintPluginForFile(plugin, filePath) {
|
|
403
|
+
const hints = plugin.lintExtensions;
|
|
404
|
+
if (!hints || hints.length === 0)
|
|
405
|
+
return true;
|
|
406
|
+
const fileExt = this.normalizeExtension(extname(filePath));
|
|
407
|
+
if (!fileExt)
|
|
408
|
+
return false;
|
|
409
|
+
const normalizedHints = hints.map(hint => this.normalizeExtension(hint)).filter(Boolean);
|
|
410
|
+
if (normalizedHints.length === 0)
|
|
411
|
+
return true;
|
|
412
|
+
return normalizedHints.includes(fileExt);
|
|
413
|
+
}
|
|
414
|
+
async runLintOnLoadPipeline(initialCode, filePath, plugins) {
|
|
415
|
+
let code = initialCode;
|
|
416
|
+
for (const plugin of plugins) {
|
|
417
|
+
if (!this.shouldRunLintPluginForFile(plugin, filePath))
|
|
418
|
+
continue;
|
|
419
|
+
try {
|
|
420
|
+
const result = await plugin.lintOnLoad?.(code, filePath);
|
|
421
|
+
if (result === null)
|
|
422
|
+
return null;
|
|
423
|
+
if (typeof result === 'string')
|
|
424
|
+
code = result;
|
|
425
|
+
}
|
|
426
|
+
catch (err) {
|
|
427
|
+
const wrapped = this.wrapError(err);
|
|
428
|
+
this.emit('error', wrapped);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return code;
|
|
432
|
+
}
|
|
433
|
+
async runLintOnResultPipeline(filePath, initialIssues, plugins) {
|
|
434
|
+
let issues = initialIssues;
|
|
435
|
+
for (const plugin of plugins) {
|
|
436
|
+
if (!this.shouldRunLintPluginForFile(plugin, filePath))
|
|
437
|
+
continue;
|
|
438
|
+
try {
|
|
439
|
+
const result = await plugin.lintOnResult?.(filePath, issues);
|
|
440
|
+
if (Array.isArray(result)) {
|
|
441
|
+
issues = result;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
const wrapped = this.wrapError(err);
|
|
446
|
+
this.emit('error', wrapped);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return issues;
|
|
450
|
+
}
|
|
367
451
|
}
|
|
368
452
|
/**
|
|
369
453
|
* Runs the i18next linter to detect hardcoded strings and other potential issues.
|
|
@@ -399,7 +483,7 @@ async function runLinter(config) {
|
|
|
399
483
|
}
|
|
400
484
|
async function runLinterCli(config, options = {}) {
|
|
401
485
|
const internalLogger = options.logger ?? new ConsoleLogger();
|
|
402
|
-
const linter = new Linter(config);
|
|
486
|
+
const linter = new Linter(config, internalLogger);
|
|
403
487
|
const spinner = createSpinnerLike('', { quiet: !!options.quiet, logger: options.logger });
|
|
404
488
|
linter.on('progress', (event) => {
|
|
405
489
|
spinner.text = event.message;
|
package/package.json
CHANGED
package/types/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkBnC,QAAA,MAAM,OAAO,SAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkBnC,QAAA,MAAM,OAAO,SAAgB,CAAA;AAyR7B,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
|
@@ -33,7 +33,10 @@ export declare function runExtractor(config: I18nextToolkitConfig, options?: {
|
|
|
33
33
|
syncAll?: boolean;
|
|
34
34
|
quiet?: boolean;
|
|
35
35
|
logger?: Logger;
|
|
36
|
-
}): Promise<
|
|
36
|
+
}): Promise<{
|
|
37
|
+
anyFileUpdated: boolean;
|
|
38
|
+
hasErrors: boolean;
|
|
39
|
+
}>;
|
|
37
40
|
/**
|
|
38
41
|
* Processes an individual source file for translation key extraction.
|
|
39
42
|
*
|
|
@@ -54,7 +57,7 @@ export declare function runExtractor(config: I18nextToolkitConfig, options?: {
|
|
|
54
57
|
*
|
|
55
58
|
* @internal
|
|
56
59
|
*/
|
|
57
|
-
export declare function processFile(file: string, plugins: Plugin[], astVisitors: ASTVisitors, pluginContext: PluginContext, config: Omit<I18nextToolkitConfig, 'plugins'>, logger?: Logger): Promise<void>;
|
|
60
|
+
export declare function processFile(file: string, plugins: Plugin[], astVisitors: ASTVisitors, pluginContext: PluginContext, config: Omit<I18nextToolkitConfig, 'plugins'>, logger?: Logger, fileErrors?: string[]): Promise<void>;
|
|
58
61
|
/**
|
|
59
62
|
* Simplified extraction function that returns translation results without file writing.
|
|
60
63
|
* Used primarily for testing and programmatic access.
|
|
@@ -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,MAAM,aAAa,CAAA;AAMtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;;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,OAAO,CAAC,
|
|
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,MAAM,aAAa,CAAA;AAMtF,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAK5C;;;;;;;;;;;;;;;;;;;;;;;;;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,CAsE1D;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,CA8Gf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,sDAO3I"}
|
|
@@ -27,7 +27,7 @@ import type { ExtractedKey, Logger, I18nextToolkitConfig } from '../../types';
|
|
|
27
27
|
* console.log(`Found ${keys.size} unique translation keys`)
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
|
-
export declare function findKeys(config: I18nextToolkitConfig, logger?: Logger): Promise<{
|
|
30
|
+
export declare function findKeys(config: I18nextToolkitConfig, logger?: Logger, fileErrors?: string[]): Promise<{
|
|
31
31
|
allKeys: Map<string, ExtractedKey>;
|
|
32
32
|
objectKeys: Set<string>;
|
|
33
33
|
}>;
|
|
@@ -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,aAAa,CAAA;AAO9F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,oBAAoB,EAC5B,MAAM,GAAE,MAA4B,
|
|
1
|
+
{"version":3,"file":"key-finder.d.ts","sourceRoot":"","sources":["../../../src/extractor/core/key-finder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAmB,MAAM,aAAa,CAAA;AAO9F;;;;;;;;;;;;;;;;;;;;;;;;;;;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,CA6E1E"}
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { I18nextToolkitConfig, Plugin, PluginContext, ExtractedKey, TranslationResult, ExtractedKeysMap, RenameKeyResult, Logger, } from './types';
|
|
1
|
+
export type { I18nextToolkitConfig, Plugin, LinterPlugin, LintPluginContext, LintIssue, PluginContext, ExtractedKey, TranslationResult, ExtractedKeysMap, RenameKeyResult, Logger, } from './types';
|
|
2
2
|
export { defineConfig } from './config';
|
|
3
3
|
export { extract, findKeys, getTranslations, runExtractor } from './extractor';
|
|
4
4
|
export { runLinter, recommendedAcceptedTags, recommendedAcceptedAttributes } from './linter';
|
package/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,MAAM,EACN,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,MAAM,GACP,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EACL,OAAO,EACP,QAAQ,EACR,eAAe,EACf,YAAY,EACb,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,oBAAoB,EACpB,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,MAAM,GACP,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EACL,OAAO,EACP,QAAQ,EACR,eAAe,EACf,YAAY,EACb,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA"}
|
package/types/linter.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import type { I18nextToolkitConfig, Logger } from './types';
|
|
2
|
+
import type { I18nextToolkitConfig, Logger, LintIssue } from './types';
|
|
3
3
|
type LinterEventMap = {
|
|
4
4
|
progress: [
|
|
5
5
|
{
|
|
@@ -10,7 +10,7 @@ type LinterEventMap = {
|
|
|
10
10
|
{
|
|
11
11
|
success: boolean;
|
|
12
12
|
message: string;
|
|
13
|
-
files: Record<string,
|
|
13
|
+
files: Record<string, LintIssue[]>;
|
|
14
14
|
}
|
|
15
15
|
];
|
|
16
16
|
error: [error: Error];
|
|
@@ -19,15 +19,22 @@ export declare const recommendedAcceptedTags: string[];
|
|
|
19
19
|
export declare const recommendedAcceptedAttributes: string[];
|
|
20
20
|
export declare class Linter extends EventEmitter<LinterEventMap> {
|
|
21
21
|
private config;
|
|
22
|
-
|
|
22
|
+
private logger;
|
|
23
|
+
constructor(config: I18nextToolkitConfig, logger?: Logger);
|
|
23
24
|
wrapError(error: unknown): Error;
|
|
24
25
|
run(): Promise<{
|
|
25
26
|
success: boolean;
|
|
26
27
|
message: string;
|
|
27
28
|
files: {
|
|
28
|
-
[k: string]:
|
|
29
|
+
[k: string]: LintIssue[];
|
|
29
30
|
};
|
|
30
31
|
}>;
|
|
32
|
+
private createLintPluginContext;
|
|
33
|
+
private initializeLintPlugins;
|
|
34
|
+
private normalizeExtension;
|
|
35
|
+
private shouldRunLintPluginForFile;
|
|
36
|
+
private runLintOnLoadPipeline;
|
|
37
|
+
private runLintOnResultPipeline;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
33
40
|
* Runs the i18next linter to detect hardcoded strings and other potential issues.
|
|
@@ -62,23 +69,12 @@ export declare function runLinter(config: I18nextToolkitConfig): Promise<{
|
|
|
62
69
|
success: boolean;
|
|
63
70
|
message: string;
|
|
64
71
|
files: {
|
|
65
|
-
[k: string]:
|
|
72
|
+
[k: string]: LintIssue[];
|
|
66
73
|
};
|
|
67
74
|
}>;
|
|
68
75
|
export declare function runLinterCli(config: I18nextToolkitConfig, options?: {
|
|
69
76
|
quiet?: boolean;
|
|
70
77
|
logger?: Logger;
|
|
71
78
|
}): Promise<void>;
|
|
72
|
-
/**
|
|
73
|
-
* Represents a found hardcoded string or interpolation parameter error with its location information.
|
|
74
|
-
*/
|
|
75
|
-
interface HardcodedString {
|
|
76
|
-
/** The hardcoded text content or error message */
|
|
77
|
-
text: string;
|
|
78
|
-
/** Line number where the string or error was found */
|
|
79
|
-
line: number;
|
|
80
|
-
/** The type of issue: 'hardcoded' for hardcoded strings, 'interpolation' for interpolation parameter errors */
|
|
81
|
-
type?: 'hardcoded' | 'interpolation';
|
|
82
|
-
}
|
|
83
79
|
export {};
|
|
84
80
|
//# sourceMappingURL=linter.d.ts.map
|
package/types/linter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAA6B,MAAM,SAAS,CAAA;AAwPjG,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE;QAAC;YACT,OAAO,EAAE,MAAM,CAAC;SACjB;KAAC,CAAC;IACH,IAAI,EAAE;QAAC;YACL,OAAO,EAAE,OAAO,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;SACpC;KAAC,CAAC;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;CACvB,CAAA;AAED,eAAO,MAAM,uBAAuB,UAET,CAAA;AAC3B,eAAO,MAAM,6BAA6B,UAAgN,CAAA;AAK1P,qBAAa,MAAO,SAAQ,YAAY,CAAC,cAAc,CAAC;IACtD,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,MAAM,CAAQ;gBAET,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAE,MAA4B;IAM/E,SAAS,CAAE,KAAK,EAAE,OAAO;IAanB,GAAG;;;;;;;IA2GT,OAAO,CAAC,uBAAuB;YAOjB,qBAAqB;IAWnC,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,0BAA0B;YAUpB,qBAAqB;YAgBrB,uBAAuB;CAgBtC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,SAAS,CAAE,MAAM,EAAE,oBAAoB;;;;;;GAE5D;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,oBAAoB,EAC5B,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,iBAkCnD"}
|
package/types/types.d.ts
CHANGED
|
@@ -223,6 +223,65 @@ export interface I18nextToolkitConfig {
|
|
|
223
223
|
dryRun?: boolean;
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
227
|
+
/**
|
|
228
|
+
* Represents a linter issue reported for a source file.
|
|
229
|
+
*/
|
|
230
|
+
export interface LintIssue {
|
|
231
|
+
/** The hardcoded text content or interpolation error message */
|
|
232
|
+
text: string;
|
|
233
|
+
/** Line number where the issue was found */
|
|
234
|
+
line: number;
|
|
235
|
+
/** Issue category */
|
|
236
|
+
type?: 'hardcoded' | 'interpolation';
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Context object provided to linter plugin hooks.
|
|
240
|
+
*/
|
|
241
|
+
export interface LintPluginContext {
|
|
242
|
+
/** The fully resolved i18next-cli configuration. */
|
|
243
|
+
config: I18nextToolkitConfig;
|
|
244
|
+
/** The logger instance used by the linter run. */
|
|
245
|
+
logger: Logger;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Linter-specific plugin hooks.
|
|
249
|
+
*
|
|
250
|
+
* This interface is kept separate so lint capabilities can evolve
|
|
251
|
+
* without coupling extractor hook definitions.
|
|
252
|
+
*/
|
|
253
|
+
export interface LinterPlugin {
|
|
254
|
+
/** Unique name for the plugin */
|
|
255
|
+
name: string;
|
|
256
|
+
/**
|
|
257
|
+
* Optional file-extension hint for lint-only hooks (optimization only).
|
|
258
|
+
*
|
|
259
|
+
* Examples: ['.vue'], ['vue', 'svelte']
|
|
260
|
+
*/
|
|
261
|
+
lintExtensions?: string[];
|
|
262
|
+
/**
|
|
263
|
+
* Hook called once at the beginning of the linting process.
|
|
264
|
+
* Use for initialization required by lint hooks.
|
|
265
|
+
*/
|
|
266
|
+
lintSetup?: (context: LintPluginContext) => MaybePromise<void>;
|
|
267
|
+
/**
|
|
268
|
+
* Hook called for each source file before lint parsing.
|
|
269
|
+
*
|
|
270
|
+
* Return semantics in lint pipeline:
|
|
271
|
+
* - string: use transformed code
|
|
272
|
+
* - undefined: pass through unchanged
|
|
273
|
+
* - null: skip linting this file entirely
|
|
274
|
+
*/
|
|
275
|
+
lintOnLoad?: (code: string, filePath: string) => MaybePromise<string | null | undefined>;
|
|
276
|
+
/**
|
|
277
|
+
* Hook called after linting one file, allowing issue post-processing.
|
|
278
|
+
*
|
|
279
|
+
* Return semantics:
|
|
280
|
+
* - LintIssue[]: replace issues for this file
|
|
281
|
+
* - undefined: keep issues unchanged
|
|
282
|
+
*/
|
|
283
|
+
lintOnResult?: (filePath: string, issues: LintIssue[]) => MaybePromise<LintIssue[] | undefined>;
|
|
284
|
+
}
|
|
226
285
|
/**
|
|
227
286
|
* Plugin interface for extending the i18next toolkit functionality.
|
|
228
287
|
* Plugins can hook into various stages of the extraction process.
|
|
@@ -254,7 +313,7 @@ export interface I18nextToolkitConfig {
|
|
|
254
313
|
* })
|
|
255
314
|
* ```
|
|
256
315
|
*/
|
|
257
|
-
export interface Plugin {
|
|
316
|
+
export interface Plugin extends LinterPlugin {
|
|
258
317
|
/** Unique name for the plugin */
|
|
259
318
|
name: string;
|
|
260
319
|
/**
|
|
@@ -285,7 +344,7 @@ export interface Plugin {
|
|
|
285
344
|
* Hook called once at the beginning of the extraction process.
|
|
286
345
|
* Use for initialization tasks like setting up resources or validating configuration.
|
|
287
346
|
*/
|
|
288
|
-
setup?: () =>
|
|
347
|
+
setup?: () => MaybePromise<void>;
|
|
289
348
|
/**
|
|
290
349
|
* Hook called for each source file before it's parsed.
|
|
291
350
|
* Allows transformation of source code before AST generation.
|
|
@@ -294,7 +353,7 @@ export interface Plugin {
|
|
|
294
353
|
* @param path - The file path being processed
|
|
295
354
|
* @returns The transformed code (or undefined to keep original)
|
|
296
355
|
*/
|
|
297
|
-
onLoad?: (code: string, path: string) =>
|
|
356
|
+
onLoad?: (code: string, path: string) => MaybePromise<string>;
|
|
298
357
|
/**
|
|
299
358
|
* Hook called for each AST node during traversal.
|
|
300
359
|
* Enables custom extraction logic by examining syntax nodes.
|
|
@@ -309,7 +368,7 @@ export interface Plugin {
|
|
|
309
368
|
*
|
|
310
369
|
* @param keys - Final map of all extracted keys
|
|
311
370
|
*/
|
|
312
|
-
onEnd?: (keys: ExtractedKeysMap) =>
|
|
371
|
+
onEnd?: (keys: ExtractedKeysMap) => MaybePromise<void>;
|
|
313
372
|
/**
|
|
314
373
|
* Hook called after all files have been processed and translation files have been generated.
|
|
315
374
|
* Useful for post-processing, validation, or reporting based on the final results.
|
|
@@ -317,7 +376,7 @@ export interface Plugin {
|
|
|
317
376
|
* @param results - Array of translation results with update status and content.
|
|
318
377
|
* @param config - The i18next toolkit configuration object.
|
|
319
378
|
*/
|
|
320
|
-
afterSync?: (results: TranslationResult[], config: I18nextToolkitConfig) =>
|
|
379
|
+
afterSync?: (results: TranslationResult[], config: I18nextToolkitConfig) => MaybePromise<void>;
|
|
321
380
|
}
|
|
322
381
|
/**
|
|
323
382
|
* Represents an extracted translation key with its metadata.
|
|
@@ -611,4 +670,5 @@ export interface RenameKeyResult {
|
|
|
611
670
|
conflicts?: string[];
|
|
612
671
|
error?: string;
|
|
613
672
|
}
|
|
673
|
+
export {};
|
|
614
674
|
//# sourceMappingURL=types.d.ts.map
|
package/types/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEnE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,2DAA2D;IAC3D,OAAO,EAAE;QACP,oEAAoE;QACpE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAEzB,4DAA4D;QAC5D,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAE3B,mGAAmG;QACnG,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;QAEpE;;;WAGG;QACH,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAE3B,gGAAgG;QAChG,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAE5B,uEAAuE;QACvE,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAErC,8EAA8E;QAC9E,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;QAEpC,oDAAoD;QACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAE1B,mDAAmD;QACnD,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,+EAA+E;QAC/E,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QAErB,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAE3B;;;;;WAKG;QACH,mBAAmB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;YACnC,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC,CAAC;QAEH,kFAAkF;QAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE7B,kGAAkG;QAClG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAEvB;;;;;WAKG;QACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE9B;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QAExB,8FAA8F;QAC9F,0BAA0B,CAAC,EAAE,MAAM,EAAE,CAAC;QAEtC,wFAAwF;QACxF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE5B;;;;;WAKG;QACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAElC,2HAA2H;QAC3H,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,KAAK,MAAM,CAAC,CAAC;QAEhE,yDAAyD;QACzD,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;QAE9B,2EAA2E;QAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;QAEtG,4EAA4E;QAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;QAEzB,0DAA0D;QAC1D,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE9B;;;;;;;;;WASG;QACH,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;QAEpF;;;;;WAKG;QACH,eAAe,CAAC,EAAE,OAAO,CAAC;QAE1B,kHAAkH;QAClH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAE3B;;;;;WAKG;QACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE5B;;;WAGG;QACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAG9B,uBAAuB,CAAC,EAAE,OAAO,CAAA;QAGjC,cAAc,CAAC,EAAE,OAAO,CAAA;QAExB;;;WAGG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;QAEvB;;;WAGG;QACH,aAAa,CAAC,EAAE,MAAM,CAAC;QAEvB;;;WAGG;QACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;QAEjC;;;WAGG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAE7B;;;WAGG;QACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IAEF,uCAAuC;IACvC,IAAI,CAAC,EAAE;QACL,kFAAkF;QAClF,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE7B,kGAAkG;QAClG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QAEvB;;;;;WAKG;QACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;QAE9B;;;;;WAKG;QACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QAExB,oGAAoG;QACpG,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAE3B,6FAA6F;QAC7F,wBAAwB,CAAC,EAAE,OAAO,CAAC;KACpC,CAAC;IAEF,2DAA2D;IAC3D,KAAK,CAAC,EAAE;QACN,mEAAmE;QACnE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAEzB,0DAA0D;QAC1D,MAAM,EAAE,MAAM,CAAC;QAEf,8EAA8E;QAC9E,cAAc,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;QAEtC,qDAAqD;QACrD,aAAa,CAAC,EAAE,MAAM,CAAC;QAEvB;;;WAGG;QACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KAC/B,CAAC;IAEF,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE;QACP,wBAAwB;QACxB,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,gEAAgE;QAChE,MAAM,CAAC,EAAE,MAAM,CAAC;QAEhB,+CAA+C;QAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB,8DAA8D;QAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;QAEvB,8CAA8C;QAC9C,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAE7B,8CAA8C;QAC9C,uBAAuB,CAAC,EAAE,OAAO,CAAC;QAElC,6GAA6G;QAC7G,OAAO,CAAC,EAAE,UAAU,GAAG,KAAK,CAAC;QAE7B,0CAA0C;QAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|