@verbaly/compiler 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,34 @@
1
- <p align="center">
2
- <img src="https://raw.githubusercontent.com/AronSoto/verbaly/develop/assets/logo-light.png" alt="Verbaly" width="300" />
3
- </p>
4
-
5
- <p align="center"><em>Message extraction, type-safe codegen and CLI for Verbaly.</em></p>
6
-
7
- <p align="center">
8
- <a href="https://www.npmjs.com/package/@verbaly/compiler"><img src="https://img.shields.io/npm/v/@verbaly/compiler?logo=npm&color=cb3837" alt="npm version" /></a>
9
- <a href="https://github.com/AronSoto/verbaly/blob/develop/LICENSE"><img src="https://img.shields.io/npm/l/@verbaly/compiler?color=blue" alt="Apache-2.0" /></a>
10
- </p>
11
-
12
- ---
13
-
14
- The compiler behind [Verbaly](https://github.com/AronSoto/verbaly): AST extraction of `t\`...\`` into stable hashed keys, flat JSON catalog sync, and typed codegen. It also ships the **`verbaly` CLI**.
15
-
16
- > Most projects don't install this directly — [`@verbaly/vite`](https://www.npmjs.com/package/@verbaly/vite) wraps it with zero config. Reach for it when scripting extraction/checks yourself.
17
-
18
- ## CLI
19
-
20
- ```bash
21
- npx verbaly extract # sync catalogs + types
22
- npx verbaly check # exit 1 if anything is missing (CI)
23
- npx verbaly extract --prune # drop orphaned keys
24
- ```
25
-
26
- Reads `verbaly.config.{js,mjs,json}`. Generates `locales/<locale>.json` (flat, portable — no proprietary format) and `verbaly.d.ts` with params typed per key.
27
-
28
- 📖 Docs: **https://verbaly-web.vercel.app/docs/cli**
29
-
30
- > ⚠️ Early development (`0.x`) — API not stable yet.
31
-
32
- ## License
33
-
34
- [Apache-2.0](https://github.com/AronSoto/verbaly/blob/develop/LICENSE) © Aron Soto
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/AronSoto/verbaly/develop/assets/logo-light.png" alt="Verbaly" width="300" />
3
+ </p>
4
+
5
+ <p align="center"><em>Message extraction, type-safe codegen and CLI for Verbaly.</em></p>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@verbaly/compiler"><img src="https://img.shields.io/npm/v/@verbaly/compiler?logo=npm&color=cb3837" alt="npm version" /></a>
9
+ <a href="https://github.com/AronSoto/verbaly/blob/develop/LICENSE"><img src="https://img.shields.io/npm/l/@verbaly/compiler?color=blue" alt="Apache-2.0" /></a>
10
+ </p>
11
+
12
+ ---
13
+
14
+ The compiler behind [Verbaly](https://github.com/AronSoto/verbaly): AST extraction of `t\`...\``**and JSX`<Trans>` children** into stable hashed keys, flat JSON catalog sync, and typed codegen. It also ships the **`verbaly` CLI**.
15
+
16
+ > Most projects don't install this directly — [`@verbaly/vite`](https://www.npmjs.com/package/@verbaly/vite) wraps it with zero config. Reach for it when scripting extraction/checks yourself.
17
+
18
+ ## CLI
19
+
20
+ ```bash
21
+ npx verbaly extract # sync catalogs + types
22
+ npx verbaly check # exit 1 if anything is missing (CI)
23
+ npx verbaly extract --prune # drop orphaned keys
24
+ ```
25
+
26
+ 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
+ 📖 Docs: **https://verbaly-web.vercel.app/docs/cli**
29
+
30
+ > ⚠️ Early development (`0.x`) — API not stable yet.
31
+
32
+ ## License
33
+
34
+ [Apache-2.0](https://github.com/AronSoto/verbaly/blob/develop/LICENSE) © Aron Soto
package/dist/cli.js CHANGED
@@ -200,12 +200,33 @@ async function loadConfigFile(root) {
200
200
  return mod.default ?? {};
201
201
  }
202
202
  }
203
+ for (const name of ["verbaly.config.ts", "verbaly.config.mts"]) {
204
+ const path = join3(root, name);
205
+ if (existsSync(path)) return loadTsConfig(path);
206
+ }
203
207
  const jsonPath = join3(root, "verbaly.config.json");
204
208
  if (existsSync(jsonPath)) {
205
209
  return JSON.parse(readFileSync2(jsonPath, "utf8"));
206
210
  }
207
211
  return {};
208
212
  }
213
+ async function loadTsConfig(path) {
214
+ try {
215
+ const { bundleRequire } = await import("bundle-require");
216
+ const { mod } = await bundleRequire({ filepath: path });
217
+ return mod.default ?? {};
218
+ } catch (error) {
219
+ if (isModuleNotFound(error, "esbuild")) {
220
+ throw new Error(`[verbaly] ${path} needs esbuild \u2014 install it: pnpm add -D esbuild`, {
221
+ cause: error
222
+ });
223
+ }
224
+ throw error;
225
+ }
226
+ }
227
+ function isModuleNotFound(error, name) {
228
+ return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
229
+ }
209
230
  async function loadConfig(root, overrides = {}) {
210
231
  const fileConfig = await loadConfigFile(root);
211
232
  return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });
@@ -265,10 +286,103 @@ function analyze(code, file) {
265
286
  if (first?.type === "StringLiteral") {
266
287
  usedKeys.push({ key: first.value, file });
267
288
  }
289
+ } else if (node.type === "JSXElement") {
290
+ handleTrans(code, node, file, tagged, usedKeys);
268
291
  }
269
292
  });
