i18next-cli 1.42.2 → 1.42.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/cli.js CHANGED
@@ -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.42.2'); // This string is replaced with the actual version at build time by rollup
31
+ .version('1.42.4'); // 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
@@ -9,6 +9,7 @@ var keyFinder = require('./key-finder.js');
9
9
  var translationManager = require('./translation-manager.js');
10
10
  var validation = require('../../utils/validation.js');
11
11
  var commentParser = require('../parsers/comment-parser.js');
12
+ var astUtils = require('../parsers/ast-utils.js');
12
13
  var logger = require('../../utils/logger.js');
13
14
  var fileUtils = require('../../utils/file-utils.js');
14
15
  var funnelMsgTracker = require('../../utils/funnel-msg-tracker.js');
@@ -182,6 +183,10 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
182
183
  throw new validation.ExtractorError('Failed to process file', file, err);
183
184
  }
184
185
  }
186
+ // Normalize SWC span offsets so every span is file-relative.
187
+ // SWC accumulates byte offsets across successive parse() calls,
188
+ // so without this, plugins would see positions beyond the file length.
189
+ astUtils.normalizeASTSpans(ast, ast.span.start);
185
190
  // "Wire up" the visitor's scope method to the context.
186
191
  // This avoids a circular dependency while giving plugins access to the scope.
187
192
  pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors);
@@ -1,5 +1,43 @@
1
1
  'use strict';
2
2
 
