i18next-cli 1.44.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 +2 -2
- 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/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/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/README.md
CHANGED
|
@@ -1138,8 +1138,8 @@ const config: I18nextToolkitConfig = {
|
|
|
1138
1138
|
};
|
|
1139
1139
|
|
|
1140
1140
|
// Run the complete extraction process
|
|
1141
|
-
const
|
|
1142
|
-
console.log('Files updated:',
|
|
1141
|
+
const { anyFileUpdated, hasErrors } = await runExtractor(config);
|
|
1142
|
+
console.log('Files updated:', anyFileUpdated);
|
|
1143
1143
|
|
|
1144
1144
|
// Check translation status programmatically
|
|
1145
1145
|
await runStatus(config);
|
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/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/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"}
|