i18next-cli 1.24.5 → 1.24.7

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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.24.7](https://github.com/i18next/i18next-cli/compare/v1.24.6...v1.24.7) - 2025-11-14
9
+
10
+ - improved Trans component parsing further [#102](https://github.com/i18next/i18next-cli/issues/102)
11
+
12
+ ## [1.24.6](https://github.com/i18next/i18next-cli/compare/v1.24.5...v1.24.6) - 2025-11-14
13
+
14
+ - improved Trans component parsing further [#102](https://github.com/i18next/i18next-cli/issues/102)
15
+
8
16
  ## [1.24.5](https://github.com/i18next/i18next-cli/compare/v1.24.4...v1.24.5) - 2025-11-13
9
17
 
10
18
  - improved Trans component parsing further [#102](https://github.com/i18next/i18next-cli/issues/102)
package/dist/cjs/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var e=require("commander"),o=require("chokidar"),t=require("glob"),n=require("minimatch"),i=require("chalk"),r=require("./config.js"),a=require("./heuristic-config.js"),c=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var s=require("./types-generator.js"),l=require("./syncer.js"),u=require("./migrator.js"),d=require("./init.js"),g=require("./linter.js"),f=require("./status.js"),p=require("./locize.js"),m=require("./rename-key.js");const y=new e.Command;y.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.24.5"),y.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),y.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async e=>{try{const t=y.opts().config,i=await r.ensureConfig(t),a=async()=>{const o=await c.runExtractor(i,{isWatchMode:!!e.watch,isDryRun:!!e.dryRun,syncPrimaryWithDefaults:!!e.syncPrimary});return e.ci&&!o?(console.log("✅ No files were updated."),process.exit(0)):e.ci&&o&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),o};if(await a(),e.watch){console.log("\nWatching for changes...");const e=await x(i.extract.input),t=h(i.extract.ignore),r=w(i.extract.output),c=[...t,...r].filter(Boolean),s=e.filter(e=>!c.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}catch(e){console.error("Error running extractor:",e),process.exit(1)}}),y.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,o)=>{const t=y.opts().config;let n=await r.loadConfig(t);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=e}await f.runStatus(n,{detail:e,namespace:o.namespace})}),y.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const t=y.opts().config,i=await r.ensureConfig(t),a=()=>s.runTypesGenerator(i);if(await a(),e.watch){console.log("\nWatching for changes...");const e=await x(i.types?.input||[]),t=[...h(i.extract?.ignore)].filter(Boolean),r=e.filter(e=>!t.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(r,{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),y.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=y.opts().config,o=await r.ensureConfig(e);await l.runSyncer(o)}),y.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await u.runMigrator(e)}),y.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(d.runInit),y.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async e=>{const t=y.opts().config,c=async()=>{let e=await r.loadConfig(t);if(!e){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await a.detectConfig();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),e=o}await g.runLinterCli(e)};if(await c(),e.watch){console.log("\nWatching for changes...");const e=await r.loadConfig(t);if(e?.extract?.input){const t=await x(e.extract.input),i=[...h(e.extract.ignore),...w(e.extract.output)].filter(Boolean),r=t.filter(e=>!i.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}}),y.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeSync(t,e)}),y.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeDownload(t,e)}),y.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeMigrate(t,e)}),y.command("rename-key <oldKey> <newKey>").description("Rename a translation key across all source files and translation files.").option("--dry-run","Preview changes without modifying files").action(async(e,o,t)=>{try{const n=y.opts().config,a=await r.ensureConfig(n),c=await m.runRenameKey(a,e,o,t);c.success||(c.conflicts&&(console.error(i.red("\n❌ Conflicts detected:")),c.conflicts.forEach(e=>console.error(` - ${e}`))),c.error&&console.error(i.red(`\n❌ ${c.error}`)),process.exit(1));0===c.sourceFiles.reduce((e,o)=>e+o.changes,0)&&console.log(i.yellow(`\n⚠️ No usages found for "${e}"`))}catch(e){console.error(i.red("Error renaming key:"),e),process.exit(1)}}),y.parse(process.argv);const h=e=>Array.isArray(e)?e:e?[e]:[],w=e=>e&&"string"==typeof e?[e.replace(/\{\{[^}]+\}\}/g,"*")]:[],x=async(e=[])=>{const o=h(e),n=await Promise.all(o.map(e=>t.glob(e||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
2
+ "use strict";var e=require("commander"),o=require("chokidar"),t=require("glob"),n=require("minimatch"),i=require("chalk"),r=require("./config.js"),a=require("./heuristic-config.js"),c=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var s=require("./types-generator.js"),l=require("./syncer.js"),u=require("./migrator.js"),d=require("./init.js"),g=require("./linter.js"),f=require("./status.js"),p=require("./locize.js"),m=require("./rename-key.js");const y=new e.Command;y.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.24.7"),y.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),y.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async e=>{try{const t=y.opts().config,i=await r.ensureConfig(t),a=async()=>{const o=await c.runExtractor(i,{isWatchMode:!!e.watch,isDryRun:!!e.dryRun,syncPrimaryWithDefaults:!!e.syncPrimary});return e.ci&&!o?(console.log("✅ No files were updated."),process.exit(0)):e.ci&&o&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),o};if(await a(),e.watch){console.log("\nWatching for changes...");const e=await x(i.extract.input),t=h(i.extract.ignore),r=w(i.extract.output),c=[...t,...r].filter(Boolean),s=e.filter(e=>!c.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}catch(e){console.error("Error running extractor:",e),process.exit(1)}}),y.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(e,o)=>{const t=y.opts().config;let n=await r.loadConfig(t);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=e}await f.runStatus(n,{detail:e,namespace:o.namespace})}),y.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async e=>{const t=y.opts().config,i=await r.ensureConfig(t),a=()=>s.runTypesGenerator(i);if(await a(),e.watch){console.log("\nWatching for changes...");const e=await x(i.types?.input||[]),t=[...h(i.extract?.ignore)].filter(Boolean),r=e.filter(e=>!t.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(r,{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),y.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=y.opts().config,o=await r.ensureConfig(e);await l.runSyncer(o)}),y.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await u.runMigrator(e)}),y.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(d.runInit),y.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async e=>{const t=y.opts().config,c=async()=>{let e=await r.loadConfig(t);if(!e){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await a.detectConfig();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),e=o}await g.runLinterCli(e)};if(await c(),e.watch){console.log("\nWatching for changes...");const e=await r.loadConfig(t);if(e?.extract?.input){const t=await x(e.extract.input),i=[...h(e.extract.ignore),...w(e.extract.output)].filter(Boolean),r=t.filter(e=>!i.some(o=>n.minimatch(e,o,{dot:!0})));o.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}}),y.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeSync(t,e)}),y.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeDownload(t,e)}),y.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const o=y.opts().config,t=await r.ensureConfig(o);await p.runLocizeMigrate(t,e)}),y.command("rename-key <oldKey> <newKey>").description("Rename a translation key across all source files and translation files.").option("--dry-run","Preview changes without modifying files").action(async(e,o,t)=>{try{const n=y.opts().config,a=await r.ensureConfig(n),c=await m.runRenameKey(a,e,o,t);c.success||(c.conflicts&&(console.error(i.red("\n❌ Conflicts detected:")),c.conflicts.forEach(e=>console.error(` - ${e}`))),c.error&&console.error(i.red(`\n❌ ${c.error}`)),process.exit(1));0===c.sourceFiles.reduce((e,o)=>e+o.changes,0)&&console.log(i.yellow(`\n⚠️ No usages found for "${e}"`))}catch(e){console.error(i.red("Error renaming key:"),e),process.exit(1)}}),y.parse(process.argv);const h=e=>Array.isArray(e)?e:e?[e]:[],w=e=>e&&"string"==typeof e?[e.replace(/\{\{[^}]+\}\}/g,"*")]:[],x=async(e=[])=>{const o=h(e),n=await Promise.all(o.map(e=>t.glob(e||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
@@ -1 +1 @@
1
- "use strict";var e=require("./ast-utils.js");function t(t){if(t)return"StringLiteral"===t.type?t.value:"TemplateLiteral"===t.type&&e.isSimpleTemplateLiteral(t)?t.quasis[0].cooked:void 0}function n(e){return"StringLiteral"===e.value?.type?e.value.value:"JSXExpressionContainer"===e.value?.type?t(e.value.expression):void 0}exports.extractFromTransComponent=function(i,s){const r=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),o=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),p=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),a=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let l;p||"JSXAttribute"!==a?.type||"JSXExpressionContainer"!==a.value?.type||"ObjectExpression"!==a.value.expression.type||(l=e.getObjectPropValueExpression(a.value.expression,"count"));const u=!!p||!!l,c=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),f="JSXAttribute"===c?.type&&"JSXExpressionContainer"===c.value?.type&&"ObjectExpression"===c.value.expression.type?c.value.expression:void 0,y=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),g=!!y,v=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let d="JSXAttribute"===v?.type&&"JSXExpressionContainer"===v.value?.type?v.value.expression:"JSXAttribute"===v?.type&&"StringLiteral"===v.value?.type?v.value:void 0;const S=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let x;x="JSXAttribute"===S?.type?n(S):void 0,f&&(void 0===x&&(x=e.getObjectPropValue(f,"ns")),void 0===d&&(d=e.getObjectPropValueExpression(f,"context")));const m=function(e,n){if(!e||0===e.length)return"";const i=new Set(n.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]),s=e=>e&&"JSXText"===e.type&&/^\s*$/.test(e.value)&&e.value.includes("\n");function r(e,n,o=!1,p=!1){if(!e||!e.length)return;const a=p&&e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length>1;let l=0,u=e.length-1;for(;l<=u&&s(e[l]);)l++;for(;u>=l&&s(e[u]);)u--;const c=l<=u?e.slice(l,u+1):[],f=c.some(e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type));for(let e=0;e<c.length;e++){const p=c[e];if(p)if("JSXText"!==p.type){if("JSXExpressionContainer"===p.type){if(o&&!f&&p.expression){const e=p.expression.type;if("ObjectExpression"===e){const e=p.expression.properties&&p.expression.properties[0];if(e&&"KeyValueProperty"===e.type)continue}const n=t(p.expression);if(void 0!==n){if(!(/^\s*$/.test(n)&&!n.includes("\n")))continue}else if("Identifier"===e||"MemberExpression"===e||"CallExpression"===e)continue}const i=t(p.expression);if(void 0!==i){const t=/^\s*$/.test(i)&&!i.includes("\n"),r=c[e-1],o=c[e+1];if(t){const t=c[e+2];if(o&&"JSXText"===o.type&&s(o)&&t&&("JSXElement"===t.type||"JSXFragment"===t.type)){const t=c[e-1],n=c[e-2];if(!t||"JSXText"!==t.type&&n&&"JSXExpressionContainer"===n.type)continue}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue;const i=!o||"JSXText"===o.type&&!s(o);if(r&&"JSXText"===r.type&&i){const e=n[n.length-1];if(e&&"JSXText"===e.type){e.value=String(e.value)+p.expression.value;continue}}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue}}n.push(p);continue}if("JSXElement"===p.type){const e=p.opening&&p.opening.name&&"Identifier"===p.opening.name.type?p.opening.name.value:void 0;if(e&&i.has(e)){const i=p.opening&&Array.isArray(p.opening.attributes)&&p.opening.attributes.length>0,s=p.children||[],o=1===s.length&&("JSXText"===s[0]?.type||"JSXExpressionContainer"===s[0]?.type&&void 0!==t(s[0].expression)),l=!s.length,u=o;i&&!o?(n.push(p),r(p.children||[],n,!0)):l?n.push(p):u||("p"===e&&a?(n.push(p),r(p.children||[],n,!0,!1)):r(p.children||[],n,!1,!1));continue}n.push(p),r(p.children||[],n,!0);continue}"JSXFragment"!==p.type||r(p.children||[],n,o)}else{if(o&&!f)continue;if(o&&s(p))continue;if(s(p)){const t=c[e-1],i=c[e+1];if(t&&("JSXElement"===t.type||"JSXFragment"===t.type)&&i&&("JSXElement"===i.type||"JSXFragment"===i.type))continue;const s=n[n.length-1],r=c[e-1];if(s){if(r&&"JSXExpressionContainer"===r.type)continue;if("JSXText"===s.type&&r&&"JSXText"===r.type){s.value=String(s.value)+p.value;continue}}}if(o&&f&&0===e)continue;n.push(p)}}}const o=[];r(e,o,!1,!0);const p=new Set,a=e=>String(e).replace(/^\s*\n\s*/g,"").replace(/\s*\n\s*$/g,"");function l(e,n,r=!1){if(!e||0===e.length)return"";let u="",c=0;for(let f=0;f<e.length;f++){const y=e[f];if(y){if("JSXText"===y.type){if(s(y))continue;const t=e[f+1],n=e[f-1];if(n&&"JSXElement"===n.type){const t="Identifier"===n.opening?.name?.type?n.opening.name.value:void 0,s=t&&i.has(t),r=0===(n.children||[]).length;if(s&&r&&/^\s*\n\s*/.test(y.value)){const e=y.value.replace(/^\s*\n\s*/,"");if(e){u+=e;continue}continue}if(!s&&/^\s*\n\s*/.test(y.value)){const t=y.value.replace(/^\s*\n\s*/,"");if(t){const n=e[f-2];if(n&&"JSXText"===n.type){const e=n.value.replace(/\n\s*$/,""),i=/[A-Za-z0-9]$/.test(e),s=/^[A-Za-z0-9]/.test(t),r=/^[a-z]/.test(t);if(i&&s&&r){u+=t;continue}}u+=" "+t;continue}continue}}if(/\n\s*$/.test(y.value)&&t&&"JSXElement"===t.type){const n=y.value.replace(/\n\s*$/,"");if(n.trim()){const r="Identifier"===t.opening?.name?.type?t.opening.name.value:void 0,o=r&&i.has(r),a=(t.children||[]).length>0,l=/\s\n/.test(y.value),c=e[f+2],g=c&&"JSXText"===c.type&&!s(c)&&/[a-zA-Z0-9]/.test(c.value),v=!!(t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0),d=/^\s/.test(n)&&!/^\n/.test(n),S=n.trim(),x=d?" "+S:S,m=/[A-Za-z0-9]$/.test(S),J=c&&"string"==typeof c.value&&/^[A-Za-z0-9]/.test(c.value.trim()),X=c&&"string"==typeof c.value&&/^[a-z]/.test(c.value.trim()),h=c&&"string"==typeof c.value&&/^\s/.test(c.value),$=v&&a&&g&&!(m&&J&&X&&!l&&!d&&!h);m&&J&&X&&!l&&!d&&!h&&p.add(t),u+=l||o&&a||!o&&g&&d||$?x+" ":x;continue}}u+=y.value;continue}if("JSXExpressionContainer"===y.type){const e=y.expression;if(!e)continue;const n=t(e);if(void 0!==n)u+=n;else if("Identifier"===e.type)u+=`{{${e.value}}}`;else if("ObjectExpression"===e.type){const t=e.properties[0];t&&"KeyValueProperty"===t.type&&t.key&&"Identifier"===t.key.type?u+=`{{${t.key.value}}}`:t&&"Identifier"===t.type?u+=`{{${t.value}}}`:u+="{{value}}"}else"MemberExpression"===e.type&&e.property&&"Identifier"===e.property.type?u+=`{{${e.property.value}}}`:"CallExpression"===e.type&&"Identifier"===e.callee?.type?u+=`{{${e.callee.value}}}`:u+="{{value}}";continue}if("JSXElement"===y.type){let p;y.opening&&y.opening.name&&"Identifier"===y.opening.name.type&&(p=y.opening.name.value);const g=r?c:void 0;if(r&&"JSXElement"===y.type&&c++,p&&i.has(p)){const r=y.opening&&Array.isArray(y.opening.attributes)&&y.opening.attributes.length>0,c=y.children||[],v=c.length>0,d=1===c.length&&("JSXText"===c[0]?.type||"JSXExpressionContainer"===c[0]?.type&&void 0!==t(c[0].expression));if(!v||d){const t=d?l(c,void 0):"";if(""!==String(t).trim())u+=`<${p}>${t}</${p}>`;else{const t=e[f-1];t&&"JSXText"===t.type&&/\n\s*$/.test(t.value)&&(u=u.replace(/\s+$/,"")),u+=`<${p} />`}}else if(r&&!d){const e=c;if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(y),n=l(e,void 0);u+=`<${t}>${a(n)}</${t}>`}else{const e=n&&n.has(y)?n.get(y):o.indexOf(y),s=new Map;let r="number"==typeof e&&e>=0?e:0;for(const e of c)if(e&&"JSXElement"===e.type){const n=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(n&&i.has(n)){const n=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,i=e.children||[],o=1===i.length&&("JSXText"===i[0]?.type||"JSXExpressionContainer"===i[0]?.type&&void 0!==t(i[0].expression));!n&&(!i.length||o)||s.set(e,r++)}else s.set(e,r++)}const p=l(c,s.size?s:void 0);u+=`<${e}>${a(p)}</${e}>`}}else{const e=o.indexOf(y);if(-1!==e){const n=void 0!==g?g:e;if((()=>{let e=!1;for(const t of c)if(t)if("JSXElement"!==t.type){if("JSXExpressionContainer"===t.type&&-1!==o.indexOf(t))return e;if("JSXText"===t.type&&-1!==o.indexOf(t)){if(s(t))continue;if(!e)return!0;if(c.slice(c.indexOf(t)+1).some(e=>e&&"JSXElement"===e.type))return!0}}else e=!0;return!1})()){const e=l(c,void 0,!1);u+=`<${n}>${a(e)}</${n}>`;continue}const r=new Map;let p=n;for(const e of c)if(e&&"JSXElement"===e.type){const n=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(n&&i.has(n)){const n=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,i=e.children||[],s=1===i.length&&("JSXText"===i[0]?.type||"JSXExpressionContainer"===i[0]?.type&&void 0!==t(i[0].expression));!n&&(!i.length||s)||r.set(e,p++)}else r.set(e,p++)}const f=l(c,r.size>0?r:void 0,!1);u+=`<${n}>${a(f)}</${n}>`}else{const e=l(c,void 0,!1);u+=`<${p}>${a(e)}</${p}>`}}}else{const e=y.children||[];if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(y),n=l(e,void 0);u+=`<${t}>${a(n)}</${t}>`}else{const t=new Map,s=n&&n.has(y)?n.get(y):o.indexOf(y);let r="number"==typeof s&&s>=0?s:0;for(const n of e)if(n&&"JSXElement"===n.type){const e=n.opening&&n.opening.name&&"Identifier"===n.opening.name.type?n.opening.name.value:void 0;if(e&&i.has(e)){const e=n.opening&&Array.isArray(n.opening.attributes)&&n.opening.attributes.length>0,i=n.children||[],s=1===i.length&&"JSXText"===i[0]?.type;!e&&(!i.length||s)||t.set(n,r++)}else t.set(n,r++)}const p=l(e,t.size?t:void 0);u+=`<${s}>${a(p)}</${s}>`}}continue}"JSXFragment"!==y.type||(u+=l(y.children||[]))}}return u}const u=l(e,void 0,!0),c=String(u).replace(/<br \/>\s*\n\s*/g,"<br />"),f=String(c),y=new Set;if(p&&p.size>0)for(let e=0;e<o.length;e++)p.has(o[e])&&y.add(e);let g=String(f);if(y&&y.size>0)for(const e of y)try{g=g.replace(new RegExp("\\s+<"+e+">","g"),"<"+e+">"),g=g.replace(new RegExp("<\\/"+e+">\\s+","g"),"</"+e+">")}catch(e){}g=g.replace(/<\/(\d+)>\s*\n\s*(\S)/g,(e,t,n)=>{const i=Number(t);return y.has(i)?`</${t}>${n}`:`</${t}> ${n}`}),g=g.replace(/(\S)\s*\n\s*<(\d+)/g,(e,t,n)=>{const i=Number(n);return y.has(i)?`${t}<${n}`:`${t} <${n}`}),g=g.replace(/\s*\n\s*/g," "),g=g.replace(/\s+/g," "),g=g.replace(/\s+\./g,".");const v=g.trim();let d=String(v);if(y&&y.size>0)for(const e of y)try{d=d.replace(new RegExp("[\\s\\u00A0]+<"+e+">","g"),"<"+e+">"),d=d.replace(new RegExp("<\\/"+e+">[\\s\\u00A0]+","g"),"</"+e+">")}catch(e){}try{for(let e=0;e<o.length;e++){const t=o[e];if(!t||"JSXElement"!==t.type)continue;const n=o[e-1],i=o[e+1];if(!n||!i)continue;if("JSXText"!==n.type||"JSXText"!==i.type)continue;const s=String(n.value),r=String(i.value),p=s.replace(/\n\s*$/,""),a=/[A-Za-z0-9]$/.test(p),l=/^[A-Za-z0-9]/.test(r.trim()),u=/^[a-z]/.test(r.trim()),c=/\s\n/.test(s),f=r&&/^\s/.test(r)&&!/^\n/.test(r);if(a&&l&&u&&!c&&!f){const t=e;d=d.replace(new RegExp("\\s+<"+t+">","g"),"<"+t+">")}}}catch(e){}return d.trim()}(i.children,s);let J;const X="JSXAttribute"===o?.type?n(o):void 0;if(void 0!==X)J=X;else{const e=s.extract.defaultValue;J="string"==typeof e?e:""}let h,$;if("JSXAttribute"===r?.type){if("StringLiteral"===r.value?.type){if(h=r.value,$=h.value,!$||""===$.trim())return null;if(x&&"StringLiteral"===h.type){const e=s.extract.nsSeparator??":",t=h.value;if(e&&t.startsWith(`${x}${e}`)){if($=t.slice(`${x}${e}`.length),!$||""===$.trim())return null;h={...h,value:$}}}}else"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(h=r.value.expression);if(!h)return null}return o||!$||m.trim()?!o&&m.trim()&&(J=m):J=$,{keyExpression:h,serializedChildren:m,ns:x,defaultValue:J,hasCount:u,isOrdinal:g,contextExpression:d,optionsNode:f,explicitDefault:void 0!==X||(e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1})(f)}};
1
+ "use strict";var e=require("./ast-utils.js");function t(t){if(t)return"StringLiteral"===t.type?t.value:"TemplateLiteral"===t.type&&e.isSimpleTemplateLiteral(t)?t.quasis[0].cooked:void 0}function n(e){return"StringLiteral"===e.value?.type?e.value.value:"JSXExpressionContainer"===e.value?.type?t(e.value.expression):void 0}exports.extractFromTransComponent=function(i,s){const r=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),o=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),p=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),a=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let l;p||"JSXAttribute"!==a?.type||"JSXExpressionContainer"!==a.value?.type||"ObjectExpression"!==a.value.expression.type||(l=e.getObjectPropValueExpression(a.value.expression,"count"));const u=!!p||!!l,c=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),f="JSXAttribute"===c?.type&&"JSXExpressionContainer"===c.value?.type&&"ObjectExpression"===c.value.expression.type?c.value.expression:void 0,y=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),g=!!y,d=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let x="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:"JSXAttribute"===d?.type&&"StringLiteral"===d.value?.type?d.value:void 0;const v=i.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let h;h="JSXAttribute"===v?.type?n(v):void 0,f&&(void 0===h&&(h=e.getObjectPropValue(f,"ns")),void 0===x&&(x=e.getObjectPropValueExpression(f,"context")));const S=function(e,n){if(!e||0===e.length)return"";const i=new Set(n.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]),s=e=>e&&"JSXText"===e.type&&/^\s*$/.test(e.value)&&e.value.includes("\n");function r(e,n,o=!1,p=!1){if(!e||!e.length)return;const a=p&&e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length>1;let l=0,u=e.length-1;for(;l<=u&&s(e[l]);)l++;for(;u>=l&&s(e[u]);)u--;const c=l<=u?e.slice(l,u+1):[],f=c.some(e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type));for(let e=0;e<c.length;e++){const p=c[e];if(p)if("JSXText"!==p.type){if("JSXExpressionContainer"===p.type){if(o&&!f&&p.expression){const e=p.expression.type;if("ObjectExpression"===e){const e=p.expression.properties&&p.expression.properties[0];if(e&&"KeyValueProperty"===e.type)continue}const n=t(p.expression);if(void 0!==n){if(!(/^\s*$/.test(n)&&!n.includes("\n")))continue}else if("Identifier"===e||"MemberExpression"===e||"CallExpression"===e)continue}const i=t(p.expression);if(void 0!==i){const t=/^\s*$/.test(i)&&!i.includes("\n"),r=c[e-1],o=c[e+1];if(t){const t=c[e+2];if(o&&"JSXText"===o.type&&s(o)&&t&&("JSXElement"===t.type||"JSXFragment"===t.type)){const t=c[e-1],n=c[e-2];if(!t||"JSXText"!==t.type&&n&&"JSXExpressionContainer"===n.type)continue}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue;const i=!o||"JSXText"===o.type&&!s(o);if(r&&"JSXText"===r.type&&i){const e=n[n.length-1];if(e&&"JSXText"===e.type){e.value=String(e.value)+p.expression.value;continue}}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue}}n.push(p);continue}if("JSXElement"===p.type){const e=p.opening&&p.opening.name&&"Identifier"===p.opening.name.type?p.opening.name.value:void 0;if(e&&i.has(e)){const i=p.opening&&Array.isArray(p.opening.attributes)&&p.opening.attributes.length>0,s=p.children||[],o=1===s.length&&("JSXText"===s[0]?.type||"JSXExpressionContainer"===s[0]?.type&&void 0!==t(s[0].expression)),l=!s.length,u=o;i&&!o?(n.push(p),r(p.children||[],n,!0)):l?n.push(p):u||("p"===e&&a?(n.push(p),r(p.children||[],n,!0,!1)):r(p.children||[],n,!1,!1));continue}n.push(p),r(p.children||[],n,!0);continue}"JSXFragment"!==p.type||r(p.children||[],n,o)}else{if(o&&!f)continue;if(o&&s(p))continue;if(s(p)){const t=c[e-1],i=c[e+1];if(t&&("JSXElement"===t.type||"JSXFragment"===t.type)&&i&&("JSXElement"===i.type||"JSXFragment"===i.type))continue;const s=n[n.length-1],r=c[e-1];if(s){if(r&&"JSXExpressionContainer"===r.type)continue;if("JSXText"===s.type&&r&&"JSXText"===r.type){s.value=String(s.value)+p.value;continue}}}if(o&&f&&0===e)continue;n.push(p)}}}const o=[];function p(e){if(!e||!e.length)return!1;let t=!1;for(const n of e)if(n)if("JSXElement"!==n.type){if("JSXExpressionContainer"===n.type&&-1!==o.indexOf(n))return t;if("JSXText"===n.type&&-1!==o.indexOf(n)){if(s(n))continue;if(!t)return!0;const i=e.indexOf(n);if(e.slice(i+1).some(e=>e&&"JSXElement"===e.type))return!0}}else t=!0;return!1}r(e,o,!1,!0);const a=new Set,l=e=>String(e).replace(/^\s*\n\s*/g,"").replace(/\s*\n\s*$/g,"");function u(e,n,r=!1){if(!e||0===e.length)return"";let c="";const f=e=>{if(!e)return-1;if(n&&n.has(e))return n.get(e);if(n)for(const[t,i]of n.entries())try{if(t&&e&&t.span&&e.span&&t.span.start===e.span.start&&t.span.end===e.span.end)return i}catch(e){}return o.indexOf(e)};let y=0;for(let n=0;n<e.length;n++){const g=e[n];if(g){if("JSXText"===g.type){if(s(g))continue;const t=e[n+1],r=e[n-1];if(r&&"JSXElement"===r.type){const t="Identifier"===r.opening?.name?.type?r.opening.name.value:void 0,s=t&&i.has(t),o=0===(r.children||[]).length;if(s&&o&&/^\s*\n\s*/.test(g.value)){const e=g.value.replace(/^\s*\n\s*/,"");if(e){c+=e;continue}continue}if(!s&&/^\s*\n\s*/.test(g.value)){const t=g.value.replace(/^\s*\n\s*/,"");if(t){const i=e[n-2];if(i&&"JSXText"===i.type){const e=i.value.replace(/\n\s*$/,""),n=/[A-Za-z0-9]$/.test(e),s=/^[A-Za-z0-9]/.test(t),r=/^[a-z]/.test(t);if(n&&s&&r){c+=t;continue}}c+=" "+t;continue}continue}}if(/\n\s*$/.test(g.value)&&t&&"JSXElement"===t.type){const r=g.value.replace(/\n\s*$/,"");if(r.trim()){const o="Identifier"===t.opening?.name?.type?t.opening.name.value:void 0,p=o&&i.has(o),l=(t.children||[]).length>0,u=/\s\n/.test(g.value),f=e[n+2],y=f&&"JSXText"===f.type&&!s(f)&&/[a-zA-Z0-9]/.test(f.value),d=!!(t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0),x=/^\s/.test(r)&&!/^\n/.test(r),v=r.trim(),h=x?" "+v:v,S=/[A-Za-z0-9]$/.test(v),m=f&&"string"==typeof f.value&&/^[A-Za-z0-9]/.test(f.value.trim()),J=f&&"string"==typeof f.value&&/^[a-z]/.test(f.value.trim()),X=f&&"string"==typeof f.value&&/^\s/.test(f.value)&&!/^\n/.test(f.value),$=d&&l&&y&&!(S&&m&&J&&!u&&!x&&!X),E=f&&"string"==typeof f.value&&/^[,;:!?.]/.test(f.value.trim()),b=$&&!E;S&&m&&J&&!u&&!x&&!X&&a.add(t),c+=u||p&&l||!p&&y&&x||b?h+" ":h;continue}}c+=g.value;continue}if("JSXExpressionContainer"===g.type){const e=g.expression;if(!e)continue;const n=t(e);if(void 0!==n)c+=n;else if("Identifier"===e.type)c+=`{{${e.value}}}`;else if("ObjectExpression"===e.type){const t=e.properties[0];t&&"KeyValueProperty"===t.type&&t.key&&"Identifier"===t.key.type?c+=`{{${t.key.value}}}`:t&&"Identifier"===t.type?c+=`{{${t.value}}}`:c+="{{value}}"}else"MemberExpression"===e.type&&e.property&&"Identifier"===e.property.type?c+=`{{${e.property.value}}}`:"CallExpression"===e.type&&"Identifier"===e.callee?.type?c+=`{{${e.callee.value}}}`:c+="{{value}}";continue}if("JSXElement"===g.type){let a;g.opening&&g.opening.name&&"Identifier"===g.opening.name.type&&(a=g.opening.name.value);const d=r?y:void 0;if(r&&"JSXElement"===g.type&&y++,a&&i.has(a)){const r=g.opening&&Array.isArray(g.opening.attributes)&&g.opening.attributes.length>0,y=g.children||[],x=y.length>0,v=1===y.length&&("JSXText"===y[0]?.type||"JSXExpressionContainer"===y[0]?.type&&void 0!==t(y[0].expression));if(!x||v){const t=v?u(y,void 0):"";if(""!==String(t).trim())c+=`<${a}>${t}</${a}>`;else{const t=e[n-1];t&&"JSXText"===t.type&&/\n\s*$/.test(t.value)&&(c=c.replace(/\s+$/,"")),c+=`<${a} />`}}else if(r&&!v){const e=y,n=f(g);if(p(e)){const s=new Map;let r=1;for(const n of e)if(n&&"JSXElement"===n.type){const e=n.opening&&n.opening.name&&"Identifier"===n.opening.name.type?n.opening.name.value:void 0;if(e&&i.has(e)){const e=n.opening&&Array.isArray(n.opening.attributes)&&n.opening.attributes.length>0,i=n.children||[],o=1===i.length&&("JSXText"===i[0]?.type||"JSXExpressionContainer"===i[0]?.type&&void 0!==t(i[0].expression));!e&&(!i.length||o)||s.set(n,r++)}else s.set(n,r++)}const o=u(e,s.size?s:void 0);c+=`<${n}>${l(o)}</${n}>`}else{const e=new Map;let s=1;for(const n of y)if(n&&"JSXElement"===n.type){const r=n.opening&&n.opening.name&&"Identifier"===n.opening.name.type?n.opening.name.value:void 0;if(r&&i.has(r)){const i=n.opening&&Array.isArray(n.opening.attributes)&&n.opening.attributes.length>0,r=n.children||[],o=1===r.length&&("JSXText"===r[0]?.type||"JSXExpressionContainer"===r[0]?.type&&void 0!==t(r[0].expression));!i&&(!r.length||o)||e.set(n,s++)}else e.set(n,s++)}const r=u(y,e.size?e:void 0);c+=`<${n}>${l(r)}</${n}>`}}else{const e=o.indexOf(g);if(-1!==e){const n=void 0!==d?d:e;if((()=>{let e=!1;for(const t of y)if(t)if("JSXElement"!==t.type){if("JSXExpressionContainer"===t.type&&-1!==o.indexOf(t))return e;if("JSXText"===t.type&&-1!==o.indexOf(t)){if(s(t))continue;if(!e)return!0;if(y.slice(y.indexOf(t)+1).some(e=>e&&"JSXElement"===e.type))return!0}}else e=!0;return!1})()){if(0===n){const e=u(y,void 0,!1);c+=`<${n}>${l(e)}</${n}>`;continue}const e=new Map;let s=1;for(const n of y)if(n&&"JSXElement"===n.type){const r=n.opening&&n.opening.name&&"Identifier"===n.opening.name.type?n.opening.name.value:void 0;if(r&&i.has(r)){const i=n.opening&&Array.isArray(n.opening.attributes)&&n.opening.attributes.length>0,r=n.children||[],o=1===r.length&&("JSXText"===r[0]?.type||"JSXExpressionContainer"===r[0]?.type&&void 0!==t(r[0].expression));!i&&(!r.length||o)||e.set(n,s++)}else e.set(n,s++)}const r=u(y,e.size?e:void 0,!1);c+=`<${n}>${l(r)}</${n}>`;continue}const r=new Map;let p=1;for(const e of y)if(e&&"JSXElement"===e.type){const n=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(n&&i.has(n)){const n=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,i=e.children||[],s=1===i.length&&("JSXText"===i[0]?.type||"JSXExpressionContainer"===i[0]?.type&&void 0!==t(i[0].expression));!n&&(!i.length||s)||r.set(e,p++)}else r.set(e,p++)}const a=u(y,r.size>0?r:void 0,!1);c+=`<${n}>${l(a)}</${n}>`}else{const e=u(y,void 0,!1);c+=`<${a}>${l(e)}</${a}>`}}}else{const e=g.children||[];if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(g),n=u(e,void 0);c+=`<${t}>${l(n)}</${t}>`}else{const t=new Map,n=f(g);let s=1;for(const n of e)if(n&&"JSXElement"===n.type){const e=n.opening&&n.opening.name&&"Identifier"===n.opening.name.type?n.opening.name.value:void 0;if(e&&i.has(e)){const e=n.opening&&Array.isArray(n.opening.attributes)&&n.opening.attributes.length>0,i=n.children||[],r=1===i.length&&"JSXText"===i[0]?.type;!e&&(!i.length||r)||t.set(n,s++)}else t.set(n,s++)}const r=u(e,t.size?t:void 0);c+=`<${n}>${l(r)}</${n}>`}}continue}"JSXFragment"!==g.type||(c+=u(g.children||[]))}}return c}const c=u(e,void 0,!0),f=String(c).replace(/<br \/>\s*\n\s*/g,"<br />"),y=String(f),g=new Set;if(a&&a.size>0)for(let e=0;e<o.length;e++)a.has(o[e])&&g.add(e);let d=String(y);if(g&&g.size>0)for(const e of g)try{d=d.replace(new RegExp("\\s+<"+e+">","g"),"<"+e+">"),d=d.replace(new RegExp("<\\/"+e+">\\s+","g"),"</"+e+">")}catch(e){}d=d.replace(/<\/(\d+)>\s*\n\s*(\S)/g,(e,t,n)=>{const i=Number(t);return g.has(i)?`</${t}>${n}`:`</${t}> ${n}`}),d=d.replace(/(\S)\s*\n\s*<(\d+)/g,(e,t,n)=>{const i=Number(n);return g.has(i)?`${t}<${n}`:`${t} <${n}`}),d=d.replace(/\s*\n\s*/g," "),d=d.replace(/\s+/g," "),d=d.replace(/\s+([,;:!?.])/g,"$1");const x=d.trim();let v=String(x);if(g&&g.size>0)for(const e of g)try{v=v.replace(new RegExp("[\\s\\u00A0]+<"+e+">","g"),"<"+e+">"),v=v.replace(new RegExp("<\\/"+e+">[\\s\\u00A0]+","g"),"</"+e+">")}catch(e){}try{for(let e=0;e<o.length;e++){const t=o[e];if(!t||"JSXElement"!==t.type)continue;const n=o[e-1],i=o[e+1];if(!n||!i)continue;if("JSXText"!==n.type||"JSXText"!==i.type)continue;const s=String(n.value),r=String(i.value),p=s.replace(/\n\s*$/,""),a=/[A-Za-z0-9]$/.test(p),l=/^[A-Za-z0-9]/.test(r.trim()),u=/^[a-z]/.test(r.trim()),c=/\s\n/.test(s),f=r&&/^\s/.test(r)&&!/^\n/.test(r);if(a&&l&&u&&!c&&!f){const t=e;v=v.replace(new RegExp("\\s+<"+t+">","g"),"<"+t+">")}}}catch(e){}function h(e){if(!e||!e.includes("<"))return e;function t(e){const t=[],n=[];let i=0;const s=/<\/?(\d+)>|<[^>]+>/g;let r;for(;r=s.exec(e);){const o=r[0],p=r.index;if(p>i){const s={type:"text",text:e.slice(i,p)};n.length?n[n.length-1].node.children.push(s):t.push(s)}const a=/^<\/(\d+)>$/.exec(o),l=/^<(\d+)>$/.exec(o);if(l){const e=Number(l[1]),i={type:"ph",idx:e,children:[]};n.length?n[n.length-1].node.children.push(i):t.push(i),n.push({node:i,idx:e})}else if(a)n.length&&n.pop();else{const e={type:"text",text:o};n.length?n[n.length-1].node.children.push(e):t.push(e)}i=s.lastIndex}if(i<e.length){const s={type:"text",text:e.slice(i)};n.length?n[n.length-1].node.children.push(s):t.push(s)}return t}function n(e,t=null){let i="";for(const t of e)if("text"===t.type)i+=t.text;else{const e=t.children.filter(e=>"ph"===e.type),s=e.map(e=>e.idx);if(!(s.length<=1||s.every((e,t)=>0===t||e===s[t-1]+1))){const e=t.children.map(e=>"text"===e.type?e.text:`<${e.idx}>${n(e.children,e.idx)}</${e.idx}>`).join("");i+=`<${t.idx}>${e}</${t.idx}>`;continue}const r=new Map;let p=0===t.idx?0:1;try{const e=o[t.idx];if(e&&e.span){const n=e.span.start,i=e.span.end;let s=-1;for(let e=t.idx+1;e<o.length;e++){const t=o[e];if(t&&t.span&&(t.span.start>=n&&t.span.end<=i&&"JSXElement"===t.type)){s=e;break}}let r=0;if(-1!==s)for(let e=t.idx+1;e<s;e++){const t=o[e];t&&t.span&&(t.span.start>=n&&t.span.end<=i&&("JSXText"!==t.type&&"JSXExpressionContainer"!==t.type||r++))}else for(let e=t.idx+1;e<o.length;e++){const t=o[e];t&&t.span&&(t.span.start>=n&&t.span.end<=i&&("JSXText"!==t.type&&"JSXExpressionContainer"!==t.type||r++))}"number"==typeof t.idx&&(p=0===t.idx?0:Math.max(1,r+1))}}catch(e){}for(const t of e)r.has(t.idx)||r.set(t.idx,p++);const a=t.children.map(e=>{if("text"===e.type)return e.text;const t=e.idx,i=r.has(t)?r.get(t):t;return`<${i}>${n(e.children,i)}</${i}>`}).join("");i+=`<${t.idx}>${a}</${t.idx}>`}return i}try{return n(t(e))}catch(t){return e}}return v=h(v),v.trim()}(i.children,s);let m;const J="JSXAttribute"===o?.type?n(o):void 0;if(void 0!==J)m=J;else{const e=s.extract.defaultValue;m="string"==typeof e?e:""}let X,$;if("JSXAttribute"===r?.type){if("StringLiteral"===r.value?.type){if(X=r.value,$=X.value,!$||""===$.trim())return null;if(h&&"StringLiteral"===X.type){const e=s.extract.nsSeparator??":",t=X.value;if(e&&t.startsWith(`${h}${e}`)){if($=t.slice(`${h}${e}`.length),!$||""===$.trim())return null;X={...X,value:$}}}}else"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(X=r.value.expression);if(!X)return null}return o||!$||S.trim()?!o&&S.trim()&&(m=S):m=$,{keyExpression:X,serializedChildren:S,ns:h,defaultValue:m,hasCount:u,isOrdinal:g,contextExpression:x,optionsNode:f,explicitDefault:void 0!==J||(e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1})(f)}};
package/dist/esm/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{Command as o}from"commander";import e from"chokidar";import{glob as t}from"glob";import{minimatch as n}from"minimatch";import i from"chalk";import{ensureConfig as a,loadConfig as r}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as s}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as l}from"./types-generator.js";import{runSyncer as p}from"./syncer.js";import{runMigrator as f}from"./migrator.js";import{runInit as m}from"./init.js";import{runLinterCli as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as u,runLocizeDownload as y,runLocizeMigrate as h}from"./locize.js";import{runRenameKey as w}from"./rename-key.js";const x=new o;x.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.24.5"),x.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),x.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async o=>{try{const t=x.opts().config,i=await a(t),r=async()=>{const e=await s(i,{isWatchMode:!!o.watch,isDryRun:!!o.dryRun,syncPrimaryWithDefaults:!!o.syncPrimary});return o.ci&&!e?(console.log("✅ No files were updated."),process.exit(0)):o.ci&&e&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),e};if(await r(),o.watch){console.log("\nWatching for changes...");const o=await z(i.extract.input),t=j(i.extract.ignore),a=k(i.extract.output),c=[...t,...a].filter(Boolean),s=o.filter(o=>!c.some(e=>n(o,e,{dot:!0})));e.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),r()})}}catch(o){console.error("Error running extractor:",o),process.exit(1)}}),x.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,e)=>{const t=x.opts().config;let n=await r(t);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=o}await g(n,{detail:o,namespace:e.namespace})}),x.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const t=x.opts().config,i=await a(t),r=()=>l(i);if(await r(),o.watch){console.log("\nWatching for changes...");const o=await z(i.types?.input||[]),t=[...j(i.extract?.ignore)].filter(Boolean),a=o.filter(o=>!t.some(e=>n(o,e,{dot:!0})));e.watch(a,{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),r()})}}),x.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=x.opts().config,e=await a(o);await p(e)}),x.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async o=>{await f(o)}),x.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(m),x.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async o=>{const t=x.opts().config,a=async()=>{let o=await r(t);if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await c();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=e}await d(o)};if(await a(),o.watch){console.log("\nWatching for changes...");const o=await r(t);if(o?.extract?.input){const t=await z(o.extract.input),i=[...j(o.extract.ignore),...k(o.extract.output)].filter(Boolean),r=t.filter(o=>!i.some(e=>n(o,e,{dot:!0})));e.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}}),x.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const e=x.opts().config,t=await a(e);await u(t,o)}),x.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const e=x.opts().config,t=await a(e);await y(t,o)}),x.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const e=x.opts().config,t=await a(e);await h(t,o)}),x.command("rename-key <oldKey> <newKey>").description("Rename a translation key across all source files and translation files.").option("--dry-run","Preview changes without modifying files").action(async(o,e,t)=>{try{const n=x.opts().config,r=await a(n),c=await w(r,o,e,t);c.success||(c.conflicts&&(console.error(i.red("\n❌ Conflicts detected:")),c.conflicts.forEach(o=>console.error(` - ${o}`))),c.error&&console.error(i.red(`\n❌ ${c.error}`)),process.exit(1));0===c.sourceFiles.reduce((o,e)=>o+e.changes,0)&&console.log(i.yellow(`\n⚠️ No usages found for "${o}"`))}catch(o){console.error(i.red("Error renaming key:"),o),process.exit(1)}}),x.parse(process.argv);const j=o=>Array.isArray(o)?o:o?[o]:[],k=o=>o&&"string"==typeof o?[o.replace(/\{\{[^}]+\}\}/g,"*")]:[],z=async(o=[])=>{const e=j(o),n=await Promise.all(e.map(o=>t(o||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
2
+ import{Command as o}from"commander";import e from"chokidar";import{glob as t}from"glob";import{minimatch as n}from"minimatch";import i from"chalk";import{ensureConfig as a,loadConfig as r}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as s}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as l}from"./types-generator.js";import{runSyncer as p}from"./syncer.js";import{runMigrator as f}from"./migrator.js";import{runInit as m}from"./init.js";import{runLinterCli as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as u,runLocizeDownload as y,runLocizeMigrate as h}from"./locize.js";import{runRenameKey as w}from"./rename-key.js";const x=new o;x.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.24.7"),x.option("-c, --config <path>","Path to i18next-cli config file (overrides detection)"),x.command("extract").description("Extract translation keys from source files and update resource files.").option("-w, --watch","Watch for file changes and re-run the extractor.").option("--ci","Exit with a non-zero status code if any files are updated.").option("--dry-run","Run the extractor without writing any files to disk.").option("--sync-primary","Sync primary language values with default values from code.").action(async o=>{try{const t=x.opts().config,i=await a(t),r=async()=>{const e=await s(i,{isWatchMode:!!o.watch,isDryRun:!!o.dryRun,syncPrimaryWithDefaults:!!o.syncPrimary});return o.ci&&!e?(console.log("✅ No files were updated."),process.exit(0)):o.ci&&e&&(console.error("❌ Some files were updated. This should not happen in CI mode."),process.exit(1)),e};if(await r(),o.watch){console.log("\nWatching for changes...");const o=await z(i.extract.input),t=j(i.extract.ignore),a=k(i.extract.output),c=[...t,...a].filter(Boolean),s=o.filter(o=>!c.some(e=>n(o,e,{dot:!0})));e.watch(s,{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),r()})}}catch(o){console.error("Error running extractor:",o),process.exit(1)}}),x.command("status [locale]").description("Display translation status. Provide a locale for a detailed key-by-key view.").option("-n, --namespace <ns>","Filter the status report by a specific namespace").action(async(o,e)=>{const t=x.opts().config;let n=await r(t);if(!n){console.log(i.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),n=o}await g(n,{detail:o,namespace:e.namespace})}),x.command("types").description("Generate TypeScript definitions from translation resource files.").option("-w, --watch","Watch for file changes and re-run the type generator.").action(async o=>{const t=x.opts().config,i=await a(t),r=()=>l(i);if(await r(),o.watch){console.log("\nWatching for changes...");const o=await z(i.types?.input||[]),t=[...j(i.extract?.ignore)].filter(Boolean),a=o.filter(o=>!t.some(e=>n(o,e,{dot:!0})));e.watch(a,{persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),r()})}}),x.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const o=x.opts().config,e=await a(o);await p(e)}),x.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async o=>{await f(o)}),x.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(m),x.command("lint").description("Find potential issues like hardcoded strings in your codebase.").option("-w, --watch","Watch for file changes and re-run the linter.").action(async o=>{const t=x.opts().config,a=async()=>{let o=await r(t);if(!o){console.log(i.blue("No config file found. Attempting to detect project structure..."));const e=await c();e||(console.error(i.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${i.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(i.green("Project structure detected successfully!")),o=e}await d(o)};if(await a(),o.watch){console.log("\nWatching for changes...");const o=await r(t);if(o?.extract?.input){const t=await z(o.extract.input),i=[...j(o.extract.ignore),...k(o.extract.output)].filter(Boolean),r=t.filter(o=>!i.some(e=>n(o,e,{dot:!0})));e.watch(r,{ignored:/node_modules/,persistent:!0}).on("change",o=>{console.log(`\nFile changed: ${o}`),a()})}}}),x.command("locize-sync").description("Synchronize local translations with your locize project.").option("--update-values","Update values of existing translations on locize.").option("--src-lng-only","Check for changes in source language only.").option("--compare-mtime","Compare modification times when syncing.").option("--dry-run","Run the command without making any changes.").action(async o=>{const e=x.opts().config,t=await a(e);await u(t,o)}),x.command("locize-download").description("Download all translations from your locize project.").action(async o=>{const e=x.opts().config,t=await a(e);await y(t,o)}),x.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async o=>{const e=x.opts().config,t=await a(e);await h(t,o)}),x.command("rename-key <oldKey> <newKey>").description("Rename a translation key across all source files and translation files.").option("--dry-run","Preview changes without modifying files").action(async(o,e,t)=>{try{const n=x.opts().config,r=await a(n),c=await w(r,o,e,t);c.success||(c.conflicts&&(console.error(i.red("\n❌ Conflicts detected:")),c.conflicts.forEach(o=>console.error(` - ${o}`))),c.error&&console.error(i.red(`\n❌ ${c.error}`)),process.exit(1));0===c.sourceFiles.reduce((o,e)=>o+e.changes,0)&&console.log(i.yellow(`\n⚠️ No usages found for "${o}"`))}catch(o){console.error(i.red("Error renaming key:"),o),process.exit(1)}}),x.parse(process.argv);const j=o=>Array.isArray(o)?o:o?[o]:[],k=o=>o&&"string"==typeof o?[o.replace(/\{\{[^}]+\}\}/g,"*")]:[],z=async(o=[])=>{const e=j(o),n=await Promise.all(e.map(o=>t(o||"",{nodir:!0})));return Array.from(new Set(n.flat()))};
@@ -1 +1 @@
1
- import{getObjectPropValueExpression as e,getObjectPropValue as t,isSimpleTemplateLiteral as n}from"./ast-utils.js";function i(e){if(e)return"StringLiteral"===e.type?e.value:"TemplateLiteral"===e.type&&n(e)?e.quasis[0].cooked:void 0}function s(e){return"StringLiteral"===e.value?.type?e.value.value:"JSXExpressionContainer"===e.value?.type?i(e.value.expression):void 0}function r(n,r){const o=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),p=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),l=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;a||"JSXAttribute"!==l?.type||"JSXExpressionContainer"!==l.value?.type||"ObjectExpression"!==l.value.expression.type||(u=e(l.value.expression,"count"));const c=!!a||!!u,f=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===f?.type&&"JSXExpressionContainer"===f.value?.type&&"ObjectExpression"===f.value.expression.type?f.value.expression:void 0,g=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),v=!!g,d=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let S="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:"JSXAttribute"===d?.type&&"StringLiteral"===d.value?.type?d.value:void 0;const x=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let m;m="JSXAttribute"===x?.type?s(x):void 0,y&&(void 0===m&&(m=t(y,"ns")),void 0===S&&(S=e(y,"context")));const J=function(e,t){if(!e||0===e.length)return"";const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]),s=e=>e&&"JSXText"===e.type&&/^\s*$/.test(e.value)&&e.value.includes("\n");function r(e,t,o=!1,p=!1){if(!e||!e.length)return;const a=p&&e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length>1;let l=0,u=e.length-1;for(;l<=u&&s(e[l]);)l++;for(;u>=l&&s(e[u]);)u--;const c=l<=u?e.slice(l,u+1):[],f=c.some(e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type));for(let e=0;e<c.length;e++){const p=c[e];if(p)if("JSXText"!==p.type){if("JSXExpressionContainer"===p.type){if(o&&!f&&p.expression){const e=p.expression.type;if("ObjectExpression"===e){const e=p.expression.properties&&p.expression.properties[0];if(e&&"KeyValueProperty"===e.type)continue}const t=i(p.expression);if(void 0!==t){if(!(/^\s*$/.test(t)&&!t.includes("\n")))continue}else if("Identifier"===e||"MemberExpression"===e||"CallExpression"===e)continue}const n=i(p.expression);if(void 0!==n){const i=/^\s*$/.test(n)&&!n.includes("\n"),r=c[e-1],o=c[e+1];if(i){const n=c[e+2];if(o&&"JSXText"===o.type&&s(o)&&n&&("JSXElement"===n.type||"JSXFragment"===n.type)){const t=c[e-1],n=c[e-2];if(!t||"JSXText"!==t.type&&n&&"JSXExpressionContainer"===n.type)continue}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue;const i=!o||"JSXText"===o.type&&!s(o);if(r&&"JSXText"===r.type&&i){const e=t[t.length-1];if(e&&"JSXText"===e.type){e.value=String(e.value)+p.expression.value;continue}}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue}}t.push(p);continue}if("JSXElement"===p.type){const e=p.opening&&p.opening.name&&"Identifier"===p.opening.name.type?p.opening.name.value:void 0;if(e&&n.has(e)){const n=p.opening&&Array.isArray(p.opening.attributes)&&p.opening.attributes.length>0,s=p.children||[],o=1===s.length&&("JSXText"===s[0]?.type||"JSXExpressionContainer"===s[0]?.type&&void 0!==i(s[0].expression)),l=!s.length,u=o;n&&!o?(t.push(p),r(p.children||[],t,!0)):l?t.push(p):u||("p"===e&&a?(t.push(p),r(p.children||[],t,!0,!1)):r(p.children||[],t,!1,!1));continue}t.push(p),r(p.children||[],t,!0);continue}"JSXFragment"!==p.type||r(p.children||[],t,o)}else{if(o&&!f)continue;if(o&&s(p))continue;if(s(p)){const n=c[e-1],i=c[e+1];if(n&&("JSXElement"===n.type||"JSXFragment"===n.type)&&i&&("JSXElement"===i.type||"JSXFragment"===i.type))continue;const s=t[t.length-1],r=c[e-1];if(s){if(r&&"JSXExpressionContainer"===r.type)continue;if("JSXText"===s.type&&r&&"JSXText"===r.type){s.value=String(s.value)+p.value;continue}}}if(o&&f&&0===e)continue;t.push(p)}}}const o=[];r(e,o,!1,!0);const p=new Set,a=e=>String(e).replace(/^\s*\n\s*/g,"").replace(/\s*\n\s*$/g,"");function l(e,t,r=!1){if(!e||0===e.length)return"";let u="",c=0;for(let f=0;f<e.length;f++){const y=e[f];if(y){if("JSXText"===y.type){if(s(y))continue;const t=e[f+1],i=e[f-1];if(i&&"JSXElement"===i.type){const t="Identifier"===i.opening?.name?.type?i.opening.name.value:void 0,s=t&&n.has(t),r=0===(i.children||[]).length;if(s&&r&&/^\s*\n\s*/.test(y.value)){const e=y.value.replace(/^\s*\n\s*/,"");if(e){u+=e;continue}continue}if(!s&&/^\s*\n\s*/.test(y.value)){const t=y.value.replace(/^\s*\n\s*/,"");if(t){const n=e[f-2];if(n&&"JSXText"===n.type){const e=n.value.replace(/\n\s*$/,""),i=/[A-Za-z0-9]$/.test(e),s=/^[A-Za-z0-9]/.test(t),r=/^[a-z]/.test(t);if(i&&s&&r){u+=t;continue}}u+=" "+t;continue}continue}}if(/\n\s*$/.test(y.value)&&t&&"JSXElement"===t.type){const i=y.value.replace(/\n\s*$/,"");if(i.trim()){const r="Identifier"===t.opening?.name?.type?t.opening.name.value:void 0,o=r&&n.has(r),a=(t.children||[]).length>0,l=/\s\n/.test(y.value),c=e[f+2],g=c&&"JSXText"===c.type&&!s(c)&&/[a-zA-Z0-9]/.test(c.value),v=!!(t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0),d=/^\s/.test(i)&&!/^\n/.test(i),S=i.trim(),x=d?" "+S:S,m=/[A-Za-z0-9]$/.test(S),J=c&&"string"==typeof c.value&&/^[A-Za-z0-9]/.test(c.value.trim()),X=c&&"string"==typeof c.value&&/^[a-z]/.test(c.value.trim()),h=c&&"string"==typeof c.value&&/^\s/.test(c.value),$=v&&a&&g&&!(m&&J&&X&&!l&&!d&&!h);m&&J&&X&&!l&&!d&&!h&&p.add(t),u+=l||o&&a||!o&&g&&d||$?x+" ":x;continue}}u+=y.value;continue}if("JSXExpressionContainer"===y.type){const e=y.expression;if(!e)continue;const t=i(e);if(void 0!==t)u+=t;else if("Identifier"===e.type)u+=`{{${e.value}}}`;else if("ObjectExpression"===e.type){const t=e.properties[0];t&&"KeyValueProperty"===t.type&&t.key&&"Identifier"===t.key.type?u+=`{{${t.key.value}}}`:t&&"Identifier"===t.type?u+=`{{${t.value}}}`:u+="{{value}}"}else"MemberExpression"===e.type&&e.property&&"Identifier"===e.property.type?u+=`{{${e.property.value}}}`:"CallExpression"===e.type&&"Identifier"===e.callee?.type?u+=`{{${e.callee.value}}}`:u+="{{value}}";continue}if("JSXElement"===y.type){let p;y.opening&&y.opening.name&&"Identifier"===y.opening.name.type&&(p=y.opening.name.value);const g=r?c:void 0;if(r&&"JSXElement"===y.type&&c++,p&&n.has(p)){const r=y.opening&&Array.isArray(y.opening.attributes)&&y.opening.attributes.length>0,c=y.children||[],v=c.length>0,d=1===c.length&&("JSXText"===c[0]?.type||"JSXExpressionContainer"===c[0]?.type&&void 0!==i(c[0].expression));if(!v||d){const t=d?l(c,void 0):"";if(""!==String(t).trim())u+=`<${p}>${t}</${p}>`;else{const t=e[f-1];t&&"JSXText"===t.type&&/\n\s*$/.test(t.value)&&(u=u.replace(/\s+$/,"")),u+=`<${p} />`}}else if(r&&!d){const e=c;if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(y),n=l(e,void 0);u+=`<${t}>${a(n)}</${t}>`}else{const e=t&&t.has(y)?t.get(y):o.indexOf(y),s=new Map;let r="number"==typeof e&&e>=0?e:0;for(const e of c)if(e&&"JSXElement"===e.type){const t=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(t&&n.has(t)){const t=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,n=e.children||[],o=1===n.length&&("JSXText"===n[0]?.type||"JSXExpressionContainer"===n[0]?.type&&void 0!==i(n[0].expression));!t&&(!n.length||o)||s.set(e,r++)}else s.set(e,r++)}const p=l(c,s.size?s:void 0);u+=`<${e}>${a(p)}</${e}>`}}else{const e=o.indexOf(y);if(-1!==e){const t=void 0!==g?g:e;if((()=>{let e=!1;for(const t of c)if(t)if("JSXElement"!==t.type){if("JSXExpressionContainer"===t.type&&-1!==o.indexOf(t))return e;if("JSXText"===t.type&&-1!==o.indexOf(t)){if(s(t))continue;if(!e)return!0;if(c.slice(c.indexOf(t)+1).some(e=>e&&"JSXElement"===e.type))return!0}}else e=!0;return!1})()){const e=l(c,void 0,!1);u+=`<${t}>${a(e)}</${t}>`;continue}const r=new Map;let p=t;for(const e of c)if(e&&"JSXElement"===e.type){const t=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(t&&n.has(t)){const t=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,n=e.children||[],s=1===n.length&&("JSXText"===n[0]?.type||"JSXExpressionContainer"===n[0]?.type&&void 0!==i(n[0].expression));!t&&(!n.length||s)||r.set(e,p++)}else r.set(e,p++)}const f=l(c,r.size>0?r:void 0,!1);u+=`<${t}>${a(f)}</${t}>`}else{const e=l(c,void 0,!1);u+=`<${p}>${a(e)}</${p}>`}}}else{const e=y.children||[];if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(y),n=l(e,void 0);u+=`<${t}>${a(n)}</${t}>`}else{const i=new Map,s=t&&t.has(y)?t.get(y):o.indexOf(y);let r="number"==typeof s&&s>=0?s:0;for(const t of e)if(t&&"JSXElement"===t.type){const e=t.opening&&t.opening.name&&"Identifier"===t.opening.name.type?t.opening.name.value:void 0;if(e&&n.has(e)){const e=t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0,n=t.children||[],s=1===n.length&&"JSXText"===n[0]?.type;!e&&(!n.length||s)||i.set(t,r++)}else i.set(t,r++)}const p=l(e,i.size?i:void 0);u+=`<${s}>${a(p)}</${s}>`}}continue}"JSXFragment"!==y.type||(u+=l(y.children||[]))}}return u}const u=l(e,void 0,!0),c=String(u).replace(/<br \/>\s*\n\s*/g,"<br />"),f=String(c),y=new Set;if(p&&p.size>0)for(let e=0;e<o.length;e++)p.has(o[e])&&y.add(e);let g=String(f);if(y&&y.size>0)for(const e of y)try{g=g.replace(new RegExp("\\s+<"+e+">","g"),"<"+e+">"),g=g.replace(new RegExp("<\\/"+e+">\\s+","g"),"</"+e+">")}catch(e){}g=g.replace(/<\/(\d+)>\s*\n\s*(\S)/g,(e,t,n)=>{const i=Number(t);return y.has(i)?`</${t}>${n}`:`</${t}> ${n}`}),g=g.replace(/(\S)\s*\n\s*<(\d+)/g,(e,t,n)=>{const i=Number(n);return y.has(i)?`${t}<${n}`:`${t} <${n}`}),g=g.replace(/\s*\n\s*/g," "),g=g.replace(/\s+/g," "),g=g.replace(/\s+\./g,".");const v=g.trim();let d=String(v);if(y&&y.size>0)for(const e of y)try{d=d.replace(new RegExp("[\\s\\u00A0]+<"+e+">","g"),"<"+e+">"),d=d.replace(new RegExp("<\\/"+e+">[\\s\\u00A0]+","g"),"</"+e+">")}catch(e){}try{for(let e=0;e<o.length;e++){const t=o[e];if(!t||"JSXElement"!==t.type)continue;const n=o[e-1],i=o[e+1];if(!n||!i)continue;if("JSXText"!==n.type||"JSXText"!==i.type)continue;const s=String(n.value),r=String(i.value),p=s.replace(/\n\s*$/,""),a=/[A-Za-z0-9]$/.test(p),l=/^[A-Za-z0-9]/.test(r.trim()),u=/^[a-z]/.test(r.trim()),c=/\s\n/.test(s),f=r&&/^\s/.test(r)&&!/^\n/.test(r);if(a&&l&&u&&!c&&!f){const t=e;d=d.replace(new RegExp("\\s+<"+t+">","g"),"<"+t+">")}}}catch(e){}return d.trim()}(n.children,r);let X;const h="JSXAttribute"===p?.type?s(p):void 0;if(void 0!==h)X=h;else{const e=r.extract.defaultValue;X="string"==typeof e?e:""}let $,E;if("JSXAttribute"===o?.type){if("StringLiteral"===o.value?.type){if($=o.value,E=$.value,!E||""===E.trim())return null;if(m&&"StringLiteral"===$.type){const e=r.extract.nsSeparator??":",t=$.value;if(e&&t.startsWith(`${m}${e}`)){if(E=t.slice(`${m}${e}`.length),!E||""===E.trim())return null;$={...$,value:E}}}}else"JSXExpressionContainer"===o.value?.type&&"JSXEmptyExpression"!==o.value.expression.type&&($=o.value.expression);if(!$)return null}p||!E||J.trim()?!p&&J.trim()&&(X=J):X=E;return{keyExpression:$,serializedChildren:J,ns:m,defaultValue:X,hasCount:c,isOrdinal:v,contextExpression:S,optionsNode:y,explicitDefault:void 0!==h||(e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1})(y)}}export{r as extractFromTransComponent};
1
+ import{getObjectPropValueExpression as e,getObjectPropValue as t,isSimpleTemplateLiteral as n}from"./ast-utils.js";function i(e){if(e)return"StringLiteral"===e.type?e.value:"TemplateLiteral"===e.type&&n(e)?e.quasis[0].cooked:void 0}function s(e){return"StringLiteral"===e.value?.type?e.value.value:"JSXExpressionContainer"===e.value?.type?i(e.value.expression):void 0}function r(n,r){const o=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),p=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),l=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;a||"JSXAttribute"!==l?.type||"JSXExpressionContainer"!==l.value?.type||"ObjectExpression"!==l.value.expression.type||(u=e(l.value.expression,"count"));const c=!!a||!!u,f=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===f?.type&&"JSXExpressionContainer"===f.value?.type&&"ObjectExpression"===f.value.expression.type?f.value.expression:void 0,g=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),d=!!g,x=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let v="JSXAttribute"===x?.type&&"JSXExpressionContainer"===x.value?.type?x.value.expression:"JSXAttribute"===x?.type&&"StringLiteral"===x.value?.type?x.value:void 0;const h=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let S;S="JSXAttribute"===h?.type?s(h):void 0,y&&(void 0===S&&(S=t(y,"ns")),void 0===v&&(v=e(y,"context")));const m=function(e,t){if(!e||0===e.length)return"";const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]),s=e=>e&&"JSXText"===e.type&&/^\s*$/.test(e.value)&&e.value.includes("\n");function r(e,t,o=!1,p=!1){if(!e||!e.length)return;const a=p&&e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length>1;let l=0,u=e.length-1;for(;l<=u&&s(e[l]);)l++;for(;u>=l&&s(e[u]);)u--;const c=l<=u?e.slice(l,u+1):[],f=c.some(e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type));for(let e=0;e<c.length;e++){const p=c[e];if(p)if("JSXText"!==p.type){if("JSXExpressionContainer"===p.type){if(o&&!f&&p.expression){const e=p.expression.type;if("ObjectExpression"===e){const e=p.expression.properties&&p.expression.properties[0];if(e&&"KeyValueProperty"===e.type)continue}const t=i(p.expression);if(void 0!==t){if(!(/^\s*$/.test(t)&&!t.includes("\n")))continue}else if("Identifier"===e||"MemberExpression"===e||"CallExpression"===e)continue}const n=i(p.expression);if(void 0!==n){const i=/^\s*$/.test(n)&&!n.includes("\n"),r=c[e-1],o=c[e+1];if(i){const n=c[e+2];if(o&&"JSXText"===o.type&&s(o)&&n&&("JSXElement"===n.type||"JSXFragment"===n.type)){const t=c[e-1],n=c[e-2];if(!t||"JSXText"!==t.type&&n&&"JSXExpressionContainer"===n.type)continue}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue;const i=!o||"JSXText"===o.type&&!s(o);if(r&&"JSXText"===r.type&&i){const e=t[t.length-1];if(e&&"JSXText"===e.type){e.value=String(e.value)+p.expression.value;continue}}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&o&&"JSXText"===o.type&&s(o))continue}}t.push(p);continue}if("JSXElement"===p.type){const e=p.opening&&p.opening.name&&"Identifier"===p.opening.name.type?p.opening.name.value:void 0;if(e&&n.has(e)){const n=p.opening&&Array.isArray(p.opening.attributes)&&p.opening.attributes.length>0,s=p.children||[],o=1===s.length&&("JSXText"===s[0]?.type||"JSXExpressionContainer"===s[0]?.type&&void 0!==i(s[0].expression)),l=!s.length,u=o;n&&!o?(t.push(p),r(p.children||[],t,!0)):l?t.push(p):u||("p"===e&&a?(t.push(p),r(p.children||[],t,!0,!1)):r(p.children||[],t,!1,!1));continue}t.push(p),r(p.children||[],t,!0);continue}"JSXFragment"!==p.type||r(p.children||[],t,o)}else{if(o&&!f)continue;if(o&&s(p))continue;if(s(p)){const n=c[e-1],i=c[e+1];if(n&&("JSXElement"===n.type||"JSXFragment"===n.type)&&i&&("JSXElement"===i.type||"JSXFragment"===i.type))continue;const s=t[t.length-1],r=c[e-1];if(s){if(r&&"JSXExpressionContainer"===r.type)continue;if("JSXText"===s.type&&r&&"JSXText"===r.type){s.value=String(s.value)+p.value;continue}}}if(o&&f&&0===e)continue;t.push(p)}}}const o=[];function p(e){if(!e||!e.length)return!1;let t=!1;for(const n of e)if(n)if("JSXElement"!==n.type){if("JSXExpressionContainer"===n.type&&-1!==o.indexOf(n))return t;if("JSXText"===n.type&&-1!==o.indexOf(n)){if(s(n))continue;if(!t)return!0;const i=e.indexOf(n);if(e.slice(i+1).some(e=>e&&"JSXElement"===e.type))return!0}}else t=!0;return!1}r(e,o,!1,!0);const a=new Set,l=e=>String(e).replace(/^\s*\n\s*/g,"").replace(/\s*\n\s*$/g,"");function u(e,t,r=!1){if(!e||0===e.length)return"";let c="";const f=e=>{if(!e)return-1;if(t&&t.has(e))return t.get(e);if(t)for(const[n,i]of t.entries())try{if(n&&e&&n.span&&e.span&&n.span.start===e.span.start&&n.span.end===e.span.end)return i}catch(e){}return o.indexOf(e)};let y=0;for(let t=0;t<e.length;t++){const g=e[t];if(g){if("JSXText"===g.type){if(s(g))continue;const i=e[t+1],r=e[t-1];if(r&&"JSXElement"===r.type){const i="Identifier"===r.opening?.name?.type?r.opening.name.value:void 0,s=i&&n.has(i),o=0===(r.children||[]).length;if(s&&o&&/^\s*\n\s*/.test(g.value)){const e=g.value.replace(/^\s*\n\s*/,"");if(e){c+=e;continue}continue}if(!s&&/^\s*\n\s*/.test(g.value)){const n=g.value.replace(/^\s*\n\s*/,"");if(n){const i=e[t-2];if(i&&"JSXText"===i.type){const e=i.value.replace(/\n\s*$/,""),t=/[A-Za-z0-9]$/.test(e),s=/^[A-Za-z0-9]/.test(n),r=/^[a-z]/.test(n);if(t&&s&&r){c+=n;continue}}c+=" "+n;continue}continue}}if(/\n\s*$/.test(g.value)&&i&&"JSXElement"===i.type){const r=g.value.replace(/\n\s*$/,"");if(r.trim()){const o="Identifier"===i.opening?.name?.type?i.opening.name.value:void 0,p=o&&n.has(o),l=(i.children||[]).length>0,u=/\s\n/.test(g.value),f=e[t+2],y=f&&"JSXText"===f.type&&!s(f)&&/[a-zA-Z0-9]/.test(f.value),d=!!(i.opening&&Array.isArray(i.opening.attributes)&&i.opening.attributes.length>0),x=/^\s/.test(r)&&!/^\n/.test(r),v=r.trim(),h=x?" "+v:v,S=/[A-Za-z0-9]$/.test(v),m=f&&"string"==typeof f.value&&/^[A-Za-z0-9]/.test(f.value.trim()),J=f&&"string"==typeof f.value&&/^[a-z]/.test(f.value.trim()),X=f&&"string"==typeof f.value&&/^\s/.test(f.value)&&!/^\n/.test(f.value),$=d&&l&&y&&!(S&&m&&J&&!u&&!x&&!X),E=f&&"string"==typeof f.value&&/^[,;:!?.]/.test(f.value.trim()),b=$&&!E;S&&m&&J&&!u&&!x&&!X&&a.add(i),c+=u||p&&l||!p&&y&&x||b?h+" ":h;continue}}c+=g.value;continue}if("JSXExpressionContainer"===g.type){const e=g.expression;if(!e)continue;const t=i(e);if(void 0!==t)c+=t;else if("Identifier"===e.type)c+=`{{${e.value}}}`;else if("ObjectExpression"===e.type){const t=e.properties[0];t&&"KeyValueProperty"===t.type&&t.key&&"Identifier"===t.key.type?c+=`{{${t.key.value}}}`:t&&"Identifier"===t.type?c+=`{{${t.value}}}`:c+="{{value}}"}else"MemberExpression"===e.type&&e.property&&"Identifier"===e.property.type?c+=`{{${e.property.value}}}`:"CallExpression"===e.type&&"Identifier"===e.callee?.type?c+=`{{${e.callee.value}}}`:c+="{{value}}";continue}if("JSXElement"===g.type){let a;g.opening&&g.opening.name&&"Identifier"===g.opening.name.type&&(a=g.opening.name.value);const d=r?y:void 0;if(r&&"JSXElement"===g.type&&y++,a&&n.has(a)){const r=g.opening&&Array.isArray(g.opening.attributes)&&g.opening.attributes.length>0,y=g.children||[],x=y.length>0,v=1===y.length&&("JSXText"===y[0]?.type||"JSXExpressionContainer"===y[0]?.type&&void 0!==i(y[0].expression));if(!x||v){const n=v?u(y,void 0):"";if(""!==String(n).trim())c+=`<${a}>${n}</${a}>`;else{const n=e[t-1];n&&"JSXText"===n.type&&/\n\s*$/.test(n.value)&&(c=c.replace(/\s+$/,"")),c+=`<${a} />`}}else if(r&&!v){const e=y,t=f(g);if(p(e)){const s=new Map;let r=1;for(const t of e)if(t&&"JSXElement"===t.type){const e=t.opening&&t.opening.name&&"Identifier"===t.opening.name.type?t.opening.name.value:void 0;if(e&&n.has(e)){const e=t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0,n=t.children||[],o=1===n.length&&("JSXText"===n[0]?.type||"JSXExpressionContainer"===n[0]?.type&&void 0!==i(n[0].expression));!e&&(!n.length||o)||s.set(t,r++)}else s.set(t,r++)}const o=u(e,s.size?s:void 0);c+=`<${t}>${l(o)}</${t}>`}else{const e=new Map;let s=1;for(const t of y)if(t&&"JSXElement"===t.type){const r=t.opening&&t.opening.name&&"Identifier"===t.opening.name.type?t.opening.name.value:void 0;if(r&&n.has(r)){const n=t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0,r=t.children||[],o=1===r.length&&("JSXText"===r[0]?.type||"JSXExpressionContainer"===r[0]?.type&&void 0!==i(r[0].expression));!n&&(!r.length||o)||e.set(t,s++)}else e.set(t,s++)}const r=u(y,e.size?e:void 0);c+=`<${t}>${l(r)}</${t}>`}}else{const e=o.indexOf(g);if(-1!==e){const t=void 0!==d?d:e;if((()=>{let e=!1;for(const t of y)if(t)if("JSXElement"!==t.type){if("JSXExpressionContainer"===t.type&&-1!==o.indexOf(t))return e;if("JSXText"===t.type&&-1!==o.indexOf(t)){if(s(t))continue;if(!e)return!0;if(y.slice(y.indexOf(t)+1).some(e=>e&&"JSXElement"===e.type))return!0}}else e=!0;return!1})()){if(0===t){const e=u(y,void 0,!1);c+=`<${t}>${l(e)}</${t}>`;continue}const e=new Map;let s=1;for(const t of y)if(t&&"JSXElement"===t.type){const r=t.opening&&t.opening.name&&"Identifier"===t.opening.name.type?t.opening.name.value:void 0;if(r&&n.has(r)){const n=t.opening&&Array.isArray(t.opening.attributes)&&t.opening.attributes.length>0,r=t.children||[],o=1===r.length&&("JSXText"===r[0]?.type||"JSXExpressionContainer"===r[0]?.type&&void 0!==i(r[0].expression));!n&&(!r.length||o)||e.set(t,s++)}else e.set(t,s++)}const r=u(y,e.size?e:void 0,!1);c+=`<${t}>${l(r)}</${t}>`;continue}const r=new Map;let p=1;for(const e of y)if(e&&"JSXElement"===e.type){const t=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0;if(t&&n.has(t)){const t=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,n=e.children||[],s=1===n.length&&("JSXText"===n[0]?.type||"JSXExpressionContainer"===n[0]?.type&&void 0!==i(n[0].expression));!t&&(!n.length||s)||r.set(e,p++)}else r.set(e,p++)}const a=u(y,r.size>0?r:void 0,!1);c+=`<${t}>${l(a)}</${t}>`}else{const e=u(y,void 0,!1);c+=`<${a}>${l(e)}</${a}>`}}}else{const e=g.children||[];if(e.some(e=>e&&("JSXText"===e.type||"JSXExpressionContainer"===e.type)&&-1!==o.indexOf(e))){const t=o.indexOf(g),n=u(e,void 0);c+=`<${t}>${l(n)}</${t}>`}else{const t=new Map,i=f(g);let s=1;for(const i of e)if(i&&"JSXElement"===i.type){const e=i.opening&&i.opening.name&&"Identifier"===i.opening.name.type?i.opening.name.value:void 0;if(e&&n.has(e)){const e=i.opening&&Array.isArray(i.opening.attributes)&&i.opening.attributes.length>0,n=i.children||[],r=1===n.length&&"JSXText"===n[0]?.type;!e&&(!n.length||r)||t.set(i,s++)}else t.set(i,s++)}const r=u(e,t.size?t:void 0);c+=`<${i}>${l(r)}</${i}>`}}continue}"JSXFragment"!==g.type||(c+=u(g.children||[]))}}return c}const c=u(e,void 0,!0),f=String(c).replace(/<br \/>\s*\n\s*/g,"<br />"),y=String(f),g=new Set;if(a&&a.size>0)for(let e=0;e<o.length;e++)a.has(o[e])&&g.add(e);let d=String(y);if(g&&g.size>0)for(const e of g)try{d=d.replace(new RegExp("\\s+<"+e+">","g"),"<"+e+">"),d=d.replace(new RegExp("<\\/"+e+">\\s+","g"),"</"+e+">")}catch(e){}d=d.replace(/<\/(\d+)>\s*\n\s*(\S)/g,(e,t,n)=>{const i=Number(t);return g.has(i)?`</${t}>${n}`:`</${t}> ${n}`}),d=d.replace(/(\S)\s*\n\s*<(\d+)/g,(e,t,n)=>{const i=Number(n);return g.has(i)?`${t}<${n}`:`${t} <${n}`}),d=d.replace(/\s*\n\s*/g," "),d=d.replace(/\s+/g," "),d=d.replace(/\s+([,;:!?.])/g,"$1");const x=d.trim();let v=String(x);if(g&&g.size>0)for(const e of g)try{v=v.replace(new RegExp("[\\s\\u00A0]+<"+e+">","g"),"<"+e+">"),v=v.replace(new RegExp("<\\/"+e+">[\\s\\u00A0]+","g"),"</"+e+">")}catch(e){}try{for(let e=0;e<o.length;e++){const t=o[e];if(!t||"JSXElement"!==t.type)continue;const n=o[e-1],i=o[e+1];if(!n||!i)continue;if("JSXText"!==n.type||"JSXText"!==i.type)continue;const s=String(n.value),r=String(i.value),p=s.replace(/\n\s*$/,""),a=/[A-Za-z0-9]$/.test(p),l=/^[A-Za-z0-9]/.test(r.trim()),u=/^[a-z]/.test(r.trim()),c=/\s\n/.test(s),f=r&&/^\s/.test(r)&&!/^\n/.test(r);if(a&&l&&u&&!c&&!f){const t=e;v=v.replace(new RegExp("\\s+<"+t+">","g"),"<"+t+">")}}}catch(e){}function h(e){if(!e||!e.includes("<"))return e;function t(e){const t=[],n=[];let i=0;const s=/<\/?(\d+)>|<[^>]+>/g;let r;for(;r=s.exec(e);){const o=r[0],p=r.index;if(p>i){const s={type:"text",text:e.slice(i,p)};n.length?n[n.length-1].node.children.push(s):t.push(s)}const a=/^<\/(\d+)>$/.exec(o),l=/^<(\d+)>$/.exec(o);if(l){const e=Number(l[1]),i={type:"ph",idx:e,children:[]};n.length?n[n.length-1].node.children.push(i):t.push(i),n.push({node:i,idx:e})}else if(a)n.length&&n.pop();else{const e={type:"text",text:o};n.length?n[n.length-1].node.children.push(e):t.push(e)}i=s.lastIndex}if(i<e.length){const s={type:"text",text:e.slice(i)};n.length?n[n.length-1].node.children.push(s):t.push(s)}return t}function n(e,t=null){let i="";for(const t of e)if("text"===t.type)i+=t.text;else{const e=t.children.filter(e=>"ph"===e.type),s=e.map(e=>e.idx);if(!(s.length<=1||s.every((e,t)=>0===t||e===s[t-1]+1))){const e=t.children.map(e=>"text"===e.type?e.text:`<${e.idx}>${n(e.children,e.idx)}</${e.idx}>`).join("");i+=`<${t.idx}>${e}</${t.idx}>`;continue}const r=new Map;let p=0===t.idx?0:1;try{const e=o[t.idx];if(e&&e.span){const n=e.span.start,i=e.span.end;let s=-1;for(let e=t.idx+1;e<o.length;e++){const t=o[e];if(t&&t.span&&(t.span.start>=n&&t.span.end<=i&&"JSXElement"===t.type)){s=e;break}}let r=0;if(-1!==s)for(let e=t.idx+1;e<s;e++){const t=o[e];t&&t.span&&(t.span.start>=n&&t.span.end<=i&&("JSXText"!==t.type&&"JSXExpressionContainer"!==t.type||r++))}else for(let e=t.idx+1;e<o.length;e++){const t=o[e];t&&t.span&&(t.span.start>=n&&t.span.end<=i&&("JSXText"!==t.type&&"JSXExpressionContainer"!==t.type||r++))}"number"==typeof t.idx&&(p=0===t.idx?0:Math.max(1,r+1))}}catch(e){}for(const t of e)r.has(t.idx)||r.set(t.idx,p++);const a=t.children.map(e=>{if("text"===e.type)return e.text;const t=e.idx,i=r.has(t)?r.get(t):t;return`<${i}>${n(e.children,i)}</${i}>`}).join("");i+=`<${t.idx}>${a}</${t.idx}>`}return i}try{return n(t(e))}catch(t){return e}}return v=h(v),v.trim()}(n.children,r);let J;const X="JSXAttribute"===p?.type?s(p):void 0;if(void 0!==X)J=X;else{const e=r.extract.defaultValue;J="string"==typeof e?e:""}let $,E;if("JSXAttribute"===o?.type){if("StringLiteral"===o.value?.type){if($=o.value,E=$.value,!E||""===E.trim())return null;if(S&&"StringLiteral"===$.type){const e=r.extract.nsSeparator??":",t=$.value;if(e&&t.startsWith(`${S}${e}`)){if(E=t.slice(`${S}${e}`.length),!E||""===E.trim())return null;$={...$,value:E}}}}else"JSXExpressionContainer"===o.value?.type&&"JSXEmptyExpression"!==o.value.expression.type&&($=o.value.expression);if(!$)return null}p||!E||m.trim()?!p&&m.trim()&&(J=m):J=E;return{keyExpression:$,serializedChildren:m,ns:S,defaultValue:J,hasCount:c,isOrdinal:d,contextExpression:v,optionsNode:y,explicitDefault:void 0!==X||(e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1})(y)}}export{r as extractFromTransComponent};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next-cli",
3
- "version": "1.24.5",
3
+ "version": "1.24.7",
4
4
  "description": "A unified, high-performance i18next CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -23,7 +23,7 @@ const program = new Command()
