@fluenti/cli 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/src/ai-provider.d.ts.map +1 -0
- package/dist/{catalog.d.ts → cli/src/catalog.d.ts} +1 -1
- package/dist/cli/src/catalog.d.ts.map +1 -0
- package/dist/cli/src/check.d.ts.map +1 -0
- package/dist/cli/src/cli.d.ts.map +1 -0
- package/dist/cli/src/codemod.d.ts +19 -0
- package/dist/cli/src/codemod.d.ts.map +1 -0
- package/dist/cli/src/compile-cache.d.ts.map +1 -0
- package/dist/cli/src/compile-runner.d.ts.map +1 -0
- package/dist/cli/src/compile-worker.d.ts.map +1 -0
- package/dist/cli/src/compile.d.ts.map +1 -0
- package/dist/cli/src/config-loader.d.ts.map +1 -0
- package/dist/{config.d.ts → cli/src/config.d.ts} +1 -1
- package/dist/cli/src/config.d.ts.map +1 -0
- package/dist/cli/src/doctor.d.ts +19 -0
- package/dist/cli/src/doctor.d.ts.map +1 -0
- package/dist/{extract-cache.d.ts → cli/src/extract-cache.d.ts} +1 -1
- package/dist/cli/src/extract-cache.d.ts.map +1 -0
- package/dist/cli/src/extract-runner.d.ts.map +1 -0
- package/dist/cli/src/glossary.d.ts.map +1 -0
- package/dist/{index.d.ts → cli/src/index.d.ts} +6 -2
- package/dist/cli/src/index.d.ts.map +1 -0
- package/dist/cli/src/init.d.ts.map +1 -0
- package/dist/cli/src/json-format.d.ts.map +1 -0
- package/dist/cli/src/lint.d.ts.map +1 -0
- package/dist/cli/src/migrate.d.ts.map +1 -0
- package/dist/cli/src/parallel-compile.d.ts.map +1 -0
- package/dist/cli/src/po-format.d.ts.map +1 -0
- package/dist/cli/src/stats-format.d.ts.map +1 -0
- package/dist/cli/src/translate-parse.d.ts.map +1 -0
- package/dist/cli/src/translate-prompt.d.ts.map +1 -0
- package/dist/cli/src/translate.d.ts.map +1 -0
- package/dist/{tsx-extractor.d.ts → cli/src/tsx-extractor.d.ts} +1 -1
- package/dist/cli/src/tsx-extractor.d.ts.map +1 -0
- package/dist/cli/src/validation.d.ts.map +1 -0
- package/dist/{vue-extractor.d.ts → cli/src/vue-extractor.d.ts} +1 -1
- package/dist/cli/src/vue-extractor.d.ts.map +1 -0
- package/dist/cli.cjs +10 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +333 -406
- package/dist/cli.js.map +1 -1
- package/dist/compile-C3VLvhUf.cjs +8 -0
- package/dist/{compile-kXClO6q4.cjs.map → compile-C3VLvhUf.cjs.map} +1 -1
- package/dist/{compile-CdA4EZ-p.js → compile-CZVpE5Md.js} +2 -2
- package/dist/{compile-CdA4EZ-p.js.map → compile-CZVpE5Md.js.map} +1 -1
- package/dist/compile-worker.cjs +1 -1
- package/dist/compile-worker.cjs.map +1 -1
- package/dist/compile-worker.js +1 -1
- package/dist/doctor-BqXXxyST.js +670 -0
- package/dist/doctor-BqXXxyST.js.map +1 -0
- package/dist/doctor-xp8WS8sr.cjs +24 -0
- package/dist/doctor-xp8WS8sr.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -64
- package/dist/index.js.map +1 -1
- package/dist/tsx-extractor-AOjsbOmp.cjs +2 -0
- package/dist/{tsx-extractor-B0vFXziu.cjs.map → tsx-extractor-AOjsbOmp.cjs.map} +1 -1
- package/dist/tsx-extractor-C-HZNobu.js.map +1 -1
- package/dist/vue-extractor.cjs +1 -1
- package/dist/vue-extractor.cjs.map +1 -1
- package/dist/vue-extractor.js.map +1 -1
- package/package.json +2 -2
- package/dist/ai-provider.d.ts.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/check.d.ts.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/compile-cache.d.ts.map +0 -1
- package/dist/compile-kXClO6q4.cjs +0 -8
- package/dist/compile-runner.d.ts.map +0 -1
- package/dist/compile-worker.d.ts.map +0 -1
- package/dist/compile.d.ts.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/extract-cache-BioSaoFo.cjs +0 -10
- package/dist/extract-cache-BioSaoFo.cjs.map +0 -1
- package/dist/extract-cache-C-MI1_ll.js +0 -325
- package/dist/extract-cache-C-MI1_ll.js.map +0 -1
- package/dist/extract-cache.d.ts.map +0 -1
- package/dist/extract-runner.d.ts.map +0 -1
- package/dist/glossary.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/init.d.ts.map +0 -1
- package/dist/json-format.d.ts.map +0 -1
- package/dist/lint.d.ts.map +0 -1
- package/dist/migrate.d.ts.map +0 -1
- package/dist/parallel-compile.d.ts.map +0 -1
- package/dist/po-format.d.ts.map +0 -1
- package/dist/stats-format.d.ts.map +0 -1
- package/dist/translate-parse.d.ts.map +0 -1
- package/dist/translate-prompt.d.ts.map +0 -1
- package/dist/translate.d.ts.map +0 -1
- package/dist/tsx-extractor-B0vFXziu.cjs +0 -2
- package/dist/tsx-extractor.d.ts.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/vue-extractor.d.ts.map +0 -1
- /package/dist/{ai-provider.d.ts → cli/src/ai-provider.d.ts} +0 -0
- /package/dist/{check.d.ts → cli/src/check.d.ts} +0 -0
- /package/dist/{cli.d.ts → cli/src/cli.d.ts} +0 -0
- /package/dist/{compile-cache.d.ts → cli/src/compile-cache.d.ts} +0 -0
- /package/dist/{compile-runner.d.ts → cli/src/compile-runner.d.ts} +0 -0
- /package/dist/{compile-worker.d.ts → cli/src/compile-worker.d.ts} +0 -0
- /package/dist/{compile.d.ts → cli/src/compile.d.ts} +0 -0
- /package/dist/{config-loader.d.ts → cli/src/config-loader.d.ts} +0 -0
- /package/dist/{extract-runner.d.ts → cli/src/extract-runner.d.ts} +0 -0
- /package/dist/{glossary.d.ts → cli/src/glossary.d.ts} +0 -0
- /package/dist/{init.d.ts → cli/src/init.d.ts} +0 -0
- /package/dist/{json-format.d.ts → cli/src/json-format.d.ts} +0 -0
- /package/dist/{lint.d.ts → cli/src/lint.d.ts} +0 -0
- /package/dist/{migrate.d.ts → cli/src/migrate.d.ts} +0 -0
- /package/dist/{parallel-compile.d.ts → cli/src/parallel-compile.d.ts} +0 -0
- /package/dist/{po-format.d.ts → cli/src/po-format.d.ts} +0 -0
- /package/dist/{stats-format.d.ts → cli/src/stats-format.d.ts} +0 -0
- /package/dist/{translate-parse.d.ts → cli/src/translate-parse.d.ts} +0 -0
- /package/dist/{translate-prompt.d.ts → cli/src/translate-prompt.d.ts} +0 -0
- /package/dist/{translate.d.ts → cli/src/translate.d.ts} +0 -0
- /package/dist/{validation.d.ts → cli/src/validation.d.ts} +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const e=require(`./doctor-xp8WS8sr.cjs`);let t=require(`@fluenti/core/compiler`);var n=e.p({CATALOG_VERSION:()=>1,collectAllIds:()=>y,compileCatalog:()=>g,compileIndex:()=>v,compileTypeDeclaration:()=>S,extractMessageVariables:()=>b}),r=/\{(\w+)\}/g,i=/\{(\w+)\}/;function a(e){return i.test(e)}function o(e){return e.replace(/\\/g,`\\\\`).replace(/'/g,`\\'`).replace(/\n/g,`\\n`).replace(/\r/g,`\\r`)}function s(e,t){return/^\d/.test(t)?`${e}['${t}']`:`${e}.${t}`}function c(e){return e.replace(/\\/g,`\\\\`).replace(/`/g,"\\`").replace(/\$\{/g,"\\${").replace(/\n/g,`\\n`).replace(/\r/g,`\\r`)}function l(e){r.lastIndex=0;let t=[],n=0,i;for(;(i=r.exec(e))!==null;)t.push(c(e.slice(n,i.index))),t.push(`\${${s(`v`,i[1])}}`),n=r.lastIndex;return t.push(c(e.slice(n))),t.join(``)}var u=/\{(\w+),\s*(plural|select|selectordinal)\s*,/;function d(e){return u.test(e)}function f(e,t){if(e.length===0)return`''`;let n=e.map(e=>p(e,t));return n.length===1?n[0]:n.join(` + `)}function p(e,t){switch(e.type){case`text`:return`'${o(e.value)}'`;case`variable`:return e.name===`#`?`String(__c)`:`String(${s(`v`,e.name)} ?? '{${e.name}}')`;case`plural`:return m(e,t);case`select`:return h(e,t);case`function`:return`String(${s(`v`,e.variable)} ?? '')`}}function m(e,t){let n=e.offset??0,r=s(`v`,e.variable),i=n?`(${r} - ${n})`:r,a=[];a.push(`((c) => { const __c = c; `);let c=Object.keys(e.options).filter(e=>e.startsWith(`=`));if(c.length>0)for(let n of c){let r=n.slice(1),i=f(e.options[n],t);a.push(`if (c === ${r}) return ${i}; `)}let l=Object.keys(e.options).filter(e=>!e.startsWith(`=`));if(l.length>1||l.length===1&&l[0]!==`other`){a.push(`const __cat = new Intl.PluralRules('${o(t)}').select(c); `);for(let n of l){if(n===`other`)continue;let r=f(e.options[n],t);a.push(`if (__cat === '${o(n)}') return ${r}; `)}}let u=e.options.other?f(e.options.other,t):`''`;return a.push(`return ${u}; `),a.push(`})(${i})`),a.join(``)}function h(e,t){let n=[];n.push(`((s) => { `);let r=Object.keys(e.options).filter(e=>e!==`other`);for(let i of r){let r=f(e.options[i],t);n.push(`if (s === '${o(i)}') return ${r}; `)}let i=e.options.other?f(e.options.other,t):`''`;return n.push(`return ${i}; `),n.push(`})(String(${s(`v`,e.variable)} ?? ''))`),n.join(``)}function g(e,n,r,i,s){let c=[];c.push(`// @fluenti/compiled v1`),c.push(`// @ts-nocheck`);let u=[],p=0,m=[],h=new Map;for(let g of r){let r=(0,t.hashMessage)(g),v=h.get(r);if(v!==void 0&&v!==g)throw Error(`Hash collision detected: messages "${v}" and "${g}" produce the same hash "${r}"`);h.set(r,g);let y=`_${r}`,b=e[g],x=_(b,g,n,i,s?.skipFuzzy);if(x===void 0)c.push(`export const ${y} = undefined`),m.push(g);else if(d(x)){let e;try{e=(0,t.parse)(x)}catch(e){console.warn(`[fluenti] Skipping malformed ICU translation for "${g}" (${n}): ${e.message}`),c.push(`export const ${y} = undefined`),m.push(g),u.push({id:g,exportName:y});continue}let r=f(e,n);c.push(`export const ${y} = (v) => ${r}`),p++}else if(a(x)){let e=l(x);c.push(`export const ${y} = (v) => \`${e}\``),p++}else c.push(`export const ${y} = '${o(x)}'`),p++;u.push({id:g,exportName:y})}if(u.length===0)return{code:`// @fluenti/compiled v1
|
|
2
|
+
// empty catalog
|
|
3
|
+
export default {}
|
|
4
|
+
`,stats:{compiled:0,missing:[]}};c.push(``),c.push(`export default {`);for(let{id:e,exportName:t}of u)c.push(` '${o(e)}': ${t},`);return c.push(`}`),c.push(``),{code:c.join(`
|
|
5
|
+
`),stats:{compiled:p,missing:m}}}function _(e,t,n,r,i){let a=r??n;if(e&&!(i&&e.fuzzy)){if(e.translation!==void 0&&e.translation.length>0)return e.translation;if(n===a)return e.message??t}}function v(e,t){let n=[];n.push(`export const locales = ${JSON.stringify(e)}`),n.push(``),n.push(`export const loaders = {`);for(let t of e)n.push(` '${o(t)}': () => import('./${o(t)}.js'),`);return n.push(`}`),n.push(``),n.join(`
|
|
6
|
+
`)}function y(e){let t=new Set;for(let n of Object.values(e))for(let[e,r]of Object.entries(n))r.obsolete||t.add(e);return[...t].sort()}function b(e){let n=(0,t.parse)(e),r=new Map;return x(n,r),[...r.entries()].sort(([e],[t])=>e.localeCompare(t)).map(([e,t])=>({name:e,type:t}))}function x(e,t){for(let n of e)switch(n.type){case`variable`:n.name!==`#`&&!t.has(n.name)&&t.set(n.name,`string | number`);break;case`plural`:{let e=n;t.set(e.variable,`number`);for(let n of Object.values(e.options))x(n,t);break}case`select`:{let e=n,r=Object.keys(e.options).filter(e=>e!==`other`),i=`other`in e.options,a=r.map(e=>`'${e}'`).join(` | `),o=i?r.length>0?`${a} | string`:`string`:r.length>0?a:`string`;t.set(e.variable,o);for(let n of Object.values(e.options))x(n,t);break}case`function`:t.has(n.variable)||t.set(n.variable,`string | number`);break;case`text`:break}}function S(e,t,n){let r=[];if(r.push(`// Auto-generated by @fluenti/cli — do not edit`),r.push(``),r.push(`export type { LocalizedString } from '@fluenti/core'`),r.push(``),e.length===0)r.push(`export type MessageId = never`);else{r.push(`export type MessageId =`);for(let t of e)r.push(` | '${o(t)}'`)}r.push(``),r.push(`export interface MessageValues {`);for(let i of e){let e=b(t[n]?.[i]?.message??i),a=o(i);if(e.length===0)r.push(` '${a}': Record<string, never>`);else{let t=e.map(e=>`${e.name}: ${e.type}`).join(`; `);r.push(` '${a}': { ${t} }`)}}r.push(`}`),r.push(``);let i=Object.keys(t).map(e=>`'${o(e)}'`).join(` | `);return r.push(`// Auto-wiring: narrows t() and setLocale() to compiled types`),r.push(`declare module '@fluenti/core' {`),r.push(` interface FluentiTypeConfig {`),r.push(` locale: ${i||`string`}`),r.push(` messageIds: MessageId`),r.push(` messageValues: MessageValues`),r.push(` }`),r.push(`}`),r.push(``),r.join(`
|
|
7
|
+
`)}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return y}});
|
|
8
|
+
//# sourceMappingURL=compile-C3VLvhUf.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-kXClO6q4.cjs","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 valid identifiers, `v['name']` for others */\nfunction propAccess(obj: string, name: string): string {\n // Names starting with a digit are not valid JS identifiers; use quoted bracket notation.\n // e.g. {0} → v['0'], {1st} → v['1st']. Pure integers (v['0']) work the same as v[0].\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\n/**\n * Convert a simple ICU message (only `{varName}` placeholders) into a JS template literal body.\n * Static segments are escaped independently so literal `${`, backticks, etc. are preserved\n * without interfering with the ICU variable interpolations that are inserted afterwards.\n */\nfunction messageToTemplateString(message: string): string {\n ICU_VAR_REGEX.lastIndex = 0\n const parts: string[] = []\n let lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = ICU_VAR_REGEX.exec(message)) !== null) {\n parts.push(escapeTemplateLiteral(message.slice(lastIndex, m.index)))\n parts.push(`\\${${propAccess('v', m[1]!)}}`)\n lastIndex = ICU_VAR_REGEX.lastIndex\n }\n parts.push(escapeTemplateLiteral(message.slice(lastIndex)))\n return parts.join('')\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 === '${escapeStringLiteral(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(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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"}
|
|
1
|
+
{"version":3,"file":"compile-C3VLvhUf.cjs","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import type { CatalogData } from './catalog'\nimport { hashMessage, parse } from '@fluenti/core/compiler'\nimport type { ASTNode, PluralNode, SelectNode, VariableNode, FunctionNode } from '@fluenti/core/compiler'\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 valid identifiers, `v['name']` for others */\nfunction propAccess(obj: string, name: string): string {\n // Names starting with a digit are not valid JS identifiers; use quoted bracket notation.\n // e.g. {0} → v['0'], {1st} → v['1st']. Pure integers (v['0']) work the same as v[0].\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\n/**\n * Convert a simple ICU message (only `{varName}` placeholders) into a JS template literal body.\n * Static segments are escaped independently so literal `${`, backticks, etc. are preserved\n * without interfering with the ICU variable interpolations that are inserted afterwards.\n */\nfunction messageToTemplateString(message: string): string {\n ICU_VAR_REGEX.lastIndex = 0\n const parts: string[] = []\n let lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = ICU_VAR_REGEX.exec(message)) !== null) {\n parts.push(escapeTemplateLiteral(message.slice(lastIndex, m.index)))\n parts.push(`\\${${propAccess('v', m[1]!)}}`)\n lastIndex = ICU_VAR_REGEX.lastIndex\n }\n parts.push(escapeTemplateLiteral(message.slice(lastIndex)))\n return parts.join('')\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 === '${escapeStringLiteral(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(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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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hashMessage as e, parse as t } from "@fluenti/core/
|
|
1
|
+
import { hashMessage as e, parse as t } from "@fluenti/core/compiler";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var n = Object.defineProperty, r = /* @__PURE__ */ ((e, t) => {
|
|
4
4
|
let r = {};
|
|
@@ -202,4 +202,4 @@ function C(e, t, n) {
|
|
|
202
202
|
//#endregion
|
|
203
203
|
export { r as a, C as i, _ as n, y as r, b as t };
|
|
204
204
|
|
|
205
|
-
//# sourceMappingURL=compile-
|
|
205
|
+
//# sourceMappingURL=compile-CZVpE5Md.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-CdA4EZ-p.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 valid identifiers, `v['name']` for others */\nfunction propAccess(obj: string, name: string): string {\n // Names starting with a digit are not valid JS identifiers; use quoted bracket notation.\n // e.g. {0} → v['0'], {1st} → v['1st']. Pure integers (v['0']) work the same as v[0].\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\n/**\n * Convert a simple ICU message (only `{varName}` placeholders) into a JS template literal body.\n * Static segments are escaped independently so literal `${`, backticks, etc. are preserved\n * without interfering with the ICU variable interpolations that are inserted afterwards.\n */\nfunction messageToTemplateString(message: string): string {\n ICU_VAR_REGEX.lastIndex = 0\n const parts: string[] = []\n let lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = ICU_VAR_REGEX.exec(message)) !== null) {\n parts.push(escapeTemplateLiteral(message.slice(lastIndex, m.index)))\n parts.push(`\\${${propAccess('v', m[1]!)}}`)\n lastIndex = ICU_VAR_REGEX.lastIndex\n }\n parts.push(escapeTemplateLiteral(message.slice(lastIndex)))\n return parts.join('')\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 === '${escapeStringLiteral(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(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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"}
|
|
1
|
+
{"version":3,"file":"compile-CZVpE5Md.js","names":[],"sources":["../src/compile.ts"],"sourcesContent":["import type { CatalogData } from './catalog'\nimport { hashMessage, parse } from '@fluenti/core/compiler'\nimport type { ASTNode, PluralNode, SelectNode, VariableNode, FunctionNode } from '@fluenti/core/compiler'\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 valid identifiers, `v['name']` for others */\nfunction propAccess(obj: string, name: string): string {\n // Names starting with a digit are not valid JS identifiers; use quoted bracket notation.\n // e.g. {0} → v['0'], {1st} → v['1st']. Pure integers (v['0']) work the same as v[0].\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\n/**\n * Convert a simple ICU message (only `{varName}` placeholders) into a JS template literal body.\n * Static segments are escaped independently so literal `${`, backticks, etc. are preserved\n * without interfering with the ICU variable interpolations that are inserted afterwards.\n */\nfunction messageToTemplateString(message: string): string {\n ICU_VAR_REGEX.lastIndex = 0\n const parts: string[] = []\n let lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = ICU_VAR_REGEX.exec(message)) !== null) {\n parts.push(escapeTemplateLiteral(message.slice(lastIndex, m.index)))\n parts.push(`\\${${propAccess('v', m[1]!)}}`)\n lastIndex = ICU_VAR_REGEX.lastIndex\n }\n parts.push(escapeTemplateLiteral(message.slice(lastIndex)))\n return parts.join('')\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 === '${escapeStringLiteral(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(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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"}
|
package/dist/compile-worker.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`./
|
|
1
|
+
require(`./doctor-xp8WS8sr.cjs`);const e=require(`./compile-C3VLvhUf.cjs`);let t=require(`node:worker_threads`);t.parentPort.on(`message`,n=>{try{let{code:r,stats:i}=e.n(n.catalog,n.locale,n.allIds,n.sourceLocale,n.options);t.parentPort.postMessage({locale:n.locale,code:r,stats:i})}catch(e){t.parentPort.postMessage({locale:n.locale,code:``,stats:{compiled:0,missing:[]},error:e instanceof Error?e.message:String(e)})}});
|
|
2
2
|
//# sourceMappingURL=compile-worker.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-worker.cjs","names":[],"sources":["../src/compile-worker.ts"],"sourcesContent":["import { parentPort } from 'node:worker_threads'\nimport { compileCatalog } from './compile'\nimport type { CatalogData } from './catalog'\nimport type { CompileOptions } from './compile'\n\nexport interface CompileWorkerRequest {\n locale: string\n catalog: CatalogData\n allIds: string[]\n sourceLocale: string\n options?: CompileOptions | undefined\n}\n\nexport interface CompileWorkerResponse {\n locale: string\n code: string\n stats: { compiled: number; missing: string[] }\n error?: string\n}\n\nparentPort!.on('message', (req: CompileWorkerRequest) => {\n try {\n const { code, stats } = compileCatalog(req.catalog, req.locale, req.allIds, req.sourceLocale, req.options)\n parentPort!.postMessage({ locale: req.locale, code, stats } satisfies CompileWorkerResponse)\n } catch (err) {\n parentPort!.postMessage({\n locale: req.locale,\n code: '',\n stats: { compiled: 0, missing: [] },\n error: err instanceof Error ? err.message : String(err),\n } satisfies CompileWorkerResponse)\n }\n})\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile-worker.cjs","names":[],"sources":["../src/compile-worker.ts"],"sourcesContent":["import { parentPort } from 'node:worker_threads'\nimport { compileCatalog } from './compile'\nimport type { CatalogData } from './catalog'\nimport type { CompileOptions } from './compile'\n\nexport interface CompileWorkerRequest {\n locale: string\n catalog: CatalogData\n allIds: string[]\n sourceLocale: string\n options?: CompileOptions | undefined\n}\n\nexport interface CompileWorkerResponse {\n locale: string\n code: string\n stats: { compiled: number; missing: string[] }\n error?: string\n}\n\nparentPort!.on('message', (req: CompileWorkerRequest) => {\n try {\n const { code, stats } = compileCatalog(req.catalog, req.locale, req.allIds, req.sourceLocale, req.options)\n parentPort!.postMessage({ locale: req.locale, code, stats } satisfies CompileWorkerResponse)\n } catch (err) {\n parentPort!.postMessage({\n locale: req.locale,\n code: '',\n stats: { compiled: 0, missing: [] },\n error: err instanceof Error ? err.message : String(err),\n } satisfies CompileWorkerResponse)\n }\n})\n"],"mappings":"gHAoBA,EAAA,WAAY,GAAG,UAAY,GAA8B,CACvD,GAAI,CACF,GAAM,CAAE,OAAM,SAAU,EAAA,EAAe,EAAI,QAAS,EAAI,OAAQ,EAAI,OAAQ,EAAI,aAAc,EAAI,QAAQ,CAC1G,EAAA,WAAY,YAAY,CAAE,OAAQ,EAAI,OAAQ,OAAM,QAAO,CAAiC,OACrF,EAAK,CACZ,EAAA,WAAY,YAAY,CACtB,OAAQ,EAAI,OACZ,KAAM,GACN,MAAO,CAAE,SAAU,EAAG,QAAS,EAAE,CAAE,CACnC,MAAO,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,CACxD,CAAiC,GAEpC"}
|
package/dist/compile-worker.js
CHANGED