@fluenti/cli 0.3.3 → 0.4.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/ai-provider.d.ts +4 -3
  2. package/dist/ai-provider.d.ts.map +1 -1
  3. package/dist/cli.cjs +13 -12
  4. package/dist/cli.cjs.map +1 -1
  5. package/dist/cli.js +486 -289
  6. package/dist/cli.js.map +1 -1
  7. package/dist/{compile-B4y2UPsX.js → compile-CdA4EZ-p.js} +36 -33
  8. package/dist/compile-CdA4EZ-p.js.map +1 -0
  9. package/dist/compile-cache.d.ts.map +1 -1
  10. package/dist/compile-kXClO6q4.cjs +8 -0
  11. package/dist/compile-kXClO6q4.cjs.map +1 -0
  12. package/dist/compile-worker.cjs +1 -1
  13. package/dist/compile-worker.cjs.map +1 -1
  14. package/dist/compile-worker.d.ts +1 -0
  15. package/dist/compile-worker.d.ts.map +1 -1
  16. package/dist/compile-worker.js +19 -7
  17. package/dist/compile-worker.js.map +1 -1
  18. package/dist/compile.d.ts.map +1 -1
  19. package/dist/{extract-cache-idNfsd7n.cjs → extract-cache-BioSaoFo.cjs} +5 -5
  20. package/dist/extract-cache-BioSaoFo.cjs.map +1 -0
  21. package/dist/{extract-cache-DYoHe5P-.js → extract-cache-C-MI1_ll.js} +57 -44
  22. package/dist/extract-cache-C-MI1_ll.js.map +1 -0
  23. package/dist/extract-cache.d.ts.map +1 -1
  24. package/dist/extract-runner.d.ts.map +1 -1
  25. package/dist/glossary.d.ts.map +1 -1
  26. package/dist/index.cjs +1 -1
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +10 -4
  29. package/dist/index.js.map +1 -1
  30. package/dist/lint.d.ts.map +1 -1
  31. package/dist/migrate.d.ts.map +1 -1
  32. package/dist/parallel-compile.d.ts.map +1 -1
  33. package/dist/po-format.d.ts.map +1 -1
  34. package/dist/translate-parse.d.ts +10 -0
  35. package/dist/translate-parse.d.ts.map +1 -0
  36. package/dist/translate-prompt.d.ts +9 -0
  37. package/dist/translate-prompt.d.ts.map +1 -0
  38. package/dist/translate.d.ts +11 -9
  39. package/dist/translate.d.ts.map +1 -1
  40. package/dist/{tsx-extractor-C-9_TnCW.cjs → tsx-extractor-B0vFXziu.cjs} +2 -2
  41. package/dist/tsx-extractor-B0vFXziu.cjs.map +1 -0
  42. package/dist/{tsx-extractor-CjQIMvKo.js → tsx-extractor-C-HZNobu.js} +2 -2
  43. package/dist/tsx-extractor-C-HZNobu.js.map +1 -0
  44. package/dist/tsx-extractor.d.ts.map +1 -1
  45. package/dist/validation.d.ts +4 -1
  46. package/dist/validation.d.ts.map +1 -1
  47. package/dist/vue-extractor.cjs +2 -2
  48. package/dist/vue-extractor.cjs.map +1 -1
  49. package/dist/vue-extractor.d.ts.map +1 -1
  50. package/dist/vue-extractor.js +3 -3
  51. package/dist/vue-extractor.js.map +1 -1
  52. package/package.json +2 -2
  53. package/dist/compile-B4y2UPsX.js.map +0 -1
  54. package/dist/compile-Brygn0bn.cjs +0 -8
  55. package/dist/compile-Brygn0bn.cjs.map +0 -1
  56. package/dist/extract-cache-DYoHe5P-.js.map +0 -1
  57. package/dist/extract-cache-idNfsd7n.cjs.map +0 -1
  58. package/dist/tsx-extractor-C-9_TnCW.cjs.map +0 -1
  59. package/dist/tsx-extractor-CjQIMvKo.js.map +0 -1
