@fluenti/cli 0.6.1 → 0.6.3

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 (90) hide show
  1. package/dist/cli/src/ai-provider.d.ts +0 -1
  2. package/dist/cli/src/catalog.d.ts +0 -1
  3. package/dist/cli/src/check.d.ts +0 -1
  4. package/dist/cli/src/cli.d.ts +0 -1
  5. package/dist/cli/src/codemod.d.ts +0 -1
  6. package/dist/cli/src/compile-cache.d.ts +0 -1
  7. package/dist/cli/src/compile-runner.d.ts +0 -1
  8. package/dist/cli/src/compile-worker.d.ts +0 -1
  9. package/dist/cli/src/compile.d.ts +0 -1
  10. package/dist/cli/src/config-loader.d.ts +0 -1
  11. package/dist/cli/src/config.d.ts +0 -1
  12. package/dist/cli/src/doctor.d.ts +0 -1
  13. package/dist/cli/src/extract-cache.d.ts +0 -1
  14. package/dist/cli/src/extract-path.d.ts +18 -0
  15. package/dist/cli/src/extract-runner.d.ts +0 -1
  16. package/dist/cli/src/extract-workflow.d.ts +17 -0
  17. package/dist/cli/src/glossary.d.ts +0 -1
  18. package/dist/cli/src/index.d.ts +0 -1
  19. package/dist/cli/src/init.d.ts +0 -1
  20. package/dist/cli/src/json-format.d.ts +0 -1
  21. package/dist/cli/src/lint.d.ts +0 -1
  22. package/dist/cli/src/migrate.d.ts +0 -1
  23. package/dist/cli/src/parallel-compile.d.ts +0 -1
  24. package/dist/cli/src/po-format.d.ts +0 -1
  25. package/dist/cli/src/stats-format.d.ts +0 -1
  26. package/dist/cli/src/translate-parse.d.ts +0 -1
  27. package/dist/cli/src/translate-prompt.d.ts +0 -1
  28. package/dist/cli/src/translate.d.ts +0 -1
  29. package/dist/cli/src/tsx-extractor.d.ts +0 -1
  30. package/dist/cli/src/validation.d.ts +0 -1
  31. package/dist/cli/src/vue-extractor.d.ts +0 -1
  32. package/dist/cli.cjs +10 -11
  33. package/dist/cli.js +270 -301
  34. package/dist/{compile-C3VLvhUf.cjs → compile-1ie6pZBL.cjs} +2 -3
  35. package/dist/compile-CZVpE5Md.js +0 -2
  36. package/dist/compile-worker.cjs +1 -2
  37. package/dist/compile-worker.js +0 -2
  38. package/dist/doctor-BjxAnm7z.cjs +23 -0
  39. package/dist/{doctor-BqXXxyST.js → doctor-CLfvuKpJ.js} +288 -183
  40. package/dist/index.cjs +1 -2
  41. package/dist/index.js +17 -81
  42. package/dist/tsx-extractor-C-HZNobu.js +0 -2
  43. package/dist/{tsx-extractor-AOjsbOmp.cjs → tsx-extractor-DFptqjVk.cjs} +1 -2
  44. package/dist/vue-extractor.cjs +2 -3
  45. package/dist/vue-extractor.js +0 -2
  46. package/package.json +2 -2
  47. package/dist/cli/src/ai-provider.d.ts.map +0 -1
  48. package/dist/cli/src/catalog.d.ts.map +0 -1
  49. package/dist/cli/src/check.d.ts.map +0 -1
  50. package/dist/cli/src/cli.d.ts.map +0 -1
  51. package/dist/cli/src/codemod.d.ts.map +0 -1
  52. package/dist/cli/src/compile-cache.d.ts.map +0 -1
  53. package/dist/cli/src/compile-runner.d.ts.map +0 -1
  54. package/dist/cli/src/compile-worker.d.ts.map +0 -1
  55. package/dist/cli/src/compile.d.ts.map +0 -1
  56. package/dist/cli/src/config-loader.d.ts.map +0 -1
  57. package/dist/cli/src/config.d.ts.map +0 -1
  58. package/dist/cli/src/doctor.d.ts.map +0 -1
  59. package/dist/cli/src/extract-cache.d.ts.map +0 -1
  60. package/dist/cli/src/extract-runner.d.ts.map +0 -1
  61. package/dist/cli/src/glossary.d.ts.map +0 -1
  62. package/dist/cli/src/index.d.ts.map +0 -1
  63. package/dist/cli/src/init.d.ts.map +0 -1
  64. package/dist/cli/src/json-format.d.ts.map +0 -1
  65. package/dist/cli/src/lint.d.ts.map +0 -1
  66. package/dist/cli/src/migrate.d.ts.map +0 -1
  67. package/dist/cli/src/parallel-compile.d.ts.map +0 -1
  68. package/dist/cli/src/po-format.d.ts.map +0 -1
  69. package/dist/cli/src/stats-format.d.ts.map +0 -1
  70. package/dist/cli/src/translate-parse.d.ts.map +0 -1
  71. package/dist/cli/src/translate-prompt.d.ts.map +0 -1
  72. package/dist/cli/src/translate.d.ts.map +0 -1
  73. package/dist/cli/src/tsx-extractor.d.ts.map +0 -1
  74. package/dist/cli/src/validation.d.ts.map +0 -1
  75. package/dist/cli/src/vue-extractor.d.ts.map +0 -1
  76. package/dist/cli.cjs.map +0 -1
  77. package/dist/cli.js.map +0 -1
  78. package/dist/compile-C3VLvhUf.cjs.map +0 -1
  79. package/dist/compile-CZVpE5Md.js.map +0 -1
  80. package/dist/compile-worker.cjs.map +0 -1
  81. package/dist/compile-worker.js.map +0 -1
  82. package/dist/doctor-BqXXxyST.js.map +0 -1
  83. package/dist/doctor-xp8WS8sr.cjs +0 -24
  84. package/dist/doctor-xp8WS8sr.cjs.map +0 -1
  85. package/dist/index.cjs.map +0 -1
  86. package/dist/index.js.map +0 -1
  87. package/dist/tsx-extractor-AOjsbOmp.cjs.map +0 -1
  88. package/dist/tsx-extractor-C-HZNobu.js.map +0 -1
  89. package/dist/vue-extractor.cjs.map +0 -1
  90. package/dist/vue-extractor.js.map +0 -1