23
23
  program
24
24
  .name('i18next-cli')
25
25
  .description('A unified, high-performance i18next CLI.')
26
- .version('1.24.5')
26
+ .version('1.24.7')
27
27
 
28
28
  // new: global config override option
29
29
  program.option('-c, --config <path>', 'Path to i18next-cli config file (overrides detection)')
@@ -578,6 +578,41 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
578
578
  const globalSlots: any[] = []
579
579
  collectSlots(children, globalSlots, false, true)
580
580
 
581
+ // Helper: more precise detection whether children contain non-element global
582
+ // slots that should force the parent to use global indexing. This mirrors
583
+ // the later inlined logic and avoids counting trailing text as forcing global.
584
+ function hasNonElementGlobalSlotsAmongChildren (childrenList: any[]) {
585
+ if (!childrenList || !childrenList.length) return false
586
+ let foundElement = false
587
+ for (const ch of childrenList) {
588
+ if (!ch) continue
589
+ if (ch.type === 'JSXElement') {
590
+ foundElement = true
591
+ continue
592
+ }
593
+ if (ch.type === 'JSXExpressionContainer' && globalSlots.indexOf(ch) !== -1) {
594
+ // Only count an expression as forcing global indexing when it appears
595
+ // after at least one element (i.e. between elements), not if it's before.
596
+ return foundElement
597
+ }
598
+ if (ch.type === 'JSXText' && globalSlots.indexOf(ch) !== -1) {
599
+ // Exclude pure formatting whitespace
600
+ if (isFormattingWhitespace(ch)) continue
601
+
602
+ // If text appears before the first element -> force global indexing
603
+ if (!foundElement) return true
604
+
605
+ // If text is between elements -> force global indexing
606
+ const idx = childrenList.indexOf(ch)
607
+ const remaining = childrenList.slice(idx + 1)
608
+ const hasMoreElements = remaining.some((n: any) => n && n.type === 'JSXElement')
609
+ if (hasMoreElements) return true
610
+ // Trailing text after last element does not force global indexing
611
+ }
612
+ }
613
+ return false
614
+ }
615
+
581
616
  // Track ELEMENT NODES that MUST be tight (no spaces) because the element splits a word across a newline.