@@ -1,2 +1,2 @@
1
- require(`./extract-cache-idNfsd7n.cjs`);let e=require(`@fluenti/core/transform`);var t=new Set([`@fluenti/core`,`@fluenti/react`,`@fluenti/vue`,`@fluenti/solid`,`@fluenti/next`]);function n(e){let t=e.trim();if(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(t))return t;if(/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(t)&&!t.endsWith(`.`)){let e=t.split(`.`);return e[e.length-1]}let n=t.match(/^([a-zA-Z_$][a-zA-Z0-9_$.]*)\s*\(/);return n?n[1].replace(/\./g,`_`):``}function r(e,t){let r=``,i=0;for(let a=0;a<e.length;a++){if(r+=e[a],a>=t.length)continue;let o=n(t[a]);if(o===``){r+=`{arg${i}}`,i++;continue}r+=`{${o}}`}return r}function i(e,t,n){if(!e.message)return;let r=n.loc?.start.line??1,i=(n.loc?.start.column??0)+1;return{id:e.id,message:e.message,...e.context===void 0?{}:{context:e.context},...e.comment===void 0?{}:{comment:e.comment},origin:{file:t,line:r,column:i}}}function a(t,n){if(!t.message)return;let r=n??e.createMessageId;return{id:t.id??r(t.message,t.context),message:t.message,...t.context===void 0?{}:{context:t.context},...t.comment===void 0?{}:{comment:t.comment}}}function o(e,t){if(e.type===`StringLiteral`)return a({message:e.value},t);if(e.type===`TemplateLiteral`){let n=e;return n.expressions.length===0?a({message:n.quasis.map(e=>e.value.cooked??e.value.raw).join(``)},t):void 0}if(e.type!==`ObjectExpression`)return;let n={};for(let t of e.properties){if(t.type!==`ObjectProperty`)continue;let e=t;if(e.computed||!T(e.key))continue;let r=e.key.name;if(![`id`,`message`,`context`,`comment`].includes(r))continue;let i=p(e.value);i!==void 0&&(n[r]=i)}if(n.message)return a(n,t)}var s=new Set([`id`,`value`,`context`,`comment`,`options`,`other`,`tag`]);function c(e,t,n){let r=[];for(let[e,n]of Object.entries(t))r.push(`${e} {${n}}`);return r.push(`other {${n}}`),`{${e}, select, ${r.join(` `)}}`}function l(e,t){let r,i,a,o,c,l={};for(let u of e.attributes){if(u.type!==`JSXAttribute`)continue;let e=u;if(e.name.type!==`JSXIdentifier`)continue;let d=String(e.name.name);if(d===`id`){i=e.value?p(e.value):void 0;continue}if(d===`value`){let i=e.value?p(e.value)??m(e.value,t):void 0;r=i?n(i)||i:void 0;continue}if(d===`context`){a=e.value?p(e.value):void 0;continue}if(d===`comment`){o=e.value?p(e.value):void 0;continue}if(d===`other`){c=e.value?p(e.value):void 0;continue}if(s.has(d))continue;let f=e.value?p(e.value):void 0;f!==void 0&&(l[d]=f)}return{varName:r,id:i,context:a,comment:o,cases:l,other:c}}function u(e){let t=[`zero`,`one`,`two`,`few`,`many`,`other`],n=e.value??e.count??`count`,r=[],i=e.offset;for(let n of t){let t=e[n];if(t===void 0)continue;let i=n===`zero`?`=0`:n;r.push(`${i} {${t}}`)}return r.length===0?``:`{${n}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function d(e){let t=0;function n(e){let r=``;for(let i of e){if(i.type===`JSXText`){r+=f(i.value);continue}if(i.type===`JSXElement`){let e=t++,a=n(i.children);if(a===void 0)return;r+=`<${e}>${a}</${e}>`;continue}if(i.type===`JSXFragment`){let e=n(i.children);if(e===void 0)return;r+=e;continue}if(i.type===`JSXExpressionContainer`){let e=i.expression;if(e.type===`StringLiteral`){r+=e.value;continue}if(e.type===`NumericLiteral`){r+=String(e.value);continue}return}}return r}let r=n(e);if(r!==void 0)return r.replace(/\s+/g,` `).trim()||void 0}function f(e){return e.replace(/\s+/g,` `)}function p(e){if(e.type===`StringLiteral`)return e.value;if(e.type===`NumericLiteral`)return String(e.value);if(e.type===`JSXExpressionContainer`)return p(e.expression);if(e.type===`TemplateLiteral`){let t=e;if(t.expressions.length===0)return t.quasis.map(e=>e.value.cooked??e.value.raw).join(``)}}function m(e,t){if(!(e.start==null||e.end==null))return e.type===`JSXExpressionContainer`?m(e.expression,t):t.slice(e.start,e.end).trim()}function h(e,t){for(let n of e.attributes){if(n.type!==`JSXAttribute`)continue;let e=n;if(e.name.type===`JSXIdentifier`&&e.name.name===t)return e}}function g(e,t){let n={};for(let r of[`id`,`value`,`count`,`offset`,`zero`,`one`,`two`,`few`,`many`,`other`]){let i=h(e,r);if(!i?.value)continue;let a=p(i.value);if(a!==void 0){n[r]=a;continue}let o=m(i.value,t);o!==void 0&&(r===`value`||r===`count`||r===`offset`)&&(n[r]=o)}return n}function _(t,n,i){let a=r(n.quasi.quasis.map(e=>e.value.cooked??e.value.raw),n.quasi.expressions.map(e=>e.start==null||e.end==null?``:t.slice(e.start,e.end)));return{id:(i??e.createMessageId)(a),message:a}}function v(e){let n=new Map,r=Array.isArray(e.body)?e.body:[];for(let e of r)if(b(e)&&t.has(e.source.value))for(let t of e.specifiers){if(!x(t))continue;let e=S(t);(e===`t`||e===`msg`)&&n.set(t.local.name,e)}return n}function y(t,n,r){let a=(0,e.parseSourceModule)(t);if(!a)return[];let s=[],f=v(a);return(0,e.walkSourceAst)(a,a=>{if(a.type===`TaggedTemplateExpression`){let e=a;if(T(e.tag)&&(e.tag.name===`t`||f.has(e.tag.name))){let a=_(t,e,r);f.get(e.tag.name)===`msg`&&(a.comment=`msg tagged template`);let o=i(a,n,e);o&&s.push(o)}return}if(a.type===`CallExpression`){let e=a,t=f.get(e.callee&&T(e.callee)?e.callee.name:``);if(T(e.callee)&&(e.callee.name===`t`||f.has(e.callee.name)&&t===`t`)){if(f.has(e.callee.name)&&e.arguments[0]?.type!==`ObjectExpression`)return;let t=e.arguments[0]?o(e.arguments[0],r):void 0,a=t?i(t,n,e):void 0;a&&s.push(a)}else if(e.callee.type===`MemberExpression`&&T(e.callee.object)&&f.get(e.callee.object.name)===`msg`&&T(e.callee.property)&&e.callee.property.name===`descriptor`&&e.arguments[0]){let t=o(e.arguments[0],r),a=t?i(t,n,e):void 0;a&&s.push(a)}return}if(a.type!==`JSXElement`)return;let m=a,v=m.openingElement,y=C(v.name);if(y===`Trans`){let e=h(v,`message`),t=h(v,`id`),a=h(v,`context`),o=h(v,`comment`),c=e?.value?w({id:t?.value?p(t.value):void 0,message:p(e.value),context:a?.value?p(a.value):void 0,comment:o?.value?p(o.value):void 0},r):w({id:t?.value?p(t.value):void 0,message:d(m.children),context:a?.value?p(a.value):void 0,comment:o?.value?p(o.value):void 0},r),l=c?i(c,n,m):void 0;l&&s.push(l);return}if(y===`Plural`){let a=g(v,t),o=u(a);if(!o)return;let c=r??e.createMessageId,l=i({id:a.id??c(o),message:o},n,m);l&&s.push(l);return}if(y===`Select`){let{varName:a,id:o,context:u,comment:d,cases:f,other:p}=l(v,t);if(!a||!p||Object.keys(f).length===0)return;let h=c(a,f,p),g=i({id:o??(r??e.createMessageId)(h,u),message:h,...u===void 0?{}:{context:u},...d===void 0?{}:{comment:d}},n,m);g&&s.push(g)}}),s}function b(t){return(0,e.isSourceNode)(t)&&t.type===`ImportDeclaration`}function x(t){return(0,e.isSourceNode)(t)&&t.type===`ImportSpecifier`}function S(e){if(e.imported.type===`Identifier`)return e.imported.name;if(e.imported.type===`StringLiteral`)return e.imported.value}function C(e){if(e.type===`JSXIdentifier`)return String(e.name)}function w(e,t){let n={};return e.id!==void 0&&(n.id=e.id),e.message!==void 0&&(n.message=e.message),e.context!==void 0&&(n.context=e.context),e.comment!==void 0&&(n.comment=e.comment),a(n,t)}function T(t){return(0,e.isSourceNode)(t)&&t.type===`Identifier`}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return y}});
2
- //# sourceMappingURL=tsx-extractor-C-9_TnCW.cjs.map
1
+ require(`./extract-cache-BioSaoFo.cjs`);let e=require(`@fluenti/core/transform`);var t=new Set([`@fluenti/core`,`@fluenti/react`,`@fluenti/vue`,`@fluenti/solid`,`@fluenti/next`]);function n(e){let t=e.trim();if(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(t))return t;if(/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(t)&&!t.endsWith(`.`)){let e=t.split(`.`);return e[e.length-1]}let n=t.match(/^([a-zA-Z_$][a-zA-Z0-9_$.]*)\s*\(/);return n?n[1].replace(/\./g,`_`):``}function r(e,t){let r=``,i=0;for(let a=0;a<e.length;a++){if(r+=e[a],a>=t.length)continue;let o=n(t[a]);if(o===``){r+=`{arg${i}}`,i++;continue}r+=`{${o}}`}return r}function i(e,t,n){if(!e.message)return;let r=n.loc?.start.line??1,i=(n.loc?.start.column??0)+1;return{id:e.id,message:e.message,...e.context===void 0?{}:{context:e.context},...e.comment===void 0?{}:{comment:e.comment},origin:{file:t,line:r,column:i}}}function a(t,n){if(!t.message)return;let r=n??e.createMessageId;return{id:t.id??r(t.message,t.context),message:t.message,...t.context===void 0?{}:{context:t.context},...t.comment===void 0?{}:{comment:t.comment}}}function o(e,t){if(e.type===`StringLiteral`)return a({message:e.value},t);if(e.type===`TemplateLiteral`){let n=e;return n.expressions.length===0?a({message:n.quasis.map(e=>e.value.cooked??e.value.raw).join(``)},t):void 0}if(e.type!==`ObjectExpression`)return;let n={};for(let t of e.properties){if(t.type!==`ObjectProperty`)continue;let e=t;if(e.computed||!T(e.key))continue;let r=e.key.name;if(![`id`,`message`,`context`,`comment`].includes(r))continue;let i=p(e.value);i!==void 0&&(n[r]=i)}if(n.message)return a(n,t)}var s=new Set([`id`,`value`,`context`,`comment`,`options`,`other`,`tag`]);function c(e,t,n){let r=[];for(let[e,n]of Object.entries(t))r.push(`${e} {${n}}`);return r.push(`other {${n}}`),`{${e}, select, ${r.join(` `)}}`}function l(e,t){let r,i,a,o,c,l={};for(let u of e.attributes){if(u.type!==`JSXAttribute`)continue;let e=u;if(e.name.type!==`JSXIdentifier`)continue;let d=String(e.name.name);if(d===`id`){i=e.value?p(e.value):void 0;continue}if(d===`value`){let i=e.value?p(e.value)??m(e.value,t):void 0;r=i?n(i)||i:void 0;continue}if(d===`context`){a=e.value?p(e.value):void 0;continue}if(d===`comment`){o=e.value?p(e.value):void 0;continue}if(d===`other`){c=e.value?p(e.value):void 0;continue}if(s.has(d))continue;let f=e.value?p(e.value):void 0;f!==void 0&&(l[d]=f)}return{varName:r,id:i,context:a,comment:o,cases:l,other:c}}function u(e){let t=[`zero`,`one`,`two`,`few`,`many`,`other`],n=e.value??e.count??`count`,r=[],i=e.offset;for(let n of t){let t=e[n];if(t===void 0)continue;let i=n===`zero`?`=0`:n;r.push(`${i} {${t}}`)}return r.length===0?``:`{${n}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function d(e){let t=0;function n(e){let r=``;for(let i of e){if(i.type===`JSXText`){r+=f(i.value);continue}if(i.type===`JSXElement`){let e=t++,a=n(i.children);if(a===void 0)return;r+=`<${e}>${a}</${e}>`;continue}if(i.type===`JSXFragment`){let e=n(i.children);if(e===void 0)return;r+=e;continue}if(i.type===`JSXExpressionContainer`){let e=i.expression;if(e.type===`StringLiteral`){r+=e.value;continue}if(e.type===`NumericLiteral`){r+=String(e.value);continue}return}}return r}let r=n(e);if(r!==void 0)return r.replace(/\s+/g,` `).trim()||void 0}function f(e){return e.replace(/\s+/g,` `)}function p(e){if(e.type===`StringLiteral`)return e.value;if(e.type===`NumericLiteral`)return String(e.value);if(e.type===`JSXExpressionContainer`)return p(e.expression);if(e.type===`TemplateLiteral`){let t=e;if(t.expressions.length===0)return t.quasis.map(e=>e.value.cooked??e.value.raw).join(``)}}function m(e,t){if(!(e.start==null||e.end==null))return e.type===`JSXExpressionContainer`?m(e.expression,t):t.slice(e.start,e.end).trim()}function h(e,t){for(let n of e.attributes){if(n.type!==`JSXAttribute`)continue;let e=n;if(e.name.type===`JSXIdentifier`&&e.name.name===t)return e}}function g(e,t){let n={};for(let r of[`id`,`value`,`count`,`offset`,`zero`,`one`,`two`,`few`,`many`,`other`]){let i=h(e,r);if(!i?.value)continue;let a=p(i.value);if(a!==void 0){n[r]=a;continue}let o=m(i.value,t);o!==void 0&&(r===`value`||r===`count`||r===`offset`)&&(n[r]=o)}return n}function _(t,n,i){let a=r(n.quasi.quasis.map(e=>e.value.cooked??e.value.raw),n.quasi.expressions.map(e=>e.start==null||e.end==null?``:t.slice(e.start,e.end)));return{id:(i??e.createMessageId)(a),message:a}}function v(e){let n=new Map,r=Array.isArray(e.body)?e.body:[];for(let e of r)if(b(e)&&t.has(e.source.value))for(let t of e.specifiers){if(!x(t))continue;let e=S(t);(e===`t`||e===`msg`)&&n.set(t.local.name,e)}return n}function y(t,n,r){let a=(0,e.parseSourceModule)(t);if(!a)return console.warn(`[fluenti] Failed to parse ${n} — skipping message extraction`),[];let s=[],f=v(a);return(0,e.walkSourceAst)(a,a=>{if(a.type===`TaggedTemplateExpression`){let e=a;if(T(e.tag)&&(e.tag.name===`t`||f.has(e.tag.name))){let a=_(t,e,r);f.get(e.tag.name)===`msg`&&(a.comment=`msg tagged template`);let o=i(a,n,e);o&&s.push(o)}return}if(a.type===`CallExpression`){let e=a,t=f.get(e.callee&&T(e.callee)?e.callee.name:``);if(T(e.callee)&&(e.callee.name===`t`||f.has(e.callee.name)&&t===`t`)){if(f.has(e.callee.name)&&e.arguments[0]?.type!==`ObjectExpression`)return;let t=e.arguments[0]?o(e.arguments[0],r):void 0,a=t?i(t,n,e):void 0;a&&s.push(a)}else if(e.callee.type===`MemberExpression`&&T(e.callee.object)&&f.get(e.callee.object.name)===`msg`&&T(e.callee.property)&&e.callee.property.name===`descriptor`&&e.arguments[0]){let t=o(e.arguments[0],r),a=t?i(t,n,e):void 0;a&&s.push(a)}return}if(a.type!==`JSXElement`)return;let m=a,v=m.openingElement,y=C(v.name);if(y===`Trans`){let e=h(v,`message`),t=h(v,`id`),a=h(v,`context`),o=h(v,`comment`),c=e?.value?w({id:t?.value?p(t.value):void 0,message:p(e.value),context:a?.value?p(a.value):void 0,comment:o?.value?p(o.value):void 0},r):w({id:t?.value?p(t.value):void 0,message:d(m.children),context:a?.value?p(a.value):void 0,comment:o?.value?p(o.value):void 0},r),l=c?i(c,n,m):void 0;l&&s.push(l);return}if(y===`Plural`){let a=g(v,t),o=u(a);if(!o)return;let c=r??e.createMessageId,l=i({id:a.id??c(o),message:o},n,m);l&&s.push(l);return}if(y===`Select`){let{varName:a,id:o,context:u,comment:d,cases:f,other:p}=l(v,t);if(!a||!p||Object.keys(f).length===0)return;let h=c(a,f,p),g=i({id:o??(r??e.createMessageId)(h,u),message:h,...u===void 0?{}:{context:u},...d===void 0?{}:{comment:d}},n,m);g&&s.push(g)}}),s}function b(t){return(0,e.isSourceNode)(t)&&t.type===`ImportDeclaration`}function x(t){return(0,e.isSourceNode)(t)&&t.type===`ImportSpecifier`}function S(e){if(e.imported.type===`Identifier`)return e.imported.name;if(e.imported.type===`StringLiteral`)return e.imported.value}function C(e){if(e.type===`JSXIdentifier`)return String(e.name)}function w(e,t){let n={};return e.id!==void 0&&(n.id=e.id),e.message!==void 0&&(n.message=e.message),e.context!==void 0&&(n.context=e.context),e.comment!==void 0&&(n.comment=e.comment),a(n,t)}function T(t){return(0,e.isSourceNode)(t)&&t.type===`Identifier`}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return y}});
2
+ //# sourceMappingURL=tsx-extractor-B0vFXziu.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsx-extractor-B0vFXziu.cjs","names":[],"sources":["../src/tsx-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport {\n createMessageId,\n isSourceNode,\n parseSourceModule,\n walkSourceAst,\n type SourceNode,\n} from '@fluenti/core/transform'\n\ninterface IdentifierNode extends SourceNode {\n type: 'Identifier'\n name: string\n}\n\ninterface StringLiteralNode extends SourceNode {\n type: 'StringLiteral'\n value: string\n}\n\ninterface NumericLiteralNode extends SourceNode {\n type: 'NumericLiteral'\n value: number\n}\n\ninterface TemplateElementNode extends SourceNode {\n type: 'TemplateElement'\n value: { raw: string; cooked: string | null }\n}\n\ninterface TemplateLiteralNode extends SourceNode {\n type: 'TemplateLiteral'\n quasis: TemplateElementNode[]\n expressions: SourceNode[]\n}\n\ninterface TaggedTemplateExpressionNode extends SourceNode {\n type: 'TaggedTemplateExpression'\n tag: SourceNode\n quasi: TemplateLiteralNode\n}\n\ninterface CallExpressionNode extends SourceNode {\n type: 'CallExpression'\n callee: SourceNode\n arguments: SourceNode[]\n}\n\ninterface ImportDeclarationNode extends SourceNode {\n type: 'ImportDeclaration'\n source: StringLiteralNode\n specifiers: SourceNode[]\n}\n\ninterface ImportSpecifierNode extends SourceNode {\n type: 'ImportSpecifier'\n imported: IdentifierNode | StringLiteralNode\n local: IdentifierNode\n}\n\ninterface ObjectExpressionNode extends SourceNode {\n type: 'ObjectExpression'\n properties: SourceNode[]\n}\n\ninterface ObjectPropertyNode extends SourceNode {\n type: 'ObjectProperty'\n key: SourceNode\n value: SourceNode\n computed?: boolean\n}\n\ninterface JSXElementNode extends SourceNode {\n type: 'JSXElement'\n openingElement: JSXOpeningElementNode\n children: SourceNode[]\n}\n\ninterface JSXFragmentNode extends SourceNode {\n type: 'JSXFragment'\n children: SourceNode[]\n}\n\ninterface JSXOpeningElementNode extends SourceNode {\n type: 'JSXOpeningElement'\n name: SourceNode\n attributes: SourceNode[]\n}\n\ninterface JSXAttributeNode extends SourceNode {\n type: 'JSXAttribute'\n name: SourceNode\n value?: SourceNode | null\n}\n\ninterface JSXExpressionContainerNode extends SourceNode {\n type: 'JSXExpressionContainer'\n expression: SourceNode\n}\n\ninterface JSXTextNode extends SourceNode {\n type: 'JSXText'\n value: string\n}\n\ninterface ExtractedDescriptor {\n id: string\n message?: string\n context?: string\n comment?: string\n}\n\nconst DIRECT_T_SOURCES = new Set([\n '@fluenti/core',\n '@fluenti/react',\n '@fluenti/vue',\n '@fluenti/solid',\n '@fluenti/next',\n])\n\nfunction classifyExpression(expr: string): string {\n const trimmed = expr.trim()\n // Simple identifier: name, count\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(trimmed)) {\n return trimmed\n }\n // Dotted path: user.name → name\n if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(trimmed) && !trimmed.endsWith('.')) {\n const parts = trimmed.split('.')\n return parts[parts.length - 1]!\n }\n // Function call: fun() → fun, obj.method() → obj_method\n const callMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$.]*)\\s*\\(/)\n if (callMatch) {\n return callMatch[1]!.replace(/\\./g, '_')\n }\n return ''\n}\n\nfunction buildICUFromTemplate(\n strings: readonly string[],\n expressions: readonly string[],\n): string {\n let result = ''\n let positionalIndex = 0\n\n for (let index = 0; index < strings.length; index++) {\n result += strings[index]!\n if (index >= expressions.length) continue\n\n const name = classifyExpression(expressions[index]!)\n if (name === '') {\n result += `{arg${positionalIndex}}`\n positionalIndex++\n continue\n }\n\n result += `{${name}}`\n }\n\n return result\n}\n\nfunction createExtractedMessage(\n descriptor: ExtractedDescriptor,\n filename: string,\n node: SourceNode,\n): ExtractedMessage | undefined {\n if (!descriptor.message) {\n return undefined\n }\n\n const line = node.loc?.start.line ?? 1\n const column = (node.loc?.start.column ?? 0) + 1\n\n return {\n id: descriptor.id,\n message: descriptor.message,\n ...(descriptor.context !== undefined ? { context: descriptor.context } : {}),\n ...(descriptor.comment !== undefined ? { comment: descriptor.comment } : {}),\n origin: { file: filename, line, column },\n }\n}\n\nfunction descriptorFromStaticParts(\n parts: {\n id?: string\n message?: string\n context?: string\n comment?: string\n },\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n if (!parts.message) {\n return undefined\n }\n\n const generateId = idGenerator ?? createMessageId\n\n return {\n id: parts.id ?? generateId(parts.message, parts.context),\n message: parts.message,\n ...(parts.context !== undefined ? { context: parts.context } : {}),\n ...(parts.comment !== undefined ? { comment: parts.comment } : {}),\n }\n}\n\nfunction extractDescriptorFromCallArgument(\n argument: SourceNode,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n if (argument.type === 'StringLiteral') {\n return descriptorFromStaticParts({ message: (argument as StringLiteralNode).value }, idGenerator)\n }\n\n if (argument.type === 'TemplateLiteral') {\n const template = argument as TemplateLiteralNode\n if (template.expressions.length === 0) {\n const message = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join('')\n return descriptorFromStaticParts({ message }, idGenerator)\n }\n return undefined\n }\n\n if (argument.type !== 'ObjectExpression') {\n return undefined\n }\n\n const staticParts: { id?: string; message?: string; context?: string; comment?: string } = {}\n for (const property of (argument as ObjectExpressionNode).properties) {\n if (property.type !== 'ObjectProperty') continue\n\n const objectProperty = property as ObjectPropertyNode\n if (objectProperty.computed || !isIdentifier(objectProperty.key)) continue\n\n const key = objectProperty.key.name\n if (!['id', 'message', 'context', 'comment'].includes(key)) continue\n\n const value = readStaticStringValue(objectProperty.value)\n if (value === undefined) continue\n staticParts[key as keyof typeof staticParts] = value\n }\n\n if (!staticParts.message) {\n return undefined\n }\n\n return descriptorFromStaticParts(staticParts, idGenerator)\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICU(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction extractSelectProps(\n openingElement: JSXOpeningElementNode,\n code: string,\n): { varName: string | undefined; id: string | undefined; context: string | undefined; comment: string | undefined; cases: Record<string, string>; other: string | undefined } {\n let varName: string | undefined\n let id: string | undefined\n let context: string | undefined\n let comment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const attribute of openingElement.attributes) {\n if (attribute.type !== 'JSXAttribute') continue\n const attr = attribute as JSXAttributeNode\n if (attr.name.type !== 'JSXIdentifier') continue\n const name = String(attr.name['name'])\n\n if (name === 'id') {\n id = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'value') {\n const raw = attr.value ? (readStaticStringValue(attr.value) ?? readExpressionSource(attr.value, code)) : undefined\n varName = raw ? classifyExpression(raw) || raw : undefined\n continue\n }\n if (name === 'context') {\n context = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'comment') {\n comment = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'other') {\n other = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n\n const staticValue = attr.value ? readStaticStringValue(attr.value) : undefined\n if (staticValue === undefined) continue\n\n cases[name] = staticValue\n }\n\n return { varName, id, context, comment, cases, other }\n}\n\nfunction buildPluralICU(props: Record<string, string>): string {\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other'] as const\n const countVar = props['value'] ?? props['count'] ?? 'count'\n const options: string[] = []\n const offset = props['offset']\n\n for (const category of categories) {\n const value = props[category]\n if (value === undefined) continue\n const key = category === 'zero' ? '=0' : category\n options.push(`${key} {${value}}`)\n }\n\n if (options.length === 0) {\n return ''\n }\n\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction extractRichTextMessage(children: readonly SourceNode[]): string | undefined {\n let nextIndex = 0\n\n function render(nodes: readonly SourceNode[]): string | undefined {\n let message = ''\n\n for (const node of nodes) {\n if (node.type === 'JSXText') {\n message += normalizeJsxText((node as JSXTextNode).value)\n continue\n }\n\n if (node.type === 'JSXElement') {\n const idx = nextIndex++\n const inner = render((node as JSXElementNode).children)\n if (inner === undefined) return undefined\n message += `<${idx}>${inner}</${idx}>`\n continue\n }\n\n if (node.type === 'JSXFragment') {\n const inner = render((node as JSXFragmentNode).children)\n if (inner === undefined) return undefined\n message += inner\n continue\n }\n\n if (node.type === 'JSXExpressionContainer') {\n const expression = (node as JSXExpressionContainerNode).expression\n if (expression.type === 'StringLiteral') {\n message += (expression as StringLiteralNode).value\n continue\n }\n if (expression.type === 'NumericLiteral') {\n message += String((expression as NumericLiteralNode).value)\n continue\n }\n return undefined\n }\n }\n\n return message\n }\n\n const message = render(children)\n if (message === undefined) return undefined\n\n const normalized = message.replace(/\\s+/g, ' ').trim()\n return normalized || undefined\n}\n\nfunction normalizeJsxText(value: string): string {\n return value.replace(/\\s+/g, ' ')\n}\n\nfunction readStaticStringValue(node: SourceNode): string | undefined {\n if (node.type === 'StringLiteral') {\n return (node as StringLiteralNode).value\n }\n\n if (node.type === 'NumericLiteral') {\n return String((node as NumericLiteralNode).value)\n }\n\n if (node.type === 'JSXExpressionContainer') {\n return readStaticStringValue((node as JSXExpressionContainerNode).expression)\n }\n\n if (node.type === 'TemplateLiteral') {\n const template = node as TemplateLiteralNode\n if (template.expressions.length === 0) {\n return template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join('')\n }\n }\n\n return undefined\n}\n\nfunction readExpressionSource(node: SourceNode, code: string): string | undefined {\n if (node.start == null || node.end == null) {\n return undefined\n }\n\n if (node.type === 'JSXExpressionContainer') {\n return readExpressionSource((node as JSXExpressionContainerNode).expression, code)\n }\n\n return code.slice(node.start, node.end).trim()\n}\n\nfunction getJsxAttribute(\n openingElement: JSXOpeningElementNode,\n name: string,\n): JSXAttributeNode | undefined {\n for (const attribute of openingElement.attributes) {\n if (attribute.type !== 'JSXAttribute') continue\n\n const jsxAttribute = attribute as JSXAttributeNode\n if (jsxAttribute.name.type === 'JSXIdentifier' && jsxAttribute.name['name'] === name) {\n return jsxAttribute\n }\n }\n\n return undefined\n}\n\nfunction extractPluralProps(\n openingElement: JSXOpeningElementNode,\n code: string,\n): Record<string, string> {\n const props: Record<string, string> = {}\n const propNames = ['id', 'value', 'count', 'offset', 'zero', 'one', 'two', 'few', 'many', 'other']\n\n for (const name of propNames) {\n const attribute = getJsxAttribute(openingElement, name)\n if (!attribute?.value) continue\n\n const staticValue = readStaticStringValue(attribute.value)\n if (staticValue !== undefined) {\n props[name] = staticValue\n continue\n }\n\n const exprValue = readExpressionSource(attribute.value, code)\n if (exprValue !== undefined && (name === 'value' || name === 'count' || name === 'offset')) {\n props[name] = exprValue\n }\n }\n\n return props\n}\n\nfunction extractTaggedTemplateMessage(\n code: string,\n node: TaggedTemplateExpressionNode,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor {\n const strings = node.quasi.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw)\n const expressions = node.quasi.expressions.map((expression) => {\n if (expression.start == null || expression.end == null) {\n return ''\n }\n return code.slice(expression.start, expression.end)\n })\n const message = buildICUFromTemplate(strings, expressions)\n const generateId = idGenerator ?? createMessageId\n\n return {\n id: generateId(message),\n message,\n }\n}\n\nfunction collectDirectBindings(ast: SourceNode): Map<string, 't' | 'msg'> {\n const bindings = new Map<string, 't' | 'msg'>()\n const body = Array.isArray(ast['body']) ? ast['body'] : []\n\n for (const entry of body) {\n if (!isImportDeclaration(entry)) continue\n if (!DIRECT_T_SOURCES.has(entry.source.value)) continue\n\n for (const specifier of entry.specifiers) {\n if (!isImportSpecifier(specifier)) continue\n const importedName = readImportedName(specifier)\n if (importedName === 't' || importedName === 'msg') {\n bindings.set(specifier.local.name, importedName as 't' | 'msg')\n }\n }\n }\n\n return bindings\n}\n\nexport function extractFromTsx(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const ast = parseSourceModule(code)\n if (!ast) {\n console.warn(`[fluenti] Failed to parse ${filename} — skipping message extraction`)\n return []\n }\n\n const messages: ExtractedMessage[] = []\n const directBindings = collectDirectBindings(ast)\n\n walkSourceAst(ast, (node: SourceNode) => {\n if (node.type === 'TaggedTemplateExpression') {\n const tagged = node as TaggedTemplateExpressionNode\n if (\n isIdentifier(tagged.tag)\n && (tagged.tag.name === 't' || directBindings.has(tagged.tag.name))\n ) {\n const descriptor = extractTaggedTemplateMessage(code, tagged, idGenerator)\n const bindingType = directBindings.get(tagged.tag.name)\n if (bindingType === 'msg') {\n descriptor.comment = 'msg tagged template'\n }\n const extracted = createExtractedMessage(\n descriptor,\n filename,\n tagged,\n )\n if (extracted) {\n messages.push(extracted)\n }\n }\n return\n }\n\n if (node.type === 'CallExpression') {\n const call = node as CallExpressionNode\n const callBindingType = directBindings.get(call.callee && isIdentifier(call.callee) ? call.callee.name : '')\n if (isIdentifier(call.callee) && (call.callee.name === 't' || (directBindings.has(call.callee.name) && callBindingType === 't'))) {\n if (directBindings.has(call.callee.name) && call.arguments[0]?.type !== 'ObjectExpression') {\n return\n }\n const descriptor = call.arguments[0] ? extractDescriptorFromCallArgument(call.arguments[0], idGenerator) : undefined\n const extracted = descriptor\n ? createExtractedMessage(descriptor, filename, call)\n : undefined\n if (extracted) {\n messages.push(extracted)\n }\n } else if (\n call.callee.type === 'MemberExpression'\n && isIdentifier(call.callee['object'])\n && directBindings.get((call.callee['object'] as IdentifierNode).name) === 'msg'\n && isIdentifier(call.callee['property'])\n && (call.callee['property'] as IdentifierNode).name === 'descriptor'\n && call.arguments[0]\n ) {\n const descriptor = extractDescriptorFromCallArgument(call.arguments[0], idGenerator)\n const extracted = descriptor ? createExtractedMessage(descriptor, filename, call) : undefined\n if (extracted) {\n messages.push(extracted)\n }\n }\n return\n }\n\n if (node.type !== 'JSXElement') {\n return\n }\n\n const element = node as JSXElementNode\n const openingElement = element.openingElement\n const elementName = readJsxElementName(openingElement.name)\n\n if (elementName === 'Trans') {\n const messageAttr = getJsxAttribute(openingElement, 'message')\n const idAttr = getJsxAttribute(openingElement, 'id')\n const contextAttr = getJsxAttribute(openingElement, 'context')\n const commentAttr = getJsxAttribute(openingElement, 'comment')\n\n const descriptor = messageAttr?.value\n ? buildStaticTransDescriptor({\n id: idAttr?.value ? readStaticStringValue(idAttr.value) : undefined,\n message: readStaticStringValue(messageAttr.value),\n context: contextAttr?.value ? readStaticStringValue(contextAttr.value) : undefined,\n comment: commentAttr?.value ? readStaticStringValue(commentAttr.value) : undefined,\n }, idGenerator)\n : buildStaticTransDescriptor({\n id: idAttr?.value ? readStaticStringValue(idAttr.value) : undefined,\n message: extractRichTextMessage(element.children),\n context: contextAttr?.value ? readStaticStringValue(contextAttr.value) : undefined,\n comment: commentAttr?.value ? readStaticStringValue(commentAttr.value) : undefined,\n }, idGenerator)\n\n const extracted = descriptor\n ? createExtractedMessage(descriptor, filename, element)\n : undefined\n if (extracted) {\n messages.push(extracted)\n }\n return\n }\n\n if (elementName === 'Plural') {\n const props = extractPluralProps(openingElement, code)\n const message = buildPluralICU(props)\n if (!message) {\n return\n }\n\n const generateId = idGenerator ?? createMessageId\n const extracted = createExtractedMessage(\n {\n id: props['id'] ?? generateId(message),\n message,\n },\n filename,\n element,\n )\n if (extracted) {\n messages.push(extracted)\n }\n return\n }\n\n if (elementName === 'Select') {\n const { varName, id, context, comment, cases, other } = extractSelectProps(openingElement, code)\n if (!varName || !other || Object.keys(cases).length === 0) {\n return\n }\n\n const message = buildSelectICU(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const extracted = createExtractedMessage(\n {\n id: id ?? generateId(message, context),\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n filename,\n element,\n )\n if (extracted) {\n messages.push(extracted)\n }\n }\n })\n\n return messages\n}\n\nfunction isImportDeclaration(node: unknown): node is ImportDeclarationNode {\n return isSourceNode(node) && node.type === 'ImportDeclaration'\n}\n\nfunction isImportSpecifier(node: unknown): node is ImportSpecifierNode {\n return isSourceNode(node) && node.type === 'ImportSpecifier'\n}\n\nfunction readImportedName(specifier: ImportSpecifierNode): string | undefined {\n if (specifier.imported.type === 'Identifier') {\n return (specifier.imported as IdentifierNode).name\n }\n if (specifier.imported.type === 'StringLiteral') {\n return (specifier.imported as StringLiteralNode).value\n }\n return undefined\n}\n\nfunction readJsxElementName(node: SourceNode): string | undefined {\n if (node.type === 'JSXIdentifier') {\n return String(node['name'])\n }\n return undefined\n}\n\nfunction buildStaticTransDescriptor(\n parts: {\n id: string | undefined\n message: string | undefined\n context: string | undefined\n comment: string | undefined\n },\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n const payload: {\n id?: string\n message?: string\n context?: string\n comment?: string\n } = {}\n\n if (parts.id !== undefined) payload.id = parts.id\n if (parts.message !== undefined) payload.message = parts.message\n if (parts.context !== undefined) payload.context = parts.context\n if (parts.comment !== undefined) payload.comment = parts.comment\n\n return descriptorFromStaticParts(payload, idGenerator)\n}\n\nfunction isIdentifier(node: unknown): node is IdentifierNode {\n return isSourceNode(node) && node.type === 'Identifier'\n}\n"],"mappings":"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"}
@@ -256,7 +256,7 @@ function x(e) {
256
256
  }