3
+ /**
4
+ * Recursively normalizes all SWC span offsets in an AST by subtracting a base
5
+ * offset. SWC's `parse()` accumulates byte offsets across successive calls in
6
+ * the same process, so `span.start`/`span.end` values can exceed the length of
7
+ * the source file. Call this once on the root `Module` node right after parsing
8
+ * to make every span file-relative.
9
+ *
10
+ * @param node - Any AST node (or the root Module)
11
+ * @param base - The base offset to subtract (`ast.span.start`)
12
+ */
13
+ function normalizeASTSpans(node, base) {
14
+ if (!node || typeof node !== 'object' || base === 0)
15
+ return;
16
+ // Normalize this node's own span
17
+ if (node.span && typeof node.span.start === 'number') {
18
+ node.span = {
19
+ ...node.span,
20
+ start: node.span.start - base,
21
+ end: node.span.end - base
22
+ };
23
+ }
24
+ // Recurse into every property (skip span itself to avoid double-processing)
25
+ for (const key of Object.keys(node)) {
26
+ if (key === 'span')
27
+ continue;
28
+ const child = node[key];
29
+ if (Array.isArray(child)) {
30
+ for (const item of child) {
31
+ if (item && typeof item === 'object') {
32
+ normalizeASTSpans(item, base);
33
+ }
34
+ }
35
+ }
36
+ else if (child && typeof child === 'object') {
37
+ normalizeASTSpans(child, base);
38
+ }
39
+ }
40
+ }
3
41
  /**
4
42
  * Finds and returns the full property node (KeyValueProperty) for the given
5
43
  * property name from an ObjectExpression.
@@ -83,3 +121,4 @@ exports.getObjectPropValue = getObjectPropValue;
83
121
  exports.getObjectPropValueExpression = getObjectPropValueExpression;
84
122
  exports.getObjectProperty = getObjectProperty;
85
123
  exports.isSimpleTemplateLiteral = isSimpleTemplateLiteral;
124
+ exports.normalizeASTSpans = normalizeASTSpans;
@@ -119,8 +119,11 @@ function lintInterpolationParams(ast, code, config) {
119
119
  const issueLineNumber = position > -1 ? getLineNumber(position) : 1;
120
120
  // Only check for unused parameters if there is at least one interpolation key in the string
121
121
  if (keys.length > 0) {
122
+ // i18next supports nested object access via dot notation (e.g. {{author.name}} with { author }).
123
+ // For each interpolation key, check if the root (part before the first dot) matches a provided param.
122
124
  for (const k of keys) {
123
- if (!paramKeys.includes(k)) {
125
+ const root = k.split('.')[0];
126
+ if (!paramKeys.includes(k) && !paramKeys.includes(root)) {
124
127
  issues.push({
125
128
  text: `Interpolation parameter "${k}" was not provided`,
126
129
  line: issueLineNumber,
@@ -128,8 +131,10 @@ function lintInterpolationParams(ast, code, config) {
128
131
  });
129
132
  }
130
133
  }
134
+ // For each provided param, check if it is used either directly or as the root of a dotted key.
131
135
  for (const pk of paramKeys) {
132
- if (!keys.includes(pk)) {
136
+ const isUsed = keys.some(k => k === pk || k.split('.')[0] === pk);
137
+ if (!isUsed) {
133
138
  issues.push({
134
139
  text: `Parameter "${pk}" is not used in translation string`,
135
140
  line: issueLineNumber,
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.42.2'); // This string is replaced with the actual version at build time by rollup
29
+ .version('1.42.4'); // 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
@@ -7,6 +7,7 @@ import { findKeys } from './key-finder.js';
7
7
  import { getTranslations } from './translation-manager.js';
8
8
  import { validateExtractorConfig, ExtractorError } from '../../utils/validation.js';
9
9
  import { extractKeysFromComments } from '../parsers/comment-parser.js';
10
+ import { normalizeASTSpans } from '../parsers/ast-utils.js';
10
11
  import { ConsoleLogger } from '../../utils/logger.js';
11
12
  import { inferFormatFromPath, loadRawJson5Content, serializeTranslationFile } from '../../utils/file-utils.js';
12
13
  import { shouldShowFunnel, recordFunnelShown } from '../../utils/funnel-msg-tracker.js';
@@ -180,6 +181,10 @@ async function processFile(file, plugins, astVisitors, pluginContext, config, lo
180
181
  throw new ExtractorError('Failed to process file', file, err);
181
182
  }
182
183
  }
184
+ // Normalize SWC span offsets so every span is file-relative.
185
+ // SWC accumulates byte offsets across successive parse() calls,
186
+ // so without this, plugins would see positions beyond the file length.
187
+ normalizeASTSpans(ast, ast.span.start);
183
188
  // "Wire up" the visitor's scope method to the context.
184
189
  // This avoids a circular dependency while giving plugins access to the scope.
185
190
  pluginContext.getVarFromScope = astVisitors.getVarFromScope.bind(astVisitors);
@@ -1,3 +1,41 @@
1
+ /**
2
+ * Recursively normalizes all SWC span offsets in an AST by subtracting a base
3
+ * offset. SWC's `parse()` accumulates byte offsets across successive calls in
4
+ * the same process, so `span.start`/`span.end` values can exceed the length of
5
+ * the source file. Call this once on the root `Module` node right after parsing
6
+ * to make every span file-relative.
7
+ *
8
+ * @param node - Any AST node (or the root Module)
9
+ * @param base - The base offset to subtract (`ast.span.start`)
10
+ */
11
+ function normalizeASTSpans(node, base) {
12
+ if (!node || typeof node !== 'object' || base === 0)
13
+ return;
14
+ // Normalize this node's own span
15
+ if (node.span && typeof node.span.start === 'number') {
16
+ node.span = {
17
+ ...node.span,
18
+ start: node.span.start - base,
19
+ end: node.span.end - base
20
+ };
21
+ }
22
+ // Recurse into every property (skip span itself to avoid double-processing)
23
+ for (const key of Object.keys(node)) {
24
+ if (key === 'span')
25
+ continue;
26
+ const child = node[key];
27
+ if (Array.isArray(child)) {
28
+ for (const item of child) {
29
+ if (item && typeof item === 'object') {
30
+ normalizeASTSpans(item, base);
31
+ }
32
+ }
33
+ }
34
+ else if (child && typeof child === 'object') {
35
+ normalizeASTSpans(child, base);
36
+ }
37
+ }
38
+ }
1
39
  /**
2
40
  * Finds and returns the full property node (KeyValueProperty) for the given
3
41
  * property name from an ObjectExpression.
@@ -77,4 +115,4 @@ function getObjectPropValue(object, propName) {
77
115
  return undefined;
78
116
  }
79
117
 
80
- export { getObjectPropValue, getObjectPropValueExpression, getObjectProperty, isSimpleTemplateLiteral };
118
+ export { getObjectPropValue, getObjectPropValueExpression, getObjectProperty, isSimpleTemplateLiteral, normalizeASTSpans };
@@ -117,8 +117,11 @@ function lintInterpolationParams(ast, code, config) {
117
117
  const issueLineNumber = position > -1 ? getLineNumber(position) : 1;
118
118
  // Only check for unused parameters if there is at least one interpolation key in the string
119
119
  if (keys.length > 0) {
120
+ // i18next supports nested object access via dot notation (e.g. {{author.name}} with { author }).
121
+ // For each interpolation key, check if the root (part before the first dot) matches a provided param.
120
122
  for (const k of keys) {
121
- if (!paramKeys.includes(k)) {
123
+ const root = k.split('.')[0];
124
+ if (!paramKeys.includes(k) && !paramKeys.includes(root)) {
122
125
  issues.push({
123
126
  text: `Interpolation parameter "${k}" was not provided`,
124
127
  line: issueLineNumber,
@@ -126,8 +129,10 @@ function lintInterpolationParams(ast, code, config) {
126
129
  });
127
130
  }
128
131
  }
132
+ // For each provided param, check if it is used either directly or as the root of a dotted key.
129
133
  for (const pk of paramKeys) {
130
- if (!keys.includes(pk)) {
134
+ const isUsed = keys.some(k => k === pk || k.split('.')[0] === pk);
135
+ if (!isUsed) {
131
136
  issues.push({
132
137
  text: `Parameter "${pk}" is not used in translation string`,
133
138
  line: issueLineNumber,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.42.2",
3
+ "version": "1.42.4",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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;AAKtF,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,CAiGf;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,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,CAsGf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,OAAO,CAAE,MAAM,EAAE,oBAAoB,EAAE,EAAE,uBAA+B,EAAE,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,sDAO3I"}
@@ -1,4 +1,15 @@
1
1
  import type { Expression, ObjectExpression, TemplateLiteral } from '@swc/core';
2
+ /**
3
+ * Recursively normalizes all SWC span offsets in an AST by subtracting a base
4
+ * offset. SWC's `parse()` accumulates byte offsets across successive calls in
5
+ * the same process, so `span.start`/`span.end` values can exceed the length of
6
+ * the source file. Call this once on the root `Module` node right after parsing
7
+ * to make every span file-relative.
8
+ *
9
+ * @param node - Any AST node (or the root Module)
10
+ * @param base - The base offset to subtract (`ast.span.start`)
11
+ */
12
+ export declare function normalizeASTSpans(node: any, base: number): void;
2
13
  /**
3
14
  * Finds and returns the full property node (KeyValueProperty) for the given
4
15
  * property name from an ObjectExpression.
@@ -1 +1 @@
1
- {"version":3,"file":"ast-utils.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE1F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,qDAU5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAKhH;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAE1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAYrH"}
1
+ {"version":3,"file":"ast-utils.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAc,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE1F;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CA0BhE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,qDAU5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAKhH;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAE1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAYrH"}
@@ -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;AAwI3D,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,eAAe,EAAE,CAAC,CAAC;SAC1C;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;gBAEvB,MAAM,EAAE,oBAAoB;IAKzC,SAAS,CAAE,KAAK,EAAE,OAAO;IAanB,GAAG;;;;;;;CA6FV;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;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+GAA+G;IAC/G,IAAI,CAAC,EAAE,WAAW,GAAG,eAAe,CAAC;CACtC"}
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;AA6I3D,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,eAAe,EAAE,CAAC,CAAC;SAC1C;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;gBAEvB,MAAM,EAAE,oBAAoB;IAKzC,SAAS,CAAE,KAAK,EAAE,OAAO;IAanB,GAAG;;;;;;;CA6FV;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;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,+GAA+G;IAC/G,IAAI,CAAC,EAAE,WAAW,GAAG,eAAe,CAAC;CACtC"}