582
617
  // We'll map these node refs to numeric global indices later before string cleanup.
583
618
  const tightNoSpaceNodes = new Set<any>()
@@ -595,6 +630,22 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
595
630
  if (!nodes || nodes.length === 0) return ''
596
631
  let out = ''
597
632
 
633
+ // Resolve a numeric index for a node using localIndexMap when possible.
634
+ // Some AST node references may not match by identity in maps (e.g. after cloning),
635
+ // so also attempt to match by span positions as a fallback.
636
+ const resolveIndex = (n: any) => {
637
+ if (!n) return -1
638
+ if (localIndexMap && localIndexMap.has(n)) return localIndexMap.get(n)
639
+ if (localIndexMap) {
640
+ for (const [k, v] of localIndexMap.entries()) {
641
+ try {
642
+ if (k && n && k.span && n.span && k.span.start === n.span.start && k.span.end === n.span.end) return v
643
+ } catch (e) { /* ignore */ }
644
+ }
645
+ }
646
+ return globalSlots.indexOf(n)
647
+ }
648
+
598
649
  // At root level, build index based on element position among siblings
599
650
  let rootElementIndex = 0
600
651
 
@@ -703,7 +754,8 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
703
754
  const prevEndsWithAlnum = /[A-Za-z0-9]$/.test(trimmed)
