i18next-cli 1.45.0 → 1.46.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/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.45.0'); // This string is replaced with the actual version at build time by rollup
31
+ .version('1.46.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
@@ -135,7 +135,13 @@ function extractFromTransComponent(node, config) {
135
135
  valuesAttr.value.expression.type === 'ObjectExpression') {
136
136
  valuesCountProperty = astUtils.getObjectPropValueExpression(valuesAttr.value.expression, 'count');
137
137
  }
138
- const hasCount = !!countAttr || !!valuesCountProperty;
138
+ // Mirror react-i18next v16.4.0: infer count from inline {{ count }} children
139
+ // when no explicit `count` prop or `values={{ count }}` is present.
140
+ // The runtime check is `typeof valuesFromChildren.count === 'number'`; at
141
+ // extraction time we can only inspect the AST shape, so we look for any
142
+ // ObjectExpression interpolation that declares a `count` key.
143
+ const hasInlineCount = !countAttr && !valuesCountProperty && childrenHaveInlineCount(node.children);
144
+ const hasCount = !!countAttr || !!valuesCountProperty || hasInlineCount;
139
145
  const tOptionsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
140
146
  attr.name.type === 'Identifier' &&
141
147
  attr.name.value === 'tOptions');
@@ -385,6 +391,74 @@ function swcChildToReactNode(node) {
385
391
  function swcChildrenToReactNodes(children) {
386
392
  return children.map(swcChildToReactNode).filter(n => n !== null);
387
393
  }
394
+ /**
395
+ * Unwraps TypeScript type-assertion and parenthesis wrappers from a JSX
396
+ * expression so callers can inspect the underlying node type.
397
+ *
398
+ * Handles:
399
+ * `{{ count } as any}` → TsAsExpression wrapping ObjectExpression
400
+ * `{{ count } as TransInterpolation}` → same
401
+ * `({{ count }})` → ParenthesisExpression wrapping ObjectExpression
402
+ */
403
+ function unwrapJSXExpression(expr) {
404
+ if (expr.type === 'TsAsExpression' || expr.type === 'TsSatisfiesExpression') {
405
+ return unwrapJSXExpression(expr.expression);
406
+ }
407
+ if (expr.type === 'ParenthesisExpression') {
408
+ return unwrapJSXExpression(expr.expression);
409
+ }
410
+ return expr;
411
+ }
412
+ /**
413
+ * Recursively walks JSX children to determine whether any interpolation
414
+ * object contains a `count` property — mirroring the runtime behaviour of
415
+ * react-i18next v16.4.0's `getValuesFromChildren`.
416
+ *
417
+ * This lets the extractor infer `hasCount = true` when a `{{ count }}`
418
+ * (or `{{ count: expr }}`) interpolation is present in children without an
419
+ * explicit `count` prop on the `<Trans>` component.
420
+ *
421
+ * Matches:
422
+ * `{{ count }}` — shorthand Identifier (prop.type === 'Identifier')
423
+ * `{{ count: someExpr }}` — KeyValueProperty with Identifier key
424
+ * `{{ count } as any}` — TsAsExpression-wrapped ObjectExpression
425
+ * Deeply nested in child JSX elements (e.g. `<strong>{{ count }}</strong>`)
426
+ *
427
+ * @param children - The JSX children array to search
428
+ * @returns `true` when a `count` interpolation is found anywhere in the tree
429
+ */
430
+ function childrenHaveInlineCount(children) {
431
+ for (const child of children) {
432
+ if (child.type === 'JSXExpressionContainer') {
433
+ const inner = unwrapJSXExpression(child.expression);
434
+ if (inner.type === 'ObjectExpression') {
435
+ const hasCount = inner.properties.some(prop => {
436
+ if (prop.type === 'KeyValueProperty') {
437
+ // { count: expr }
438
+ return ((prop.key.type === 'Identifier' || prop.key.type === 'StringLiteral') &&
439
+ prop.key.value === 'count');
440
+ }
441
+ if (prop.type === 'Identifier') {
442
+ // shorthand { count }
443
+ return prop.value === 'count';
444
+ }
445
+ return false;
446
+ });
447
+ if (hasCount)
448
+ return true;
449
+ }
450
+ }
451
+ else if (child.type === 'JSXElement') {
452
+ if (childrenHaveInlineCount(child.children))
453
+ return true;
454
+ }
455
+ else if (child.type === 'JSXFragment') {
456
+ if (childrenHaveInlineCount(child.children))
457
+ return true;
458
+ }
459
+ }
460
+ return false;
461
+ }
388
462
  function serializeJSXChildren(children, config) {
389
463
  const i18nextOptions = { ...reactI18next.getDefaults() };
390
464
  if (config.extract.transKeepBasicHtmlNodesFor) {
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.45.0'); // This string is replaced with the actual version at build time by rollup
29
+ .version('1.46.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
@@ -133,7 +133,13 @@ function extractFromTransComponent(node, config) {
133
133
  valuesAttr.value.expression.type === 'ObjectExpression') {
134
134
  valuesCountProperty = getObjectPropValueExpression(valuesAttr.value.expression, 'count');
135
135
  }
136
- const hasCount = !!countAttr || !!valuesCountProperty;
136
+ // Mirror react-i18next v16.4.0: infer count from inline {{ count }} children
137
+ // when no explicit `count` prop or `values={{ count }}` is present.
138
+ // The runtime check is `typeof valuesFromChildren.count === 'number'`; at
139
+ // extraction time we can only inspect the AST shape, so we look for any
140
+ // ObjectExpression interpolation that declares a `count` key.
141
+ const hasInlineCount = !countAttr && !valuesCountProperty && childrenHaveInlineCount(node.children);
142
+ const hasCount = !!countAttr || !!valuesCountProperty || hasInlineCount;
137
143
  const tOptionsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
138
144
  attr.name.type === 'Identifier' &&
139
145
  attr.name.value === 'tOptions');
@@ -383,6 +389,74 @@ function swcChildToReactNode(node) {
383
389
  function swcChildrenToReactNodes(children) {
384
390
  return children.map(swcChildToReactNode).filter(n => n !== null);
385
391
  }
392
+ /**
393
+ * Unwraps TypeScript type-assertion and parenthesis wrappers from a JSX
394
+ * expression so callers can inspect the underlying node type.
395
+ *
396
+ * Handles:
397
+ * `{{ count } as any}` → TsAsExpression wrapping ObjectExpression
398
+ * `{{ count } as TransInterpolation}` → same
399
+ * `({{ count }})` → ParenthesisExpression wrapping ObjectExpression
400
+ */
401
+ function unwrapJSXExpression(expr) {
402
+ if (expr.type === 'TsAsExpression' || expr.type === 'TsSatisfiesExpression') {
403
+ return unwrapJSXExpression(expr.expression);
404
+ }
405
+ if (expr.type === 'ParenthesisExpression') {
406
+ return unwrapJSXExpression(expr.expression);
407
+ }
408
+ return expr;
409
+ }
410
+ /**
411
+ * Recursively walks JSX children to determine whether any interpolation
412
+ * object contains a `count` property — mirroring the runtime behaviour of
413
+ * react-i18next v16.4.0's `getValuesFromChildren`.
414
+ *
415
+ * This lets the extractor infer `hasCount = true` when a `{{ count }}`
416
+ * (or `{{ count: expr }}`) interpolation is present in children without an
417
+ * explicit `count` prop on the `<Trans>` component.
418
+ *
419
+ * Matches:
420
+ * `{{ count }}` — shorthand Identifier (prop.type === 'Identifier')
421
+ * `{{ count: someExpr }}` — KeyValueProperty with Identifier key
422
+ * `{{ count } as any}` — TsAsExpression-wrapped ObjectExpression
423
+ * Deeply nested in child JSX elements (e.g. `<strong>{{ count }}</strong>`)
424
+ *
425
+ * @param children - The JSX children array to search
426
+ * @returns `true` when a `count` interpolation is found anywhere in the tree
427
+ */
428
+ function childrenHaveInlineCount(children) {
429
+ for (const child of children) {
430
+ if (child.type === 'JSXExpressionContainer') {
431
+ const inner = unwrapJSXExpression(child.expression);
432
+ if (inner.type === 'ObjectExpression') {
433
+ const hasCount = inner.properties.some(prop => {
434
+ if (prop.type === 'KeyValueProperty') {
435
+ // { count: expr }
436
+ return ((prop.key.type === 'Identifier' || prop.key.type === 'StringLiteral') &&
437
+ prop.key.value === 'count');
438
+ }
439
+ if (prop.type === 'Identifier') {
440
+ // shorthand { count }
441
+ return prop.value === 'count';
442
+ }
443
+ return false;
444
+ });
445
+ if (hasCount)
446
+ return true;
447
+ }
448
+ }
449
+ else if (child.type === 'JSXElement') {
450
+ if (childrenHaveInlineCount(child.children))
451
+ return true;
452
+ }
453
+ else if (child.type === 'JSXFragment') {
454
+ if (childrenHaveInlineCount(child.children))
455
+ return true;
456
+ }
457
+ }
458
+ return false;
459
+ }
386
460
  function serializeJSXChildren(children, config) {
387
461
  const i18nextOptions = { ...getDefaults() };
388
462
  if (config.extract.transKeepBasicHtmlNodesFor) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.45.0",
3
+ "version": "1.46.0",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +1 @@
1
- {"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAsC,UAAU,EAAkD,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC7J,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAsEvD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAE/B,kHAAkH;IAClH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,IAAI,CA2LxH"}
1
+ {"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAsC,UAAU,EAAkD,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC7J,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAsEvD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAE/B,kHAAkH;IAClH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,IAAI,CAkMxH"}