@@ -1 +0,0 @@
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 +0,0 @@
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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"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"compile-worker.js","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":";;;AAoBA,EAAY,GAAG,YAAY,MAA8B;AACvD,KAAI;EACF,IAAM,EAAE,SAAM,aAAU,EAAe,EAAI,SAAS,EAAI,QAAQ,EAAI,QAAQ,EAAI,cAAc,EAAI,QAAQ;AAC1G,IAAY,YAAY;GAAE,QAAQ,EAAI;GAAQ;GAAM;GAAO,CAAiC;UACrF,GAAK;AACZ,IAAY,YAAY;GACtB,QAAQ,EAAI;GACZ,MAAM;GACN,OAAO;IAAE,UAAU;IAAG,SAAS,EAAE;IAAE;GACnC,OAAO,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI;GACxD,CAAiC;;EAEpC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor-BqXXxyST.js","names":[],"sources":["../src/catalog.ts","../src/json-format.ts","../src/po-format.ts","../src/parallel-compile.ts","../src/extract-cache.ts","../src/codemod.ts","../src/init.ts","../src/doctor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/compiler'\n\nexport interface CatalogEntry {\n message?: string | undefined\n context?: string | undefined\n comment?: string | undefined\n translation?: string | undefined\n origin?: string | string[] | undefined\n obsolete?: boolean | undefined\n fuzzy?: boolean | undefined\n}\n\nexport type CatalogData = Record<string, CatalogEntry>\n\nexport interface UpdateResult {\n added: number\n unchanged: number\n obsolete: number\n}\n\nexport interface UpdateCatalogOptions {\n stripFuzzy?: boolean\n}\n\n/** Update catalog with newly extracted messages */\nexport function updateCatalog(\n existing: CatalogData,\n extracted: ExtractedMessage[],\n options?: UpdateCatalogOptions,\n): { catalog: CatalogData; result: UpdateResult } {\n const extractedIds = new Set(extracted.map((m) => m.id))\n const consumedCarryForwardIds = new Set<string>()\n const catalog: CatalogData = {}\n let added = 0\n let unchanged = 0\n let obsolete = 0\n\n for (const msg of extracted) {\n const existingEntry = existing[msg.id]\n const carried = existingEntry\n ? undefined\n : findCarryForwardEntry(existing, msg, consumedCarryForwardIds)\n const origin = `${msg.origin.file}:${msg.origin.line}`\n const baseEntry = existingEntry ?? carried?.entry\n\n if (carried) {\n consumedCarryForwardIds.add(carried.id)\n }\n\n if (baseEntry) {\n catalog[msg.id] = {\n ...baseEntry,\n message: msg.message ?? baseEntry.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n obsolete: false,\n }\n unchanged++\n } else if (catalog[msg.id]) {\n // Same ID already seen in this extraction batch — merge origin\n const existing = catalog[msg.id]!\n catalog[msg.id] = {\n ...existing,\n origin: mergeOrigins(existing.origin, origin),\n }\n } else {\n catalog[msg.id] = {\n message: msg.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n }\n added++\n }\n\n if (options?.stripFuzzy) {\n const { fuzzy: _fuzzy, ...rest } = catalog[msg.id]!\n catalog[msg.id] = rest\n }\n }\n\n for (const [id, entry] of Object.entries(existing)) {\n if (!extractedIds.has(id)) {\n const { fuzzy: _fuzzy, ...rest } = entry\n const obsoleteEntry = options?.stripFuzzy\n ? { ...rest, obsolete: true }\n : { ...entry, obsolete: true }\n catalog[id] = obsoleteEntry\n obsolete++\n }\n }\n\n return { catalog, result: { added, unchanged, obsolete } }\n}\n\nfunction mergeOrigins(\n existing: string | string[] | undefined,\n newOrigin: string,\n): string | string[] {\n if (!existing) return newOrigin\n const existingArray = Array.isArray(existing) ? existing : [existing]\n const merged = [...new Set([...existingArray, newOrigin])]\n return merged.length === 1 ? merged[0]! : merged\n}\n\nfunction findCarryForwardEntry(\n existing: CatalogData,\n extracted: ExtractedMessage,\n consumedCarryForwardIds: Set<string>,\n): { id: string; entry: CatalogEntry } | undefined {\n if (!extracted.context) {\n return undefined\n }\n\n const extractedOrigin = `${extracted.origin.file}:${extracted.origin.line}`\n for (const [id, entry] of Object.entries(existing)) {\n if (consumedCarryForwardIds.has(id)) continue\n if (entry.context !== undefined) continue\n if (entry.message !== extracted.message) continue\n if (!sameOrigin(entry.origin, extractedOrigin)) continue\n return { id, entry }\n }\n\n return undefined\n}\n\nfunction sameOrigin(previous: string | string[] | undefined, next: string): boolean {\n if (!previous) return false\n const origins = Array.isArray(previous) ? previous : [previous]\n return origins.some((o) => o === next || originFile(o) === originFile(next))\n}\n\nfunction originFile(origin: string): string {\n const match = origin.match(/^(.*):\\d+$/)\n return match?.[1] ?? origin\n}\n","import type { CatalogData } from './catalog'\n\n/** Read a JSON catalog file */\nexport function readJsonCatalog(content: string): CatalogData {\n const raw = JSON.parse(content) as Record<string, unknown>\n const catalog: CatalogData = {}\n\n for (const [id, entry] of Object.entries(raw)) {\n if (typeof entry === 'object' && entry !== null) {\n const e = entry as Record<string, unknown>\n catalog[id] = {\n message: typeof e['message'] === 'string' ? e['message'] : undefined,\n context: typeof e['context'] === 'string' ? e['context'] : undefined,\n comment: typeof e['comment'] === 'string' ? e['comment'] : undefined,\n translation: typeof e['translation'] === 'string' ? e['translation'] : undefined,\n origin: typeof e['origin'] === 'string'\n ? e['origin']\n : Array.isArray(e['origin']) && (e['origin'] as unknown[]).every((v) => typeof v === 'string')\n ? (e['origin'] as string[])\n : undefined,\n obsolete: typeof e['obsolete'] === 'boolean' ? e['obsolete'] : undefined,\n fuzzy: typeof e['fuzzy'] === 'boolean' ? e['fuzzy'] : undefined,\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to JSON format */\nexport function writeJsonCatalog(catalog: CatalogData): string {\n const output: Record<string, Record<string, unknown>> = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n const obj: Record<string, unknown> = {}\n if (entry.message !== undefined) obj['message'] = entry.message\n if (entry.context !== undefined) obj['context'] = entry.context\n if (entry.comment !== undefined) obj['comment'] = entry.comment\n if (entry.translation !== undefined) obj['translation'] = entry.translation\n if (entry.origin !== undefined) obj['origin'] = entry.origin\n if (entry.obsolete) obj['obsolete'] = true\n if (entry.fuzzy) obj['fuzzy'] = true\n output[id] = obj\n }\n\n return JSON.stringify(output, null, 2) + '\\n'\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core/compiler'\nimport * as gettextParser from 'gettext-parser'\n\nconst CUSTOM_ID_MARKER = 'fluenti-id:'\n\ninterface POTranslation {\n msgid: string\n msgctxt?: string\n msgstr: string[]\n comments?: {\n reference?: string\n extracted?: string\n flag?: string\n translator?: string\n previous?: string\n }\n}\n\ninterface POData {\n headers?: Record<string, string>\n translations: Record<string, Record<string, POTranslation>>\n obsolete?: Record<string, Record<string, POTranslation>>\n}\n\ninterface ParsedExtractedComment {\n comment?: string\n customId?: string\n sourceMessage?: string\n}\n\nfunction processPoEntries(\n contextMap: Record<string, Record<string, POTranslation>>,\n catalog: CatalogData,\n isObsolete: boolean,\n): void {\n for (const [contextKey, entries] of Object.entries(contextMap)) {\n for (const [msgid, entry] of Object.entries(entries)) {\n if (!msgid) continue\n\n const context = contextKey || entry.msgctxt || undefined\n const translation = entry.msgstr?.[0] ?? undefined\n const rawReference = entry.comments?.reference ?? undefined\n const origin = rawReference?.includes('\\n')\n ? rawReference.split('\\n').map((r: string) => r.trim()).filter(Boolean)\n : rawReference?.includes(' ')\n ? rawReference.split(/\\s+/).filter(Boolean)\n : rawReference\n const normalizedOrigin = Array.isArray(origin) && origin.length === 1 ? origin[0] : origin\n const isFuzzy = !isObsolete && (entry.comments?.flag?.includes('fuzzy') ?? false)\n const { comment, customId, sourceMessage } = parseExtractedComment(entry.comments?.extracted)\n // When a custom ID is present, the msgid may be the custom key (not source text),\n // so accept sourceMessage directly without requiring a hash match.\n // For hash-ID entries (no customId), verify the hash to prevent double-hashing.\n const resolvedSourceMessage = sourceMessage\n && (customId !== undefined || hashMessage(sourceMessage, context) === msgid)\n ? sourceMessage\n : undefined\n const id = customId\n ?? (resolvedSourceMessage ? msgid : hashMessage(msgid, context))\n\n catalog[id] = {\n message: resolvedSourceMessage ?? msgid,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n ...(translation ? { translation } : {}),\n ...(normalizedOrigin !== undefined ? { origin: normalizedOrigin } : {}),\n ...(isFuzzy ? { fuzzy: true } : {}),\n ...(isObsolete ? { obsolete: true } : {}),\n }\n }\n }\n}\n\n/** Read a PO catalog file */\nexport function readPoCatalog(content: string): CatalogData {\n const po = gettextParser.po.parse(content) as POData\n const catalog: CatalogData = {}\n\n processPoEntries(po.translations ?? {}, catalog, false)\n if (po.obsolete) {\n processPoEntries(po.obsolete, catalog, true)\n }\n\n return catalog\n}\n\n/** Write a catalog to PO format */\nexport function writePoCatalog(catalog: CatalogData): string {\n const translations: POData['translations'] = {\n '': {\n '': {\n msgid: '',\n msgstr: ['Content-Type: text/plain; charset=UTF-8\\n'],\n },\n },\n }\n const obsolete: POData['obsolete'] = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n // Use custom ID as msgid for non-hash entries to prevent collision when two\n // entries share the same source message but have different custom IDs.\n // Hash-ID entries keep source message as msgid (PO-friendly for translators).\n const isHashId = entry.message !== undefined && hashMessage(entry.message, entry.context) === id\n const msgid = isHashId ? (entry.message ?? id) : id\n const poEntry: POTranslation = {\n msgid,\n ...(entry.context !== undefined ? { msgctxt: entry.context } : {}),\n msgstr: [entry.translation ?? ''],\n }\n\n const comments: POTranslation['comments'] = {}\n if (entry.origin && !entry.obsolete) {\n comments.reference = Array.isArray(entry.origin)\n ? entry.origin.join('\\n')\n : entry.origin\n }\n const extractedComment = buildExtractedComment(id, entry.message ?? id, entry.context, entry.comment)\n if (extractedComment) {\n comments.extracted = extractedComment\n }\n if (entry.fuzzy && !entry.obsolete) {\n comments.flag = 'fuzzy'\n }\n if (comments.reference || comments.extracted || comments.flag) {\n poEntry.comments = comments\n }\n\n if (entry.obsolete) {\n const contextKey = entry.context ?? ''\n obsolete[contextKey] ??= {}\n obsolete[contextKey]![poEntry.msgid] = poEntry\n } else {\n const contextKey = entry.context ?? ''\n translations[contextKey] ??= {}\n translations[contextKey]![poEntry.msgid] = poEntry\n }\n }\n\n const poData: POData = {\n headers: {\n 'Content-Type': 'text/plain; charset=UTF-8',\n },\n translations,\n ...(Object.keys(obsolete).length > 0 ? { obsolete } : {}),\n }\n\n const buffer = gettextParser.po.compile(poData as Parameters<typeof gettextParser.po.compile>[0])\n return buffer.toString()\n}\n\nfunction parseExtractedComment(\n extracted: string | undefined,\n): ParsedExtractedComment {\n if (!extracted) {\n return {}\n }\n\n const lines = extracted.split('\\n').map((line) => line.trim()).filter(Boolean)\n let customId: string | undefined\n let sourceMessage: string | undefined\n const commentLines: string[] = []\n\n for (const line of lines) {\n if (line.startsWith(CUSTOM_ID_MARKER)) {\n customId = line.slice(CUSTOM_ID_MARKER.length).trim() || undefined\n continue\n }\n if (line.startsWith('msg`') && line.endsWith('`')) {\n sourceMessage = line.slice(4, -1)\n continue\n }\n if (line.startsWith('Trans: ')) {\n sourceMessage = normalizeRichTextComment(line.slice('Trans: '.length))\n continue\n }\n commentLines.push(line)\n }\n\n return {\n ...(commentLines.length > 0 ? { comment: commentLines.join('\\n') } : {}),\n ...(customId ? { customId } : {}),\n ...(sourceMessage ? { sourceMessage } : {}),\n }\n}\n\nfunction normalizeRichTextComment(comment: string): string {\n let stack: Array<{ tag: string; index: number }> = []\n let nextIndex = 0\n\n return comment.replace(/<\\/?([a-zA-Z][\\w-]*)>/g, (match, rawTag: string) => {\n const tag = rawTag\n if (match.startsWith('</')) {\n for (let index = stack.length - 1; index >= 0; index--) {\n const entry = stack[index]\n if (entry?.tag !== tag) continue\n stack = stack.filter((_, i) => i !== index)\n return `</${entry.index}>`\n }\n return match\n }\n\n const index = nextIndex++\n stack.push({ tag, index })\n return `<${index}>`\n })\n}\n\nfunction buildExtractedComment(\n id: string,\n message: string,\n context: string | undefined,\n comment: string | undefined,\n): string | undefined {\n const lines: string[] = []\n\n if (comment) {\n lines.push(comment)\n }\n\n if (id !== hashMessage(message, context)) {\n lines.push(`${CUSTOM_ID_MARKER} ${id}`)\n // Preserve source message so round-trip works when msgid is the custom ID (not source text)\n lines.push(`msg\\`${message}\\``)\n }\n\n return lines.length > 0 ? lines.join('\\n') : undefined\n}\n","import { Worker } from 'node:worker_threads'\nimport { availableParallelism } from 'node:os'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { CatalogData } from './catalog'\nimport type { CompileOptions, CompileStats } from './compile'\nimport type { CompileWorkerRequest, CompileWorkerResponse } from './compile-worker'\n\nexport interface ParallelCompileTask {\n locale: string\n catalog: CatalogData\n allIds: string[]\n sourceLocale: string\n options?: CompileOptions\n}\n\nexport interface ParallelCompileResult {\n locale: string\n code: string\n stats: CompileStats\n}\n\n/** Default timeout per worker in milliseconds (30 seconds) */\nconst WORKER_TIMEOUT_MS = 30_000\n\nfunction getWorkerPath(): string {\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url))\n return resolve(thisDir, 'compile-worker.js')\n}\n\n/**\n * Compile multiple locales in parallel using worker threads.\n *\n * - Single task → returns result directly (no worker overhead)\n * - Multiple tasks → spawns min(tasks, availableParallelism()) workers\n * - Workers are created per-call and destroyed after completion\n *\n * Falls back to in-process compilation when the compiled worker is unavailable\n * (e.g. when running from TypeScript source in development/tests).\n */\nexport async function parallelCompile(\n tasks: ParallelCompileTask[],\n concurrency?: number,\n): Promise<ParallelCompileResult[]> {\n if (tasks.length === 0) return []\n\n // Single task: compile in-process, no worker overhead\n if (tasks.length === 1) {\n const { compileCatalog } = await import('./compile')\n const task = tasks[0]!\n const { code, stats } = compileCatalog(task.catalog, task.locale, task.allIds, task.sourceLocale, task.options)\n return [{ locale: task.locale, code, stats }]\n }\n\n const workerPath = getWorkerPath()\n\n // If compiled worker doesn't exist (dev/test), fall back to concurrent in-process compilation\n if (!existsSync(workerPath)) {\n return inProcessParallelCompile(tasks)\n }\n\n const maxWorkers = concurrency ?? Math.min(tasks.length, availableParallelism())\n const results: ParallelCompileResult[] = []\n const queue = [...tasks]\n let rejected = false\n\n return new Promise<ParallelCompileResult[]>((resolveAll, rejectAll) => {\n let activeWorkers = 0\n\n function spawnNext(): void {\n if (rejected) return\n const task = queue.shift()\n if (!task) {\n if (activeWorkers === 0) {\n resolveAll(results)\n }\n return\n }\n\n activeWorkers++\n const worker = new Worker(workerPath)\n\n const timer = setTimeout(() => {\n if (!rejected) {\n rejected = true\n activeWorkers--\n worker.terminate()\n rejectAll(new Error(`Worker timed out after ${WORKER_TIMEOUT_MS}ms compiling locale \"${task.locale}\"`))\n }\n }, WORKER_TIMEOUT_MS)\n\n worker.on('message', (response: CompileWorkerResponse) => {\n clearTimeout(timer)\n if (rejected) return\n if (response.error) {\n rejected = true\n activeWorkers--\n worker.terminate()\n rejectAll(new Error(`Failed to compile locale \"${response.locale}\": ${response.error}`))\n return\n }\n results.push({\n locale: response.locale,\n code: response.code,\n stats: response.stats,\n })\n activeWorkers--\n worker.terminate()\n spawnNext()\n })\n\n worker.on('error', (err: Error) => {\n clearTimeout(timer)\n if (!rejected) {\n rejected = true\n worker.terminate()\n rejectAll(new Error(`Worker error compiling locale \"${task.locale}\": ${err.message}`))\n }\n })\n\n worker.on('exit', (code) => {\n clearTimeout(timer)\n if (code !== 0 && !rejected) {\n rejected = true\n rejectAll(new Error(`Worker exited with code ${code} while compiling locale \"${task.locale}\"`))\n }\n })\n\n const request: CompileWorkerRequest = {\n locale: task.locale,\n catalog: task.catalog,\n allIds: task.allIds,\n sourceLocale: task.sourceLocale,\n options: task.options,\n }\n worker.postMessage(request)\n }\n\n // Start initial batch of workers\n const initialBatch = Math.min(maxWorkers, queue.length)\n for (let i = 0; i < initialBatch; i++) {\n spawnNext()\n }\n })\n}\n\n/**\n * In-process fallback for parallel compilation.\n * Uses Promise.all for concurrency when workers are unavailable.\n */\nasync function inProcessParallelCompile(\n tasks: ParallelCompileTask[],\n): Promise<ParallelCompileResult[]> {\n const { compileCatalog } = await import('./compile')\n return Promise.all(\n tasks.map((task) => {\n const { code, stats } = compileCatalog(task.catalog, task.locale, task.allIds, task.sourceLocale, task.options)\n return { locale: task.locale, code, stats }\n }),\n )\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { ExtractedMessage } from '@fluenti/core/compiler'\n\n/** Cache format version — bump when the structure changes */\nconst CACHE_VERSION = '1'\n\ninterface ExtractCacheEntry {\n mtime: number\n size: number\n messages: ExtractedMessage[]\n}\n\ninterface ExtractCacheData {\n version: string\n entries: Record<string, ExtractCacheEntry>\n}\n\n/**\n * File-level extract cache that skips re-extraction for unchanged files.\n *\n * Cache is keyed by file path, with mtime + size as change detection.\n */\nexport class ExtractCache {\n private data: ExtractCacheData\n private cachePath: string\n private dirty = false\n\n constructor(catalogDir: string, projectId?: string) {\n const cacheDir = projectId\n ? resolve(catalogDir, '.cache', projectId)\n : resolve(catalogDir, '.cache')\n this.cachePath = resolve(cacheDir, 'extract-cache.json')\n this.data = this.load()\n }\n\n /**\n * Check if a file has changed since the last extraction.\n * Returns cached messages if unchanged, undefined if re-extraction needed.\n */\n get(filePath: string): ExtractedMessage[] | undefined {\n const entry = this.data.entries[filePath]\n if (!entry) return undefined\n\n try {\n const stat = statSync(filePath)\n if (stat.mtimeMs === entry.mtime && stat.size === entry.size) {\n return entry.messages\n }\n } catch {\n // File no longer exists or can't be stat'd — cache miss\n }\n\n return undefined\n }\n\n /**\n * Update the cache for a file after extraction.\n */\n set(filePath: string, messages: ExtractedMessage[]): void {\n try {\n const stat = statSync(filePath)\n this.data.entries[filePath] = {\n mtime: stat.mtimeMs,\n size: stat.size,\n messages,\n }\n this.dirty = true\n } catch {\n // File doesn't exist — skip caching\n }\n }\n\n /**\n * Remove entries for files that no longer exist in the file list.\n */\n prune(currentFiles: Set<string>): void {\n for (const filePath of Object.keys(this.data.entries)) {\n if (!currentFiles.has(filePath)) {\n delete this.data.entries[filePath]\n this.dirty = true\n }\n }\n }\n\n /**\n * Write the cache to disk if any changes were made.\n */\n save(): void {\n if (!this.dirty) return\n\n try {\n mkdirSync(dirname(this.cachePath), { recursive: true })\n writeFileSync(this.cachePath, JSON.stringify(this.data), 'utf-8')\n this.dirty = false\n } catch {\n // Cache write failure is non-fatal — next run will re-extract\n }\n }\n\n /** Number of cached entries */\n get size(): number {\n return Object.keys(this.data.entries).length\n }\n\n private load(): ExtractCacheData {\n try {\n if (existsSync(this.cachePath)) {\n const raw = readFileSync(this.cachePath, 'utf-8')\n const parsed = JSON.parse(raw) as ExtractCacheData\n if (parsed.version === CACHE_VERSION) {\n return parsed\n }\n }\n } catch {\n // Corrupt or unreadable cache — start fresh\n }\n\n return { version: CACHE_VERSION, entries: {} }\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport fg from 'fast-glob'\n\nconst FRAMEWORKS = ['react', 'vue', 'solid'] as const\nconst COMPONENT_EXPORTS = new Set(['Trans', 'Plural', 'Select', 'DateTime', 'NumberFormat'])\n\ntype FrameworkName = (typeof FRAMEWORKS)[number]\n\nexport interface CodemodOptions {\n cwd: string\n include?: string[]\n write?: boolean\n}\n\nexport interface CodemodFileResult {\n file: string\n changed: boolean\n}\n\nexport interface CodemodResult {\n changedFiles: CodemodFileResult[]\n changedCount: number\n}\n\ninterface ParsedSpecifier {\n imported: string\n local: string\n isType: boolean\n}\n\nconst DEFAULT_GLOBS = [\n 'src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n]\n\nfunction parseSpecifier(raw: string): ParsedSpecifier {\n const trimmed = raw.trim()\n const isType = trimmed.startsWith('type ')\n const normalized = isType ? trimmed.slice(5).trim() : trimmed\n const parts = normalized.split(/\\s+as\\s+/)\n const imported = parts[0]!.trim()\n const local = (parts[1] ?? parts[0])!.trim()\n return {\n imported,\n local,\n isType,\n }\n}\n\nfunction formatSpecifier(specifier: ParsedSpecifier): string {\n const prefix = specifier.isType ? 'type ' : ''\n if (specifier.imported === specifier.local) {\n return `${prefix}${specifier.imported}`\n }\n return `${prefix}${specifier.imported} as ${specifier.local}`\n}\n\nfunction dedupeSpecifiers(specifiers: ParsedSpecifier[]): ParsedSpecifier[] {\n const seen = new Set<string>()\n const result: ParsedSpecifier[] = []\n for (const specifier of specifiers) {\n const key = `${specifier.isType ? 'type:' : 'value:'}${specifier.imported}:${specifier.local}`\n if (seen.has(key)) continue\n seen.add(key)\n result.push(specifier)\n }\n return result\n}\n\nfunction mergeNamedImports(code: string, source: string): string {\n const pattern = new RegExp(`import\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s*['\"]${source.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}['\"];?\\\\n?`, 'g')\n const matches = [...code.matchAll(pattern)]\n if (matches.length <= 1) return code\n\n const specifiers = dedupeSpecifiers(\n matches.flatMap((match) => match[1]!.split(',').map((entry) => parseSpecifier(entry)).filter((entry) => entry.imported.length > 0)),\n )\n const mergedImport = `import { ${specifiers.map(formatSpecifier).join(', ')} } from '${source}'\\n`\n\n let first = true\n return code.replace(pattern, () => {\n if (first) {\n first = false\n return mergedImport\n }\n return ''\n })\n}\n\nexport function rewriteFluentiImports(source: string): { code: string; changed: boolean } {\n let code = source\n let changed = false\n let renameSolidFactory = false\n\n const importPattern = /import\\s*\\{([\\s\\S]*?)\\}\\s*from\\s*['\"](@fluenti\\/(react|vue|solid)(?:\\/components)?)['\"];?/g\n\n code = code.replace(importPattern, (full, specifiersRaw: string, sourcePath: string, framework: FrameworkName) => {\n const specifiers = specifiersRaw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n .map(parseSpecifier)\n\n const main: ParsedSpecifier[] = []\n const components: ParsedSpecifier[] = []\n const runtime: ParsedSpecifier[] = []\n\n for (const specifier of specifiers) {\n const next = { ...specifier }\n if (framework === 'solid' && next.imported === 'createFluentiContext') {\n next.imported = 'createFluenti'\n if (next.local === 'createFluentiContext') {\n next.local = 'createFluenti'\n renameSolidFactory = true\n }\n }\n\n if (next.imported === 'interpolate') {\n runtime.push({ ...next, isType: false })\n continue\n }\n\n if (!next.isType && COMPONENT_EXPORTS.has(next.imported) && sourcePath.endsWith('/components')) {\n components.push(next)\n continue\n }\n\n main.push(next)\n }\n\n const nextImports: string[] = []\n if (main.length > 0 && !sourcePath.endsWith('/components')) {\n nextImports.push(`import { ${dedupeSpecifiers(main).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}'`)\n }\n if (components.length > 0) {\n nextImports.push(`import { ${dedupeSpecifiers(components).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}/components'`)\n }\n if (runtime.length > 0) {\n nextImports.push(`import { ${dedupeSpecifiers(runtime).map(formatSpecifier).join(', ')} } from '@fluenti/core/runtime'`)\n }\n if (main.length > 0 && sourcePath.endsWith('/components')) {\n nextImports.unshift(`import { ${dedupeSpecifiers(main).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}'`)\n }\n\n const replacement = nextImports.join('\\n')\n if (replacement !== full) {\n changed = true\n }\n return replacement\n })\n\n if (renameSolidFactory) {\n const renamed = code.replace(/\\bcreateFluentiContext\\b/g, 'createFluenti')\n if (renamed !== code) {\n code = renamed\n changed = true\n }\n }\n\n for (const sourcePath of [\n '@fluenti/react',\n '@fluenti/react/components',\n '@fluenti/vue',\n '@fluenti/vue/components',\n '@fluenti/solid',\n '@fluenti/solid/components',\n '@fluenti/core/runtime',\n ]) {\n const merged = mergeNamedImports(code, sourcePath)\n if (merged !== code) {\n code = merged\n changed = true\n }\n }\n\n return { code, changed }\n}\n\nexport async function runCodemod(options: CodemodOptions): Promise<CodemodResult> {\n const include = options.include && options.include.length > 0 ? options.include : DEFAULT_GLOBS\n const files = await fg(include, {\n cwd: options.cwd,\n absolute: false,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.fluenti/**'],\n })\n\n const changedFiles: CodemodFileResult[] = []\n\n for (const file of files) {\n const path = resolve(options.cwd, file)\n const current = readFileSync(path, 'utf-8')\n const rewritten = rewriteFluentiImports(current)\n if (!rewritten.changed) continue\n\n if (options.write) {\n writeFileSync(path, rewritten.code, 'utf-8')\n }\n\n changedFiles.push({ file, changed: true })\n }\n\n return {\n changedFiles,\n changedCount: changedFiles.length,\n }\n}\n","import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport consola from 'consola'\n\nconst LOCALE_PATTERN = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/\n\nexport function validateLocale(locale: string): string {\n if (!LOCALE_PATTERN.test(locale)) {\n throw new Error(`Invalid locale format: \"${locale}\"`)\n }\n return locale\n}\n\nexport interface DetectedFramework {\n name: 'nextjs' | 'nuxt' | 'vue' | 'solid' | 'solidstart' | 'react' | 'unknown'\n pluginPackage: string | null\n}\n\nconst FRAMEWORK_DETECTION: Array<{\n dep: string\n name: DetectedFramework['name']\n pluginPackage: string\n}> = [\n { dep: 'next', name: 'nextjs', pluginPackage: '@fluenti/next' },\n { dep: 'nuxt', name: 'nuxt', pluginPackage: '@fluenti/vue' },\n { dep: '@solidjs/start', name: 'solidstart', pluginPackage: '@fluenti/solid' },\n { dep: 'vue', name: 'vue', pluginPackage: '@fluenti/vue' },\n { dep: 'solid-js', name: 'solid', pluginPackage: '@fluenti/solid' },\n { dep: 'react', name: 'react', pluginPackage: '@fluenti/react' },\n]\n\n/**\n * Detect the framework from package.json dependencies.\n */\nexport function detectFramework(deps: Record<string, string>): DetectedFramework {\n for (const entry of FRAMEWORK_DETECTION) {\n if (entry.dep in deps) {\n return { name: entry.name, pluginPackage: entry.pluginPackage }\n }\n }\n return { name: 'unknown', pluginPackage: null }\n}\n\n/**\n * Generate fluenti.config.ts content.\n */\nexport function generateFluentiConfig(opts: {\n sourceLocale: string\n locales: string[]\n format: 'po' | 'json'\n}): string {\n const localesList = opts.locales.map((l) => JSON.stringify(l)).join(', ')\n return `import { defineConfig } from '@fluenti/cli'\n\nexport default defineConfig({\n sourceLocale: ${JSON.stringify(opts.sourceLocale)},\n locales: [${localesList}],\n catalogDir: './locales',\n format: ${JSON.stringify(opts.format)},\n include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],\n compileOutDir: './src/locales/compiled',\n})\n`\n}\n\n/**\n * Interactive init flow.\n */\nexport async function runInit(options: { cwd: string }): Promise<void> {\n const pkgPath = resolve(options.cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n consola.error('No package.json found in current directory.')\n return\n }\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n const framework = detectFramework(allDeps)\n\n consola.info(`Detected framework: ${framework.name}`)\n if (framework.pluginPackage) {\n consola.info(`Recommended plugin: ${framework.pluginPackage}`)\n }\n\n // Check if config already exists\n const configPath = resolve(options.cwd, 'fluenti.config.ts')\n if (existsSync(configPath)) {\n consola.warn('fluenti.config.ts already exists. Skipping config generation.')\n return\n }\n\n // Prompt for configuration\n const sourceLocale = await consola.prompt('Source locale?', {\n type: 'text',\n default: 'en',\n placeholder: 'en',\n }) as unknown as string\n\n if (typeof sourceLocale === 'symbol') return // user cancelled\n\n const targetLocalesInput = await consola.prompt('Target locales (comma-separated)?', {\n type: 'text',\n default: 'ja,zh-CN',\n placeholder: 'ja,zh-CN',\n }) as unknown as string\n\n if (typeof targetLocalesInput === 'symbol') return\n\n const format = await consola.prompt('Catalog format?', {\n type: 'select',\n options: ['po', 'json'],\n initial: 'po',\n }) as unknown as string\n\n if (typeof format === 'symbol') return\n\n const targetLocales = targetLocalesInput.split(',').map((l) => l.trim()).filter(Boolean)\n\n // Validate locale formats\n validateLocale(sourceLocale)\n for (const locale of targetLocales) {\n validateLocale(locale)\n }\n\n const allLocales = [sourceLocale, ...targetLocales.filter((l) => l !== sourceLocale)]\n\n // Write config\n const configContent = generateFluentiConfig({\n sourceLocale,\n locales: allLocales,\n format: format as 'po' | 'json',\n })\n writeFileSync(configPath, configContent, 'utf-8')\n consola.success('Created fluenti.config.ts')\n\n // Append to .gitignore\n const gitignorePath = resolve(options.cwd, '.gitignore')\n const gitignoreEntry = 'src/locales/compiled/'\n if (existsSync(gitignorePath)) {\n const existing = readFileSync(gitignorePath, 'utf-8')\n if (!existing.includes(gitignoreEntry)) {\n appendFileSync(gitignorePath, `\\n# Fluenti compiled catalogs\\n${gitignoreEntry}\\n`)\n consola.success('Updated .gitignore')\n }\n } else {\n writeFileSync(gitignorePath, `# Fluenti compiled catalogs\\n${gitignoreEntry}\\n`)\n consola.success('Created .gitignore')\n }\n\n // Patch package.json scripts\n const existingScripts = pkg.scripts ?? {}\n const newScripts: Record<string, string> = {}\n let scriptsChanged = false\n if (!existingScripts['i18n:extract']) {\n newScripts['i18n:extract'] = 'fluenti extract'\n scriptsChanged = true\n }\n if (!existingScripts['i18n:compile']) {\n newScripts['i18n:compile'] = 'fluenti compile'\n scriptsChanged = true\n }\n if (scriptsChanged) {\n const updatedPkg = {\n ...pkg,\n scripts: { ...existingScripts, ...newScripts },\n }\n writeFileSync(pkgPath, JSON.stringify(updatedPkg, null, 2) + '\\n', 'utf-8')\n consola.success('Added i18n:extract and i18n:compile scripts to package.json')\n }\n\n // Print next steps\n consola.log('')\n consola.box({\n title: 'Next steps',\n message: [\n framework.pluginPackage\n ? `1. Install: pnpm add -D ${framework.pluginPackage} @fluenti/cli`\n : '1. Install: pnpm add -D @fluenti/cli',\n framework.name === 'nextjs'\n ? '2. Add withFluenti() to your next.config.ts'\n : framework.name !== 'unknown'\n ? '2. Add the Vite plugin to your vite.config.ts (e.g. fluentiVue() from @fluenti/vue/vite-plugin)'\n : '2. Configure your build tool with the framework Vite plugin or @fluenti/next',\n '3. Run: npx fluenti extract',\n '4. Translate your messages',\n '5. Run: npx fluenti compile',\n ].join('\\n'),\n })\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport fg from 'fast-glob'\nimport { detectFramework, type DetectedFramework } from './init'\nimport { loadConfig } from './config-loader'\n\nexport type DoctorSeverity = 'error' | 'warning' | 'info'\n\nexport interface DoctorFinding {\n severity: DoctorSeverity\n code: string\n message: string\n}\n\nexport interface DoctorReport {\n framework: DetectedFramework['name']\n findings: DoctorFinding[]\n configPath?: string\n}\n\nexport interface DoctorOptions {\n cwd: string\n config?: string\n}\n\nconst SOURCE_GLOBS = [\n 'src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n]\n\nconst MAIN_INTERPOLATE_IMPORT_RE = /import\\s*\\{[\\s\\S]*?\\binterpolate\\b[\\s\\S]*?\\}\\s*from\\s*['\"]@fluenti\\/(react|vue|solid)(?:\\/components)?['\"]/g\nconst INTERNAL_IMPORT_RE = /from\\s*['\"]@fluenti\\/core\\/internal['\"]/g\n\nfunction readIfExists(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf-8') : undefined\n}\n\nfunction detectConfigPath(cwd: string, explicit?: string): string | undefined {\n if (explicit) return resolve(cwd, explicit)\n\n for (const candidate of ['fluenti.config.ts', 'fluenti.config.mts', 'fluenti.config.js', 'fluenti.config.mjs']) {\n const resolved = resolve(cwd, candidate)\n if (existsSync(resolved)) return resolved\n }\n\n return undefined\n}\n\nasync function collectSourceFiles(cwd: string): Promise<string[]> {\n return fg(SOURCE_GLOBS, {\n cwd,\n absolute: false,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.fluenti/**'],\n })\n}\n\nfunction pushFinding(findings: DoctorFinding[], severity: DoctorSeverity, code: string, message: string): void {\n findings.push({ severity, code, message })\n}\n\nfunction getPackageJson(cwd: string): { dependencies?: Record<string, string>; devDependencies?: Record<string, string> } | undefined {\n const pkgPath = resolve(cwd, 'package.json')\n const content = readIfExists(pkgPath)\n if (!content) return undefined\n return JSON.parse(content) as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n}\n\nfunction hasVitePlugin(content: string | undefined): boolean {\n if (!content) return false\n return content.includes('@fluenti/') && content.includes('vite-plugin')\n}\n\nfunction countMatches(content: string, pattern: RegExp): number {\n return [...content.matchAll(pattern)].length\n}\n\nexport async function runDoctor(options: DoctorOptions): Promise<DoctorReport> {\n const findings: DoctorFinding[] = []\n const pkg = getPackageJson(options.cwd)\n\n if (!pkg) {\n return {\n framework: 'unknown',\n findings: [{ severity: 'error', code: 'missing-package-json', message: 'No package.json found in the current directory.' }],\n }\n }\n\n const framework = detectFramework({ ...pkg.dependencies, ...pkg.devDependencies })\n const configPath = detectConfigPath(options.cwd, options.config)\n const files = await collectSourceFiles(options.cwd)\n\n let mainInterpolateImports = 0\n let internalImports = 0\n let importsFluenti = false\n let usesCompileTimeT = false\n\n for (const file of files) {\n const content = readFileSync(resolve(options.cwd, file), 'utf-8')\n mainInterpolateImports += countMatches(content, MAIN_INTERPOLATE_IMPORT_RE)\n internalImports += countMatches(content, INTERNAL_IMPORT_RE)\n importsFluenti = importsFluenti || content.includes('@fluenti/')\n usesCompileTimeT = usesCompileTimeT || content.includes(\"from '@fluenti/\") && content.includes(' t`')\n }\n\n if (mainInterpolateImports > 0) {\n pushFinding(\n findings,\n 'warning',\n 'main-entry-interpolate',\n `Found ${mainInterpolateImports} import(s) of interpolate from framework packages. Import interpolate from @fluenti/core/runtime instead.`,\n )\n }\n\n if (internalImports > 0) {\n pushFinding(\n findings,\n 'warning',\n 'core-internal-imports',\n `Found ${internalImports} import(s) from @fluenti/core/internal. Use @fluenti/core/runtime or @fluenti/core/compiler.`,\n )\n }\n\n if (!configPath) {\n pushFinding(findings, 'warning', 'missing-config', 'No fluenti.config.* file found.')\n }\n\n if (framework.name === 'nextjs') {\n const nextConfig = ['next.config.ts', 'next.config.mjs', 'next.config.js']\n .map((name) => resolve(options.cwd, name))\n .find((candidate) => existsSync(candidate))\n const nextContent = nextConfig ? readIfExists(nextConfig) : undefined\n if (importsFluenti && !nextContent?.includes('withFluenti(')) {\n pushFinding(findings, 'error', 'missing-with-fluenti', 'Next.js project imports Fluenti but next.config does not call withFluenti().')\n }\n }\n\n if (framework.name === 'react' || framework.name === 'vue' || framework.name === 'solid') {\n const viteConfig = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs']\n .map((name) => resolve(options.cwd, name))\n .find((candidate) => existsSync(candidate))\n const viteContent = viteConfig ? readIfExists(viteConfig) : undefined\n if ((importsFluenti || usesCompileTimeT) && !hasVitePlugin(viteContent)) {\n pushFinding(findings, 'warning', 'missing-vite-plugin', 'Project imports Fluenti but vite.config does not appear to include a Fluenti Vite plugin.')\n }\n }\n\n if (configPath && existsSync(configPath)) {\n try {\n const config = await loadConfig(configPath)\n const sourceCatalogExt = config.format === 'json' ? '.json' : '.po'\n const sourceCatalog = resolve(options.cwd, config.catalogDir, `${config.sourceLocale}${sourceCatalogExt}`)\n if (!existsSync(sourceCatalog)) {\n pushFinding(findings, 'warning', 'missing-source-catalog', `Source catalog not found: ${sourceCatalog}`)\n }\n\n const compiledIndex = resolve(options.cwd, config.compileOutDir, 'index.js')\n if (!existsSync(compiledIndex)) {\n pushFinding(findings, 'warning', 'missing-compiled-catalogs', `Compiled catalogs not found at ${compiledIndex}. Run \"fluenti compile\".`)\n }\n } catch (error) {\n pushFinding(\n findings,\n 'error',\n 'invalid-config',\n `Failed to load Fluenti config: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n if (findings.length === 0) {\n pushFinding(findings, 'info', 'ok', 'No Fluenti migration or configuration issues detected.')\n }\n\n return {\n framework: framework.name,\n findings,\n ...(configPath ? { configPath } : {}),\n }\n}\n\nexport function formatDoctorReport(report: DoctorReport): string {\n const lines: string[] = [`Framework: ${report.framework}`]\n if (report.configPath) {\n lines.push(`Config: ${report.configPath}`)\n }\n lines.push('')\n\n for (const finding of report.findings) {\n const prefix = finding.severity === 'error'\n ? '✖'\n : finding.severity === 'warning'\n ? '⚠'\n : '•'\n lines.push(`${prefix} [${finding.code}] ${finding.message}`)\n }\n\n return lines.join('\\n')\n}\n"],"mappings":"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"}
@@ -1,24 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`@fluenti/core/compiler`),u=require(`gettext-parser`);u=c(u);let d=require(`@fluenti/core/config`),f=require(`node:fs`),p=require(`node:path`),m=require(`node:worker_threads`),h=require(`node:os`),g=require(`node:url`),_=require(`fast-glob`);_=c(_);let v=require(`consola`);v=c(v);function y(e,t,n){let r=new Set(t.map(e=>e.id)),i=new Set,a={},o=0,s=0,c=0;for(let r of t){let t=e[r.id],c=t?void 0:x(e,r,i),l=`${r.origin.file}:${r.origin.line}`,u=t??c?.entry;if(c&&i.add(c.id),u)a[r.id]={...u,message:r.message??u.message,context:r.context,comment:r.comment,origin:l,obsolete:!1},s++;else if(a[r.id]){let e=a[r.id];a[r.id]={...e,origin:b(e.origin,l)}}else a[r.id]={message:r.message,context:r.context,comment:r.comment,origin:l},o++;if(n?.stripFuzzy){let{fuzzy:e,...t}=a[r.id];a[r.id]=t}}for(let[t,i]of Object.entries(e))if(!r.has(t)){let{fuzzy:e,...r}=i;a[t]=n?.stripFuzzy?{...r,obsolete:!0}:{...i,obsolete:!0},c++}return{catalog:a,result:{added:o,unchanged:s,obsolete:c}}}function b(e,t){if(!e)return t;let n=[...new Set([...Array.isArray(e)?e:[e],t])];return n.length===1?n[0]:n}function x(e,t,n){if(!t.context)return;let r=`${t.origin.file}:${t.origin.line}`;for(let[i,a]of Object.entries(e))if(!n.has(i)&&a.context===void 0&&a.message===t.message&&ee(a.origin,r))return{id:i,entry:a}}function ee(e,t){return e?(Array.isArray(e)?e:[e]).some(e=>e===t||S(e)===S(t)):!1}function S(e){return e.match(/^(.*):\d+$/)?.[1]??e}function C(e){let t=JSON.parse(e),n={};for(let[e,r]of Object.entries(t))if(typeof r==`object`&&r){let t=r;n[e]={message:typeof t.message==`string`?t.message:void 0,context:typeof t.context==`string`?t.context:void 0,comment:typeof t.comment==`string`?t.comment:void 0,translation:typeof t.translation==`string`?t.translation:void 0,origin:typeof t.origin==`string`||Array.isArray(t.origin)&&t.origin.every(e=>typeof e==`string`)?t.origin:void 0,obsolete:typeof t.obsolete==`boolean`?t.obsolete:void 0,fuzzy:typeof t.fuzzy==`boolean`?t.fuzzy:void 0}}return n}function w(e){let t={};for(let[n,r]of Object.entries(e)){let e={};r.message!==void 0&&(e.message=r.message),r.context!==void 0&&(e.context=r.context),r.comment!==void 0&&(e.comment=r.comment),r.translation!==void 0&&(e.translation=r.translation),r.origin!==void 0&&(e.origin=r.origin),r.obsolete&&(e.obsolete=!0),r.fuzzy&&(e.fuzzy=!0),t[n]=e}return JSON.stringify(t,null,2)+`
2
- `}var T=`fluenti-id:`;function E(e,t,n){for(let[r,i]of Object.entries(e))for(let[e,a]of Object.entries(i)){if(!e)continue;let i=r||a.msgctxt||void 0,o=a.msgstr?.[0]??void 0,s=a.comments?.reference??void 0,c=s?.includes(`
3
- `)?s.split(`
4
- `).map(e=>e.trim()).filter(Boolean):s?.includes(` `)?s.split(/\s+/).filter(Boolean):s,u=Array.isArray(c)&&c.length===1?c[0]:c,d=!n&&(a.comments?.flag?.includes(`fuzzy`)??!1),{comment:f,customId:p,sourceMessage:m}=te(a.comments?.extracted),h=m&&(p!==void 0||(0,l.hashMessage)(m,i)===e)?m:void 0,g=p??(h?e:(0,l.hashMessage)(e,i));t[g]={message:h??e,...i===void 0?{}:{context:i},...f===void 0?{}:{comment:f},...o?{translation:o}:{},...u===void 0?{}:{origin:u},...d?{fuzzy:!0}:{},...n?{obsolete:!0}:{}}}}function D(e){let t=u.po.parse(e),n={};return E(t.translations??{},n,!1),t.obsolete&&E(t.obsolete,n,!0),n}function O(e){let t={"":{"":{msgid:``,msgstr:[`Content-Type: text/plain; charset=UTF-8
5
- `]}}},n={};for(let[r,i]of Object.entries(e)){let e={msgid:i.message!==void 0&&(0,l.hashMessage)(i.message,i.context)===r?i.message??r:r,...i.context===void 0?{}:{msgctxt:i.context},msgstr:[i.translation??``]},a={};i.origin&&!i.obsolete&&(a.reference=Array.isArray(i.origin)?i.origin.join(`
6
- `):i.origin);let o=re(r,i.message??r,i.context,i.comment);if(o&&(a.extracted=o),i.fuzzy&&!i.obsolete&&(a.flag=`fuzzy`),(a.reference||a.extracted||a.flag)&&(e.comments=a),i.obsolete){let t=i.context??``;n[t]??={},n[t][e.msgid]=e}else{let n=i.context??``;t[n]??={},t[n][e.msgid]=e}}let r={headers:{"Content-Type":`text/plain; charset=UTF-8`},translations:t,...Object.keys(n).length>0?{obsolete:n}:{}};return u.po.compile(r).toString()}function te(e){if(!e)return{};let t=e.split(`
7
- `).map(e=>e.trim()).filter(Boolean),n,r,i=[];for(let e of t){if(e.startsWith(T)){n=e.slice(11).trim()||void 0;continue}if(e.startsWith("msg`")&&e.endsWith("`")){r=e.slice(4,-1);continue}if(e.startsWith(`Trans: `)){r=ne(e.slice(7));continue}i.push(e)}return{...i.length>0?{comment:i.join(`
8
- `)}:{},...n?{customId:n}:{},...r?{sourceMessage:r}:{}}}function ne(e){let t=[],n=0;return e.replace(/<\/?([a-zA-Z][\w-]*)>/g,(e,r)=>{let i=r;if(e.startsWith(`</`)){for(let e=t.length-1;e>=0;e--){let n=t[e];if(n?.tag===i)return t=t.filter((t,n)=>n!==e),`</${n.index}>`}return e}let a=n++;return t.push({tag:i,index:a}),`<${a}>`})}function re(e,t,n,r){let i=[];return r&&i.push(r),e!==(0,l.hashMessage)(t,n)&&(i.push(`${T} ${e}`),i.push(`msg\`${t}\``)),i.length>0?i.join(`
9
- `):void 0}var k=3e4;function A(){return(0,p.resolve)(typeof __dirname<`u`?__dirname:(0,p.dirname)((0,g.fileURLToPath)({}.url)),`compile-worker.js`)}async function j(e,t){if(e.length===0)return[];if(e.length===1){let{compileCatalog:t}=await Promise.resolve().then(()=>require(`./compile-C3VLvhUf.cjs`)).then(e=>e.a),n=e[0],{code:r,stats:i}=t(n.catalog,n.locale,n.allIds,n.sourceLocale,n.options);return[{locale:n.locale,code:r,stats:i}]}let n=A();if(!(0,f.existsSync)(n))return M(e);let r=t??Math.min(e.length,(0,h.availableParallelism)()),i=[],a=[...e],o=!1;return new Promise((e,t)=>{let s=0;function c(){if(o)return;let r=a.shift();if(!r){s===0&&e(i);return}s++;let l=new m.Worker(n),u=setTimeout(()=>{o||(o=!0,s--,l.terminate(),t(Error(`Worker timed out after ${k}ms compiling locale "${r.locale}"`)))},k);l.on(`message`,e=>{if(clearTimeout(u),!o){if(e.error){o=!0,s--,l.terminate(),t(Error(`Failed to compile locale "${e.locale}": ${e.error}`));return}i.push({locale:e.locale,code:e.code,stats:e.stats}),s--,l.terminate(),c()}}),l.on(`error`,e=>{clearTimeout(u),o||(o=!0,l.terminate(),t(Error(`Worker error compiling locale "${r.locale}": ${e.message}`)))}),l.on(`exit`,e=>{clearTimeout(u),e!==0&&!o&&(o=!0,t(Error(`Worker exited with code ${e} while compiling locale "${r.locale}"`)))});let d={locale:r.locale,catalog:r.catalog,allIds:r.allIds,sourceLocale:r.sourceLocale,options:r.options};l.postMessage(d)}let l=Math.min(r,a.length);for(let e=0;e<l;e++)c()})}async function M(e){let{compileCatalog:t}=await Promise.resolve().then(()=>require(`./compile-C3VLvhUf.cjs`)).then(e=>e.a);return Promise.all(e.map(e=>{let{code:n,stats:r}=t(e.catalog,e.locale,e.allIds,e.sourceLocale,e.options);return{locale:e.locale,code:n,stats:r}}))}var N=`1`,P=class{data;cachePath;dirty=!1;constructor(e,t){this.cachePath=(0,p.resolve)(t?(0,p.resolve)(e,`.cache`,t):(0,p.resolve)(e,`.cache`),`extract-cache.json`),this.data=this.load()}get(e){let t=this.data.entries[e];if(t)try{let n=(0,f.statSync)(e);if(n.mtimeMs===t.mtime&&n.size===t.size)return t.messages}catch{}}set(e,t){try{let n=(0,f.statSync)(e);this.data.entries[e]={mtime:n.mtimeMs,size:n.size,messages:t},this.dirty=!0}catch{}}prune(e){for(let t of Object.keys(this.data.entries))e.has(t)||(delete this.data.entries[t],this.dirty=!0)}save(){if(this.dirty)try{(0,f.mkdirSync)((0,p.dirname)(this.cachePath),{recursive:!0}),(0,f.writeFileSync)(this.cachePath,JSON.stringify(this.data),`utf-8`),this.dirty=!1}catch{}}get size(){return Object.keys(this.data.entries).length}load(){try{if((0,f.existsSync)(this.cachePath)){let e=(0,f.readFileSync)(this.cachePath,`utf-8`),t=JSON.parse(e);if(t.version===N)return t}}catch{}return{version:N,entries:{}}}},F=new Set([`Trans`,`Plural`,`Select`,`DateTime`,`NumberFormat`]),I=[`src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`];function L(e){let t=e.trim(),n=t.startsWith(`type `),r=(n?t.slice(5).trim():t).split(/\s+as\s+/);return{imported:r[0].trim(),local:(r[1]??r[0]).trim(),isType:n}}function R(e){let t=e.isType?`type `:``;return e.imported===e.local?`${t}${e.imported}`:`${t}${e.imported} as ${e.local}`}function z(e){let t=new Set,n=[];for(let r of e){let e=`${r.isType?`type:`:`value:`}${r.imported}:${r.local}`;t.has(e)||(t.add(e),n.push(r))}return n}function B(e,t){let n=RegExp(`import\\s*\\{([\\s\\S]*?)\\}\\s*from\\s*['"]${t.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`)}['"];?\\n?`,`g`),r=[...e.matchAll(n)];if(r.length<=1)return e;let i=`import { ${z(r.flatMap(e=>e[1].split(`,`).map(e=>L(e)).filter(e=>e.imported.length>0))).map(R).join(`, `)} } from '${t}'\n`,a=!0;return e.replace(n,()=>a?(a=!1,i):``)}function V(e){let t=e,n=!1,r=!1;if(t=t.replace(/import\s*\{([\s\S]*?)\}\s*from\s*['"](@fluenti\/(react|vue|solid)(?:\/components)?)['"];?/g,(e,t,i,a)=>{let o=t.split(`,`).map(e=>e.trim()).filter(Boolean).map(L),s=[],c=[],l=[];for(let e of o){let t={...e};if(a===`solid`&&t.imported===`createFluentiContext`&&(t.imported=`createFluenti`,t.local===`createFluentiContext`&&(t.local=`createFluenti`,r=!0)),t.imported===`interpolate`){l.push({...t,isType:!1});continue}if(!t.isType&&F.has(t.imported)&&i.endsWith(`/components`)){c.push(t);continue}s.push(t)}let u=[];s.length>0&&!i.endsWith(`/components`)&&u.push(`import { ${z(s).map(R).join(`, `)} } from '@fluenti/${a}'`),c.length>0&&u.push(`import { ${z(c).map(R).join(`, `)} } from '@fluenti/${a}/components'`),l.length>0&&u.push(`import { ${z(l).map(R).join(`, `)} } from '@fluenti/core/runtime'`),s.length>0&&i.endsWith(`/components`)&&u.unshift(`import { ${z(s).map(R).join(`, `)} } from '@fluenti/${a}'`);let d=u.join(`
10
- `);return d!==e&&(n=!0),d}),r){let e=t.replace(/\bcreateFluentiContext\b/g,`createFluenti`);e!==t&&(t=e,n=!0)}for(let e of[`@fluenti/react`,`@fluenti/react/components`,`@fluenti/vue`,`@fluenti/vue/components`,`@fluenti/solid`,`@fluenti/solid/components`,`@fluenti/core/runtime`]){let r=B(t,e);r!==t&&(t=r,n=!0)}return{code:t,changed:n}}async function H(e){let t=await(0,_.default)(e.include&&e.include.length>0?e.include:I,{cwd:e.cwd,absolute:!1,ignore:[`**/node_modules/**`,`**/dist/**`,`**/.fluenti/**`]}),n=[];for(let r of t){let t=(0,p.resolve)(e.cwd,r),i=V((0,f.readFileSync)(t,`utf-8`));i.changed&&(e.write&&(0,f.writeFileSync)(t,i.code,`utf-8`),n.push({file:r,changed:!0}))}return{changedFiles:n,changedCount:n.length}}var U=/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/;function W(e){if(!U.test(e))throw Error(`Invalid locale format: "${e}"`);return e}var G=[{dep:`next`,name:`nextjs`,pluginPackage:`@fluenti/next`},{dep:`nuxt`,name:`nuxt`,pluginPackage:`@fluenti/vue`},{dep:`@solidjs/start`,name:`solidstart`,pluginPackage:`@fluenti/solid`},{dep:`vue`,name:`vue`,pluginPackage:`@fluenti/vue`},{dep:`solid-js`,name:`solid`,pluginPackage:`@fluenti/solid`},{dep:`react`,name:`react`,pluginPackage:`@fluenti/react`}];function K(e){for(let t of G)if(t.dep in e)return{name:t.name,pluginPackage:t.pluginPackage};return{name:`unknown`,pluginPackage:null}}function q(e){let t=e.locales.map(e=>JSON.stringify(e)).join(`, `);return`import { defineConfig } from '@fluenti/cli'
11
-
12
- export default defineConfig({
13
- sourceLocale: ${JSON.stringify(e.sourceLocale)},
14
- locales: [${t}],
15
- catalogDir: './locales',
16
- format: ${JSON.stringify(e.format)},
17
- include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],
18
- compileOutDir: './src/locales/compiled',
19
- })
20
- `}async function J(e){let t=(0,p.resolve)(e.cwd,`package.json`);if(!(0,f.existsSync)(t)){v.default.error(`No package.json found in current directory.`);return}let n=JSON.parse((0,f.readFileSync)(t,`utf-8`)),r=K({...n.dependencies,...n.devDependencies});v.default.info(`Detected framework: ${r.name}`),r.pluginPackage&&v.default.info(`Recommended plugin: ${r.pluginPackage}`);let i=(0,p.resolve)(e.cwd,`fluenti.config.ts`);if((0,f.existsSync)(i)){v.default.warn(`fluenti.config.ts already exists. Skipping config generation.`);return}let a=await v.default.prompt(`Source locale?`,{type:`text`,default:`en`,placeholder:`en`});if(typeof a==`symbol`)return;let o=await v.default.prompt(`Target locales (comma-separated)?`,{type:`text`,default:`ja,zh-CN`,placeholder:`ja,zh-CN`});if(typeof o==`symbol`)return;let s=await v.default.prompt(`Catalog format?`,{type:`select`,options:[`po`,`json`],initial:`po`});if(typeof s==`symbol`)return;let c=o.split(`,`).map(e=>e.trim()).filter(Boolean);W(a);for(let e of c)W(e);(0,f.writeFileSync)(i,q({sourceLocale:a,locales:[a,...c.filter(e=>e!==a)],format:s}),`utf-8`),v.default.success(`Created fluenti.config.ts`);let l=(0,p.resolve)(e.cwd,`.gitignore`),u=`src/locales/compiled/`;(0,f.existsSync)(l)?(0,f.readFileSync)(l,`utf-8`).includes(u)||((0,f.appendFileSync)(l,`\n# Fluenti compiled catalogs\n${u}\n`),v.default.success(`Updated .gitignore`)):((0,f.writeFileSync)(l,`# Fluenti compiled catalogs\n${u}\n`),v.default.success(`Created .gitignore`));let d=n.scripts??{},m={},h=!1;if(d[`i18n:extract`]||(m[`i18n:extract`]=`fluenti extract`,h=!0),d[`i18n:compile`]||(m[`i18n:compile`]=`fluenti compile`,h=!0),h){let e={...n,scripts:{...d,...m}};(0,f.writeFileSync)(t,JSON.stringify(e,null,2)+`
21
- `,`utf-8`),v.default.success(`Added i18n:extract and i18n:compile scripts to package.json`)}v.default.log(``),v.default.box({title:`Next steps`,message:[r.pluginPackage?`1. Install: pnpm add -D ${r.pluginPackage} @fluenti/cli`:`1. Install: pnpm add -D @fluenti/cli`,r.name===`nextjs`?`2. Add withFluenti() to your next.config.ts`:r.name===`unknown`?`2. Configure your build tool with the framework Vite plugin or @fluenti/next`:`2. Add the Vite plugin to your vite.config.ts (e.g. fluentiVue() from @fluenti/vue/vite-plugin)`,`3. Run: npx fluenti extract`,`4. Translate your messages`,`5. Run: npx fluenti compile`].join(`
22
- `)})}var Y=[`src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`,`layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}`],ie=/import\s*\{[\s\S]*?\binterpolate\b[\s\S]*?\}\s*from\s*['"]@fluenti\/(react|vue|solid)(?:\/components)?['"]/g,ae=/from\s*['"]@fluenti\/core\/internal['"]/g;function X(e){return(0,f.existsSync)(e)?(0,f.readFileSync)(e,`utf-8`):void 0}function oe(e,t){if(t)return(0,p.resolve)(e,t);for(let t of[`fluenti.config.ts`,`fluenti.config.mts`,`fluenti.config.js`,`fluenti.config.mjs`]){let n=(0,p.resolve)(e,t);if((0,f.existsSync)(n))return n}}async function se(e){return(0,_.default)(Y,{cwd:e,absolute:!1,ignore:[`**/node_modules/**`,`**/dist/**`,`**/.fluenti/**`]})}function Z(e,t,n,r){e.push({severity:t,code:n,message:r})}function ce(e){let t=X((0,p.resolve)(e,`package.json`));if(t)return JSON.parse(t)}function le(e){return e?e.includes(`@fluenti/`)&&e.includes(`vite-plugin`):!1}function Q(e,t){return[...e.matchAll(t)].length}async function $(e){let t=[],n=ce(e.cwd);if(!n)return{framework:`unknown`,findings:[{severity:`error`,code:`missing-package-json`,message:`No package.json found in the current directory.`}]};let r=K({...n.dependencies,...n.devDependencies}),i=oe(e.cwd,e.config),a=await se(e.cwd),o=0,s=0,c=!1,l=!1;for(let t of a){let n=(0,f.readFileSync)((0,p.resolve)(e.cwd,t),`utf-8`);o+=Q(n,ie),s+=Q(n,ae),c||=n.includes(`@fluenti/`),l||=n.includes(`from '@fluenti/`)&&n.includes(" t`")}if(o>0&&Z(t,`warning`,`main-entry-interpolate`,`Found ${o} import(s) of interpolate from framework packages. Import interpolate from @fluenti/core/runtime instead.`),s>0&&Z(t,`warning`,`core-internal-imports`,`Found ${s} import(s) from @fluenti/core/internal. Use @fluenti/core/runtime or @fluenti/core/compiler.`),i||Z(t,`warning`,`missing-config`,`No fluenti.config.* file found.`),r.name===`nextjs`){let n=[`next.config.ts`,`next.config.mjs`,`next.config.js`].map(t=>(0,p.resolve)(e.cwd,t)).find(e=>(0,f.existsSync)(e)),r=n?X(n):void 0;c&&!r?.includes(`withFluenti(`)&&Z(t,`error`,`missing-with-fluenti`,`Next.js project imports Fluenti but next.config does not call withFluenti().`)}if(r.name===`react`||r.name===`vue`||r.name===`solid`){let n=[`vite.config.ts`,`vite.config.mts`,`vite.config.js`,`vite.config.mjs`].map(t=>(0,p.resolve)(e.cwd,t)).find(e=>(0,f.existsSync)(e)),r=n?X(n):void 0;(c||l)&&!le(r)&&Z(t,`warning`,`missing-vite-plugin`,`Project imports Fluenti but vite.config does not appear to include a Fluenti Vite plugin.`)}if(i&&(0,f.existsSync)(i))try{let n=await(0,d.loadConfig)(i),r=n.format===`json`?`.json`:`.po`,a=(0,p.resolve)(e.cwd,n.catalogDir,`${n.sourceLocale}${r}`);(0,f.existsSync)(a)||Z(t,`warning`,`missing-source-catalog`,`Source catalog not found: ${a}`);let o=(0,p.resolve)(e.cwd,n.compileOutDir,`index.js`);(0,f.existsSync)(o)||Z(t,`warning`,`missing-compiled-catalogs`,`Compiled catalogs not found at ${o}. Run "fluenti compile".`)}catch(e){Z(t,`error`,`invalid-config`,`Failed to load Fluenti config: ${e instanceof Error?e.message:String(e)}`)}return t.length===0&&Z(t,`info`,`ok`,`No Fluenti migration or configuration issues detected.`),{framework:r.name,findings:t,...i?{configPath:i}:{}}}function ue(e){let t=[`Framework: ${e.framework}`];e.configPath&&t.push(`Config: ${e.configPath}`),t.push(``);for(let n of e.findings){let e=n.severity===`error`?`✖`:n.severity===`warning`?`⚠`:`•`;t.push(`${e} [${n.code}] ${n.message}`)}return t.join(`
23
- `)}Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return O}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return ue}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return C}});
24
- //# sourceMappingURL=doctor-xp8WS8sr.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"doctor-xp8WS8sr.cjs","names":[],"sources":["../src/catalog.ts","../src/json-format.ts","../src/po-format.ts","../src/parallel-compile.ts","../src/extract-cache.ts","../src/codemod.ts","../src/init.ts","../src/doctor.ts"],"sourcesContent":["import type { ExtractedMessage } from '@fluenti/core/compiler'\n\nexport interface CatalogEntry {\n message?: string | undefined\n context?: string | undefined\n comment?: string | undefined\n translation?: string | undefined\n origin?: string | string[] | undefined\n obsolete?: boolean | undefined\n fuzzy?: boolean | undefined\n}\n\nexport type CatalogData = Record<string, CatalogEntry>\n\nexport interface UpdateResult {\n added: number\n unchanged: number\n obsolete: number\n}\n\nexport interface UpdateCatalogOptions {\n stripFuzzy?: boolean\n}\n\n/** Update catalog with newly extracted messages */\nexport function updateCatalog(\n existing: CatalogData,\n extracted: ExtractedMessage[],\n options?: UpdateCatalogOptions,\n): { catalog: CatalogData; result: UpdateResult } {\n const extractedIds = new Set(extracted.map((m) => m.id))\n const consumedCarryForwardIds = new Set<string>()\n const catalog: CatalogData = {}\n let added = 0\n let unchanged = 0\n let obsolete = 0\n\n for (const msg of extracted) {\n const existingEntry = existing[msg.id]\n const carried = existingEntry\n ? undefined\n : findCarryForwardEntry(existing, msg, consumedCarryForwardIds)\n const origin = `${msg.origin.file}:${msg.origin.line}`\n const baseEntry = existingEntry ?? carried?.entry\n\n if (carried) {\n consumedCarryForwardIds.add(carried.id)\n }\n\n if (baseEntry) {\n catalog[msg.id] = {\n ...baseEntry,\n message: msg.message ?? baseEntry.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n obsolete: false,\n }\n unchanged++\n } else if (catalog[msg.id]) {\n // Same ID already seen in this extraction batch — merge origin\n const existing = catalog[msg.id]!\n catalog[msg.id] = {\n ...existing,\n origin: mergeOrigins(existing.origin, origin),\n }\n } else {\n catalog[msg.id] = {\n message: msg.message,\n context: msg.context,\n comment: msg.comment,\n origin,\n }\n added++\n }\n\n if (options?.stripFuzzy) {\n const { fuzzy: _fuzzy, ...rest } = catalog[msg.id]!\n catalog[msg.id] = rest\n }\n }\n\n for (const [id, entry] of Object.entries(existing)) {\n if (!extractedIds.has(id)) {\n const { fuzzy: _fuzzy, ...rest } = entry\n const obsoleteEntry = options?.stripFuzzy\n ? { ...rest, obsolete: true }\n : { ...entry, obsolete: true }\n catalog[id] = obsoleteEntry\n obsolete++\n }\n }\n\n return { catalog, result: { added, unchanged, obsolete } }\n}\n\nfunction mergeOrigins(\n existing: string | string[] | undefined,\n newOrigin: string,\n): string | string[] {\n if (!existing) return newOrigin\n const existingArray = Array.isArray(existing) ? existing : [existing]\n const merged = [...new Set([...existingArray, newOrigin])]\n return merged.length === 1 ? merged[0]! : merged\n}\n\nfunction findCarryForwardEntry(\n existing: CatalogData,\n extracted: ExtractedMessage,\n consumedCarryForwardIds: Set<string>,\n): { id: string; entry: CatalogEntry } | undefined {\n if (!extracted.context) {\n return undefined\n }\n\n const extractedOrigin = `${extracted.origin.file}:${extracted.origin.line}`\n for (const [id, entry] of Object.entries(existing)) {\n if (consumedCarryForwardIds.has(id)) continue\n if (entry.context !== undefined) continue\n if (entry.message !== extracted.message) continue\n if (!sameOrigin(entry.origin, extractedOrigin)) continue\n return { id, entry }\n }\n\n return undefined\n}\n\nfunction sameOrigin(previous: string | string[] | undefined, next: string): boolean {\n if (!previous) return false\n const origins = Array.isArray(previous) ? previous : [previous]\n return origins.some((o) => o === next || originFile(o) === originFile(next))\n}\n\nfunction originFile(origin: string): string {\n const match = origin.match(/^(.*):\\d+$/)\n return match?.[1] ?? origin\n}\n","import type { CatalogData } from './catalog'\n\n/** Read a JSON catalog file */\nexport function readJsonCatalog(content: string): CatalogData {\n const raw = JSON.parse(content) as Record<string, unknown>\n const catalog: CatalogData = {}\n\n for (const [id, entry] of Object.entries(raw)) {\n if (typeof entry === 'object' && entry !== null) {\n const e = entry as Record<string, unknown>\n catalog[id] = {\n message: typeof e['message'] === 'string' ? e['message'] : undefined,\n context: typeof e['context'] === 'string' ? e['context'] : undefined,\n comment: typeof e['comment'] === 'string' ? e['comment'] : undefined,\n translation: typeof e['translation'] === 'string' ? e['translation'] : undefined,\n origin: typeof e['origin'] === 'string'\n ? e['origin']\n : Array.isArray(e['origin']) && (e['origin'] as unknown[]).every((v) => typeof v === 'string')\n ? (e['origin'] as string[])\n : undefined,\n obsolete: typeof e['obsolete'] === 'boolean' ? e['obsolete'] : undefined,\n fuzzy: typeof e['fuzzy'] === 'boolean' ? e['fuzzy'] : undefined,\n }\n }\n }\n\n return catalog\n}\n\n/** Write a catalog to JSON format */\nexport function writeJsonCatalog(catalog: CatalogData): string {\n const output: Record<string, Record<string, unknown>> = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n const obj: Record<string, unknown> = {}\n if (entry.message !== undefined) obj['message'] = entry.message\n if (entry.context !== undefined) obj['context'] = entry.context\n if (entry.comment !== undefined) obj['comment'] = entry.comment\n if (entry.translation !== undefined) obj['translation'] = entry.translation\n if (entry.origin !== undefined) obj['origin'] = entry.origin\n if (entry.obsolete) obj['obsolete'] = true\n if (entry.fuzzy) obj['fuzzy'] = true\n output[id] = obj\n }\n\n return JSON.stringify(output, null, 2) + '\\n'\n}\n","import type { CatalogData } from './catalog'\nimport { hashMessage } from '@fluenti/core/compiler'\nimport * as gettextParser from 'gettext-parser'\n\nconst CUSTOM_ID_MARKER = 'fluenti-id:'\n\ninterface POTranslation {\n msgid: string\n msgctxt?: string\n msgstr: string[]\n comments?: {\n reference?: string\n extracted?: string\n flag?: string\n translator?: string\n previous?: string\n }\n}\n\ninterface POData {\n headers?: Record<string, string>\n translations: Record<string, Record<string, POTranslation>>\n obsolete?: Record<string, Record<string, POTranslation>>\n}\n\ninterface ParsedExtractedComment {\n comment?: string\n customId?: string\n sourceMessage?: string\n}\n\nfunction processPoEntries(\n contextMap: Record<string, Record<string, POTranslation>>,\n catalog: CatalogData,\n isObsolete: boolean,\n): void {\n for (const [contextKey, entries] of Object.entries(contextMap)) {\n for (const [msgid, entry] of Object.entries(entries)) {\n if (!msgid) continue\n\n const context = contextKey || entry.msgctxt || undefined\n const translation = entry.msgstr?.[0] ?? undefined\n const rawReference = entry.comments?.reference ?? undefined\n const origin = rawReference?.includes('\\n')\n ? rawReference.split('\\n').map((r: string) => r.trim()).filter(Boolean)\n : rawReference?.includes(' ')\n ? rawReference.split(/\\s+/).filter(Boolean)\n : rawReference\n const normalizedOrigin = Array.isArray(origin) && origin.length === 1 ? origin[0] : origin\n const isFuzzy = !isObsolete && (entry.comments?.flag?.includes('fuzzy') ?? false)\n const { comment, customId, sourceMessage } = parseExtractedComment(entry.comments?.extracted)\n // When a custom ID is present, the msgid may be the custom key (not source text),\n // so accept sourceMessage directly without requiring a hash match.\n // For hash-ID entries (no customId), verify the hash to prevent double-hashing.\n const resolvedSourceMessage = sourceMessage\n && (customId !== undefined || hashMessage(sourceMessage, context) === msgid)\n ? sourceMessage\n : undefined\n const id = customId\n ?? (resolvedSourceMessage ? msgid : hashMessage(msgid, context))\n\n catalog[id] = {\n message: resolvedSourceMessage ?? msgid,\n ...(context !== undefined ? { context } : {}),\n ...(comment !== undefined ? { comment } : {}),\n ...(translation ? { translation } : {}),\n ...(normalizedOrigin !== undefined ? { origin: normalizedOrigin } : {}),\n ...(isFuzzy ? { fuzzy: true } : {}),\n ...(isObsolete ? { obsolete: true } : {}),\n }\n }\n }\n}\n\n/** Read a PO catalog file */\nexport function readPoCatalog(content: string): CatalogData {\n const po = gettextParser.po.parse(content) as POData\n const catalog: CatalogData = {}\n\n processPoEntries(po.translations ?? {}, catalog, false)\n if (po.obsolete) {\n processPoEntries(po.obsolete, catalog, true)\n }\n\n return catalog\n}\n\n/** Write a catalog to PO format */\nexport function writePoCatalog(catalog: CatalogData): string {\n const translations: POData['translations'] = {\n '': {\n '': {\n msgid: '',\n msgstr: ['Content-Type: text/plain; charset=UTF-8\\n'],\n },\n },\n }\n const obsolete: POData['obsolete'] = {}\n\n for (const [id, entry] of Object.entries(catalog)) {\n // Use custom ID as msgid for non-hash entries to prevent collision when two\n // entries share the same source message but have different custom IDs.\n // Hash-ID entries keep source message as msgid (PO-friendly for translators).\n const isHashId = entry.message !== undefined && hashMessage(entry.message, entry.context) === id\n const msgid = isHashId ? (entry.message ?? id) : id\n const poEntry: POTranslation = {\n msgid,\n ...(entry.context !== undefined ? { msgctxt: entry.context } : {}),\n msgstr: [entry.translation ?? ''],\n }\n\n const comments: POTranslation['comments'] = {}\n if (entry.origin && !entry.obsolete) {\n comments.reference = Array.isArray(entry.origin)\n ? entry.origin.join('\\n')\n : entry.origin\n }\n const extractedComment = buildExtractedComment(id, entry.message ?? id, entry.context, entry.comment)\n if (extractedComment) {\n comments.extracted = extractedComment\n }\n if (entry.fuzzy && !entry.obsolete) {\n comments.flag = 'fuzzy'\n }\n if (comments.reference || comments.extracted || comments.flag) {\n poEntry.comments = comments\n }\n\n if (entry.obsolete) {\n const contextKey = entry.context ?? ''\n obsolete[contextKey] ??= {}\n obsolete[contextKey]![poEntry.msgid] = poEntry\n } else {\n const contextKey = entry.context ?? ''\n translations[contextKey] ??= {}\n translations[contextKey]![poEntry.msgid] = poEntry\n }\n }\n\n const poData: POData = {\n headers: {\n 'Content-Type': 'text/plain; charset=UTF-8',\n },\n translations,\n ...(Object.keys(obsolete).length > 0 ? { obsolete } : {}),\n }\n\n const buffer = gettextParser.po.compile(poData as Parameters<typeof gettextParser.po.compile>[0])\n return buffer.toString()\n}\n\nfunction parseExtractedComment(\n extracted: string | undefined,\n): ParsedExtractedComment {\n if (!extracted) {\n return {}\n }\n\n const lines = extracted.split('\\n').map((line) => line.trim()).filter(Boolean)\n let customId: string | undefined\n let sourceMessage: string | undefined\n const commentLines: string[] = []\n\n for (const line of lines) {\n if (line.startsWith(CUSTOM_ID_MARKER)) {\n customId = line.slice(CUSTOM_ID_MARKER.length).trim() || undefined\n continue\n }\n if (line.startsWith('msg`') && line.endsWith('`')) {\n sourceMessage = line.slice(4, -1)\n continue\n }\n if (line.startsWith('Trans: ')) {\n sourceMessage = normalizeRichTextComment(line.slice('Trans: '.length))\n continue\n }\n commentLines.push(line)\n }\n\n return {\n ...(commentLines.length > 0 ? { comment: commentLines.join('\\n') } : {}),\n ...(customId ? { customId } : {}),\n ...(sourceMessage ? { sourceMessage } : {}),\n }\n}\n\nfunction normalizeRichTextComment(comment: string): string {\n let stack: Array<{ tag: string; index: number }> = []\n let nextIndex = 0\n\n return comment.replace(/<\\/?([a-zA-Z][\\w-]*)>/g, (match, rawTag: string) => {\n const tag = rawTag\n if (match.startsWith('</')) {\n for (let index = stack.length - 1; index >= 0; index--) {\n const entry = stack[index]\n if (entry?.tag !== tag) continue\n stack = stack.filter((_, i) => i !== index)\n return `</${entry.index}>`\n }\n return match\n }\n\n const index = nextIndex++\n stack.push({ tag, index })\n return `<${index}>`\n })\n}\n\nfunction buildExtractedComment(\n id: string,\n message: string,\n context: string | undefined,\n comment: string | undefined,\n): string | undefined {\n const lines: string[] = []\n\n if (comment) {\n lines.push(comment)\n }\n\n if (id !== hashMessage(message, context)) {\n lines.push(`${CUSTOM_ID_MARKER} ${id}`)\n // Preserve source message so round-trip works when msgid is the custom ID (not source text)\n lines.push(`msg\\`${message}\\``)\n }\n\n return lines.length > 0 ? lines.join('\\n') : undefined\n}\n","import { Worker } from 'node:worker_threads'\nimport { availableParallelism } from 'node:os'\nimport { existsSync } from 'node:fs'\nimport { resolve, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { CatalogData } from './catalog'\nimport type { CompileOptions, CompileStats } from './compile'\nimport type { CompileWorkerRequest, CompileWorkerResponse } from './compile-worker'\n\nexport interface ParallelCompileTask {\n locale: string\n catalog: CatalogData\n allIds: string[]\n sourceLocale: string\n options?: CompileOptions\n}\n\nexport interface ParallelCompileResult {\n locale: string\n code: string\n stats: CompileStats\n}\n\n/** Default timeout per worker in milliseconds (30 seconds) */\nconst WORKER_TIMEOUT_MS = 30_000\n\nfunction getWorkerPath(): string {\n const thisDir = typeof __dirname !== 'undefined'\n ? __dirname\n : dirname(fileURLToPath(import.meta.url))\n return resolve(thisDir, 'compile-worker.js')\n}\n\n/**\n * Compile multiple locales in parallel using worker threads.\n *\n * - Single task → returns result directly (no worker overhead)\n * - Multiple tasks → spawns min(tasks, availableParallelism()) workers\n * - Workers are created per-call and destroyed after completion\n *\n * Falls back to in-process compilation when the compiled worker is unavailable\n * (e.g. when running from TypeScript source in development/tests).\n */\nexport async function parallelCompile(\n tasks: ParallelCompileTask[],\n concurrency?: number,\n): Promise<ParallelCompileResult[]> {\n if (tasks.length === 0) return []\n\n // Single task: compile in-process, no worker overhead\n if (tasks.length === 1) {\n const { compileCatalog } = await import('./compile')\n const task = tasks[0]!\n const { code, stats } = compileCatalog(task.catalog, task.locale, task.allIds, task.sourceLocale, task.options)\n return [{ locale: task.locale, code, stats }]\n }\n\n const workerPath = getWorkerPath()\n\n // If compiled worker doesn't exist (dev/test), fall back to concurrent in-process compilation\n if (!existsSync(workerPath)) {\n return inProcessParallelCompile(tasks)\n }\n\n const maxWorkers = concurrency ?? Math.min(tasks.length, availableParallelism())\n const results: ParallelCompileResult[] = []\n const queue = [...tasks]\n let rejected = false\n\n return new Promise<ParallelCompileResult[]>((resolveAll, rejectAll) => {\n let activeWorkers = 0\n\n function spawnNext(): void {\n if (rejected) return\n const task = queue.shift()\n if (!task) {\n if (activeWorkers === 0) {\n resolveAll(results)\n }\n return\n }\n\n activeWorkers++\n const worker = new Worker(workerPath)\n\n const timer = setTimeout(() => {\n if (!rejected) {\n rejected = true\n activeWorkers--\n worker.terminate()\n rejectAll(new Error(`Worker timed out after ${WORKER_TIMEOUT_MS}ms compiling locale \"${task.locale}\"`))\n }\n }, WORKER_TIMEOUT_MS)\n\n worker.on('message', (response: CompileWorkerResponse) => {\n clearTimeout(timer)\n if (rejected) return\n if (response.error) {\n rejected = true\n activeWorkers--\n worker.terminate()\n rejectAll(new Error(`Failed to compile locale \"${response.locale}\": ${response.error}`))\n return\n }\n results.push({\n locale: response.locale,\n code: response.code,\n stats: response.stats,\n })\n activeWorkers--\n worker.terminate()\n spawnNext()\n })\n\n worker.on('error', (err: Error) => {\n clearTimeout(timer)\n if (!rejected) {\n rejected = true\n worker.terminate()\n rejectAll(new Error(`Worker error compiling locale \"${task.locale}\": ${err.message}`))\n }\n })\n\n worker.on('exit', (code) => {\n clearTimeout(timer)\n if (code !== 0 && !rejected) {\n rejected = true\n rejectAll(new Error(`Worker exited with code ${code} while compiling locale \"${task.locale}\"`))\n }\n })\n\n const request: CompileWorkerRequest = {\n locale: task.locale,\n catalog: task.catalog,\n allIds: task.allIds,\n sourceLocale: task.sourceLocale,\n options: task.options,\n }\n worker.postMessage(request)\n }\n\n // Start initial batch of workers\n const initialBatch = Math.min(maxWorkers, queue.length)\n for (let i = 0; i < initialBatch; i++) {\n spawnNext()\n }\n })\n}\n\n/**\n * In-process fallback for parallel compilation.\n * Uses Promise.all for concurrency when workers are unavailable.\n */\nasync function inProcessParallelCompile(\n tasks: ParallelCompileTask[],\n): Promise<ParallelCompileResult[]> {\n const { compileCatalog } = await import('./compile')\n return Promise.all(\n tasks.map((task) => {\n const { code, stats } = compileCatalog(task.catalog, task.locale, task.allIds, task.sourceLocale, task.options)\n return { locale: task.locale, code, stats }\n }),\n )\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport type { ExtractedMessage } from '@fluenti/core/compiler'\n\n/** Cache format version — bump when the structure changes */\nconst CACHE_VERSION = '1'\n\ninterface ExtractCacheEntry {\n mtime: number\n size: number\n messages: ExtractedMessage[]\n}\n\ninterface ExtractCacheData {\n version: string\n entries: Record<string, ExtractCacheEntry>\n}\n\n/**\n * File-level extract cache that skips re-extraction for unchanged files.\n *\n * Cache is keyed by file path, with mtime + size as change detection.\n */\nexport class ExtractCache {\n private data: ExtractCacheData\n private cachePath: string\n private dirty = false\n\n constructor(catalogDir: string, projectId?: string) {\n const cacheDir = projectId\n ? resolve(catalogDir, '.cache', projectId)\n : resolve(catalogDir, '.cache')\n this.cachePath = resolve(cacheDir, 'extract-cache.json')\n this.data = this.load()\n }\n\n /**\n * Check if a file has changed since the last extraction.\n * Returns cached messages if unchanged, undefined if re-extraction needed.\n */\n get(filePath: string): ExtractedMessage[] | undefined {\n const entry = this.data.entries[filePath]\n if (!entry) return undefined\n\n try {\n const stat = statSync(filePath)\n if (stat.mtimeMs === entry.mtime && stat.size === entry.size) {\n return entry.messages\n }\n } catch {\n // File no longer exists or can't be stat'd — cache miss\n }\n\n return undefined\n }\n\n /**\n * Update the cache for a file after extraction.\n */\n set(filePath: string, messages: ExtractedMessage[]): void {\n try {\n const stat = statSync(filePath)\n this.data.entries[filePath] = {\n mtime: stat.mtimeMs,\n size: stat.size,\n messages,\n }\n this.dirty = true\n } catch {\n // File doesn't exist — skip caching\n }\n }\n\n /**\n * Remove entries for files that no longer exist in the file list.\n */\n prune(currentFiles: Set<string>): void {\n for (const filePath of Object.keys(this.data.entries)) {\n if (!currentFiles.has(filePath)) {\n delete this.data.entries[filePath]\n this.dirty = true\n }\n }\n }\n\n /**\n * Write the cache to disk if any changes were made.\n */\n save(): void {\n if (!this.dirty) return\n\n try {\n mkdirSync(dirname(this.cachePath), { recursive: true })\n writeFileSync(this.cachePath, JSON.stringify(this.data), 'utf-8')\n this.dirty = false\n } catch {\n // Cache write failure is non-fatal — next run will re-extract\n }\n }\n\n /** Number of cached entries */\n get size(): number {\n return Object.keys(this.data.entries).length\n }\n\n private load(): ExtractCacheData {\n try {\n if (existsSync(this.cachePath)) {\n const raw = readFileSync(this.cachePath, 'utf-8')\n const parsed = JSON.parse(raw) as ExtractCacheData\n if (parsed.version === CACHE_VERSION) {\n return parsed\n }\n }\n } catch {\n // Corrupt or unreadable cache — start fresh\n }\n\n return { version: CACHE_VERSION, entries: {} }\n }\n}\n","import { readFileSync, writeFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport fg from 'fast-glob'\n\nconst FRAMEWORKS = ['react', 'vue', 'solid'] as const\nconst COMPONENT_EXPORTS = new Set(['Trans', 'Plural', 'Select', 'DateTime', 'NumberFormat'])\n\ntype FrameworkName = (typeof FRAMEWORKS)[number]\n\nexport interface CodemodOptions {\n cwd: string\n include?: string[]\n write?: boolean\n}\n\nexport interface CodemodFileResult {\n file: string\n changed: boolean\n}\n\nexport interface CodemodResult {\n changedFiles: CodemodFileResult[]\n changedCount: number\n}\n\ninterface ParsedSpecifier {\n imported: string\n local: string\n isType: boolean\n}\n\nconst DEFAULT_GLOBS = [\n 'src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n]\n\nfunction parseSpecifier(raw: string): ParsedSpecifier {\n const trimmed = raw.trim()\n const isType = trimmed.startsWith('type ')\n const normalized = isType ? trimmed.slice(5).trim() : trimmed\n const parts = normalized.split(/\\s+as\\s+/)\n const imported = parts[0]!.trim()\n const local = (parts[1] ?? parts[0])!.trim()\n return {\n imported,\n local,\n isType,\n }\n}\n\nfunction formatSpecifier(specifier: ParsedSpecifier): string {\n const prefix = specifier.isType ? 'type ' : ''\n if (specifier.imported === specifier.local) {\n return `${prefix}${specifier.imported}`\n }\n return `${prefix}${specifier.imported} as ${specifier.local}`\n}\n\nfunction dedupeSpecifiers(specifiers: ParsedSpecifier[]): ParsedSpecifier[] {\n const seen = new Set<string>()\n const result: ParsedSpecifier[] = []\n for (const specifier of specifiers) {\n const key = `${specifier.isType ? 'type:' : 'value:'}${specifier.imported}:${specifier.local}`\n if (seen.has(key)) continue\n seen.add(key)\n result.push(specifier)\n }\n return result\n}\n\nfunction mergeNamedImports(code: string, source: string): string {\n const pattern = new RegExp(`import\\\\s*\\\\{([\\\\s\\\\S]*?)\\\\}\\\\s*from\\\\s*['\"]${source.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}['\"];?\\\\n?`, 'g')\n const matches = [...code.matchAll(pattern)]\n if (matches.length <= 1) return code\n\n const specifiers = dedupeSpecifiers(\n matches.flatMap((match) => match[1]!.split(',').map((entry) => parseSpecifier(entry)).filter((entry) => entry.imported.length > 0)),\n )\n const mergedImport = `import { ${specifiers.map(formatSpecifier).join(', ')} } from '${source}'\\n`\n\n let first = true\n return code.replace(pattern, () => {\n if (first) {\n first = false\n return mergedImport\n }\n return ''\n })\n}\n\nexport function rewriteFluentiImports(source: string): { code: string; changed: boolean } {\n let code = source\n let changed = false\n let renameSolidFactory = false\n\n const importPattern = /import\\s*\\{([\\s\\S]*?)\\}\\s*from\\s*['\"](@fluenti\\/(react|vue|solid)(?:\\/components)?)['\"];?/g\n\n code = code.replace(importPattern, (full, specifiersRaw: string, sourcePath: string, framework: FrameworkName) => {\n const specifiers = specifiersRaw\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n .map(parseSpecifier)\n\n const main: ParsedSpecifier[] = []\n const components: ParsedSpecifier[] = []\n const runtime: ParsedSpecifier[] = []\n\n for (const specifier of specifiers) {\n const next = { ...specifier }\n if (framework === 'solid' && next.imported === 'createFluentiContext') {\n next.imported = 'createFluenti'\n if (next.local === 'createFluentiContext') {\n next.local = 'createFluenti'\n renameSolidFactory = true\n }\n }\n\n if (next.imported === 'interpolate') {\n runtime.push({ ...next, isType: false })\n continue\n }\n\n if (!next.isType && COMPONENT_EXPORTS.has(next.imported) && sourcePath.endsWith('/components')) {\n components.push(next)\n continue\n }\n\n main.push(next)\n }\n\n const nextImports: string[] = []\n if (main.length > 0 && !sourcePath.endsWith('/components')) {\n nextImports.push(`import { ${dedupeSpecifiers(main).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}'`)\n }\n if (components.length > 0) {\n nextImports.push(`import { ${dedupeSpecifiers(components).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}/components'`)\n }\n if (runtime.length > 0) {\n nextImports.push(`import { ${dedupeSpecifiers(runtime).map(formatSpecifier).join(', ')} } from '@fluenti/core/runtime'`)\n }\n if (main.length > 0 && sourcePath.endsWith('/components')) {\n nextImports.unshift(`import { ${dedupeSpecifiers(main).map(formatSpecifier).join(', ')} } from '@fluenti/${framework}'`)\n }\n\n const replacement = nextImports.join('\\n')\n if (replacement !== full) {\n changed = true\n }\n return replacement\n })\n\n if (renameSolidFactory) {\n const renamed = code.replace(/\\bcreateFluentiContext\\b/g, 'createFluenti')\n if (renamed !== code) {\n code = renamed\n changed = true\n }\n }\n\n for (const sourcePath of [\n '@fluenti/react',\n '@fluenti/react/components',\n '@fluenti/vue',\n '@fluenti/vue/components',\n '@fluenti/solid',\n '@fluenti/solid/components',\n '@fluenti/core/runtime',\n ]) {\n const merged = mergeNamedImports(code, sourcePath)\n if (merged !== code) {\n code = merged\n changed = true\n }\n }\n\n return { code, changed }\n}\n\nexport async function runCodemod(options: CodemodOptions): Promise<CodemodResult> {\n const include = options.include && options.include.length > 0 ? options.include : DEFAULT_GLOBS\n const files = await fg(include, {\n cwd: options.cwd,\n absolute: false,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.fluenti/**'],\n })\n\n const changedFiles: CodemodFileResult[] = []\n\n for (const file of files) {\n const path = resolve(options.cwd, file)\n const current = readFileSync(path, 'utf-8')\n const rewritten = rewriteFluentiImports(current)\n if (!rewritten.changed) continue\n\n if (options.write) {\n writeFileSync(path, rewritten.code, 'utf-8')\n }\n\n changedFiles.push({ file, changed: true })\n }\n\n return {\n changedFiles,\n changedCount: changedFiles.length,\n }\n}\n","import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport consola from 'consola'\n\nconst LOCALE_PATTERN = /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$/\n\nexport function validateLocale(locale: string): string {\n if (!LOCALE_PATTERN.test(locale)) {\n throw new Error(`Invalid locale format: \"${locale}\"`)\n }\n return locale\n}\n\nexport interface DetectedFramework {\n name: 'nextjs' | 'nuxt' | 'vue' | 'solid' | 'solidstart' | 'react' | 'unknown'\n pluginPackage: string | null\n}\n\nconst FRAMEWORK_DETECTION: Array<{\n dep: string\n name: DetectedFramework['name']\n pluginPackage: string\n}> = [\n { dep: 'next', name: 'nextjs', pluginPackage: '@fluenti/next' },\n { dep: 'nuxt', name: 'nuxt', pluginPackage: '@fluenti/vue' },\n { dep: '@solidjs/start', name: 'solidstart', pluginPackage: '@fluenti/solid' },\n { dep: 'vue', name: 'vue', pluginPackage: '@fluenti/vue' },\n { dep: 'solid-js', name: 'solid', pluginPackage: '@fluenti/solid' },\n { dep: 'react', name: 'react', pluginPackage: '@fluenti/react' },\n]\n\n/**\n * Detect the framework from package.json dependencies.\n */\nexport function detectFramework(deps: Record<string, string>): DetectedFramework {\n for (const entry of FRAMEWORK_DETECTION) {\n if (entry.dep in deps) {\n return { name: entry.name, pluginPackage: entry.pluginPackage }\n }\n }\n return { name: 'unknown', pluginPackage: null }\n}\n\n/**\n * Generate fluenti.config.ts content.\n */\nexport function generateFluentiConfig(opts: {\n sourceLocale: string\n locales: string[]\n format: 'po' | 'json'\n}): string {\n const localesList = opts.locales.map((l) => JSON.stringify(l)).join(', ')\n return `import { defineConfig } from '@fluenti/cli'\n\nexport default defineConfig({\n sourceLocale: ${JSON.stringify(opts.sourceLocale)},\n locales: [${localesList}],\n catalogDir: './locales',\n format: ${JSON.stringify(opts.format)},\n include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],\n compileOutDir: './src/locales/compiled',\n})\n`\n}\n\n/**\n * Interactive init flow.\n */\nexport async function runInit(options: { cwd: string }): Promise<void> {\n const pkgPath = resolve(options.cwd, 'package.json')\n if (!existsSync(pkgPath)) {\n consola.error('No package.json found in current directory.')\n return\n }\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n scripts?: Record<string, string>\n }\n const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }\n const framework = detectFramework(allDeps)\n\n consola.info(`Detected framework: ${framework.name}`)\n if (framework.pluginPackage) {\n consola.info(`Recommended plugin: ${framework.pluginPackage}`)\n }\n\n // Check if config already exists\n const configPath = resolve(options.cwd, 'fluenti.config.ts')\n if (existsSync(configPath)) {\n consola.warn('fluenti.config.ts already exists. Skipping config generation.')\n return\n }\n\n // Prompt for configuration\n const sourceLocale = await consola.prompt('Source locale?', {\n type: 'text',\n default: 'en',\n placeholder: 'en',\n }) as unknown as string\n\n if (typeof sourceLocale === 'symbol') return // user cancelled\n\n const targetLocalesInput = await consola.prompt('Target locales (comma-separated)?', {\n type: 'text',\n default: 'ja,zh-CN',\n placeholder: 'ja,zh-CN',\n }) as unknown as string\n\n if (typeof targetLocalesInput === 'symbol') return\n\n const format = await consola.prompt('Catalog format?', {\n type: 'select',\n options: ['po', 'json'],\n initial: 'po',\n }) as unknown as string\n\n if (typeof format === 'symbol') return\n\n const targetLocales = targetLocalesInput.split(',').map((l) => l.trim()).filter(Boolean)\n\n // Validate locale formats\n validateLocale(sourceLocale)\n for (const locale of targetLocales) {\n validateLocale(locale)\n }\n\n const allLocales = [sourceLocale, ...targetLocales.filter((l) => l !== sourceLocale)]\n\n // Write config\n const configContent = generateFluentiConfig({\n sourceLocale,\n locales: allLocales,\n format: format as 'po' | 'json',\n })\n writeFileSync(configPath, configContent, 'utf-8')\n consola.success('Created fluenti.config.ts')\n\n // Append to .gitignore\n const gitignorePath = resolve(options.cwd, '.gitignore')\n const gitignoreEntry = 'src/locales/compiled/'\n if (existsSync(gitignorePath)) {\n const existing = readFileSync(gitignorePath, 'utf-8')\n if (!existing.includes(gitignoreEntry)) {\n appendFileSync(gitignorePath, `\\n# Fluenti compiled catalogs\\n${gitignoreEntry}\\n`)\n consola.success('Updated .gitignore')\n }\n } else {\n writeFileSync(gitignorePath, `# Fluenti compiled catalogs\\n${gitignoreEntry}\\n`)\n consola.success('Created .gitignore')\n }\n\n // Patch package.json scripts\n const existingScripts = pkg.scripts ?? {}\n const newScripts: Record<string, string> = {}\n let scriptsChanged = false\n if (!existingScripts['i18n:extract']) {\n newScripts['i18n:extract'] = 'fluenti extract'\n scriptsChanged = true\n }\n if (!existingScripts['i18n:compile']) {\n newScripts['i18n:compile'] = 'fluenti compile'\n scriptsChanged = true\n }\n if (scriptsChanged) {\n const updatedPkg = {\n ...pkg,\n scripts: { ...existingScripts, ...newScripts },\n }\n writeFileSync(pkgPath, JSON.stringify(updatedPkg, null, 2) + '\\n', 'utf-8')\n consola.success('Added i18n:extract and i18n:compile scripts to package.json')\n }\n\n // Print next steps\n consola.log('')\n consola.box({\n title: 'Next steps',\n message: [\n framework.pluginPackage\n ? `1. Install: pnpm add -D ${framework.pluginPackage} @fluenti/cli`\n : '1. Install: pnpm add -D @fluenti/cli',\n framework.name === 'nextjs'\n ? '2. Add withFluenti() to your next.config.ts'\n : framework.name !== 'unknown'\n ? '2. Add the Vite plugin to your vite.config.ts (e.g. fluentiVue() from @fluenti/vue/vite-plugin)'\n : '2. Configure your build tool with the framework Vite plugin or @fluenti/next',\n '3. Run: npx fluenti extract',\n '4. Translate your messages',\n '5. Run: npx fluenti compile',\n ].join('\\n'),\n })\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport fg from 'fast-glob'\nimport { detectFramework, type DetectedFramework } from './init'\nimport { loadConfig } from './config-loader'\n\nexport type DoctorSeverity = 'error' | 'warning' | 'info'\n\nexport interface DoctorFinding {\n severity: DoctorSeverity\n code: string\n message: string\n}\n\nexport interface DoctorReport {\n framework: DetectedFramework['name']\n findings: DoctorFinding[]\n configPath?: string\n}\n\nexport interface DoctorOptions {\n cwd: string\n config?: string\n}\n\nconst SOURCE_GLOBS = [\n 'src/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'app/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'pages/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'components/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n 'layouts/**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,vue,mdx}',\n]\n\nconst MAIN_INTERPOLATE_IMPORT_RE = /import\\s*\\{[\\s\\S]*?\\binterpolate\\b[\\s\\S]*?\\}\\s*from\\s*['\"]@fluenti\\/(react|vue|solid)(?:\\/components)?['\"]/g\nconst INTERNAL_IMPORT_RE = /from\\s*['\"]@fluenti\\/core\\/internal['\"]/g\n\nfunction readIfExists(path: string): string | undefined {\n return existsSync(path) ? readFileSync(path, 'utf-8') : undefined\n}\n\nfunction detectConfigPath(cwd: string, explicit?: string): string | undefined {\n if (explicit) return resolve(cwd, explicit)\n\n for (const candidate of ['fluenti.config.ts', 'fluenti.config.mts', 'fluenti.config.js', 'fluenti.config.mjs']) {\n const resolved = resolve(cwd, candidate)\n if (existsSync(resolved)) return resolved\n }\n\n return undefined\n}\n\nasync function collectSourceFiles(cwd: string): Promise<string[]> {\n return fg(SOURCE_GLOBS, {\n cwd,\n absolute: false,\n ignore: ['**/node_modules/**', '**/dist/**', '**/.fluenti/**'],\n })\n}\n\nfunction pushFinding(findings: DoctorFinding[], severity: DoctorSeverity, code: string, message: string): void {\n findings.push({ severity, code, message })\n}\n\nfunction getPackageJson(cwd: string): { dependencies?: Record<string, string>; devDependencies?: Record<string, string> } | undefined {\n const pkgPath = resolve(cwd, 'package.json')\n const content = readIfExists(pkgPath)\n if (!content) return undefined\n return JSON.parse(content) as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n}\n\nfunction hasVitePlugin(content: string | undefined): boolean {\n if (!content) return false\n return content.includes('@fluenti/') && content.includes('vite-plugin')\n}\n\nfunction countMatches(content: string, pattern: RegExp): number {\n return [...content.matchAll(pattern)].length\n}\n\nexport async function runDoctor(options: DoctorOptions): Promise<DoctorReport> {\n const findings: DoctorFinding[] = []\n const pkg = getPackageJson(options.cwd)\n\n if (!pkg) {\n return {\n framework: 'unknown',\n findings: [{ severity: 'error', code: 'missing-package-json', message: 'No package.json found in the current directory.' }],\n }\n }\n\n const framework = detectFramework({ ...pkg.dependencies, ...pkg.devDependencies })\n const configPath = detectConfigPath(options.cwd, options.config)\n const files = await collectSourceFiles(options.cwd)\n\n let mainInterpolateImports = 0\n let internalImports = 0\n let importsFluenti = false\n let usesCompileTimeT = false\n\n for (const file of files) {\n const content = readFileSync(resolve(options.cwd, file), 'utf-8')\n mainInterpolateImports += countMatches(content, MAIN_INTERPOLATE_IMPORT_RE)\n internalImports += countMatches(content, INTERNAL_IMPORT_RE)\n importsFluenti = importsFluenti || content.includes('@fluenti/')\n usesCompileTimeT = usesCompileTimeT || content.includes(\"from '@fluenti/\") && content.includes(' t`')\n }\n\n if (mainInterpolateImports > 0) {\n pushFinding(\n findings,\n 'warning',\n 'main-entry-interpolate',\n `Found ${mainInterpolateImports} import(s) of interpolate from framework packages. Import interpolate from @fluenti/core/runtime instead.`,\n )\n }\n\n if (internalImports > 0) {\n pushFinding(\n findings,\n 'warning',\n 'core-internal-imports',\n `Found ${internalImports} import(s) from @fluenti/core/internal. Use @fluenti/core/runtime or @fluenti/core/compiler.`,\n )\n }\n\n if (!configPath) {\n pushFinding(findings, 'warning', 'missing-config', 'No fluenti.config.* file found.')\n }\n\n if (framework.name === 'nextjs') {\n const nextConfig = ['next.config.ts', 'next.config.mjs', 'next.config.js']\n .map((name) => resolve(options.cwd, name))\n .find((candidate) => existsSync(candidate))\n const nextContent = nextConfig ? readIfExists(nextConfig) : undefined\n if (importsFluenti && !nextContent?.includes('withFluenti(')) {\n pushFinding(findings, 'error', 'missing-with-fluenti', 'Next.js project imports Fluenti but next.config does not call withFluenti().')\n }\n }\n\n if (framework.name === 'react' || framework.name === 'vue' || framework.name === 'solid') {\n const viteConfig = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs']\n .map((name) => resolve(options.cwd, name))\n .find((candidate) => existsSync(candidate))\n const viteContent = viteConfig ? readIfExists(viteConfig) : undefined\n if ((importsFluenti || usesCompileTimeT) && !hasVitePlugin(viteContent)) {\n pushFinding(findings, 'warning', 'missing-vite-plugin', 'Project imports Fluenti but vite.config does not appear to include a Fluenti Vite plugin.')\n }\n }\n\n if (configPath && existsSync(configPath)) {\n try {\n const config = await loadConfig(configPath)\n const sourceCatalogExt = config.format === 'json' ? '.json' : '.po'\n const sourceCatalog = resolve(options.cwd, config.catalogDir, `${config.sourceLocale}${sourceCatalogExt}`)\n if (!existsSync(sourceCatalog)) {\n pushFinding(findings, 'warning', 'missing-source-catalog', `Source catalog not found: ${sourceCatalog}`)\n }\n\n const compiledIndex = resolve(options.cwd, config.compileOutDir, 'index.js')\n if (!existsSync(compiledIndex)) {\n pushFinding(findings, 'warning', 'missing-compiled-catalogs', `Compiled catalogs not found at ${compiledIndex}. Run \"fluenti compile\".`)\n }\n } catch (error) {\n pushFinding(\n findings,\n 'error',\n 'invalid-config',\n `Failed to load Fluenti config: ${error instanceof Error ? error.message : String(error)}`,\n )\n }\n }\n\n if (findings.length === 0) {\n pushFinding(findings, 'info', 'ok', 'No Fluenti migration or configuration issues detected.')\n }\n\n return {\n framework: framework.name,\n findings,\n ...(configPath ? { configPath } : {}),\n }\n}\n\nexport function formatDoctorReport(report: DoctorReport): string {\n const lines: string[] = [`Framework: ${report.framework}`]\n if (report.configPath) {\n lines.push(`Config: ${report.configPath}`)\n }\n lines.push('')\n\n for (const finding of report.findings) {\n const prefix = finding.severity === 'error'\n ? '✖'\n : finding.severity === 'warning'\n ? '⚠'\n : '•'\n lines.push(`${prefix} [${finding.code}] ${finding.message}`)\n }\n\n return lines.join('\\n')\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/config.ts","../src/compile-runner.ts","../src/extract-runner.ts"],"sourcesContent":["import type { FluentiBuildConfig } from '@fluenti/core/compiler'\n\n/**\n * Define a Fluenti configuration with full type inference and IDE autocompletion.\n *\n * @example\n * ```ts\n * // fluenti.config.ts\n * import { defineConfig } from '@fluenti/cli'\n *\n * export default defineConfig({\n * sourceLocale: 'en',\n * locales: ['en', 'ja', 'zh-CN'],\n * catalogDir: './locales',\n * format: 'po',\n * include: ['./src/**\\/*.{vue,tsx,ts}'],\n * compileOutDir: './src/locales/compiled',\n * })\n * ```\n */\nexport function defineConfig(config: Partial<FluentiBuildConfig>): Partial<FluentiBuildConfig> {\n return config\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { resolveLocaleCodes, createPluginRunner } from '@fluenti/core/compiler'\nimport type { PluginCompileContext } from '@fluenti/core/compiler'\nimport { loadConfig } from './config-loader'\nimport { compileCatalog, compileIndex, collectAllIds, compileTypeDeclaration } from './compile'\nimport { parallelCompile } from './parallel-compile'\nimport type { CatalogData } from './catalog'\nimport { readJsonCatalog } from './json-format'\nimport { readPoCatalog } from './po-format'\n\nfunction readCatalog(filePath: string, format: 'json' | 'po'): CatalogData {\n if (!existsSync(filePath)) return {}\n const content = readFileSync(filePath, 'utf-8')\n return format === 'json' ? readJsonCatalog(content) : readPoCatalog(content)\n}\n\n/** Build a messages record (id → translation or message) from catalog data */\nfunction catalogToMessages(catalog: CatalogData): Record<string, string> {\n const messages: Record<string, string> = {}\n for (const [id, entry] of Object.entries(catalog)) {\n if (entry.obsolete) continue\n const text = entry.translation ?? entry.message ?? id\n messages[id] = text\n }\n return messages\n}\n\nexport interface RunCompileOptions {\n parallel?: boolean\n}\n\n/**\n * Programmatic compile entry point.\n * Loads config from `cwd`, reads catalogs, and writes compiled output.\n * This is the in-process equivalent of `fluenti compile`.\n */\nexport async function runCompile(cwd: string, options?: RunCompileOptions): Promise<void> {\n const config = await loadConfig(undefined, cwd)\n const localeCodes = resolveLocaleCodes(config.locales)\n const ext = config.format === 'json' ? '.json' : '.po'\n\n const outDir = resolve(cwd, config.compileOutDir)\n mkdirSync(outDir, { recursive: true })\n\n const pluginRunner = config.plugins?.length\n ? createPluginRunner(config.plugins)\n : undefined\n\n const allCatalogs: Record<string, CatalogData> = {}\n for (const locale of localeCodes) {\n const catalogPath = resolve(cwd, config.catalogDir, `${locale}${ext}`)\n allCatalogs[locale] = readCatalog(catalogPath, config.format)\n }\n\n const allIds = collectAllIds(allCatalogs)\n\n // Run plugin hooks around compilation\n for (const locale of localeCodes) {\n if (pluginRunner) {\n const compileContext: PluginCompileContext = {\n locale,\n messages: catalogToMessages(allCatalogs[locale]!),\n outDir,\n config,\n }\n await pluginRunner.runBeforeCompile(compileContext)\n }\n }\n\n if (options?.parallel && localeCodes.length > 1) {\n const tasks = localeCodes.map((locale) => ({\n locale,\n catalog: allCatalogs[locale]!,\n allIds,\n sourceLocale: config.sourceLocale,\n }))\n\n const results = await parallelCompile(tasks)\n\n for (const result of results) {\n writeFileSync(resolve(outDir, `${result.locale}.js`), result.code, 'utf-8')\n }\n } else {\n for (const locale of localeCodes) {\n const { code } = compileCatalog(allCatalogs[locale]!, locale, allIds, config.sourceLocale)\n writeFileSync(resolve(outDir, `${locale}.js`), code, 'utf-8')\n }\n }\n\n const indexCode = compileIndex(localeCodes, config.compileOutDir)\n writeFileSync(resolve(outDir, 'index.js'), indexCode, 'utf-8')\n\n const typesCode = compileTypeDeclaration(allIds, allCatalogs, config.sourceLocale)\n writeFileSync(resolve(outDir, 'messages.d.ts'), typesCode, 'utf-8')\n\n // Run after-compile hooks\n for (const locale of localeCodes) {\n if (pluginRunner) {\n const compileContext: PluginCompileContext = {\n locale,\n messages: catalogToMessages(allCatalogs[locale]!),\n outDir,\n config,\n }\n await pluginRunner.runAfterCompile(compileContext)\n }\n }\n}\n","import fg from 'fast-glob'\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport { resolve, dirname, extname } from 'node:path'\nimport { extractFromTsx } from './tsx-extractor'\nimport { updateCatalog } from './catalog'\nimport type { CatalogData } from './catalog'\nimport { readJsonCatalog, writeJsonCatalog } from './json-format'\nimport { readPoCatalog, writePoCatalog } from './po-format'\nimport { ExtractCache } from './extract-cache'\nimport { loadConfig } from './config-loader'\nimport type { ExtractedMessage } from '@fluenti/core/compiler'\nimport { resolveLocaleCodes, createPluginRunner } from '@fluenti/core/compiler'\nimport type { PluginExtractContext } from '@fluenti/core/compiler'\n\nfunction deriveProjectId(cwd: string): string {\n return createHash('md5').update(cwd).digest('hex').slice(0, 8)\n}\n\nexport interface RunExtractOptions {\n clean?: boolean\n stripFuzzy?: boolean\n useCache?: boolean\n}\n\nfunction readCatalog(filePath: string, format: 'json' | 'po'): CatalogData {\n if (!existsSync(filePath)) return {}\n const content = readFileSync(filePath, 'utf-8')\n return format === 'json' ? readJsonCatalog(content) : readPoCatalog(content)\n}\n\nfunction writeCatalog(filePath: string, catalog: CatalogData, format: 'json' | 'po'): void {\n const content = format === 'json' ? writeJsonCatalog(catalog) : writePoCatalog(catalog)\n try {\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content, 'utf-8')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to write catalog \"${filePath}\": ${message}`)\n }\n}\n\nasync function extractFromFile(filePath: string, code: string): Promise<ExtractedMessage[]> {\n const ext = extname(filePath)\n if (ext === '.vue') {\n try {\n const { extractFromVue } = await import('./vue-extractor')\n return extractFromVue(code, filePath)\n } catch {\n return []\n }\n }\n return extractFromTsx(code, filePath)\n}\n\n/**\n * Programmatic extract entry point.\n * Loads config from `cwd`, extracts messages, and writes catalogs.\n * This is the in-process equivalent of `fluenti extract`.\n */\nexport async function runExtract(cwd: string, options?: RunExtractOptions): Promise<void> {\n const config = await loadConfig(undefined, cwd)\n const localeCodes = resolveLocaleCodes(config.locales)\n\n const files = await fg(config.include, { cwd, ignore: config.exclude ?? [], absolute: false })\n const allMessages: ExtractedMessage[] = []\n const useCache = options?.useCache !== false\n const cache = useCache ? new ExtractCache(resolve(cwd, config.catalogDir), deriveProjectId(cwd)) : null\n\n for (const file of files) {\n const absFile = resolve(cwd, file)\n if (cache) {\n const cached = cache.get(absFile)\n if (cached) {\n allMessages.push(...cached)\n continue\n }\n }\n\n const code = readFileSync(absFile, 'utf-8')\n const messages = await extractFromFile(absFile, code)\n allMessages.push(...messages)\n\n if (cache) {\n cache.set(absFile, messages)\n }\n }\n\n if (cache) {\n cache.prune(new Set(files.map((f) => resolve(cwd, f))))\n cache.save()\n }\n\n // Run onAfterExtract plugin hooks\n const pluginRunner = config.plugins?.length\n ? createPluginRunner(config.plugins)\n : undefined\n\n if (pluginRunner) {\n const messageMap = new Map<string, ExtractedMessage>()\n for (const msg of allMessages) {\n messageMap.set(msg.id, msg)\n }\n const extractContext: PluginExtractContext = {\n messages: messageMap,\n sourceLocale: config.sourceLocale,\n targetLocales: localeCodes.filter((l) => l !== config.sourceLocale),\n config,\n }\n await pluginRunner.runAfterExtract(extractContext)\n }\n\n const ext = config.format === 'json' ? '.json' : '.po'\n const clean = options?.clean ?? false\n const stripFuzzy = options?.stripFuzzy ?? false\n\n for (const locale of localeCodes) {\n const catalogPath = resolve(cwd, config.catalogDir, `${locale}${ext}`)\n const existing = readCatalog(catalogPath, config.format)\n const { catalog } = updateCatalog(existing, allMessages, { stripFuzzy })\n\n const finalCatalog = clean\n ? Object.fromEntries(Object.entries(catalog).filter(([, entry]) => !entry.obsolete))\n : catalog\n\n writeCatalog(catalogPath, finalCatalog, config.format)\n }\n}\n"],"mappings":"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"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/config.ts","../src/compile-runner.ts","../src/extract-runner.ts"],"sourcesContent":["import type { FluentiBuildConfig } from '@fluenti/core/compiler'\n\n/**\n * Define a Fluenti configuration with full type inference and IDE autocompletion.\n *\n * @example\n * ```ts\n * // fluenti.config.ts\n * import { defineConfig } from '@fluenti/cli'\n *\n * export default defineConfig({\n * sourceLocale: 'en',\n * locales: ['en', 'ja', 'zh-CN'],\n * catalogDir: './locales',\n * format: 'po',\n * include: ['./src/**\\/*.{vue,tsx,ts}'],\n * compileOutDir: './src/locales/compiled',\n * })\n * ```\n */\nexport function defineConfig(config: Partial<FluentiBuildConfig>): Partial<FluentiBuildConfig> {\n return config\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { resolveLocaleCodes, createPluginRunner } from '@fluenti/core/compiler'\nimport type { PluginCompileContext } from '@fluenti/core/compiler'\nimport { loadConfig } from './config-loader'\nimport { compileCatalog, compileIndex, collectAllIds, compileTypeDeclaration } from './compile'\nimport { parallelCompile } from './parallel-compile'\nimport type { CatalogData } from './catalog'\nimport { readJsonCatalog } from './json-format'\nimport { readPoCatalog } from './po-format'\n\nfunction readCatalog(filePath: string, format: 'json' | 'po'): CatalogData {\n if (!existsSync(filePath)) return {}\n const content = readFileSync(filePath, 'utf-8')\n return format === 'json' ? readJsonCatalog(content) : readPoCatalog(content)\n}\n\n/** Build a messages record (id → translation or message) from catalog data */\nfunction catalogToMessages(catalog: CatalogData): Record<string, string> {\n const messages: Record<string, string> = {}\n for (const [id, entry] of Object.entries(catalog)) {\n if (entry.obsolete) continue\n const text = entry.translation ?? entry.message ?? id\n messages[id] = text\n }\n return messages\n}\n\nexport interface RunCompileOptions {\n parallel?: boolean\n}\n\n/**\n * Programmatic compile entry point.\n * Loads config from `cwd`, reads catalogs, and writes compiled output.\n * This is the in-process equivalent of `fluenti compile`.\n */\nexport async function runCompile(cwd: string, options?: RunCompileOptions): Promise<void> {\n const config = await loadConfig(undefined, cwd)\n const localeCodes = resolveLocaleCodes(config.locales)\n const ext = config.format === 'json' ? '.json' : '.po'\n\n const outDir = resolve(cwd, config.compileOutDir)\n mkdirSync(outDir, { recursive: true })\n\n const pluginRunner = config.plugins?.length\n ? createPluginRunner(config.plugins)\n : undefined\n\n const allCatalogs: Record<string, CatalogData> = {}\n for (const locale of localeCodes) {\n const catalogPath = resolve(cwd, config.catalogDir, `${locale}${ext}`)\n allCatalogs[locale] = readCatalog(catalogPath, config.format)\n }\n\n const allIds = collectAllIds(allCatalogs)\n\n // Run plugin hooks around compilation\n for (const locale of localeCodes) {\n if (pluginRunner) {\n const compileContext: PluginCompileContext = {\n locale,\n messages: catalogToMessages(allCatalogs[locale]!),\n outDir,\n config,\n }\n await pluginRunner.runBeforeCompile(compileContext)\n }\n }\n\n if (options?.parallel && localeCodes.length > 1) {\n const tasks = localeCodes.map((locale) => ({\n locale,\n catalog: allCatalogs[locale]!,\n allIds,\n sourceLocale: config.sourceLocale,\n }))\n\n const results = await parallelCompile(tasks)\n\n for (const result of results) {\n writeFileSync(resolve(outDir, `${result.locale}.js`), result.code, 'utf-8')\n }\n } else {\n for (const locale of localeCodes) {\n const { code } = compileCatalog(allCatalogs[locale]!, locale, allIds, config.sourceLocale)\n writeFileSync(resolve(outDir, `${locale}.js`), code, 'utf-8')\n }\n }\n\n const indexCode = compileIndex(localeCodes, config.compileOutDir)\n writeFileSync(resolve(outDir, 'index.js'), indexCode, 'utf-8')\n\n const typesCode = compileTypeDeclaration(allIds, allCatalogs, config.sourceLocale)\n writeFileSync(resolve(outDir, 'messages.d.ts'), typesCode, 'utf-8')\n\n // Run after-compile hooks\n for (const locale of localeCodes) {\n if (pluginRunner) {\n const compileContext: PluginCompileContext = {\n locale,\n messages: catalogToMessages(allCatalogs[locale]!),\n outDir,\n config,\n }\n await pluginRunner.runAfterCompile(compileContext)\n }\n }\n}\n","import fg from 'fast-glob'\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'\nimport { createHash } from 'node:crypto'\nimport { resolve, dirname, extname } from 'node:path'\nimport { extractFromTsx } from './tsx-extractor'\nimport { updateCatalog } from './catalog'\nimport type { CatalogData } from './catalog'\nimport { readJsonCatalog, writeJsonCatalog } from './json-format'\nimport { readPoCatalog, writePoCatalog } from './po-format'\nimport { ExtractCache } from './extract-cache'\nimport { loadConfig } from './config-loader'\nimport type { ExtractedMessage } from '@fluenti/core/compiler'\nimport { resolveLocaleCodes, createPluginRunner } from '@fluenti/core/compiler'\nimport type { PluginExtractContext } from '@fluenti/core/compiler'\n\nfunction deriveProjectId(cwd: string): string {\n return createHash('md5').update(cwd).digest('hex').slice(0, 8)\n}\n\nexport interface RunExtractOptions {\n clean?: boolean\n stripFuzzy?: boolean\n useCache?: boolean\n}\n\nfunction readCatalog(filePath: string, format: 'json' | 'po'): CatalogData {\n if (!existsSync(filePath)) return {}\n const content = readFileSync(filePath, 'utf-8')\n return format === 'json' ? readJsonCatalog(content) : readPoCatalog(content)\n}\n\nfunction writeCatalog(filePath: string, catalog: CatalogData, format: 'json' | 'po'): void {\n const content = format === 'json' ? writeJsonCatalog(catalog) : writePoCatalog(catalog)\n try {\n mkdirSync(dirname(filePath), { recursive: true })\n writeFileSync(filePath, content, 'utf-8')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n throw new Error(`Failed to write catalog \"${filePath}\": ${message}`)\n }\n}\n\nasync function extractFromFile(filePath: string, code: string): Promise<ExtractedMessage[]> {\n const ext = extname(filePath)\n if (ext === '.vue') {\n try {\n const { extractFromVue } = await import('./vue-extractor')\n return extractFromVue(code, filePath)\n } catch {\n return []\n }\n }\n return extractFromTsx(code, filePath)\n}\n\n/**\n * Programmatic extract entry point.\n * Loads config from `cwd`, extracts messages, and writes catalogs.\n * This is the in-process equivalent of `fluenti extract`.\n */\nexport async function runExtract(cwd: string, options?: RunExtractOptions): Promise<void> {\n const config = await loadConfig(undefined, cwd)\n const localeCodes = resolveLocaleCodes(config.locales)\n\n const files = await fg(config.include, { cwd, ignore: config.exclude ?? [], absolute: false })\n const allMessages: ExtractedMessage[] = []\n const useCache = options?.useCache !== false\n const cache = useCache ? new ExtractCache(resolve(cwd, config.catalogDir), deriveProjectId(cwd)) : null\n\n for (const file of files) {\n const absFile = resolve(cwd, file)\n if (cache) {\n const cached = cache.get(absFile)\n if (cached) {\n allMessages.push(...cached)\n continue\n }\n }\n\n const code = readFileSync(absFile, 'utf-8')\n const messages = await extractFromFile(absFile, code)\n allMessages.push(...messages)\n\n if (cache) {\n cache.set(absFile, messages)\n }\n }\n\n if (cache) {\n cache.prune(new Set(files.map((f) => resolve(cwd, f))))\n cache.save()\n }\n\n // Run onAfterExtract plugin hooks\n const pluginRunner = config.plugins?.length\n ? createPluginRunner(config.plugins)\n : undefined\n\n if (pluginRunner) {\n const messageMap = new Map<string, ExtractedMessage>()\n for (const msg of allMessages) {\n messageMap.set(msg.id, msg)\n }\n const extractContext: PluginExtractContext = {\n messages: messageMap,\n sourceLocale: config.sourceLocale,\n targetLocales: localeCodes.filter((l) => l !== config.sourceLocale),\n config,\n }\n await pluginRunner.runAfterExtract(extractContext)\n }\n\n const ext = config.format === 'json' ? '.json' : '.po'\n const clean = options?.clean ?? false\n const stripFuzzy = options?.stripFuzzy ?? false\n\n for (const locale of localeCodes) {\n const catalogPath = resolve(cwd, config.catalogDir, `${locale}${ext}`)\n const existing = readCatalog(catalogPath, config.format)\n const { catalog } = updateCatalog(existing, allMessages, { stripFuzzy })\n\n const finalCatalog = clean\n ? Object.fromEntries(Object.entries(catalog).filter(([, entry]) => !entry.obsolete))\n : catalog\n\n writeCatalog(catalogPath, finalCatalog, config.format)\n }\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,EAAa,GAAkE;AAC7F,QAAO;;;;ACVT,SAAS,EAAY,GAAkB,GAAoC;AACzE,KAAI,CAAC,EAAW,EAAS,CAAE,QAAO,EAAE;CACpC,IAAM,IAAU,EAAa,GAAU,QAAQ;AAC/C,QAAO,MAAW,SAAS,EAAgB,EAAQ,GAAG,EAAc,EAAQ;;AAI9E,SAAS,EAAkB,GAA8C;CACvE,IAAM,IAAmC,EAAE;AAC3C,MAAK,IAAM,CAAC,GAAI,MAAU,OAAO,QAAQ,EAAQ,CAC3C,GAAM,aAEV,EAAS,KADI,EAAM,eAAe,EAAM,WAAW;AAGrD,QAAO;;AAYT,eAAsB,EAAW,GAAa,GAA4C;CACxF,IAAM,IAAS,MAAM,EAAW,KAAA,GAAW,EAAI,EACzC,IAAc,EAAmB,EAAO,QAAQ,EAChD,IAAM,EAAO,WAAW,SAAS,UAAU,OAE3C,IAAS,EAAQ,GAAK,EAAO,cAAc;AACjD,GAAU,GAAQ,EAAE,WAAW,IAAM,CAAC;CAEtC,IAAM,IAAe,EAAO,SAAS,SACjC,EAAmB,EAAO,QAAQ,GAClC,KAAA,GAEE,IAA2C,EAAE;AACnD,MAAK,IAAM,KAAU,EAEnB,GAAY,KAAU,EADF,EAAQ,GAAK,EAAO,YAAY,GAAG,IAAS,IAAM,EACvB,EAAO,OAAO;CAG/D,IAAM,IAAS,EAAc,EAAY;AAGzC,MAAK,IAAM,KAAU,EACnB,KAAI,GAAc;EAChB,IAAM,IAAuC;GAC3C;GACA,UAAU,EAAkB,EAAY,GAAS;GACjD;GACA;GACD;AACD,QAAM,EAAa,iBAAiB,EAAe;;AAIvD,KAAI,GAAS,YAAY,EAAY,SAAS,GAAG;EAQ/C,IAAM,IAAU,MAAM,EAPR,EAAY,KAAK,OAAY;GACzC;GACA,SAAS,EAAY;GACrB;GACA,cAAc,EAAO;GACtB,EAAE,CAEyC;AAE5C,OAAK,IAAM,KAAU,EACnB,GAAc,EAAQ,GAAQ,GAAG,EAAO,OAAO,KAAK,EAAE,EAAO,MAAM,QAAQ;OAG7E,MAAK,IAAM,KAAU,GAAa;EAChC,IAAM,EAAE,YAAS,EAAe,EAAY,IAAU,GAAQ,GAAQ,EAAO,aAAa;AAC1F,IAAc,EAAQ,GAAQ,GAAG,EAAO,KAAK,EAAE,GAAM,QAAQ;;CAIjE,IAAM,IAAY,EAAa,GAAa,EAAO,cAAc;AACjE,GAAc,EAAQ,GAAQ,WAAW,EAAE,GAAW,QAAQ;CAE9D,IAAM,IAAY,EAAuB,GAAQ,GAAa,EAAO,aAAa;AAClF,GAAc,EAAQ,GAAQ,gBAAgB,EAAE,GAAW,QAAQ;AAGnE,MAAK,IAAM,KAAU,EACnB,KAAI,GAAc;EAChB,IAAM,IAAuC;GAC3C;GACA,UAAU,EAAkB,EAAY,GAAS;GACjD;GACA;GACD;AACD,QAAM,EAAa,gBAAgB,EAAe;;;;;AC1FxD,SAAS,EAAgB,GAAqB;AAC5C,QAAO,EAAW,MAAM,CAAC,OAAO,EAAI,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,EAAE;;AAShE,SAAS,EAAY,GAAkB,GAAoC;AACzE,KAAI,CAAC,EAAW,EAAS,CAAE,QAAO,EAAE;CACpC,IAAM,IAAU,EAAa,GAAU,QAAQ;AAC/C,QAAO,MAAW,SAAS,EAAgB,EAAQ,GAAG,EAAc,EAAQ;;AAG9E,SAAS,EAAa,GAAkB,GAAsB,GAA6B;CACzF,IAAM,IAAU,MAAW,SAAS,EAAiB,EAAQ,GAAG,EAAe,EAAQ;AACvF,KAAI;AAEF,EADA,EAAU,EAAQ,EAAS,EAAE,EAAE,WAAW,IAAM,CAAC,EACjD,EAAc,GAAU,GAAS,QAAQ;UAClC,GAAK;EACZ,IAAM,IAAU,aAAe,QAAQ,EAAI,UAAU,OAAO,EAAI;AAChE,QAAU,MAAM,4BAA4B,EAAS,KAAK,IAAU;;;AAIxE,eAAe,EAAgB,GAAkB,GAA2C;AAE1F,KADY,EAAQ,EAAS,KACjB,OACV,KAAI;EACF,IAAM,EAAE,sBAAmB,MAAM,OAAO;AACxC,SAAO,EAAe,GAAM,EAAS;SAC/B;AACN,SAAO,EAAE;;AAGb,QAAO,EAAe,GAAM,EAAS;;AAQvC,eAAsB,EAAW,GAAa,GAA4C;CACxF,IAAM,IAAS,MAAM,EAAW,KAAA,GAAW,EAAI,EACzC,IAAc,EAAmB,EAAO,QAAQ,EAEhD,IAAQ,MAAM,EAAG,EAAO,SAAS;EAAE;EAAK,QAAQ,EAAO,WAAW,EAAE;EAAE,UAAU;EAAO,CAAC,EACxF,IAAkC,EAAE,EAEpC,IADW,GAAS,aAAa,KAC4D,OAA1E,IAAI,EAAa,EAAQ,GAAK,EAAO,WAAW,EAAE,EAAgB,EAAI,CAAC;AAEhG,MAAK,IAAM,KAAQ,GAAO;EACxB,IAAM,IAAU,EAAQ,GAAK,EAAK;AAClC,MAAI,GAAO;GACT,IAAM,IAAS,EAAM,IAAI,EAAQ;AACjC,OAAI,GAAQ;AACV,MAAY,KAAK,GAAG,EAAO;AAC3B;;;EAKJ,IAAM,IAAW,MAAM,EAAgB,GAD1B,EAAa,GAAS,QAAQ,CACU;AAGrD,EAFA,EAAY,KAAK,GAAG,EAAS,EAEzB,KACF,EAAM,IAAI,GAAS,EAAS;;AAIhC,CAAI,MACF,EAAM,MAAM,IAAI,IAAI,EAAM,KAAK,MAAM,EAAQ,GAAK,EAAE,CAAC,CAAC,CAAC,EACvD,EAAM,MAAM;CAId,IAAM,IAAe,EAAO,SAAS,SACjC,EAAmB,EAAO,QAAQ,GAClC,KAAA;AAEJ,KAAI,GAAc;EAChB,IAAM,oBAAa,IAAI,KAA+B;AACtD,OAAK,IAAM,KAAO,EAChB,GAAW,IAAI,EAAI,IAAI,EAAI;EAE7B,IAAM,IAAuC;GAC3C,UAAU;GACV,cAAc,EAAO;GACrB,eAAe,EAAY,QAAQ,MAAM,MAAM,EAAO,aAAa;GACnE;GACD;AACD,QAAM,EAAa,gBAAgB,EAAe;;CAGpD,IAAM,IAAM,EAAO,WAAW,SAAS,UAAU,OAC3C,IAAQ,GAAS,SAAS,IAC1B,IAAa,GAAS,cAAc;AAE1C,MAAK,IAAM,KAAU,GAAa;EAChC,IAAM,IAAc,EAAQ,GAAK,EAAO,YAAY,GAAG,IAAS,IAAM,EAEhE,EAAE,eAAY,EADH,EAAY,GAAa,EAAO,OAAO,EACZ,GAAa,EAAE,eAAY,CAAC;AAMxE,IAAa,GAJQ,IACjB,OAAO,YAAY,OAAO,QAAQ,EAAQ,CAAC,QAAQ,GAAG,OAAW,CAAC,EAAM,SAAS,CAAC,GAClF,GAEoC,EAAO,OAAO"}