@verbaly/compiler 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,10 +21,19 @@ The compiler behind [Verbaly](https://github.com/AronSoto/verbaly): AST extracti
21
21
  npx verbaly extract # sync catalogs + types
22
22
  npx verbaly check # exit 1 if anything is missing (CI)
23
23
  npx verbaly extract --prune # drop orphaned keys
24
+ npx verbaly translate # fill missing translations via Claude (or your provider)
24
25
  ```
25
26
 
26
27
  Reads `verbaly.config.{js,mjs,ts,mts,json}` (TS configs need `esbuild` installed). Generates `locales/<locale>.json` (flat, portable — no proprietary format) and `verbaly.d.ts` with params typed per key.
27
28
 
29
+ ## Machine translation
30
+
31
+ `verbaly translate` fills the `""` holes `check` reports. The default provider uses Claude via the official SDK — install it as a dev dependency (translation is a build-time step, not an app runtime dependency): `pnpm add -D @anthropic-ai/sdk` (or `npm i -D`), plus `ANTHROPIC_API_KEY`. Default model is `claude-sonnet-5` (balanced quality/cost); override with `translate.model` in config or `--model <id>`. Placeholders, variants and tags are validated after translation — anything not preserved verbatim stays `""` so `check` keeps failing. Plug your own provider in `verbaly.config.ts`:
32
+
33
+ ```ts
34
+ translate: { provider: async ({ sourceLocale, targetLocale, messages }) => ({ ...translated }) }
35
+ ```
36
+
28
37
  📖 Docs: **https://verbaly-web.vercel.app/docs/cli**
29
38
 
30
39
  > ⚠️ Early development (`0.x`) — API not stable yet.
@@ -0,0 +1,63 @@
1
+ // src/providers/claude.ts
2
+ var DEFAULT_MODEL = "claude-sonnet-5";
3
+ var SYSTEM = `You translate UI strings for the Verbaly i18n library.
4
+ Rules:
5
+ - Translate only the human-readable text, naturally for the target locale.
6
+ - Preserve verbatim: placeholders like {name}, format specs like {price:currency/EUR}, variant blocks like {count | one: ... | other: # ...} (translate only the text inside each variant, keep keys and # as-is), ICU syntax, named tags like <em>...</em> and escapes {{ }} || ##.
7
+ - Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;
8
+ function claudeProvider(options = {}) {
9
+ return async (request) => {
10
+ const Anthropic = await loadSdk();
11
+ const client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});
12
+ const response = await client.messages.create({
13
+ model: options.model ?? DEFAULT_MODEL,
14
+ max_tokens: options.maxTokens ?? 16e3,
15
+ thinking: { type: "disabled" },
16
+ system: SYSTEM,
17
+ messages: [{ role: "user", content: buildPrompt(request) }],
18
+ output_config: { format: batchFormat(request) }
19
+ });
20
+ const text = response.content.find((block) => block.type === "text")?.text ?? "{}";
21
+ return JSON.parse(text);
22
+ };
23
+ }
24
+ function buildPrompt(request) {
25
+ return `Translate each value from "${request.sourceLocale}" to "${request.targetLocale}". Return a JSON object with the same keys and translated values.
26
+
27
+ ` + JSON.stringify(request.messages, null, 2);
28
+ }
29
+ function batchFormat(request) {
30
+ const keys = Object.keys(request.messages);
31
+ return {
32
+ type: "json_schema",
33
+ schema: {
34
+ type: "object",
35
+ properties: Object.fromEntries(keys.map((key) => [key, { type: "string" }])),
36
+ required: keys,
37
+ additionalProperties: false
38
+ }
39
+ };
40
+ }
41
+ async function loadSdk() {
42
+ try {
43
+ const mod = await import("@anthropic-ai/sdk");
44
+ return mod.default;
45
+ } catch (error) {
46
+ if (isModuleNotFound(error, "@anthropic-ai/sdk")) {
47
+ throw new Error(
48
+ "[verbaly] the claude translate provider needs @anthropic-ai/sdk \u2014 install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY",
49
+ { cause: error }
50
+ );
51
+ }
52
+ throw error;
53
+ }
54
+ }
55
+ function isModuleNotFound(error, name) {
56
+ return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
57
+ }
58
+ export {
59
+ batchFormat,
60
+ buildPrompt,
61
+ claudeProvider
62
+ };
63
+ //# sourceMappingURL=claude-RQATA6OI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/claude.ts"],"sourcesContent":["import type { TranslateProvider, TranslateRequest } from '../translate';\r\n\r\nexport interface ClaudeProviderOptions {\r\n model?: string;\r\n apiKey?: string;\r\n maxTokens?: number;\r\n}\r\n\r\n// balanced default for translation\r\nconst DEFAULT_MODEL = 'claude-sonnet-5';\r\n\r\nconst SYSTEM = `You translate UI strings for the Verbaly i18n library.\r\nRules:\r\n- Translate only the human-readable text, naturally for the target locale.\r\n- Preserve verbatim: placeholders like {name}, format specs like {price:currency/EUR}, variant blocks like {count | one: ... | other: # ...} (translate only the text inside each variant, keep keys and # as-is), ICU syntax, named tags like <em>...</em> and escapes {{ }} || ##.\r\n- Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;\r\n\r\nexport function claudeProvider(options: ClaudeProviderOptions = {}): TranslateProvider {\r\n return async (request: TranslateRequest) => {\r\n const Anthropic = await loadSdk();\r\n const client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});\r\n const response = await client.messages.create({\r\n model: options.model ?? DEFAULT_MODEL,\r\n max_tokens: options.maxTokens ?? 16000,\r\n thinking: { type: 'disabled' },\r\n system: SYSTEM,\r\n messages: [{ role: 'user', content: buildPrompt(request) }],\r\n output_config: { format: batchFormat(request) },\r\n });\r\n const text = response.content.find((block) => block.type === 'text')?.text ?? '{}';\r\n return JSON.parse(text) as Record<string, string>;\r\n };\r\n}\r\n\r\nexport function buildPrompt(request: TranslateRequest): string {\r\n return (\r\n `Translate each value from \"${request.sourceLocale}\" to \"${request.targetLocale}\". ` +\r\n `Return a JSON object with the same keys and translated values.\\n\\n` +\r\n JSON.stringify(request.messages, null, 2)\r\n );\r\n}\r\n\r\nexport function batchFormat(request: TranslateRequest) {\r\n const keys = Object.keys(request.messages);\r\n return {\r\n type: 'json_schema' as const,\r\n schema: {\r\n type: 'object',\r\n properties: Object.fromEntries(keys.map((key) => [key, { type: 'string' }])),\r\n required: keys,\r\n additionalProperties: false,\r\n },\r\n };\r\n}\r\n\r\nasync function loadSdk(): Promise<typeof import('@anthropic-ai/sdk').default> {\r\n try {\r\n const mod = await import('@anthropic-ai/sdk');\r\n return mod.default;\r\n } catch (error) {\r\n if (isModuleNotFound(error, '@anthropic-ai/sdk')) {\r\n throw new Error(\r\n '[verbaly] the claude translate provider needs @anthropic-ai/sdk — install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY',\r\n { cause: error },\r\n );\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n"],"mappings":";AASA,IAAM,gBAAgB;AAEtB,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAMR,SAAS,eAAe,UAAiC,CAAC,GAAsB;AACrF,SAAO,OAAO,YAA8B;AAC1C,UAAM,YAAY,MAAM,QAAQ;AAChC,UAAM,SAAS,IAAI,UAAU,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC,CAAC;AAC7E,UAAM,WAAW,MAAM,OAAO,SAAS,OAAO;AAAA,MAC5C,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,QAAQ,aAAa;AAAA,MACjC,UAAU,EAAE,MAAM,WAAW;AAAA,MAC7B,QAAQ;AAAA,MACR,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,OAAO,EAAE,CAAC;AAAA,MAC1D,eAAe,EAAE,QAAQ,YAAY,OAAO,EAAE;AAAA,IAChD,CAAC;AACD,UAAM,OAAO,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM,GAAG,QAAQ;AAC9E,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AACF;AAEO,SAAS,YAAY,SAAmC;AAC7D,SACE,8BAA8B,QAAQ,YAAY,SAAS,QAAQ,YAAY;AAAA;AAAA,IAE/E,KAAK,UAAU,QAAQ,UAAU,MAAM,CAAC;AAE5C;AAEO,SAAS,YAAY,SAA2B;AACrD,QAAM,OAAO,OAAO,KAAK,QAAQ,QAAQ;AACzC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,YAAY,OAAO,YAAY,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,MAC3E,UAAU;AAAA,MACV,sBAAsB;AAAA,IACxB;AAAA,EACF;AACF;AAEA,eAAe,UAA+D;AAC5E,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,mBAAmB;AAC5C,WAAO,IAAI;AAAA,EACb,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO,mBAAmB,GAAG;AAChD,YAAM,IAAI;AAAA,QACR;AAAA,QACA,EAAE,OAAO,MAAM;AAAA,MACjB;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,iBAAiB,OAAgB,MAAuB;AAC/D,SACE,iBAAiB,SAChB,MAA4B,SAAS,0BACtC,MAAM,QAAQ,SAAS,IAAI;AAE/B;","names":[]}
package/dist/cli.js CHANGED
@@ -194,7 +194,8 @@ function resolveConfig(config = {}) {
194
194
  sourceLocale,
195
195
  locales: [...locales],
196
196
  include: config.include ?? ["src/**/*.{js,jsx,ts,tsx,mjs,mts}"],
197
- exclude: config.exclude ?? ["**/node_modules/**", "**/dist/**"]
197
+ exclude: config.exclude ?? ["**/node_modules/**", "**/dist/**"],
198
+ translate: config.translate ?? {}
198
199
  };
199
200
  }