270
293
  return { tagged, usedKeys };
271
294
  }
295
+ function handleTrans(code, node, file, tagged, usedKeys) {
296
+ const opening = node.openingElement;
297
+ const nameNode = opening.name;
298
+ if (nameNode.type !== "JSXIdentifier" || nameNode.name !== "Trans") return;
299
+ const attrs = opening.attributes;
300
+ const idAttr = attrs.find((a) => a.type === "JSXAttribute" && a.name.name === "id");
301
+ if (idAttr) {
302
+ const value = idAttr.value;
303
+ if (value?.type === "StringLiteral") usedKeys.push({ key: value.value, file });
304
+ return;
305
+ }
306
+ if (attrs.length > 0) return;
307
+ const children = node.children;
308
+ if (!children?.length) return;
309
+ const built = buildTransMessage(code, children);
310
+ if (!built || !built.text.trim()) return;
311
+ tagged.push({
312
+ key: stableKey(built.text),
313
+ message: built.text,
314
+ params: built.params,
315
+ start: node.start,
316
+ end: node.end,
317
+ tagStart: nameNode.start,
318
+ tagEnd: nameNode.end,
319
+ file,
320
+ jsx: { name: nameNode.name, components: built.components }
321
+ });
322
+ }
323
+ function buildTransMessage(code, children) {
324
+ const params = [];
325
+ const components = [];
326
+ const takenParams = /* @__PURE__ */ new Map();
327
+ const takenTags = /* @__PURE__ */ new Map();
328
+ function walkChildren(nodes) {
329
+ let text2 = "";
330
+ for (const child of nodes) {
331
+ if (child.type === "JSXText") {
332
+ text2 += escapeText(cleanJsxText(child.value));
333
+ } else if (child.type === "JSXExpressionContainer") {
334
+ const expr = child.expression;
335
+ if (expr.type === "JSXEmptyExpression") continue;
336
+ if (expr.type === "StringLiteral") {
337
+ text2 += escapeText(expr.value);
338
+ continue;
339
+ }
340
+ if (expr.type === "TaggedTemplateExpression") return void 0;
341
+ const source = code.slice(expr.start, expr.end);
342
+ const name = uniqueName(deriveName(expr, params.length), source, takenParams);
343
+ params.push({ name, start: expr.start, end: expr.end });
344
+ text2 += `{${name}}`;
345
+ } else if (child.type === "JSXElement") {
346
+ const opening = child.openingElement;
347
+ const nameNode = opening.name;
348
+ if (nameNode.type !== "JSXIdentifier" || nameNode.name === "Trans") return void 0;
349
+ const source = selfClosedSource(code, opening);
350
+ const tag = uniqueName(nameNode.name.toLowerCase(), source, takenTags);
351
+ if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });
352
+ if (!child.closingElement) {
353
+ text2 += `<${tag}/>`;
354
+ } else {
355
+ const inner = walkChildren(child.children);
356
+ if (inner === void 0) return void 0;
357
+ text2 += `<${tag}>${inner}</${tag}>`;
358
+ }
359
+ } else {
360
+ return void 0;
361
+ }
362
+ }
363
+ return text2;
364
+ }
365
+ const text = walkChildren(children);
366
+ if (text === void 0) return void 0;
367
+ return { text, params, components };
368
+ }
369
+ function cleanJsxText(raw) {
370
+ const lines = raw.split(/\r\n|[\r\n]/);
371
+ let out = "";
372
+ for (let i = 0; i < lines.length; i++) {
373
+ let line = lines[i].replace(/\t/g, " ");
374
+ if (i !== 0) line = line.replace(/^ +/, "");
375
+ if (i !== lines.length - 1) line = line.replace(/ +$/, "");
376
+ if (!line) continue;
377
+ if (out && i !== 0) out += " ";
378
+ out += line;
379
+ }
380
+ return out;
381
+ }
382
+ function selfClosedSource(code, opening) {
383
+ const src = code.slice(opening.start, opening.end);
384
+ return src.endsWith("/>") ? src : `${src.slice(0, -1).trimEnd()} />`;
385
+ }
272
386
  function isTReference(node) {
273
387
  if (node.type === "Identifier") return node.name === "t";
274
388
  if (node.type === "MemberExpression" && !node.computed) {
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((locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`)\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst loaders = {\n${loaders}\n};\nconst loaded = new Set([${src}]);\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\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 const target = loaders[locale] ? locale : locale.split('-')[0];\n if (!loaded.has(target) && loaders[target]) {\n const mod = await loaders[target]();\n v.addMessages(target, mod.default);\n loaded.add(target);\n }\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\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';\nimport { join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport interface VerbalyConfig {\n root?: string;\n dir?: string;\n sourceLocale?: string;\n locales?: string[];\n include?: string[];\n exclude?: string[];\n}\n\nexport interface ResolvedConfig {\n root: string;\n dir: string;\n sourceLocale: string;\n locales: string[];\n include: string[];\n exclude: string[];\n}\n\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\n const root = resolve(config.root ?? process.cwd());\n const dir = resolve(root, config.dir ?? 'locales');\n const sourceLocale = config.sourceLocale ?? 'en';\n\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\n if (existsSync(dir)) {\n for (const file of readdirSync(dir)) {\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\n }\n }\n\n return {\n root,\n dir,\n sourceLocale,\n locales: [...locales],\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\n };\n}\n\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\n const path = join(root, name);\n if (existsSync(path)) {\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\n return mod.default ?? {};\n }\n }\n const jsonPath = join(root, 'verbaly.config.json');\n if (existsSync(jsonPath)) {\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\n }\n return {};\n}\n\n// file config + inline overrides\nexport async function loadConfig(\n root: string,\n overrides: VerbalyConfig = {},\n): Promise<ResolvedConfig> {\n const fileConfig = await loadConfigFile(root);\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\n}\n\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\n return Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as VerbalyConfig;\n}\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';\nimport { stableKey } from './key';\n\nexport interface TaggedParam {\n name: string;\n start: number;\n end: number;\n}\n\nexport interface TaggedMessage {\n key: string;\n message: string;\n params: TaggedParam[];\n start: number;\n end: number;\n tagStart: number;\n tagEnd: number;\n file: string;\n}\n\nexport interface UsedKey {\n key: string;\n file: string;\n}\n\nexport interface Analysis {\n tagged: TaggedMessage[];\n usedKeys: UsedKey[];\n}\n\ninterface AstNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\n\nexport function analyze(code: string, file: string): Analysis {\n const jsx = /\\.[jt]sx$/.test(file);\n const ast = parse(code, {\n sourceType: 'module',\n errorRecovery: true,\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\n });\n\n const tagged: TaggedMessage[] = [];\n const usedKeys: UsedKey[] = [];\n\n walk(ast.program as unknown as AstNode, (node) => {\n if (node.type === 'TaggedTemplateExpression') {\n const tag = node.tag as AstNode;\n if (!isTReference(tag)) return;\n const quasi = node.quasi as AstNode;\n const message = buildMessage(code, quasi);\n if (!message) return;\n tagged.push({\n key: stableKey(message.text),\n message: message.text,\n params: message.params,\n start: node.start,\n end: node.end,\n tagStart: tag.start,\n tagEnd: tag.end,\n file,\n });\n } else if (node.type === 'CallExpression') {\n const callee = node.callee as AstNode;\n if (!isTReference(callee)) return;\n const args = node.arguments as AstNode[];\n const first = args[0];\n if (first?.type === 'StringLiteral') {\n usedKeys.push({ key: first.value as string, file });\n }\n }\n });\n\n return { tagged, usedKeys };\n}\n\nfunction isTReference(node: AstNode): boolean {\n if (node.type === 'Identifier') return node.name === 't';\n if (node.type === 'MemberExpression' && !node.computed) {\n const prop = node.property as AstNode;\n return prop.type === 'Identifier' && prop.name === 't';\n }\n return false;\n}\n\ninterface BuiltMessage {\n text: string;\n params: TaggedParam[];\n}\n\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\n const quasis = quasi.quasis as AstNode[];\n const expressions = quasi.expressions as AstNode[];\n const params: TaggedParam[] = [];\n const taken = new Map<string, string>();\n\n let text = escapeText(cookedValue(quasis[0]));\n\n for (let i = 0; i < expressions.length; i++) {\n const expr = expressions[i];\n if (!expr) return undefined;\n const source = code.slice(expr.start, expr.end);\n const name = uniqueName(deriveName(expr, i), source, taken);\n params.push({ name, start: expr.start, end: expr.end });\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\n }\n return { text, params };\n}\n\nfunction cookedValue(element: AstNode | undefined): string {\n if (!element) return '';\n const value = element.value as { cooked?: string; raw: string };\n return value.cooked ?? value.raw;\n}\n\n// literal braces → escaped\nfunction escapeText(text: string): string {\n return text.replace(/[{}]/g, (m) => m + m);\n}\n\nfunction deriveName(expr: AstNode, index: number): string {\n if (expr.type === 'Identifier') return expr.name as string;\n if (expr.type === 'MemberExpression' && !expr.computed) {\n const prop = expr.property as AstNode;\n if (prop.type === 'Identifier') return prop.name as string;\n }\n return `_${index}`;\n}\n\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\n let name = base;\n let n = 2;\n while (taken.has(name) && taken.get(name) !== source) {\n name = `${base}${n}`;\n n += 1;\n }\n taken.set(name, source);\n return name;\n}\n\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\n visit(node);\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key)) continue;\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isNode(item)) walk(item, visit);\n }\n } else if (isNode(value)) {\n walk(value, visit);\n }\n }\n}\n\nfunction isNode(value: unknown): value is AstNode {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\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","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"],"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((locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`)\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst loaders = {\n${loaders}\n};\nconst loaded = new Set([${src}]);\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\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 const target = loaders[locale] ? locale : locale.split('-')[0];\n if (!loaded.has(target) && loaders[target]) {\n const mod = await loaders[target]();\n v.addMessages(target, mod.default);\n loaded.add(target);\n }\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\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 if (!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: 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: tag.start,\r\n tagEnd: 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 if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type === 'StringLiteral') usedKeys.push({ key: value.value as string, 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 const children = node.children as AstNode[] | undefined;\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\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","text","visit","readFileSync"]}
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@ interface TaggedParam {
5
5
  start: number;
6
6
  end: number;
7
7
  }
8
+ interface TransComponent {
9
+ name: string;
10
+ source: string;
11
+ }
8
12
  interface TaggedMessage {
9
13
  key: string;
10
14
  message: string;
@@ -14,6 +18,10 @@ interface TaggedMessage {
14
18
  tagStart: number;
15
19
  tagEnd: number;
16
20
  file: string;
21
+ jsx?: {
22
+ name: string;
23
+ components: TransComponent[];
24
+ };
17
25
  }
18
26
  interface UsedKey {
19
27
  key: string;
@@ -103,4 +111,4 @@ interface TransformResult {
103
111
  }
104
112
  declare function transformCode(code: string, file: string, analysis?: Analysis): TransformResult | null;
105
113
 
106
- export { type Analysis, type Catalog, type Catalogs, type CheckResult, MessageRegistry, type MissingEntry, type ParamType, type ResolvedConfig, type SyncResult, type TaggedMessage, type TaggedParam, 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 };
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 };
package/dist/index.js CHANGED
@@ -43,10 +43,103 @@ function analyze(code, file) {
43
43
  if (first?.type === "StringLiteral") {
44
44
  usedKeys.push({ key: first.value, file });
45
45
  }
46
+ } else if (node.type === "JSXElement") {
47
+ handleTrans(code, node, file, tagged, usedKeys);
46
48
  }
47
49
  });
48
50
  return { tagged, usedKeys };
49
51
  }
52
+ function handleTrans(code, node, file, tagged, usedKeys) {
53
+ const opening = node.openingElement;
54
+ const nameNode = opening.name;
55
+ if (nameNode.type !== "JSXIdentifier" || nameNode.name !== "Trans") return;
56
+ const attrs = opening.attributes;
57
+ const idAttr = attrs.find((a) => a.type === "JSXAttribute" && a.name.name === "id");
58
+ if (idAttr) {
59
+ const value = idAttr.value;
60
+ if (value?.type === "StringLiteral") usedKeys.push({ key: value.value, file });
61
+ return;
62
+ }
63
+ if (attrs.length > 0) return;
64
+ const children = node.children;
65
+ if (!children?.length) return;
66
+ const built = buildTransMessage(code, children);
67
+ if (!built || !built.text.trim()) return;
68
+ tagged.push({
69
+ key: stableKey(built.text),
70
+ message: built.text,
71
+ params: built.params,
72
+ start: node.start,
73
+ end: node.end,
74
+ tagStart: nameNode.start,
75
+ tagEnd: nameNode.end,
76
+ file,
77
+ jsx: { name: nameNode.name, components: built.components }
78
+ });
79
+ }
80
+ function buildTransMessage(code, children) {
81
+ const params = [];
82
+ const components = [];
83
+ const takenParams = /* @__PURE__ */ new Map();
84
+ const takenTags = /* @__PURE__ */ new Map();
85
+ function walkChildren(nodes) {
86
+ let text2 = "";
87
+ for (const child of nodes) {
88
+ if (child.type === "JSXText") {
89
+ text2 += escapeText(cleanJsxText(child.value));
90
+ } else if (child.type === "JSXExpressionContainer") {
91
+ const expr = child.expression;
92
+ if (expr.type === "JSXEmptyExpression") continue;
93
+ if (expr.type === "StringLiteral") {
94
+ text2 += escapeText(expr.value);
95
+ continue;
96
+ }
97
+ if (expr.type === "TaggedTemplateExpression") return void 0;
98
+ const source = code.slice(expr.start, expr.end);
99
+ const name = uniqueName(deriveName(expr, params.length), source, takenParams);
100
+ params.push({ name, start: expr.start, end: expr.end });
101
+ text2 += `{${name}}`;
102
+ } else if (child.type === "JSXElement") {
103
+ const opening = child.openingElement;
104
+ const nameNode = opening.name;
105
+ if (nameNode.type !== "JSXIdentifier" || nameNode.name === "Trans") return void 0;
106
+ const source = selfClosedSource(code, opening);
107
+ const tag = uniqueName(nameNode.name.toLowerCase(), source, takenTags);
108
+ if (!components.some((c) => c.name === tag)) components.push({ name: tag, source });
109
+ if (!child.closingElement) {
110
+ text2 += `<${tag}/>`;
111
+ } else {
112
+ const inner = walkChildren(child.children);
113
+ if (inner === void 0) return void 0;
114
+ text2 += `<${tag}>${inner}</${tag}>`;
115
+ }
116
+ } else {
117
+ return void 0;
118
+ }
119
+ }
120
+ return text2;
121
+ }
122
+ const text = walkChildren(children);
123
+ if (text === void 0) return void 0;
124
+ return { text, params, components };
125
+ }
126
+ function cleanJsxText(raw) {
127
+ const lines = raw.split(/\r\n|[\r\n]/);
128
+ let out = "";
129
+ for (let i = 0; i < lines.length; i++) {
130
+ let line = lines[i].replace(/\t/g, " ");
131
+ if (i !== 0) line = line.replace(/^ +/, "");
132
+ if (i !== lines.length - 1) line = line.replace(/ +$/, "");
133
+ if (!line) continue;
134
+ if (out && i !== 0) out += " ";
135
+ out += line;
136
+ }
137
+ return out;
138
+ }
139
+ function selfClosedSource(code, opening) {
140
+ const src = code.slice(opening.start, opening.end);
141
+ return src.endsWith("/>") ? src : `${src.slice(0, -1).trimEnd()} />`;
142
+ }
50
143
  function isTReference(node) {
51
144
  if (node.type === "Identifier") return node.name === "t";
52
145
  if (node.type === "MemberExpression" && !node.computed) {
@@ -356,12 +449,33 @@ async function loadConfigFile(root) {
356
449
  return mod.default ?? {};
357
450
  }
358
451
  }
452
+ for (const name of ["verbaly.config.ts", "verbaly.config.mts"]) {
453
+ const path = join3(root, name);
454
+ if (existsSync(path)) return loadTsConfig(path);
455
+ }
359
456
  const jsonPath = join3(root, "verbaly.config.json");
360
457
  if (existsSync(jsonPath)) {
361
458
  return JSON.parse(readFileSync2(jsonPath, "utf8"));
362
459
  }
363
460
  return {};
364
461
  }
462
+ async function loadTsConfig(path) {
463
+ try {
464
+ const { bundleRequire } = await import("bundle-require");
465
+ const { mod } = await bundleRequire({ filepath: path });
466
+ return mod.default ?? {};
467
+ } catch (error) {
468
+ if (isModuleNotFound(error, "esbuild")) {
469
+ throw new Error(`[verbaly] ${path} needs esbuild \u2014 install it: pnpm add -D esbuild`, {
470
+ cause: error
471
+ });
472
+ }
473
+ throw error;
474
+ }
475
+ }
476
+ function isModuleNotFound(error, name) {
477
+ return error instanceof Error && error.code === "ERR_MODULE_NOT_FOUND" && error.message.includes(name);
478
+ }
365
479
  async function loadConfig(root, overrides = {}) {
366
480
  const fileConfig = await loadConfigFile(root);
367
481
  return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });
@@ -474,10 +588,21 @@ function transformCode(code, file, analysis) {
474
588
  if (tagged.length === 0) return null;
475
589
  const s = new MagicString(code);
476
590
  for (const msg of tagged) {
477
- const tagSource = code.slice(msg.tagStart, msg.tagEnd);
478
591
  const seen = /* @__PURE__ */ new Set();
479
592
  const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));
480
- const args = entries.length ? `, { ${entries.map((p) => `${JSON.stringify(p.name)}: ${code.slice(p.start, p.end)}`).join(", ")} }` : "";
593
+ const pairs = entries.map((p) => `${JSON.stringify(p.name)}: ${code.slice(p.start, p.end)}`).join(", ");
594
+ if (msg.jsx) {
595
+ const values = entries.length ? ` values={{ ${pairs} }}` : "";
596
+ const components = msg.jsx.components.length ? ` components={{ ${msg.jsx.components.map((c) => `${JSON.stringify(c.name)}: ${c.source}`).join(", ")} }}` : "";
597
+ s.overwrite(
598
+ msg.start,
599
+ msg.end,
600
+ `<${msg.jsx.name} id=${JSON.stringify(msg.key)}${values}${components} />`
601
+ );
602
+ continue;
603
+ }
604
+ const tagSource = code.slice(msg.tagStart, msg.tagEnd);
605
+ const args = entries.length ? `, { ${pairs} }` : "";
481
606
  s.overwrite(msg.start, msg.end, `${tagSource}(${JSON.stringify(msg.key)}${args})`);
482
607
  }
483
608
  return { code: s.toString(), map: s.generateMap({ hires: true }) };
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';\nimport { stableKey } from './key';\n\nexport interface TaggedParam {\n name: string;\n start: number;\n end: number;\n}\n\nexport interface TaggedMessage {\n key: string;\n message: string;\n params: TaggedParam[];\n start: number;\n end: number;\n tagStart: number;\n tagEnd: number;\n file: string;\n}\n\nexport interface UsedKey {\n key: string;\n file: string;\n}\n\nexport interface Analysis {\n tagged: TaggedMessage[];\n usedKeys: UsedKey[];\n}\n\ninterface AstNode {\n type: string;\n start: number;\n end: number;\n [key: string]: unknown;\n}\n\nconst SKIP_KEYS = new Set(['loc', 'leadingComments', 'trailingComments', 'innerComments', 'extra']);\n\nexport function analyze(code: string, file: string): Analysis {\n const jsx = /\\.[jt]sx$/.test(file);\n const ast = parse(code, {\n sourceType: 'module',\n errorRecovery: true,\n plugins: jsx ? ['typescript', 'jsx'] : ['typescript'],\n });\n\n const tagged: TaggedMessage[] = [];\n const usedKeys: UsedKey[] = [];\n\n walk(ast.program as unknown as AstNode, (node) => {\n if (node.type === 'TaggedTemplateExpression') {\n const tag = node.tag as AstNode;\n if (!isTReference(tag)) return;\n const quasi = node.quasi as AstNode;\n const message = buildMessage(code, quasi);\n if (!message) return;\n tagged.push({\n key: stableKey(message.text),\n message: message.text,\n params: message.params,\n start: node.start,\n end: node.end,\n tagStart: tag.start,\n tagEnd: tag.end,\n file,\n });\n } else if (node.type === 'CallExpression') {\n const callee = node.callee as AstNode;\n if (!isTReference(callee)) return;\n const args = node.arguments as AstNode[];\n const first = args[0];\n if (first?.type === 'StringLiteral') {\n usedKeys.push({ key: first.value as string, file });\n }\n }\n });\n\n return { tagged, usedKeys };\n}\n\nfunction isTReference(node: AstNode): boolean {\n if (node.type === 'Identifier') return node.name === 't';\n if (node.type === 'MemberExpression' && !node.computed) {\n const prop = node.property as AstNode;\n return prop.type === 'Identifier' && prop.name === 't';\n }\n return false;\n}\n\ninterface BuiltMessage {\n text: string;\n params: TaggedParam[];\n}\n\nfunction buildMessage(code: string, quasi: AstNode): BuiltMessage | undefined {\n const quasis = quasi.quasis as AstNode[];\n const expressions = quasi.expressions as AstNode[];\n const params: TaggedParam[] = [];\n const taken = new Map<string, string>();\n\n let text = escapeText(cookedValue(quasis[0]));\n\n for (let i = 0; i < expressions.length; i++) {\n const expr = expressions[i];\n if (!expr) return undefined;\n const source = code.slice(expr.start, expr.end);\n const name = uniqueName(deriveName(expr, i), source, taken);\n params.push({ name, start: expr.start, end: expr.end });\n text += `{${name}}` + escapeText(cookedValue(quasis[i + 1]));\n }\n return { text, params };\n}\n\nfunction cookedValue(element: AstNode | undefined): string {\n if (!element) return '';\n const value = element.value as { cooked?: string; raw: string };\n return value.cooked ?? value.raw;\n}\n\n// literal braces → escaped\nfunction escapeText(text: string): string {\n return text.replace(/[{}]/g, (m) => m + m);\n}\n\nfunction deriveName(expr: AstNode, index: number): string {\n if (expr.type === 'Identifier') return expr.name as string;\n if (expr.type === 'MemberExpression' && !expr.computed) {\n const prop = expr.property as AstNode;\n if (prop.type === 'Identifier') return prop.name as string;\n }\n return `_${index}`;\n}\n\nfunction uniqueName(base: string, source: string, taken: Map<string, string>): string {\n let name = base;\n let n = 2;\n while (taken.has(name) && taken.get(name) !== source) {\n name = `${base}${n}`;\n n += 1;\n }\n taken.set(name, source);\n return name;\n}\n\nfunction walk(node: AstNode, visit: (node: AstNode) => void): void {\n visit(node);\n for (const [key, value] of Object.entries(node)) {\n if (SKIP_KEYS.has(key)) continue;\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isNode(item)) walk(item, visit);\n }\n } else if (isNode(value)) {\n walk(value, visit);\n }\n }\n}\n\nfunction isNode(value: unknown): value is AstNode {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { type?: unknown }).type === 'string'\n );\n}\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((locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`)\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst loaders = {\n${loaders}\n};\nconst loaded = new Set([${src}]);\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\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 const target = loaders[locale] ? locale : locale.split('-')[0];\n if (!loaded.has(target) && loaders[target]) {\n const mod = await loaders[target]();\n v.addMessages(target, mod.default);\n loaded.add(target);\n }\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\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';\nimport { join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nexport interface VerbalyConfig {\n root?: string;\n dir?: string;\n sourceLocale?: string;\n locales?: string[];\n include?: string[];\n exclude?: string[];\n}\n\nexport interface ResolvedConfig {\n root: string;\n dir: string;\n sourceLocale: string;\n locales: string[];\n include: string[];\n exclude: string[];\n}\n\nexport function resolveConfig(config: VerbalyConfig = {}): ResolvedConfig {\n const root = resolve(config.root ?? process.cwd());\n const dir = resolve(root, config.dir ?? 'locales');\n const sourceLocale = config.sourceLocale ?? 'en';\n\n const locales = new Set<string>([sourceLocale, ...(config.locales ?? [])]);\n if (existsSync(dir)) {\n for (const file of readdirSync(dir)) {\n if (file.endsWith('.json')) locales.add(file.slice(0, -5));\n }\n }\n\n return {\n root,\n dir,\n sourceLocale,\n locales: [...locales],\n include: config.include ?? ['src/**/*.{js,jsx,ts,tsx,mjs,mts}'],\n exclude: config.exclude ?? ['**/node_modules/**', '**/dist/**'],\n };\n}\n\nexport async function loadConfigFile(root: string): Promise<VerbalyConfig> {\n for (const name of ['verbaly.config.js', 'verbaly.config.mjs']) {\n const path = join(root, name);\n if (existsSync(path)) {\n const mod = (await import(pathToFileURL(path).href)) as { default?: VerbalyConfig };\n return mod.default ?? {};\n }\n }\n const jsonPath = join(root, 'verbaly.config.json');\n if (existsSync(jsonPath)) {\n return JSON.parse(readFileSync(jsonPath, 'utf8')) as VerbalyConfig;\n }\n return {};\n}\n\n// file config + inline overrides\nexport async function loadConfig(\n root: string,\n overrides: VerbalyConfig = {},\n): Promise<ResolvedConfig> {\n const fileConfig = await loadConfigFile(root);\n return resolveConfig({ ...fileConfig, ...definedOnly(overrides), root });\n}\n\nfunction definedOnly(config: VerbalyConfig): VerbalyConfig {\n return Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as VerbalyConfig;\n}\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';\nimport { analyze, type Analysis } from './analyze';\n\nexport interface TransformResult {\n code: string;\n map: ReturnType<MagicString['generateMap']>;\n}\n\nexport function transformCode(\n code: string,\n file: string,\n analysis?: Analysis,\n): TransformResult | null {\n const { tagged } = analysis ?? analyze(code, file);\n if (tagged.length === 0) return null;\n\n const s = new MagicString(code);\n for (const msg of tagged) {\n const tagSource = code.slice(msg.tagStart, msg.tagEnd);\n const seen = new Set<string>();\n const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));\n const args = entries.length\n ? `, { ${entries\n .map((p) => `${JSON.stringify(p.name)}: ${code.slice(p.start, p.end)}`)\n .join(', ')} }`\n : '';\n s.overwrite(msg.start, msg.end, `${tagSource}(${JSON.stringify(msg.key)}${args})`);\n }\n return { code: s.toString(), map: s.generateMap({ hires: true }) };\n}\n"],"mappings":"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","names":["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/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 if (!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: 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: tag.start,\r\n tagEnd: 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 if (idAttr) {\r\n const value = idAttr.value as AstNode | null;\r\n if (value?.type === 'StringLiteral') usedKeys.push({ key: value.value as string, 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 const children = node.children as AstNode[] | undefined;\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\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((locale) => ` ${JSON.stringify(locale)}: () => import('${VIRTUAL_ID}/locale/${locale}'),`)\n .join('\\n');\n\n return `import { createVerbaly } from 'verbaly';\nimport source from '${VIRTUAL_ID}/locale/${cfg.sourceLocale}';\n\nconst loaders = {\n${loaders}\n};\nconst loaded = new Set([${src}]);\n\nconst v = createVerbaly({\n locale: ${src},\n fallback: ${src},\n messages: { [${src}]: source },\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 const target = loaders[locale] ? locale : locale.split('-')[0];\n if (!loaded.has(target) && loaders[target]) {\n const mod = await loaders[target]();\n v.addMessages(target, mod.default);\n loaded.add(target);\n }\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\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":["text","visit","writeFileSync","join","parse","writeFileSync","join","readFileSync","join","readFileSync","readFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/compiler",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Message extraction and type-safe codegen for Verbaly.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -43,9 +43,21 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@babel/parser": "^8.0.0",
46
+ "bundle-require": "^5.1.0",
46
47
  "magic-string": "^0.30.21",
47
48
  "tinyglobby": "^0.2.17",
48
- "verbaly": "0.5.0"
49
+ "verbaly": "0.6.0"
50
+ },
51
+ "peerDependencies": {
52
+ "esbuild": ">=0.18"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "esbuild": {
56
+ "optional": true
57
+ }
58
+ },
59
+ "devDependencies": {
60
+ "esbuild": "^0.28.1"
49
61
  },
50
62
  "scripts": {
51
63
  "build": "tsup",