i18next-cli 1.33.5 → 1.34.1

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.
Files changed (65) hide show
  1. package/README.md +2 -1
  2. package/dist/cjs/cli.js +271 -1
  3. package/dist/cjs/config.js +211 -1
  4. package/dist/cjs/extractor/core/ast-visitors.js +364 -1
  5. package/dist/cjs/extractor/core/extractor.js +245 -1
  6. package/dist/cjs/extractor/core/key-finder.js +132 -1
  7. package/dist/cjs/extractor/core/translation-manager.js +745 -1
  8. package/dist/cjs/extractor/parsers/ast-utils.js +85 -1
  9. package/dist/cjs/extractor/parsers/call-expression-handler.js +941 -1
  10. package/dist/cjs/extractor/parsers/comment-parser.js +375 -1
  11. package/dist/cjs/extractor/parsers/expression-resolver.js +362 -1
  12. package/dist/cjs/extractor/parsers/jsx-handler.js +492 -1
  13. package/dist/cjs/extractor/parsers/jsx-parser.js +355 -1
  14. package/dist/cjs/extractor/parsers/scope-manager.js +408 -1
  15. package/dist/cjs/extractor/plugin-manager.js +106 -1
  16. package/dist/cjs/heuristic-config.js +99 -1
  17. package/dist/cjs/index.js +28 -1
  18. package/dist/cjs/init.js +174 -1
  19. package/dist/cjs/linter.js +431 -1
  20. package/dist/cjs/locize.js +269 -1
  21. package/dist/cjs/migrator.js +196 -1
  22. package/dist/cjs/rename-key.js +354 -1
  23. package/dist/cjs/status.js +336 -1
  24. package/dist/cjs/syncer.js +120 -1
  25. package/dist/cjs/types-generator.js +165 -1
  26. package/dist/cjs/utils/default-value.js +43 -1
  27. package/dist/cjs/utils/file-utils.js +136 -1
  28. package/dist/cjs/utils/funnel-msg-tracker.js +75 -1
  29. package/dist/cjs/utils/logger.js +36 -1
  30. package/dist/cjs/utils/nested-object.js +124 -1
  31. package/dist/cjs/utils/validation.js +71 -1
  32. package/dist/esm/cli.js +269 -1
  33. package/dist/esm/config.js +206 -1
  34. package/dist/esm/extractor/core/ast-visitors.js +362 -1
  35. package/dist/esm/extractor/core/extractor.js +241 -1
  36. package/dist/esm/extractor/core/key-finder.js +130 -1
  37. package/dist/esm/extractor/core/translation-manager.js +743 -1
  38. package/dist/esm/extractor/parsers/ast-utils.js +80 -1
  39. package/dist/esm/extractor/parsers/call-expression-handler.js +939 -1
  40. package/dist/esm/extractor/parsers/comment-parser.js +373 -1
  41. package/dist/esm/extractor/parsers/expression-resolver.js +360 -1
  42. package/dist/esm/extractor/parsers/jsx-handler.js +490 -1
  43. package/dist/esm/extractor/parsers/jsx-parser.js +334 -1
  44. package/dist/esm/extractor/parsers/scope-manager.js +406 -1
  45. package/dist/esm/extractor/plugin-manager.js +103 -1
  46. package/dist/esm/heuristic-config.js +97 -1
  47. package/dist/esm/index.js +11 -1
  48. package/dist/esm/init.js +172 -1
  49. package/dist/esm/linter.js +425 -1
  50. package/dist/esm/locize.js +265 -1
  51. package/dist/esm/migrator.js +194 -1
  52. package/dist/esm/rename-key.js +352 -1
  53. package/dist/esm/status.js +334 -1
  54. package/dist/esm/syncer.js +118 -1
  55. package/dist/esm/types-generator.js +163 -1
  56. package/dist/esm/utils/default-value.js +41 -1
  57. package/dist/esm/utils/file-utils.js +131 -1
  58. package/dist/esm/utils/funnel-msg-tracker.js +72 -1
  59. package/dist/esm/utils/logger.js +34 -1
  60. package/dist/esm/utils/nested-object.js +120 -1
  61. package/dist/esm/utils/validation.js +68 -1
  62. package/package.json +4 -2
  63. package/types/extractor/core/extractor.d.ts.map +1 -1
  64. package/types/extractor/parsers/jsx-parser.d.ts.map +1 -1
  65. package/types/locize.d.ts.map +1 -1
