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 CHANGED
@@ -1138,8 +1138,8 @@ const config: I18nextToolkitConfig = {
1138
1138
  };
1139
1139
 
1140
1140
  // Run the complete extraction process
1141
- const wasUpdated = await runExtractor(config);
1142
- console.log('Files updated:', wasUpdated);
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.44.0'); // This string is replaced with the actual version at build time by rollup
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 success = await extractor.runExtractor(config$1, {
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 && !success) {
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 && success) {
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
- return success;
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 { allKeys, objectKeys } = await keyFinder.findKeys(config, internalLogger);
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
- spinner.succeed(node_util.styleText('bold', 'Extraction complete!'));
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.44.0'); // This string is replaced with the actual version at build time by rollup
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 success = await runExtractor(config, {
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 && !success) {
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 && success) {
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
- return success;
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 { allKeys, objectKeys } = await findKeys(config, internalLogger);
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
- spinner.succeed(styleText('bold', 'Extraction complete!'));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.44.0",
3
+ "version": "1.45.0",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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;AAqR7B,OAAO,EAAE,OAAO,EAAE,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<boolean>;
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,CAkElB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,SAAS,CAAC,EAC7C,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,IAAI,CAAC,CA2Gf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,sDAO3I"}
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,GACnC,OAAO,CAAC;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC,CA6E1E"}
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"}