704
755
  const nextStartsWithAlnum = nodeAfterNext && typeof nodeAfterNext.value === 'string' && /^[A-Za-z0-9]/.test(nodeAfterNext.value.trim())
705
756
  const nextStartsWithLowercase = nodeAfterNext && typeof nodeAfterNext.value === 'string' && /^[a-z]/.test(nodeAfterNext.value.trim())
706
- const nextHasLeadingSpace = nodeAfterNext && typeof nodeAfterNext.value === 'string' && /^\s/.test(nodeAfterNext.value)
757
+ // Treat newline-leading indentation as NOT an explicit leading space.
758
+ const nextHasLeadingSpace = nodeAfterNext && typeof nodeAfterNext.value === 'string' && /^\s/.test(nodeAfterNext.value) && !/^\n/.test(nodeAfterNext.value)
707
759
 
708
760
  // Only treat as a word-split (no space) when the following word begins
709
761
  // with a lowercase letter — this avoids removing spaces between separate
@@ -716,6 +768,9 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
716
768
  !hasLeadingSpace &&
717
769
  !nextHasLeadingSpace
718
770
  )
771
+ // If the text after the next element begins with punctuation, do not insert a space
772
+ const nextStartsWithPunctuation = nodeAfterNext && typeof nodeAfterNext.value === 'string' && /^[,;:!?.]/.test(nodeAfterNext.value.trim())
773
+ const shouldInsertForNextWithAttrsFinal = shouldInsertForNextWithAttrs && !nextStartsWithPunctuation
719
774
 