@@ -1 +1,334 @@
1
- import{getObjectPropValueExpression as e,getObjectPropValue as t,isSimpleTemplateLiteral as n}from"./ast-utils.js";function i(e){if(e)return"StringLiteral"===e.type?e.value:"TemplateLiteral"===e.type&&n(e)?e.quasis[0].cooked:void 0}function r(e){if(e){if("StringLiteral"===e.type)return e.value;if("ParenExpression"===e.type&&e.expr)return r(e.expr);if("ParenthesizedExpression"===e.type&&e.expression)return r(e.expression);if("TsAsExpression"===e.type&&e.expression)return r(e.expression);if("JSXFragment"===e.type||"JSXElement"===e.type){const t=e.children??[];let n="";for(const e of t)if(e){if("JSXText"!==e.type){if("JSXExpressionContainer"===e.type){if(!e.expression||"JSXEmptyExpression"===e.expression.type)continue;const t=i(e.expression);if(void 0===t)return;n+=t;continue}return}n+=e.value}return n}if("TemplateLiteral"===e.type&&Array.isArray(e.expressions)&&0===e.expressions.length){return(e.quasis??[]).map(e=>e?.cooked??e?.raw??"").join("")}if("TemplateLiteral"===e.type&&n(e))return e.quasis?.[0]?.cooked;if("BinaryExpression"===e.type){if("+"===(e.operator??e.op)){const t=r(e.left),n=r(e.right);if(void 0!==t&&void 0!==n)return t+n}}}}function s(e){return"StringLiteral"===e.value?.type?e.value.value:"JSXExpressionContainer"===e.value?.type?i(e.value.expression):void 0}function o(n,o){const p=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),l=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"count"===e.name.value),u=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"values"===e.name.value);let c;l||"JSXAttribute"!==u?.type||"JSXExpressionContainer"!==u.value?.type||"ObjectExpression"!==u.value.expression.type||(c=e(u.value.expression,"count"));const f=!!l||!!c,y=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"tOptions"===e.name.value),g="JSXAttribute"===y?.type&&"JSXExpressionContainer"===y.value?.type&&"ObjectExpression"===y.value.expression.type?y.value.expression:void 0,d=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ordinal"===e.name.value),v=!!d,S=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"context"===e.name.value);let x="JSXAttribute"===S?.type&&"JSXExpressionContainer"===S.value?.type?S.value.expression:"JSXAttribute"===S?.type&&"StringLiteral"===S.value?.type?S.value:void 0;const m=n.opening.attributes?.find(e=>"JSXAttribute"===e.type&&"Identifier"===e.name.type&&"ns"===e.name.value);let h;h="JSXAttribute"===m?.type?s(m):void 0,g&&(void 0===h&&(h=t(g,"ns")),void 0===x&&(x=e(g,"context")));const J=function(e,t){if(!e||0===e.length)return"";const n=new Set(t.extract.transKeepBasicHtmlNodesFor??["br","strong","i","p"]),s=e=>e&&"JSXText"===e.type&&/^\s*$/.test(e.value)&&e.value.includes("\n"),o=e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type),p=(e,t)=>{for(let n=t;n<e.length;n++){const t=e[n];if(t&&("JSXText"!==t.type||!s(t)))return t}};function a(e,t,r=!1,l=!1){if(!e||!e.length)return;const u=l&&e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length>1;let c=0,f=e.length-1;for(;c<=f&&s(e[c]);)c++;for(;f>=c&&s(e[f]);)f--;const y=c<=f?e.slice(c,f+1):[],g=y.some(e=>e&&("JSXElement"===e.type||"JSXFragment"===e.type));for(let e=0;e<y.length;e++){const c=y[e];if(c)if("JSXText"!==c.type){if("JSXExpressionContainer"===c.type){if(r&&!g)continue;const n=i(c.expression);if(void 0!==n){const i=/^\s*$/.test(n)&&!n.includes("\n"),r=y[e-1],a=y[e+1];if(i){const n=p(y,e+1);if(o(r)&&o(n));else{const n=y[e+2];if(a&&"JSXText"===a.type&&s(a)&&n&&("JSXElement"===n.type||"JSXFragment"===n.type)){const t=y[e-1],n=y[e-2];if(!t||"JSXText"!==t.type&&n&&"JSXExpressionContainer"===n.type)continue}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&a&&"JSXText"===a.type&&s(a))continue;const i=!a||"JSXText"===a.type&&!s(a);if(r&&"JSXText"===r.type&&i){const e=t[t.length-1];if(e&&"JSXText"===e.type){e.value=String(e.value)+c.expression.value;continue}}if(r&&("JSXElement"===r.type||"JSXFragment"===r.type)&&a&&"JSXText"===a.type&&s(a))continue}}}t.push(c);continue}if("JSXElement"===c.type){const e=c.opening&&c.opening.name&&"Identifier"===c.opening.name.type?c.opening.name.value:void 0;if(e&&n.has(e)){const r=c.opening&&Array.isArray(c.opening.attributes)&&c.opening.attributes.length>0,o=c.children||[],p=1===o.length&&("JSXText"===o[0]?.type||"JSXExpressionContainer"===o[0]?.type&&void 0!==i(o[0].expression)),f=!o.length,g=p;if(r&&!p)t.push(c),a(c.children||[],t,!0);else if(f)t.push(c);else if(!g){if((c.children||[]).some(e=>"JSXExpressionContainer"===e.type&&void 0===i(e.expression)))t.push(c),a(c.children||[],t,!0);else if("p"===e){const e=(o||[]).some(e=>{if(!e||"JSXElement"!==e.type)return!1;const t=e.opening&&e.opening.name&&"Identifier"===e.opening.name.type?e.opening.name.value:void 0,r=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0,s=e.children||[],o=1===s.length&&("JSXText"===s[0]?.type||"JSXExpressionContainer"===s[0]?.type&&void 0!==i(s[0].expression));return!(t&&n.has(t)&&!r&&(0===s.length||o))}),r=l&&y.some(e=>e&&e!==c&&"JSXText"===e.type&&!s(e));u||l&&e&&!r?(t.push(c),a(c.children||[],t,!0,!1)):a(c.children||[],t,!1,!1)}else a(c.children||[],t,!1,!1)}continue}t.push(c),a(c.children||[],t,!0);continue}"JSXFragment"!==c.type||a(c.children||[],t,r)}else{if(r&&!g)continue;if(r&&s(c))continue;if(s(c)){const n=y[e-1],i=y[e+1];if(n&&("JSXElement"===n.type||"JSXFragment"===n.type)&&i&&("JSXElement"===i.type||"JSXFragment"===i.type))continue;const r=t[t.length-1],s=y[e-1];if(r){if(s&&"JSXExpressionContainer"===s.type)continue;if("JSXText"===r.type&&s&&"JSXText"===s.type){r.value=String(r.value)+c.value;continue}}}if(r&&g&&0===e)continue;t.push(c)}}}const l=[];function u(e){const t=new Map;if(!e||!e.length)return t;const r=e=>e&&"JSXText"===e.type&&!s(e)&&String(e.value).length>0,o=e=>{if(!e||"JSXExpressionContainer"!==e.type)return!1;const t=e.expression;if(!t||"JSXEmptyExpression"===t.type)return!1;const n=i(t);return void 0===n||(!/^\s*$/.test(n)||!n.includes("\n"))},p=e=>"Identifier"===e?.opening?.name?.type?e.opening.name.value:void 0,a=e=>{const t=e?.children||[];return 1===t.length&&("JSXText"===t[0]?.type||"JSXExpressionContainer"===t[0]?.type&&void 0!==i(t[0].expression))},u=e=>{const t=p(e);if(!t||!n.has(t))return!0;if("p"===t)return!0;const i=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0;return!!((e.children||[]).length>0)&&(!a(e)&&(!!i||-1!==l.indexOf(e)))},c=e=>{const t=p(e);if(!t||!n.has(t))return!1;if(0!==(e.children||[]).length)return!1;const i=e.opening&&Array.isArray(e.opening.attributes)&&e.opening.attributes.length>0;return"br"===t&&!i};let f=0;for(const n of e)n&&(r(n)||o(n)?f+=1:"JSXElement"!==n.type||(u(n)?(t.set(n,f),f+=1):c(n)&&(f+=1)));return t}a(e,l,!1,!0);const c=new Set,f=e=>String(e).replace(/^\s*\n\s*/g,"").replace(/\s*\n\s*$/g,""),y=e=>"Identifier"===e?.opening?.name?.type?e.opening.name.value:void 0,g=e=>{const t=y(e);if(!t||!n.has(t))return;const r=e?.children||[];if(1!==r.length)return;const s=r[0];return s?"JSXText"===s.type?String(s.value):"JSXExpressionContainer"===s.type?i(s.expression):void 0:void 0};function d(e,t,o=!1){if(!e||0===e.length)return"";let p="";const a=e=>{if(!e)return-1;if(t&&t.has(e))return t.get(e);if(t)for(const[n,i]of t.entries())try{if(n&&e&&n.span&&e.span&&n.span.start===e.span.start&&n.span.end===e.span.end)return i}catch(e){}return l.indexOf(e)},y=t=>{if(!o)return l.indexOf(t);const n=e.indexOf(t);if(-1===n)return-1;let i=0;for(let t=0;t<n;t++){const n=e[t];n&&("JSXText"===n.type?s(n)||i++:"JSXExpressionContainer"===n.type?n.expression&&"JSXEmptyExpression"!==n.expression.type&&i++:"JSXElement"===n.type&&i++)}return i};for(let t=0;t<e.length;t++){const v=e[t];if(v){if("JSXText"===v.type){if(s(v))continue;const i=e[t+1],r=e[t-1];if(r&&"JSXElement"===r.type){const i="Identifier"===r.opening?.name?.type?r.opening.name.value:void 0,s=i&&n.has(i),o=0===(r.children||[]).length;if(s&&o&&/^\s*\n\s*/.test(v.value)){const e=v.value.replace(/^\s*\n\s*/,"");if(e){p+=e;continue}continue}if(s&&!o&&/^\s*\n\s*/.test(v.value)){const e=g(r);if(void 0!==e&&/\s$/.test(e)){const e=v.value.replace(/^\s*\n\s*/g,"").replace(/^\s+/g,"");if(e){p+=e;continue}continue}}if(!s&&/^\s*\n\s*/.test(v.value)){const n=v.value.replace(/^\s*\n\s*/,"");if(n){if(r&&"JSXElement"===r.type&&Array.isArray(r.children)&&0===r.children.length){p+=n;continue}const i=e[t-2];if(i&&"JSXText"===i.type){const e=i.value.replace(/\n\s*$/,""),t=/[A-Za-z0-9]$/.test(e),r=/^[A-Za-z0-9]/.test(n),s=/^[a-z]/.test(n);if(t&&r&&s){p+=n;continue}}p+=" "+n;continue}continue}}if(/\n\s*$/.test(v.value)&&i&&"JSXElement"===i.type){const r=v.value.replace(/\n\s*$/,"");if(r.trim()){const o="Identifier"===i.opening?.name?.type?i.opening.name.value:void 0,a=o&&n.has(o),l=(i.children||[]).length>0,u=g(i),f=void 0!==u&&/^\s/.test(u),y=/\s\n/.test(v.value),d=e[t+2],S=d&&"JSXText"===d.type&&!s(d)&&/[a-zA-Z0-9]/.test(d.value),x=!!(i.opening&&Array.isArray(i.opening.attributes)&&i.opening.attributes.length>0),m=/^\s/.test(r)&&!/^\n/.test(r),h=r.trim(),J=m?" "+h:h,X=/[A-Za-z0-9]$/.test(h),E=d&&"string"==typeof d.value&&/^[A-Za-z0-9]/.test(d.value.trim()),$=d&&"string"==typeof d.value&&/^[a-z]/.test(d.value.trim()),b=d&&"string"==typeof d.value&&/^\s/.test(d.value)&&!/^\n/.test(d.value),A=x&&l&&S&&!(X&&E&&$&&!y&&!m&&!b),T=d&&"string"==typeof d.value&&/^[,;:!?.]/.test(d.value.trim()),I=A&&!T;X&&E&&$&&!y&&!m&&!b&&c.add(i),p+=y||a&&l&&!f||!a&&S&&m||I?J+" ":J;continue}}p+=v.value;continue}if("JSXExpressionContainer"===v.type){const e=v.expression;if(!e)continue;const t=i(e);if(void 0!==t)p+=t;else if("Identifier"===e.type)p+=`{{${e.value}}}`;else if("TsAsExpression"===e.type&&"ObjectExpression"===e.expression?.type){const t=e.expression,n=t.properties.filter(e=>"KeyValueProperty"===e.type&&e.key&&"Identifier"===e.key.type).map(e=>e.key.value);if(n.length>0)p+=n.map(e=>`{{${e}}}`).join("");else{const e=t.properties[0];e&&"Identifier"===e.type?p+=`{{${e.value}}}`:p+=""}}else if("ObjectExpression"===e.type){const t=e.properties[0];t&&"KeyValueProperty"===t.type&&t.key&&"Identifier"===t.key.type?p+=`{{${t.key.value}}}`:t&&"Identifier"===t.type?p+=`{{${t.value}}}`:p+=""}else if("MemberExpression"===e.type&&e.property&&"Identifier"===e.property.type)p+=`{{${e.property.value}}}`;else if("CallExpression"===e.type&&"Identifier"===e.callee?.type)p+=`{{${e.callee.value}}}`;else if("ConditionalExpression"===e.type){const t=r(e.consequent),n=r(e.alternate);if(void 0!==t&&void 0!==n){const e=t.trim(),i=n.trim(),r=i.startsWith(e),s=e.startsWith(i);r&&i.length>e.length?p+=i:s&&e.length>i.length?p+=e:p+=t}else p+=void 0!==t?t:void 0!==n?n:""}else"JSXEmptyExpression"===e.type||(p+="");continue}if("JSXElement"===v.type){const r=o?y(v):void 0;let s;if(v.opening&&v.opening.name&&"Identifier"===v.opening.name.type&&(s=v.opening.name.value),s&&n.has(s)){const c=v.opening&&Array.isArray(v.opening.attributes)&&v.opening.attributes.length>0,y=v.children||[],g=y.length>0,S=1===y.length&&("JSXText"===y[0]?.type||"JSXExpressionContainer"===y[0]?.type&&void 0!==i(y[0].expression)),x="p"===s;let m=0;if(x&&o&&(m=e.filter(e=>e&&"JSXElement"===e.type&&"p"===e.opening?.name?.value).length),!g||S){const i=S?d(y,void 0):"";if(""!==String(i).trim())if(n.has(s)&&"p"!==s)p+=`<${s}>${f(i)}</${s}>`;else if(x&&o&&m>1)p+=`<${s}>${f(i)}</${s}>`;else if(x){const e=o&&void 0!==r?r:l.indexOf(v);p+=`<${e}>${f(i)}</${e}>`}else{const e=o&&void 0!==r?r:l.indexOf(v);p+=`<${e}>${f(i)}</${e}>`}else{const n=e[t-1];n&&"JSXText"===n.type&&/\n\s*$/.test(n.value)&&(p=p.replace(/\s+$/,"")),p+=`<${s} />`}}else if(c&&!S){const e=o&&void 0!==r?r:a(v),t=u(y),n=d(y,t.size?t:void 0);p+=`<${e}>${f(n)}</${e}>`}else{const e=l.indexOf(v);if(-1!==e){const t=void 0!==r?r:e,n=u(y),i=d(y,n.size?n:void 0,!1);p+=`<${t}>${f(i)}</${t}>`}else{const e=d(y,void 0,!1);p+=`<${s}>${f(e)}</${s}>`}}}else{const e=v.children||[],t=a(v),n=u(e),i=d(e,n.size?n:void 0);p+=`<${t}>${f(i)}</${t}>`}continue}"JSXFragment"!==v.type||(p+=d(v.children||[]))}}return p}const v=d(e,void 0,!0),S=String(v).replace(/<br \/>\s*\n\s*/g,"<br />"),x=String(S),m=new Set;if(c&&c.size>0)for(let e=0;e<l.length;e++)c.has(l[e])&&m.add(e);let h=String(x);if(m&&m.size>0)for(const e of m)try{h=h.replace(new RegExp("\\s+<"+e+">","g"),"<"+e+">"),h=h.replace(new RegExp("<\\/"+e+">\\s+","g"),"</"+e+">")}catch(e){}h=h.replace(/<\/(\d+)>\s*\n\s*(\S)/g,(e,t,n)=>{const i=Number(t);return m.has(i)?`</${t}>${n}`:`</${t}> ${n}`}),h=h.replace(/(\S)\s*\n\s*<(\d+)/g,(e,t,n)=>{const i=Number(n);return m.has(i)?`${t}<${n}`:`${t} <${n}`}),h=h.replace(/\s*\n\s*/g," "),h=h.replace(/\s+/g," "),h=h.replace(/\(\s+(\{\{)/g,"($1"),h=h.replace(/(\}\})\s+\)/g,"$1)"),h=h.replace(/\s+([,;:!?.])/g,"$1");const J=h.trim();let X=String(J);if(m&&m.size>0)for(const e of m)try{X=X.replace(new RegExp("[\\s\\u00A0]+<"+e+">","g"),"<"+e+">"),X=X.replace(new RegExp("<\\/"+e+">[\\s\\u00A0]+","g"),"</"+e+">")}catch(e){}try{for(let e=0;e<l.length;e++){const t=l[e];if(!t||"JSXElement"!==t.type)continue;const n=l[e-1],i=l[e+1];if(!n||!i)continue;if("JSXText"!==n.type||"JSXText"!==i.type)continue;const r=String(n.value),s=String(i.value),o=r.replace(/\n\s*$/,""),p=/[A-Za-z0-9]$/.test(o),a=/^[A-Za-z0-9]/.test(s.trim()),u=/^[a-z]/.test(s.trim()),c=/\s\n/.test(r),f=s&&/^\s/.test(s)&&!/^\n/.test(s);if(p&&a&&u&&!c&&!f){const t=e;X=X.replace(new RegExp("\\s+<"+t+">","g"),"<"+t+">")}}}catch(e){}return X.trim()}(n.children,o);let X;const E="JSXAttribute"===a?.type?s(a):void 0;if(void 0!==E)X=E;else{const e=o.extract.defaultValue;X="string"==typeof e?e:""}let $,b;if("JSXAttribute"===p?.type){if("StringLiteral"===p.value?.type){if($=p.value,b=$.value,!b||""===b.trim())return null;if(h&&"StringLiteral"===$.type){const e=o.extract.nsSeparator??":",t=$.value;if(e&&t.startsWith(`${h}${e}`)){if(b=t.slice(`${h}${e}`.length),!b||""===b.trim())return null;$={...$,value:b}}}}else"JSXExpressionContainer"===p.value?.type&&"JSXEmptyExpression"!==p.value.expression.type&&($=p.value.expression);if(!$)return null}a||!b||J.trim()?!a&&J.trim()&&(X=J):X=b;return{keyExpression:$,serializedChildren:J,ns:h,defaultValue:X,hasCount:f,isOrdinal:v,contextExpression:x,optionsNode:g,explicitDefault:void 0!==E||(e=>{if(!e||!Array.isArray(e.properties))return!1;for(const t of e.properties)if(t&&"KeyValueProperty"===t.type&&t.key){const e="Identifier"===t.key.type&&t.key.value||"StringLiteral"===t.key.type&&t.key.value;if("string"==typeof e&&e.startsWith("defaultValue"))return!0}return!1})(g)}}export{o as extractFromTransComponent};
1
+ import { getObjectPropValueExpression, getObjectPropValue, isSimpleTemplateLiteral } from './ast-utils.js';
2
+ import * as React from 'react';
3
+ import { getDefaults, nodesToString } from 'react-i18next';
4
+
5
+ function getStringLiteralFromExpression(expression) {
6
+ if (!expression)
7
+ return undefined;
8
+ if (expression.type === 'StringLiteral') {
9
+ return expression.value;
10
+ }
11
+ if (expression.type === 'TemplateLiteral' && isSimpleTemplateLiteral(expression)) {
12
+ return expression.quasis[0].cooked;
13
+ }
14
+ return undefined;
15
+ }
16
+ function getStringLiteralFromAttribute(attr) {
17
+ if (attr.value?.type === 'StringLiteral') {
18
+ return attr.value.value;
19
+ }
20
+ if (attr.value?.type === 'JSXExpressionContainer') {
21
+ return getStringLiteralFromExpression(attr.value.expression);
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Extracts translation keys from JSX Trans components.
27
+ *
28
+ * This function handles various Trans component patterns:
29
+ * - Explicit i18nKey prop: `<Trans i18nKey="my.key">content</Trans>`
30
+ * - Implicit keys from children: `<Trans>Hello World</Trans>`
31
+ * - Namespace specification: `<Trans ns="common">content</Trans>`
32
+ * - Default values: `<Trans defaults="Default text">content</Trans>`
33
+ * - Pluralization: `<Trans count={count}>content</Trans>`
34
+ * - HTML preservation: `<Trans>Hello <strong>world</strong></Trans>`
35
+ *
36
+ * @param node - The JSX element node to process
37
+ * @param config - The toolkit configuration containing extraction settings
38
+ * @returns Extracted key information or null if no valid key found
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * // Input JSX:
43
+ * // <Trans i18nKey="welcome.title" ns="home" defaults="Welcome!">
44
+ * // Welcome to our <strong>amazing</strong> app!
45
+ * // </Trans>
46
+ *
47
+ * const result = extractFromTransComponent(jsxNode, config)
48
+ * // Returns: {
49
+ * // key: 'welcome.title',
50
+ * // keyExpression: { ... },
51
+ * // ns: 'home',
52
+ * // defaultValue: 'Welcome!',
53
+ * // hasCount: false
54
+ * // }
55
+ * ```
56
+ */
57
+ function extractFromTransComponent(node, config) {
58
+ const i18nKeyAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
59
+ attr.name.type === 'Identifier' &&
60
+ attr.name.value === 'i18nKey');
61
+ const defaultsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
62
+ attr.name.type === 'Identifier' &&
63
+ attr.name.value === 'defaults');
64
+ const countAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
65
+ attr.name.type === 'Identifier' &&
66
+ attr.name.value === 'count');
67
+ const valuesAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'values');
68
+ // Find the 'count' property in the 'values' object if count={...} is not defined
69
+ let valuesCountProperty;
70
+ if (!countAttr &&
71
+ valuesAttr?.type === 'JSXAttribute' &&
72
+ valuesAttr.value?.type === 'JSXExpressionContainer' &&
73
+ valuesAttr.value.expression.type === 'ObjectExpression') {
74
+ valuesCountProperty = getObjectPropValueExpression(valuesAttr.value.expression, 'count');
75
+ }
76
+ const hasCount = !!countAttr || !!valuesCountProperty;
77
+ const tOptionsAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
78
+ attr.name.type === 'Identifier' &&
79
+ attr.name.value === 'tOptions');
80
+ const optionsNode = (tOptionsAttr?.type === 'JSXAttribute' && tOptionsAttr.value?.type === 'JSXExpressionContainer' && tOptionsAttr.value.expression.type === 'ObjectExpression')
81
+ ? tOptionsAttr.value.expression
82
+ : undefined;
83
+ // Find isOrdinal prop on the <Trans> component
84
+ const ordinalAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
85
+ attr.name.type === 'Identifier' &&
86
+ attr.name.value === 'ordinal');
87
+ const isOrdinal = !!ordinalAttr;
88
+ const contextAttr = node.opening.attributes?.find((attr) => attr.type === 'JSXAttribute' &&
89
+ attr.name.type === 'Identifier' &&
90
+ attr.name.value === 'context');
91
+ let contextExpression = (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'JSXExpressionContainer')
92
+ ? contextAttr.value.expression
93
+ : (contextAttr?.type === 'JSXAttribute' && contextAttr.value?.type === 'StringLiteral')
94
+ ? contextAttr.value
95
+ : undefined;
96
+ // 1. Prioritize direct props for 'ns' and 'context'
97
+ const nsAttr = node.opening.attributes?.find(attr => attr.type === 'JSXAttribute' && attr.name.type === 'Identifier' && attr.name.value === 'ns');
98
+ let ns;
99
+ if (nsAttr?.type === 'JSXAttribute') {
100
+ ns = getStringLiteralFromAttribute(nsAttr);
101
+ }
102
+ else {
103
+ ns = undefined;
104
+ }
105
+ // 2. If not found, fall back to looking inside tOptions
106
+ if (optionsNode) {
107
+ if (ns === undefined) {
108
+ ns = getObjectPropValue(optionsNode, 'ns');
109
+ }
110
+ if (contextExpression === undefined) {
111
+ contextExpression = getObjectPropValueExpression(optionsNode, 'context');
112
+ }
113
+ }
114
+ const serialized = serializeJSXChildren(node.children, config);
115
+ // Handle default value properly
116
+ let defaultValue;
117
+ const defaultAttributeLiteral = defaultsAttr?.type === 'JSXAttribute' ? getStringLiteralFromAttribute(defaultsAttr) : undefined;
118
+ if (defaultAttributeLiteral !== undefined) {
119
+ // Explicit defaults attribute takes precedence
120
+ defaultValue = defaultAttributeLiteral;
121
+ }
122
+ else {
123
+ // Use the configured default value or fall back to empty string
124
+ const configuredDefault = config.extract.defaultValue;
125
+ if (typeof configuredDefault === 'string') {
126
+ defaultValue = configuredDefault;
127
+ }
128
+ else {
129
+ // For function-based defaults or undefined, use empty string as placeholder
130
+ // The translation manager will handle function resolution with proper context
131
+ defaultValue = '';
132
+ }
133
+ }
134
+ let keyExpression;
135
+ let processedKeyValue;
136
+ if (i18nKeyAttr?.type === 'JSXAttribute') {
137
+ if (i18nKeyAttr.value?.type === 'StringLiteral') {
138
+ keyExpression = i18nKeyAttr.value;
139
+ processedKeyValue = keyExpression.value;
140
+ // Validate that the key is not empty
141
+ if (!processedKeyValue || processedKeyValue.trim() === '') {
142
+ return null;
143
+ }
144
+ // Handle namespace prefix removal when both ns and i18nKey are provided
145
+ if (ns && keyExpression.type === 'StringLiteral') {
146
+ const nsSeparator = config.extract.nsSeparator ?? ':';
147
+ const keyValue = keyExpression.value;
148
+ // If the key starts with the namespace followed by the separator, remove the prefix
149
+ if (nsSeparator && keyValue.startsWith(`${ns}${nsSeparator}`)) {
150
+ processedKeyValue = keyValue.slice(`${ns}${nsSeparator}`.length);
151
+ // Validate processed key is not empty
152
+ if (!processedKeyValue || processedKeyValue.trim() === '') {
153
+ return null;
154
+ }
155
+ // Create a new StringLiteral with the namespace prefix removed
156
+ keyExpression = {
157
+ ...keyExpression,
158
+ value: processedKeyValue
159
+ };
160
+ }
161
+ }
162
+ }
163
+ else if (i18nKeyAttr.value?.type === 'JSXExpressionContainer' &&
164
+ i18nKeyAttr.value.expression.type !== 'JSXEmptyExpression') {
165
+ keyExpression = i18nKeyAttr.value.expression;
166
+ }
167
+ if (!keyExpression)
168
+ return null;
169
+ }
170
+ // If no explicit defaults provided and we have a processed key, use it as default value
171
+ // This matches the behavior of other similar tests in the codebase
172
+ if (!defaultsAttr && processedKeyValue && !serialized.trim()) {
173
+ defaultValue = processedKeyValue;
174
+ }
175
+ else if (!defaultsAttr && serialized.trim()) {
176
+ defaultValue = serialized;
177
+ }
178
+ // Determine if tOptions contained explicit defaultValue* properties
179
+ const optionsHasDefaultProps = (opts) => {
180
+ if (!opts || !Array.isArray(opts.properties))
181
+ return false;
182
+ for (const p of opts.properties) {
183
+ if (p && p.type === 'KeyValueProperty' && p.key) {
184
+ const keyName = (p.key.type === 'Identifier' && p.key.value) || (p.key.type === 'StringLiteral' && p.key.value);
185
+ if (typeof keyName === 'string' && keyName.startsWith('defaultValue'))
186
+ return true;
187
+ }
188
+ }
189
+ return false;
190
+ };
191
+ const explicitDefault = defaultAttributeLiteral !== undefined || optionsHasDefaultProps(optionsNode);
192
+ return {
193
+ keyExpression,
194
+ serializedChildren: serialized,
195
+ ns,
196
+ defaultValue,
197
+ hasCount,
198
+ isOrdinal,
199
+ contextExpression,
200
+ optionsNode,
201
+ explicitDefault
202
+ };
203
+ }
204
+ /**
205
+ * Creates a dummy React component. The implementation / return value is
206
+ * irrelevant, as long as we have something realistic-looking to pass to
207
+ * react-i18next.
208
+ */
209
+ function makeDummyComponent(name) {
210
+ const result = () => null;
211
+ Object.defineProperty(result, 'name', { value: name });
212
+ result.displayName = name;
213
+ return result;
214
+ }
215
+ function makeDummyProps(attributes) {
216
+ return attributes.length
217
+ ? Object.fromEntries(attributes.map((attr) => {
218
+ if (attr.type === 'SpreadElement') {
219
+ return null;
220
+ }
221
+ else if (attr.name.type === 'Identifier') {
222
+ return [attr.name.value, ''];
223
+ }
224
+ else {
225
+ return [`${attr.name.namespace.value}:${attr.name.name.value}`, ''];
226
+ }
227
+ }).filter(i => i != null))
228
+ : null;
229
+ }
230
+ function getElementName(element) {
231
+ switch (element.type) {
232
+ case 'Identifier':
233
+ return /\p{Uppercase_Letter}/u.test(element.value) ? makeDummyComponent(element.value) : element.value;
234
+ case 'JSXMemberExpression':
235
+ // element.object should be irrelevant for naming purposes here
236
+ return makeDummyComponent(element.property.value);
237
+ case 'JSXNamespacedName':
238
+ return `${element.namespace.value}:${element.name.value}`;
239
+ }
240
+ }
241
+ function trimTextNode(text) {
242
+ text = text.replace(/\r\n/g, '\n'); // Normalize line endings
243
+ // If text is ONLY whitespace AND contains a newline, remove it entirely
244
+ if (/^\s+$/.test(text) && /\n/.test(text)) {
245
+ return null;
246
+ }
247
+ // Trim leading/trailing whitespace sequences containing newlines
248
+ text = text.replace(/^[ \t]*\n[ \t]*/, '');
249
+ text = text.replace(/[ \t]*\n[ \t]*$/, '');
250
+ // Replace whitespace sequences containing newlines with single space
251
+ text = text.replace(/[ \t]*\n[ \t]*/g, ' ');
252
+ return text;
253
+ }
254
+ function swcExpressionToReactNode(expr) {
255
+ switch (expr.type) {
256
+ case 'JSXEmptyExpression':
257
+ return null;
258
+ case 'TsAsExpression':
259
+ return swcExpressionToReactNode(expr.expression);
260
+ case 'ParenthesisExpression':
261
+ return swcExpressionToReactNode(expr.expression);
262
+ case 'ConditionalExpression': {
263
+ const consequent = swcExpressionToReactNode(expr.consequent);
264
+ const alternate = swcExpressionToReactNode(expr.alternate);
265
+ // Heuristic:
266
+ // - If one branch is a strict prefix of the other, pick the longer (keeps extra static tail),
267
+ // e.g. "to select" vs "to select, or right click..."
268
+ // - Otherwise, stay deterministic and prefer consequent (avoids choosing alternates just because they’re 1 char longer).
269
+ if (typeof consequent === 'string' &&
270
+ typeof alternate === 'string' &&
271
+ alternate.length !== consequent.length &&
272
+ alternate.startsWith(consequent)) {
273
+ return alternate;
274
+ }
275
+ return consequent;
276
+ }
277
+ case 'StringLiteral':
278
+ return expr.value;
279
+ case 'TemplateLiteral':
280
+ if (isSimpleTemplateLiteral(expr)) {
281
+ return expr.quasis[0].raw;
282
+ }
283
+ // Too complex!
284
+ break;
285
+ case 'Identifier':
286
+ // Not a valid React element, but props for Trans interpolation
287
+ // TODO: This might actually be an error - not sure that react-i18next can handle at runtime
288
+ return { [expr.value]: expr.value };
289
+ case 'ObjectExpression': {
290
+ const keys = expr.properties.map((prop) => {
291
+ if (prop.type === 'KeyValueProperty' && (prop.key.type === 'Identifier' || prop.key.type === 'StringLiteral')) {
292
+ return prop.key.value;
293
+ }
294
+ else if (prop.type === 'Identifier') {
295
+ return prop.value;
296
+ }
297
+ else {
298
+ // Too complex to represent! TODO: Flag an error
299
+ return null;
300
+ }
301
+ }).filter(k => k !== null);
302
+ // Not a valid React element, but props for Trans interpolation
303
+ return Object.fromEntries(keys.map(k => [k, k]));
304
+ }
305
+ }
306
+ // Too complex to represent! TODO: Flag an error
307
+ return React.createElement('expression', { expression: expr });
308
+ }
309
+ function swcChildToReactNode(node) {
310
+ switch (node.type) {
311
+ case 'JSXText':
312
+ return trimTextNode(node.value);
313
+ case 'JSXExpressionContainer':
314
+ return swcExpressionToReactNode(node.expression);
315
+ case 'JSXSpreadChild':
316
+ return '';
317
+ case 'JSXElement':
318
+ return React.createElement(getElementName(node.opening.name), makeDummyProps(node.opening.attributes), ...swcChildrenToReactNodes(node.children));
319
+ case 'JSXFragment':
320
+ return React.createElement(React.Fragment, null, ...swcChildrenToReactNodes(node.children));
321
+ }
322
+ }
323
+ function swcChildrenToReactNodes(children) {
324
+ return children.map(swcChildToReactNode).filter(n => n !== null);
325
+ }
326
+ function serializeJSXChildren(children, config) {
327
+ const i18nextOptions = { ...getDefaults() };
328
+ if (config.extract.transKeepBasicHtmlNodesFor) {
329
+ i18nextOptions.transKeepBasicHtmlNodesFor = config.extract.transKeepBasicHtmlNodesFor;
330
+ }
331
+ return nodesToString(swcChildrenToReactNodes(children), i18nextOptions);
332
+ }
333
+
334
+ export { extractFromTransComponent };