@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 +9 -0
- package/dist/claude-RQATA6OI.js +63 -0
- package/dist/claude-RQATA6OI.js.map +1 -0
- package/dist/cli.js +110 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +122 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
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
|
|
587
|
-
verbaly check
|
|
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
|
|
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":"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","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.
|
|
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.
|
|
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": {
|