i18next-cli 1.5.7 → 1.5.9
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 +15 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/extractor/parsers/ast-utils.js +1 -1
- package/dist/cjs/extractor/parsers/ast-visitors.js +1 -1
- package/dist/cjs/extractor/parsers/jsx-parser.js +1 -1
- package/dist/esm/cli.js +1 -1
- package/dist/esm/extractor/parsers/ast-utils.js +1 -1
- package/dist/esm/extractor/parsers/ast-visitors.js +1 -1
- package/dist/esm/extractor/parsers/jsx-parser.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +1 -1
- package/src/extractor/parsers/ast-utils.ts +10 -9
- package/src/extractor/parsers/ast-visitors.ts +245 -75
- package/src/extractor/parsers/jsx-parser.ts +84 -20
- package/types/extractor/parsers/ast-utils.d.ts +1 -1
- package/types/extractor/parsers/ast-utils.d.ts.map +1 -1
- package/types/extractor/parsers/ast-visitors.d.ts +21 -1
- package/types/extractor/parsers/ast-visitors.d.ts.map +1 -1
- package/types/extractor/parsers/jsx-parser.d.ts +22 -3
- package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
- package/vitest.config.ts +0 -17
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ 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.5.9](https://github.com/i18next/i18next-cli/compare/v1.5.8...v1.5.9) - 2025-10-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Extractor (`<Trans>`):** Added support for plural-specific default values from `tOptions` prop (e.g., `defaultValue_other: "Items"`). The extractor now correctly uses these values when generating plural keys for Trans components. [#36](https://github.com/i18next/i18next-cli/pull/36)
|
|
12
|
+
- **Extractor:** Added support for dynamic expressions in `t()` function arguments. The extractor can now resolve ternary operators and other static expressions to extract all possible key variants (e.g., `t(isOpen ? 'open' : 'closed')`). [#37](https://github.com/i18next/i18next-cli/pull/37)
|
|
13
|
+
|
|
14
|
+
### Enhanced
|
|
15
|
+
- **Extractor (`<Trans>`):** Improved namespace resolution consistency by prioritizing namespace from `i18nKey` prop over other sources. When a Trans component uses `i18nKey="ns:key"`, the namespace from the key now takes precedence over the `t` prop namespace, matching i18next's behavior. [#38](https://github.com/i18next/i18next-cli/pull/38)
|
|
16
|
+
|
|
17
|
+
## [1.5.8](https://github.com/i18next/i18next-cli/compare/v1.5.7...v1.5.8) - 2025-10-05
|
|
18
|
+
|
|
19
|
+
- **Extractor:** Fixed namespace resolution/override order where explicitly passed `ns` options in `t()` calls were being incorrectly overridden by hook-level namespaces. The extractor now properly prioritizes explicit namespace options over inferred ones. [#32](https://github.com/i18next/i18next-cli/issues/32)
|
|
20
|
+
- **Extractor (`<Trans>`):** Fixed a bug where `context` and `count` props on Trans components were treated as mutually exclusive. The extractor now correctly generates all combinations of context and plural forms (e.g., `key_context_one`, `key_context_other`) to match i18next's behavior. [#33](https://github.com/i18next/i18next-cli/issues/33)
|
|
21
|
+
- **Extractor:** Fixed a bug where passing an empty string as a context value (e.g., `context: test ? 'male' : ''`) resulted in keys with trailing underscores. Empty strings are now treated as "no context" like i18next does, ensuring clean key generation. [#34](https://github.com/i18next/i18next-cli/issues/34)
|
|
22
|
+
|
|
8
23
|
## [1.5.7](https://github.com/i18next/i18next-cli/compare/v1.5.6...v1.5.7) - 2025-10-04
|
|
9
24
|
|
|
10
25
|
### Added
|
package/dist/cjs/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var e=require("commander"),t=require("chokidar"),n=require("glob"),o=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.5.
|
|
2
|
+
"use strict";var e=require("commander"),t=require("chokidar"),n=require("glob"),o=require("chalk"),i=require("./config.js"),a=require("./heuristic-config.js"),r=require("./extractor/core/extractor.js");require("node:path"),require("node:fs/promises"),require("jiti");var c=require("./types-generator.js"),s=require("./syncer.js"),l=require("./migrator.js"),u=require("./init.js"),d=require("./linter.js"),g=require("./status.js"),p=require("./locize.js");const f=new e.Command;f.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.5.9"),f.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.").action(async e=>{const a=await i.ensureConfig(),c=async()=>{const t=await r.runExtractor(a,{isWatchMode:e.watch,isDryRun:e.dryRun});e.ci&&t&&(console.error(o.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(o.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${o.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),e.watch){console.log("\nWatching for changes...");t.watch(await n.glob(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),c()})}}),f.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,t)=>{let n=await i.loadConfig();if(!n){console.log(o.blue("No config file found. Attempting to detect project structure..."));const e=await a.detectConfig();e||(console.error(o.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${o.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(o.green("Project structure detected successfully!")),n=e}await g.runStatus(n,{detail:e,namespace:t.namespace})}),f.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 o=await i.ensureConfig(),a=()=>c.runTypesGenerator(o);if(await a(),e.watch){console.log("\nWatching for changes...");t.watch(await n.glob(o.types?.input||[]),{persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),a()})}}),f.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const e=await i.ensureConfig();await s.runSyncer(e)}),f.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async e=>{await l.runMigrator(e)}),f.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(u.runInit),f.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 r=async()=>{let e=await i.loadConfig();if(!e){console.log(o.blue("No config file found. Attempting to detect project structure..."));const t=await a.detectConfig();t||(console.error(o.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${o.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(o.green("Project structure detected successfully!")),e=t}await d.runLinter(e)};if(await r(),e.watch){console.log("\nWatching for changes...");const e=await i.loadConfig();if(e?.extract?.input){t.watch(await n.glob(e.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",e=>{console.log(`\nFile changed: ${e}`),r()})}}}),f.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 t=await i.ensureConfig();await p.runLocizeSync(t,e)}),f.command("locize-download").description("Download all translations from your locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeDownload(t,e)}),f.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async e=>{const t=await i.ensureConfig();await p.runLocizeMigrate(t,e)}),f.parse(process.argv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";function e(e,t){return e.properties.
|
|
1
|
+
"use strict";function e(e,t){return e.properties.filter(e=>"KeyValueProperty"===e.type).find(e=>"Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t)}exports.getObjectPropValue=function(t,r){const i=e(t,r);if("KeyValueProperty"===i?.type){const e=i.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}},exports.getObjectProperty=e;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&"object"==typeof n&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){const t=e.init;if(!t)return;const n="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!n)return;const r=n.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return void this.handleUseTranslationDeclarator(e,n,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,n)}handleUseTranslationDeclarator(e,n,r){let i;if("Identifier"===e.id.type&&(i=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(i=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){i="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){i=t.value.value;break}}if(!i)return;const s=n.arguments?.[r.nsArg]?.expression;let a;"StringLiteral"===s?.type?a=s.value:"ArrayExpression"===s?.type&&"StringLiteral"===s.elements[0]?.expression.type&&(a=s.elements[0].expression.value);const o=n.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===o?.type){const e=t.getObjectPropValue(o,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(i,{defaultNs:a,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const n=e.id.value,r=t.arguments,i=r[1]?.expression,s=r[2]?.expression,a="StringLiteral"===i?.type?i.value:void 0,o="StringLiteral"===s?.type?s.value:void 0;(a||o)&&this.setVarInScope(n,{defaultNs:a,keyPrefix:o})}handleCallExpression(e){const n=this.getFunctionName(e.callee);if(!n)return;const r=this.getVarFromScope(n),i=this.config.extract.functions||["t","*.t"];let s=void 0!==r;if(!s)for(const e of i)if(e.startsWith("*.")){if(n.endsWith(e.substring(1))){s=!0;break}}else if(e===n){s=!0;break}if(!s||0===e.arguments.length)return;const a=e.arguments[0].expression;let o=[],l=!1;if("StringLiteral"===a.type)o.push(a.value);else if("ArrowFunctionExpression"===a.type){const e=this.extractKeyFromSelector(a);e&&(o.push(e),l=!0)}else if("ArrayExpression"===a.type)for(const e of a.elements)"StringLiteral"===e?.expression.type&&o.push(e.expression.value);if(o=o.filter(e=>!!e),0===o.length)return;let p=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${u}ordinal`)&&(p=!0,o[e]=o[e].slice(0,-8));let c,f;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?f=t:"StringLiteral"===t.type&&(c=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(f=t)}const y=f?t.getObjectPropValue(f,"defaultValue"):void 0,g="string"==typeof y?y:c;for(let e=0;e<o.length;e++){let n,i=o[e];if(f){const e=t.getObjectPropValue(f,"ns");"string"==typeof e&&(n=e)}!n&&r?.defaultNs&&(n=r.defaultNs);const s=this.config.extract.nsSeparator??":";if(!n&&s&&i.includes(s)){const e=i.split(s);n=e.shift(),i=e.join(s)}n||(n=this.config.extract.defaultNS);let a=i;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";a=`${r.keyPrefix}${e}${i}`}const u=e===o.length-1&&g||i;if(f){const e=t.getObjectProperty(f,"context");if("ConditionalExpression"===e?.value?.type){const t=this.resolvePossibleStringValues(e.value),r=this.config.extract.contextSeparator??"_";if(t.length>0){t.forEach(e=>{this.pluginContext.addKey({key:`${a}${r}${e}`,ns:n,defaultValue:u})}),this.pluginContext.addKey({key:a,ns:n,defaultValue:u});continue}}const r=t.getObjectPropValue(f,"context");if("string"==typeof r&&r){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${a}${e}${r}`,ns:n,defaultValue:u});continue}const i=void 0!==t.getObjectPropValue(f,"count"),s=!0===t.getObjectPropValue(f,"ordinal");if(i||p){this.handlePluralKeys(a,n,f,s||p);continue}!0===t.getObjectPropValue(f,"returnObjects")&&this.objectKeys.add(a)}l&&this.objectKeys.add(a),this.pluginContext.addKey({key:a,ns:n,defaultValue:u})}}handlePluralKeys(e,n,r,i){try{const s=i?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:s}).resolvedOptions().pluralCategories,o=this.config.extract.pluralSeparator??"_",l=t.getObjectPropValue(r,"defaultValue"),p=t.getObjectPropValue(r,`defaultValue${o}other`),u=t.getObjectPropValue(r,`defaultValue${o}ordinal${o}other`);for(const s of a){const a=i?`defaultValue${o}ordinal${o}${s}`:`defaultValue${o}${s}`,c=t.getObjectPropValue(r,a);let f;f="string"==typeof c?c:"one"===s&&"string"==typeof l?l:i&&"string"==typeof u?u:i||"string"!=typeof p?"string"==typeof l?l:e:p;const y=i?`${e}${o}ordinal${o}${s}`:`${e}${o}${s}`;this.pluginContext.addKey({key:y,ns:n,defaultValue:f,hasCount:!0,isOrdinal:i})}}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const s=t.getObjectPropValue(r,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof s?s:e})}}handleSimplePluralKeys(e,t,n){try{const r=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,i=this.config.extract.pluralSeparator??"_";for(const s of r)this.pluginContext.addKey({key:`${e}${i}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,r=this.getVarFromScope(t);r?.defaultNs&&(n.ns=r.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const r of e)this.pluginContext.addKey({key:`${n.key}${t}${r}`,ns:n.ns,defaultValue:n.defaultValue});"StringLiteral"!==n.contextExpression.type&&this.pluginContext.addKey(n)}}else if(n.hasCount){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!e,i=n.optionsNode??{type:"ObjectExpression",properties:[],span:{start:0,end:0,ctxt:0}};i.properties.push({type:"KeyValueProperty",key:{type:"Identifier",value:"defaultValue",optional:!1,span:{start:0,end:0,ctxt:0}},value:{type:"StringLiteral",value:n.defaultValue,span:{start:0,end:0,ctxt:0}}}),this.handlePluralKeys(n.key,n.ns,i,r)}else this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const r=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)r.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;r.unshift(e.expression.value)}n=n.object}if(r.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return r.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const n of t){if("string"==typeof n&&n===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof n&&n.name===e)return{name:n.name,nsArg:n.nsArg??0,keyPrefixArg:n.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let n=e;for(;"MemberExpression"===n.type;){if("Identifier"!==n.property.type)return null;t.unshift(n.property.value),n=n.object}if("ThisExpression"===n.type)t.unshift("this");else{if("Identifier"!==n.type)return null;t.unshift(n.value)}return t.join(".")}return null}};
|
|
1
|
+
"use strict";var e=require("./jsx-parser.js"),t=require("./ast-utils.js");exports.ASTVisitors=class{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&"object"==typeof n&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){const t=e.init;if(!t)return;const n="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!n)return;const r=n.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return void this.handleUseTranslationDeclarator(e,n,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,n)}handleUseTranslationDeclarator(e,n,r){let s;if("Identifier"===e.id.type&&(s=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(s=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){s="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){s=t.value.value;break}}if(!s)return;const i=n.arguments?.[r.nsArg]?.expression;let a;"StringLiteral"===i?.type?a=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(a=i.elements[0].expression.value);const o=n.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===o?.type){const e=t.getObjectPropValue(o,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(s,{defaultNs:a,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const n=e.id.value,r=t.arguments,s=r[1]?.expression,i=r[2]?.expression,a="StringLiteral"===s?.type?s.value:void 0,o="StringLiteral"===i?.type?i.value:void 0;(a||o)&&this.setVarInScope(n,{defaultNs:a,keyPrefix:o})}handleCallExpression(e){const n=this.getFunctionName(e.callee);if(!n)return;const r=this.getVarFromScope(n),s=this.config.extract.functions||["t","*.t"];let i=void 0!==r;if(!i)for(const e of s)if(e.startsWith("*.")){if(n.endsWith(e.substring(1))){i=!0;break}}else if(e===n){i=!0;break}if(!i||0===e.arguments.length)return;const{keysToProcess:a,isSelectorAPI:o}=this.handleCallExpressionArgument(e,0);if(0===a.length)return;let l=!1;const u=this.config.extract.pluralSeparator??"_";for(let e=0;e<a.length;e++)a[e].endsWith(`${u}ordinal`)&&(l=!0,a[e]=a[e].slice(0,-8));let p,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(p=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const f=c?t.getObjectPropValue(c,"defaultValue"):void 0,g="string"==typeof f?f:p;for(let e=0;e<a.length;e++){let n,s=a[e];if(c){const e=t.getObjectPropValue(c,"ns");"string"==typeof e&&(n=e)}const i=this.config.extract.nsSeparator??":";if(!n&&i&&s.includes(i)){const e=s.split(i);n=e.shift(),s=e.join(i)}!n&&r?.defaultNs&&(n=r.defaultNs),n||(n=this.config.extract.defaultNS);let u=s;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";u=`${r.keyPrefix}${e}${s}`}const p=e===a.length-1&&g||s;if(c){const e=t.getObjectProperty(c,"context"),r=[];if("ConditionalExpression"===e?.value?.type){const t=this.resolvePossibleStringValues(e.value),s=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{r.push({key:`${u}${s}${e}`,ns:n,defaultValue:p})}),r.push({key:u,ns:n,defaultValue:p}))}else if("StringLiteral"===e?.value?.type){const t=e.value.value,s=this.config.extract.contextSeparator??"_";r.push({key:`${u}${s}${t}`,ns:n,defaultValue:p})}const s=void 0!==t.getObjectPropValue(c,"count"),i=!0===t.getObjectPropValue(c,"ordinal");if(s||l){if(r.length>0)for(const{key:e,ns:t}of r)this.handlePluralKeys(e,t,c,i||l);else this.handlePluralKeys(u,n,c,i||l);continue}if(r.length>0){r.forEach(this.pluginContext.addKey);continue}!0===t.getObjectPropValue(c,"returnObjects")&&this.objectKeys.add(u)}o&&this.objectKeys.add(u),this.pluginContext.addKey({key:u,ns:n,defaultValue:p})}}handleCallExpressionArgument(e,t){const n=e.arguments[t].expression,r=[];let s=!1;if("ArrowFunctionExpression"===n.type){const e=this.extractKeyFromSelector(n);e&&(r.push(e),s=!0)}else if("ArrayExpression"===n.type)for(const e of n.elements)e?.expression&&r.push(...this.resolvePossibleStringValues(e.expression));else r.push(...this.resolvePossibleStringValues(n));return{keysToProcess:r.filter(e=>!!e),isSelectorAPI:s}}handlePluralKeys(e,n,r,s){try{const i=s?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:i}).resolvedOptions().pluralCategories,o=this.config.extract.pluralSeparator??"_",l=t.getObjectPropValue(r,"defaultValue"),u=t.getObjectPropValue(r,`defaultValue${o}other`),p=t.getObjectPropValue(r,`defaultValue${o}ordinal${o}other`);for(const i of a){const a=s?`defaultValue${o}ordinal${o}${i}`:`defaultValue${o}${i}`,c=t.getObjectPropValue(r,a);let f;f="string"==typeof c?c:"one"===i&&"string"==typeof l?l:s&&"string"==typeof p?p:s||"string"!=typeof u?"string"==typeof l?l:e:u;const g=s?`${e}${o}ordinal${o}${i}`:`${e}${o}${i}`;this.pluginContext.addKey({key:g,ns:n,defaultValue:f,hasCount:!0,isOrdinal:s})}}catch(s){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=t.getObjectPropValue(r,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof i?i:e})}}handleSimplePluralKeys(e,t,n){try{const r=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,s=this.config.extract.pluralSeparator??"_";for(const i of r)this.pluginContext.addKey({key:`${e}${s}${i}`,ns:n,defaultValue:t,hasCount:!0})}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e.extractFromTransComponent(t,this.config),r=[];if(n){if(n.keyExpression){const e=this.resolvePossibleStringValues(n.keyExpression);r.push(...e)}else r.push(n.serializedChildren);let e;const{contextExpression:s,optionsNode:i,defaultValue:a,hasCount:o,isOrdinal:l,serializedChildren:u}=n;if(n.ns){const{ns:t}=n;e=r.map(e=>({key:e,ns:t,defaultValue:a||u,hasCount:o,isOrdinal:l}))}else{e=r.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let r;[n,...r]=e.split(t),e=r.join(t)}return{key:e,ns:n,defaultValue:a||u,hasCount:o,isOrdinal:l}});const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===n?.type&&"JSXExpressionContainer"===n.value?.type&&"Identifier"===n.value.expression.type){const t=n.value.expression.value,r=this.getVarFromScope(t);r?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=r.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),s&&o){const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!n,a=this.resolvePossibleStringValues(s),o=this.config.extract.contextSeparator??"_";if(a.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i));for(const t of a)for(const n of e){const e=`${n.key}${o}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,r,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else if(s){const t=this.resolvePossibleStringValues(s),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const r of t)for(const{key:t,ns:s,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${n}${r}`,ns:s,defaultValue:i});"StringLiteral"!==s.type&&e.forEach(this.pluginContext.addKey)}}else if(o){const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else e.forEach(this.pluginContext.addKey)}}}generatePluralKeysForTrans(e,n,r,s,i){try{const a=s?"ordinal":"cardinal",o=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:a}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t.getObjectPropValue(i,`defaultValue${l}other`),p=t.getObjectPropValue(i,`defaultValue${l}ordinal${l}other`));for(const a of o){const o=s?`defaultValue${l}ordinal${l}${a}`:`defaultValue${l}${a}`,c=i?t.getObjectPropValue(i,o):void 0;let f;f="string"==typeof c?c:"one"===a&&"string"==typeof n?n:s&&"string"==typeof p?p:s||"string"!=typeof u?"string"==typeof n?n:e:u;const g=s?`${e}${l}ordinal${l}${a}`:`${e}${l}${a}`;this.pluginContext.addKey({key:g,ns:r,defaultValue:f,hasCount:!0,isOrdinal:s})}}catch(t){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,ns:r,defaultValue:n})}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const r=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)r.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;r.unshift(e.expression.value)}n=n.object}if(r.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return r.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return e.value?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const n of t){if("string"==typeof n&&n===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof n&&n.name===e)return{name:n.name,nsArg:n.nsArg??0,keyPrefixArg:n.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let n=e;for(;"MemberExpression"===n.type;){if("Identifier"!==n.property.type)return null;t.unshift(n.property.value),n=n.object}if("ThisExpression"===n.type)t.unshift("this");else{if("Identifier"!==n.type)return null;t.unshift(n.value)}return t.join(".")}return null}};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("./ast-utils.js");
|
|
1
|
+
"use strict";var e=require("./ast-utils.js");exports.extractFromTransComponent=function(t,n){const i=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),r=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),a=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let p;a||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(p=e.getObjectProperty(s.value.expression,"count"));const u=!!a||!!p,o=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),l="JSXAttribute"===o?.type&&"JSXExpressionContainer"===o.value?.type&&"ObjectExpression"===o.value.expression.type?o.value.expression:void 0,y=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),v=!!y,d=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===d?.type&&"JSXExpressionContainer"===d.value?.type?d.value.expression:void 0;const f=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let b;if(b="JSXAttribute"===f?.type&&"StringLiteral"===f.value?.type?f.value.value:void 0,l&&(void 0===b&&(b=e.getObjectPropValue(l,"ns")),void 0===c)){const t=e.getObjectProperty(l,"context");t?.value&&(c=t.value)}const S=function(e,t){const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function i(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const n=e.expression;if("StringLiteral"===n.type)t+=n.value;else if("Identifier"===n.type)t+=`{{${n.value}}}`;else if("ObjectExpression"===n.type){const e=n.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const s=i(e.children);a&&n.has(a)?t+=`<${a}>${s}</${a}>`:t+=`<${r}>${s}</${r}>`}else"JSXFragment"===e.type&&(t+=i(e.children))}),t}return i(e).trim().replace(/\s{2,}/g," ")}(t.children,n);let x,m=n.extract.defaultValue||"";return"JSXAttribute"===r?.type&&"StringLiteral"===r.value?.type&&(m=r.value.value),"JSXAttribute"!==i?.type||("StringLiteral"===i.value?.type?x=i.value:"JSXExpressionContainer"===i.value?.type&&"JSXEmptyExpression"!==i.value.expression.type&&(x=i.value.expression),x)?{keyExpression:x,serializedChildren:S,ns:b,defaultValue:m,hasCount:u,isOrdinal:v,contextExpression:c,optionsNode:l}:null};
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import n from"chalk";import{ensureConfig as i,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as f,runLocizeDownload as u,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.5.
|
|
2
|
+
import{Command as t}from"commander";import o from"chokidar";import{glob as e}from"glob";import n from"chalk";import{ensureConfig as i,loadConfig as a}from"./config.js";import{detectConfig as c}from"./heuristic-config.js";import{runExtractor as r}from"./extractor/core/extractor.js";import"node:path";import"node:fs/promises";import"jiti";import{runTypesGenerator as s}from"./types-generator.js";import{runSyncer as l}from"./syncer.js";import{runMigrator as m}from"./migrator.js";import{runInit as p}from"./init.js";import{runLinter as d}from"./linter.js";import{runStatus as g}from"./status.js";import{runLocizeSync as f,runLocizeDownload as u,runLocizeMigrate as h}from"./locize.js";const w=new t;w.name("i18next-cli").description("A unified, high-performance i18next CLI.").version("1.5.9"),w.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.").action(async t=>{const a=await i(),c=async()=>{const o=await r(a,{isWatchMode:t.watch,isDryRun:t.dryRun});t.ci&&o&&(console.error(n.red.bold("\n[CI Mode] Error: Translation files were updated. Please commit the changes.")),console.log(n.yellow("💡 Tip: Tired of committing JSON files? locize syncs your team automatically => https://www.locize.com/docs/getting-started")),console.log(` Learn more: ${n.cyan("npx i18next-cli locize-sync")}`),process.exit(1))};if(await c(),t.watch){console.log("\nWatching for changes...");o.watch(await e(a.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),c()})}}),w.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(t,o)=>{let e=await a();if(!e){console.log(n.blue("No config file found. Attempting to detect project structure..."));const t=await c();t||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),e=t}await g(e,{detail:t,namespace:o.namespace})}),w.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 t=>{const n=await i(),a=()=>s(n);if(await a(),t.watch){console.log("\nWatching for changes...");o.watch(await e(n.types?.input||[]),{persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),a()})}}),w.command("sync").description("Synchronize secondary language files with the primary language file.").action(async()=>{const t=await i();await l(t)}),w.command("migrate-config [configPath]").description("Migrate a legacy i18next-parser.config.js to the new format.").action(async t=>{await m(t)}),w.command("init").description("Create a new i18next.config.ts/js file with an interactive setup wizard.").action(p),w.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 t=>{const i=async()=>{let t=await a();if(!t){console.log(n.blue("No config file found. Attempting to detect project structure..."));const o=await c();o||(console.error(n.red("Could not automatically detect your project structure.")),console.log(`Please create a config file first by running: ${n.cyan("npx i18next-cli init")}`),process.exit(1)),console.log(n.green("Project structure detected successfully!")),t=o}await d(t)};if(await i(),t.watch){console.log("\nWatching for changes...");const t=await a();if(t?.extract?.input){o.watch(await e(t.extract.input),{ignored:/node_modules/,persistent:!0}).on("change",t=>{console.log(`\nFile changed: ${t}`),i()})}}}),w.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 t=>{const o=await i();await f(o,t)}),w.command("locize-download").description("Download all translations from your locize project.").action(async t=>{const o=await i();await u(o,t)}),w.command("locize-migrate").description("Migrate local translation files to a new locize project.").action(async t=>{const o=await i();await h(o,t)}),w.parse(process.argv);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t){return e.properties.
|
|
1
|
+
function e(e,t){return e.properties.filter(e=>"KeyValueProperty"===e.type).find(e=>"Identifier"===e.key?.type&&e.key.value===t||"StringLiteral"===e.key?.type&&e.key.value===t)}function t(t,r){const i=e(t,r);if("KeyValueProperty"===i?.type){const e=i.value;return"StringLiteral"===e.type||("BooleanLiteral"===e.type||"NumericLiteral"===e.type)?e.value:""}}export{t as getObjectPropValue,e as getObjectProperty};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t,getObjectProperty as n}from"./ast-utils.js";class i{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&"object"==typeof n&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){const t=e.init;if(!t)return;const n="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!n)return;const i=n.callee;if("Identifier"===i.type){const t=this.getUseTranslationConfig(i.value);if(t)return void this.handleUseTranslationDeclarator(e,n,t)}"MemberExpression"===i.type&&"Identifier"===i.property.type&&"getFixedT"===i.property.value&&this.handleGetFixedTDeclarator(e,n)}handleUseTranslationDeclarator(e,n,i){let r;if("Identifier"===e.id.type&&(r=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(r=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){r="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){r=t.value.value;break}}if(!r)return;const s=n.arguments?.[i.nsArg]?.expression;let a;"StringLiteral"===s?.type?a=s.value:"ArrayExpression"===s?.type&&"StringLiteral"===s.elements[0]?.expression.type&&(a=s.elements[0].expression.value);const o=n.arguments?.[i.keyPrefixArg]?.expression;let l;if("ObjectExpression"===o?.type){const e=t(o,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(r,{defaultNs:a,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const n=e.id.value,i=t.arguments,r=i[1]?.expression,s=i[2]?.expression,a="StringLiteral"===r?.type?r.value:void 0,o="StringLiteral"===s?.type?s.value:void 0;(a||o)&&this.setVarInScope(n,{defaultNs:a,keyPrefix:o})}handleCallExpression(e){const i=this.getFunctionName(e.callee);if(!i)return;const r=this.getVarFromScope(i),s=this.config.extract.functions||["t","*.t"];let a=void 0!==r;if(!a)for(const e of s)if(e.startsWith("*.")){if(i.endsWith(e.substring(1))){a=!0;break}}else if(e===i){a=!0;break}if(!a||0===e.arguments.length)return;const o=e.arguments[0].expression;let l=[],p=!1;if("StringLiteral"===o.type)l.push(o.value);else if("ArrowFunctionExpression"===o.type){const e=this.extractKeyFromSelector(o);e&&(l.push(e),p=!0)}else if("ArrayExpression"===o.type)for(const e of o.elements)"StringLiteral"===e?.expression.type&&l.push(e.expression.value);if(l=l.filter(e=>!!e),0===l.length)return;let u=!1;const c=this.config.extract.pluralSeparator??"_";for(let e=0;e<l.length;e++)l[e].endsWith(`${c}ordinal`)&&(u=!0,l[e]=l[e].slice(0,-8));let f,y;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?y=t:"StringLiteral"===t.type&&(f=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(y=t)}const g=y?t(y,"defaultValue"):void 0,d="string"==typeof g?g:f;for(let e=0;e<l.length;e++){let i,s=l[e];if(y){const e=t(y,"ns");"string"==typeof e&&(i=e)}!i&&r?.defaultNs&&(i=r.defaultNs);const a=this.config.extract.nsSeparator??":";if(!i&&a&&s.includes(a)){const e=s.split(a);i=e.shift(),s=e.join(a)}i||(i=this.config.extract.defaultNS);let o=s;if(r?.keyPrefix){const e=this.config.extract.keySeparator??".";o=`${r.keyPrefix}${e}${s}`}const c=e===l.length-1&&d||s;if(y){const e=n(y,"context");if("ConditionalExpression"===e?.value?.type){const t=this.resolvePossibleStringValues(e.value),n=this.config.extract.contextSeparator??"_";if(t.length>0){t.forEach(e=>{this.pluginContext.addKey({key:`${o}${n}${e}`,ns:i,defaultValue:c})}),this.pluginContext.addKey({key:o,ns:i,defaultValue:c});continue}}const r=t(y,"context");if("string"==typeof r&&r){const e=this.config.extract.contextSeparator??"_";this.pluginContext.addKey({key:`${o}${e}${r}`,ns:i,defaultValue:c});continue}const s=void 0!==t(y,"count"),a=!0===t(y,"ordinal");if(s||u){this.handlePluralKeys(o,i,y,a||u);continue}!0===t(y,"returnObjects")&&this.objectKeys.add(o)}p&&this.objectKeys.add(o),this.pluginContext.addKey({key:o,ns:i,defaultValue:c})}}handlePluralKeys(e,n,i,r){try{const s=r?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:s}).resolvedOptions().pluralCategories,o=this.config.extract.pluralSeparator??"_",l=t(i,"defaultValue"),p=t(i,`defaultValue${o}other`),u=t(i,`defaultValue${o}ordinal${o}other`);for(const s of a){const a=t(i,r?`defaultValue${o}ordinal${o}${s}`:`defaultValue${o}${s}`);let c;c="string"==typeof a?a:"one"===s&&"string"==typeof l?l:r&&"string"==typeof u?u:r||"string"!=typeof p?"string"==typeof l?l:e:p;const f=r?`${e}${o}ordinal${o}${s}`:`${e}${o}${s}`;this.pluginContext.addKey({key:f,ns:n,defaultValue:c,hasCount:!0,isOrdinal:r})}}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const s=t(i,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof s?s:e})}}handleSimplePluralKeys(e,t,n){try{const i=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,r=this.config.extract.pluralSeparator??"_";for(const s of i)this.pluginContext.addKey({key:`${e}${r}${s}`,ns:n,defaultValue:t,hasCount:!0})}catch(i){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config);if(n){if(!n.ns){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===e?.type&&"JSXExpressionContainer"===e.value?.type&&"Identifier"===e.value.expression.type){const t=e.value.expression.value,i=this.getVarFromScope(t);i?.defaultNs&&(n.ns=i.defaultNs)}}if(n.ns||(n.ns=this.config.extract.defaultNS),n.contextExpression){const e=this.resolvePossibleStringValues(n.contextExpression),t=this.config.extract.contextSeparator??"_";if(e.length>0){for(const i of e)this.pluginContext.addKey({key:`${n.key}${t}${i}`,ns:n.ns,defaultValue:n.defaultValue});"StringLiteral"!==n.contextExpression.type&&this.pluginContext.addKey(n)}}else if(n.hasCount){const e=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),i=!!e,r=n.optionsNode??{type:"ObjectExpression",properties:[],span:{start:0,end:0,ctxt:0}};r.properties.push({type:"KeyValueProperty",key:{type:"Identifier",value:"defaultValue",optional:!1,span:{start:0,end:0,ctxt:0}},value:{type:"StringLiteral",value:n.defaultValue,span:{start:0,end:0,ctxt:0}}}),this.handlePluralKeys(n.key,n.ns,r,i)}else this.pluginContext.addKey(n)}}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const i=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)i.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;i.unshift(e.expression.value)}n=n.object}if(i.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return i.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return[e.value];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const n of t){if("string"==typeof n&&n===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof n&&n.name===e)return{name:n.name,nsArg:n.nsArg??0,keyPrefixArg:n.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let n=e;for(;"MemberExpression"===n.type;){if("Identifier"!==n.property.type)return null;t.unshift(n.property.value),n=n.object}if("ThisExpression"===n.type)t.unshift("this");else{if("Identifier"!==n.type)return null;t.unshift(n.value)}return t.join(".")}return null}}export{i as ASTVisitors};
|
|
1
|
+
import{extractFromTransComponent as e}from"./jsx-parser.js";import{getObjectPropValue as t,getObjectProperty as n}from"./ast-utils.js";class r{pluginContext;config;logger;scopeStack=[];objectKeys=new Set;constructor(e,t,n){this.pluginContext=t,this.config=e,this.logger=n}visit(e){this.enterScope(),this.walk(e),this.exitScope()}walk(e){if(!e)return;let t=!1;switch("Function"!==e.type&&"ArrowFunctionExpression"!==e.type&&"FunctionExpression"!==e.type||(this.enterScope(),t=!0),e.type){case"VariableDeclarator":this.handleVariableDeclarator(e);break;case"CallExpression":this.handleCallExpression(e);break;case"JSXElement":this.handleJSXElement(e)}for(const t in e){if("span"===t)continue;const n=e[t];if(Array.isArray(n))for(const e of n)e&&"object"==typeof e&&this.walk(e);else n&&"object"==typeof n&&this.walk(n)}t&&this.exitScope()}enterScope(){this.scopeStack.push(new Map)}exitScope(){this.scopeStack.pop()}setVarInScope(e,t){this.scopeStack.length>0&&this.scopeStack[this.scopeStack.length-1].set(e,t)}getVarFromScope(e){for(let t=this.scopeStack.length-1;t>=0;t--)if(this.scopeStack[t].has(e))return this.scopeStack[t].get(e)}handleVariableDeclarator(e){const t=e.init;if(!t)return;const n="AwaitExpression"===t.type&&"CallExpression"===t.argument.type?t.argument:"CallExpression"===t.type?t:null;if(!n)return;const r=n.callee;if("Identifier"===r.type){const t=this.getUseTranslationConfig(r.value);if(t)return void this.handleUseTranslationDeclarator(e,n,t)}"MemberExpression"===r.type&&"Identifier"===r.property.type&&"getFixedT"===r.property.value&&this.handleGetFixedTDeclarator(e,n)}handleUseTranslationDeclarator(e,n,r){let s;if("Identifier"===e.id.type&&(s=e.id.value),"ArrayPattern"===e.id.type){const t=e.id.elements[0];"Identifier"===t?.type&&(s=t.value)}if("ObjectPattern"===e.id.type)for(const t of e.id.properties){if("AssignmentPatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value){s="t";break}if("KeyValuePatternProperty"===t.type&&"Identifier"===t.key.type&&"t"===t.key.value&&"Identifier"===t.value.type){s=t.value.value;break}}if(!s)return;const i=n.arguments?.[r.nsArg]?.expression;let a;"StringLiteral"===i?.type?a=i.value:"ArrayExpression"===i?.type&&"StringLiteral"===i.elements[0]?.expression.type&&(a=i.elements[0].expression.value);const o=n.arguments?.[r.keyPrefixArg]?.expression;let l;if("ObjectExpression"===o?.type){const e=t(o,"keyPrefix");l="string"==typeof e?e:void 0}this.setVarInScope(s,{defaultNs:a,keyPrefix:l})}handleGetFixedTDeclarator(e,t){if("Identifier"!==e.id.type||!e.init||"CallExpression"!==e.init.type)return;const n=e.id.value,r=t.arguments,s=r[1]?.expression,i=r[2]?.expression,a="StringLiteral"===s?.type?s.value:void 0,o="StringLiteral"===i?.type?i.value:void 0;(a||o)&&this.setVarInScope(n,{defaultNs:a,keyPrefix:o})}handleCallExpression(e){const r=this.getFunctionName(e.callee);if(!r)return;const s=this.getVarFromScope(r),i=this.config.extract.functions||["t","*.t"];let a=void 0!==s;if(!a)for(const e of i)if(e.startsWith("*.")){if(r.endsWith(e.substring(1))){a=!0;break}}else if(e===r){a=!0;break}if(!a||0===e.arguments.length)return;const{keysToProcess:o,isSelectorAPI:l}=this.handleCallExpressionArgument(e,0);if(0===o.length)return;let u=!1;const p=this.config.extract.pluralSeparator??"_";for(let e=0;e<o.length;e++)o[e].endsWith(`${p}ordinal`)&&(u=!0,o[e]=o[e].slice(0,-8));let f,c;if(e.arguments.length>1){const t=e.arguments[1].expression;"ObjectExpression"===t.type?c=t:"StringLiteral"===t.type&&(f=t.value)}if(e.arguments.length>2){const t=e.arguments[2].expression;"ObjectExpression"===t.type&&(c=t)}const y=c?t(c,"defaultValue"):void 0,g="string"==typeof y?y:f;for(let e=0;e<o.length;e++){let r,i=o[e];if(c){const e=t(c,"ns");"string"==typeof e&&(r=e)}const a=this.config.extract.nsSeparator??":";if(!r&&a&&i.includes(a)){const e=i.split(a);r=e.shift(),i=e.join(a)}!r&&s?.defaultNs&&(r=s.defaultNs),r||(r=this.config.extract.defaultNS);let p=i;if(s?.keyPrefix){const e=this.config.extract.keySeparator??".";p=`${s.keyPrefix}${e}${i}`}const f=e===o.length-1&&g||i;if(c){const e=n(c,"context"),s=[];if("ConditionalExpression"===e?.value?.type){const t=this.resolvePossibleStringValues(e.value),n=this.config.extract.contextSeparator??"_";t.length>0&&(t.forEach(e=>{s.push({key:`${p}${n}${e}`,ns:r,defaultValue:f})}),s.push({key:p,ns:r,defaultValue:f}))}else if("StringLiteral"===e?.value?.type){const t=e.value.value,n=this.config.extract.contextSeparator??"_";s.push({key:`${p}${n}${t}`,ns:r,defaultValue:f})}const i=void 0!==t(c,"count"),a=!0===t(c,"ordinal");if(i||u){if(s.length>0)for(const{key:e,ns:t}of s)this.handlePluralKeys(e,t,c,a||u);else this.handlePluralKeys(p,r,c,a||u);continue}if(s.length>0){s.forEach(this.pluginContext.addKey);continue}!0===t(c,"returnObjects")&&this.objectKeys.add(p)}l&&this.objectKeys.add(p),this.pluginContext.addKey({key:p,ns:r,defaultValue:f})}}handleCallExpressionArgument(e,t){const n=e.arguments[t].expression,r=[];let s=!1;if("ArrowFunctionExpression"===n.type){const e=this.extractKeyFromSelector(n);e&&(r.push(e),s=!0)}else if("ArrayExpression"===n.type)for(const e of n.elements)e?.expression&&r.push(...this.resolvePossibleStringValues(e.expression));else r.push(...this.resolvePossibleStringValues(n));return{keysToProcess:r.filter(e=>!!e),isSelectorAPI:s}}handlePluralKeys(e,n,r,s){try{const i=s?"ordinal":"cardinal",a=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:i}).resolvedOptions().pluralCategories,o=this.config.extract.pluralSeparator??"_",l=t(r,"defaultValue"),u=t(r,`defaultValue${o}other`),p=t(r,`defaultValue${o}ordinal${o}other`);for(const i of a){const a=t(r,s?`defaultValue${o}ordinal${o}${i}`:`defaultValue${o}${i}`);let f;f="string"==typeof a?a:"one"===i&&"string"==typeof l?l:s&&"string"==typeof p?p:s||"string"!=typeof u?"string"==typeof l?l:e:u;const c=s?`${e}${o}ordinal${o}${i}`:`${e}${o}${i}`;this.pluginContext.addKey({key:c,ns:n,defaultValue:f,hasCount:!0,isOrdinal:s})}}catch(s){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`);const i=t(r,"defaultValue");this.pluginContext.addKey({key:e,ns:n,defaultValue:"string"==typeof i?i:e})}}handleSimplePluralKeys(e,t,n){try{const r=new Intl.PluralRules(this.config.extract?.primaryLanguage).resolvedOptions().pluralCategories,s=this.config.extract.pluralSeparator??"_";for(const i of r)this.pluginContext.addKey({key:`${e}${s}${i}`,ns:n,defaultValue:t,hasCount:!0})}catch(r){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}".`),this.pluginContext.addKey({key:e,ns:n,defaultValue:t})}}handleJSXElement(t){const n=this.getElementName(t);if(n&&(this.config.extract.transComponents||["Trans"]).includes(n)){const n=e(t,this.config),r=[];if(n){if(n.keyExpression){const e=this.resolvePossibleStringValues(n.keyExpression);r.push(...e)}else r.push(n.serializedChildren);let e;const{contextExpression:s,optionsNode:i,defaultValue:a,hasCount:o,isOrdinal:l,serializedChildren:u}=n;if(n.ns){const{ns:t}=n;e=r.map(e=>({key:e,ns:t,defaultValue:a||u,hasCount:o,isOrdinal:l}))}else{e=r.map(e=>{const t=this.config.extract.nsSeparator??":";let n;if(t&&e.includes(t)){let r;[n,...r]=e.split(t),e=r.join(t)}return{key:e,ns:n,defaultValue:a||u,hasCount:o,isOrdinal:l}});const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"t"===e.name.value);if("JSXAttribute"===n?.type&&"JSXExpressionContainer"===n.value?.type&&"Identifier"===n.value.expression.type){const t=n.value.expression.value,r=this.getVarFromScope(t);r?.defaultNs&&e.forEach(e=>{e.ns||(e.ns=r.defaultNs)})}}if(e.forEach(e=>{e.ns||(e.ns=this.config.extract.defaultNS)}),s&&o){const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!n,a=this.resolvePossibleStringValues(s),o=this.config.extract.contextSeparator??"_";if(a.length>0){e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i));for(const t of a)for(const n of e){const e=`${n.key}${o}${t}`;this.generatePluralKeysForTrans(e,n.defaultValue,n.ns,r,i)}}else e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else if(s){const t=this.resolvePossibleStringValues(s),n=this.config.extract.contextSeparator??"_";if(t.length>0){for(const r of t)for(const{key:t,ns:s,defaultValue:i}of e)this.pluginContext.addKey({key:`${t}${n}${r}`,ns:s,defaultValue:i});"StringLiteral"!==s.type&&e.forEach(this.pluginContext.addKey)}}else if(o){const n=t.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),r=!!n;e.forEach(e=>this.generatePluralKeysForTrans(e.key,e.defaultValue,e.ns,r,i))}else e.forEach(this.pluginContext.addKey)}}}generatePluralKeysForTrans(e,n,r,s,i){try{const a=s?"ordinal":"cardinal",o=new Intl.PluralRules(this.config.extract?.primaryLanguage,{type:a}).resolvedOptions().pluralCategories,l=this.config.extract.pluralSeparator??"_";let u,p;i&&(u=t(i,`defaultValue${l}other`),p=t(i,`defaultValue${l}ordinal${l}other`));for(const a of o){const o=i?t(i,s?`defaultValue${l}ordinal${l}${a}`:`defaultValue${l}${a}`):void 0;let f;f="string"==typeof o?o:"one"===a&&"string"==typeof n?n:s&&"string"==typeof p?p:s||"string"!=typeof u?"string"==typeof n?n:e:u;const c=s?`${e}${l}ordinal${l}${a}`:`${e}${l}${a}`;this.pluginContext.addKey({key:c,ns:r,defaultValue:f,hasCount:!0,isOrdinal:s})}}catch(t){this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`),this.pluginContext.addKey({key:e,ns:r,defaultValue:n})}}getElementName(e){if("Identifier"===e.opening.name.type)return e.opening.name.value;if("JSXMemberExpression"===e.opening.name.type){let t=e.opening.name;const n=[];for(;"JSXMemberExpression"===t.type;)"Identifier"===t.property.type&&n.unshift(t.property.value),t=t.object;return"Identifier"===t.type&&n.unshift(t.value),n.join(".")}}extractKeyFromSelector(e){let t=e.body;if("BlockStatement"===t.type){const e=t.stmts.find(e=>"ReturnStatement"===e.type);if("ReturnStatement"!==e?.type||!e.argument)return null;t=e.argument}let n=t;const r=[];for(;"MemberExpression"===n.type;){const e=n.property;if("Identifier"===e.type)r.unshift(e.value);else{if("Computed"!==e.type||"StringLiteral"!==e.expression.type)return null;r.unshift(e.expression.value)}n=n.object}if(r.length>0){const e=this.config.extract.keySeparator,t="string"==typeof e?e:".";return r.join(t)}return null}resolvePossibleStringValues(e){if("StringLiteral"===e.type)return e.value?[e.value]:[];if("ConditionalExpression"===e.type){return[...this.resolvePossibleStringValues(e.consequent),...this.resolvePossibleStringValues(e.alternate)]}return"Identifier"===e.type&&e.value,[]}getUseTranslationConfig(e){const t=this.config.extract.useTranslationNames||["useTranslation"];for(const n of t){if("string"==typeof n&&n===e)return{name:e,nsArg:0,keyPrefixArg:1};if("object"==typeof n&&n.name===e)return{name:n.name,nsArg:n.nsArg??0,keyPrefixArg:n.keyPrefixArg??1}}}getFunctionName(e){if("Identifier"===e.type)return e.value;if("MemberExpression"===e.type){const t=[];let n=e;for(;"MemberExpression"===n.type;){if("Identifier"!==n.property.type)return null;t.unshift(n.property.value),n=n.object}if("ThisExpression"===n.type)t.unshift("this");else{if("Identifier"!==n.type)return null;t.unshift(n.value)}return t.join(".")}return null}}export{r as ASTVisitors};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{getObjectProperty as e,getObjectPropValue as t}from"./ast-utils.js";function n(n,i){const r=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"i18nKey"===e.name.value),a=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"defaults"===e.name.value),p=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),s=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let u;p||"JSXAttribute"!==s?.type||"JSXExpressionContainer"!==s.value?.type||"ObjectExpression"!==s.value.expression.type||(u=e(s.value.expression,"count"));const o=!!p||!!u,l=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),y="JSXAttribute"===l?.type&&"JSXExpressionContainer"===l.value?.type&&"ObjectExpression"===l.value.expression.type?l.value.expression:void 0,v=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),d=!!v,f=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let c="JSXAttribute"===f?.type&&"JSXExpressionContainer"===f.value?.type?f.value.expression:void 0;const S=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let b;if(b="JSXAttribute"===S?.type&&"StringLiteral"===S.value?.type?S.value.value:void 0,y&&(void 0===b&&(b=t(y,"ns")),void 0===c)){const t=e(y,"context");t?.value&&(c=t.value)}const x=function(e,t){const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]);function i(e){let t="";return e.forEach((e,r)=>{if("JSXText"===e.type)t+=e.value;else if("JSXExpressionContainer"===e.type){const n=e.expression;if("StringLiteral"===n.type)t+=n.value;else if("Identifier"===n.type)t+=`{{${n.value}}}`;else if("ObjectExpression"===n.type){const e=n.properties[0];e&&"Identifier"===e.type&&(t+=`{{${e.value}}}`)}}else if("JSXElement"===e.type){let a;"Identifier"===e.opening.name.type&&(a=e.opening.name.value);const p=i(e.children);a&&n.has(a)?t+=`<${a}>${p}</${a}>`:t+=`<${r}>${p}</${r}>`}else"JSXFragment"===e.type&&(t+=i(e.children))}),t}return i(e).trim().replace(/\s{2,}/g," ")}(n.children,i);let m,J=i.extract.defaultValue||"";return"JSXAttribute"===a?.type&&"StringLiteral"===a.value?.type&&(J=a.value.value),"JSXAttribute"!==r?.type||("StringLiteral"===r.value?.type?m=r.value:"JSXExpressionContainer"===r.value?.type&&"JSXEmptyExpression"!==r.value.expression.type&&(m=r.value.expression),m)?{keyExpression:m,serializedChildren:x,ns:b,defaultValue:J,hasCount:o,isOrdinal:d,contextExpression:c,optionsNode:y}:null}export{n as extractFromTransComponent};
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -15,15 +15,16 @@ import type { ObjectExpression } from '@swc/core'
|
|
|
15
15
|
* @param propName - The property name to locate
|
|
16
16
|
* @returns The matching KeyValueProperty node if found, otherwise undefined.
|
|
17
17
|
*/
|
|
18
|
-
export function getObjectProperty (object: ObjectExpression, propName: string)
|
|
19
|
-
return (object.properties).
|
|
20
|
-
(p) =>
|
|
21
|
-
|
|
22
|
-
(
|
|
23
|
-
(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
export function getObjectProperty (object: ObjectExpression, propName: string) {
|
|
19
|
+
return (object.properties).filter(
|
|
20
|
+
(p) => p.type === 'KeyValueProperty')
|
|
21
|
+
.find(
|
|
22
|
+
(p) =>
|
|
23
|
+
(
|
|
24
|
+
(p.key?.type === 'Identifier' && p.key.value === propName) ||
|
|
25
|
+
(p.key?.type === 'StringLiteral' && p.key.value === propName)
|
|
26
|
+
)
|
|
27
|
+
)
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Module, Node, CallExpression, VariableDeclarator, JSXElement, ArrowFunctionExpression, ObjectExpression, Expression } from '@swc/core'
|
|
2
|
-
import type { PluginContext, I18nextToolkitConfig, Logger } from '../../types'
|
|
2
|
+
import type { PluginContext, I18nextToolkitConfig, Logger, ExtractedKey } from '../../types'
|
|
3
3
|
import { extractFromTransComponent } from './jsx-parser'
|
|
4
4
|
import { getObjectProperty, getObjectPropValue } from './ast-utils'
|
|
5
5
|
|
|
@@ -218,11 +218,11 @@ export class ASTVisitors {
|
|
|
218
218
|
|
|
219
219
|
// Determine the actual call expression, looking inside AwaitExpressions.
|
|
220
220
|
const callExpr =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
221
|
+
init.type === 'AwaitExpression' && init.argument.type === 'CallExpression'
|
|
222
|
+
? init.argument
|
|
223
|
+
: init.type === 'CallExpression'
|
|
224
|
+
? init
|
|
225
|
+
: null
|
|
226
226
|
|
|
227
227
|
if (!callExpr) return
|
|
228
228
|
|
|
@@ -394,28 +394,8 @@ export class ASTVisitors {
|
|
|
394
394
|
}
|
|
395
395
|
if (!isFunctionToParse || node.arguments.length === 0) return
|
|
396
396
|
|
|
397
|
-
const
|
|
398
|
-
let keysToProcess: string[] = []
|
|
399
|
-
let isSelectorAPI = false
|
|
397
|
+
const { keysToProcess, isSelectorAPI } = this.handleCallExpressionArgument(node, 0)
|
|
400
398
|
|
|
401
|
-
if (firstArg.type === 'StringLiteral') {
|
|
402
|
-
keysToProcess.push(firstArg.value)
|
|
403
|
-
} else if (firstArg.type === 'ArrowFunctionExpression') {
|
|
404
|
-
const key = this.extractKeyFromSelector(firstArg)
|
|
405
|
-
if (key) {
|
|
406
|
-
keysToProcess.push(key)
|
|
407
|
-
isSelectorAPI = true
|
|
408
|
-
}
|
|
409
|
-
} else if (firstArg.type === 'ArrayExpression') {
|
|
410
|
-
for (const element of firstArg.elements) {
|
|
411
|
-
// We only extract static string literals from the array
|
|
412
|
-
if (element?.expression.type === 'StringLiteral') {
|
|
413
|
-
keysToProcess.push(element.expression.value)
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
keysToProcess = keysToProcess.filter(key => !!key)
|
|
419
399
|
if (keysToProcess.length === 0) return
|
|
420
400
|
|
|
421
401
|
let isOrdinalByKey = false
|
|
@@ -454,12 +434,12 @@ export class ASTVisitors {
|
|
|
454
434
|
let key = keysToProcess[i]
|
|
455
435
|
let ns: string | undefined
|
|
456
436
|
|
|
457
|
-
// Determine namespace (explicit ns >
|
|
437
|
+
// Determine namespace (explicit ns > ns:key > scope ns > default)
|
|
438
|
+
// See https://www.i18next.com/overview/api#getfixedt
|
|
458
439
|
if (options) {
|
|
459
440
|
const nsVal = getObjectPropValue(options, 'ns')
|
|
460
441
|
if (typeof nsVal === 'string') ns = nsVal
|
|
461
442
|
}
|
|
462
|
-
if (!ns && scopeInfo?.defaultNs) ns = scopeInfo.defaultNs
|
|
463
443
|
|
|
464
444
|
const nsSeparator = this.config.extract.nsSeparator ?? ':'
|
|
465
445
|
if (!ns && nsSeparator && key.includes(nsSeparator)) {
|
|
@@ -467,6 +447,8 @@ export class ASTVisitors {
|
|
|
467
447
|
ns = parts.shift()
|
|
468
448
|
key = parts.join(nsSeparator)
|
|
469
449
|
}
|
|
450
|
+
|
|
451
|
+
if (!ns && scopeInfo?.defaultNs) ns = scopeInfo.defaultNs
|
|
470
452
|
if (!ns) ns = this.config.extract.defaultNS
|
|
471
453
|
|
|
472
454
|
let finalKey = key
|
|
@@ -482,56 +464,106 @@ export class ASTVisitors {
|
|
|
482
464
|
if (options) {
|
|
483
465
|
const contextProp = getObjectProperty(options, 'context')
|
|
484
466
|
|
|
485
|
-
|
|
467
|
+
const keysWithContext: ExtractedKey[] = []
|
|
468
|
+
|
|
469
|
+
// 1. Handle Context
|
|
486
470
|
if (contextProp?.value?.type === 'ConditionalExpression') {
|
|
487
471
|
const contextValues = this.resolvePossibleStringValues(contextProp.value)
|
|
488
472
|
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
489
473
|
|
|
490
474
|
if (contextValues.length > 0) {
|
|
491
475
|
contextValues.forEach(context => {
|
|
492
|
-
|
|
476
|
+
keysWithContext.push({ key: `${finalKey}${contextSeparator}${context}`, ns, defaultValue: dv })
|
|
493
477
|
})
|
|
494
478
|
// For dynamic context, also add the base key as a fallback
|
|
495
|
-
|
|
496
|
-
continue // This key is fully handled, move to the next in the array
|
|
479
|
+
keysWithContext.push({ key: finalKey, ns, defaultValue: dv })
|
|
497
480
|
}
|
|
498
|
-
}
|
|
481
|
+
} else if (contextProp?.value?.type === 'StringLiteral') {
|
|
482
|
+
const contextValue = contextProp.value.value
|
|
499
483
|
|
|
500
|
-
// 2. Handle Static Context
|
|
501
|
-
const contextValue = getObjectPropValue(options, 'context')
|
|
502
|
-
if (typeof contextValue === 'string' && contextValue) {
|
|
503
484
|
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
504
|
-
|
|
505
|
-
continue // This key is fully handled
|
|
485
|
+
keysWithContext.push({ key: `${finalKey}${contextSeparator}${contextValue}`, ns, defaultValue: dv })
|
|
506
486
|
}
|
|
507
487
|
|
|
508
|
-
//
|
|
488
|
+
// 2. Handle Plurals
|
|
509
489
|
const hasCount = getObjectPropValue(options, 'count') !== undefined
|
|
510
490
|
const isOrdinalByOption = getObjectPropValue(options, 'ordinal') === true
|
|
511
491
|
if (hasCount || isOrdinalByKey) {
|
|
512
|
-
//
|
|
513
|
-
|
|
514
|
-
|
|
492
|
+
// If we have keys with context pluralize them
|
|
493
|
+
if (keysWithContext.length > 0) {
|
|
494
|
+
for (const { key, ns } of keysWithContext) {
|
|
495
|
+
// Pass the combined ordinal flag to the handler
|
|
496
|
+
this.handlePluralKeys(key, ns, options, isOrdinalByOption || isOrdinalByKey)
|
|
497
|
+
}
|
|
498
|
+
} else {
|
|
499
|
+
// Otherwise pluralize the base key
|
|
500
|
+
this.handlePluralKeys(finalKey, ns, options, isOrdinalByOption || isOrdinalByKey)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
continue // This key is fully handled
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (keysWithContext.length > 0) {
|
|
507
|
+
keysWithContext.forEach(this.pluginContext.addKey)
|
|
508
|
+
|
|
509
|
+
continue // This key is now fully handled
|
|
515
510
|
}
|
|
516
511
|
|
|
517
|
-
//
|
|
512
|
+
// 3. Handle returnObjects
|
|
518
513
|
if (getObjectPropValue(options, 'returnObjects') === true) {
|
|
519
514
|
this.objectKeys.add(finalKey)
|
|
520
515
|
// Fall through to add the base key itself
|
|
521
516
|
}
|
|
522
517
|
}
|
|
523
518
|
|
|
524
|
-
//
|
|
519
|
+
// 4. Handle selector API as implicit returnObjects
|
|
525
520
|
if (isSelectorAPI) {
|
|
526
521
|
this.objectKeys.add(finalKey)
|
|
527
522
|
// Fall through to add the base key itself
|
|
528
523
|
}
|
|
529
524
|
|
|
530
|
-
//
|
|
525
|
+
// 5. Default case: Add the simple key
|
|
531
526
|
this.pluginContext.addKey({ key: finalKey, ns, defaultValue: dv })
|
|
532
527
|
}
|
|
533
528
|
}
|
|
534
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Processed a call expression to extract keys from the specified argument.
|
|
532
|
+
*
|
|
533
|
+
* @param node - The call expression node
|
|
534
|
+
* @param argIndex - The index of the argument to process
|
|
535
|
+
* @returns An object containing the keys to process and a flag indicating if the selector API is used
|
|
536
|
+
*/
|
|
537
|
+
private handleCallExpressionArgument (
|
|
538
|
+
node: CallExpression,
|
|
539
|
+
argIndex: number
|
|
540
|
+
): { keysToProcess: string[]; isSelectorAPI: boolean } {
|
|
541
|
+
const firstArg = node.arguments[argIndex].expression
|
|
542
|
+
const keysToProcess: string[] = []
|
|
543
|
+
let isSelectorAPI = false
|
|
544
|
+
|
|
545
|
+
if (firstArg.type === 'ArrowFunctionExpression') {
|
|
546
|
+
const key = this.extractKeyFromSelector(firstArg)
|
|
547
|
+
if (key) {
|
|
548
|
+
keysToProcess.push(key)
|
|
549
|
+
isSelectorAPI = true
|
|
550
|
+
}
|
|
551
|
+
} else if (firstArg.type === 'ArrayExpression') {
|
|
552
|
+
for (const element of firstArg.elements) {
|
|
553
|
+
if (element?.expression) {
|
|
554
|
+
keysToProcess.push(...this.resolvePossibleStringValues(element.expression))
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
keysToProcess.push(...this.resolvePossibleStringValues(firstArg))
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
keysToProcess: keysToProcess.filter((key) => !!key),
|
|
563
|
+
isSelectorAPI,
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
535
567
|
/**
|
|
536
568
|
* Generates plural form keys based on the primary language's plural rules.
|
|
537
569
|
*
|
|
@@ -647,10 +679,41 @@ export class ASTVisitors {
|
|
|
647
679
|
const elementName = this.getElementName(node)
|
|
648
680
|
|
|
649
681
|
if (elementName && (this.config.extract.transComponents || ['Trans']).includes(elementName)) {
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
682
|
+
const extractedAttributes = extractFromTransComponent(node, this.config)
|
|
683
|
+
|
|
684
|
+
const keysToProcess: string[] = []
|
|
685
|
+
|
|
686
|
+
if (extractedAttributes) {
|
|
687
|
+
if (extractedAttributes.keyExpression) {
|
|
688
|
+
const keyValues = this.resolvePossibleStringValues(extractedAttributes.keyExpression)
|
|
689
|
+
keysToProcess.push(...keyValues)
|
|
690
|
+
} else {
|
|
691
|
+
keysToProcess.push(extractedAttributes.serializedChildren)
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
let extractedKeys: ExtractedKey[]
|
|
695
|
+
|
|
696
|
+
const { contextExpression, optionsNode, defaultValue, hasCount, isOrdinal, serializedChildren } = extractedAttributes
|
|
697
|
+
|
|
698
|
+
// If ns is not explicitly set on the component, try to find it from the key
|
|
699
|
+
// or the `t` prop
|
|
700
|
+
if (!extractedAttributes.ns) {
|
|
701
|
+
extractedKeys = keysToProcess.map(key => {
|
|
702
|
+
const nsSeparator = this.config.extract.nsSeparator ?? ':'
|
|
703
|
+
let ns: string | undefined
|
|
704
|
+
|
|
705
|
+
// If the key contains a namespace separator, it takes precedence
|
|
706
|
+
// over the default t ns value
|
|
707
|
+
if (nsSeparator && key.includes(nsSeparator)) {
|
|
708
|
+
let parts: string[]
|
|
709
|
+
([ns, ...parts] = key.split(nsSeparator))
|
|
710
|
+
|
|
711
|
+
key = parts.join(nsSeparator)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return { key, ns, defaultValue: defaultValue || serializedChildren, hasCount, isOrdinal }
|
|
715
|
+
})
|
|
716
|
+
|
|
654
717
|
const tProp = node.opening.attributes?.find(
|
|
655
718
|
attr =>
|
|
656
719
|
attr.type === 'JSXAttribute' &&
|
|
@@ -667,54 +730,160 @@ export class ASTVisitors {
|
|
|
667
730
|
const tIdentifier = tProp.value.expression.value
|
|
668
731
|
const scopeInfo = this.getVarFromScope(tIdentifier)
|
|
669
732
|
if (scopeInfo?.defaultNs) {
|
|
670
|
-
|
|
733
|
+
extractedKeys.forEach(key => {
|
|
734
|
+
if (!key.ns) {
|
|
735
|
+
key.ns = scopeInfo.defaultNs
|
|
736
|
+
}
|
|
737
|
+
})
|
|
671
738
|
}
|
|
672
739
|
}
|
|
740
|
+
} else {
|
|
741
|
+
const { ns } = extractedAttributes
|
|
742
|
+
extractedKeys = keysToProcess.map(key => {
|
|
743
|
+
return { key, ns, defaultValue: defaultValue || serializedChildren, hasCount, isOrdinal, }
|
|
744
|
+
})
|
|
673
745
|
}
|
|
674
746
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
747
|
+
extractedKeys.forEach(key => {
|
|
748
|
+
// Apply defaultNS from config if no namespace was found on the component and
|
|
749
|
+
// the key does not contain a namespace prefix
|
|
750
|
+
if (!key.ns) {
|
|
751
|
+
key.ns = this.config.extract.defaultNS
|
|
752
|
+
}
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
// Handle the combination of context and count
|
|
756
|
+
if (contextExpression && hasCount) {
|
|
757
|
+
// Find isOrdinal prop on the <Trans> component
|
|
758
|
+
const ordinalAttr = node.opening.attributes?.find(
|
|
759
|
+
(attr) =>
|
|
760
|
+
attr.type === 'JSXAttribute' &&
|
|
761
|
+
attr.name.type === 'Identifier' &&
|
|
762
|
+
attr.name.value === 'ordinal'
|
|
763
|
+
)
|
|
764
|
+
const isOrdinal = !!ordinalAttr
|
|
679
765
|
|
|
680
|
-
|
|
681
|
-
const contextValues = this.resolvePossibleStringValues(extractedKey.contextExpression)
|
|
766
|
+
const contextValues = this.resolvePossibleStringValues(contextExpression)
|
|
682
767
|
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
683
768
|
|
|
769
|
+
// Generate all combinations of context and plural forms
|
|
684
770
|
if (contextValues.length > 0) {
|
|
771
|
+
// Generate base plural forms (no context)
|
|
772
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
773
|
+
|
|
774
|
+
// Generate context + plural combinations
|
|
685
775
|
for (const context of contextValues) {
|
|
686
|
-
|
|
776
|
+
for (const extractedKey of extractedKeys) {
|
|
777
|
+
const contextKey = `${extractedKey.key}${contextSeparator}${context}`
|
|
778
|
+
this.generatePluralKeysForTrans(contextKey, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode)
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
// Fallback to just plural forms if context resolution fails
|
|
783
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
784
|
+
}
|
|
785
|
+
} else if (contextExpression) {
|
|
786
|
+
const contextValues = this.resolvePossibleStringValues(contextExpression)
|
|
787
|
+
const contextSeparator = this.config.extract.contextSeparator ?? '_'
|
|
788
|
+
|
|
789
|
+
if (contextValues.length > 0) {
|
|
790
|
+
for (const context of contextValues) {
|
|
791
|
+
for (const { key, ns, defaultValue } of extractedKeys) {
|
|
792
|
+
this.pluginContext.addKey({ key: `${key}${contextSeparator}${context}`, ns, defaultValue })
|
|
793
|
+
}
|
|
687
794
|
}
|
|
688
795
|
// Only add the base key as a fallback if the context is dynamic (i.e., not a simple string).
|
|
689
|
-
if (
|
|
690
|
-
this.pluginContext.addKey
|
|
796
|
+
if (contextExpression.type !== 'StringLiteral') {
|
|
797
|
+
extractedKeys.forEach(this.pluginContext.addKey)
|
|
691
798
|
}
|
|
692
799
|
}
|
|
693
|
-
} else if (
|
|
800
|
+
} else if (hasCount) {
|
|
694
801
|
// Find isOrdinal prop on the <Trans> component
|
|
695
802
|
const ordinalAttr = node.opening.attributes?.find(
|
|
696
803
|
(attr) =>
|
|
697
804
|
attr.type === 'JSXAttribute' &&
|
|
698
|
-
|
|
699
|
-
|
|
805
|
+
attr.name.type === 'Identifier' &&
|
|
806
|
+
attr.name.value === 'ordinal'
|
|
700
807
|
)
|
|
701
808
|
const isOrdinal = !!ordinalAttr
|
|
702
809
|
|
|
703
|
-
|
|
704
|
-
|
|
810
|
+
extractedKeys.forEach(extractedKey => this.generatePluralKeysForTrans(extractedKey.key, extractedKey.defaultValue, extractedKey.ns, isOrdinal, optionsNode))
|
|
811
|
+
} else {
|
|
812
|
+
extractedKeys.forEach(this.pluginContext.addKey)
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}
|
|
705
817
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
818
|
+
/**
|
|
819
|
+
* Generates plural keys for Trans components, with support for tOptions plural defaults.
|
|
820
|
+
*
|
|
821
|
+
* @param key - Base key name for pluralization
|
|
822
|
+
* @param defaultValue - Default value for the keys
|
|
823
|
+
* @param ns - Namespace for the keys
|
|
824
|
+
* @param isOrdinal - Whether to generate ordinal plural forms
|
|
825
|
+
* @param optionsNode - Optional tOptions object expression for plural-specific defaults
|
|
826
|
+
*
|
|
827
|
+
* @private
|
|
828
|
+
*/
|
|
829
|
+
private generatePluralKeysForTrans (key: string, defaultValue: string | undefined, ns: string | undefined, isOrdinal: boolean, optionsNode?: ObjectExpression): void {
|
|
830
|
+
try {
|
|
831
|
+
const type = isOrdinal ? 'ordinal' : 'cardinal'
|
|
832
|
+
const pluralCategories = new Intl.PluralRules(this.config.extract?.primaryLanguage, { type }).resolvedOptions().pluralCategories
|
|
833
|
+
const pluralSeparator = this.config.extract.pluralSeparator ?? '_'
|
|
834
|
+
|
|
835
|
+
// Get plural-specific default values from tOptions if available
|
|
836
|
+
let otherDefault: string | undefined
|
|
837
|
+
let ordinalOtherDefault: string | undefined
|
|
838
|
+
|
|
839
|
+
if (optionsNode) {
|
|
840
|
+
otherDefault = getObjectPropValue(optionsNode, `defaultValue${pluralSeparator}other`) as string | undefined
|
|
841
|
+
ordinalOtherDefault = getObjectPropValue(optionsNode, `defaultValue${pluralSeparator}ordinal${pluralSeparator}other`) as string | undefined
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
for (const category of pluralCategories) {
|
|
845
|
+
// Look for the most specific default value (e.g., defaultValue_ordinal_one)
|
|
846
|
+
const specificDefaultKey = isOrdinal ? `defaultValue${pluralSeparator}ordinal${pluralSeparator}${category}` : `defaultValue${pluralSeparator}${category}`
|
|
847
|
+
const specificDefault = optionsNode ? getObjectPropValue(optionsNode, specificDefaultKey) as string | undefined : undefined
|
|
712
848
|
|
|
713
|
-
|
|
849
|
+
// Determine the final default value using a clear fallback chain
|
|
850
|
+
let finalDefaultValue: string | undefined
|
|
851
|
+
if (typeof specificDefault === 'string') {
|
|
852
|
+
// 1. Use the most specific default if it exists (e.g., defaultValue_one)
|
|
853
|
+
finalDefaultValue = specificDefault
|
|
854
|
+
} else if (category === 'one' && typeof defaultValue === 'string') {
|
|
855
|
+
// 2. SPECIAL CASE: The 'one' category falls back to the main default value (children content)
|
|
856
|
+
finalDefaultValue = defaultValue
|
|
857
|
+
} else if (isOrdinal && typeof ordinalOtherDefault === 'string') {
|
|
858
|
+
// 3a. Other ordinal categories fall back to 'defaultValue_ordinal_other'
|
|
859
|
+
finalDefaultValue = ordinalOtherDefault
|
|
860
|
+
} else if (!isOrdinal && typeof otherDefault === 'string') {
|
|
861
|
+
// 3b. Other cardinal categories fall back to 'defaultValue_other'
|
|
862
|
+
finalDefaultValue = otherDefault
|
|
863
|
+
} else if (typeof defaultValue === 'string') {
|
|
864
|
+
// 4. If no '_other' is found, all categories can fall back to the main default value
|
|
865
|
+
finalDefaultValue = defaultValue
|
|
714
866
|
} else {
|
|
715
|
-
|
|
867
|
+
// 5. Final fallback to the base key itself
|
|
868
|
+
finalDefaultValue = key
|
|
716
869
|
}
|
|
870
|
+
|
|
871
|
+
const finalKey = isOrdinal
|
|
872
|
+
? `${key}${pluralSeparator}ordinal${pluralSeparator}${category}`
|
|
873
|
+
: `${key}${pluralSeparator}${category}`
|
|
874
|
+
|
|
875
|
+
this.pluginContext.addKey({
|
|
876
|
+
key: finalKey,
|
|
877
|
+
ns,
|
|
878
|
+
defaultValue: finalDefaultValue,
|
|
879
|
+
hasCount: true,
|
|
880
|
+
isOrdinal
|
|
881
|
+
})
|
|
717
882
|
}
|
|
883
|
+
} catch (e) {
|
|
884
|
+
this.logger.warn(`Could not determine plural rules for language "${this.config.extract?.primaryLanguage}". Falling back to simple key extraction.`)
|
|
885
|
+
// Fallback to a simple key if Intl API fails
|
|
886
|
+
this.pluginContext.addKey({ key, ns, defaultValue })
|
|
718
887
|
}
|
|
719
888
|
}
|
|
720
889
|
|
|
@@ -810,7 +979,7 @@ export class ASTVisitors {
|
|
|
810
979
|
* determined statically from the AST.
|
|
811
980
|
*
|
|
812
981
|
* Supports:
|
|
813
|
-
* - StringLiteral -> single value
|
|
982
|
+
* - StringLiteral -> single value (filtered to exclude empty strings for context)
|
|
814
983
|
* - ConditionalExpression (ternary) -> union of consequent and alternate resolved values
|
|
815
984
|
* - The identifier `undefined` -> empty array
|
|
816
985
|
*
|
|
@@ -823,7 +992,8 @@ export class ASTVisitors {
|
|
|
823
992
|
*/
|
|
824
993
|
private resolvePossibleStringValues (expression: Expression): string[] {
|
|
825
994
|
if (expression.type === 'StringLiteral') {
|
|
826
|
-
|
|
995
|
+
// Filter out empty strings as they should be treated as "no context" like i18next does
|
|
996
|
+
return expression.value ? [expression.value] : []
|
|
827
997
|
}
|
|
828
998
|
|
|
829
999
|
if (expression.type === 'ConditionalExpression') { // This is a ternary operator
|
|
@@ -1,7 +1,33 @@
|
|
|
1
|
-
import type { JSXElement } from '@swc/core'
|
|
2
|
-
import type {
|
|
1
|
+
import type { Expression, JSXElement, ObjectExpression, Property } from '@swc/core'
|
|
2
|
+
import type { I18nextToolkitConfig } from '../../types'
|
|
3
3
|
import { getObjectProperty, getObjectPropValue } from './ast-utils'
|
|
4
4
|
|
|
5
|
+
export interface ExtractedJSXAttributes {
|
|
6
|
+
/** holds the raw key expression from the AST */
|
|
7
|
+
keyExpression?: Expression;
|
|
8
|
+
|
|
9
|
+
/** holds the serialized JSX children from the AST */
|
|
10
|
+
serializedChildren: string;
|
|
11
|
+
|
|
12
|
+
/** Default value to use in the primary language */
|
|
13
|
+
defaultValue?: string;
|
|
14
|
+
|
|
15
|
+
/** Namespace this key belongs to (if defined on <Trans />) */
|
|
16
|
+
ns?: string;
|
|
17
|
+
|
|
18
|
+
/** Whether this key is used with pluralization (count parameter) */
|
|
19
|
+
hasCount?: boolean;
|
|
20
|
+
|
|
21
|
+
/** Whether this key is used with ordinal pluralization */
|
|
22
|
+
isOrdinal?: boolean;
|
|
23
|
+
|
|
24
|
+
/** AST node for options object, used for advanced plural handling in Trans */
|
|
25
|
+
optionsNode?: ObjectExpression;
|
|
26
|
+
|
|
27
|
+
/** hold the raw context expression from the AST */
|
|
28
|
+
contextExpression?: Expression;
|
|
29
|
+
}
|
|
30
|
+
|
|
5
31
|
/**
|
|
6
32
|
* Extracts translation keys from JSX Trans components.
|
|
7
33
|
*
|
|
@@ -27,13 +53,14 @@ import { getObjectProperty, getObjectPropValue } from './ast-utils'
|
|
|
27
53
|
* const result = extractFromTransComponent(jsxNode, config)
|
|
28
54
|
* // Returns: {
|
|
29
55
|
* // key: 'welcome.title',
|
|
56
|
+
* // keyExpression: { ... },
|
|
30
57
|
* // ns: 'home',
|
|
31
58
|
* // defaultValue: 'Welcome!',
|
|
32
59
|
* // hasCount: false
|
|
33
60
|
* // }
|
|
34
61
|
* ```
|
|
35
62
|
*/
|
|
36
|
-
export function extractFromTransComponent (node: JSXElement, config: I18nextToolkitConfig):
|
|
63
|
+
export function extractFromTransComponent (node: JSXElement, config: I18nextToolkitConfig): ExtractedJSXAttributes | null {
|
|
37
64
|
const i18nKeyAttr = node.opening.attributes?.find(
|
|
38
65
|
(attr) =>
|
|
39
66
|
attr.type === 'JSXAttribute' &&
|
|
@@ -54,7 +81,23 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
54
81
|
attr.name.type === 'Identifier' &&
|
|
55
82
|
attr.name.value === 'count'
|
|
56
83
|
)
|
|
57
|
-
|
|
84
|
+
|
|
85
|
+
const valuesAttr = node.opening.attributes?.find(
|
|
86
|
+
(attr) => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'values'
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
// Find the 'count' property in the 'values' object if count={...} is not defined
|
|
90
|
+
let valuesCountProperty: Property | undefined
|
|
91
|
+
if (
|
|
92
|
+
!countAttr &&
|
|
93
|
+
valuesAttr?.type === 'JSXAttribute' &&
|
|
94
|
+
valuesAttr.value?.type === 'JSXExpressionContainer' &&
|
|
95
|
+
valuesAttr.value.expression.type === 'ObjectExpression'
|
|
96
|
+
) {
|
|
97
|
+
valuesCountProperty = getObjectProperty(valuesAttr.value.expression, 'count')
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const hasCount = !!countAttr || !!valuesCountProperty
|
|
58
101
|
|
|
59
102
|
const tOptionsAttr = node.opening.attributes?.find(
|
|
60
103
|
(attr) =>
|
|
@@ -66,27 +109,25 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
66
109
|
? tOptionsAttr.value.expression
|
|
67
110
|
: undefined
|
|
68
111
|
|
|
112
|
+
// Find isOrdinal prop on the <Trans> component
|
|
113
|
+
const ordinalAttr = node.opening.attributes?.find(
|
|
114
|
+
(attr) =>
|
|
115
|
+
attr.type === 'JSXAttribute' &&
|
|
116
|
+
attr.name.type === 'Identifier' &&
|
|
117
|
+
attr.name.value === 'ordinal'
|
|
118
|
+
)
|
|
119
|
+
const isOrdinal = !!ordinalAttr
|
|
120
|
+
|
|
69
121
|
const contextAttr = node.opening.attributes?.find(
|
|
70
122
|
(attr) =>
|
|
71
123
|
attr.type === 'JSXAttribute' &&
|
|
72
|
-
|
|
73
|
-
|
|
124
|
+
attr.name.type === 'Identifier' &&
|
|
125
|
+
attr.name.value === 'context'
|
|
74
126
|
)
|
|
75
127
|
let contextExpression = (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'JSXExpressionContainer')
|
|
76
128
|
? contextAttr.value.expression
|
|
77
129
|
: undefined
|
|
78
130
|
|
|
79
|
-
let key: string
|
|
80
|
-
if (i18nKeyAttr?.type === 'JSXAttribute' && i18nKeyAttr.value?.type === 'StringLiteral') {
|
|
81
|
-
key = i18nKeyAttr.value.value
|
|
82
|
-
} else {
|
|
83
|
-
key = serializeJSXChildren(node.children, config)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!key) {
|
|
87
|
-
return null
|
|
88
|
-
}
|
|
89
|
-
|
|
90
131
|
// 1. Prioritize direct props for 'ns' and 'context'
|
|
91
132
|
const nsAttr = node.opening.attributes?.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'ns')
|
|
92
133
|
let ns: string | undefined
|
|
@@ -109,14 +150,37 @@ export function extractFromTransComponent (node: JSXElement, config: I18nextTool
|
|
|
109
150
|
}
|
|
110
151
|
}
|
|
111
152
|
|
|
153
|
+
const serialized = serializeJSXChildren(node.children, config)
|
|
154
|
+
|
|
112
155
|
let defaultValue = config.extract.defaultValue || ''
|
|
113
156
|
if (defaultsAttr?.type === 'JSXAttribute' && defaultsAttr.value?.type === 'StringLiteral') {
|
|
114
157
|
defaultValue = defaultsAttr.value.value
|
|
115
|
-
} else {
|
|
116
|
-
defaultValue = serializeJSXChildren(node.children, config)
|
|
117
158
|
}
|
|
118
159
|
|
|
119
|
-
|
|
160
|
+
let keyExpression: Expression | undefined
|
|
161
|
+
if (i18nKeyAttr?.type === 'JSXAttribute') {
|
|
162
|
+
if (i18nKeyAttr.value?.type === 'StringLiteral') {
|
|
163
|
+
keyExpression = i18nKeyAttr.value
|
|
164
|
+
} else if (
|
|
165
|
+
i18nKeyAttr.value?.type === 'JSXExpressionContainer' &&
|
|
166
|
+
i18nKeyAttr.value.expression.type !== 'JSXEmptyExpression'
|
|
167
|
+
) {
|
|
168
|
+
keyExpression = i18nKeyAttr.value.expression
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!keyExpression) return null
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
keyExpression,
|
|
176
|
+
serializedChildren: serialized,
|
|
177
|
+
ns,
|
|
178
|
+
defaultValue,
|
|
179
|
+
hasCount,
|
|
180
|
+
isOrdinal,
|
|
181
|
+
contextExpression,
|
|
182
|
+
optionsNode,
|
|
183
|
+
}
|
|
120
184
|
}
|
|
121
185
|
|
|
122
186
|
/**
|
|
@@ -14,7 +14,7 @@ import type { ObjectExpression } from '@swc/core';
|
|
|
14
14
|
* @param propName - The property name to locate
|
|
15
15
|
* @returns The matching KeyValueProperty node if found, otherwise undefined.
|
|
16
16
|
*/
|
|
17
|
-
export declare function getObjectProperty(object: ObjectExpression, propName: string):
|
|
17
|
+
export declare function getObjectProperty(object: ObjectExpression, propName: string): import("@swc/types").KeyValueProperty | undefined;
|
|
18
18
|
/**
|
|
19
19
|
* Extracts string value from object property.
|
|
20
20
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast-utils.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEjD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ast-utils.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAEjD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,qDAU5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAE,MAAM,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAWrH"}
|
|
@@ -152,6 +152,14 @@ export declare class ASTVisitors {
|
|
|
152
152
|
* @private
|
|
153
153
|
*/
|
|
154
154
|
private handleCallExpression;
|
|
155
|
+
/**
|
|
156
|
+
* Processed a call expression to extract keys from the specified argument.
|
|
157
|
+
*
|
|
158
|
+
* @param node - The call expression node
|
|
159
|
+
* @param argIndex - The index of the argument to process
|
|
160
|
+
* @returns An object containing the keys to process and a flag indicating if the selector API is used
|
|
161
|
+
*/
|
|
162
|
+
private handleCallExpressionArgument;
|
|
155
163
|
/**
|
|
156
164
|
* Generates plural form keys based on the primary language's plural rules.
|
|
157
165
|
*
|
|
@@ -188,6 +196,18 @@ export declare class ASTVisitors {
|
|
|
188
196
|
* @private
|
|
189
197
|
*/
|
|
190
198
|
private handleJSXElement;
|
|
199
|
+
/**
|
|
200
|
+
* Generates plural keys for Trans components, with support for tOptions plural defaults.
|
|
201
|
+
*
|
|
202
|
+
* @param key - Base key name for pluralization
|
|
203
|
+
* @param defaultValue - Default value for the keys
|
|
204
|
+
* @param ns - Namespace for the keys
|
|
205
|
+
* @param isOrdinal - Whether to generate ordinal plural forms
|
|
206
|
+
* @param optionsNode - Optional tOptions object expression for plural-specific defaults
|
|
207
|
+
*
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
private generatePluralKeysForTrans;
|
|
191
211
|
/**
|
|
192
212
|
* Extracts element name from JSX opening tag.
|
|
193
213
|
*
|
|
@@ -223,7 +243,7 @@ export declare class ASTVisitors {
|
|
|
223
243
|
* determined statically from the AST.
|
|
224
244
|
*
|
|
225
245
|
* Supports:
|
|
226
|
-
* - StringLiteral -> single value
|
|
246
|
+
* - StringLiteral -> single value (filtered to exclude empty strings for context)
|
|
227
247
|
* - ConditionalExpression (ternary) -> union of consequent and alternate resolved values
|
|
228
248
|
* - The identifier `undefined` -> empty array
|
|
229
249
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAA+G,MAAM,WAAW,CAAA;AACpJ,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ast-visitors.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/ast-visitors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAA+G,MAAM,WAAW,CAAA;AACpJ,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,EAAgB,MAAM,aAAa,CAAA;AAqB5F;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAoC;IAE/C,UAAU,cAAoB;IAErC;;;;;;OAMG;gBAED,MAAM,EAAE,oBAAoB,EAC5B,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,MAAM;IAOhB;;;;;OAKG;IACI,KAAK,CAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAMjC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,IAAI;IAsDZ;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAIjB;;;;;;;;OAQG;IACH,OAAO,CAAC,aAAa;IAMrB;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IASvB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,wBAAwB;IAmChC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,8BAA8B;IAwDtC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,yBAAyB;IAoBjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IAgK5B;;;;;;OAMG;IACH,OAAO,CAAC,4BAA4B;IA8BpC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IA4DxB;;;;;;;;OAQG;IACH,OAAO,CAAC,sBAAsB;IAkB9B;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IA4IxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IA6DlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,sBAAsB;IA2C9B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,2BAA2B;IAoBnC;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,OAAO,CAAC,eAAe;CA2BxB"}
|
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
import type { JSXElement } from '@swc/core';
|
|
2
|
-
import type {
|
|
1
|
+
import type { Expression, JSXElement, ObjectExpression } from '@swc/core';
|
|
2
|
+
import type { I18nextToolkitConfig } from '../../types';
|
|
3
|
+
export interface ExtractedJSXAttributes {
|
|
4
|
+
/** holds the raw key expression from the AST */
|
|
5
|
+
keyExpression?: Expression;
|
|
6
|
+
/** holds the serialized JSX children from the AST */
|
|
7
|
+
serializedChildren: string;
|
|
8
|
+
/** Default value to use in the primary language */
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
/** Namespace this key belongs to (if defined on <Trans />) */
|
|
11
|
+
ns?: string;
|
|
12
|
+
/** Whether this key is used with pluralization (count parameter) */
|
|
13
|
+
hasCount?: boolean;
|
|
14
|
+
/** Whether this key is used with ordinal pluralization */
|
|
15
|
+
isOrdinal?: boolean;
|
|
16
|
+
/** AST node for options object, used for advanced plural handling in Trans */
|
|
17
|
+
optionsNode?: ObjectExpression;
|
|
18
|
+
/** hold the raw context expression from the AST */
|
|
19
|
+
contextExpression?: Expression;
|
|
20
|
+
}
|
|
3
21
|
/**
|
|
4
22
|
* Extracts translation keys from JSX Trans components.
|
|
5
23
|
*
|
|
@@ -25,11 +43,12 @@ import type { ExtractedKey, I18nextToolkitConfig } from '../../types';
|
|
|
25
43
|
* const result = extractFromTransComponent(jsxNode, config)
|
|
26
44
|
* // Returns: {
|
|
27
45
|
* // key: 'welcome.title',
|
|
46
|
+
* // keyExpression: { ... },
|
|
28
47
|
* // ns: 'home',
|
|
29
48
|
* // defaultValue: 'Welcome!',
|
|
30
49
|
* // hasCount: false
|
|
31
50
|
* // }
|
|
32
51
|
* ```
|
|
33
52
|
*/
|
|
34
|
-
export declare function extractFromTransComponent(node: JSXElement, config: I18nextToolkitConfig):
|
|
53
|
+
export declare function extractFromTransComponent(node: JSXElement, config: I18nextToolkitConfig): ExtractedJSXAttributes | null;
|
|
35
54
|
//# sourceMappingURL=jsx-parser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"jsx-parser.d.ts","sourceRoot":"","sources":["../../../src/extractor/parsers/jsx-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,gBAAgB,EAAY,MAAM,WAAW,CAAA;AACnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAGvD,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,aAAa,CAAC,EAAE,UAAU,CAAC;IAE3B,qDAAqD;IACrD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,oEAAoE;IACpE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,mDAAmD;IACnD,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,yBAAyB,CAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,IAAI,CAyHxH"}
|
package/vitest.config.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-unresolved
|
|
2
|
-
import swc from 'unplugin-swc'
|
|
3
|
-
import { defineConfig } from 'vitest/config'
|
|
4
|
-
|
|
5
|
-
export default defineConfig({
|
|
6
|
-
test: {
|
|
7
|
-
// Make vitest globals like `describe` and `it` available without importing
|
|
8
|
-
globals: true,
|
|
9
|
-
// Look for test files in the entire project
|
|
10
|
-
root: './',
|
|
11
|
-
coverage: {
|
|
12
|
-
include: ['src/**/*'],
|
|
13
|
-
exclude: ['src/types.ts', 'src/index.ts', 'src/extractor/index.ts']
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
plugins: [swc.vite()]
|
|
17
|
-
})
|