200
201
  async function loadConfigFile(root) {
@@ -579,21 +580,84 @@ function pruneCatalogs(cfg, catalogs, registry) {
579
580
  return removed;
580
581
  }
581
582
 
583
+ // src/translate.ts
584
+ async function translateCatalogs(cfg, catalogs, provider, options = {}) {
585
+ const batchSize = options.batchSize ?? 20;
586
+ const targets = (options.locales ?? cfg.locales).filter(
587
+ (locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale)
588
+ );
589
+ const source = catalogs[cfg.sourceLocale] ?? {};
590
+ const result = { translated: {}, invalid: {}, pending: {} };
591
+ for (const locale of targets) {
592
+ const catalog = catalogs[locale] ??= {};
593
+ const missing = Object.keys(source).filter((key) => source[key] && !catalog[key]);
594
+ if (missing.length === 0) continue;
595
+ if (options.dryRun) {
596
+ result.pending[locale] = missing;
597
+ continue;
598
+ }
599
+ for (let i = 0; i < missing.length; i += batchSize) {
600
+ const keys = missing.slice(i, i + batchSize);
601
+ const messages = Object.fromEntries(keys.map((key) => [key, source[key]]));
602
+ const out = await provider({
603
+ sourceLocale: cfg.sourceLocale,
604
+ targetLocale: locale,
605
+ messages
606
+ });
607
+ for (const key of keys) {
608
+ const text = out[key];
609
+ if (typeof text === "string" && text.trim() && structureMatches(source[key], text)) {
610
+ catalog[key] = text;
611
+ (result.translated[locale] ??= []).push(key);
612
+ } else {
613
+ (result.invalid[locale] ??= []).push(key);
614
+ }
615
+ }
616
+ }
617
+ }
618
+ return result;
619
+ }
620
+ function structureMatches(source, translated) {
621
+ return sameMembers(paramNames(source), paramNames(translated)) && sameMembers(tagTokens(source), tagTokens(translated));
622
+ }
623
+ function paramNames(message) {
624
+ try {
625
+ return [...collectParams(message).keys()].sort();
626
+ } catch {
627
+ return ["\0invalid"];
628
+ }
629
+ }
630
+ var TAG = /<(\/?)([a-zA-Z][\w-]*)(\/?)>/g;
631
+ function tagTokens(message) {
632
+ const out = [];
633
+ for (const match of message.matchAll(TAG)) {
634
+ out.push(`${match[1]}${match[2]}${match[3]}`);
635
+ }
636
+ return out.sort();
637
+ }
638
+ function sameMembers(a, b) {
639
+ return a.length === b.length && a.every((value, i) => value === b[i]);
640
+ }
641
+
582
642
  // src/cli.ts
583
643
  var HELP = `verbaly \u2014 i18n compiler
584
644
 
585
645
  Usage:
586
- verbaly extract scan sources, update catalogs and types
587
- verbaly check verify translations are complete (CI)
646
+ verbaly extract scan sources, update catalogs and types
647
+ verbaly check verify translations are complete (CI)
648
+ verbaly translate fill missing translations via a provider (default: claude)
588
649
 
589
650
  Options:
590
651
  --root <path> project root (default: cwd)
591
652
  --dir <path> catalogs directory (default: locales)
592
653
  --source <locale> source locale (default: en)
593
- --locales <csv> extra locales (default: from catalog files)
654
+ --locales <csv> extra locales; for translate: target locales to fill
594
655
  --prune drop keys no longer referenced (extract)
656
+ --model <id> model override for the claude provider (translate)
657
+ --dry-run list what would be translated, write nothing (translate)
595
658
 
596
- Config file: verbaly.config.{js,mjs,json} at root (flags win).
659
+ Config file: verbaly.config.{js,mjs,ts,mts,json} at root (flags win).
660
+ The claude provider needs @anthropic-ai/sdk installed and ANTHROPIC_API_KEY set.
597
661
  `;
598
662
  async function main() {
599
663
  const { positionals, values } = parseArgs({
@@ -604,6 +668,8 @@ async function main() {
604
668
  source: { type: "string" },
605
669
  locales: { type: "string" },
606
670
  prune: { type: "boolean" },
671
+ model: { type: "string" },
672
+ "dry-run": { type: "boolean" },
607
673
  help: { type: "boolean", short: "h" }
608
674
  }
609
675
  });
@@ -651,10 +717,49 @@ ${formatCheckResult(result)}`);
651
717
  process.exitCode = 1;
652
718
  return;
653
719
  }
720
+ if (command === "translate") {
721
+ const catalogs = loadCatalogs(cfg);
722
+ const provider = await resolveProvider(cfg, values.model);
723
+ const result = await translateCatalogs(cfg, catalogs, provider, {
724
+ locales: values.locales?.split(","),
725
+ batchSize: cfg.translate.batchSize,
726
+ dryRun: values["dry-run"]
727
+ });
728
+ if (values["dry-run"]) {
729
+ const entries = Object.entries(result.pending);
730
+ if (entries.length === 0) {
731
+ console.log("[verbaly] nothing to translate \u2713");
732
+ return;
733
+ }
734
+ for (const [locale, keys] of entries) {
735
+ console.log(` ${locale}: ${keys.length} missing \u2014 ${keys.join(", ")}`);
736
+ }
737
+ return;
738
+ }
739
+ for (const locale of Object.keys(result.translated)) {
740
+ writeCatalog(cfg, locale, catalogs[locale] ?? {});
741
+ console.log(` ${locale}: +${result.translated[locale].length} translated`);
742
+ }
743
+ for (const [locale, keys] of Object.entries(result.invalid)) {
744
+ console.warn(
745
+ ` ${locale}: ${keys.length} rejected (params/tags not preserved): ${keys.join(", ")}`
746
+ );
747
+ }
748
+ if (Object.keys(result.translated).length === 0 && Object.keys(result.invalid).length === 0) {
749
+ console.log("[verbaly] nothing to translate \u2713");
750
+ }
751
+ return;
752
+ }
654
753
  console.error(`[verbaly] unknown command "${command}"
655
754
  ${HELP}`);
656
755
  process.exitCode = 1;
657
756
  }
757
+ async function resolveProvider(cfg, model) {
758
+ const configured = cfg.translate.provider;
759
+ if (typeof configured === "function") return configured;
760
+ const { claudeProvider } = await import("./claude-RQATA6OI.js");
761
+ return claudeProvider({ model: model ?? cfg.translate.model });
762
+ }
658
763
  main().catch((error) => {
659
764
  console.error("[verbaly]", error instanceof Error ? error.message : error);
660
765
  process.exitCode = 1;
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/catalog.ts","../src/check.ts","../src/codegen.ts","../src/params.ts","../src/config.ts","../src/extract.ts","../src/analyze.ts","../src/key.ts","../src/registry.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from 'node:util';\nimport { loadCatalogs, writeCatalog } from './catalog';\nimport { check, formatCheckResult } from './check';\nimport { writeDts } from './codegen';\nimport { loadConfig } from './config';\nimport { extractProject, pruneCatalogs, syncCatalogs } from './extract';\n\nconst HELP = `verbaly — i18n compiler\n\nUsage:\n verbaly extract scan sources, update catalogs and types\n verbaly check verify translations are complete (CI)\n\nOptions:\n --root <path> project root (default: cwd)\n --dir <path> catalogs directory (default: locales)\n --source <locale> source locale (default: en)\n --locales <csv> extra locales (default: from catalog files)\n --prune drop keys no longer referenced (extract)\n\nConfig file: verbaly.config.{js,mjs,json} at root (flags win).\n`;\n\nasync function main(): Promise<void> {\n const { positionals, values } = parseArgs({\n allowPositionals: true,\n options: {\n root: { type: 'string' },\n dir: { type: 'string' },\n source: { type: 'string' },\n locales: { type: 'string' },\n prune: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n });\n\n const command = positionals[0];\n if (values.help || !command) {\n console.log(HELP);\n process.exitCode = command ? 0 : 1;\n return;\n }\n\n const cfg = await loadConfig(values.root ?? process.cwd(), {\n dir: values.dir,\n sourceLocale: values.source,\n locales: values.locales?.split(','),\n });\n\n if (command === 'extract') {\n const registry = await extractProject(cfg);\n const catalogs = loadCatalogs(cfg);\n if (values.prune) {\n const removed = pruneCatalogs(cfg, catalogs, registry);\n for (const [locale, keys] of Object.entries(removed)) {\n console.log(` ${locale}: -${keys.length} pruned`);\n }\n }\n const { added } = syncCatalogs(cfg, catalogs, registry);\n for (const locale of cfg.locales) {\n writeCatalog(cfg, locale, catalogs[locale] ?? {});\n }\n writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\n const total = registry.messages().size;\n console.log(`[verbaly] ${total} messages · locales: ${cfg.locales.join(', ')}`);\n for (const [locale, keys] of Object.entries(added)) {\n console.log(` ${locale}: +${keys.length}`);\n }\n return;\n }\n\n if (command === 'check') {\n const registry = await extractProject(cfg);\n const result = check(cfg, loadCatalogs(cfg), registry);\n if (result.ok) {\n console.log('[verbaly] all translations complete ✓');\n return;\n }\n console.error(`[verbaly] check failed\\n${formatCheckResult(result)}`);\n process.exitCode = 1;\n return;\n }\n\n console.error(`[verbaly] unknown command \"${command}\"\\n${HELP}`);\n process.exitCode = 1;\n}\n\nmain().catch((error: unknown) => {\n console.error('[verbaly]', error instanceof Error ? error.message : error);\n process.exitCode = 1;\n});\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from './config';\n\nexport type Catalog = Record<string, string>;\nexport type Catalogs = Record<string, Catalog>;\n\nexport function catalogPath(cfg: ResolvedConfig, locale: string): string {\n return join(cfg.dir, `${locale}.json`);\n}\n\nexport function readCatalog(cfg: ResolvedConfig, locale: string): Catalog {\n try {\n return JSON.parse(readFileSync(catalogPath(cfg, locale), 'utf8')) as Catalog;\n } catch {\n return {};\n }\n}\n\nexport function loadCatalogs(cfg: ResolvedConfig): Catalogs {\n const catalogs: Catalogs = {};\n for (const locale of cfg.locales) catalogs[locale] = readCatalog(cfg, locale);\n return catalogs;\n}\n\nexport function serializeCatalog(catalog: Catalog): string {\n const sorted: Catalog = {};\n for (const key of Object.keys(catalog).sort()) {\n const value = catalog[key];\n if (value !== undefined) sorted[key] = value;\n }\n return JSON.stringify(sorted, null, 2) + '\\n';\n}\n\nexport function writeCatalog(cfg: ResolvedConfig, locale: string, catalog: Catalog): string {\n const serialized = serializeCatalog(catalog);\n mkdirSync(cfg.dir, { recursive: true });\n writeFileSync(catalogPath(cfg, locale), serialized);\n return serialized;\n}\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport type { MessageRegistry } from './registry';\n\nexport interface MissingEntry {\n locale: string;\n key: string;\n source?: string;\n}\n\nexport interface UnknownEntry {\n key: string;\n files: string[];\n}\n\nexport interface CheckResult {\n ok: boolean;\n missing: MissingEntry[];\n unknown: UnknownEntry[];\n}\n\nexport function check(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): CheckResult {\n const source = catalogs[cfg.sourceLocale] ?? {};\n const extracted = registry.messages();\n\n const unknown: UnknownEntry[] = [];\n for (const [key, files] of registry.usedKeys()) {\n const known =\n extracted.has(key) || cfg.locales.some((locale) => catalogs[locale]?.[key] !== undefined);\n if (!known) unknown.push({ key, files });\n }\n\n const needed = new Set<string>([...extracted.keys(), ...Object.keys(source)]);\n\n const missing: MissingEntry[] = [];\n for (const key of extracted.keys()) {\n if (!source[key]) {\n missing.push({ locale: cfg.sourceLocale, key, source: extracted.get(key)?.message });\n }\n }\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n for (const key of needed) {\n if (!catalogs[locale]?.[key]) {\n missing.push({ locale, key, source: source[key] ?? extracted.get(key)?.message });\n }\n }\n }\n\n return { ok: missing.length === 0 && unknown.length === 0, missing, unknown };\n}\n\nexport function formatCheckResult(result: CheckResult): string {\n const lines: string[] = [];\n if (result.missing.length > 0) {\n lines.push('missing translations:');\n for (const entry of result.missing) {\n const hint = entry.source ? ` — \"${truncate(entry.source, 40)}\"` : '';\n lines.push(` [${entry.locale}] ${entry.key}${hint}`);\n }\n }\n if (result.unknown.length > 0) {\n lines.push('unknown keys (not in any catalog):');\n for (const entry of result.unknown) {\n lines.push(` ${entry.key} — used in ${entry.files.join(', ')}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? text.slice(0, max - 1) + '…' : text;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Catalog } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams, renderParamType } from './params';\n\nexport const VIRTUAL_ID = 'virtual:verbaly';\n\nexport function generateRuntimeModule(cfg: ResolvedConfig): string {\n const others = cfg.locales.filter((locale) => locale !== cfg.sourceLocale);\n const src = JSON.stringify(cfg.sourceLocale);\n const loaders = others\n .map(\n (locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`,\n )\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\n loaders: {\n${loaders}\n },\n});\n\nexport const verbaly = v;\nexport const t = v.t;\n\nexport function getLocale() {\n return v.locale;\n}\n\nexport function subscribe(listener) {\n return v.subscribe(listener);\n}\n\nexport async function setLocale(locale) {\n await v.loadLocale(locale);\n v.setLocale(locale);\n}\n`;\n}\n\nexport function generateLocaleModule(catalog: Catalog): string {\n return `export default ${JSON.stringify(catalog)};\\n`;\n}\n\nexport function generateDts(entries: Map<string, string>): string {\n const lines: string[] = [];\n for (const [key, message] of [...entries.entries()].sort(([a], [b]) => a.localeCompare(b))) {\n const params = collectParams(message);\n if (params.size === 0) {\n lines.push(` ${JSON.stringify(key)}: never;`);\n } else {\n const fields = [...params.entries()]\n .map(([name, types]) => `${JSON.stringify(name)}: ${renderParamType(types)}`)\n .join('; ');\n lines.push(` ${JSON.stringify(key)}: { ${fields} };`);\n }\n }\n\n return `// generated by verbaly — do not edit\ndeclare module 'virtual:verbaly' {\n export interface VerbalyMessages {\n${lines.join('\\n')}\n }\n export type VerbalyKey = keyof VerbalyMessages & string;\n\n export const verbaly: import('verbaly').Verbaly<VerbalyKey>;\n\n export function t<K extends VerbalyKey>(\n key: K,\n ...args: [VerbalyMessages[K]] extends [never] ? [] : [VerbalyMessages[K]]\n ): string;\n export function t(strings: TemplateStringsArray, ...values: unknown[]): string;\n export namespace t {\n export function id(\n key: string,\n ): (strings: TemplateStringsArray, ...values: unknown[]) => string;\n }\n\n export function setLocale(locale: string): Promise<void>;\n export function getLocale(): string;\n export function subscribe(listener: () => void): () => void;\n}\n\ndeclare module 'virtual:verbaly/locale/*' {\n const messages: Record<string, string>;\n export default messages;\n}\n`;\n}\n\nexport function writeDts(cfg: ResolvedConfig, entries: Map<string, string>): void {\n writeFileSync(join(cfg.root, 'verbaly.d.ts'), generateDts(entries));\n}\n","import { parse, type MessageNode } from 'verbaly';\n\nexport type ParamType = 'number' | 'string' | 'date' | 'unknown';\n\nconst PLURAL_KEYS = new Set(['zero', 'one', 'two', 'few', 'many']);\nconst NUMBER_FORMATS = new Set(['number', 'integer', 'percent', 'currency']);\nconst DATE_FORMATS = new Set(['date', 'time']);\n\nexport function collectParams(message: string): Map<string, Set<ParamType>> {\n const out = new Map<string, Set<ParamType>>();\n visit(parse(message), out);\n return out;\n}\n\nfunction visit(nodes: MessageNode[], out: Map<string, Set<ParamType>>): void {\n for (const node of nodes) {\n if (node.kind !== 'param') continue;\n const types = out.get(node.name) ?? new Set<ParamType>();\n out.set(node.name, types);\n\n if (node.variants) {\n for (const [key, body] of node.variants) {\n if (PLURAL_KEYS.has(key) || /^=\\d+$/.test(key)) types.add('number');\n else if (key !== 'other') types.add('string');\n visit(body, out);\n }\n if (types.size === 0) types.add('string');\n } else if (node.format && NUMBER_FORMATS.has(node.format)) {\n types.add('number');\n } else if (node.format && DATE_FORMATS.has(node.format)) {\n types.add('date');\n } else {\n types.add('unknown');\n }\n }\n}\n\nexport function renderParamType(types: Set<ParamType>): string {\n if (types.has('unknown') || types.size === 0) return 'unknown';\n const parts: string[] = [];\n if (types.has('number')) parts.push('number');\n if (types.has('string')) parts.push('string');\n if (types.has('date')) parts.push('Date | number | string');\n return parts.join(' | ');\n}\n","import { existsSync, readFileSync, readdirSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\nexport interface VerbalyConfig {\r\n root?: string;\r\n dir?: string;\r\n sourceLocale?: string;\r\n locales?: string[];\r\n include?: string[];\r\n exclude?: string[];\r\n}\r\n\r\nexport interface ResolvedConfig {\r\n root: string;\r\n dir: string;\r\n sourceLocale: string;\r\n locales: string[];\r\n include: string[];\r\n exclude: string[];\r\n}\r\n\r\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\r\n const root = resolve(config.root ?? process.cwd());\r\n const dir = resolve(root, config.dir ?? 'locales');\r\n const sourceLocale = config.sourceLocale ?? 'en';\r\n\r\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\r\n if (existsSync(dir)) {\r\n for (const file of readdirSync(dir)) {\r\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\r\n }\r\n }\r\n\r\n return {\r\n root,\r\n dir,\r\n sourceLocale,\r\n locales: [...locales],\r\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\r\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\r\n };\r\n}\r\n\r\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\r\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) {\r\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\r\n return mod.default ?? {};\r\n }\r\n }\r\n for (const name of ['verbaly.config.ts', 'verbaly.config.mts']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) return loadTsConfig(path);\r\n }\r\n const jsonPath = join(root, 'verbaly.config.json');\r\n if (existsSync(jsonPath)) {\r\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\r\n }\r\n return {};\r\n}\r\n\r\nasync function loadTsConfig(path: string): Promise<VerbalyConfig> {\r\n try {\r\n const { bundleRequire } = await import('bundle-require');\r\n const { mod } = await bundleRequire<{ default?: VerbalyConfig }>({ filepath: path });\r\n return mod.default ?? {};\r\n } catch (error) {\r\n if (isModuleNotFound(error, 'esbuild')) {\r\n throw new Error(`[verbaly] ${path} needs esbuild — install it: pnpm add -D esbuild`, {\r\n cause: error,\r\n });\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n\r\n// file config + inline overrides\r\nexport async function loadConfig(\r\n root: string,\r\n overrides: VerbalyConfig = {},\r\n): Promise<ResolvedConfig> {\r\n const fileConfig = await loadConfigFile(root);\r\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\r\n}\r\n\r\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\r\n return Object.fromEntries(\r\n Object.entries(config).filter(([, value]) => value !== undefined),\r\n ) as VerbalyConfig;\r\n}\r\n","import { readFileSync } from 'node:fs';\nimport { glob } from 'tinyglobby';\nimport { analyze } from './analyze';\nimport type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { MessageRegistry } from './registry';\n\nexport async function extractProject(cfg: ResolvedConfig): Promise<MessageRegistry> {\n const files = await glob(cfg.include, {\n cwd: cfg.root,\n ignore: cfg.exclude,\n absolute: true,\n });\n const registry = new MessageRegistry();\n for (const file of files) {\n registry.update(file, analyze(readFileSync(file, 'utf8'), file));\n }\n return registry;\n}\n\nexport interface SyncResult {\n added: Record<string, string[]>;\n}\n\n// fills source texts + '' placeholders\nexport function syncCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): SyncResult {\n const added: Record<string, string[]> = {};\n const source = (catalogs[cfg.sourceLocale] ??= {});\n\n for (const [key, msg] of registry.messages()) {\n if (source[key] !== msg.message) {\n source[key] = msg.message;\n (added[cfg.sourceLocale] ??= []).push(key);\n }\n }\n\n const needed = Object.keys(source);\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n const catalog = (catalogs[locale] ??= {});\n for (const key of needed) {\n if (catalog[key] === undefined) {\n catalog[key] = '';\n (added[locale] ??= []).push(key);\n }\n }\n }\n return { added };\n}\n\n// drops keys no longer referenced\nexport function pruneCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): Record<string, string[]> {\n const keep = new Set([...registry.messages().keys(), ...registry.usedKeys().keys()]);\n const removed: Record<string, string[]> = {};\n for (const locale of cfg.locales) {\n const catalog = catalogs[locale];\n if (!catalog) continue;\n for (const key of Object.keys(catalog)) {\n if (!keep.has(key)) {\n delete catalog[key];\n (removed[locale] ??= []).push(key);\n }\n }\n }\n return removed;\n}\n","import { parse } from '@babel/parser';\r\nimport { stableKey } from './key';\r\n\r\nexport interface TaggedParam {\r\n name: string;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nexport interface TransComponent {\r\n name: string;\r\n source: string;\r\n}\r\n\r\nexport interface TaggedMessage {\r\n key: string;\r\n message: string;\r\n params: TaggedParam[];\r\n start: number;\r\n end: number;\r\n tagStart: number;\r\n tagEnd: number;\r\n file: string;\r\n jsx?: { name: string; components: TransComponent[] };\r\n}\r\n\r\nexport interface UsedKey {\r\n key: string;\r\n file: string;\r\n}\r\n\r\nexport interface Analysis {\r\n tagged: TaggedMessage[];\r\n usedKeys: UsedKey[];\r\n}\r\n\r\ninterface AstNode {\r\n type: string;\r\n start: number;\r\n end: number;\r\n [key: string]: unknown;\r\n}\r\n\r\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\r\n\r\nexport function analyze(code: string, file: string): Analysis {\r\n const jsx = /\\.[jt]sx$/.test(file);\r\n const ast = parse(code, {\r\n sourceType: 'module',\r\n errorRecovery: true,\r\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\r\n });\r\n\r\n const tagged: TaggedMessage[] = [];\r\n const usedKeys: UsedKey[] = [];\r\n\r\n walk(ast.program as unknown as AstNode, (node) => {\r\n if (node.type === 'TaggedTemplateExpression') {\r\n const tag = node.tag as AstNode;\r\n const explicit = explicitId(tag);\r\n if (!explicit && !isTReference(tag)) return;\r\n const quasi = node.quasi as AstNode;\r\n const message = buildMessage(code, quasi);\r\n if (!message) return;\r\n tagged.push({\r\n key: explicit ? explicit.key : stableKey(message.text),\r\n message: message.text,\r\n params: message.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: explicit ? explicit.refStart : tag.start,\r\n tagEnd: explicit ? explicit.refEnd : tag.end,\r\n file,\r\n });\r\n } else if (node.type === 'CallExpression') {\r\n const callee = node.callee as AstNode;\r\n if (!isTReference(callee)) return;\r\n const args = node.arguments as AstNode[];\r\n const first = args[0];\r\n if (first?.type === 'StringLiteral') {\r\n usedKeys.push({ key: first.value as string, file });\r\n }\r\n } else if (node.type === 'JSXElement') {\r\n handleTrans(code, node, file, tagged, usedKeys);\r\n }\r\n });\r\n\r\n return { tagged, usedKeys };\r\n}\r\n\r\nfunction handleTrans(\r\n code: string,\r\n node: AstNode,\r\n file: string,\r\n tagged: TaggedMessage[],\r\n usedKeys: UsedKey[],\r\n): void {\r\n const opening = node.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name !== 'Trans') return;\r\n\r\n const attrs = opening.attributes as AstNode[];\r\n const idAttr = attrs.find((a) => a.type === 'JSXAttribute' && (a.name as AstNode).name === 'id');\r\n const children = node.children as AstNode[] | undefined;\r\n\r\n if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type !== 'StringLiteral') return;\r\n const id = value.value as string;\r\n // id + children → extract under the explicit key\r\n if (attrs.length === 1 && children?.length) {\r\n const built = buildTransMessage(code, children);\r\n if (built?.text.trim()) {\r\n tagged.push({\r\n key: id,\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n return;\r\n }\r\n }\r\n usedKeys.push({ key: id, file });\r\n return; // runtime-first, untouched\r\n }\r\n if (attrs.length > 0) return; // hand-written props → don't guess\r\n\r\n if (!children?.length) return;\r\n\r\n const built = buildTransMessage(code, children);\r\n if (!built || !built.text.trim()) return;\r\n\r\n tagged.push({\r\n key: stableKey(built.text),\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n}\r\n\r\n// t.id('key')`…` → explicit readable key\r\nfunction explicitId(tag: AstNode): { key: string; refStart: number; refEnd: number } | undefined {\r\n if (tag.type !== 'CallExpression') return undefined;\r\n const callee = tag.callee as AstNode;\r\n if (callee.type !== 'MemberExpression' || callee.computed) return undefined;\r\n const prop = callee.property as AstNode;\r\n if (prop.type !== 'Identifier' || prop.name !== 'id') return undefined;\r\n const obj = callee.object as AstNode;\r\n if (!isTReference(obj)) return undefined;\r\n const args = tag.arguments as AstNode[];\r\n const first = args[0];\r\n if (args.length !== 1 || first?.type !== 'StringLiteral') return undefined;\r\n return { key: first.value as string, refStart: obj.start, refEnd: obj.end };\r\n}\r\n\r\ninterface BuiltTrans {\r\n text: string;\r\n params: TaggedParam[];\r\n components: TransComponent[];\r\n}\r\n\r\nfunction buildTransMessage(code: string, children: AstNode[]): BuiltTrans | undefined {\r\n const params: TaggedParam[] = [];\r\n const components: TransComponent[] = [];\r\n const takenParams = new Map<string, string>();\r\n const takenTags = new Map<string, string>();\r\n\r\n function walkChildren(nodes: AstNode[]): string | undefined {\r\n let text = '';\r\n for (const child of nodes) {\r\n if (child.type === 'JSXText') {\r\n text += escapeText(cleanJsxText(child.value as string));\r\n } else if (child.type === 'JSXExpressionContainer') {\r\n const expr = child.expression as AstNode;\r\n if (expr.type === 'JSXEmptyExpression') continue;\r\n if (expr.type === 'StringLiteral') {\r\n text += escapeText(expr.value as string);\r\n continue;\r\n }\r\n if (expr.type === 'TaggedTemplateExpression') return undefined; // overlap hazard\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, params.length), source, takenParams);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}`;\r\n } else if (child.type === 'JSXElement') {\r\n const opening = child.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name === 'Trans') return undefined;\r\n const source = selfClosedSource(code, opening);\r\n const tag = uniqueName((nameNode.name as string).toLowerCase(), source, takenTags);\r\n if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });\r\n if (!child.closingElement) {\r\n text += `<${tag}/>`;\r\n } else {\r\n const inner = walkChildren(child.children as AstNode[]);\r\n if (inner === undefined) return undefined;\r\n text += `<${tag}>${inner}</${tag}>`;\r\n }\r\n } else {\r\n return undefined; // fragments / spread children → bail\r\n }\r\n }\r\n return text;\r\n }\r\n\r\n const text = walkChildren(children);\r\n if (text === undefined) return undefined;\r\n return { text, params, components };\r\n}\r\n\r\n// JSX text semantics: newline-indent boundaries removed, interior joins = one space\r\nfunction cleanJsxText(raw: string): string {\r\n const lines = raw.split(/\\r\\n|[\\r\\n]/);\r\n let out = '';\r\n for (let i = 0; i < lines.length; i++) {\r\n let line = lines[i]!.replace(/\\t/g, ' ');\r\n if (i !== 0) line = line.replace(/^ +/, '');\r\n if (i !== lines.length - 1) line = line.replace(/ +$/, '');\r\n if (!line) continue;\r\n if (out && i !== 0) out += ' ';\r\n out += line;\r\n }\r\n return out;\r\n}\r\n\r\nfunction selfClosedSource(code: string, opening: AstNode): string {\r\n const src = code.slice(opening.start, opening.end);\r\n return src.endsWith('/>') ? src : `${src.slice(0, -1).trimEnd()} />`;\r\n}\r\n\r\nfunction isTReference(node: AstNode): boolean {\r\n if (node.type === 'Identifier') return node.name === 't';\r\n if (node.type === 'MemberExpression' && !node.computed) {\r\n const prop = node.property as AstNode;\r\n return prop.type === 'Identifier' && prop.name === 't';\r\n }\r\n return false;\r\n}\r\n\r\ninterface BuiltMessage {\r\n text: string;\r\n params: TaggedParam[];\r\n}\r\n\r\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\r\n const quasis = quasi.quasis as AstNode[];\r\n const expressions = quasi.expressions as AstNode[];\r\n const params: TaggedParam[] = [];\r\n const taken = new Map<string, string>();\r\n\r\n let text = escapeText(cookedValue(quasis[0]));\r\n\r\n for (let i = 0; i < expressions.length; i++) {\r\n const expr = expressions[i];\r\n if (!expr) return undefined;\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, i), source, taken);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\r\n }\r\n return { text, params };\r\n}\r\n\r\nfunction cookedValue(element: AstNode | undefined): string {\r\n if (!element) return '';\r\n const value = element.value as { cooked?: string; raw: string };\r\n return value.cooked ?? value.raw;\r\n}\r\n\r\n// literal braces → escaped\r\nfunction escapeText(text: string): string {\r\n return text.replace(/[{}]/g, (m) => m + m);\r\n}\r\n\r\nfunction deriveName(expr: AstNode, index: number): string {\r\n if (expr.type === 'Identifier') return expr.name as string;\r\n if (expr.type === 'MemberExpression' && !expr.computed) {\r\n const prop = expr.property as AstNode;\r\n if (prop.type === 'Identifier') return prop.name as string;\r\n }\r\n return `_${index}`;\r\n}\r\n\r\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\r\n let name = base;\r\n let n = 2;\r\n while (taken.has(name) && taken.get(name) !== source) {\r\n name = `${base}${n}`;\r\n n += 1;\r\n }\r\n taken.set(name, source);\r\n return name;\r\n}\r\n\r\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\r\n visit(node);\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n if (isNode(item)) walk(item, visit);\r\n }\r\n } else if (isNode(value)) {\r\n walk(value, visit);\r\n }\r\n }\r\n}\r\n\r\nfunction isNode(value: unknown): value is AstNode {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as { type?: unknown }).type === 'string'\r\n );\r\n}\r\n","import { createHash } from 'node:crypto';\n\nexport function stableKey(message: string): string {\n return createHash('sha256').update(message).digest('base64url').slice(0, 8);\n}\n","import type { Analysis, TaggedMessage } from './analyze';\n\nexport class MessageRegistry {\n private files = new Map<string, Analysis>();\n\n update(file: string, analysis: Analysis): void {\n this.files.set(file, analysis);\n }\n\n remove(file: string): void {\n this.files.delete(file);\n }\n\n messages(): Map<string, TaggedMessage> {\n const out = new Map<string, TaggedMessage>();\n for (const analysis of this.files.values()) {\n for (const msg of analysis.tagged) {\n const existing = out.get(msg.key);\n if (existing) {\n if (existing.message !== msg.message) {\n console.warn(\n `[verbaly] key collision \"${msg.key}\": ` +\n `${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)} — second dropped.`,\n );\n }\n continue;\n }\n out.set(msg.key, msg);\n }\n }\n return out;\n }\n\n usedKeys(): Map<string, string[]> {\n const out = new Map<string, string[]>();\n for (const analysis of this.files.values()) {\n for (const used of analysis.usedKeys) {\n const files = out.get(used.key) ?? [];\n if (!files.includes(used.file)) files.push(used.file);\n out.set(used.key, files);\n }\n }\n return out;\n }\n}\n"],"mappings":"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","names":["writeFileSync","join","writeFileSync","join","readFileSync","join","readFileSync","parse","parse","built","text","visit","readFileSync"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/catalog.ts","../src/check.ts","../src/codegen.ts","../src/params.ts","../src/config.ts","../src/extract.ts","../src/analyze.ts","../src/key.ts","../src/registry.ts","../src/translate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport { parseArgs } from 'node:util';\r\nimport { loadCatalogs, writeCatalog } from './catalog';\r\nimport { check, formatCheckResult } from './check';\r\nimport { writeDts } from './codegen';\r\nimport { loadConfig } from './config';\r\nimport { extractProject, pruneCatalogs, syncCatalogs } from './extract';\r\nimport { translateCatalogs, type TranslateProvider } from './translate';\r\nimport type { ResolvedConfig } from './config';\r\n\r\nconst HELP = `verbaly — i18n compiler\r\n\r\nUsage:\r\n verbaly extract scan sources, update catalogs and types\r\n verbaly check verify translations are complete (CI)\r\n verbaly translate fill missing translations via a provider (default: claude)\r\n\r\nOptions:\r\n --root <path> project root (default: cwd)\r\n --dir <path> catalogs directory (default: locales)\r\n --source <locale> source locale (default: en)\r\n --locales <csv> extra locales; for translate: target locales to fill\r\n --prune drop keys no longer referenced (extract)\r\n --model <id> model override for the claude provider (translate)\r\n --dry-run list what would be translated, write nothing (translate)\r\n\r\nConfig file: verbaly.config.{js,mjs,ts,mts,json} at root (flags win).\r\nThe claude provider needs @anthropic-ai/sdk installed and ANTHROPIC_API_KEY set.\r\n`;\r\n\r\nasync function main(): Promise<void> {\r\n const { positionals, values } = parseArgs({\r\n allowPositionals: true,\r\n options: {\r\n root: { type: 'string' },\r\n dir: { type: 'string' },\r\n source: { type: 'string' },\r\n locales: { type: 'string' },\r\n prune: { type: 'boolean' },\r\n model: { type: 'string' },\r\n 'dry-run': { type: 'boolean' },\r\n help: { type: 'boolean', short: 'h' },\r\n },\r\n });\r\n\r\n const command = positionals[0];\r\n if (values.help || !command) {\r\n console.log(HELP);\r\n process.exitCode = command ? 0 : 1;\r\n return;\r\n }\r\n\r\n const cfg = await loadConfig(values.root ?? process.cwd(), {\r\n dir: values.dir,\r\n sourceLocale: values.source,\r\n locales: values.locales?.split(','),\r\n });\r\n\r\n if (command === 'extract') {\r\n const registry = await extractProject(cfg);\r\n const catalogs = loadCatalogs(cfg);\r\n if (values.prune) {\r\n const removed = pruneCatalogs(cfg, catalogs, registry);\r\n for (const [locale, keys] of Object.entries(removed)) {\r\n console.log(` ${locale}: -${keys.length} pruned`);\r\n }\r\n }\r\n const { added } = syncCatalogs(cfg, catalogs, registry);\r\n for (const locale of cfg.locales) {\r\n writeCatalog(cfg, locale, catalogs[locale] ?? {});\r\n }\r\n writeDts(cfg, new Map(Object.entries(catalogs[cfg.sourceLocale] ?? {})));\r\n const total = registry.messages().size;\r\n console.log(`[verbaly] ${total} messages · locales: ${cfg.locales.join(', ')}`);\r\n for (const [locale, keys] of Object.entries(added)) {\r\n console.log(` ${locale}: +${keys.length}`);\r\n }\r\n return;\r\n }\r\n\r\n if (command === 'check') {\r\n const registry = await extractProject(cfg);\r\n const result = check(cfg, loadCatalogs(cfg), registry);\r\n if (result.ok) {\r\n console.log('[verbaly] all translations complete ✓');\r\n return;\r\n }\r\n console.error(`[verbaly] check failed\\n${formatCheckResult(result)}`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n\r\n if (command === 'translate') {\r\n const catalogs = loadCatalogs(cfg);\r\n const provider = await resolveProvider(cfg, values.model);\r\n const result = await translateCatalogs(cfg, catalogs, provider, {\r\n locales: values.locales?.split(','),\r\n batchSize: cfg.translate.batchSize,\r\n dryRun: values['dry-run'],\r\n });\r\n\r\n if (values['dry-run']) {\r\n const entries = Object.entries(result.pending);\r\n if (entries.length === 0) {\r\n console.log('[verbaly] nothing to translate ✓');\r\n return;\r\n }\r\n for (const [locale, keys] of entries) {\r\n console.log(` ${locale}: ${keys.length} missing — ${keys.join(', ')}`);\r\n }\r\n return;\r\n }\r\n\r\n for (const locale of Object.keys(result.translated)) {\r\n writeCatalog(cfg, locale, catalogs[locale] ?? {});\r\n console.log(` ${locale}: +${result.translated[locale]!.length} translated`);\r\n }\r\n for (const [locale, keys] of Object.entries(result.invalid)) {\r\n console.warn(\r\n ` ${locale}: ${keys.length} rejected (params/tags not preserved): ${keys.join(', ')}`,\r\n );\r\n }\r\n if (Object.keys(result.translated).length === 0 && Object.keys(result.invalid).length === 0) {\r\n console.log('[verbaly] nothing to translate ✓');\r\n }\r\n return;\r\n }\r\n\r\n console.error(`[verbaly] unknown command \"${command}\"\\n${HELP}`);\r\n process.exitCode = 1;\r\n}\r\n\r\nasync function resolveProvider(\r\n cfg: ResolvedConfig,\r\n model: string | undefined,\r\n): Promise<TranslateProvider> {\r\n const configured = cfg.translate.provider;\r\n if (typeof configured === 'function') return configured;\r\n const { claudeProvider } = await import('./providers/claude');\r\n return claudeProvider({ model: model ?? cfg.translate.model });\r\n}\r\n\r\nmain().catch((error: unknown) => {\r\n console.error('[verbaly]', error instanceof Error ? error.message : error);\r\n process.exitCode = 1;\r\n});\r\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from './config';\n\nexport type Catalog = Record<string, string>;\nexport type Catalogs = Record<string, Catalog>;\n\nexport function catalogPath(cfg: ResolvedConfig, locale: string): string {\n return join(cfg.dir, `${locale}.json`);\n}\n\nexport function readCatalog(cfg: ResolvedConfig, locale: string): Catalog {\n try {\n return JSON.parse(readFileSync(catalogPath(cfg, locale), 'utf8')) as Catalog;\n } catch {\n return {};\n }\n}\n\nexport function loadCatalogs(cfg: ResolvedConfig): Catalogs {\n const catalogs: Catalogs = {};\n for (const locale of cfg.locales) catalogs[locale] = readCatalog(cfg, locale);\n return catalogs;\n}\n\nexport function serializeCatalog(catalog: Catalog): string {\n const sorted: Catalog = {};\n for (const key of Object.keys(catalog).sort()) {\n const value = catalog[key];\n if (value !== undefined) sorted[key] = value;\n }\n return JSON.stringify(sorted, null, 2) + '\\n';\n}\n\nexport function writeCatalog(cfg: ResolvedConfig, locale: string, catalog: Catalog): string {\n const serialized = serializeCatalog(catalog);\n mkdirSync(cfg.dir, { recursive: true });\n writeFileSync(catalogPath(cfg, locale), serialized);\n return serialized;\n}\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport type { MessageRegistry } from './registry';\n\nexport interface MissingEntry {\n locale: string;\n key: string;\n source?: string;\n}\n\nexport interface UnknownEntry {\n key: string;\n files: string[];\n}\n\nexport interface CheckResult {\n ok: boolean;\n missing: MissingEntry[];\n unknown: UnknownEntry[];\n}\n\nexport function check(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): CheckResult {\n const source = catalogs[cfg.sourceLocale] ?? {};\n const extracted = registry.messages();\n\n const unknown: UnknownEntry[] = [];\n for (const [key, files] of registry.usedKeys()) {\n const known =\n extracted.has(key) || cfg.locales.some((locale) => catalogs[locale]?.[key] !== undefined);\n if (!known) unknown.push({ key, files });\n }\n\n const needed = new Set<string>([...extracted.keys(), ...Object.keys(source)]);\n\n const missing: MissingEntry[] = [];\n for (const key of extracted.keys()) {\n if (!source[key]) {\n missing.push({ locale: cfg.sourceLocale, key, source: extracted.get(key)?.message });\n }\n }\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n for (const key of needed) {\n if (!catalogs[locale]?.[key]) {\n missing.push({ locale, key, source: source[key] ?? extracted.get(key)?.message });\n }\n }\n }\n\n return { ok: missing.length === 0 && unknown.length === 0, missing, unknown };\n}\n\nexport function formatCheckResult(result: CheckResult): string {\n const lines: string[] = [];\n if (result.missing.length > 0) {\n lines.push('missing translations:');\n for (const entry of result.missing) {\n const hint = entry.source ? ` — \"${truncate(entry.source, 40)}\"` : '';\n lines.push(` [${entry.locale}] ${entry.key}${hint}`);\n }\n }\n if (result.unknown.length > 0) {\n lines.push('unknown keys (not in any catalog):');\n for (const entry of result.unknown) {\n lines.push(` ${entry.key} — used in ${entry.files.join(', ')}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? text.slice(0, max - 1) + '…' : text;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Catalog } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams, renderParamType } from './params';\n\nexport const VIRTUAL_ID = 'virtual:verbaly';\n\nexport function generateRuntimeModule(cfg: ResolvedConfig): string {\n const others = cfg.locales.filter((locale) => locale !== cfg.sourceLocale);\n const src = JSON.stringify(cfg.sourceLocale);\n const loaders = others\n .map(\n (locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`,\n )\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\n loaders: {\n${loaders}\n },\n});\n\nexport const verbaly = v;\nexport const t = v.t;\n\nexport function getLocale() {\n return v.locale;\n}\n\nexport function subscribe(listener) {\n return v.subscribe(listener);\n}\n\nexport async function setLocale(locale) {\n await v.loadLocale(locale);\n v.setLocale(locale);\n}\n`;\n}\n\nexport function generateLocaleModule(catalog: Catalog): string {\n return `export default ${JSON.stringify(catalog)};\\n`;\n}\n\nexport function generateDts(entries: Map<string, string>): string {\n const lines: string[] = [];\n for (const [key, message] of [...entries.entries()].sort(([a], [b]) => a.localeCompare(b))) {\n const params = collectParams(message);\n if (params.size === 0) {\n lines.push(` ${JSON.stringify(key)}: never;`);\n } else {\n const fields = [...params.entries()]\n .map(([name, types]) => `${JSON.stringify(name)}: ${renderParamType(types)}`)\n .join('; ');\n lines.push(` ${JSON.stringify(key)}: { ${fields} };`);\n }\n }\n\n return `// generated by verbaly — do not edit\ndeclare module 'virtual:verbaly' {\n export interface VerbalyMessages {\n${lines.join('\\n')}\n }\n export type VerbalyKey = keyof VerbalyMessages & string;\n\n export const verbaly: import('verbaly').Verbaly<VerbalyKey>;\n\n export function t<K extends VerbalyKey>(\n key: K,\n ...args: [VerbalyMessages[K]] extends [never] ? [] : [VerbalyMessages[K]]\n ): string;\n export function t(strings: TemplateStringsArray, ...values: unknown[]): string;\n export namespace t {\n export function id(\n key: string,\n ): (strings: TemplateStringsArray, ...values: unknown[]) => string;\n }\n\n export function setLocale(locale: string): Promise<void>;\n export function getLocale(): string;\n export function subscribe(listener: () => void): () => void;\n}\n\ndeclare module 'virtual:verbaly/locale/*' {\n const messages: Record<string, string>;\n export default messages;\n}\n`;\n}\n\nexport function writeDts(cfg: ResolvedConfig, entries: Map<string, string>): void {\n writeFileSync(join(cfg.root, 'verbaly.d.ts'), generateDts(entries));\n}\n","import { parse, type MessageNode } from 'verbaly';\n\nexport type ParamType = 'number' | 'string' | 'date' | 'unknown';\n\nconst PLURAL_KEYS = new Set(['zero', 'one', 'two', 'few', 'many']);\nconst NUMBER_FORMATS = new Set(['number', 'integer', 'percent', 'currency']);\nconst DATE_FORMATS = new Set(['date', 'time']);\n\nexport function collectParams(message: string): Map<string, Set<ParamType>> {\n const out = new Map<string, Set<ParamType>>();\n visit(parse(message), out);\n return out;\n}\n\nfunction visit(nodes: MessageNode[], out: Map<string, Set<ParamType>>): void {\n for (const node of nodes) {\n if (node.kind !== 'param') continue;\n const types = out.get(node.name) ?? new Set<ParamType>();\n out.set(node.name, types);\n\n if (node.variants) {\n for (const [key, body] of node.variants) {\n if (PLURAL_KEYS.has(key) || /^=\\d+$/.test(key)) types.add('number');\n else if (key !== 'other') types.add('string');\n visit(body, out);\n }\n if (types.size === 0) types.add('string');\n } else if (node.format && NUMBER_FORMATS.has(node.format)) {\n types.add('number');\n } else if (node.format && DATE_FORMATS.has(node.format)) {\n types.add('date');\n } else {\n types.add('unknown');\n }\n }\n}\n\nexport function renderParamType(types: Set<ParamType>): string {\n if (types.has('unknown') || types.size === 0) return 'unknown';\n const parts: string[] = [];\n if (types.has('number')) parts.push('number');\n if (types.has('string')) parts.push('string');\n if (types.has('date')) parts.push('Date | number | string');\n return parts.join(' | ');\n}\n","import { existsSync, readFileSync, readdirSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport type { TranslateProvider } from './translate';\r\n\r\nexport interface TranslateConfig {\r\n provider?: 'claude' | TranslateProvider;\r\n model?: string;\r\n batchSize?: number;\r\n}\r\n\r\nexport interface VerbalyConfig {\r\n root?: string;\r\n dir?: string;\r\n sourceLocale?: string;\r\n locales?: string[];\r\n include?: string[];\r\n exclude?: string[];\r\n translate?: TranslateConfig;\r\n}\r\n\r\nexport interface ResolvedConfig {\r\n root: string;\r\n dir: string;\r\n sourceLocale: string;\r\n locales: string[];\r\n include: string[];\r\n exclude: string[];\r\n translate: TranslateConfig;\r\n}\r\n\r\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\r\n const root = resolve(config.root ?? process.cwd());\r\n const dir = resolve(root, config.dir ?? 'locales');\r\n const sourceLocale = config.sourceLocale ?? 'en';\r\n\r\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\r\n if (existsSync(dir)) {\r\n for (const file of readdirSync(dir)) {\r\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\r\n }\r\n }\r\n\r\n return {\r\n root,\r\n dir,\r\n sourceLocale,\r\n locales: [...locales],\r\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\r\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\r\n translate: config.translate ?? {},\r\n };\r\n}\r\n\r\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\r\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) {\r\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\r\n return mod.default ?? {};\r\n }\r\n }\r\n for (const name of ['verbaly.config.ts', 'verbaly.config.mts']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) return loadTsConfig(path);\r\n }\r\n const jsonPath = join(root, 'verbaly.config.json');\r\n if (existsSync(jsonPath)) {\r\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\r\n }\r\n return {};\r\n}\r\n\r\nasync function loadTsConfig(path: string): Promise<VerbalyConfig> {\r\n try {\r\n const { bundleRequire } = await import('bundle-require');\r\n const { mod } = await bundleRequire<{ default?: VerbalyConfig }>({ filepath: path });\r\n return mod.default ?? {};\r\n } catch (error) {\r\n if (isModuleNotFound(error, 'esbuild')) {\r\n throw new Error(`[verbaly] ${path} needs esbuild — install it: pnpm add -D esbuild`, {\r\n cause: error,\r\n });\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n\r\n// file config + inline overrides\r\nexport async function loadConfig(\r\n root: string,\r\n overrides: VerbalyConfig = {},\r\n): Promise<ResolvedConfig> {\r\n const fileConfig = await loadConfigFile(root);\r\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\r\n}\r\n\r\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\r\n return Object.fromEntries(\r\n Object.entries(config).filter(([, value]) => value !== undefined),\r\n ) as VerbalyConfig;\r\n}\r\n","import { readFileSync } from 'node:fs';\nimport { glob } from 'tinyglobby';\nimport { analyze } from './analyze';\nimport type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { MessageRegistry } from './registry';\n\nexport async function extractProject(cfg: ResolvedConfig): Promise<MessageRegistry> {\n const files = await glob(cfg.include, {\n cwd: cfg.root,\n ignore: cfg.exclude,\n absolute: true,\n });\n const registry = new MessageRegistry();\n for (const file of files) {\n registry.update(file, analyze(readFileSync(file, 'utf8'), file));\n }\n return registry;\n}\n\nexport interface SyncResult {\n added: Record<string, string[]>;\n}\n\n// fills source texts + '' placeholders\nexport function syncCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): SyncResult {\n const added: Record<string, string[]> = {};\n const source = (catalogs[cfg.sourceLocale] ??= {});\n\n for (const [key, msg] of registry.messages()) {\n if (source[key] !== msg.message) {\n source[key] = msg.message;\n (added[cfg.sourceLocale] ??= []).push(key);\n }\n }\n\n const needed = Object.keys(source);\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n const catalog = (catalogs[locale] ??= {});\n for (const key of needed) {\n if (catalog[key] === undefined) {\n catalog[key] = '';\n (added[locale] ??= []).push(key);\n }\n }\n }\n return { added };\n}\n\n// drops keys no longer referenced\nexport function pruneCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): Record<string, string[]> {\n const keep = new Set([...registry.messages().keys(), ...registry.usedKeys().keys()]);\n const removed: Record<string, string[]> = {};\n for (const locale of cfg.locales) {\n const catalog = catalogs[locale];\n if (!catalog) continue;\n for (const key of Object.keys(catalog)) {\n if (!keep.has(key)) {\n delete catalog[key];\n (removed[locale] ??= []).push(key);\n }\n }\n }\n return removed;\n}\n","import { parse } from '@babel/parser';\r\nimport { stableKey } from './key';\r\n\r\nexport interface TaggedParam {\r\n name: string;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nexport interface TransComponent {\r\n name: string;\r\n source: string;\r\n}\r\n\r\nexport interface TaggedMessage {\r\n key: string;\r\n message: string;\r\n params: TaggedParam[];\r\n start: number;\r\n end: number;\r\n tagStart: number;\r\n tagEnd: number;\r\n file: string;\r\n jsx?: { name: string; components: TransComponent[] };\r\n}\r\n\r\nexport interface UsedKey {\r\n key: string;\r\n file: string;\r\n}\r\n\r\nexport interface Analysis {\r\n tagged: TaggedMessage[];\r\n usedKeys: UsedKey[];\r\n}\r\n\r\ninterface AstNode {\r\n type: string;\r\n start: number;\r\n end: number;\r\n [key: string]: unknown;\r\n}\r\n\r\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\r\n\r\nexport function analyze(code: string, file: string): Analysis {\r\n const jsx = /\\.[jt]sx$/.test(file);\r\n const ast = parse(code, {\r\n sourceType: 'module',\r\n errorRecovery: true,\r\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\r\n });\r\n\r\n const tagged: TaggedMessage[] = [];\r\n const usedKeys: UsedKey[] = [];\r\n\r\n walk(ast.program as unknown as AstNode, (node) => {\r\n if (node.type === 'TaggedTemplateExpression') {\r\n const tag = node.tag as AstNode;\r\n const explicit = explicitId(tag);\r\n if (!explicit && !isTReference(tag)) return;\r\n const quasi = node.quasi as AstNode;\r\n const message = buildMessage(code, quasi);\r\n if (!message) return;\r\n tagged.push({\r\n key: explicit ? explicit.key : stableKey(message.text),\r\n message: message.text,\r\n params: message.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: explicit ? explicit.refStart : tag.start,\r\n tagEnd: explicit ? explicit.refEnd : tag.end,\r\n file,\r\n });\r\n } else if (node.type === 'CallExpression') {\r\n const callee = node.callee as AstNode;\r\n if (!isTReference(callee)) return;\r\n const args = node.arguments as AstNode[];\r\n const first = args[0];\r\n if (first?.type === 'StringLiteral') {\r\n usedKeys.push({ key: first.value as string, file });\r\n }\r\n } else if (node.type === 'JSXElement') {\r\n handleTrans(code, node, file, tagged, usedKeys);\r\n }\r\n });\r\n\r\n return { tagged, usedKeys };\r\n}\r\n\r\nfunction handleTrans(\r\n code: string,\r\n node: AstNode,\r\n file: string,\r\n tagged: TaggedMessage[],\r\n usedKeys: UsedKey[],\r\n): void {\r\n const opening = node.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name !== 'Trans') return;\r\n\r\n const attrs = opening.attributes as AstNode[];\r\n const idAttr = attrs.find((a) => a.type === 'JSXAttribute' && (a.name as AstNode).name === 'id');\r\n const children = node.children as AstNode[] | undefined;\r\n\r\n if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type !== 'StringLiteral') return;\r\n const id = value.value as string;\r\n // id + children → extract under the explicit key\r\n if (attrs.length === 1 && children?.length) {\r\n const built = buildTransMessage(code, children);\r\n if (built?.text.trim()) {\r\n tagged.push({\r\n key: id,\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n return;\r\n }\r\n }\r\n usedKeys.push({ key: id, file });\r\n return; // runtime-first, untouched\r\n }\r\n if (attrs.length > 0) return; // hand-written props → don't guess\r\n\r\n if (!children?.length) return;\r\n\r\n const built = buildTransMessage(code, children);\r\n if (!built || !built.text.trim()) return;\r\n\r\n tagged.push({\r\n key: stableKey(built.text),\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n}\r\n\r\n// t.id('key')`…` → explicit readable key\r\nfunction explicitId(tag: AstNode): { key: string; refStart: number; refEnd: number } | undefined {\r\n if (tag.type !== 'CallExpression') return undefined;\r\n const callee = tag.callee as AstNode;\r\n if (callee.type !== 'MemberExpression' || callee.computed) return undefined;\r\n const prop = callee.property as AstNode;\r\n if (prop.type !== 'Identifier' || prop.name !== 'id') return undefined;\r\n const obj = callee.object as AstNode;\r\n if (!isTReference(obj)) return undefined;\r\n const args = tag.arguments as AstNode[];\r\n const first = args[0];\r\n if (args.length !== 1 || first?.type !== 'StringLiteral') return undefined;\r\n return { key: first.value as string, refStart: obj.start, refEnd: obj.end };\r\n}\r\n\r\ninterface BuiltTrans {\r\n text: string;\r\n params: TaggedParam[];\r\n components: TransComponent[];\r\n}\r\n\r\nfunction buildTransMessage(code: string, children: AstNode[]): BuiltTrans | undefined {\r\n const params: TaggedParam[] = [];\r\n const components: TransComponent[] = [];\r\n const takenParams = new Map<string, string>();\r\n const takenTags = new Map<string, string>();\r\n\r\n function walkChildren(nodes: AstNode[]): string | undefined {\r\n let text = '';\r\n for (const child of nodes) {\r\n if (child.type === 'JSXText') {\r\n text += escapeText(cleanJsxText(child.value as string));\r\n } else if (child.type === 'JSXExpressionContainer') {\r\n const expr = child.expression as AstNode;\r\n if (expr.type === 'JSXEmptyExpression') continue;\r\n if (expr.type === 'StringLiteral') {\r\n text += escapeText(expr.value as string);\r\n continue;\r\n }\r\n if (expr.type === 'TaggedTemplateExpression') return undefined; // overlap hazard\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, params.length), source, takenParams);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}`;\r\n } else if (child.type === 'JSXElement') {\r\n const opening = child.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name === 'Trans') return undefined;\r\n const source = selfClosedSource(code, opening);\r\n const tag = uniqueName((nameNode.name as string).toLowerCase(), source, takenTags);\r\n if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });\r\n if (!child.closingElement) {\r\n text += `<${tag}/>`;\r\n } else {\r\n const inner = walkChildren(child.children as AstNode[]);\r\n if (inner === undefined) return undefined;\r\n text += `<${tag}>${inner}</${tag}>`;\r\n }\r\n } else {\r\n return undefined; // fragments / spread children → bail\r\n }\r\n }\r\n return text;\r\n }\r\n\r\n const text = walkChildren(children);\r\n if (text === undefined) return undefined;\r\n return { text, params, components };\r\n}\r\n\r\n// JSX text semantics: newline-indent boundaries removed, interior joins = one space\r\nfunction cleanJsxText(raw: string): string {\r\n const lines = raw.split(/\\r\\n|[\\r\\n]/);\r\n let out = '';\r\n for (let i = 0; i < lines.length; i++) {\r\n let line = lines[i]!.replace(/\\t/g, ' ');\r\n if (i !== 0) line = line.replace(/^ +/, '');\r\n if (i !== lines.length - 1) line = line.replace(/ +$/, '');\r\n if (!line) continue;\r\n if (out && i !== 0) out += ' ';\r\n out += line;\r\n }\r\n return out;\r\n}\r\n\r\nfunction selfClosedSource(code: string, opening: AstNode): string {\r\n const src = code.slice(opening.start, opening.end);\r\n return src.endsWith('/>') ? src : `${src.slice(0, -1).trimEnd()} />`;\r\n}\r\n\r\nfunction isTReference(node: AstNode): boolean {\r\n if (node.type === 'Identifier') return node.name === 't';\r\n if (node.type === 'MemberExpression' && !node.computed) {\r\n const prop = node.property as AstNode;\r\n return prop.type === 'Identifier' && prop.name === 't';\r\n }\r\n return false;\r\n}\r\n\r\ninterface BuiltMessage {\r\n text: string;\r\n params: TaggedParam[];\r\n}\r\n\r\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\r\n const quasis = quasi.quasis as AstNode[];\r\n const expressions = quasi.expressions as AstNode[];\r\n const params: TaggedParam[] = [];\r\n const taken = new Map<string, string>();\r\n\r\n let text = escapeText(cookedValue(quasis[0]));\r\n\r\n for (let i = 0; i < expressions.length; i++) {\r\n const expr = expressions[i];\r\n if (!expr) return undefined;\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, i), source, taken);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\r\n }\r\n return { text, params };\r\n}\r\n\r\nfunction cookedValue(element: AstNode | undefined): string {\r\n if (!element) return '';\r\n const value = element.value as { cooked?: string; raw: string };\r\n return value.cooked ?? value.raw;\r\n}\r\n\r\n// literal braces → escaped\r\nfunction escapeText(text: string): string {\r\n return text.replace(/[{}]/g, (m) => m + m);\r\n}\r\n\r\nfunction deriveName(expr: AstNode, index: number): string {\r\n if (expr.type === 'Identifier') return expr.name as string;\r\n if (expr.type === 'MemberExpression' && !expr.computed) {\r\n const prop = expr.property as AstNode;\r\n if (prop.type === 'Identifier') return prop.name as string;\r\n }\r\n return `_${index}`;\r\n}\r\n\r\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\r\n let name = base;\r\n let n = 2;\r\n while (taken.has(name) && taken.get(name) !== source) {\r\n name = `${base}${n}`;\r\n n += 1;\r\n }\r\n taken.set(name, source);\r\n return name;\r\n}\r\n\r\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\r\n visit(node);\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n if (isNode(item)) walk(item, visit);\r\n }\r\n } else if (isNode(value)) {\r\n walk(value, visit);\r\n }\r\n }\r\n}\r\n\r\nfunction isNode(value: unknown): value is AstNode {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as { type?: unknown }).type === 'string'\r\n );\r\n}\r\n","import { createHash } from 'node:crypto';\n\nexport function stableKey(message: string): string {\n return createHash('sha256').update(message).digest('base64url').slice(0, 8);\n}\n","import type { Analysis, TaggedMessage } from './analyze';\n\nexport class MessageRegistry {\n private files = new Map<string, Analysis>();\n\n update(file: string, analysis: Analysis): void {\n this.files.set(file, analysis);\n }\n\n remove(file: string): void {\n this.files.delete(file);\n }\n\n messages(): Map<string, TaggedMessage> {\n const out = new Map<string, TaggedMessage>();\n for (const analysis of this.files.values()) {\n for (const msg of analysis.tagged) {\n const existing = out.get(msg.key);\n if (existing) {\n if (existing.message !== msg.message) {\n console.warn(\n `[verbaly] key collision \"${msg.key}\": ` +\n `${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)} — second dropped.`,\n );\n }\n continue;\n }\n out.set(msg.key, msg);\n }\n }\n return out;\n }\n\n usedKeys(): Map<string, string[]> {\n const out = new Map<string, string[]>();\n for (const analysis of this.files.values()) {\n for (const used of analysis.usedKeys) {\n const files = out.get(used.key) ?? [];\n if (!files.includes(used.file)) files.push(used.file);\n out.set(used.key, files);\n }\n }\n return out;\n }\n}\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams } from './params';\n\nexport interface TranslateRequest {\n sourceLocale: string;\n targetLocale: string;\n messages: Record<string, string>;\n}\n\nexport type TranslateProvider = (request: TranslateRequest) => Promise<Record<string, string>>;\n\nexport interface TranslateOptions {\n locales?: string[];\n batchSize?: number;\n dryRun?: boolean;\n}\n\nexport interface TranslateResult {\n translated: Record<string, string[]>;\n invalid: Record<string, string[]>;\n pending: Record<string, string[]>;\n}\n\n// fills '' entries via the provider; invalid translations stay '' (check keeps failing)\nexport async function translateCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n provider: TranslateProvider,\n options: TranslateOptions = {},\n): Promise<TranslateResult> {\n const batchSize = options.batchSize ?? 20;\n const targets = (options.locales ?? cfg.locales).filter(\n (locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale),\n );\n const source = catalogs[cfg.sourceLocale] ?? {};\n const result: TranslateResult = { translated: {}, invalid: {}, pending: {} };\n\n for (const locale of targets) {\n const catalog = (catalogs[locale] ??= {});\n const missing = Object.keys(source).filter((key) => source[key] && !catalog[key]);\n if (missing.length === 0) continue;\n\n if (options.dryRun) {\n result.pending[locale] = missing;\n continue;\n }\n\n for (let i = 0; i < missing.length; i += batchSize) {\n const keys = missing.slice(i, i + batchSize);\n const messages = Object.fromEntries(keys.map((key) => [key, source[key]!]));\n const out = await provider({\n sourceLocale: cfg.sourceLocale,\n targetLocale: locale,\n messages,\n });\n for (const key of keys) {\n const text = out[key];\n if (typeof text === 'string' && text.trim() && structureMatches(source[key]!, text)) {\n catalog[key] = text;\n (result.translated[locale] ??= []).push(key);\n } else {\n (result.invalid[locale] ??= []).push(key);\n }\n }\n }\n }\n return result;\n}\n\n// params and tags must survive translation verbatim\nexport function structureMatches(source: string, translated: string): boolean {\n return sameMembers(paramNames(source), paramNames(translated)) && sameMembers(tagTokens(source), tagTokens(translated));\n}\n\nfunction paramNames(message: string): string[] {\n try {\n return [...collectParams(message).keys()].sort();\n } catch {\n return ['\u0000invalid'];\n }\n}\n\nconst TAG = /<(\\/?)([a-zA-Z][\\w-]*)(\\/?)>/g;\n\nfunction tagTokens(message: string): string[] {\n const out: string[] = [];\n for (const match of message.matchAll(TAG)) {\n out.push(`${match[1]}${match[2]}${match[3]}`);\n }\n return out.sort();\n}\n\nfunction sameMembers(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((value, i) => value === b[i]);\n}\n"],"mappings":"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","names":["writeFileSync","join","writeFileSync","join","readFileSync","join","readFileSync","parse","parse","built","text","visit","readFileSync"]}
package/dist/index.d.ts CHANGED
@@ -33,6 +33,30 @@ interface Analysis {
33
33
  }
34
34
  declare function analyze(code: string, file: string): Analysis;
35
35
 
36
+ interface TranslateRequest {
37
+ sourceLocale: string;
38
+ targetLocale: string;
39
+ messages: Record<string, string>;
40
+ }
41
+ type TranslateProvider = (request: TranslateRequest) => Promise<Record<string, string>>;
42
+ interface TranslateOptions {
43
+ locales?: string[];
44
+ batchSize?: number;
45
+ dryRun?: boolean;
46
+ }
47
+ interface TranslateResult {
48
+ translated: Record<string, string[]>;
49
+ invalid: Record<string, string[]>;
50
+ pending: Record<string, string[]>;
51
+ }
52
+ declare function translateCatalogs(cfg: ResolvedConfig, catalogs: Catalogs, provider: TranslateProvider, options?: TranslateOptions): Promise<TranslateResult>;
53
+ declare function structureMatches(source: string, translated: string): boolean;
54
+
55
+ interface TranslateConfig {
56
+ provider?: 'claude' | TranslateProvider;
57
+ model?: string;
58
+ batchSize?: number;
59
+ }
36
60
  interface VerbalyConfig {
37
61
  root?: string;
38
62
  dir?: string;
@@ -40,6 +64,7 @@ interface VerbalyConfig {
40
64
  locales?: string[];
41
65
  include?: string[];
42
66
  exclude?: string[];
67
+ translate?: TranslateConfig;
43
68
  }
44
69
  interface ResolvedConfig {
45
70
  root: string;
@@ -48,6 +73,7 @@ interface ResolvedConfig {
48
73
  locales: string[];
49
74
  include: string[];
50
75
  exclude: string[];
76
+ translate: TranslateConfig;
51
77
  }
52
78
  declare function resolveConfig(config?: VerbalyConfig): ResolvedConfig;
53
79
  declare function loadConfigFile(root: string): Promise<VerbalyConfig>;
@@ -105,10 +131,17 @@ type ParamType = 'number' | 'string' | 'date' | 'unknown';
105
131
  declare function collectParams(message: string): Map<string, Set<ParamType>>;
106
132
  declare function renderParamType(types: Set<ParamType>): string;
107
133
 
134
+ interface ClaudeProviderOptions {
135
+ model?: string;
136
+ apiKey?: string;
137
+ maxTokens?: number;
138
+ }
139
+ declare function claudeProvider(options?: ClaudeProviderOptions): TranslateProvider;
140
+
108
141
  interface TransformResult {
109
142
  code: string;
110
143
  map: ReturnType<MagicString['generateMap']>;
111
144
  }
112
145
  declare function transformCode(code: string, file: string, analysis?: Analysis): TransformResult | null;
113
146
 
114
- export { type Analysis, type Catalog, type Catalogs, type CheckResult, MessageRegistry, type MissingEntry, type ParamType, type ResolvedConfig, type SyncResult, type TaggedMessage, type TaggedParam, type TransComponent, type TransformResult, type UnknownEntry, type UsedKey, VIRTUAL_ID, type VerbalyConfig, analyze, catalogPath, check, collectParams, extractProject, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, loadCatalogs, loadConfig, loadConfigFile, pruneCatalogs, readCatalog, renderParamType, resolveConfig, serializeCatalog, stableKey, syncCatalogs, transformCode, writeCatalog, writeDts };
147
+ export { type Analysis, type Catalog, type Catalogs, type CheckResult, type ClaudeProviderOptions, MessageRegistry, type MissingEntry, type ParamType, type ResolvedConfig, type SyncResult, type TaggedMessage, type TaggedParam, type TransComponent, type TransformResult, type TranslateConfig, type TranslateOptions, type TranslateProvider, type TranslateRequest, type TranslateResult, type UnknownEntry, type UsedKey, VIRTUAL_ID, type VerbalyConfig, analyze, catalogPath, check, claudeProvider, collectParams, extractProject, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, loadCatalogs, loadConfig, loadConfigFile, pruneCatalogs, readCatalog, renderParamType, resolveConfig, serializeCatalog, stableKey, structureMatches, syncCatalogs, transformCode, translateCatalogs, writeCatalog, writeDts };
package/dist/index.js CHANGED
@@ -471,7 +471,8 @@ function resolveConfig(config = {}) {
471
471
  sourceLocale,
472
472
  locales: [...locales],
473
473
  include: config.include ?? ["src/**/*.{js,jsx,ts,tsx,mjs,mts}"],
474
- exclude: config.exclude ?? ["**/node_modules/**", "**/dist/**"]
474
+ exclude: config.exclude ?? ["**/node_modules/**", "**/dist/**"],
475
+ translate: config.translate ?? {}
475
476
  };
476
477
  }
477
478
  async function loadConfigFile(root) {
@@ -614,6 +615,64 @@ function pruneCatalogs(cfg, catalogs, registry) {
614
615
  return removed;
615
616
  }
616
617
 
618
+ // src/providers/claude.ts
619
+ var DEFAULT_MODEL = "claude-sonnet-5";
620
+ var SYSTEM = `You translate UI strings for the Verbaly i18n library.
621
+ Rules:
622
+ - Translate only the human-readable text, naturally for the target locale.
623
+ - Preserve verbatim: placeholders like {name}, format specs like {price:currency/EUR}, variant blocks like {count | one: ... | other: # ...} (translate only the text inside each variant, keep keys and # as-is), ICU syntax, named tags like <em>...</em> and escapes {{ }} || ##.
624
+ - Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;
625
+ function claudeProvider(options = {}) {
626
+ return async (request) => {
627
+ const Anthropic = await loadSdk();
628
+ const client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});
629
+ const response = await client.messages.create({
630
+ model: options.model ?? DEFAULT_MODEL,
631
+ max_tokens: options.maxTokens ?? 16e3,
632
+ thinking: { type: "disabled" },
633
+ system: SYSTEM,
634
+ messages: [{ role: "user", content: buildPrompt(request) }],
635
+ output_config: { format: batchFormat(request) }
636
+ });
637
+ const text = response.content.find((block) => block.type === "text")?.text ?? "{}";
638
+ return JSON.parse(text);
639
+ };
640
+ }
641
+ function buildPrompt(request) {
642
+ return `Translate each value from "${request.sourceLocale}" to "${request.targetLocale}". Return a JSON object with the same keys and translated values.
643
+
644
+ ` + JSON.stringify(request.messages, null, 2);
645
+ }
646
+ function batchFormat(request) {
647
+ const keys = Object.keys(request.messages);
648
+ return {
649
+ type: "json_schema",
650
+ schema: {
651
+ type: "object",
652
+ properties: Object.fromEntries(keys.map((key) => [key, { type: "string" }])),
653
+ required: keys,
654
+ additionalProperties: false
655
+ }
656
+ };
657
+ }
658
+ async function loadSdk() {
659
+ try {
660
+ const mod = await import("@anthropic-ai/sdk");
661
+ return mod.default;
662
+ } catch (error) {
663
+ if (isModuleNotFound2(error, "@anthropic-ai/sdk")) {
664
+ throw new Error(
665
+ "[verbaly] the claude translate provider needs @anthropic-ai/sdk \u2014 install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY",
666
+ { cause: error }
667
+ );
668
+ }
669
+ throw error;
670
+ }
671
+ }
672
+ function isModuleNotFound2(error, name) {
673
+ return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
674
+ }
675
+
617
676
  // src/transform.ts
618
677
  import MagicString from "magic-string";
619
678
  function transformCode(code, file, analysis) {
@@ -640,12 +699,72 @@ function transformCode(code, file, analysis) {
640
699
  }
641
700
  return { code: s.toString(), map: s.generateMap({ hires: true }) };
642
701
  }
702
+
703
+ // src/translate.ts
704
+ async function translateCatalogs(cfg, catalogs, provider, options = {}) {
705
+ const batchSize = options.batchSize ?? 20;
706
+ const targets = (options.locales ?? cfg.locales).filter(
707
+ (locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale)
708
+ );
709
+ const source = catalogs[cfg.sourceLocale] ?? {};
710
+ const result = { translated: {}, invalid: {}, pending: {} };
711
+ for (const locale of targets) {
712
+ const catalog = catalogs[locale] ??= {};
713
+ const missing = Object.keys(source).filter((key) => source[key] && !catalog[key]);
714
+ if (missing.length === 0) continue;
715
+ if (options.dryRun) {
716
+ result.pending[locale] = missing;
717
+ continue;
718
+ }
719
+ for (let i = 0; i < missing.length; i += batchSize) {
720
+ const keys = missing.slice(i, i + batchSize);
721
+ const messages = Object.fromEntries(keys.map((key) => [key, source[key]]));
722
+ const out = await provider({
723
+ sourceLocale: cfg.sourceLocale,
724
+ targetLocale: locale,
725
+ messages
726
+ });
727
+ for (const key of keys) {
728
+ const text = out[key];
729
+ if (typeof text === "string" && text.trim() && structureMatches(source[key], text)) {
730
+ catalog[key] = text;
731
+ (result.translated[locale] ??= []).push(key);
732
+ } else {
733
+ (result.invalid[locale] ??= []).push(key);
734
+ }
735
+ }
736
+ }
737
+ }
738
+ return result;
739
+ }
740
+ function structureMatches(source, translated) {
741
+ return sameMembers(paramNames(source), paramNames(translated)) && sameMembers(tagTokens(source), tagTokens(translated));
742
+ }
743
+ function paramNames(message) {
744
+ try {
745
+ return [...collectParams(message).keys()].sort();
746
+ } catch {
747
+ return ["\0invalid"];
748
+ }
749
+ }
750
+ var TAG = /<(\/?)([a-zA-Z][\w-]*)(\/?)>/g;
751
+ function tagTokens(message) {
752
+ const out = [];
753
+ for (const match of message.matchAll(TAG)) {
754
+ out.push(`${match[1]}${match[2]}${match[3]}`);
755
+ }
756
+ return out.sort();
757
+ }
758
+ function sameMembers(a, b) {
759
+ return a.length === b.length && a.every((value, i) => value === b[i]);
760
+ }
643
761
  export {
644
762
  MessageRegistry,
645
763
  VIRTUAL_ID,
646
764
  analyze,
647
765
  catalogPath,
648
766
  check,
767
+ claudeProvider,
649
768
  collectParams,
650
769
  extractProject,
651
770
  formatCheckResult,
@@ -661,8 +780,10 @@ export {
661
780
  resolveConfig,
662
781
  serializeCatalog,
663
782
  stableKey,
783
+ structureMatches,
664
784
  syncCatalogs,
665
785
  transformCode,
786
+ translateCatalogs,
666
787
  writeCatalog,
667
788
  writeDts
668
789
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/analyze.ts","../src/key.ts","../src/catalog.ts","../src/check.ts","../src/codegen.ts","../src/params.ts","../src/config.ts","../src/extract.ts","../src/registry.ts","../src/transform.ts"],"sourcesContent":["import { parse } from '@babel/parser';\r\nimport { stableKey } from './key';\r\n\r\nexport interface TaggedParam {\r\n name: string;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nexport interface TransComponent {\r\n name: string;\r\n source: string;\r\n}\r\n\r\nexport interface TaggedMessage {\r\n key: string;\r\n message: string;\r\n params: TaggedParam[];\r\n start: number;\r\n end: number;\r\n tagStart: number;\r\n tagEnd: number;\r\n file: string;\r\n jsx?: { name: string; components: TransComponent[] };\r\n}\r\n\r\nexport interface UsedKey {\r\n key: string;\r\n file: string;\r\n}\r\n\r\nexport interface Analysis {\r\n tagged: TaggedMessage[];\r\n usedKeys: UsedKey[];\r\n}\r\n\r\ninterface AstNode {\r\n type: string;\r\n start: number;\r\n end: number;\r\n [key: string]: unknown;\r\n}\r\n\r\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\r\n\r\nexport function analyze(code: string, file: string): Analysis {\r\n const jsx = /\\.[jt]sx$/.test(file);\r\n const ast = parse(code, {\r\n sourceType: 'module',\r\n errorRecovery: true,\r\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\r\n });\r\n\r\n const tagged: TaggedMessage[] = [];\r\n const usedKeys: UsedKey[] = [];\r\n\r\n walk(ast.program as unknown as AstNode, (node) => {\r\n if (node.type === 'TaggedTemplateExpression') {\r\n const tag = node.tag as AstNode;\r\n const explicit = explicitId(tag);\r\n if (!explicit && !isTReference(tag)) return;\r\n const quasi = node.quasi as AstNode;\r\n const message = buildMessage(code, quasi);\r\n if (!message) return;\r\n tagged.push({\r\n key: explicit ? explicit.key : stableKey(message.text),\r\n message: message.text,\r\n params: message.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: explicit ? explicit.refStart : tag.start,\r\n tagEnd: explicit ? explicit.refEnd : tag.end,\r\n file,\r\n });\r\n } else if (node.type === 'CallExpression') {\r\n const callee = node.callee as AstNode;\r\n if (!isTReference(callee)) return;\r\n const args = node.arguments as AstNode[];\r\n const first = args[0];\r\n if (first?.type === 'StringLiteral') {\r\n usedKeys.push({ key: first.value as string, file });\r\n }\r\n } else if (node.type === 'JSXElement') {\r\n handleTrans(code, node, file, tagged, usedKeys);\r\n }\r\n });\r\n\r\n return { tagged, usedKeys };\r\n}\r\n\r\nfunction handleTrans(\r\n code: string,\r\n node: AstNode,\r\n file: string,\r\n tagged: TaggedMessage[],\r\n usedKeys: UsedKey[],\r\n): void {\r\n const opening = node.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name !== 'Trans') return;\r\n\r\n const attrs = opening.attributes as AstNode[];\r\n const idAttr = attrs.find((a) => a.type === 'JSXAttribute' && (a.name as AstNode).name === 'id');\r\n const children = node.children as AstNode[] | undefined;\r\n\r\n if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type !== 'StringLiteral') return;\r\n const id = value.value as string;\r\n // id + children → extract under the explicit key\r\n if (attrs.length === 1 && children?.length) {\r\n const built = buildTransMessage(code, children);\r\n if (built?.text.trim()) {\r\n tagged.push({\r\n key: id,\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n return;\r\n }\r\n }\r\n usedKeys.push({ key: id, file });\r\n return; // runtime-first, untouched\r\n }\r\n if (attrs.length > 0) return; // hand-written props → don't guess\r\n\r\n if (!children?.length) return;\r\n\r\n const built = buildTransMessage(code, children);\r\n if (!built || !built.text.trim()) return;\r\n\r\n tagged.push({\r\n key: stableKey(built.text),\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n}\r\n\r\n// t.id('key')`…` → explicit readable key\r\nfunction explicitId(tag: AstNode): { key: string; refStart: number; refEnd: number } | undefined {\r\n if (tag.type !== 'CallExpression') return undefined;\r\n const callee = tag.callee as AstNode;\r\n if (callee.type !== 'MemberExpression' || callee.computed) return undefined;\r\n const prop = callee.property as AstNode;\r\n if (prop.type !== 'Identifier' || prop.name !== 'id') return undefined;\r\n const obj = callee.object as AstNode;\r\n if (!isTReference(obj)) return undefined;\r\n const args = tag.arguments as AstNode[];\r\n const first = args[0];\r\n if (args.length !== 1 || first?.type !== 'StringLiteral') return undefined;\r\n return { key: first.value as string, refStart: obj.start, refEnd: obj.end };\r\n}\r\n\r\ninterface BuiltTrans {\r\n text: string;\r\n params: TaggedParam[];\r\n components: TransComponent[];\r\n}\r\n\r\nfunction buildTransMessage(code: string, children: AstNode[]): BuiltTrans | undefined {\r\n const params: TaggedParam[] = [];\r\n const components: TransComponent[] = [];\r\n const takenParams = new Map<string, string>();\r\n const takenTags = new Map<string, string>();\r\n\r\n function walkChildren(nodes: AstNode[]): string | undefined {\r\n let text = '';\r\n for (const child of nodes) {\r\n if (child.type === 'JSXText') {\r\n text += escapeText(cleanJsxText(child.value as string));\r\n } else if (child.type === 'JSXExpressionContainer') {\r\n const expr = child.expression as AstNode;\r\n if (expr.type === 'JSXEmptyExpression') continue;\r\n if (expr.type === 'StringLiteral') {\r\n text += escapeText(expr.value as string);\r\n continue;\r\n }\r\n if (expr.type === 'TaggedTemplateExpression') return undefined; // overlap hazard\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, params.length), source, takenParams);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}`;\r\n } else if (child.type === 'JSXElement') {\r\n const opening = child.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name === 'Trans') return undefined;\r\n const source = selfClosedSource(code, opening);\r\n const tag = uniqueName((nameNode.name as string).toLowerCase(), source, takenTags);\r\n if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });\r\n if (!child.closingElement) {\r\n text += `<${tag}/>`;\r\n } else {\r\n const inner = walkChildren(child.children as AstNode[]);\r\n if (inner === undefined) return undefined;\r\n text += `<${tag}>${inner}</${tag}>`;\r\n }\r\n } else {\r\n return undefined; // fragments / spread children → bail\r\n }\r\n }\r\n return text;\r\n }\r\n\r\n const text = walkChildren(children);\r\n if (text === undefined) return undefined;\r\n return { text, params, components };\r\n}\r\n\r\n// JSX text semantics: newline-indent boundaries removed, interior joins = one space\r\nfunction cleanJsxText(raw: string): string {\r\n const lines = raw.split(/\\r\\n|[\\r\\n]/);\r\n let out = '';\r\n for (let i = 0; i < lines.length; i++) {\r\n let line = lines[i]!.replace(/\\t/g, ' ');\r\n if (i !== 0) line = line.replace(/^ +/, '');\r\n if (i !== lines.length - 1) line = line.replace(/ +$/, '');\r\n if (!line) continue;\r\n if (out && i !== 0) out += ' ';\r\n out += line;\r\n }\r\n return out;\r\n}\r\n\r\nfunction selfClosedSource(code: string, opening: AstNode): string {\r\n const src = code.slice(opening.start, opening.end);\r\n return src.endsWith('/>') ? src : `${src.slice(0, -1).trimEnd()} />`;\r\n}\r\n\r\nfunction isTReference(node: AstNode): boolean {\r\n if (node.type === 'Identifier') return node.name === 't';\r\n if (node.type === 'MemberExpression' && !node.computed) {\r\n const prop = node.property as AstNode;\r\n return prop.type === 'Identifier' && prop.name === 't';\r\n }\r\n return false;\r\n}\r\n\r\ninterface BuiltMessage {\r\n text: string;\r\n params: TaggedParam[];\r\n}\r\n\r\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\r\n const quasis = quasi.quasis as AstNode[];\r\n const expressions = quasi.expressions as AstNode[];\r\n const params: TaggedParam[] = [];\r\n const taken = new Map<string, string>();\r\n\r\n let text = escapeText(cookedValue(quasis[0]));\r\n\r\n for (let i = 0; i < expressions.length; i++) {\r\n const expr = expressions[i];\r\n if (!expr) return undefined;\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, i), source, taken);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\r\n }\r\n return { text, params };\r\n}\r\n\r\nfunction cookedValue(element: AstNode | undefined): string {\r\n if (!element) return '';\r\n const value = element.value as { cooked?: string; raw: string };\r\n return value.cooked ?? value.raw;\r\n}\r\n\r\n// literal braces → escaped\r\nfunction escapeText(text: string): string {\r\n return text.replace(/[{}]/g, (m) => m + m);\r\n}\r\n\r\nfunction deriveName(expr: AstNode, index: number): string {\r\n if (expr.type === 'Identifier') return expr.name as string;\r\n if (expr.type === 'MemberExpression' && !expr.computed) {\r\n const prop = expr.property as AstNode;\r\n if (prop.type === 'Identifier') return prop.name as string;\r\n }\r\n return `_${index}`;\r\n}\r\n\r\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\r\n let name = base;\r\n let n = 2;\r\n while (taken.has(name) && taken.get(name) !== source) {\r\n name = `${base}${n}`;\r\n n += 1;\r\n }\r\n taken.set(name, source);\r\n return name;\r\n}\r\n\r\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\r\n visit(node);\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n if (isNode(item)) walk(item, visit);\r\n }\r\n } else if (isNode(value)) {\r\n walk(value, visit);\r\n }\r\n }\r\n}\r\n\r\nfunction isNode(value: unknown): value is AstNode {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as { type?: unknown }).type === 'string'\r\n );\r\n}\r\n","import { createHash } from 'node:crypto';\n\nexport function stableKey(message: string): string {\n return createHash('sha256').update(message).digest('base64url').slice(0, 8);\n}\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from './config';\n\nexport type Catalog = Record<string, string>;\nexport type Catalogs = Record<string, Catalog>;\n\nexport function catalogPath(cfg: ResolvedConfig, locale: string): string {\n return join(cfg.dir, `${locale}.json`);\n}\n\nexport function readCatalog(cfg: ResolvedConfig, locale: string): Catalog {\n try {\n return JSON.parse(readFileSync(catalogPath(cfg, locale), 'utf8')) as Catalog;\n } catch {\n return {};\n }\n}\n\nexport function loadCatalogs(cfg: ResolvedConfig): Catalogs {\n const catalogs: Catalogs = {};\n for (const locale of cfg.locales) catalogs[locale] = readCatalog(cfg, locale);\n return catalogs;\n}\n\nexport function serializeCatalog(catalog: Catalog): string {\n const sorted: Catalog = {};\n for (const key of Object.keys(catalog).sort()) {\n const value = catalog[key];\n if (value !== undefined) sorted[key] = value;\n }\n return JSON.stringify(sorted, null, 2) + '\\n';\n}\n\nexport function writeCatalog(cfg: ResolvedConfig, locale: string, catalog: Catalog): string {\n const serialized = serializeCatalog(catalog);\n mkdirSync(cfg.dir, { recursive: true });\n writeFileSync(catalogPath(cfg, locale), serialized);\n return serialized;\n}\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport type { MessageRegistry } from './registry';\n\nexport interface MissingEntry {\n locale: string;\n key: string;\n source?: string;\n}\n\nexport interface UnknownEntry {\n key: string;\n files: string[];\n}\n\nexport interface CheckResult {\n ok: boolean;\n missing: MissingEntry[];\n unknown: UnknownEntry[];\n}\n\nexport function check(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): CheckResult {\n const source = catalogs[cfg.sourceLocale] ?? {};\n const extracted = registry.messages();\n\n const unknown: UnknownEntry[] = [];\n for (const [key, files] of registry.usedKeys()) {\n const known =\n extracted.has(key) || cfg.locales.some((locale) => catalogs[locale]?.[key] !== undefined);\n if (!known) unknown.push({ key, files });\n }\n\n const needed = new Set<string>([...extracted.keys(), ...Object.keys(source)]);\n\n const missing: MissingEntry[] = [];\n for (const key of extracted.keys()) {\n if (!source[key]) {\n missing.push({ locale: cfg.sourceLocale, key, source: extracted.get(key)?.message });\n }\n }\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n for (const key of needed) {\n if (!catalogs[locale]?.[key]) {\n missing.push({ locale, key, source: source[key] ?? extracted.get(key)?.message });\n }\n }\n }\n\n return { ok: missing.length === 0 && unknown.length === 0, missing, unknown };\n}\n\nexport function formatCheckResult(result: CheckResult): string {\n const lines: string[] = [];\n if (result.missing.length > 0) {\n lines.push('missing translations:');\n for (const entry of result.missing) {\n const hint = entry.source ? ` — \"${truncate(entry.source, 40)}\"` : '';\n lines.push(` [${entry.locale}] ${entry.key}${hint}`);\n }\n }\n if (result.unknown.length > 0) {\n lines.push('unknown keys (not in any catalog):');\n for (const entry of result.unknown) {\n lines.push(` ${entry.key} — used in ${entry.files.join(', ')}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? text.slice(0, max - 1) + '…' : text;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Catalog } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams, renderParamType } from './params';\n\nexport const VIRTUAL_ID = 'virtual:verbaly';\n\nexport function generateRuntimeModule(cfg: ResolvedConfig): string {\n const others = cfg.locales.filter((locale) => locale !== cfg.sourceLocale);\n const src = JSON.stringify(cfg.sourceLocale);\n const loaders = others\n .map(\n (locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`,\n )\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\n loaders: {\n${loaders}\n },\n});\n\nexport const verbaly = v;\nexport const t = v.t;\n\nexport function getLocale() {\n return v.locale;\n}\n\nexport function subscribe(listener) {\n return v.subscribe(listener);\n}\n\nexport async function setLocale(locale) {\n await v.loadLocale(locale);\n v.setLocale(locale);\n}\n`;\n}\n\nexport function generateLocaleModule(catalog: Catalog): string {\n return `export default ${JSON.stringify(catalog)};\\n`;\n}\n\nexport function generateDts(entries: Map<string, string>): string {\n const lines: string[] = [];\n for (const [key, message] of [...entries.entries()].sort(([a], [b]) => a.localeCompare(b))) {\n const params = collectParams(message);\n if (params.size === 0) {\n lines.push(` ${JSON.stringify(key)}: never;`);\n } else {\n const fields = [...params.entries()]\n .map(([name, types]) => `${JSON.stringify(name)}: ${renderParamType(types)}`)\n .join('; ');\n lines.push(` ${JSON.stringify(key)}: { ${fields} };`);\n }\n }\n\n return `// generated by verbaly — do not edit\ndeclare module 'virtual:verbaly' {\n export interface VerbalyMessages {\n${lines.join('\\n')}\n }\n export type VerbalyKey = keyof VerbalyMessages & string;\n\n export const verbaly: import('verbaly').Verbaly<VerbalyKey>;\n\n export function t<K extends VerbalyKey>(\n key: K,\n ...args: [VerbalyMessages[K]] extends [never] ? [] : [VerbalyMessages[K]]\n ): string;\n export function t(strings: TemplateStringsArray, ...values: unknown[]): string;\n export namespace t {\n export function id(\n key: string,\n ): (strings: TemplateStringsArray, ...values: unknown[]) => string;\n }\n\n export function setLocale(locale: string): Promise<void>;\n export function getLocale(): string;\n export function subscribe(listener: () => void): () => void;\n}\n\ndeclare module 'virtual:verbaly/locale/*' {\n const messages: Record<string, string>;\n export default messages;\n}\n`;\n}\n\nexport function writeDts(cfg: ResolvedConfig, entries: Map<string, string>): void {\n writeFileSync(join(cfg.root, 'verbaly.d.ts'), generateDts(entries));\n}\n","import { parse, type MessageNode } from 'verbaly';\n\nexport type ParamType = 'number' | 'string' | 'date' | 'unknown';\n\nconst PLURAL_KEYS = new Set(['zero', 'one', 'two', 'few', 'many']);\nconst NUMBER_FORMATS = new Set(['number', 'integer', 'percent', 'currency']);\nconst DATE_FORMATS = new Set(['date', 'time']);\n\nexport function collectParams(message: string): Map<string, Set<ParamType>> {\n const out = new Map<string, Set<ParamType>>();\n visit(parse(message), out);\n return out;\n}\n\nfunction visit(nodes: MessageNode[], out: Map<string, Set<ParamType>>): void {\n for (const node of nodes) {\n if (node.kind !== 'param') continue;\n const types = out.get(node.name) ?? new Set<ParamType>();\n out.set(node.name, types);\n\n if (node.variants) {\n for (const [key, body] of node.variants) {\n if (PLURAL_KEYS.has(key) || /^=\\d+$/.test(key)) types.add('number');\n else if (key !== 'other') types.add('string');\n visit(body, out);\n }\n if (types.size === 0) types.add('string');\n } else if (node.format && NUMBER_FORMATS.has(node.format)) {\n types.add('number');\n } else if (node.format && DATE_FORMATS.has(node.format)) {\n types.add('date');\n } else {\n types.add('unknown');\n }\n }\n}\n\nexport function renderParamType(types: Set<ParamType>): string {\n if (types.has('unknown') || types.size === 0) return 'unknown';\n const parts: string[] = [];\n if (types.has('number')) parts.push('number');\n if (types.has('string')) parts.push('string');\n if (types.has('date')) parts.push('Date | number | string');\n return parts.join(' | ');\n}\n","import { existsSync, readFileSync, readdirSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\n\r\nexport interface VerbalyConfig {\r\n root?: string;\r\n dir?: string;\r\n sourceLocale?: string;\r\n locales?: string[];\r\n include?: string[];\r\n exclude?: string[];\r\n}\r\n\r\nexport interface ResolvedConfig {\r\n root: string;\r\n dir: string;\r\n sourceLocale: string;\r\n locales: string[];\r\n include: string[];\r\n exclude: string[];\r\n}\r\n\r\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\r\n const root = resolve(config.root ?? process.cwd());\r\n const dir = resolve(root, config.dir ?? 'locales');\r\n const sourceLocale = config.sourceLocale ?? 'en';\r\n\r\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\r\n if (existsSync(dir)) {\r\n for (const file of readdirSync(dir)) {\r\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\r\n }\r\n }\r\n\r\n return {\r\n root,\r\n dir,\r\n sourceLocale,\r\n locales: [...locales],\r\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\r\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\r\n };\r\n}\r\n\r\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\r\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) {\r\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\r\n return mod.default ?? {};\r\n }\r\n }\r\n for (const name of ['verbaly.config.ts', 'verbaly.config.mts']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) return loadTsConfig(path);\r\n }\r\n const jsonPath = join(root, 'verbaly.config.json');\r\n if (existsSync(jsonPath)) {\r\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\r\n }\r\n return {};\r\n}\r\n\r\nasync function loadTsConfig(path: string): Promise<VerbalyConfig> {\r\n try {\r\n const { bundleRequire } = await import('bundle-require');\r\n const { mod } = await bundleRequire<{ default?: VerbalyConfig }>({ filepath: path });\r\n return mod.default ?? {};\r\n } catch (error) {\r\n if (isModuleNotFound(error, 'esbuild')) {\r\n throw new Error(`[verbaly] ${path} needs esbuild — install it: pnpm add -D esbuild`, {\r\n cause: error,\r\n });\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n\r\n// file config + inline overrides\r\nexport async function loadConfig(\r\n root: string,\r\n overrides: VerbalyConfig = {},\r\n): Promise<ResolvedConfig> {\r\n const fileConfig = await loadConfigFile(root);\r\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\r\n}\r\n\r\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\r\n return Object.fromEntries(\r\n Object.entries(config).filter(([, value]) => value !== undefined),\r\n ) as VerbalyConfig;\r\n}\r\n","import { readFileSync } from 'node:fs';\nimport { glob } from 'tinyglobby';\nimport { analyze } from './analyze';\nimport type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { MessageRegistry } from './registry';\n\nexport async function extractProject(cfg: ResolvedConfig): Promise<MessageRegistry> {\n const files = await glob(cfg.include, {\n cwd: cfg.root,\n ignore: cfg.exclude,\n absolute: true,\n });\n const registry = new MessageRegistry();\n for (const file of files) {\n registry.update(file, analyze(readFileSync(file, 'utf8'), file));\n }\n return registry;\n}\n\nexport interface SyncResult {\n added: Record<string, string[]>;\n}\n\n// fills source texts + '' placeholders\nexport function syncCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): SyncResult {\n const added: Record<string, string[]> = {};\n const source = (catalogs[cfg.sourceLocale] ??= {});\n\n for (const [key, msg] of registry.messages()) {\n if (source[key] !== msg.message) {\n source[key] = msg.message;\n (added[cfg.sourceLocale] ??= []).push(key);\n }\n }\n\n const needed = Object.keys(source);\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n const catalog = (catalogs[locale] ??= {});\n for (const key of needed) {\n if (catalog[key] === undefined) {\n catalog[key] = '';\n (added[locale] ??= []).push(key);\n }\n }\n }\n return { added };\n}\n\n// drops keys no longer referenced\nexport function pruneCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): Record<string, string[]> {\n const keep = new Set([...registry.messages().keys(), ...registry.usedKeys().keys()]);\n const removed: Record<string, string[]> = {};\n for (const locale of cfg.locales) {\n const catalog = catalogs[locale];\n if (!catalog) continue;\n for (const key of Object.keys(catalog)) {\n if (!keep.has(key)) {\n delete catalog[key];\n (removed[locale] ??= []).push(key);\n }\n }\n }\n return removed;\n}\n","import type { Analysis, TaggedMessage } from './analyze';\n\nexport class MessageRegistry {\n private files = new Map<string, Analysis>();\n\n update(file: string, analysis: Analysis): void {\n this.files.set(file, analysis);\n }\n\n remove(file: string): void {\n this.files.delete(file);\n }\n\n messages(): Map<string, TaggedMessage> {\n const out = new Map<string, TaggedMessage>();\n for (const analysis of this.files.values()) {\n for (const msg of analysis.tagged) {\n const existing = out.get(msg.key);\n if (existing) {\n if (existing.message !== msg.message) {\n console.warn(\n `[verbaly] key collision \"${msg.key}\": ` +\n `${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)} — second dropped.`,\n );\n }\n continue;\n }\n out.set(msg.key, msg);\n }\n }\n return out;\n }\n\n usedKeys(): Map<string, string[]> {\n const out = new Map<string, string[]>();\n for (const analysis of this.files.values()) {\n for (const used of analysis.usedKeys) {\n const files = out.get(used.key) ?? [];\n if (!files.includes(used.file)) files.push(used.file);\n out.set(used.key, files);\n }\n }\n return out;\n }\n}\n","import MagicString from 'magic-string';\r\nimport { analyze, type Analysis } from './analyze';\r\n\r\nexport interface TransformResult {\r\n code: string;\r\n map: ReturnType<MagicString['generateMap']>;\r\n}\r\n\r\nexport function transformCode(\r\n code: string,\r\n file: string,\r\n analysis?: Analysis,\r\n): TransformResult | null {\r\n const { tagged } = analysis ?? analyze(code, file);\r\n if (tagged.length === 0) return null;\r\n\r\n const s = new MagicString(code);\r\n for (const msg of tagged) {\r\n const seen = new Set<string>();\r\n const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));\r\n const pairs = entries\r\n .map((p) => `${JSON.stringify(p.name)}: ${code.slice(p.start, p.end)}`)\r\n .join(', ');\r\n\r\n if (msg.jsx) {\r\n const values = entries.length ? ` values={{ ${pairs} }}` : '';\r\n const components = msg.jsx.components.length\r\n ? ` components={{ ${msg.jsx.components\r\n .map((c) => `${JSON.stringify(c.name)}: ${c.source}`)\r\n .join(', ')} }}`\r\n : '';\r\n s.overwrite(\r\n msg.start,\r\n msg.end,\r\n `<${msg.jsx.name} id=${JSON.stringify(msg.key)}${values}${components} />`,\r\n );\r\n continue;\r\n }\r\n\r\n const tagSource = code.slice(msg.tagStart, msg.tagEnd);\r\n const args = entries.length ? `, { ${pairs} }` : '';\r\n s.overwrite(msg.start, msg.end, `${tagSource}(${JSON.stringify(msg.key)}${args})`);\r\n }\r\n return { code: s.toString(), map: s.generateMap({ hires: true }) };\r\n}\r\n"],"mappings":"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","names":["built","text","visit","writeFileSync","join","parse","writeFileSync","join","readFileSync","join","readFileSync","readFileSync"]}
1
+ {"version":3,"sources":["../src/analyze.ts","../src/key.ts","../src/catalog.ts","../src/check.ts","../src/codegen.ts","../src/params.ts","../src/config.ts","../src/extract.ts","../src/registry.ts","../src/providers/claude.ts","../src/transform.ts","../src/translate.ts"],"sourcesContent":["import { parse } from '@babel/parser';\r\nimport { stableKey } from './key';\r\n\r\nexport interface TaggedParam {\r\n name: string;\r\n start: number;\r\n end: number;\r\n}\r\n\r\nexport interface TransComponent {\r\n name: string;\r\n source: string;\r\n}\r\n\r\nexport interface TaggedMessage {\r\n key: string;\r\n message: string;\r\n params: TaggedParam[];\r\n start: number;\r\n end: number;\r\n tagStart: number;\r\n tagEnd: number;\r\n file: string;\r\n jsx?: { name: string; components: TransComponent[] };\r\n}\r\n\r\nexport interface UsedKey {\r\n key: string;\r\n file: string;\r\n}\r\n\r\nexport interface Analysis {\r\n tagged: TaggedMessage[];\r\n usedKeys: UsedKey[];\r\n}\r\n\r\ninterface AstNode {\r\n type: string;\r\n start: number;\r\n end: number;\r\n [key: string]: unknown;\r\n}\r\n\r\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\r\n\r\nexport function analyze(code: string, file: string): Analysis {\r\n const jsx = /\\.[jt]sx$/.test(file);\r\n const ast = parse(code, {\r\n sourceType: 'module',\r\n errorRecovery: true,\r\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\r\n });\r\n\r\n const tagged: TaggedMessage[] = [];\r\n const usedKeys: UsedKey[] = [];\r\n\r\n walk(ast.program as unknown as AstNode, (node) => {\r\n if (node.type === 'TaggedTemplateExpression') {\r\n const tag = node.tag as AstNode;\r\n const explicit = explicitId(tag);\r\n if (!explicit && !isTReference(tag)) return;\r\n const quasi = node.quasi as AstNode;\r\n const message = buildMessage(code, quasi);\r\n if (!message) return;\r\n tagged.push({\r\n key: explicit ? explicit.key : stableKey(message.text),\r\n message: message.text,\r\n params: message.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: explicit ? explicit.refStart : tag.start,\r\n tagEnd: explicit ? explicit.refEnd : tag.end,\r\n file,\r\n });\r\n } else if (node.type === 'CallExpression') {\r\n const callee = node.callee as AstNode;\r\n if (!isTReference(callee)) return;\r\n const args = node.arguments as AstNode[];\r\n const first = args[0];\r\n if (first?.type === 'StringLiteral') {\r\n usedKeys.push({ key: first.value as string, file });\r\n }\r\n } else if (node.type === 'JSXElement') {\r\n handleTrans(code, node, file, tagged, usedKeys);\r\n }\r\n });\r\n\r\n return { tagged, usedKeys };\r\n}\r\n\r\nfunction handleTrans(\r\n code: string,\r\n node: AstNode,\r\n file: string,\r\n tagged: TaggedMessage[],\r\n usedKeys: UsedKey[],\r\n): void {\r\n const opening = node.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name !== 'Trans') return;\r\n\r\n const attrs = opening.attributes as AstNode[];\r\n const idAttr = attrs.find((a) => a.type === 'JSXAttribute' && (a.name as AstNode).name === 'id');\r\n const children = node.children as AstNode[] | undefined;\r\n\r\n if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type !== 'StringLiteral') return;\r\n const id = value.value as string;\r\n // id + children → extract under the explicit key\r\n if (attrs.length === 1 && children?.length) {\r\n const built = buildTransMessage(code, children);\r\n if (built?.text.trim()) {\r\n tagged.push({\r\n key: id,\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n return;\r\n }\r\n }\r\n usedKeys.push({ key: id, file });\r\n return; // runtime-first, untouched\r\n }\r\n if (attrs.length > 0) return; // hand-written props → don't guess\r\n\r\n if (!children?.length) return;\r\n\r\n const built = buildTransMessage(code, children);\r\n if (!built || !built.text.trim()) return;\r\n\r\n tagged.push({\r\n key: stableKey(built.text),\r\n message: built.text,\r\n params: built.params,\r\n start: node.start,\r\n end: node.end,\r\n tagStart: nameNode.start,\r\n tagEnd: nameNode.end,\r\n file,\r\n jsx: { name: nameNode.name as string, components: built.components },\r\n });\r\n}\r\n\r\n// t.id('key')`…` → explicit readable key\r\nfunction explicitId(tag: AstNode): { key: string; refStart: number; refEnd: number } | undefined {\r\n if (tag.type !== 'CallExpression') return undefined;\r\n const callee = tag.callee as AstNode;\r\n if (callee.type !== 'MemberExpression' || callee.computed) return undefined;\r\n const prop = callee.property as AstNode;\r\n if (prop.type !== 'Identifier' || prop.name !== 'id') return undefined;\r\n const obj = callee.object as AstNode;\r\n if (!isTReference(obj)) return undefined;\r\n const args = tag.arguments as AstNode[];\r\n const first = args[0];\r\n if (args.length !== 1 || first?.type !== 'StringLiteral') return undefined;\r\n return { key: first.value as string, refStart: obj.start, refEnd: obj.end };\r\n}\r\n\r\ninterface BuiltTrans {\r\n text: string;\r\n params: TaggedParam[];\r\n components: TransComponent[];\r\n}\r\n\r\nfunction buildTransMessage(code: string, children: AstNode[]): BuiltTrans | undefined {\r\n const params: TaggedParam[] = [];\r\n const components: TransComponent[] = [];\r\n const takenParams = new Map<string, string>();\r\n const takenTags = new Map<string, string>();\r\n\r\n function walkChildren(nodes: AstNode[]): string | undefined {\r\n let text = '';\r\n for (const child of nodes) {\r\n if (child.type === 'JSXText') {\r\n text += escapeText(cleanJsxText(child.value as string));\r\n } else if (child.type === 'JSXExpressionContainer') {\r\n const expr = child.expression as AstNode;\r\n if (expr.type === 'JSXEmptyExpression') continue;\r\n if (expr.type === 'StringLiteral') {\r\n text += escapeText(expr.value as string);\r\n continue;\r\n }\r\n if (expr.type === 'TaggedTemplateExpression') return undefined; // overlap hazard\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, params.length), source, takenParams);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}`;\r\n } else if (child.type === 'JSXElement') {\r\n const opening = child.openingElement as AstNode;\r\n const nameNode = opening.name as AstNode;\r\n if (nameNode.type !== 'JSXIdentifier' || nameNode.name === 'Trans') return undefined;\r\n const source = selfClosedSource(code, opening);\r\n const tag = uniqueName((nameNode.name as string).toLowerCase(), source, takenTags);\r\n if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });\r\n if (!child.closingElement) {\r\n text += `<${tag}/>`;\r\n } else {\r\n const inner = walkChildren(child.children as AstNode[]);\r\n if (inner === undefined) return undefined;\r\n text += `<${tag}>${inner}</${tag}>`;\r\n }\r\n } else {\r\n return undefined; // fragments / spread children → bail\r\n }\r\n }\r\n return text;\r\n }\r\n\r\n const text = walkChildren(children);\r\n if (text === undefined) return undefined;\r\n return { text, params, components };\r\n}\r\n\r\n// JSX text semantics: newline-indent boundaries removed, interior joins = one space\r\nfunction cleanJsxText(raw: string): string {\r\n const lines = raw.split(/\\r\\n|[\\r\\n]/);\r\n let out = '';\r\n for (let i = 0; i < lines.length; i++) {\r\n let line = lines[i]!.replace(/\\t/g, ' ');\r\n if (i !== 0) line = line.replace(/^ +/, '');\r\n if (i !== lines.length - 1) line = line.replace(/ +$/, '');\r\n if (!line) continue;\r\n if (out && i !== 0) out += ' ';\r\n out += line;\r\n }\r\n return out;\r\n}\r\n\r\nfunction selfClosedSource(code: string, opening: AstNode): string {\r\n const src = code.slice(opening.start, opening.end);\r\n return src.endsWith('/>') ? src : `${src.slice(0, -1).trimEnd()} />`;\r\n}\r\n\r\nfunction isTReference(node: AstNode): boolean {\r\n if (node.type === 'Identifier') return node.name === 't';\r\n if (node.type === 'MemberExpression' && !node.computed) {\r\n const prop = node.property as AstNode;\r\n return prop.type === 'Identifier' && prop.name === 't';\r\n }\r\n return false;\r\n}\r\n\r\ninterface BuiltMessage {\r\n text: string;\r\n params: TaggedParam[];\r\n}\r\n\r\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\r\n const quasis = quasi.quasis as AstNode[];\r\n const expressions = quasi.expressions as AstNode[];\r\n const params: TaggedParam[] = [];\r\n const taken = new Map<string, string>();\r\n\r\n let text = escapeText(cookedValue(quasis[0]));\r\n\r\n for (let i = 0; i < expressions.length; i++) {\r\n const expr = expressions[i];\r\n if (!expr) return undefined;\r\n const source = code.slice(expr.start, expr.end);\r\n const name = uniqueName(deriveName(expr, i), source, taken);\r\n params.push({ name, start: expr.start, end: expr.end });\r\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\r\n }\r\n return { text, params };\r\n}\r\n\r\nfunction cookedValue(element: AstNode | undefined): string {\r\n if (!element) return '';\r\n const value = element.value as { cooked?: string; raw: string };\r\n return value.cooked ?? value.raw;\r\n}\r\n\r\n// literal braces → escaped\r\nfunction escapeText(text: string): string {\r\n return text.replace(/[{}]/g, (m) => m + m);\r\n}\r\n\r\nfunction deriveName(expr: AstNode, index: number): string {\r\n if (expr.type === 'Identifier') return expr.name as string;\r\n if (expr.type === 'MemberExpression' && !expr.computed) {\r\n const prop = expr.property as AstNode;\r\n if (prop.type === 'Identifier') return prop.name as string;\r\n }\r\n return `_${index}`;\r\n}\r\n\r\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\r\n let name = base;\r\n let n = 2;\r\n while (taken.has(name) && taken.get(name) !== source) {\r\n name = `${base}${n}`;\r\n n += 1;\r\n }\r\n taken.set(name, source);\r\n return name;\r\n}\r\n\r\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\r\n visit(node);\r\n for (const [key, value] of Object.entries(node)) {\r\n if (SKIP_KEYS.has(key)) continue;\r\n if (Array.isArray(value)) {\r\n for (const item of value) {\r\n if (isNode(item)) walk(item, visit);\r\n }\r\n } else if (isNode(value)) {\r\n walk(value, visit);\r\n }\r\n }\r\n}\r\n\r\nfunction isNode(value: unknown): value is AstNode {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as { type?: unknown }).type === 'string'\r\n );\r\n}\r\n","import { createHash } from 'node:crypto';\n\nexport function stableKey(message: string): string {\n return createHash('sha256').update(message).digest('base64url').slice(0, 8);\n}\n","import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ResolvedConfig } from './config';\n\nexport type Catalog = Record<string, string>;\nexport type Catalogs = Record<string, Catalog>;\n\nexport function catalogPath(cfg: ResolvedConfig, locale: string): string {\n return join(cfg.dir, `${locale}.json`);\n}\n\nexport function readCatalog(cfg: ResolvedConfig, locale: string): Catalog {\n try {\n return JSON.parse(readFileSync(catalogPath(cfg, locale), 'utf8')) as Catalog;\n } catch {\n return {};\n }\n}\n\nexport function loadCatalogs(cfg: ResolvedConfig): Catalogs {\n const catalogs: Catalogs = {};\n for (const locale of cfg.locales) catalogs[locale] = readCatalog(cfg, locale);\n return catalogs;\n}\n\nexport function serializeCatalog(catalog: Catalog): string {\n const sorted: Catalog = {};\n for (const key of Object.keys(catalog).sort()) {\n const value = catalog[key];\n if (value !== undefined) sorted[key] = value;\n }\n return JSON.stringify(sorted, null, 2) + '\\n';\n}\n\nexport function writeCatalog(cfg: ResolvedConfig, locale: string, catalog: Catalog): string {\n const serialized = serializeCatalog(catalog);\n mkdirSync(cfg.dir, { recursive: true });\n writeFileSync(catalogPath(cfg, locale), serialized);\n return serialized;\n}\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport type { MessageRegistry } from './registry';\n\nexport interface MissingEntry {\n locale: string;\n key: string;\n source?: string;\n}\n\nexport interface UnknownEntry {\n key: string;\n files: string[];\n}\n\nexport interface CheckResult {\n ok: boolean;\n missing: MissingEntry[];\n unknown: UnknownEntry[];\n}\n\nexport function check(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): CheckResult {\n const source = catalogs[cfg.sourceLocale] ?? {};\n const extracted = registry.messages();\n\n const unknown: UnknownEntry[] = [];\n for (const [key, files] of registry.usedKeys()) {\n const known =\n extracted.has(key) || cfg.locales.some((locale) => catalogs[locale]?.[key] !== undefined);\n if (!known) unknown.push({ key, files });\n }\n\n const needed = new Set<string>([...extracted.keys(), ...Object.keys(source)]);\n\n const missing: MissingEntry[] = [];\n for (const key of extracted.keys()) {\n if (!source[key]) {\n missing.push({ locale: cfg.sourceLocale, key, source: extracted.get(key)?.message });\n }\n }\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n for (const key of needed) {\n if (!catalogs[locale]?.[key]) {\n missing.push({ locale, key, source: source[key] ?? extracted.get(key)?.message });\n }\n }\n }\n\n return { ok: missing.length === 0 && unknown.length === 0, missing, unknown };\n}\n\nexport function formatCheckResult(result: CheckResult): string {\n const lines: string[] = [];\n if (result.missing.length > 0) {\n lines.push('missing translations:');\n for (const entry of result.missing) {\n const hint = entry.source ? ` — \"${truncate(entry.source, 40)}\"` : '';\n lines.push(` [${entry.locale}] ${entry.key}${hint}`);\n }\n }\n if (result.unknown.length > 0) {\n lines.push('unknown keys (not in any catalog):');\n for (const entry of result.unknown) {\n lines.push(` ${entry.key} — used in ${entry.files.join(', ')}`);\n }\n }\n return lines.join('\\n');\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length > max ? text.slice(0, max - 1) + '…' : text;\n}\n","import { writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Catalog } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams, renderParamType } from './params';\n\nexport const VIRTUAL_ID = 'virtual:verbaly';\n\nexport function generateRuntimeModule(cfg: ResolvedConfig): string {\n const others = cfg.locales.filter((locale) => locale !== cfg.sourceLocale);\n const src = JSON.stringify(cfg.sourceLocale);\n const loaders = others\n .map(\n (locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`,\n )\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\n loaders: {\n${loaders}\n },\n});\n\nexport const verbaly = v;\nexport const t = v.t;\n\nexport function getLocale() {\n return v.locale;\n}\n\nexport function subscribe(listener) {\n return v.subscribe(listener);\n}\n\nexport async function setLocale(locale) {\n await v.loadLocale(locale);\n v.setLocale(locale);\n}\n`;\n}\n\nexport function generateLocaleModule(catalog: Catalog): string {\n return `export default ${JSON.stringify(catalog)};\\n`;\n}\n\nexport function generateDts(entries: Map<string, string>): string {\n const lines: string[] = [];\n for (const [key, message] of [...entries.entries()].sort(([a], [b]) => a.localeCompare(b))) {\n const params = collectParams(message);\n if (params.size === 0) {\n lines.push(` ${JSON.stringify(key)}: never;`);\n } else {\n const fields = [...params.entries()]\n .map(([name, types]) => `${JSON.stringify(name)}: ${renderParamType(types)}`)\n .join('; ');\n lines.push(` ${JSON.stringify(key)}: { ${fields} };`);\n }\n }\n\n return `// generated by verbaly — do not edit\ndeclare module 'virtual:verbaly' {\n export interface VerbalyMessages {\n${lines.join('\\n')}\n }\n export type VerbalyKey = keyof VerbalyMessages & string;\n\n export const verbaly: import('verbaly').Verbaly<VerbalyKey>;\n\n export function t<K extends VerbalyKey>(\n key: K,\n ...args: [VerbalyMessages[K]] extends [never] ? [] : [VerbalyMessages[K]]\n ): string;\n export function t(strings: TemplateStringsArray, ...values: unknown[]): string;\n export namespace t {\n export function id(\n key: string,\n ): (strings: TemplateStringsArray, ...values: unknown[]) => string;\n }\n\n export function setLocale(locale: string): Promise<void>;\n export function getLocale(): string;\n export function subscribe(listener: () => void): () => void;\n}\n\ndeclare module 'virtual:verbaly/locale/*' {\n const messages: Record<string, string>;\n export default messages;\n}\n`;\n}\n\nexport function writeDts(cfg: ResolvedConfig, entries: Map<string, string>): void {\n writeFileSync(join(cfg.root, 'verbaly.d.ts'), generateDts(entries));\n}\n","import { parse, type MessageNode } from 'verbaly';\n\nexport type ParamType = 'number' | 'string' | 'date' | 'unknown';\n\nconst PLURAL_KEYS = new Set(['zero', 'one', 'two', 'few', 'many']);\nconst NUMBER_FORMATS = new Set(['number', 'integer', 'percent', 'currency']);\nconst DATE_FORMATS = new Set(['date', 'time']);\n\nexport function collectParams(message: string): Map<string, Set<ParamType>> {\n const out = new Map<string, Set<ParamType>>();\n visit(parse(message), out);\n return out;\n}\n\nfunction visit(nodes: MessageNode[], out: Map<string, Set<ParamType>>): void {\n for (const node of nodes) {\n if (node.kind !== 'param') continue;\n const types = out.get(node.name) ?? new Set<ParamType>();\n out.set(node.name, types);\n\n if (node.variants) {\n for (const [key, body] of node.variants) {\n if (PLURAL_KEYS.has(key) || /^=\\d+$/.test(key)) types.add('number');\n else if (key !== 'other') types.add('string');\n visit(body, out);\n }\n if (types.size === 0) types.add('string');\n } else if (node.format && NUMBER_FORMATS.has(node.format)) {\n types.add('number');\n } else if (node.format && DATE_FORMATS.has(node.format)) {\n types.add('date');\n } else {\n types.add('unknown');\n }\n }\n}\n\nexport function renderParamType(types: Set<ParamType>): string {\n if (types.has('unknown') || types.size === 0) return 'unknown';\n const parts: string[] = [];\n if (types.has('number')) parts.push('number');\n if (types.has('string')) parts.push('string');\n if (types.has('date')) parts.push('Date | number | string');\n return parts.join(' | ');\n}\n","import { existsSync, readFileSync, readdirSync } from 'node:fs';\r\nimport { join, resolve } from 'node:path';\r\nimport { pathToFileURL } from 'node:url';\r\nimport type { TranslateProvider } from './translate';\r\n\r\nexport interface TranslateConfig {\r\n provider?: 'claude' | TranslateProvider;\r\n model?: string;\r\n batchSize?: number;\r\n}\r\n\r\nexport interface VerbalyConfig {\r\n root?: string;\r\n dir?: string;\r\n sourceLocale?: string;\r\n locales?: string[];\r\n include?: string[];\r\n exclude?: string[];\r\n translate?: TranslateConfig;\r\n}\r\n\r\nexport interface ResolvedConfig {\r\n root: string;\r\n dir: string;\r\n sourceLocale: string;\r\n locales: string[];\r\n include: string[];\r\n exclude: string[];\r\n translate: TranslateConfig;\r\n}\r\n\r\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\r\n const root = resolve(config.root ?? process.cwd());\r\n const dir = resolve(root, config.dir ?? 'locales');\r\n const sourceLocale = config.sourceLocale ?? 'en';\r\n\r\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\r\n if (existsSync(dir)) {\r\n for (const file of readdirSync(dir)) {\r\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\r\n }\r\n }\r\n\r\n return {\r\n root,\r\n dir,\r\n sourceLocale,\r\n locales: [...locales],\r\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\r\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\r\n translate: config.translate ?? {},\r\n };\r\n}\r\n\r\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\r\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) {\r\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\r\n return mod.default ?? {};\r\n }\r\n }\r\n for (const name of ['verbaly.config.ts', 'verbaly.config.mts']) {\r\n const path = join(root, name);\r\n if (existsSync(path)) return loadTsConfig(path);\r\n }\r\n const jsonPath = join(root, 'verbaly.config.json');\r\n if (existsSync(jsonPath)) {\r\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\r\n }\r\n return {};\r\n}\r\n\r\nasync function loadTsConfig(path: string): Promise<VerbalyConfig> {\r\n try {\r\n const { bundleRequire } = await import('bundle-require');\r\n const { mod } = await bundleRequire<{ default?: VerbalyConfig }>({ filepath: path });\r\n return mod.default ?? {};\r\n } catch (error) {\r\n if (isModuleNotFound(error, 'esbuild')) {\r\n throw new Error(`[verbaly] ${path} needs esbuild — install it: pnpm add -D esbuild`, {\r\n cause: error,\r\n });\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n\r\n// file config + inline overrides\r\nexport async function loadConfig(\r\n root: string,\r\n overrides: VerbalyConfig = {},\r\n): Promise<ResolvedConfig> {\r\n const fileConfig = await loadConfigFile(root);\r\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\r\n}\r\n\r\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\r\n return Object.fromEntries(\r\n Object.entries(config).filter(([, value]) => value !== undefined),\r\n ) as VerbalyConfig;\r\n}\r\n","import { readFileSync } from 'node:fs';\nimport { glob } from 'tinyglobby';\nimport { analyze } from './analyze';\nimport type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { MessageRegistry } from './registry';\n\nexport async function extractProject(cfg: ResolvedConfig): Promise<MessageRegistry> {\n const files = await glob(cfg.include, {\n cwd: cfg.root,\n ignore: cfg.exclude,\n absolute: true,\n });\n const registry = new MessageRegistry();\n for (const file of files) {\n registry.update(file, analyze(readFileSync(file, 'utf8'), file));\n }\n return registry;\n}\n\nexport interface SyncResult {\n added: Record<string, string[]>;\n}\n\n// fills source texts + '' placeholders\nexport function syncCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): SyncResult {\n const added: Record<string, string[]> = {};\n const source = (catalogs[cfg.sourceLocale] ??= {});\n\n for (const [key, msg] of registry.messages()) {\n if (source[key] !== msg.message) {\n source[key] = msg.message;\n (added[cfg.sourceLocale] ??= []).push(key);\n }\n }\n\n const needed = Object.keys(source);\n for (const locale of cfg.locales) {\n if (locale === cfg.sourceLocale) continue;\n const catalog = (catalogs[locale] ??= {});\n for (const key of needed) {\n if (catalog[key] === undefined) {\n catalog[key] = '';\n (added[locale] ??= []).push(key);\n }\n }\n }\n return { added };\n}\n\n// drops keys no longer referenced\nexport function pruneCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n registry: MessageRegistry,\n): Record<string, string[]> {\n const keep = new Set([...registry.messages().keys(), ...registry.usedKeys().keys()]);\n const removed: Record<string, string[]> = {};\n for (const locale of cfg.locales) {\n const catalog = catalogs[locale];\n if (!catalog) continue;\n for (const key of Object.keys(catalog)) {\n if (!keep.has(key)) {\n delete catalog[key];\n (removed[locale] ??= []).push(key);\n }\n }\n }\n return removed;\n}\n","import type { Analysis, TaggedMessage } from './analyze';\n\nexport class MessageRegistry {\n private files = new Map<string, Analysis>();\n\n update(file: string, analysis: Analysis): void {\n this.files.set(file, analysis);\n }\n\n remove(file: string): void {\n this.files.delete(file);\n }\n\n messages(): Map<string, TaggedMessage> {\n const out = new Map<string, TaggedMessage>();\n for (const analysis of this.files.values()) {\n for (const msg of analysis.tagged) {\n const existing = out.get(msg.key);\n if (existing) {\n if (existing.message !== msg.message) {\n console.warn(\n `[verbaly] key collision \"${msg.key}\": ` +\n `${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)} — second dropped.`,\n );\n }\n continue;\n }\n out.set(msg.key, msg);\n }\n }\n return out;\n }\n\n usedKeys(): Map<string, string[]> {\n const out = new Map<string, string[]>();\n for (const analysis of this.files.values()) {\n for (const used of analysis.usedKeys) {\n const files = out.get(used.key) ?? [];\n if (!files.includes(used.file)) files.push(used.file);\n out.set(used.key, files);\n }\n }\n return out;\n }\n}\n","import type { TranslateProvider, TranslateRequest } from '../translate';\r\n\r\nexport interface ClaudeProviderOptions {\r\n model?: string;\r\n apiKey?: string;\r\n maxTokens?: number;\r\n}\r\n\r\n// balanced default for translation\r\nconst DEFAULT_MODEL = 'claude-sonnet-5';\r\n\r\nconst SYSTEM = `You translate UI strings for the Verbaly i18n library.\r\nRules:\r\n- Translate only the human-readable text, naturally for the target locale.\r\n- Preserve verbatim: placeholders like {name}, format specs like {price:currency/EUR}, variant blocks like {count | one: ... | other: # ...} (translate only the text inside each variant, keep keys and # as-is), ICU syntax, named tags like <em>...</em> and escapes {{ }} || ##.\r\n- Keys are opaque identifiers: return exactly the same keys, never translate or rename them.`;\r\n\r\nexport function claudeProvider(options: ClaudeProviderOptions = {}): TranslateProvider {\r\n return async (request: TranslateRequest) => {\r\n const Anthropic = await loadSdk();\r\n const client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});\r\n const response = await client.messages.create({\r\n model: options.model ?? DEFAULT_MODEL,\r\n max_tokens: options.maxTokens ?? 16000,\r\n thinking: { type: 'disabled' },\r\n system: SYSTEM,\r\n messages: [{ role: 'user', content: buildPrompt(request) }],\r\n output_config: { format: batchFormat(request) },\r\n });\r\n const text = response.content.find((block) => block.type === 'text')?.text ?? '{}';\r\n return JSON.parse(text) as Record<string, string>;\r\n };\r\n}\r\n\r\nexport function buildPrompt(request: TranslateRequest): string {\r\n return (\r\n `Translate each value from \"${request.sourceLocale}\" to \"${request.targetLocale}\". ` +\r\n `Return a JSON object with the same keys and translated values.\\n\\n` +\r\n JSON.stringify(request.messages, null, 2)\r\n );\r\n}\r\n\r\nexport function batchFormat(request: TranslateRequest) {\r\n const keys = Object.keys(request.messages);\r\n return {\r\n type: 'json_schema' as const,\r\n schema: {\r\n type: 'object',\r\n properties: Object.fromEntries(keys.map((key) => [key, { type: 'string' }])),\r\n required: keys,\r\n additionalProperties: false,\r\n },\r\n };\r\n}\r\n\r\nasync function loadSdk(): Promise<typeof import('@anthropic-ai/sdk').default> {\r\n try {\r\n const mod = await import('@anthropic-ai/sdk');\r\n return mod.default;\r\n } catch (error) {\r\n if (isModuleNotFound(error, '@anthropic-ai/sdk')) {\r\n throw new Error(\r\n '[verbaly] the claude translate provider needs @anthropic-ai/sdk — install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY',\r\n { cause: error },\r\n );\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\nfunction isModuleNotFound(error: unknown, name: string): boolean {\r\n return (\r\n error instanceof Error &&\r\n (error as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' &&\r\n error.message.includes(name)\r\n );\r\n}\r\n","import MagicString from 'magic-string';\r\nimport { analyze, type Analysis } from './analyze';\r\n\r\nexport interface TransformResult {\r\n code: string;\r\n map: ReturnType<MagicString['generateMap']>;\r\n}\r\n\r\nexport function transformCode(\r\n code: string,\r\n file: string,\r\n analysis?: Analysis,\r\n): TransformResult | null {\r\n const { tagged } = analysis ?? analyze(code, file);\r\n if (tagged.length === 0) return null;\r\n\r\n const s = new MagicString(code);\r\n for (const msg of tagged) {\r\n const seen = new Set<string>();\r\n const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));\r\n const pairs = entries\r\n .map((p) => `${JSON.stringify(p.name)}: ${code.slice(p.start, p.end)}`)\r\n .join(', ');\r\n\r\n if (msg.jsx) {\r\n const values = entries.length ? ` values={{ ${pairs} }}` : '';\r\n const components = msg.jsx.components.length\r\n ? ` components={{ ${msg.jsx.components\r\n .map((c) => `${JSON.stringify(c.name)}: ${c.source}`)\r\n .join(', ')} }}`\r\n : '';\r\n s.overwrite(\r\n msg.start,\r\n msg.end,\r\n `<${msg.jsx.name} id=${JSON.stringify(msg.key)}${values}${components} />`,\r\n );\r\n continue;\r\n }\r\n\r\n const tagSource = code.slice(msg.tagStart, msg.tagEnd);\r\n const args = entries.length ? `, { ${pairs} }` : '';\r\n s.overwrite(msg.start, msg.end, `${tagSource}(${JSON.stringify(msg.key)}${args})`);\r\n }\r\n return { code: s.toString(), map: s.generateMap({ hires: true }) };\r\n}\r\n","import type { Catalogs } from './catalog';\nimport type { ResolvedConfig } from './config';\nimport { collectParams } from './params';\n\nexport interface TranslateRequest {\n sourceLocale: string;\n targetLocale: string;\n messages: Record<string, string>;\n}\n\nexport type TranslateProvider = (request: TranslateRequest) => Promise<Record<string, string>>;\n\nexport interface TranslateOptions {\n locales?: string[];\n batchSize?: number;\n dryRun?: boolean;\n}\n\nexport interface TranslateResult {\n translated: Record<string, string[]>;\n invalid: Record<string, string[]>;\n pending: Record<string, string[]>;\n}\n\n// fills '' entries via the provider; invalid translations stay '' (check keeps failing)\nexport async function translateCatalogs(\n cfg: ResolvedConfig,\n catalogs: Catalogs,\n provider: TranslateProvider,\n options: TranslateOptions = {},\n): Promise<TranslateResult> {\n const batchSize = options.batchSize ?? 20;\n const targets = (options.locales ?? cfg.locales).filter(\n (locale) => locale !== cfg.sourceLocale && cfg.locales.includes(locale),\n );\n const source = catalogs[cfg.sourceLocale] ?? {};\n const result: TranslateResult = { translated: {}, invalid: {}, pending: {} };\n\n for (const locale of targets) {\n const catalog = (catalogs[locale] ??= {});\n const missing = Object.keys(source).filter((key) => source[key] && !catalog[key]);\n if (missing.length === 0) continue;\n\n if (options.dryRun) {\n result.pending[locale] = missing;\n continue;\n }\n\n for (let i = 0; i < missing.length; i += batchSize) {\n const keys = missing.slice(i, i + batchSize);\n const messages = Object.fromEntries(keys.map((key) => [key, source[key]!]));\n const out = await provider({\n sourceLocale: cfg.sourceLocale,\n targetLocale: locale,\n messages,\n });\n for (const key of keys) {\n const text = out[key];\n if (typeof text === 'string' && text.trim() && structureMatches(source[key]!, text)) {\n catalog[key] = text;\n (result.translated[locale] ??= []).push(key);\n } else {\n (result.invalid[locale] ??= []).push(key);\n }\n }\n }\n }\n return result;\n}\n\n// params and tags must survive translation verbatim\nexport function structureMatches(source: string, translated: string): boolean {\n return sameMembers(paramNames(source), paramNames(translated)) && sameMembers(tagTokens(source), tagTokens(translated));\n}\n\nfunction paramNames(message: string): string[] {\n try {\n return [...collectParams(message).keys()].sort();\n } catch {\n return ['\u0000invalid'];\n }\n}\n\nconst TAG = /<(\\/?)([a-zA-Z][\\w-]*)(\\/?)>/g;\n\nfunction tagTokens(message: string): string[] {\n const out: string[] = [];\n for (const match of message.matchAll(TAG)) {\n out.push(`${match[1]}${match[2]}${match[3]}`);\n }\n return out.sort();\n}\n\nfunction sameMembers(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((value, i) => value === b[i]);\n}\n"],"mappings":";AAAA,SAAS,aAAa;;;ACAtB,SAAS,kBAAkB;AAEpB,SAAS,UAAU,SAAyB;AACjD,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,WAAW,EAAE,MAAM,GAAG,CAAC;AAC5E;;;ADuCA,IAAM,YAAY,oBAAI,IAAI,CAAC,OAAO,mBAAmB,oBAAoB,iBAAiB,OAAO,CAAC;AAE3F,SAAS,QAAQ,MAAc,MAAwB;AAC5D,QAAM,MAAM,YAAY,KAAK,IAAI;AACjC,QAAM,MAAM,MAAM,MAAM;AAAA,IACtB,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,SAAS,MAAM,CAAC,cAAc,KAAK,IAAI,CAAC,YAAY;AAAA,EACtD,CAAC;AAED,QAAM,SAA0B,CAAC;AACjC,QAAM,WAAsB,CAAC;AAE7B,OAAK,IAAI,SAA+B,CAAC,SAAS;AAChD,QAAI,KAAK,SAAS,4BAA4B;AAC5C,YAAM,MAAM,KAAK;AACjB,YAAM,WAAW,WAAW,GAAG;AAC/B,UAAI,CAAC,YAAY,CAAC,aAAa,GAAG,EAAG;AACrC,YAAM,QAAQ,KAAK;AACnB,YAAM,UAAU,aAAa,MAAM,KAAK;AACxC,UAAI,CAAC,QAAS;AACd,aAAO,KAAK;AAAA,QACV,KAAK,WAAW,SAAS,MAAM,UAAU,QAAQ,IAAI;AAAA,QACrD,SAAS,QAAQ;AAAA,QACjB,QAAQ,QAAQ;AAAA,QAChB,OAAO,KAAK;AAAA,QACZ,KAAK,KAAK;AAAA,QACV,UAAU,WAAW,SAAS,WAAW,IAAI;AAAA,QAC7C,QAAQ,WAAW,SAAS,SAAS,IAAI;AAAA,QACzC;AAAA,MACF,CAAC;AAAA,IACH,WAAW,KAAK,SAAS,kBAAkB;AACzC,YAAM,SAAS,KAAK;AACpB,UAAI,CAAC,aAAa,MAAM,EAAG;AAC3B,YAAM,OAAO,KAAK;AAClB,YAAM,QAAQ,KAAK,CAAC;AACpB,UAAI,OAAO,SAAS,iBAAiB;AACnC,iBAAS,KAAK,EAAE,KAAK,MAAM,OAAiB,KAAK,CAAC;AAAA,MACpD;AAAA,IACF,WAAW,KAAK,SAAS,cAAc;AACrC,kBAAY,MAAM,MAAM,MAAM,QAAQ,QAAQ;AAAA,IAChD;AAAA,EACF,CAAC;AAED,SAAO,EAAE,QAAQ,SAAS;AAC5B;AAEA,SAAS,YACP,MACA,MACA,MACA,QACA,UACM;AACN,QAAM,UAAU,KAAK;AACrB,QAAM,WAAW,QAAQ;AACzB,MAAI,SAAS,SAAS,mBAAmB,SAAS,SAAS,QAAS;AAEpE,QAAM,QAAQ,QAAQ;AACtB,QAAM,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,kBAAmB,EAAE,KAAiB,SAAS,IAAI;AAC/F,QAAM,WAAW,KAAK;AAEtB,MAAI,QAAQ;AACV,UAAM,QAAQ,OAAO;AACrB,QAAI,OAAO,SAAS,gBAAiB;AACrC,UAAM,KAAK,MAAM;AAEjB,QAAI,MAAM,WAAW,KAAK,UAAU,QAAQ;AAC1C,YAAMA,SAAQ,kBAAkB,MAAM,QAAQ;AAC9C,UAAIA,QAAO,KAAK,KAAK,GAAG;AACtB,eAAO,KAAK;AAAA,UACV,KAAK;AAAA,UACL,SAASA,OAAM;AAAA,UACf,QAAQA,OAAM;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,KAAK,KAAK;AAAA,UACV,UAAU,SAAS;AAAA,UACnB,QAAQ,SAAS;AAAA,UACjB;AAAA,UACA,KAAK,EAAE,MAAM,SAAS,MAAgB,YAAYA,OAAM,WAAW;AAAA,QACrE,CAAC;AACD;AAAA,MACF;AAAA,IACF;AACA,aAAS,KAAK,EAAE,KAAK,IAAI,KAAK,CAAC;AAC/B;AAAA,EACF;AACA,MAAI,MAAM,SAAS,EAAG;AAEtB,MAAI,CAAC,UAAU,OAAQ;AAEvB,QAAM,QAAQ,kBAAkB,MAAM,QAAQ;AAC9C,MAAI,CAAC,SAAS,CAAC,MAAM,KAAK,KAAK,EAAG;AAElC,SAAO,KAAK;AAAA,IACV,KAAK,UAAU,MAAM,IAAI;AAAA,IACzB,SAAS,MAAM;AAAA,IACf,QAAQ,MAAM;AAAA,IACd,OAAO,KAAK;AAAA,IACZ,KAAK,KAAK;AAAA,IACV,UAAU,SAAS;AAAA,IACnB,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA,KAAK,EAAE,MAAM,SAAS,MAAgB,YAAY,MAAM,WAAW;AAAA,EACrE,CAAC;AACH;AAGA,SAAS,WAAW,KAA6E;AAC/F,MAAI,IAAI,SAAS,iBAAkB,QAAO;AAC1C,QAAM,SAAS,IAAI;AACnB,MAAI,OAAO,SAAS,sBAAsB,OAAO,SAAU,QAAO;AAClE,QAAM,OAAO,OAAO;AACpB,MAAI,KAAK,SAAS,gBAAgB,KAAK,SAAS,KAAM,QAAO;AAC7D,QAAM,MAAM,OAAO;AACnB,MAAI,CAAC,aAAa,GAAG,EAAG,QAAO;AAC/B,QAAM,OAAO,IAAI;AACjB,QAAM,QAAQ,KAAK,CAAC;AACpB,MAAI,KAAK,WAAW,KAAK,OAAO,SAAS,gBAAiB,QAAO;AACjE,SAAO,EAAE,KAAK,MAAM,OAAiB,UAAU,IAAI,OAAO,QAAQ,IAAI,IAAI;AAC5E;AAQA,SAAS,kBAAkB,MAAc,UAA6C;AACpF,QAAM,SAAwB,CAAC;AAC/B,QAAM,aAA+B,CAAC;AACtC,QAAM,cAAc,oBAAI,IAAoB;AAC5C,QAAM,YAAY,oBAAI,IAAoB;AAE1C,WAAS,aAAa,OAAsC;AAC1D,QAAIC,QAAO;AACX,eAAW,SAAS,OAAO;AACzB,UAAI,MAAM,SAAS,WAAW;AAC5B,QAAAA,SAAQ,WAAW,aAAa,MAAM,KAAe,CAAC;AAAA,MACxD,WAAW,MAAM,SAAS,0BAA0B;AAClD,cAAM,OAAO,MAAM;AACnB,YAAI,KAAK,SAAS,qBAAsB;AACxC,YAAI,KAAK,SAAS,iBAAiB;AACjC,UAAAA,SAAQ,WAAW,KAAK,KAAe;AACvC;AAAA,QACF;AACA,YAAI,KAAK,SAAS,2BAA4B,QAAO;AACrD,cAAM,SAAS,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG;AAC9C,cAAM,OAAO,WAAW,WAAW,MAAM,OAAO,MAAM,GAAG,QAAQ,WAAW;AAC5E,eAAO,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,IAAI,CAAC;AACtD,QAAAA,SAAQ,IAAI,IAAI;AAAA,MAClB,WAAW,MAAM,SAAS,cAAc;AACtC,cAAM,UAAU,MAAM;AACtB,cAAM,WAAW,QAAQ;AACzB,YAAI,SAAS,SAAS,mBAAmB,SAAS,SAAS,QAAS,QAAO;AAC3E,cAAM,SAAS,iBAAiB,MAAM,OAAO;AAC7C,cAAM,MAAM,WAAY,SAAS,KAAgB,YAAY,GAAG,QAAQ,SAAS;AACjF,YAAI,CAAC,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,EAAG,YAAW,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAClF,YAAI,CAAC,MAAM,gBAAgB;AACzB,UAAAA,SAAQ,IAAI,GAAG;AAAA,QACjB,OAAO;AACL,gBAAM,QAAQ,aAAa,MAAM,QAAqB;AACtD,cAAI,UAAU,OAAW,QAAO;AAChC,UAAAA,SAAQ,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG;AAAA,QAClC;AAAA,MACF,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAOA;AAAA,EACT;AAEA,QAAM,OAAO,aAAa,QAAQ;AAClC,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,EAAE,MAAM,QAAQ,WAAW;AACpC;AAGA,SAAS,aAAa,KAAqB;AACzC,QAAM,QAAQ,IAAI,MAAM,aAAa;AACrC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,QAAI,OAAO,MAAM,CAAC,EAAG,QAAQ,OAAO,GAAG;AACvC,QAAI,MAAM,EAAG,QAAO,KAAK,QAAQ,OAAO,EAAE;AAC1C,QAAI,MAAM,MAAM,SAAS,EAAG,QAAO,KAAK,QAAQ,OAAO,EAAE;AACzD,QAAI,CAAC,KAAM;AACX,QAAI,OAAO,MAAM,EAAG,QAAO;AAC3B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,MAAc,SAA0B;AAChE,QAAM,MAAM,KAAK,MAAM,QAAQ,OAAO,QAAQ,GAAG;AACjD,SAAO,IAAI,SAAS,IAAI,IAAI,MAAM,GAAG,IAAI,MAAM,GAAG,EAAE,EAAE,QAAQ,CAAC;AACjE;AAEA,SAAS,aAAa,MAAwB;AAC5C,MAAI,KAAK,SAAS,aAAc,QAAO,KAAK,SAAS;AACrD,MAAI,KAAK,SAAS,sBAAsB,CAAC,KAAK,UAAU;AACtD,UAAM,OAAO,KAAK;AAClB,WAAO,KAAK,SAAS,gBAAgB,KAAK,SAAS;AAAA,EACrD;AACA,SAAO;AACT;AAOA,SAAS,aAAa,MAAc,OAA0C;AAC5E,QAAM,SAAS,MAAM;AACrB,QAAM,cAAc,MAAM;AAC1B,QAAM,SAAwB,CAAC;AAC/B,QAAM,QAAQ,oBAAI,IAAoB;AAEtC,MAAI,OAAO,WAAW,YAAY,OAAO,CAAC,CAAC,CAAC;AAE5C,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,OAAO,YAAY,CAAC;AAC1B,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,SAAS,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG;AAC9C,UAAM,OAAO,WAAW,WAAW,MAAM,CAAC,GAAG,QAAQ,KAAK;AAC1D,WAAO,KAAK,EAAE,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,IAAI,CAAC;AACtD,YAAQ,IAAI,IAAI,MAAM,WAAW,YAAY,OAAO,IAAI,CAAC,CAAC,CAAC;AAAA,EAC7D;AACA,SAAO,EAAE,MAAM,OAAO;AACxB;AAEA,SAAS,YAAY,SAAsC;AACzD,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ;AACtB,SAAO,MAAM,UAAU,MAAM;AAC/B;AAGA,SAAS,WAAW,MAAsB;AACxC,SAAO,KAAK,QAAQ,SAAS,CAAC,MAAM,IAAI,CAAC;AAC3C;AAEA,SAAS,WAAW,MAAe,OAAuB;AACxD,MAAI,KAAK,SAAS,aAAc,QAAO,KAAK;AAC5C,MAAI,KAAK,SAAS,sBAAsB,CAAC,KAAK,UAAU;AACtD,UAAM,OAAO,KAAK;AAClB,QAAI,KAAK,SAAS,aAAc,QAAO,KAAK;AAAA,EAC9C;AACA,SAAO,IAAI,KAAK;AAClB;AAEA,SAAS,WAAW,MAAc,QAAgB,OAAoC;AACpF,MAAI,OAAO;AACX,MAAI,IAAI;AACR,SAAO,MAAM,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,MAAM,QAAQ;AACpD,WAAO,GAAG,IAAI,GAAG,CAAC;AAClB,SAAK;AAAA,EACP;AACA,QAAM,IAAI,MAAM,MAAM;AACtB,SAAO;AACT;AAEA,SAAS,KAAK,MAAeC,QAAsC;AACjE,EAAAA,OAAM,IAAI;AACV,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,UAAU,IAAI,GAAG,EAAG;AACxB,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,QAAQ,OAAO;AACxB,YAAI,OAAO,IAAI,EAAG,MAAK,MAAMA,MAAK;AAAA,MACpC;AAAA,IACF,WAAW,OAAO,KAAK,GAAG;AACxB,WAAK,OAAOA,MAAK;AAAA,IACnB;AAAA,EACF;AACF;AAEA,SAAS,OAAO,OAAkC;AAChD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA6B,SAAS;AAElD;;;AEpUA,SAAS,WAAW,cAAc,qBAAqB;AACvD,SAAS,YAAY;AAMd,SAAS,YAAY,KAAqB,QAAwB;AACvE,SAAO,KAAK,IAAI,KAAK,GAAG,MAAM,OAAO;AACvC;AAEO,SAAS,YAAY,KAAqB,QAAyB;AACxE,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,YAAY,KAAK,MAAM,GAAG,MAAM,CAAC;AAAA,EAClE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,aAAa,KAA+B;AAC1D,QAAM,WAAqB,CAAC;AAC5B,aAAW,UAAU,IAAI,QAAS,UAAS,MAAM,IAAI,YAAY,KAAK,MAAM;AAC5E,SAAO;AACT;AAEO,SAAS,iBAAiB,SAA0B;AACzD,QAAM,SAAkB,CAAC;AACzB,aAAW,OAAO,OAAO,KAAK,OAAO,EAAE,KAAK,GAAG;AAC7C,UAAM,QAAQ,QAAQ,GAAG;AACzB,QAAI,UAAU,OAAW,QAAO,GAAG,IAAI;AAAA,EACzC;AACA,SAAO,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI;AAC3C;AAEO,SAAS,aAAa,KAAqB,QAAgB,SAA0B;AAC1F,QAAM,aAAa,iBAAiB,OAAO;AAC3C,YAAU,IAAI,KAAK,EAAE,WAAW,KAAK,CAAC;AACtC,gBAAc,YAAY,KAAK,MAAM,GAAG,UAAU;AAClD,SAAO;AACT;;;AClBO,SAAS,MACd,KACA,UACA,UACa;AACb,QAAM,SAAS,SAAS,IAAI,YAAY,KAAK,CAAC;AAC9C,QAAM,YAAY,SAAS,SAAS;AAEpC,QAAM,UAA0B,CAAC;AACjC,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,SAAS,GAAG;AAC9C,UAAM,QACJ,UAAU,IAAI,GAAG,KAAK,IAAI,QAAQ,KAAK,CAAC,WAAW,SAAS,MAAM,IAAI,GAAG,MAAM,MAAS;AAC1F,QAAI,CAAC,MAAO,SAAQ,KAAK,EAAE,KAAK,MAAM,CAAC;AAAA,EACzC;AAEA,QAAM,SAAS,oBAAI,IAAY,CAAC,GAAG,UAAU,KAAK,GAAG,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC;AAE5E,QAAM,UAA0B,CAAC;AACjC,aAAW,OAAO,UAAU,KAAK,GAAG;AAClC,QAAI,CAAC,OAAO,GAAG,GAAG;AAChB,cAAQ,KAAK,EAAE,QAAQ,IAAI,cAAc,KAAK,QAAQ,UAAU,IAAI,GAAG,GAAG,QAAQ,CAAC;AAAA,IACrF;AAAA,EACF;AACA,aAAW,UAAU,IAAI,SAAS;AAChC,QAAI,WAAW,IAAI,aAAc;AACjC,eAAW,OAAO,QAAQ;AACxB,UAAI,CAAC,SAAS,MAAM,IAAI,GAAG,GAAG;AAC5B,gBAAQ,KAAK,EAAE,QAAQ,KAAK,QAAQ,OAAO,GAAG,KAAK,UAAU,IAAI,GAAG,GAAG,QAAQ,CAAC;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,IAAI,QAAQ,WAAW,KAAK,QAAQ,WAAW,GAAG,SAAS,QAAQ;AAC9E;AAEO,SAAS,kBAAkB,QAA6B;AAC7D,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,UAAM,KAAK,uBAAuB;AAClC,eAAW,SAAS,OAAO,SAAS;AAClC,YAAM,OAAO,MAAM,SAAS,YAAO,SAAS,MAAM,QAAQ,EAAE,CAAC,MAAM;AACnE,YAAM,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,GAAG,GAAG,IAAI,EAAE;AAAA,IACtD;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,SAAS,GAAG;AAC7B,UAAM,KAAK,oCAAoC;AAC/C,eAAW,SAAS,OAAO,SAAS;AAClC,YAAM,KAAK,KAAK,MAAM,GAAG,mBAAc,MAAM,MAAM,KAAK,IAAI,CAAC,EAAE;AAAA,IACjE;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,SAAS,MAAc,KAAqB;AACnD,SAAO,KAAK,SAAS,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC,IAAI,WAAM;AAC5D;;;AC5EA,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,QAAAC,aAAY;;;ACDrB,SAAS,SAAAC,cAA+B;AAIxC,IAAM,cAAc,oBAAI,IAAI,CAAC,QAAQ,OAAO,OAAO,OAAO,MAAM,CAAC;AACjE,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,WAAW,WAAW,UAAU,CAAC;AAC3E,IAAM,eAAe,oBAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;AAEtC,SAAS,cAAc,SAA8C;AAC1E,QAAM,MAAM,oBAAI,IAA4B;AAC5C,QAAMA,OAAM,OAAO,GAAG,GAAG;AACzB,SAAO;AACT;AAEA,SAAS,MAAM,OAAsB,KAAwC;AAC3E,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAS;AAC3B,UAAM,QAAQ,IAAI,IAAI,KAAK,IAAI,KAAK,oBAAI,IAAe;AACvD,QAAI,IAAI,KAAK,MAAM,KAAK;AAExB,QAAI,KAAK,UAAU;AACjB,iBAAW,CAAC,KAAK,IAAI,KAAK,KAAK,UAAU;AACvC,YAAI,YAAY,IAAI,GAAG,KAAK,SAAS,KAAK,GAAG,EAAG,OAAM,IAAI,QAAQ;AAAA,iBACzD,QAAQ,QAAS,OAAM,IAAI,QAAQ;AAC5C,cAAM,MAAM,GAAG;AAAA,MACjB;AACA,UAAI,MAAM,SAAS,EAAG,OAAM,IAAI,QAAQ;AAAA,IAC1C,WAAW,KAAK,UAAU,eAAe,IAAI,KAAK,MAAM,GAAG;AACzD,YAAM,IAAI,QAAQ;AAAA,IACpB,WAAW,KAAK,UAAU,aAAa,IAAI,KAAK,MAAM,GAAG;AACvD,YAAM,IAAI,MAAM;AAAA,IAClB,OAAO;AACL,YAAM,IAAI,SAAS;AAAA,IACrB;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB,OAA+B;AAC7D,MAAI,MAAM,IAAI,SAAS,KAAK,MAAM,SAAS,EAAG,QAAO;AACrD,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAM,IAAI,QAAQ,EAAG,OAAM,KAAK,QAAQ;AAC5C,MAAI,MAAM,IAAI,QAAQ,EAAG,OAAM,KAAK,QAAQ;AAC5C,MAAI,MAAM,IAAI,MAAM,EAAG,OAAM,KAAK,wBAAwB;AAC1D,SAAO,MAAM,KAAK,KAAK;AACzB;;;ADtCO,IAAM,aAAa;AAEnB,SAAS,sBAAsB,KAA6B;AACjE,QAAM,SAAS,IAAI,QAAQ,OAAO,CAAC,WAAW,WAAW,IAAI,YAAY;AACzE,QAAM,MAAM,KAAK,UAAU,IAAI,YAAY;AAC3C,QAAM,UAAU,OACb;AAAA,IACC,CAAC,WAAW,OAAO,KAAK,UAAU,MAAM,CAAC,mBAAmB,UAAU,WAAW,MAAM;AAAA,EACzF,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA,sBACa,UAAU,WAAW,IAAI,YAAY;AAAA;AAAA;AAAA,YAG/C,GAAG;AAAA,cACD,GAAG;AAAA,iBACA,GAAG;AAAA;AAAA,EAElB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBT;AAEO,SAAS,qBAAqB,SAA0B;AAC7D,SAAO,kBAAkB,KAAK,UAAU,OAAO,CAAC;AAAA;AAClD;AAEO,SAAS,YAAY,SAAsC;AAChE,QAAM,QAAkB,CAAC;AACzB,aAAW,CAAC,KAAK,OAAO,KAAK,CAAC,GAAG,QAAQ,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,GAAG;AAC1F,UAAM,SAAS,cAAc,OAAO;AACpC,QAAI,OAAO,SAAS,GAAG;AACrB,YAAM,KAAK,OAAO,KAAK,UAAU,GAAG,CAAC,UAAU;AAAA,IACjD,OAAO;AACL,YAAM,SAAS,CAAC,GAAG,OAAO,QAAQ,CAAC,EAChC,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,GAAG,KAAK,UAAU,IAAI,CAAC,KAAK,gBAAgB,KAAK,CAAC,EAAE,EAC3E,KAAK,IAAI;AACZ,YAAM,KAAK,OAAO,KAAK,UAAU,GAAG,CAAC,OAAO,MAAM,KAAK;AAAA,IACzD;AAAA,EACF;AAEA,SAAO;AAAA;AAAA;AAAA,EAGP,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BlB;AAEO,SAAS,SAAS,KAAqB,SAAoC;AAChF,EAAAC,eAAcC,MAAK,IAAI,MAAM,cAAc,GAAG,YAAY,OAAO,CAAC;AACpE;;;AEnGA,SAAS,YAAY,gBAAAC,eAAc,mBAAmB;AACtD,SAAS,QAAAC,OAAM,eAAe;AAC9B,SAAS,qBAAqB;AA6BvB,SAAS,cAAc,SAAwB,CAAC,GAAmB;AACxE,QAAM,OAAO,QAAQ,OAAO,QAAQ,QAAQ,IAAI,CAAC;AACjD,QAAM,MAAM,QAAQ,MAAM,OAAO,OAAO,SAAS;AACjD,QAAM,eAAe,OAAO,gBAAgB;AAE5C,QAAM,UAAU,oBAAI,IAAY,CAAC,cAAc,GAAI,OAAO,WAAW,CAAC,CAAE,CAAC;AACzE,MAAI,WAAW,GAAG,GAAG;AACnB,eAAW,QAAQ,YAAY,GAAG,GAAG;AACnC,UAAI,KAAK,SAAS,OAAO,EAAG,SAAQ,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,CAAC,GAAG,OAAO;AAAA,IACpB,SAAS,OAAO,WAAW,CAAC,kCAAkC;AAAA,IAC9D,SAAS,OAAO,WAAW,CAAC,sBAAsB,YAAY;AAAA,IAC9D,WAAW,OAAO,aAAa,CAAC;AAAA,EAClC;AACF;AAEA,eAAsB,eAAe,MAAsC;AACzE,aAAW,QAAQ,CAAC,qBAAqB,oBAAoB,GAAG;AAC9D,UAAM,OAAOA,MAAK,MAAM,IAAI;AAC5B,QAAI,WAAW,IAAI,GAAG;AACpB,YAAM,MAAO,MAAM,OAAO,cAAc,IAAI,EAAE;AAC9C,aAAO,IAAI,WAAW,CAAC;AAAA,IACzB;AAAA,EACF;AACA,aAAW,QAAQ,CAAC,qBAAqB,oBAAoB,GAAG;AAC9D,UAAM,OAAOA,MAAK,MAAM,IAAI;AAC5B,QAAI,WAAW,IAAI,EAAG,QAAO,aAAa,IAAI;AAAA,EAChD;AACA,QAAM,WAAWA,MAAK,MAAM,qBAAqB;AACjD,MAAI,WAAW,QAAQ,GAAG;AACxB,WAAO,KAAK,MAAMD,cAAa,UAAU,MAAM,CAAC;AAAA,EAClD;AACA,SAAO,CAAC;AACV;AAEA,eAAe,aAAa,MAAsC;AAChE,MAAI;AACF,UAAM,EAAE,cAAc,IAAI,MAAM,OAAO,gBAAgB;AACvD,UAAM,EAAE,IAAI,IAAI,MAAM,cAA2C,EAAE,UAAU,KAAK,CAAC;AACnF,WAAO,IAAI,WAAW,CAAC;AAAA,EACzB,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO,SAAS,GAAG;AACtC,YAAM,IAAI,MAAM,aAAa,IAAI,yDAAoD;AAAA,QACnF,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,iBAAiB,OAAgB,MAAuB;AAC/D,SACE,iBAAiB,SAChB,MAA4B,SAAS,0BACtC,MAAM,QAAQ,SAAS,IAAI;AAE/B;AAGA,eAAsB,WACpB,MACA,YAA2B,CAAC,GACH;AACzB,QAAM,aAAa,MAAM,eAAe,IAAI;AAC5C,SAAO,cAAc,EAAE,GAAG,YAAY,GAAG,YAAY,SAAS,GAAG,KAAK,CAAC;AACzE;AAEA,SAAS,YAAY,QAAsC;AACzD,SAAO,OAAO;AAAA,IACZ,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,MAAS;AAAA,EAClE;AACF;;;AC7GA,SAAS,gBAAAE,qBAAoB;AAC7B,SAAS,YAAY;;;ACCd,IAAM,kBAAN,MAAsB;AAAA,EACnB,QAAQ,oBAAI,IAAsB;AAAA,EAE1C,OAAO,MAAc,UAA0B;AAC7C,SAAK,MAAM,IAAI,MAAM,QAAQ;AAAA,EAC/B;AAAA,EAEA,OAAO,MAAoB;AACzB,SAAK,MAAM,OAAO,IAAI;AAAA,EACxB;AAAA,EAEA,WAAuC;AACrC,UAAM,MAAM,oBAAI,IAA2B;AAC3C,eAAW,YAAY,KAAK,MAAM,OAAO,GAAG;AAC1C,iBAAW,OAAO,SAAS,QAAQ;AACjC,cAAM,WAAW,IAAI,IAAI,IAAI,GAAG;AAChC,YAAI,UAAU;AACZ,cAAI,SAAS,YAAY,IAAI,SAAS;AACpC,oBAAQ;AAAA,cACN,4BAA4B,IAAI,GAAG,MAC9B,KAAK,UAAU,SAAS,OAAO,CAAC,OAAO,KAAK,UAAU,IAAI,OAAO,CAAC;AAAA,YACzE;AAAA,UACF;AACA;AAAA,QACF;AACA,YAAI,IAAI,IAAI,KAAK,GAAG;AAAA,MACtB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAAkC;AAChC,UAAM,MAAM,oBAAI,IAAsB;AACtC,eAAW,YAAY,KAAK,MAAM,OAAO,GAAG;AAC1C,iBAAW,QAAQ,SAAS,UAAU;AACpC,cAAM,QAAQ,IAAI,IAAI,KAAK,GAAG,KAAK,CAAC;AACpC,YAAI,CAAC,MAAM,SAAS,KAAK,IAAI,EAAG,OAAM,KAAK,KAAK,IAAI;AACpD,YAAI,IAAI,KAAK,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;;;ADrCA,eAAsB,eAAe,KAA+C;AAClF,QAAM,QAAQ,MAAM,KAAK,IAAI,SAAS;AAAA,IACpC,KAAK,IAAI;AAAA,IACT,QAAQ,IAAI;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AACD,QAAM,WAAW,IAAI,gBAAgB;AACrC,aAAW,QAAQ,OAAO;AACxB,aAAS,OAAO,MAAM,QAAQC,cAAa,MAAM,MAAM,GAAG,IAAI,CAAC;AAAA,EACjE;AACA,SAAO;AACT;AAOO,SAAS,aACd,KACA,UACA,UACY;AACZ,QAAM,QAAkC,CAAC;AACzC,QAAM,SAAU,SAAS,IAAI,YAAY,MAAM,CAAC;AAEhD,aAAW,CAAC,KAAK,GAAG,KAAK,SAAS,SAAS,GAAG;AAC5C,QAAI,OAAO,GAAG,MAAM,IAAI,SAAS;AAC/B,aAAO,GAAG,IAAI,IAAI;AAClB,OAAC,MAAM,IAAI,YAAY,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,KAAK,MAAM;AACjC,aAAW,UAAU,IAAI,SAAS;AAChC,QAAI,WAAW,IAAI,aAAc;AACjC,UAAM,UAAW,SAAS,MAAM,MAAM,CAAC;AACvC,eAAW,OAAO,QAAQ;AACxB,UAAI,QAAQ,GAAG,MAAM,QAAW;AAC9B,gBAAQ,GAAG,IAAI;AACf,SAAC,MAAM,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,MAAM;AACjB;AAGO,SAAS,cACd,KACA,UACA,UAC0B;AAC1B,QAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,SAAS,SAAS,EAAE,KAAK,GAAG,GAAG,SAAS,SAAS,EAAE,KAAK,CAAC,CAAC;AACnF,QAAM,UAAoC,CAAC;AAC3C,aAAW,UAAU,IAAI,SAAS;AAChC,UAAM,UAAU,SAAS,MAAM;AAC/B,QAAI,CAAC,QAAS;AACd,eAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,UAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AAClB,eAAO,QAAQ,GAAG;AAClB,SAAC,QAAQ,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AEhEA,IAAM,gBAAgB;AAEtB,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAMR,SAAS,eAAe,UAAiC,CAAC,GAAsB;AACrF,SAAO,OAAO,YAA8B;AAC1C,UAAM,YAAY,MAAM,QAAQ;AAChC,UAAM,SAAS,IAAI,UAAU,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC,CAAC;AAC7E,UAAM,WAAW,MAAM,OAAO,SAAS,OAAO;AAAA,MAC5C,OAAO,QAAQ,SAAS;AAAA,MACxB,YAAY,QAAQ,aAAa;AAAA,MACjC,UAAU,EAAE,MAAM,WAAW;AAAA,MAC7B,QAAQ;AAAA,MACR,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,OAAO,EAAE,CAAC;AAAA,MAC1D,eAAe,EAAE,QAAQ,YAAY,OAAO,EAAE;AAAA,IAChD,CAAC;AACD,UAAM,OAAO,SAAS,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,MAAM,GAAG,QAAQ;AAC9E,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AACF;AAEO,SAAS,YAAY,SAAmC;AAC7D,SACE,8BAA8B,QAAQ,YAAY,SAAS,QAAQ,YAAY;AAAA;AAAA,IAE/E,KAAK,UAAU,QAAQ,UAAU,MAAM,CAAC;AAE5C;AAEO,SAAS,YAAY,SAA2B;AACrD,QAAM,OAAO,OAAO,KAAK,QAAQ,QAAQ;AACzC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,YAAY,OAAO,YAAY,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,MAC3E,UAAU;AAAA,MACV,sBAAsB;AAAA,IACxB;AAAA,EACF;AACF;AAEA,eAAe,UAA+D;AAC5E,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,mBAAmB;AAC5C,WAAO,IAAI;AAAA,EACb,SAAS,OAAO;AACd,QAAIC,kBAAiB,OAAO,mBAAmB,GAAG;AAChD,YAAM,IAAI;AAAA,QACR;AAAA,QACA,EAAE,OAAO,MAAM;AAAA,MACjB;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAASA,kBAAiB,OAAgB,MAAuB;AAC/D,SACE,iBAAiB,SAChB,MAA4B,SAAS,0BACtC,MAAM,QAAQ,SAAS,IAAI;AAE/B;;;AC5EA,OAAO,iBAAiB;AAQjB,SAAS,cACd,MACA,MACA,UACwB;AACxB,QAAM,EAAE,OAAO,IAAI,YAAY,QAAQ,MAAM,IAAI;AACjD,MAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,QAAM,IAAI,IAAI,YAAY,IAAI;AAC9B,aAAW,OAAO,QAAQ;AACxB,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,UAAU,IAAI,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,EAAE,IAAI,CAAC;AAC9E,UAAM,QAAQ,QACX,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,EAAE,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,EACrE,KAAK,IAAI;AAEZ,QAAI,IAAI,KAAK;AACX,YAAM,SAAS,QAAQ,SAAS,cAAc,KAAK,QAAQ;AAC3D,YAAM,aAAa,IAAI,IAAI,WAAW,SAClC,kBAAkB,IAAI,IAAI,WACvB,IAAI,CAAC,MAAM,GAAG,KAAK,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EACnD,KAAK,IAAI,CAAC,QACb;AACJ,QAAE;AAAA,QACA,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,GAAG,CAAC,GAAG,MAAM,GAAG,UAAU;AAAA,MACtE;AACA;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,MAAM,IAAI,UAAU,IAAI,MAAM;AACrD,UAAM,OAAO,QAAQ,SAAS,OAAO,KAAK,OAAO;AACjD,MAAE,UAAU,IAAI,OAAO,IAAI,KAAK,GAAG,SAAS,IAAI,KAAK,UAAU,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG;AAAA,EACnF;AACA,SAAO,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,EAAE,YAAY,EAAE,OAAO,KAAK,CAAC,EAAE;AACnE;;;ACnBA,eAAsB,kBACpB,KACA,UACA,UACA,UAA4B,CAAC,GACH;AAC1B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,WAAW,QAAQ,WAAW,IAAI,SAAS;AAAA,IAC/C,CAAC,WAAW,WAAW,IAAI,gBAAgB,IAAI,QAAQ,SAAS,MAAM;AAAA,EACxE;AACA,QAAM,SAAS,SAAS,IAAI,YAAY,KAAK,CAAC;AAC9C,QAAM,SAA0B,EAAE,YAAY,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAE3E,aAAW,UAAU,SAAS;AAC5B,UAAM,UAAW,SAAS,MAAM,MAAM,CAAC;AACvC,UAAM,UAAU,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,QAAQ,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,CAAC;AAChF,QAAI,QAAQ,WAAW,EAAG;AAE1B,QAAI,QAAQ,QAAQ;AAClB,aAAO,QAAQ,MAAM,IAAI;AACzB;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;AAClD,YAAM,OAAO,QAAQ,MAAM,GAAG,IAAI,SAAS;AAC3C,YAAM,WAAW,OAAO,YAAY,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,OAAO,GAAG,CAAE,CAAC,CAAC;AAC1E,YAAM,MAAM,MAAM,SAAS;AAAA,QACzB,cAAc,IAAI;AAAA,QAClB,cAAc;AAAA,QACd;AAAA,MACF,CAAC;AACD,iBAAW,OAAO,MAAM;AACtB,cAAM,OAAO,IAAI,GAAG;AACpB,YAAI,OAAO,SAAS,YAAY,KAAK,KAAK,KAAK,iBAAiB,OAAO,GAAG,GAAI,IAAI,GAAG;AACnF,kBAAQ,GAAG,IAAI;AACf,WAAC,OAAO,WAAW,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,QAC7C,OAAO;AACL,WAAC,OAAO,QAAQ,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,QAAgB,YAA6B;AAC5E,SAAO,YAAY,WAAW,MAAM,GAAG,WAAW,UAAU,CAAC,KAAK,YAAY,UAAU,MAAM,GAAG,UAAU,UAAU,CAAC;AACxH;AAEA,SAAS,WAAW,SAA2B;AAC7C,MAAI;AACF,WAAO,CAAC,GAAG,cAAc,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK;AAAA,EACjD,QAAQ;AACN,WAAO,CAAC,WAAU;AAAA,EACpB;AACF;AAEA,IAAM,MAAM;AAEZ,SAAS,UAAU,SAA2B;AAC5C,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,QAAQ,SAAS,GAAG,GAAG;AACzC,QAAI,KAAK,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE;AAAA,EAC9C;AACA,SAAO,IAAI,KAAK;AAClB;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,SAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,OAAO,MAAM,UAAU,EAAE,CAAC,CAAC;AACtE;","names":["built","text","visit","writeFileSync","join","parse","writeFileSync","join","readFileSync","join","readFileSync","readFileSync","isModuleNotFound"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/compiler",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Message extraction and type-safe codegen for Verbaly.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -46,17 +46,22 @@
46
46
  "bundle-require": "^5.1.0",
47
47
  "magic-string": "^0.30.21",
48
48
  "tinyglobby": "^0.2.17",
49
- "verbaly": "0.8.0"
49
+ "verbaly": "0.9.0"
50
50
  },
51
51
  "peerDependencies": {
52
+ "@anthropic-ai/sdk": ">=0.110.0",
52
53
  "esbuild": ">=0.18"
53
54
  },
54
55
  "peerDependenciesMeta": {
56
+ "@anthropic-ai/sdk": {
57
+ "optional": true
58
+ },
55
59
  "esbuild": {
56
60
  "optional": true
57
61
  }
58
62
  },
59
63
  "devDependencies": {
64
+ "@anthropic-ai/sdk": "^0.110.0",
60
65
  "esbuild": "^0.28.1"
61
66
  },
62
67
  "scripts": {