257
257
  function S(t, i, a) {
258
258
  let o = n(t);
259
- if (!o) return [];
259
+ if (!o) return console.warn(`[fluenti] Failed to parse ${i} — skipping message extraction`), [];
260
260
  let c = [], u = x(o);
261
261
  return r(o, (n) => {
262
262
  if (n.type === "TaggedTemplateExpression") {
@@ -344,4 +344,4 @@ function O(e) {
344
344
  //#endregion
345
345
  export { S as t };
346
346
 
347
- //# sourceMappingURL=tsx-extractor-CjQIMvKo.js.map
347
+ //# sourceMappingURL=tsx-extractor-C-HZNobu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsx-extractor-C-HZNobu.js","names":[],"sources":["../src/tsx-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport {\n createMessageId,\n isSourceNode,\n parseSourceModule,\n walkSourceAst,\n type SourceNode,\n} from '@fluenti/core/transform'\n\ninterface IdentifierNode extends SourceNode {\n type: 'Identifier'\n name: string\n}\n\ninterface StringLiteralNode extends SourceNode {\n type: 'StringLiteral'\n value: string\n}\n\ninterface NumericLiteralNode extends SourceNode {\n type: 'NumericLiteral'\n value: number\n}\n\ninterface TemplateElementNode extends SourceNode {\n type: 'TemplateElement'\n value: { raw: string; cooked: string | null }\n}\n\ninterface TemplateLiteralNode extends SourceNode {\n type: 'TemplateLiteral'\n quasis: TemplateElementNode[]\n expressions: SourceNode[]\n}\n\ninterface TaggedTemplateExpressionNode extends SourceNode {\n type: 'TaggedTemplateExpression'\n tag: SourceNode\n quasi: TemplateLiteralNode\n}\n\ninterface CallExpressionNode extends SourceNode {\n type: 'CallExpression'\n callee: SourceNode\n arguments: SourceNode[]\n}\n\ninterface ImportDeclarationNode extends SourceNode {\n type: 'ImportDeclaration'\n source: StringLiteralNode\n specifiers: SourceNode[]\n}\n\ninterface ImportSpecifierNode extends SourceNode {\n type: 'ImportSpecifier'\n imported: IdentifierNode | StringLiteralNode\n local: IdentifierNode\n}\n\ninterface ObjectExpressionNode extends SourceNode {\n type: 'ObjectExpression'\n properties: SourceNode[]\n}\n\ninterface ObjectPropertyNode extends SourceNode {\n type: 'ObjectProperty'\n key: SourceNode\n value: SourceNode\n computed?: boolean\n}\n\ninterface JSXElementNode extends SourceNode {\n type: 'JSXElement'\n openingElement: JSXOpeningElementNode\n children: SourceNode[]\n}\n\ninterface JSXFragmentNode extends SourceNode {\n type: 'JSXFragment'\n children: SourceNode[]\n}\n\ninterface JSXOpeningElementNode extends SourceNode {\n type: 'JSXOpeningElement'\n name: SourceNode\n attributes: SourceNode[]\n}\n\ninterface JSXAttributeNode extends SourceNode {\n type: 'JSXAttribute'\n name: SourceNode\n value?: SourceNode | null\n}\n\ninterface JSXExpressionContainerNode extends SourceNode {\n type: 'JSXExpressionContainer'\n expression: SourceNode\n}\n\ninterface JSXTextNode extends SourceNode {\n type: 'JSXText'\n value: string\n}\n\ninterface ExtractedDescriptor {\n id: string\n message?: string\n context?: string\n comment?: string\n}\n\nconst DIRECT_T_SOURCES = new Set([\n '@fluenti/core',\n '@fluenti/react',\n '@fluenti/vue',\n '@fluenti/solid',\n '@fluenti/next',\n])\n\nfunction classifyExpression(expr: string): string {\n const trimmed = expr.trim()\n // Simple identifier: name, count\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(trimmed)) {\n return trimmed\n }\n // Dotted path: user.name → name\n if (/^[a-zA-Z_$][a-zA-Z0-9_$.]*$/.test(trimmed) && !trimmed.endsWith('.')) {\n const parts = trimmed.split('.')\n return parts[parts.length - 1]!\n }\n // Function call: fun() → fun, obj.method() → obj_method\n const callMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$.]*)\\s*\\(/)\n if (callMatch) {\n return callMatch[1]!.replace(/\\./g, '_')\n }\n return ''\n}\n\nfunction buildICUFromTemplate(\n strings: readonly string[],\n expressions: readonly string[],\n): string {\n let result = ''\n let positionalIndex = 0\n\n for (let index = 0; index < strings.length; index++) {\n result += strings[index]!\n if (index >= expressions.length) continue\n\n const name = classifyExpression(expressions[index]!)\n if (name === '') {\n result += `{arg${positionalIndex}}`\n positionalIndex++\n continue\n }\n\n result += `{${name}}`\n }\n\n return result\n}\n\nfunction createExtractedMessage(\n descriptor: ExtractedDescriptor,\n filename: string,\n node: SourceNode,\n): ExtractedMessage | undefined {\n if (!descriptor.message) {\n return undefined\n }\n\n const line = node.loc?.start.line ?? 1\n const column = (node.loc?.start.column ?? 0) + 1\n\n return {\n id: descriptor.id,\n message: descriptor.message,\n ...(descriptor.context !== undefined ? { context: descriptor.context } : {}),\n ...(descriptor.comment !== undefined ? { comment: descriptor.comment } : {}),\n origin: { file: filename, line, column },\n }\n}\n\nfunction descriptorFromStaticParts(\n parts: {\n id?: string\n message?: string\n context?: string\n comment?: string\n },\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n if (!parts.message) {\n return undefined\n }\n\n const generateId = idGenerator ?? createMessageId\n\n return {\n id: parts.id ?? generateId(parts.message, parts.context),\n message: parts.message,\n ...(parts.context !== undefined ? { context: parts.context } : {}),\n ...(parts.comment !== undefined ? { comment: parts.comment } : {}),\n }\n}\n\nfunction extractDescriptorFromCallArgument(\n argument: SourceNode,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n if (argument.type === 'StringLiteral') {\n return descriptorFromStaticParts({ message: (argument as StringLiteralNode).value }, idGenerator)\n }\n\n if (argument.type === 'TemplateLiteral') {\n const template = argument as TemplateLiteralNode\n if (template.expressions.length === 0) {\n const message = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join('')\n return descriptorFromStaticParts({ message }, idGenerator)\n }\n return undefined\n }\n\n if (argument.type !== 'ObjectExpression') {\n return undefined\n }\n\n const staticParts: { id?: string; message?: string; context?: string; comment?: string } = {}\n for (const property of (argument as ObjectExpressionNode).properties) {\n if (property.type !== 'ObjectProperty') continue\n\n const objectProperty = property as ObjectPropertyNode\n if (objectProperty.computed || !isIdentifier(objectProperty.key)) continue\n\n const key = objectProperty.key.name\n if (!['id', 'message', 'context', 'comment'].includes(key)) continue\n\n const value = readStaticStringValue(objectProperty.value)\n if (value === undefined) continue\n staticParts[key as keyof typeof staticParts] = value\n }\n\n if (!staticParts.message) {\n return undefined\n }\n\n return descriptorFromStaticParts(staticParts, idGenerator)\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICU(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction extractSelectProps(\n openingElement: JSXOpeningElementNode,\n code: string,\n): { varName: string | undefined; id: string | undefined; context: string | undefined; comment: string | undefined; cases: Record<string, string>; other: string | undefined } {\n let varName: string | undefined\n let id: string | undefined\n let context: string | undefined\n let comment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const attribute of openingElement.attributes) {\n if (attribute.type !== 'JSXAttribute') continue\n const attr = attribute as JSXAttributeNode\n if (attr.name.type !== 'JSXIdentifier') continue\n const name = String(attr.name['name'])\n\n if (name === 'id') {\n id = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'value') {\n const raw = attr.value ? (readStaticStringValue(attr.value) ?? readExpressionSource(attr.value, code)) : undefined\n varName = raw ? classifyExpression(raw) || raw : undefined\n continue\n }\n if (name === 'context') {\n context = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'comment') {\n comment = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (name === 'other') {\n other = attr.value ? readStaticStringValue(attr.value) : undefined\n continue\n }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n\n const staticValue = attr.value ? readStaticStringValue(attr.value) : undefined\n if (staticValue === undefined) continue\n\n cases[name] = staticValue\n }\n\n return { varName, id, context, comment, cases, other }\n}\n\nfunction buildPluralICU(props: Record<string, string>): string {\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other'] as const\n const countVar = props['value'] ?? props['count'] ?? 'count'\n const options: string[] = []\n const offset = props['offset']\n\n for (const category of categories) {\n const value = props[category]\n if (value === undefined) continue\n const key = category === 'zero' ? '=0' : category\n options.push(`${key} {${value}}`)\n }\n\n if (options.length === 0) {\n return ''\n }\n\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction extractRichTextMessage(children: readonly SourceNode[]): string | undefined {\n let nextIndex = 0\n\n function render(nodes: readonly SourceNode[]): string | undefined {\n let message = ''\n\n for (const node of nodes) {\n if (node.type === 'JSXText') {\n message += normalizeJsxText((node as JSXTextNode).value)\n continue\n }\n\n if (node.type === 'JSXElement') {\n const idx = nextIndex++\n const inner = render((node as JSXElementNode).children)\n if (inner === undefined) return undefined\n message += `<${idx}>${inner}</${idx}>`\n continue\n }\n\n if (node.type === 'JSXFragment') {\n const inner = render((node as JSXFragmentNode).children)\n if (inner === undefined) return undefined\n message += inner\n continue\n }\n\n if (node.type === 'JSXExpressionContainer') {\n const expression = (node as JSXExpressionContainerNode).expression\n if (expression.type === 'StringLiteral') {\n message += (expression as StringLiteralNode).value\n continue\n }\n if (expression.type === 'NumericLiteral') {\n message += String((expression as NumericLiteralNode).value)\n continue\n }\n return undefined\n }\n }\n\n return message\n }\n\n const message = render(children)\n if (message === undefined) return undefined\n\n const normalized = message.replace(/\\s+/g, ' ').trim()\n return normalized || undefined\n}\n\nfunction normalizeJsxText(value: string): string {\n return value.replace(/\\s+/g, ' ')\n}\n\nfunction readStaticStringValue(node: SourceNode): string | undefined {\n if (node.type === 'StringLiteral') {\n return (node as StringLiteralNode).value\n }\n\n if (node.type === 'NumericLiteral') {\n return String((node as NumericLiteralNode).value)\n }\n\n if (node.type === 'JSXExpressionContainer') {\n return readStaticStringValue((node as JSXExpressionContainerNode).expression)\n }\n\n if (node.type === 'TemplateLiteral') {\n const template = node as TemplateLiteralNode\n if (template.expressions.length === 0) {\n return template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join('')\n }\n }\n\n return undefined\n}\n\nfunction readExpressionSource(node: SourceNode, code: string): string | undefined {\n if (node.start == null || node.end == null) {\n return undefined\n }\n\n if (node.type === 'JSXExpressionContainer') {\n return readExpressionSource((node as JSXExpressionContainerNode).expression, code)\n }\n\n return code.slice(node.start, node.end).trim()\n}\n\nfunction getJsxAttribute(\n openingElement: JSXOpeningElementNode,\n name: string,\n): JSXAttributeNode | undefined {\n for (const attribute of openingElement.attributes) {\n if (attribute.type !== 'JSXAttribute') continue\n\n const jsxAttribute = attribute as JSXAttributeNode\n if (jsxAttribute.name.type === 'JSXIdentifier' && jsxAttribute.name['name'] === name) {\n return jsxAttribute\n }\n }\n\n return undefined\n}\n\nfunction extractPluralProps(\n openingElement: JSXOpeningElementNode,\n code: string,\n): Record<string, string> {\n const props: Record<string, string> = {}\n const propNames = ['id', 'value', 'count', 'offset', 'zero', 'one', 'two', 'few', 'many', 'other']\n\n for (const name of propNames) {\n const attribute = getJsxAttribute(openingElement, name)\n if (!attribute?.value) continue\n\n const staticValue = readStaticStringValue(attribute.value)\n if (staticValue !== undefined) {\n props[name] = staticValue\n continue\n }\n\n const exprValue = readExpressionSource(attribute.value, code)\n if (exprValue !== undefined && (name === 'value' || name === 'count' || name === 'offset')) {\n props[name] = exprValue\n }\n }\n\n return props\n}\n\nfunction extractTaggedTemplateMessage(\n code: string,\n node: TaggedTemplateExpressionNode,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor {\n const strings = node.quasi.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw)\n const expressions = node.quasi.expressions.map((expression) => {\n if (expression.start == null || expression.end == null) {\n return ''\n }\n return code.slice(expression.start, expression.end)\n })\n const message = buildICUFromTemplate(strings, expressions)\n const generateId = idGenerator ?? createMessageId\n\n return {\n id: generateId(message),\n message,\n }\n}\n\nfunction collectDirectBindings(ast: SourceNode): Map<string, 't' | 'msg'> {\n const bindings = new Map<string, 't' | 'msg'>()\n const body = Array.isArray(ast['body']) ? ast['body'] : []\n\n for (const entry of body) {\n if (!isImportDeclaration(entry)) continue\n if (!DIRECT_T_SOURCES.has(entry.source.value)) continue\n\n for (const specifier of entry.specifiers) {\n if (!isImportSpecifier(specifier)) continue\n const importedName = readImportedName(specifier)\n if (importedName === 't' || importedName === 'msg') {\n bindings.set(specifier.local.name, importedName as 't' | 'msg')\n }\n }\n }\n\n return bindings\n}\n\nexport function extractFromTsx(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const ast = parseSourceModule(code)\n if (!ast) {\n console.warn(`[fluenti] Failed to parse ${filename} — skipping message extraction`)\n return []\n }\n\n const messages: ExtractedMessage[] = []\n const directBindings = collectDirectBindings(ast)\n\n walkSourceAst(ast, (node: SourceNode) => {\n if (node.type === 'TaggedTemplateExpression') {\n const tagged = node as TaggedTemplateExpressionNode\n if (\n isIdentifier(tagged.tag)\n && (tagged.tag.name === 't' || directBindings.has(tagged.tag.name))\n ) {\n const descriptor = extractTaggedTemplateMessage(code, tagged, idGenerator)\n const bindingType = directBindings.get(tagged.tag.name)\n if (bindingType === 'msg') {\n descriptor.comment = 'msg tagged template'\n }\n const extracted = createExtractedMessage(\n descriptor,\n filename,\n tagged,\n )\n if (extracted) {\n messages.push(extracted)\n }\n }\n return\n }\n\n if (node.type === 'CallExpression') {\n const call = node as CallExpressionNode\n const callBindingType = directBindings.get(call.callee && isIdentifier(call.callee) ? call.callee.name : '')\n if (isIdentifier(call.callee) && (call.callee.name === 't' || (directBindings.has(call.callee.name) && callBindingType === 't'))) {\n if (directBindings.has(call.callee.name) && call.arguments[0]?.type !== 'ObjectExpression') {\n return\n }\n const descriptor = call.arguments[0] ? extractDescriptorFromCallArgument(call.arguments[0], idGenerator) : undefined\n const extracted = descriptor\n ? createExtractedMessage(descriptor, filename, call)\n : undefined\n if (extracted) {\n messages.push(extracted)\n }\n } else if (\n call.callee.type === 'MemberExpression'\n && isIdentifier(call.callee['object'])\n && directBindings.get((call.callee['object'] as IdentifierNode).name) === 'msg'\n && isIdentifier(call.callee['property'])\n && (call.callee['property'] as IdentifierNode).name === 'descriptor'\n && call.arguments[0]\n ) {\n const descriptor = extractDescriptorFromCallArgument(call.arguments[0], idGenerator)\n const extracted = descriptor ? createExtractedMessage(descriptor, filename, call) : undefined\n if (extracted) {\n messages.push(extracted)\n }\n }\n return\n }\n\n if (node.type !== 'JSXElement') {\n return\n }\n\n const element = node as JSXElementNode\n const openingElement = element.openingElement\n const elementName = readJsxElementName(openingElement.name)\n\n if (elementName === 'Trans') {\n const messageAttr = getJsxAttribute(openingElement, 'message')\n const idAttr = getJsxAttribute(openingElement, 'id')\n const contextAttr = getJsxAttribute(openingElement, 'context')\n const commentAttr = getJsxAttribute(openingElement, 'comment')\n\n const descriptor = messageAttr?.value\n ? buildStaticTransDescriptor({\n id: idAttr?.value ? readStaticStringValue(idAttr.value) : undefined,\n message: readStaticStringValue(messageAttr.value),\n context: contextAttr?.value ? readStaticStringValue(contextAttr.value) : undefined,\n comment: commentAttr?.value ? readStaticStringValue(commentAttr.value) : undefined,\n }, idGenerator)\n : buildStaticTransDescriptor({\n id: idAttr?.value ? readStaticStringValue(idAttr.value) : undefined,\n message: extractRichTextMessage(element.children),\n context: contextAttr?.value ? readStaticStringValue(contextAttr.value) : undefined,\n comment: commentAttr?.value ? readStaticStringValue(commentAttr.value) : undefined,\n }, idGenerator)\n\n const extracted = descriptor\n ? createExtractedMessage(descriptor, filename, element)\n : undefined\n if (extracted) {\n messages.push(extracted)\n }\n return\n }\n\n if (elementName === 'Plural') {\n const props = extractPluralProps(openingElement, code)\n const message = buildPluralICU(props)\n if (!message) {\n return\n }\n\n const generateId = idGenerator ?? createMessageId\n const extracted = createExtractedMessage(\n {\n id: props['id'] ?? generateId(message),\n message,\n },\n filename,\n element,\n )\n if (extracted) {\n messages.push(extracted)\n }\n return\n }\n\n if (elementName === 'Select') {\n const { varName, id, context, comment, cases, other } = extractSelectProps(openingElement, code)\n if (!varName || !other || Object.keys(cases).length === 0) {\n return\n }\n\n const message = buildSelectICU(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const extracted = createExtractedMessage(\n {\n id: id ?? generateId(message, context),\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n },\n filename,\n element,\n )\n if (extracted) {\n messages.push(extracted)\n }\n }\n })\n\n return messages\n}\n\nfunction isImportDeclaration(node: unknown): node is ImportDeclarationNode {\n return isSourceNode(node) && node.type === 'ImportDeclaration'\n}\n\nfunction isImportSpecifier(node: unknown): node is ImportSpecifierNode {\n return isSourceNode(node) && node.type === 'ImportSpecifier'\n}\n\nfunction readImportedName(specifier: ImportSpecifierNode): string | undefined {\n if (specifier.imported.type === 'Identifier') {\n return (specifier.imported as IdentifierNode).name\n }\n if (specifier.imported.type === 'StringLiteral') {\n return (specifier.imported as StringLiteralNode).value\n }\n return undefined\n}\n\nfunction readJsxElementName(node: SourceNode): string | undefined {\n if (node.type === 'JSXIdentifier') {\n return String(node['name'])\n }\n return undefined\n}\n\nfunction buildStaticTransDescriptor(\n parts: {\n id: string | undefined\n message: string | undefined\n context: string | undefined\n comment: string | undefined\n },\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedDescriptor | undefined {\n const payload: {\n id?: string\n message?: string\n context?: string\n comment?: string\n } = {}\n\n if (parts.id !== undefined) payload.id = parts.id\n if (parts.message !== undefined) payload.message = parts.message\n if (parts.context !== undefined) payload.context = parts.context\n if (parts.comment !== undefined) payload.comment = parts.comment\n\n return descriptorFromStaticParts(payload, idGenerator)\n}\n\nfunction isIdentifier(node: unknown): node is IdentifierNode {\n return isSourceNode(node) && node.type === 'Identifier'\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"tsx-extractor.d.ts","sourceRoot":"","sources":["../src/tsx-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAuf9D,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAC1D,gBAAgB,EAAE,CAoJpB"}
1
+ {"version":3,"file":"tsx-extractor.d.ts","sourceRoot":"","sources":["../src/tsx-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAuf9D,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAC1D,gBAAgB,EAAE,CAqJpB"}
@@ -6,7 +6,10 @@ export interface ValidationResult {
6
6
  extraHtmlTags: string[];
7
7
  syntaxErrors: string[];
8
8
  }
9
- /** Extract unique ICU placeholder names from a message, sorted alphabetically */
9
+ /** Extract unique ICU placeholder names from a message, sorted alphabetically.
10
+ * Uses the ICU parser so only top-level variables are returned — select/plural case
11
+ * body words (e.g. the `he`/`she` in `{g, select, male {he} other {she}}`) are not
12
+ * mistakenly treated as placeholder names. */
10
13
  export declare function extractPlaceholders(message: string): string[];
11
14
  /** Extract unique HTML tag names from a message, lowercased and sorted */
12
15
  export declare function extractHtmlTags(message: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAA;IACd,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAMD,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ7D;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQzD;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAgCzF;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAclC"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAA;IACd,mBAAmB,EAAE,MAAM,EAAE,CAAA;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,YAAY,EAAE,MAAM,EAAE,CAAA;CACvB;AAOD;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA8B7D;AAiBD,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAQzD;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CAgCzF;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACnC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAclC"}
@@ -1,3 +1,3 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./extract-cache-idNfsd7n.cjs`);const e=require(`./tsx-extractor-C-9_TnCW.cjs`);let t=require(`@fluenti/core/transform`),n=require(`@vue/compiler-sfc`);var r=1,i=2,a=7,o=6;function s(e){return e.filter(e=>e.type===i).map(e=>(e.content??``).trim()).join(``)}function c(e,t){let n=e.split(`|`).map(e=>e.trim()),r=[`one`,`other`,`zero`,`few`,`many`],i=[];if(n.length===2)i.push(`one {${n[0]}}`),i.push(`other {${n[1]}}`);else for(let e=0;e<n.length&&e<r.length;e++)i.push(`${r[e]} {${n[e]}}`);return`{${t}, plural, ${i.join(` `)}}`}var l=new Set([`id`,`value`,`context`,`comment`,`options`,`other`,`tag`]);function u(e,t,n){let r=[];for(let[e,n]of Object.entries(t))r.push(`${e} {${n}}`);return r.push(`other {${n}}`),`{${e}, select, ${r.join(` `)}}`}function d(e){let t=e.count??`count`,n=[`zero`,`one`,`two`,`few`,`many`,`other`],r=[],i=e.offset;for(let t of n)if(e[t]!==void 0){let n=t===`zero`?`=0`:t;r.push(`${n} {${e[t]}}`)}return r.length===0?``:`{${t}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function f(e,n,i,h){if(e.type===r){let r=e.props?.find(e=>e.type===a&&m(e)===`t`);if(r){let a=new Set([`plural`]),o=(r.modifiers??[]).map(e=>typeof e==`string`?e:e.content),l=o.includes(`plural`),u=o.filter(e=>!a.has(e)),d=r.arg?.content,f=d?[d,...u].join(`.`):void 0,p=s(e.children??[]);if(l){let e=c(p,r.exp?.content??`count`),a=f??(h??t.createMessageId)(e);i.push({id:a,message:e,origin:{file:n,line:r.loc.start.line,column:r.loc.start.column}})}else if(p){let e=f??(h??t.createMessageId)(p);i.push({id:e,message:p,origin:{file:n,line:r.loc.start.line,column:r.loc.start.column}})}}if(e.tag===`Trans`){let r=e.props?.find(e=>e.type===o&&m(e)===`message`),a=e.props?.find(e=>e.type===o&&m(e)===`id`),s=e.props?.find(e=>e.type===o&&m(e)===`context`),c=e.props?.find(e=>e.type===o&&m(e)===`comment`),l=s?.value?.content,u=c?.value?.content;if(r?.value){let o=r.value.content,s=h??t.createMessageId,c=a?.value?.content??s(o,l);i.push({id:c,message:o,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}else if(e.children&&e.children.length>0){let r=p(e.children);if(r.message){let o=h??t.createMessageId,s=a?.value?.content??o(r.message,l);i.push({id:s,message:r.message,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.tag===`Plural`){let r={},s,c;for(let t of e.props??[])t.type===o&&t.value&&(r[m(t)]=t.value.content),t.type===a&&m(t)===`bind`&&t.arg?.content===`value`&&t.exp&&(s=t.exp.content),t.type===a&&m(t)===`bind`&&t.arg?.content===`offset`&&t.exp&&(c=t.exp.content);let l=s??r.count??`count`,u=c??r.offset,f=d({...r,count:l,...u===void 0?{}:{offset:u}});if(f){let a=r.id??(h??t.createMessageId)(f);i.push({id:a,message:f,origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}if(e.tag===`Select`){let r,s,c,d,f,p={};for(let t of e.props??[]){if(t.type===o&&t.value){let e=m(t);if(e===`id`){s=t.value.content;continue}if(e===`context`){c=t.value.content;continue}if(e===`comment`){d=t.value.content;continue}if(e===`other`){f=t.value.content;continue}if(l.has(e))continue;p[e]=t.value.content}t.type===a&&m(t)===`bind`&&t.arg?.content===`value`&&t.exp&&(r=t.exp.content)}if(r&&f&&Object.keys(p).length>0){let a=u(r,p,f),o=s??(h??t.createMessageId)(a,c);i.push({id:o,message:a,...c===void 0?{}:{context:c},...d===void 0?{}:{comment:d},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.children)for(let t of e.children)f(t,n,i,h)}function p(e){let t=0,n=!1;return{message:e.map(e=>{if(e.type===i)return(e.content??``).trim()?e.content??``:``;if(e.type===r&&e.tag){n=!0;let r=t++;return`<${r}>${p(e.children??[]).message}</${r}>`}return``}).join(``).trim(),hasElements:n}}function m(e){return typeof e.name==`string`?e.name:e.name.content}function h(t,n,r){let i=[],a=/\{\{([\s\S]*?)\}\}/g,o;for(;(o=a.exec(t))!==null;){let a=o[1]?.trim();if(!a)continue;let s=e.t(a,n,r);if(s.length===0)continue;let c=t.slice(0,o.index).split(`
2
- `).length-1;for(let e of s)i.push({...e,origin:{...e.origin,line:e.origin.line+c}})}return i}function g(t,r,i){let a=[],{descriptor:o}=(0,n.parse)(t,{filename:r});if(o.template?.ast&&f(o.template.ast,r,a,i),o.template?.content){let t=e.t(o.template.content,r,i),n=o.template.loc.start.line-1,s=new Set(a.map(e=>e.id));for(let e of t)s.has(e.id)||a.push({...e,origin:{...e.origin,line:e.origin.line+n}});let c=h(o.template.content,r,i);for(let e of c)s.has(e.id)||a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}if(o.scriptSetup?.content){let t=e.t(o.scriptSetup.content,r,i),n=o.scriptSetup.loc.start.line-1;for(let e of t)a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}if(o.script?.content){let t=e.t(o.script.content,r,i),n=o.script.loc.start.line-1;for(let e of t)a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}return a}exports.extractFromVue=g;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./extract-cache-BioSaoFo.cjs`);const e=require(`./tsx-extractor-B0vFXziu.cjs`);let t=require(`@fluenti/core/transform`),n=require(`@vue/compiler-sfc`);var r=1,i=2,a=7,o=6;function s(e){return e.filter(e=>e.type===i).map(e=>(e.content??``).trim()).join(``)}function c(e,t){let n=e.split(`|`).map(e=>e.trim()),r=[`one`,`other`,`zero`,`few`,`many`],i=[];if(n.length===2)i.push(`one {${n[0]}}`),i.push(`other {${n[1]}}`);else for(let e=0;e<n.length&&e<r.length;e++)i.push(`${r[e]} {${n[e]}}`);return`{${t}, plural, ${i.join(` `)}}`}var l=new Set([`id`,`value`,`context`,`comment`,`options`,`other`,`tag`]);function u(e,t,n){let r=[];for(let[e,n]of Object.entries(t))r.push(`${e} {${n}}`);return r.push(`other {${n}}`),`{${e}, select, ${r.join(` `)}}`}function d(e){let t=e.count??`count`,n=[`zero`,`one`,`two`,`few`,`many`,`other`],r=[],i=e.offset;for(let t of n)if(e[t]!==void 0){let n=t===`zero`?`=0`:t;r.push(`${n} {${e[t]}}`)}return r.length===0?``:`{${t}, plural, ${i?`offset:${i} `:``}${r.join(` `)}}`}function f(e,n,i,h){if(e.type===r){let r=e.props?.find(e=>e.type===a&&m(e)===`t`);if(r){let a=new Set([`plural`]),o=(r.modifiers??[]).map(e=>typeof e==`string`?e:e.content),l=o.includes(`plural`),u=o.filter(e=>!a.has(e)),d=r.arg?.content,f=d?[d,...u].join(`.`):void 0,p=s(e.children??[]);if(l){let e=c(p,r.exp?.content??`count`),a=f??(h??t.createMessageId)(e);i.push({id:a,message:e,origin:{file:n,line:r.loc.start.line,column:r.loc.start.column}})}else if(p){let e=f??(h??t.createMessageId)(p);i.push({id:e,message:p,origin:{file:n,line:r.loc.start.line,column:r.loc.start.column}})}}if(e.tag===`Trans`){let r=e.props?.find(e=>e.type===o&&m(e)===`message`),a=e.props?.find(e=>e.type===o&&m(e)===`id`),s=e.props?.find(e=>e.type===o&&m(e)===`context`),c=e.props?.find(e=>e.type===o&&m(e)===`comment`),l=s?.value?.content,u=c?.value?.content;if(r?.value){let o=r.value.content,s=h??t.createMessageId,c=a?.value?.content??s(o,l);i.push({id:c,message:o,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}else if(e.children&&e.children.length>0){let r=p(e.children);if(r.message){let o=h??t.createMessageId,s=a?.value?.content??o(r.message,l);i.push({id:s,message:r.message,...l===void 0?{}:{context:l},...u===void 0?{}:{comment:u},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.tag===`Plural`){let r={},s,c;for(let t of e.props??[])t.type===o&&t.value&&(r[m(t)]=t.value.content),t.type===a&&m(t)===`bind`&&t.arg?.content===`value`&&t.exp&&(s=t.exp.content),t.type===a&&m(t)===`bind`&&t.arg?.content===`offset`&&t.exp&&(c=t.exp.content);let l=s??r.count??`count`,u=c??r.offset,f=d({...r,count:l,...u===void 0?{}:{offset:u}});if(f){let a=r.id??(h??t.createMessageId)(f);i.push({id:a,message:f,origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}if(e.tag===`Select`){let r,s,c,d,f,p={};for(let t of e.props??[]){if(t.type===o&&t.value){let e=m(t);if(e===`id`){s=t.value.content;continue}if(e===`context`){c=t.value.content;continue}if(e===`comment`){d=t.value.content;continue}if(e===`other`){f=t.value.content;continue}if(l.has(e))continue;p[e]=t.value.content}t.type===a&&m(t)===`bind`&&t.arg?.content===`value`&&t.exp&&(r=t.exp.content)}if(r&&f&&Object.keys(p).length>0){let a=u(r,p,f),o=s??(h??t.createMessageId)(a,c);i.push({id:o,message:a,...c===void 0?{}:{context:c},...d===void 0?{}:{comment:d},origin:{file:n,line:e.loc.start.line,column:e.loc.start.column}})}}}if(e.children)for(let t of e.children)f(t,n,i,h)}function p(e){let t=0,n=!1;return{message:e.map(e=>{if(e.type===i)return(e.content??``).trim()?e.content??``:``;if(e.type===r&&e.tag){n=!0;let r=t++;return`<${r}>${p(e.children??[]).message}</${r}>`}return``}).join(``).trim(),hasElements:n}}function m(e){return typeof e.name==`string`?e.name:e.name.content}function h(t,n,r){let i=[],a=/\{\{([\s\S]*?)\}\}/g,o;for(;(o=a.exec(t))!==null;){let a=o[1]?.trim();if(!a)continue;let s=e.t(a,n,r);if(s.length===0)continue;let c=t.slice(0,o.index).split(`
2
+ `).length-1;for(let e of s)i.push({...e,origin:{...e.origin,line:e.origin.line+c}})}return i}function g(t,r,i){let a=[],{descriptor:o}=(0,n.parse)(t,{filename:r});if(o.template?.ast&&f(o.template.ast,r,a,i),o.template?.content){let t=e.t(o.template.content,r,i),n=o.template.loc.start.line-1,s=new Set(a.map(e=>e.id));for(let e of t)s.has(e.id)||a.push({...e,origin:{...e.origin,line:e.origin.line+n}});let c=new Set(a.map(e=>e.id)),l=h(o.template.content,r,i);for(let e of l)c.has(e.id)||a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}if(o.scriptSetup?.content){let t=e.t(o.scriptSetup.content,r,i),n=o.scriptSetup.loc.start.line-1;for(let e of t)a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}if(o.script?.content){let t=e.t(o.script.content,r,i),n=o.script.loc.start.line-1;for(let e of t)a.push({...e,origin:{...e.origin,line:e.origin.line+n}})}return a}exports.extractFromVue=g;
3
3
  //# sourceMappingURL=vue-extractor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"vue-extractor.cjs","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/transform'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICUFromProps(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n idGenerator?: (message: string, context?: string) => string,\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? (idGenerator ?? createMessageId)(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? (idGenerator ?? createMessageId)(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? (idGenerator ?? createMessageId)(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Select') {\n let varName: string | undefined\n let selectId: string | undefined\n let selectContext: string | undefined\n let selectComment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n const name = getPropName(prop)\n if (name === 'id') { selectId = prop.value.content; continue }\n if (name === 'context') { selectContext = prop.value.content; continue }\n if (name === 'comment') { selectComment = prop.value.content; continue }\n if (name === 'other') { other = prop.value.content; continue }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n cases[name] = prop.value.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n varName = prop.exp.content\n }\n }\n\n if (varName && other && Object.keys(cases).length > 0) {\n const message = buildSelectICUFromProps(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const id = selectId ?? generateId(message, selectContext)\n messages.push({\n id,\n message,\n ...(selectContext !== undefined ? { context: selectContext } : {}),\n ...(selectComment !== undefined ? { comment: selectComment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages, idGenerator)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename, idGenerator)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages, idGenerator)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename, idGenerator)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename, idGenerator)\n for (const msg of interpolationMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename, idGenerator)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename, idGenerator)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vue-extractor.cjs","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/transform'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICUFromProps(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n idGenerator?: (message: string, context?: string) => string,\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? (idGenerator ?? createMessageId)(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? (idGenerator ?? createMessageId)(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? (idGenerator ?? createMessageId)(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Select') {\n let varName: string | undefined\n let selectId: string | undefined\n let selectContext: string | undefined\n let selectComment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n const name = getPropName(prop)\n if (name === 'id') { selectId = prop.value.content; continue }\n if (name === 'context') { selectContext = prop.value.content; continue }\n if (name === 'comment') { selectComment = prop.value.content; continue }\n if (name === 'other') { other = prop.value.content; continue }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n cases[name] = prop.value.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n varName = prop.exp.content\n }\n }\n\n if (varName && other && Object.keys(cases).length > 0) {\n const message = buildSelectICUFromProps(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const id = selectId ?? generateId(message, selectContext)\n messages.push({\n id,\n message,\n ...(selectContext !== undefined ? { context: selectContext } : {}),\n ...(selectComment !== undefined ? { comment: selectComment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages, idGenerator)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename, idGenerator)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages, idGenerator)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename, idGenerator)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n // Rebuild the set after template pass to include newly added IDs\n const afterTemplateIds = new Set(messages.map((m) => m.id))\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename, idGenerator)\n for (const msg of interpolationMessages) {\n if (!afterTemplateIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename, idGenerator)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename, idGenerator)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"vue-extractor.d.ts","sourceRoot":"","sources":["../src/vue-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAiW9D,0CAA0C;AAC1C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAC1D,gBAAgB,EAAE,CAyEpB"}
1
+ {"version":3,"file":"vue-extractor.d.ts","sourceRoot":"","sources":["../src/vue-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAiW9D,0CAA0C;AAC1C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,MAAM,GAC1D,gBAAgB,EAAE,CA2EpB"}
@@ -1,4 +1,4 @@
1
- import { t as e } from "./tsx-extractor-CjQIMvKo.js";
1
+ import { t as e } from "./tsx-extractor-C-HZNobu.js";
2
2
  import { createMessageId as t } from "@fluenti/core/transform";
3
3
  import { parse as n } from "@vue/compiler-sfc";
4
4
  //#region src/vue-extractor.ts
@@ -221,8 +221,8 @@ function g(t, r, i) {
221
221
  line: e.origin.line + n
222
222
  }
223
223
  });
224
- let c = h(o.template.content, r, i);
225
- for (let e of c) s.has(e.id) || a.push({
224
+ let c = new Set(a.map((e) => e.id)), l = h(o.template.content, r, i);
225
+ for (let e of l) c.has(e.id) || a.push({
226
226
  ...e,
227
227
  origin: {
228
228
  ...e.origin,
@@ -1 +1 @@
1
- {"version":3,"file":"vue-extractor.js","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/transform'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICUFromProps(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n idGenerator?: (message: string, context?: string) => string,\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? (idGenerator ?? createMessageId)(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? (idGenerator ?? createMessageId)(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? (idGenerator ?? createMessageId)(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Select') {\n let varName: string | undefined\n let selectId: string | undefined\n let selectContext: string | undefined\n let selectComment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n const name = getPropName(prop)\n if (name === 'id') { selectId = prop.value.content; continue }\n if (name === 'context') { selectContext = prop.value.content; continue }\n if (name === 'comment') { selectComment = prop.value.content; continue }\n if (name === 'other') { other = prop.value.content; continue }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n cases[name] = prop.value.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n varName = prop.exp.content\n }\n }\n\n if (varName && other && Object.keys(cases).length > 0) {\n const message = buildSelectICUFromProps(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const id = selectId ?? generateId(message, selectContext)\n messages.push({\n id,\n message,\n ...(selectContext !== undefined ? { context: selectContext } : {}),\n ...(selectComment !== undefined ? { comment: selectComment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages, idGenerator)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename, idGenerator)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages, idGenerator)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename, idGenerator)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename, idGenerator)\n for (const msg of interpolationMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename, idGenerator)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename, idGenerator)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"vue-extractor.js","names":[],"sources":["../src/vue-extractor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/internal'\nimport { parse as parseSFC } from '@vue/compiler-sfc'\nimport { createMessageId } from '@fluenti/core/transform'\nimport { extractFromTsx } from './tsx-extractor'\n\n// Vue template AST node types\nconst ELEMENT_NODE = 1\nconst TEXT_NODE = 2\nconst DIRECTIVE_PROP = 7\nconst ATTRIBUTE_PROP = 6\n\ninterface LocInfo {\n line: number\n column: number\n offset: number\n}\n\ninterface SourceLoc {\n start: LocInfo\n end: LocInfo\n source: string\n}\n\ninterface TemplateNode {\n type: number\n tag?: string\n tagType?: number\n props?: TemplateProp[]\n children?: TemplateNode[]\n content?: string\n loc: SourceLoc\n}\n\ninterface TemplateProp {\n type: number\n name: string | { content: string }\n rawName?: string\n arg?: { content: string; isStatic: boolean }\n exp?: { content: string }\n modifiers?: Array<{ content: string } | string>\n value?: { content: string }\n nameLoc?: SourceLoc\n loc: SourceLoc\n}\n\nfunction getTextContent(children: TemplateNode[]): string {\n return children\n .filter((c) => c.type === TEXT_NODE)\n .map((c) => (c.content ?? '').trim())\n .join('')\n}\n\nfunction buildPluralICUFromPipe(text: string, countVar: string): string {\n const forms = text.split('|').map((s) => s.trim())\n const categories = ['one', 'other', 'zero', 'few', 'many']\n const options: string[] = []\n\n if (forms.length === 2) {\n options.push(`one {${forms[0]}}`)\n options.push(`other {${forms[1]}}`)\n } else {\n for (let i = 0; i < forms.length && i < categories.length; i++) {\n options.push(`${categories[i]} {${forms[i]}}`)\n }\n }\n\n return `{${countVar}, plural, ${options.join(' ')}}`\n}\n\nconst SELECT_RESERVED_PROPS = new Set(['id', 'value', 'context', 'comment', 'options', 'other', 'tag'])\n\nfunction buildSelectICUFromProps(varName: string, cases: Record<string, string>, other: string): string {\n const options: string[] = []\n for (const [key, value] of Object.entries(cases)) {\n options.push(`${key} {${value}}`)\n }\n options.push(`other {${other}}`)\n return `{${varName}, select, ${options.join(' ')}}`\n}\n\nfunction buildPluralICUFromProps(props: Record<string, string>): string {\n const countVar = props['count'] ?? 'count'\n const categories = ['zero', 'one', 'two', 'few', 'many', 'other']\n const options: string[] = []\n const offset = props['offset']\n\n for (const cat of categories) {\n if (props[cat] !== undefined) {\n const key = cat === 'zero' ? '=0' : cat\n options.push(`${key} {${props[cat]}}`)\n }\n }\n\n if (options.length === 0) return ''\n const offsetPrefix = offset ? `offset:${offset} ` : ''\n return `{${countVar}, plural, ${offsetPrefix}${options.join(' ')}}`\n}\n\nfunction walkTemplate(\n node: TemplateNode,\n filename: string,\n messages: ExtractedMessage[],\n idGenerator?: (message: string, context?: string) => string,\n): void {\n if (node.type === ELEMENT_NODE) {\n const vtDirective = node.props?.find(\n (p) => p.type === DIRECTIVE_PROP && getPropName(p) === 't',\n )\n\n if (vtDirective) {\n const RESERVED_MODIFIERS = new Set(['plural'])\n const modifiers = (vtDirective.modifiers ?? []).map(\n (m: string | { content: string }) => (typeof m === 'string' ? m : m.content),\n )\n const isPlural = modifiers.includes('plural')\n // Reconstruct dotted ID: v-t:checkout.title → arg=\"checkout\", modifier=\"title\" → \"checkout.title\"\n // Non-reserved modifiers are treated as ID path segments\n const idSegments = modifiers.filter((m: string) => !RESERVED_MODIFIERS.has(m))\n const argContent = vtDirective.arg?.content\n const explicitId = argContent\n ? [argContent, ...idSegments].join('.')\n : undefined\n const textContent = getTextContent(node.children ?? [])\n\n if (isPlural) {\n const countVar = vtDirective.exp?.content ?? 'count'\n const message = buildPluralICUFromPipe(textContent, countVar)\n const id = explicitId ?? (idGenerator ?? createMessageId)(message)\n messages.push({\n id,\n message,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n } else if (textContent) {\n const id = explicitId ?? (idGenerator ?? createMessageId)(textContent)\n messages.push({\n id,\n message: textContent,\n origin: {\n file: filename,\n line: vtDirective.loc.start.line,\n column: vtDirective.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Trans') {\n const messageProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'message',\n )\n const idProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'id',\n )\n const contextProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'context',\n )\n const commentProp = node.props?.find(\n (p) => p.type === ATTRIBUTE_PROP && getPropName(p) === 'comment',\n )\n const context = contextProp?.value?.content\n const comment = commentProp?.value?.content\n\n if (messageProp?.value) {\n // Old API: <Trans message=\"...\" />\n const message = messageProp.value.content\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(message, context)\n messages.push({\n id,\n message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n } else if (node.children && node.children.length > 0) {\n // New API: <Trans>content with <a>rich text</a></Trans>\n const richText = extractRichTextFromTemplateChildren(node.children)\n if (richText.message) {\n const generateId = idGenerator ?? createMessageId\n const id = idProp?.value?.content ?? generateId(richText.message, context)\n messages.push({\n id,\n message: richText.message,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.tag === 'Plural') {\n const propsMap: Record<string, string> = {}\n let valueExpr: string | undefined\n let offsetExpr: string | undefined\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n propsMap[getPropName(prop)] = prop.value.content\n }\n // Handle :value=\"expr\" binding (directive prop)\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n valueExpr = prop.exp.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'offset' && prop.exp) {\n offsetExpr = prop.exp.content\n }\n }\n\n // Use :value binding expression as count variable, fall back to 'count' static prop\n const countVar = valueExpr ?? propsMap['count'] ?? 'count'\n const offset = offsetExpr ?? propsMap['offset']\n const pluralMessage = buildPluralICUFromProps({\n ...propsMap,\n count: countVar,\n ...(offset !== undefined ? { offset } : {}),\n })\n if (pluralMessage) {\n const id = propsMap['id'] ?? (idGenerator ?? createMessageId)(pluralMessage)\n messages.push({\n id,\n message: pluralMessage,\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n\n if (node.tag === 'Select') {\n let varName: string | undefined\n let selectId: string | undefined\n let selectContext: string | undefined\n let selectComment: string | undefined\n let other: string | undefined\n const cases: Record<string, string> = {}\n\n for (const prop of node.props ?? []) {\n if (prop.type === ATTRIBUTE_PROP && prop.value) {\n const name = getPropName(prop)\n if (name === 'id') { selectId = prop.value.content; continue }\n if (name === 'context') { selectContext = prop.value.content; continue }\n if (name === 'comment') { selectComment = prop.value.content; continue }\n if (name === 'other') { other = prop.value.content; continue }\n if (SELECT_RESERVED_PROPS.has(name)) continue\n cases[name] = prop.value.content\n }\n if (prop.type === DIRECTIVE_PROP && getPropName(prop) === 'bind' && prop.arg?.content === 'value' && prop.exp) {\n varName = prop.exp.content\n }\n }\n\n if (varName && other && Object.keys(cases).length > 0) {\n const message = buildSelectICUFromProps(varName, cases, other)\n const generateId = idGenerator ?? createMessageId\n const id = selectId ?? generateId(message, selectContext)\n messages.push({\n id,\n message,\n ...(selectContext !== undefined ? { context: selectContext } : {}),\n ...(selectComment !== undefined ? { comment: selectComment } : {}),\n origin: {\n file: filename,\n line: node.loc.start.line,\n column: node.loc.start.column,\n },\n })\n }\n }\n }\n\n if (node.children) {\n for (const child of node.children) {\n walkTemplate(child, filename, messages, idGenerator)\n }\n }\n}\n\nfunction extractRichTextFromTemplateChildren(\n children: TemplateNode[],\n): { message: string; hasElements: boolean } {\n let elementIndex = 0\n let hasElements = false\n\n const parts = children.map((child) => {\n if (child.type === TEXT_NODE) {\n return (child.content ?? '').trim() ? child.content ?? '' : ''\n }\n if (child.type === ELEMENT_NODE && child.tag) {\n hasElements = true\n const idx = elementIndex++\n const innerText = extractRichTextFromTemplateChildren(child.children ?? []).message\n return `<${idx}>${innerText}</${idx}>`\n }\n return ''\n })\n\n return {\n message: parts.join('').trim(),\n hasElements,\n }\n}\n\nfunction getPropName(prop: TemplateProp): string {\n if (typeof prop.name === 'string') return prop.name\n return prop.name.content\n}\n\nfunction extractTemplateInterpolations(\n content: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n const interpolationRegex = /\\{\\{([\\s\\S]*?)\\}\\}/g\n let match: RegExpExecArray | null\n\n while ((match = interpolationRegex.exec(content)) !== null) {\n const expression = match[1]?.trim()\n if (!expression) continue\n\n const extracted = extractFromTsx(expression, filename, idGenerator)\n if (extracted.length === 0) continue\n\n const lineOffset = content.slice(0, match.index).split('\\n').length - 1\n for (const msg of extracted) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n\n/** Extract messages from Vue SFC files */\nexport function extractFromVue(\n code: string,\n filename: string,\n idGenerator?: (message: string, context?: string) => string,\n): ExtractedMessage[] {\n const messages: ExtractedMessage[] = []\n\n const { descriptor } = parseSFC(code, { filename })\n\n if (descriptor.template?.ast) {\n walkTemplate(descriptor.template.ast as unknown as TemplateNode, filename, messages, idGenerator)\n }\n\n // Also extract t() function calls from raw template source\n // (picks up t('source text') in template expressions like {{ t('...') }})\n if (descriptor.template?.content) {\n const templateMessages = extractFromTsx(descriptor.template.content, filename, idGenerator)\n const templateLoc = descriptor.template.loc\n const lineOffset = templateLoc.start.line - 1\n const existingIds = new Set(messages.map((m) => m.id))\n for (const msg of templateMessages) {\n if (!existingIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n // Rebuild the set after template pass to include newly added IDs\n const afterTemplateIds = new Set(messages.map((m) => m.id))\n const interpolationMessages = extractTemplateInterpolations(descriptor.template.content, filename, idGenerator)\n for (const msg of interpolationMessages) {\n if (!afterTemplateIds.has(msg.id)) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n }\n\n if (descriptor.scriptSetup?.content) {\n const scriptMessages = extractFromTsx(descriptor.scriptSetup.content, filename, idGenerator)\n const scriptLoc = descriptor.scriptSetup.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n if (descriptor.script?.content) {\n const scriptMessages = extractFromTsx(descriptor.script.content, filename, idGenerator)\n const scriptLoc = descriptor.script.loc\n const lineOffset = scriptLoc.start.line - 1\n for (const msg of scriptMessages) {\n messages.push({\n ...msg,\n origin: {\n ...msg.origin,\n line: msg.origin.line + lineOffset,\n },\n })\n }\n }\n\n return messages\n}\n"],"mappings":";;;;AAMA,IAAM,IAAe,GACf,IAAY,GACZ,IAAiB,GACjB,IAAiB;AAoCvB,SAAS,EAAe,GAAkC;AACxD,QAAO,EACJ,QAAQ,MAAM,EAAE,SAAS,EAAU,CACnC,KAAK,OAAO,EAAE,WAAW,IAAI,MAAM,CAAC,CACpC,KAAK,GAAG;;AAGb,SAAS,EAAuB,GAAc,GAA0B;CACtE,IAAM,IAAQ,EAAK,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,EAC5C,IAAa;EAAC;EAAO;EAAS;EAAQ;EAAO;EAAO,EACpD,IAAoB,EAAE;AAE5B,KAAI,EAAM,WAAW,EAEnB,CADA,EAAQ,KAAK,QAAQ,EAAM,GAAG,GAAG,EACjC,EAAQ,KAAK,UAAU,EAAM,GAAG,GAAG;KAEnC,MAAK,IAAI,IAAI,GAAG,IAAI,EAAM,UAAU,IAAI,EAAW,QAAQ,IACzD,GAAQ,KAAK,GAAG,EAAW,GAAG,IAAI,EAAM,GAAG,GAAG;AAIlD,QAAO,IAAI,EAAS,YAAY,EAAQ,KAAK,IAAI,CAAC;;AAGpD,IAAM,IAAwB,IAAI,IAAI;CAAC;CAAM;CAAS;CAAW;CAAW;CAAW;CAAS;CAAM,CAAC;AAEvG,SAAS,EAAwB,GAAiB,GAA+B,GAAuB;CACtG,IAAM,IAAoB,EAAE;AAC5B,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAM,CAC9C,GAAQ,KAAK,GAAG,EAAI,IAAI,EAAM,GAAG;AAGnC,QADA,EAAQ,KAAK,UAAU,EAAM,GAAG,EACzB,IAAI,EAAQ,YAAY,EAAQ,KAAK,IAAI,CAAC;;AAGnD,SAAS,EAAwB,GAAuC;CACtE,IAAM,IAAW,EAAM,SAAY,SAC7B,IAAa;EAAC;EAAQ;EAAO;EAAO;EAAO;EAAQ;EAAQ,EAC3D,IAAoB,EAAE,EACtB,IAAS,EAAM;AAErB,MAAK,IAAM,KAAO,EAChB,KAAI,EAAM,OAAS,KAAA,GAAW;EAC5B,IAAM,IAAM,MAAQ,SAAS,OAAO;AACpC,IAAQ,KAAK,GAAG,EAAI,IAAI,EAAM,GAAK,GAAG;;AAM1C,QAFI,EAAQ,WAAW,IAAU,KAE1B,IAAI,EAAS,YADC,IAAS,UAAU,EAAO,KAAK,KACL,EAAQ,KAAK,IAAI,CAAC;;AAGnE,SAAS,EACP,GACA,GACA,GACA,GACM;AACN,KAAI,EAAK,SAAS,GAAc;EAC9B,IAAM,IAAc,EAAK,OAAO,MAC7B,MAAM,EAAE,SAAS,KAAkB,EAAY,EAAE,KAAK,IACxD;AAED,MAAI,GAAa;GACf,IAAM,IAAqB,IAAI,IAAI,CAAC,SAAS,CAAC,EACxC,KAAa,EAAY,aAAa,EAAE,EAAE,KAC7C,MAAqC,OAAO,KAAM,WAAW,IAAI,EAAE,QACrE,EACK,IAAW,EAAU,SAAS,SAAS,EAGvC,IAAa,EAAU,QAAQ,MAAc,CAAC,EAAmB,IAAI,EAAE,CAAC,EACxE,IAAa,EAAY,KAAK,SAC9B,IAAa,IACf,CAAC,GAAY,GAAG,EAAW,CAAC,KAAK,IAAI,GACrC,KAAA,GACE,IAAc,EAAe,EAAK,YAAY,EAAE,CAAC;AAEvD,OAAI,GAAU;IAEZ,IAAM,IAAU,EAAuB,GADtB,EAAY,KAAK,WAAW,QACgB,EACvD,IAAK,MAAe,KAAe,GAAiB,EAAQ;AAClE,MAAS,KAAK;KACZ;KACA;KACA,QAAQ;MACN,MAAM;MACN,MAAM,EAAY,IAAI,MAAM;MAC5B,QAAQ,EAAY,IAAI,MAAM;MAC/B;KACF,CAAC;cACO,GAAa;IACtB,IAAM,IAAK,MAAe,KAAe,GAAiB,EAAY;AACtE,MAAS,KAAK;KACZ;KACA,SAAS;KACT,QAAQ;MACN,MAAM;MACN,MAAM,EAAY,IAAI,MAAM;MAC5B,QAAQ,EAAY,IAAI,MAAM;MAC/B;KACF,CAAC;;;AAIN,MAAI,EAAK,QAAQ,SAAS;GACxB,IAAM,IAAc,EAAK,OAAO,MAC7B,MAAM,EAAE,SAAS,KAAkB,EAAY,EAAE,KAAK,UACxD,EACK,IAAS,EAAK,OAAO,MACxB,MAAM,EAAE,SAAS,KAAkB,EAAY,EAAE,KAAK,KACxD,EACK,IAAc,EAAK,OAAO,MAC7B,MAAM,EAAE,SAAS,KAAkB,EAAY,EAAE,KAAK,UACxD,EACK,IAAc,EAAK,OAAO,MAC7B,MAAM,EAAE,SAAS,KAAkB,EAAY,EAAE,KAAK,UACxD,EACK,IAAU,GAAa,OAAO,SAC9B,IAAU,GAAa,OAAO;AAEpC,OAAI,GAAa,OAAO;IAEtB,IAAM,IAAU,EAAY,MAAM,SAC5B,IAAa,KAAe,GAC5B,IAAK,GAAQ,OAAO,WAAW,EAAW,GAAS,EAAQ;AACjE,MAAS,KAAK;KACZ;KACA;KACA,GAAI,MAAY,KAAA,IAA0B,EAAE,GAAhB,EAAE,YAAS;KACvC,GAAI,MAAY,KAAA,IAA0B,EAAE,GAAhB,EAAE,YAAS;KACvC,QAAQ;MACN,MAAM;MACN,MAAM,EAAK,IAAI,MAAM;MACrB,QAAQ,EAAK,IAAI,MAAM;MACxB;KACF,CAAC;cACO,EAAK,YAAY,EAAK,SAAS,SAAS,GAAG;IAEpD,IAAM,IAAW,EAAoC,EAAK,SAAS;AACnE,QAAI,EAAS,SAAS;KACpB,IAAM,IAAa,KAAe,GAC5B,IAAK,GAAQ,OAAO,WAAW,EAAW,EAAS,SAAS,EAAQ;AAC1E,OAAS,KAAK;MACZ;MACA,SAAS,EAAS;MAClB,GAAI,MAAY,KAAA,IAA0B,EAAE,GAAhB,EAAE,YAAS;MACvC,GAAI,MAAY,KAAA,IAA0B,EAAE,GAAhB,EAAE,YAAS;MACvC,QAAQ;OACN,MAAM;OACN,MAAM,EAAK,IAAI,MAAM;OACrB,QAAQ,EAAK,IAAI,MAAM;OACxB;MACF,CAAC;;;;AAKR,MAAI,EAAK,QAAQ,UAAU;GACzB,IAAM,IAAmC,EAAE,EACvC,GACA;AACJ,QAAK,IAAM,KAAQ,EAAK,SAAS,EAAE,CAQjC,CAPI,EAAK,SAAS,KAAkB,EAAK,UACvC,EAAS,EAAY,EAAK,IAAI,EAAK,MAAM,UAGvC,EAAK,SAAS,KAAkB,EAAY,EAAK,KAAK,UAAU,EAAK,KAAK,YAAY,WAAW,EAAK,QACxG,IAAY,EAAK,IAAI,UAEnB,EAAK,SAAS,KAAkB,EAAY,EAAK,KAAK,UAAU,EAAK,KAAK,YAAY,YAAY,EAAK,QACzG,IAAa,EAAK,IAAI;GAK1B,IAAM,IAAW,KAAa,EAAS,SAAY,SAC7C,IAAS,KAAc,EAAS,QAChC,IAAgB,EAAwB;IAC5C,GAAG;IACH,OAAO;IACP,GAAI,MAAW,KAAA,IAAyB,EAAE,GAAf,EAAE,WAAQ;IACtC,CAAC;AACF,OAAI,GAAe;IACjB,IAAM,IAAK,EAAS,OAAU,KAAe,GAAiB,EAAc;AAC5E,MAAS,KAAK;KACZ;KACA,SAAS;KACT,QAAQ;MACN,MAAM;MACN,MAAM,EAAK,IAAI,MAAM;MACrB,QAAQ,EAAK,IAAI,MAAM;MACxB;KACF,CAAC;;;AAIN,MAAI,EAAK,QAAQ,UAAU;GACzB,IAAI,GACA,GACA,GACA,GACA,GACE,IAAgC,EAAE;AAExC,QAAK,IAAM,KAAQ,EAAK,SAAS,EAAE,EAAE;AACnC,QAAI,EAAK,SAAS,KAAkB,EAAK,OAAO;KAC9C,IAAM,IAAO,EAAY,EAAK;AAC9B,SAAI,MAAS,MAAM;AAAE,UAAW,EAAK,MAAM;AAAS;;AACpD,SAAI,MAAS,WAAW;AAAE,UAAgB,EAAK,MAAM;AAAS;;AAC9D,SAAI,MAAS,WAAW;AAAE,UAAgB,EAAK,MAAM;AAAS;;AAC9D,SAAI,MAAS,SAAS;AAAE,UAAQ,EAAK,MAAM;AAAS;;AACpD,SAAI,EAAsB,IAAI,EAAK,CAAE;AACrC,OAAM,KAAQ,EAAK,MAAM;;AAE3B,IAAI,EAAK,SAAS,KAAkB,EAAY,EAAK,KAAK,UAAU,EAAK,KAAK,YAAY,WAAW,EAAK,QACxG,IAAU,EAAK,IAAI;;AAIvB,OAAI,KAAW,KAAS,OAAO,KAAK,EAAM,CAAC,SAAS,GAAG;IACrD,IAAM,IAAU,EAAwB,GAAS,GAAO,EAAM,EAExD,IAAK,MADQ,KAAe,GACA,GAAS,EAAc;AACzD,MAAS,KAAK;KACZ;KACA;KACA,GAAI,MAAkB,KAAA,IAAyC,EAAE,GAA/B,EAAE,SAAS,GAAe;KAC5D,GAAI,MAAkB,KAAA,IAAyC,EAAE,GAA/B,EAAE,SAAS,GAAe;KAC5D,QAAQ;MACN,MAAM;MACN,MAAM,EAAK,IAAI,MAAM;MACrB,QAAQ,EAAK,IAAI,MAAM;MACxB;KACF,CAAC;;;;AAKR,KAAI,EAAK,SACP,MAAK,IAAM,KAAS,EAAK,SACvB,GAAa,GAAO,GAAU,GAAU,EAAY;;AAK1D,SAAS,EACP,GAC2C;CAC3C,IAAI,IAAe,GACf,IAAc;AAelB,QAAO;EACL,SAdY,EAAS,KAAK,MAAU;AACpC,OAAI,EAAM,SAAS,EACjB,SAAQ,EAAM,WAAW,IAAI,MAAM,GAAG,EAAM,WAAW,KAAK;AAE9D,OAAI,EAAM,SAAS,KAAgB,EAAM,KAAK;AAC5C,QAAc;IACd,IAAM,IAAM;AAEZ,WAAO,IAAI,EAAI,GADG,EAAoC,EAAM,YAAY,EAAE,CAAC,CAAC,QAChD,IAAI,EAAI;;AAEtC,UAAO;IACP,CAGe,KAAK,GAAG,CAAC,MAAM;EAC9B;EACD;;AAGH,SAAS,EAAY,GAA4B;AAE/C,QADI,OAAO,EAAK,QAAS,WAAiB,EAAK,OACxC,EAAK,KAAK;;AAGnB,SAAS,EACP,GACA,GACA,GACoB;CACpB,IAAM,IAA+B,EAAE,EACjC,IAAqB,uBACvB;AAEJ,SAAQ,IAAQ,EAAmB,KAAK,EAAQ,MAAM,OAAM;EAC1D,IAAM,IAAa,EAAM,IAAI,MAAM;AACnC,MAAI,CAAC,EAAY;EAEjB,IAAM,IAAY,EAAe,GAAY,GAAU,EAAY;AACnE,MAAI,EAAU,WAAW,EAAG;EAE5B,IAAM,IAAa,EAAQ,MAAM,GAAG,EAAM,MAAM,CAAC,MAAM,KAAK,CAAC,SAAS;AACtE,OAAK,IAAM,KAAO,EAChB,GAAS,KAAK;GACZ,GAAG;GACH,QAAQ;IACN,GAAG,EAAI;IACP,MAAM,EAAI,OAAO,OAAO;IACzB;GACF,CAAC;;AAIN,QAAO;;AAIT,SAAgB,EACd,GACA,GACA,GACoB;CACpB,IAAM,IAA+B,EAAE,EAEjC,EAAE,kBAAe,EAAS,GAAM,EAAE,aAAU,CAAC;AAQnD,KANI,EAAW,UAAU,OACvB,EAAa,EAAW,SAAS,KAAgC,GAAU,GAAU,EAAY,EAK/F,EAAW,UAAU,SAAS;EAChC,IAAM,IAAmB,EAAe,EAAW,SAAS,SAAS,GAAU,EAAY,EAErF,IADc,EAAW,SAAS,IACT,MAAM,OAAO,GACtC,IAAc,IAAI,IAAI,EAAS,KAAK,MAAM,EAAE,GAAG,CAAC;AACtD,OAAK,IAAM,KAAO,EAChB,CAAK,EAAY,IAAI,EAAI,GAAG,IAC1B,EAAS,KAAK;GACZ,GAAG;GACH,QAAQ;IACN,GAAG,EAAI;IACP,MAAM,EAAI,OAAO,OAAO;IACzB;GACF,CAAC;EAKN,IAAM,IAAmB,IAAI,IAAI,EAAS,KAAK,MAAM,EAAE,GAAG,CAAC,EACrD,IAAwB,EAA8B,EAAW,SAAS,SAAS,GAAU,EAAY;AAC/G,OAAK,IAAM,KAAO,EAChB,CAAK,EAAiB,IAAI,EAAI,GAAG,IAC/B,EAAS,KAAK;GACZ,GAAG;GACH,QAAQ;IACN,GAAG,EAAI;IACP,MAAM,EAAI,OAAO,OAAO;IACzB;GACF,CAAC;;AAKR,KAAI,EAAW,aAAa,SAAS;EACnC,IAAM,IAAiB,EAAe,EAAW,YAAY,SAAS,GAAU,EAAY,EAEtF,IADY,EAAW,YAAY,IACZ,MAAM,OAAO;AAC1C,OAAK,IAAM,KAAO,EAChB,GAAS,KAAK;GACZ,GAAG;GACH,QAAQ;IACN,GAAG,EAAI;IACP,MAAM,EAAI,OAAO,OAAO;IACzB;GACF,CAAC;;AAIN,KAAI,EAAW,QAAQ,SAAS;EAC9B,IAAM,IAAiB,EAAe,EAAW,OAAO,SAAS,GAAU,EAAY,EAEjF,IADY,EAAW,OAAO,IACP,MAAM,OAAO;AAC1C,OAAK,IAAM,KAAO,EAChB,GAAS,KAAK;GACZ,GAAG;GACH,QAAQ;IACN,GAAG,EAAI;IACP,MAAM,EAAI,OAAO,OAAO;IACzB;GACF,CAAC;;AAIN,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluenti/cli",
3
- "version": "0.3.3",
3
+ "version": "0.4.0-rc.0",
4
4
  "type": "module",
5
5
  "description": "Fluenti CLI — message extraction from Vue SFC & TSX, PO/JSON catalog compilation",
6
6
  "homepage": "https://fluenti.dev",
@@ -72,7 +72,7 @@
72
72
  "fast-glob": "^3",
73
73
  "gettext-parser": "^9",
74
74
  "jiti": "^2",
75
- "@fluenti/core": "0.3.3"
75
+ "@fluenti/core": "0.4.0-rc.0"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "@vue/compiler-sfc": "^3.5"
@@ -1 +0,0 @@
1
- {"version":3,"file":"compile-B4y2UPsX.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import type { CatalogData } from './catalog'\nimport { hashMessage, parse } from '@fluenti/core/internal'\nimport type { ASTNode, PluralNode, SelectNode, VariableNode, FunctionNode } from '@fluenti/core/internal'\n\nconst ICU_VAR_REGEX = /\\{(\\w+)\\}/g\nconst ICU_VAR_TEST = /\\{(\\w+)\\}/\n\nfunction hasVariables(message: string): boolean {\n return ICU_VAR_TEST.test(message)\n}\n\n\nfunction escapeStringLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\n/** Generate safe JS property access: `v.name` for identifiers, `v[0]` for numeric names */\nfunction propAccess(obj: string, name: string): string {\n return /^\\d/.test(name) ? `${obj}[${name}]` : `${obj}.${name}`\n}\n\nfunction escapeTemplateLiteral(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/`/g, '\\\\`')\n .replace(/\\$\\{/g, '\\\\${')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n}\n\nfunction messageToTemplateString(message: string): string {\n return message.replace(ICU_VAR_REGEX, (_match, name: string) => `\\${${propAccess('v', name)}}`)\n}\n\n\n// ─── ICU → JS code generation for split mode ───────────────────────────────\n\nconst ICU_PLURAL_SELECT_REGEX = /\\{(\\w+),\\s*(plural|select|selectordinal)\\s*,/\n\n/** Check if message contains ICU plural/select syntax */\nfunction hasIcuPluralOrSelect(message: string): boolean {\n return ICU_PLURAL_SELECT_REGEX.test(message)\n}\n\n/**\n * Compile an ICU AST node array into a JS expression string.\n * Used for generating static code (not runtime evaluation).\n */\nfunction astToJsExpression(nodes: ASTNode[], locale: string): string {\n if (nodes.length === 0) return \"''\"\n\n const parts = nodes.map((node) => astNodeToJs(node, locale))\n\n if (parts.length === 1) return parts[0]!\n return parts.join(' + ')\n}\n\nfunction astNodeToJs(node: ASTNode, locale: string): string {\n switch (node.type) {\n case 'text':\n return `'${escapeStringLiteral(node.value)}'`\n\n case 'variable':\n if (node.name === '#') return 'String(__c)'\n return `String(${propAccess('v', node.name)} ?? '{${node.name}}')`\n\n case 'plural':\n return pluralToJs(node as PluralNode, locale)\n\n case 'select':\n return selectToJs(node as SelectNode, locale)\n\n case 'function':\n return `String(${propAccess('v', node.variable)} ?? '')`\n }\n}\n\nfunction pluralToJs(node: PluralNode, locale: string): string {\n const offset = node.offset ?? 0\n const access = propAccess('v', node.variable)\n const countExpr = offset ? `(${access} - ${offset})` : access\n\n const lines: string[] = []\n lines.push(`((c) => { const __c = c; `)\n\n // Exact matches first\n const exactKeys = Object.keys(node.options).filter((k) => k.startsWith('='))\n if (exactKeys.length > 0) {\n for (const key of exactKeys) {\n const num = key.slice(1)\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (c === ${num}) return ${body}; `)\n }\n }\n\n // CLDR categories via Intl.PluralRules\n const cldrKeys = Object.keys(node.options).filter((k) => !k.startsWith('='))\n if (cldrKeys.length > 1 || (cldrKeys.length === 1 && cldrKeys[0] !== 'other')) {\n lines.push(`const __cat = new Intl.PluralRules('${escapeStringLiteral(locale)}').select(c); `)\n for (const key of cldrKeys) {\n if (key === 'other') continue\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (__cat === '${key}') return ${body}; `)\n }\n }\n\n // Fallback to 'other'\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(${countExpr})`)\n\n return lines.join('')\n}\n\nfunction selectToJs(node: SelectNode, locale: string): string {\n const lines: string[] = []\n lines.push(`((s) => { `)\n\n const keys = Object.keys(node.options).filter((k) => k !== 'other')\n for (const key of keys) {\n const body = astToJsExpression(node.options[key]!, locale)\n lines.push(`if (s === '${escapeStringLiteral(key)}') return ${body}; `)\n }\n\n const otherBody = node.options['other']\n ? astToJsExpression(node.options['other'], locale)\n : \"''\"\n lines.push(`return ${otherBody}; `)\n lines.push(`})(String(${propAccess('v', node.variable)} ?? ''))`)\n\n return lines.join('')\n}\n\n/**\n * Compile a catalog to ES module with tree-shakeable named exports.\n * Each message becomes a `/* @__PURE__ *​/` annotated named export.\n * A default export maps message IDs to their compiled values for runtime lookup.\n */\n/** Catalog format version. Bump when the compiled output format changes. */\nexport const CATALOG_VERSION = 1\n\nexport interface CompileStats {\n compiled: number\n missing: string[]\n}\n\nexport interface CompileOptions {\n skipFuzzy?: boolean\n}\n\nexport function compileCatalog(\n catalog: CatalogData,\n locale: string,\n allIds: string[],\n sourceLocale?: string,\n options?: CompileOptions,\n): { code: string; stats: CompileStats } {\n const lines: string[] = []\n lines.push(`// @fluenti/compiled v${CATALOG_VERSION}`)\n lines.push(`// @ts-nocheck`)\n const exportNames: Array<{ id: string; exportName: string }> = []\n let compiled = 0\n const missing: string[] = []\n\n const hashToId = new Map<string, string>()\n\n for (const id of allIds) {\n const hash = hashMessage(id)\n\n const existingId = hashToId.get(hash)\n if (existingId !== undefined && existingId !== id) {\n throw new Error(\n `Hash collision detected: messages \"${existingId}\" and \"${id}\" produce the same hash \"${hash}\"`,\n )\n }\n hashToId.set(hash, id)\n\n const exportName = `_${hash}`\n const entry = catalog[id]\n const translated = resolveCompiledMessage(entry, id, locale, sourceLocale, options?.skipFuzzy)\n\n if (translated === undefined) {\n lines.push(`export const ${exportName} = undefined`)\n missing.push(id)\n } else if (hasIcuPluralOrSelect(translated)) {\n // Parse ICU and compile to JS\n let ast\n try {\n ast = parse(translated)\n } catch (err) {\n console.warn(\n `[fluenti] Skipping malformed ICU translation for \"${id}\" (${locale}): ${(err as Error).message}`,\n )\n lines.push(`export const ${exportName} = undefined`)\n missing.push(id)\n exportNames.push({ id, exportName })\n continue\n }\n const jsExpr = astToJsExpression(ast, locale)\n lines.push(`export const ${exportName} = (v) => ${jsExpr}`)\n compiled++\n } else if (hasVariables(translated)) {\n const templateStr = messageToTemplateString(escapeTemplateLiteral(translated))\n lines.push(`export const ${exportName} = (v) => \\`${templateStr}\\``)\n compiled++\n } else {\n lines.push(`export const ${exportName} = '${escapeStringLiteral(translated)}'`)\n compiled++\n }\n\n exportNames.push({ id, exportName })\n }\n\n if (exportNames.length === 0) {\n return {\n code: `// @fluenti/compiled v${CATALOG_VERSION}\\n// empty catalog\\nexport default {}\\n`,\n stats: { compiled: 0, missing: [] },\n }\n }\n\n // Default export maps message IDs → compiled values for runtime lookup\n lines.push('')\n lines.push('export default {')\n for (const { id, exportName } of exportNames) {\n lines.push(` '${escapeStringLiteral(id)}': ${exportName},`)\n }\n lines.push('}')\n lines.push('')\n\n return { code: lines.join('\\n'), stats: { compiled, missing } }\n}\n\nfunction resolveCompiledMessage(\n entry: CatalogData[string] | undefined,\n id: string,\n locale: string,\n sourceLocale: string | undefined,\n skipFuzzy?: boolean,\n): string | undefined {\n const effectiveSourceLocale = sourceLocale ?? locale\n\n if (!entry) {\n return undefined\n }\n\n if (skipFuzzy && entry.fuzzy) {\n return undefined\n }\n\n if (entry.translation !== undefined && entry.translation.length > 0) {\n return entry.translation\n }\n\n if (locale === effectiveSourceLocale) {\n return entry.message ?? id\n }\n\n return undefined\n}\n\n/**\n * Generate the index module that exports locale list and lazy loaders.\n */\nexport function compileIndex(locales: string[], _catalogDir: string): string {\n const lines: string[] = []\n lines.push(`export const locales = ${JSON.stringify(locales)}`)\n lines.push('')\n lines.push('export const loaders = {')\n for (const locale of locales) {\n lines.push(` '${escapeStringLiteral(locale)}': () => import('./${escapeStringLiteral(locale)}.js'),`)\n }\n lines.push('}')\n lines.push('')\n return lines.join('\\n')\n}\n\n/**\n * Collect the union of all message IDs across all locale catalogs.\n * Ensures every locale file exports the same names.\n */\nexport function collectAllIds(catalogs: Record<string, CatalogData>): string[] {\n const idSet = new Set<string>()\n for (const catalog of Object.values(catalogs)) {\n for (const [id, entry] of Object.entries(catalog)) {\n if (!entry.obsolete) {\n idSet.add(id)\n }\n }\n }\n return [...idSet].sort()\n}\n\n// ─── Type-safe message ID generation ─────────────────────────────────────────\n\nexport interface MessageVariable {\n name: string\n type: string\n}\n\n/**\n * Extract variable names and their TypeScript types from an ICU message string.\n */\nexport function extractMessageVariables(message: string): MessageVariable[] {\n const ast = parse(message)\n const vars = new Map<string, string>()\n collectVariablesFromNodes(ast, vars)\n return [...vars.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([name, type]) => ({ name, type }))\n}\n\nfunction collectVariablesFromNodes(nodes: ASTNode[], vars: Map<string, string>): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'variable':\n if (node.name !== '#' && !vars.has(node.name)) {\n vars.set((node as VariableNode).name, 'string | number')\n }\n break\n case 'plural': {\n const pn = node as PluralNode\n // Plural variable is always a number\n vars.set(pn.variable, 'number')\n // Recurse into plural option bodies\n for (const optionNodes of Object.values(pn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'select': {\n const sn = node as SelectNode\n const keys = Object.keys(sn.options).filter((k) => k !== 'other')\n const hasOther = 'other' in sn.options\n const literalTypes = keys.map((k) => `'${k}'`).join(' | ')\n const selectType = hasOther\n ? (keys.length > 0 ? `${literalTypes} | string` : 'string')\n : (keys.length > 0 ? literalTypes : 'string')\n vars.set(sn.variable, selectType)\n // Recurse into select option bodies\n for (const optionNodes of Object.values(sn.options)) {\n collectVariablesFromNodes(optionNodes, vars)\n }\n break\n }\n case 'function':\n if (!vars.has((node as FunctionNode).variable)) {\n vars.set((node as FunctionNode).variable, 'string | number')\n }\n break\n case 'text':\n break\n }\n }\n}\n\n/**\n * Generate a TypeScript declaration file with MessageId union and MessageValues interface.\n */\nexport function compileTypeDeclaration(\n allIds: string[],\n catalogs: Record<string, CatalogData>,\n sourceLocale: string,\n): string {\n const lines: string[] = []\n lines.push('// Auto-generated by @fluenti/cli — do not edit')\n lines.push('')\n lines.push('export type { LocalizedString } from \\'@fluenti/core\\'')\n lines.push('')\n\n // MessageId union\n if (allIds.length === 0) {\n lines.push('export type MessageId = never')\n } else {\n lines.push('export type MessageId =')\n for (const id of allIds) {\n lines.push(` | '${escapeStringLiteral(id)}'`)\n }\n }\n\n lines.push('')\n\n // MessageValues interface\n lines.push('export interface MessageValues {')\n for (const id of allIds) {\n // Use source locale catalog to get the message for variable extraction\n const sourceCatalog = catalogs[sourceLocale]\n const entry = sourceCatalog?.[id]\n const message = entry?.message ?? id\n const vars = extractMessageVariables(message)\n\n const escapedId = escapeStringLiteral(id)\n if (vars.length === 0) {\n lines.push(` '${escapedId}': Record<string, never>`)\n } else {\n const fields = vars.map((v) => `${v.name}: ${v.type}`).join('; ')\n lines.push(` '${escapedId}': { ${fields} }`)\n }\n }\n lines.push('}')\n lines.push('')\n\n // Module augmentation: auto-wire MessageId and MessageValues into CompileTimeT\n // Locale union from catalog keys\n const localeKeys = Object.keys(catalogs).map((l) => `'${escapeStringLiteral(l)}'`).join(' | ')\n\n lines.push('// Auto-wiring: narrows t() and setLocale() to compiled types')\n lines.push(\"declare module '@fluenti/core' {\")\n lines.push(' interface FluentiTypeConfig {')\n lines.push(` locale: ${localeKeys || 'string'}`)\n lines.push(' messageIds: MessageId')\n lines.push(' messageValues: MessageValues')\n lines.push(' }')\n lines.push('}')\n lines.push('')\n\n return lines.join('\\n')\n}\n"],"mappings":"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"}