720
775
  // Persist a "tight" decision so post-normalization can remove any artificial
721
776
  // spaces that were introduced by whitespace collapsing/newline handling.
@@ -732,7 +787,7 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
732
787
  // non-preserved with text after must have an explicit leading space
733
788
  (!isPreservedTag && hasTextAfter && hasLeadingSpace) ||
734
789
  // next element with attrs: only when not a word-split (see above)
735
- shouldInsertForNextWithAttrs
790
+ shouldInsertForNextWithAttrsFinal
736
791
  ) {
737
792
  out += withLeading + ' '
738
793
  } else {
@@ -828,21 +883,50 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
828
883
  } else if (hasAttrs && !isSinglePureTextChild) {
829
884
  // Has attributes -> treat as indexed element with numeric placeholder
830
885
  const childrenLocal = children
831
- const hasNonElementGlobalSlots = childrenLocal.some((ch: any) =>
832
- ch && (ch.type === 'JSXText' || ch.type === 'JSXExpressionContainer') && globalSlots.indexOf(ch) !== -1
833
- )
886
+ // determine this element's numeric index once for all branches
887
+ const idx = resolveIndex(node)
888
+ // Use precise detection so trailing text doesn't force global indexing
889
+ const hasNonElementGlobalSlots = hasNonElementGlobalSlotsAmongChildren(childrenLocal)
834
890
 
835
891
  if (hasNonElementGlobalSlots) {
836
- const idx = globalSlots.indexOf(node)
837
- const inner = visitNodes(childrenLocal, undefined)
892
+ // Build a local index map for inner children so nested placeholders
893
+ // restart locally instead of using global indices.
894
+ const childrenLocalMap = new Map<any, number>()
895
+ // always restart local child indices at 1
896
+ let localIdxCounter = 1
897
+ for (const ch of childrenLocal) {
898
+ if (!ch) continue
899
+ if (ch.type === 'JSXElement') {
900
+ const chTag = ch.opening && ch.opening.name && ch.opening.name.type === 'Identifier'
901
+ ? ch.opening.name.value
902
+ : undefined
903
+ if (chTag && allowedTags.has(chTag)) {
904
+ const chHasAttrs =
905
+ ch.opening &&
906
+ Array.isArray((ch.opening as any).attributes) &&
907
+ (ch.opening as any).attributes.length > 0
908
+ const chChildren = ch.children || []
909
+ const chIsSinglePureText =
910
+ chChildren.length === 1 && (
911
+ chChildren[0]?.type === 'JSXText' ||
912
+ (chChildren[0]?.type === 'JSXExpressionContainer' &&
913
+ getStringLiteralFromExpression(chChildren[0].expression) !== undefined)
914
+ )
915
+ const chWillBePreserved = !chHasAttrs && (!chChildren.length || chIsSinglePureText)
916
+ if (!chWillBePreserved) {
917
+ childrenLocalMap.set(ch, localIdxCounter++)
918
+ }
919
+ } else {
920
+ childrenLocalMap.set(ch, localIdxCounter++)
921
+ }
922
+ }
923
+ }
924
+ const inner = visitNodes(childrenLocal, childrenLocalMap.size ? childrenLocalMap : undefined)
838
925
  out += `<${idx}>${trimFormattingEdges(inner)}</${idx}>`
839
926
  } else {
840
- // Determine the starting index for local child indexes:
841
- // reuse the parent's global index so nested placeholders get
842
- // indices consistent with parent indexing (avoid starting at 0).
843
- const idx = localIndexMap && localIndexMap.has(node) ? localIndexMap.get(node) : globalSlots.indexOf(node)
844
927
  const childrenLocalMap = new Map<any, number>()
845
- let localIdxCounter = typeof idx === 'number' && idx >= 0 ? idx : 0
928
+ // Local child indexes always restart at 1 for the inner mapping.
929
+ let localIdxCounter = 1
846
930
 
847
931
  for (const ch of children) {
848
932
  if (!ch) continue
@@ -932,14 +1016,57 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
932
1016
  // If children have non-element global slots, use global indexes
933
1017
  // Otherwise use local indexes starting from parent's index + 1
934
1018
  if (hasNonElementGlobalSlots) {
935
- const inner = visitNodes(children, undefined, false)
1019
+ // For non-root parents we compact child indexes locally.
1020
+ // For root-level parents (index 0) preserve global indexes so tests
1021
+ // that expect global numbering (1,3,5...) keep working.
1022
+ if (indexToUse === 0) {
1023
+ const inner = visitNodes(children, undefined, false)
1024
+ out += `<${indexToUse}>${trimFormattingEdges(inner)}</${indexToUse}>`
1025
+ continue
1026
+ }
1027
+
1028
+ // Build a local index map for the inner children so nested placeholders
1029
+ // restart locally (avoids leaking global indices into the parent's inner string).
1030
+ const childrenLocalMap = new Map<any, number>()
1031
+ // local children numbering should start at 1
1032
+ let localIdxCounter = 1
1033
+ for (const ch of children) {
1034
+ if (!ch) continue
1035
+ if (ch.type === 'JSXElement') {
1036
+ const chTag = ch.opening && ch.opening.name && ch.opening.name.type === 'Identifier'
1037
+ ? ch.opening.name.value
1038
+ : undefined
1039
+ if (chTag && allowedTags.has(chTag)) {
1040
+ const chHasAttrs =
1041
+ ch.opening &&
1042
+ Array.isArray((ch.opening as any).attributes) &&
1043
+ (ch.opening as any).attributes.length > 0
1044
+ const chChildren = ch.children || []
1045
+ const chIsSinglePureText =
1046
+ chChildren.length === 1 && (
1047
+ chChildren[0]?.type === 'JSXText' ||
1048
+ (chChildren[0]?.type === 'JSXExpressionContainer' &&
1049
+ getStringLiteralFromExpression(chChildren[0].expression) !== undefined)
1050
+ )
1051
+ const chWillBePreserved = !chHasAttrs && (!chChildren.length || chIsSinglePureText)
1052
+ if (!chWillBePreserved) {
1053
+ childrenLocalMap.set(ch, localIdxCounter++)
1054
+ }
1055
+ } else {
1056
+ childrenLocalMap.set(ch, localIdxCounter++)
1057
+ }
1058
+ }
1059
+ }
1060
+
1061
+ const inner = visitNodes(children, childrenLocalMap.size ? childrenLocalMap : undefined, false)
936
1062
  out += `<${indexToUse}>${trimFormattingEdges(inner)}</${indexToUse}>`
937
1063
  continue
938
1064
  }
939
1065
 
940
1066
  // Build local index map for children of this indexed element
941
1067
  const childrenLocalMap = new Map<any, number>()
942
- let localIdxCounter = indexToUse // Start from parent index (reuse parent's index for first child)
1068
+ // Local child indexes restart at 1 inside this element (do not start from parent index)
1069
+ let localIdxCounter = 1
943
1070
  for (const ch of children) {
944
1071
  if (!ch) continue
945
1072
  if (ch.type === 'JSXElement') {
@@ -999,8 +1126,9 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
999
1126
  out += `<${idx}>${trimFormattingEdges(inner)}</${idx}>`
1000
1127
  } else {
1001
1128
  const childrenLocalMap = new Map<any, number>()
1002
- const idx = localIndexMap && localIndexMap.has(node) ? localIndexMap.get(node) : globalSlots.indexOf(node)
1003
- let localIdxCounter = typeof idx === 'number' && idx >= 0 ? idx : 0
1129
+ const idx = resolveIndex(node)
1130
+ // Local child indexes: restart at 0 when parent idx === 0, otherwise at 1
1131
+ let localIdxCounter = 1
1004
1132
 
1005
1133
  for (const ch of children) {
1006
1134
  if (!ch) continue
@@ -1047,6 +1175,25 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
1047
1175
 
1048
1176
  const result = visitNodes(children, undefined, true)
1049
1177
 
1178
+ // console.log('[serializeJSXChildren] result before cleanup:', JSON.stringify(result))
1179
+ // console.log('[serializeJSXChildren] tightNoSpaceNodes:', Array.from(tightNoSpaceNodes || []))
1180
+ // console.log('[serializeJSXChildren] globalSlots:', JSON.stringify(globalSlots, null, 2))
1181
+ // const slotContexts = globalSlots.map((s, idx) => {
1182
+ // const prev = globalSlots[idx - 1]
1183
+ // const next = globalSlots[idx + 1]
1184
+ // return {
1185
+ // idx,
1186
+ // type: s ? s.type : null,
1187
+ // tag: s && s.type === 'JSXElement' ? s.opening?.name?.value : undefined,
1188
+ // preview: s && s.type === 'JSXText' ? String(s.value).slice(0, 40) : undefined,
1189
+ // prevType: prev ? prev.type : null,
1190
+ // prevPreview: prev && prev.type === 'JSXText' ? String(prev.value).slice(0, 40) : undefined,
1191
+ // nextType: next ? next.type : null,
1192
+ // nextPreview: next && next.type === 'JSXText' ? String(next.value).slice(0, 40) : undefined
1193
+ // }
1194
+ // })
1195
+ // console.log('[serializeJSXChildren] slotContexts:', JSON.stringify(slotContexts, null, 2))
1196
+
1050
1197
  // Final cleanup in correct order:
1051
1198
  // 1. First, handle <br /> followed by whitespace+newline (boundary formatting)
1052
1199
  const afterBrCleanup = String(result).replace(/<br \/>\s*\n\s*/g, '<br />')
@@ -1091,7 +1238,8 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
1091
1238
  // remove space before period and trim.
1092
1239
  tmp = tmp.replace(/\s*\n\s*/g, ' ')
1093
1240
  tmp = tmp.replace(/\s+/g, ' ')
1094
- tmp = tmp.replace(/\s+\./g, '.')
1241
+ // remove spaces before common punctuation (comma, semicolon, colon, question, exclamation, period)
1242
+ tmp = tmp.replace(/\s+([,;:!?.])/g, '$1')
1095
1243
  const finalResult = tmp.trim()
1096
1244
 
1097
1245
  // Final guaranteed cleanup for tight (word-split) placeholders:
@@ -1140,5 +1288,166 @@ function serializeJSXChildren (children: any[], config: I18nextToolkitConfig): s
1140
1288
  }
1141
1289
  } catch (e) { /* ignore */ }
1142
1290
 
1291
+ // Remap numeric placeholders to compact local indices inside each parent placeholder.
1292
+ // This fixes cases where global pre-order indices leak into a parent's inner string
1293
+ // (e.g. "<4>...<6>...</6></4>") — we want the inner child placeholders to restart
1294
+ // locally (1,2...) when appropriate.
1295
+ function remapNumericPlaceholders (input: string) {
1296
+ if (!input || !input.includes('<')) return input
1297
+
1298
+ type Node = { type: 'text'; text: string } | { type: 'ph'; idx: number; children: Node[] }
1299
+
1300
+ // Parse into a simple tree of numeric-placeholder nodes and text nodes.
1301
+ function parse (s: string): Node[] {
1302
+ const nodes: Node[] = []
1303
+ const stack: { node: Node; idx: number }[] = []
1304
+ let lastIndex = 0
1305
+ const re = /<\/?(\d+)>|<[^>]+>/g
1306
+ let m: RegExpExecArray | null
1307
+ while ((m = re.exec(s))) {
1308
+ const match = m[0]
1309
+ const matchIndex = m.index
1310
+ if (matchIndex > lastIndex) {
1311
+ const text = s.slice(lastIndex, matchIndex)
1312
+ const textNode: Node = { type: 'text', text }
1313
+ if (stack.length) (stack[stack.length - 1].node as any).children.push(textNode)
1314
+ else nodes.push(textNode)
1315
+ }
1316
+
1317
+ const closingNumeric = /^<\/(\d+)>$/.exec(match)
1318
+ const openingNumeric = /^<(\d+)>$/.exec(match)
1319
+
1320
+ if (openingNumeric) {
1321
+ const idx = Number(openingNumeric[1])
1322
+ const ph: Node = { type: 'ph', idx, children: [] }
1323
+ if (stack.length) (stack[stack.length - 1].node as any).children.push(ph)
1324
+ else nodes.push(ph)
1325
+ stack.push({ node: ph, idx })
1326
+ } else if (closingNumeric) {
1327
+ // pop matching numeric placeholder; if mismatch, just pop last
1328
+ if (stack.length) {
1329
+ stack.pop()
1330
+ }
1331
+ } else {
1332
+ // non-numeric tag (preserved HTML like <br /> or <strong>) -> treat as text
1333
+ const textNode: Node = { type: 'text', text: match }
1334
+ if (stack.length) (stack[stack.length - 1].node as any).children.push(textNode)
1335
+ else nodes.push(textNode)
1336
+ }
1337
+
1338
+ lastIndex = re.lastIndex
1339
+ }
1340
+ if (lastIndex < s.length) {
1341
+ const text = s.slice(lastIndex)
1342
+ const textNode: Node = { type: 'text', text }
1343
+ if (stack.length) (stack[stack.length - 1].node as any).children.push(textNode)
1344
+ else nodes.push(textNode)
1345
+ }
1346
+ return nodes
1347
+ }
1348
+
1349
+ // Reconstruct string with remapped local indices.
1350
+ function build (nodes: Node[], parentIdx: number | null = null): string {
1351
+ let out = ''
1352
+ for (const n of nodes) {
1353
+ if (n.type === 'text') {
1354
+ out += n.text
1355
+ } else {
1356
+ // Map direct child placeholder indices to local sequence
1357
+ const childPhs = (n as any).children.filter((c: any) => c.type === 'ph') as { type: 'ph'; idx: number; children: Node[] }[]
1358
+ // If the child's original global indices are NOT contiguous (i.e. gaps),
1359
+ // do not remap — emit original numbers to preserve tests that expect
1360
+ // global pre-order indices like 1,3,5.
1361
+ const origIndices = childPhs.map(c => c.idx)
1362
+ const isContiguous = origIndices.length <= 1 || origIndices.every((v, i) => i === 0 || v === origIndices[i - 1] + 1)
1363
+ // Do not remap when original child indices are non-contiguous.
1364
+ if (!isContiguous) {
1365
+ const innerNoRemap = (n as any).children.map((child: any) => {
1366
+ if (child.type === 'text') return child.text
1367
+ return `<${child.idx}>${build(child.children, child.idx)}</${child.idx}>`
1368
+ }).join('')
1369
+ out += `<${n.idx}>${innerNoRemap}</${n.idx}>`
1370
+ continue
1371
+ }
1372
+ const map = new Map<number, number>()
1373
+ // Determine start index: if this parent AST node contains N non-element global slots
1374
+ // inside its span, then the first indexed child should be (N + 1). This mirrors
1375
+ // how local indexing must account for explicit expression/text slots that appear
1376
+ // before an element child inside the same parent.
1377
+ let start = (n.idx === 0) ? 0 : 1
1378
+ try {
1379
+ const parentAst = globalSlots[n.idx]
1380
+ if (parentAst && parentAst.span) {
1381
+ const parentStart = parentAst.span.start
1382
+ const parentEnd = parentAst.span.end
1383
+ // Find the first element child (by globalSlots index) that lies inside this parent.
1384
+ let firstElementGIdx = -1
1385
+ for (let gIdx = n.idx + 1; gIdx < globalSlots.length; gIdx++) {
1386
+ const s = globalSlots[gIdx]
1387
+ if (!s || !s.span) continue
1388
+ if (s.span.start >= parentStart && s.span.end <= parentEnd && s.type === 'JSXElement') {
1389
+ firstElementGIdx = gIdx
1390
+ break
1391
+ }
1392
+ }
1393
+
1394
+ // Count only non-element global slots that appear before the first element child
1395
+ // (these shift the local numbering of element children).
1396
+ let nonElementBefore = 0
1397
+ if (firstElementGIdx !== -1) {
1398
+ for (let gIdx = n.idx + 1; gIdx < firstElementGIdx; gIdx++) {
1399
+ const s = globalSlots[gIdx]
1400
+ if (!s || !s.span) continue
1401
+ if (s.span.start >= parentStart && s.span.end <= parentEnd) {
1402
+ if (s.type === 'JSXText' || s.type === 'JSXExpressionContainer') nonElementBefore++
1403
+ }
1404
+ }
1405
+ } else {
1406
+ // No element child found: count non-element slots inside parent (fallback)
1407
+ for (let gIdx = n.idx + 1; gIdx < globalSlots.length; gIdx++) {
1408
+ const s = globalSlots[gIdx]
1409
+ if (!s || !s.span) continue
1410
+ if (s.span.start >= parentStart && s.span.end <= parentEnd) {
1411
+ if (s.type === 'JSXText' || s.type === 'JSXExpressionContainer') nonElementBefore++
1412
+ }
1413
+ }
1414
+ }
1415
+
1416
+ if (typeof n.idx === 'number') {
1417
+ start = n.idx === 0 ? 0 : Math.max(1, nonElementBefore + 1)
1418
+ }
1419
+ }
1420
+ } catch (e) { /* ignore and fall back to default start */ }
1421
+ // assign new numbers in order of appearance among direct children
1422
+ for (const c of childPhs) {
1423
+ if (!map.has(c.idx)) {
1424
+ map.set(c.idx, start++)
1425
+ }
1426
+ }
1427
+
1428
+ // recursively build children, but when emitting child ph tags replace indices
1429
+ const inner = (n as any).children.map((child: any) => {
1430
+ if (child.type === 'text') return child.text
1431
+ const orig = child.idx
1432
+ const newIdx = map.has(orig) ? map.get(orig)! : orig
1433
+ return `<${newIdx}>${build(child.children, newIdx)}</${newIdx}>`
1434
+ }).join('')
1435
+
1436
+ out += `<${n.idx}>${inner}</${n.idx}>`
1437
+ }
1438
+ }
1439
+ return out
1440
+ }
1441
+
1442
+ try {
1443
+ const parsed = parse(input)
1444
+ return build(parsed)
1445
+ } catch (e) {
1446
+ return input
1447
+ }
1448
+ }
1449
+
1450
+ postFinal = remapNumericPlaceholders(postFinal)
1451
+
1143
1452
  return postFinal.trim()
1144
1453
  }