@verbaly/compiler 0.20.0 → 0.21.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/dist/index.d.ts CHANGED
@@ -148,17 +148,31 @@ interface RuntimeModuleOptions {
148
148
  }
149
149
  declare function generateRuntimeModule(cfg: ResolvedConfig, options?: RuntimeModuleOptions): string;
150
150
  declare function generateLocaleModule(catalog: Catalog): string;
151
- declare function generateDts(entries: Map<string, string>): string;
152
- declare function writeDts(cfg: ResolvedConfig, entries: Map<string, string>): void;
151
+ declare function generateDts(catalog: Catalog): string;
152
+ declare function writeDts(cfg: ResolvedConfig, catalog: Catalog): void;
153
+ //#endregion
154
+ //#region src/transform.d.ts
155
+ interface TransformResult {
156
+ code: string;
157
+ map: ReturnType<MagicString['generateMap']>;
158
+ }
159
+ declare function transformCode(code: string, file: string, analysis?: Analysis): TransformResult | null;
153
160
  //#endregion
154
161
  //#region src/plugin.d.ts
162
+ interface PluginOptions extends VerbalyConfig {
163
+ failOnMissing?: boolean;
164
+ }
155
165
  declare const RESOLVED_VIRTUAL_ID: string;
156
166
  declare const LOCALE_MODULE_PREFIX = "\0virtual:verbaly/locale/";
157
167
  declare const SOURCE_FILE_RE: RegExp;
158
168
  declare function resolveVirtualId(id: string): string | undefined;
159
169
  declare function loadVirtualModule(id: string, cfg: ResolvedConfig, catalogs: Catalogs): string | undefined;
160
170
  declare function isTransformTarget(id: string): boolean;
161
- declare function runBuildGate(cfg: ResolvedConfig, registry: MessageRegistry): void;
171
+ declare function transformSource(code: string, id: string, registry: MessageRegistry): {
172
+ analysis: Analysis;
173
+ result: TransformResult | null;
174
+ };
175
+ declare function runBuildGate(cfg: ResolvedConfig, registry: MessageRegistry, failOnMissing?: boolean): void;
162
176
  //#endregion
163
177
  //#region src/doctor.d.ts
164
178
  interface DoctorEntry {
@@ -295,12 +309,5 @@ interface RenderSiteResult {
295
309
  }
296
310
  declare function renderSite(cfg: ResolvedConfig, options?: RenderSiteOptions): Promise<RenderSiteResult>;
297
311
  //#endregion
298
- //#region src/transform.d.ts
299
- interface TransformResult {
300
- code: string;
301
- map: ReturnType<MagicString['generateMap']>;
302
- }
303
- declare function transformCode(code: string, file: string, analysis?: Analysis): TransformResult | null;
304
- //#endregion
305
- export { type Alternate, type Analysis, type AnalyzeOptions, type Catalog, type Catalogs, type CheckResult, type ClaudeProviderOptions, type DoctorEntry, type DoctorResult, type ExchangeFormat, type ExportOptions, type ExportResult, type ExportedFile, type ImportOptions, type ImportResult, type InitOptions, type InitResult, LOCALE_MODULE_PREFIX, MessageRegistry, type MissingEntry, PSEUDO_LOCALE, type ParamType, RESOLVED_VIRTUAL_ID, type RenderConfig, type RenderHtmlOptions, type RenderHtmlResult, type RenderSiteOptions, type RenderSiteResult, type ResolvedConfig, type RuntimeModuleOptions, SFC_FILE_RE, SOURCE_FILE_RE, type SyncResult, type TaggedMessage, type TaggedParam, type TransComponent, type TransformResult, type TranslateConfig, type TranslateOptions, type TranslateProvider, type TranslateRequest, type TranslateResult, type UnknownEntry, type UsedKey, VIRTUAL_ID, type VerbalyConfig, analyze, analyzeFile, analyzeSfc, catalogPath, check, claudeProvider, collectParams, detectBundler, doctor, exportCatalogs, extractProject, findConfigFile, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, importCatalogs, init, isTransformTarget, loadCatalogs, loadConfig, loadConfigFile, loadVirtualModule, parseExchangeFile, pruneCatalogs, pseudoCatalogs, pseudoLocalize, readCatalog, renderHtml, renderParamType, renderSite, resolveConfig, resolveVirtualId, runBuildGate, serializeCatalog, stableKey, structureMatches, syncCatalogs, targetLocales, transformCode, translateCatalogs, writeCatalog, writeDts };
312
+ export { type Alternate, type Analysis, type AnalyzeOptions, type Catalog, type Catalogs, type CheckResult, type ClaudeProviderOptions, type DoctorEntry, type DoctorResult, type ExchangeFormat, type ExportOptions, type ExportResult, type ExportedFile, type ImportOptions, type ImportResult, type InitOptions, type InitResult, LOCALE_MODULE_PREFIX, MessageRegistry, type MissingEntry, PSEUDO_LOCALE, type ParamType, type PluginOptions, RESOLVED_VIRTUAL_ID, type RenderConfig, type RenderHtmlOptions, type RenderHtmlResult, type RenderSiteOptions, type RenderSiteResult, type ResolvedConfig, type RuntimeModuleOptions, SFC_FILE_RE, SOURCE_FILE_RE, type SyncResult, type TaggedMessage, type TaggedParam, type TransComponent, type TransformResult, type TranslateConfig, type TranslateOptions, type TranslateProvider, type TranslateRequest, type TranslateResult, type UnknownEntry, type UsedKey, VIRTUAL_ID, type VerbalyConfig, analyze, analyzeFile, analyzeSfc, catalogPath, check, claudeProvider, collectParams, detectBundler, doctor, exportCatalogs, extractProject, findConfigFile, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, importCatalogs, init, isTransformTarget, loadCatalogs, loadConfig, loadConfigFile, loadVirtualModule, parseExchangeFile, pruneCatalogs, pseudoCatalogs, pseudoLocalize, readCatalog, renderHtml, renderParamType, renderSite, resolveConfig, resolveVirtualId, runBuildGate, serializeCatalog, stableKey, structureMatches, syncCatalogs, targetLocales, transformCode, transformSource, translateCatalogs, writeCatalog, writeDts };
306
313
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync
4
4
  import { basename, dirname, join, relative, resolve } from "node:path";
5
5
  import { RICH_TAGS, createVerbaly, parse as parse$1, parseTags, safeHref } from "verbaly";
6
6
  import { pathToFileURL } from "node:url";
7
- import { glob } from "tinyglobby";
8
7
  import MagicString from "magic-string";
8
+ import { glob } from "tinyglobby";
9
9
  //#region src/key.ts
10
10
  function stableKey(message) {
11
11
  return createHash("sha256").update(message).digest("base64url").slice(0, 8);
@@ -69,6 +69,20 @@ function handleTrans(code, node, file, tagged, usedKeys, names) {
69
69
  const attrs = opening.attributes;
70
70
  const idAttr = attrs.find((a) => a.type === "JSXAttribute" && a.name.name === "id");
71
71
  const children = node.children;
72
+ const push = (key, built) => tagged.push({
73
+ key,
74
+ message: built.text,
75
+ params: built.params,
76
+ start: node.start,
77
+ end: node.end,
78
+ tagStart: nameNode.start,
79
+ tagEnd: nameNode.end,
80
+ file,
81
+ jsx: {
82
+ name: nameNode.name,
83
+ components: built.components
84
+ }
85
+ });
72
86
  if (idAttr) {
73
87
  const value = idAttr.value;
74
88
  if (value?.type !== "StringLiteral") return;
@@ -76,20 +90,7 @@ function handleTrans(code, node, file, tagged, usedKeys, names) {
76
90
  if (attrs.length === 1 && children?.length) {
77
91
  const built = buildTransMessage(code, children, names);
78
92
  if (built?.text.trim()) {
79
- tagged.push({
80
- key: id,
81
- message: built.text,
82
- params: built.params,
83
- start: node.start,
84
- end: node.end,
85
- tagStart: nameNode.start,
86
- tagEnd: nameNode.end,
87
- file,
88
- jsx: {
89
- name: nameNode.name,
90
- components: built.components
91
- }
92
- });
93
+ push(id, built);
93
94
  return;
94
95
  }
95
96
  }
@@ -103,20 +104,7 @@ function handleTrans(code, node, file, tagged, usedKeys, names) {
103
104
  if (!children?.length) return;
104
105
  const built = buildTransMessage(code, children, names);
105
106
  if (!built || !built.text.trim()) return;
106
- tagged.push({
107
- key: stableKey(built.text),
108
- message: built.text,
109
- params: built.params,
110
- start: node.start,
111
- end: node.end,
112
- tagStart: nameNode.start,
113
- tagEnd: nameNode.end,
114
- file,
115
- jsx: {
116
- name: nameNode.name,
117
- components: built.components
118
- }
119
- });
107
+ push(stableKey(built.text), built);
120
108
  }
121
109
  function explicitId(tag, names) {
122
110
  if (tag.type !== "CallExpression") return void 0;
@@ -272,8 +260,9 @@ function uniqueName(base, source, taken) {
272
260
  }
273
261
  function walk(node, visit) {
274
262
  visit(node);
275
- for (const [key, value] of Object.entries(node)) {
263
+ for (const key in node) {
276
264
  if (SKIP_KEYS.has(key)) continue;
265
+ const value = node[key];
277
266
  if (Array.isArray(value)) {
278
267
  for (const item of value) if (isNode(item)) walk(item, visit);
279
268
  } else if (isNode(value)) walk(value, visit);
@@ -454,13 +443,13 @@ function formatCheckResult(result) {
454
443
  if (result.missing.length > 0) {
455
444
  lines.push("missing translations:");
456
445
  for (const entry of result.missing) {
457
- const hint = entry.source ? ` "${truncate(entry.source, 40)}"` : "";
446
+ const hint = entry.source ? `: "${truncate(entry.source, 40)}"` : "";
458
447
  lines.push(` [${entry.locale}] ${entry.key}${hint}`);
459
448
  }
460
449
  }
461
450
  if (result.unknown.length > 0) {
462
451
  lines.push("unknown keys (not in any catalog):");
463
- for (const entry of result.unknown) lines.push(` ${entry.key} used in ${entry.files.join(", ")}`);
452
+ for (const entry of result.unknown) lines.push(` ${entry.key} (used in ${entry.files.join(", ")})`);
464
453
  }
465
454
  return lines.join("\n");
466
455
  }
@@ -507,11 +496,15 @@ function visit(nodes, out) {
507
496
  }
508
497
  function renderParamType(types) {
509
498
  if (types.has("unknown") || types.size === 0) return "unknown";
510
- const parts = [];
511
- if (types.has("number")) parts.push("number");
512
- if (types.has("string")) parts.push("string");
513
- if (types.has("date")) parts.push("Date | number | string");
514
- return parts.join(" | ");
499
+ const members = /* @__PURE__ */ new Set();
500
+ if (types.has("number")) members.add("number");
501
+ if (types.has("string")) members.add("string");
502
+ if (types.has("date")) for (const m of [
503
+ "Date",
504
+ "number",
505
+ "string"
506
+ ]) members.add(m);
507
+ return [...members].join(" | ");
515
508
  }
516
509
  //#endregion
517
510
  //#region src/codegen.ts
@@ -578,9 +571,9 @@ ${options.extraExports ?? ""}`;
578
571
  function generateLocaleModule(catalog) {
579
572
  return `export default ${JSON.stringify(catalog)};\n`;
580
573
  }
581
- function generateDts(entries) {
574
+ function generateDts(catalog) {
582
575
  const lines = [];
583
- for (const [key, message] of [...entries.entries()].sort(([a], [b]) => a.localeCompare(b))) {
576
+ for (const [key, message] of Object.entries(catalog).sort(([a], [b]) => a.localeCompare(b))) {
584
577
  const params = collectParams(message);
585
578
  if (params.size === 0) lines.push(` ${JSON.stringify(key)}: never;`);
586
579
  else {
@@ -629,8 +622,13 @@ declare module 'virtual:verbaly/locale/*' {
629
622
  }
630
623
  `;
631
624
  }
632
- function writeDts(cfg, entries) {
633
- writeFileSync(join(cfg.root, "verbaly.d.ts"), generateDts(entries));
625
+ function writeDts(cfg, catalog) {
626
+ const file = join(cfg.root, "verbaly.d.ts");
627
+ const content = generateDts(catalog);
628
+ try {
629
+ if (readFileSync(file, "utf8") === content) return;
630
+ } catch {}
631
+ writeFileSync(file, content);
634
632
  }
635
633
  //#endregion
636
634
  //#region src/pseudo.ts
@@ -801,7 +799,7 @@ async function loadTsConfig(path) {
801
799
  const { mod } = await bundleRequire({ filepath: path });
802
800
  return mod.default ?? {};
803
801
  } catch (error) {
804
- if (isModuleNotFound(error, "esbuild")) throw new Error(`[verbaly] ${path} needs esbuild install it: pnpm add -D esbuild`, { cause: error });
802
+ if (isModuleNotFound(error, "esbuild")) throw new Error(`[verbaly] ${path} needs esbuild: install it with pnpm add -D esbuild`, { cause: error });
805
803
  throw error;
806
804
  }
807
805
  }
@@ -822,6 +820,35 @@ function definedOnly(config) {
822
820
  return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0));
823
821
  }
824
822
  //#endregion
823
+ //#region src/transform.ts
824
+ function quote(text, single) {
825
+ if (!single) return JSON.stringify(text);
826
+ return `'${text.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
827
+ }
828
+ function transformCode(code, file, analysis) {
829
+ const { tagged } = analysis ?? analyzeFile(code, file);
830
+ if (tagged.length === 0) return null;
831
+ const s = new MagicString(code);
832
+ for (const msg of tagged) {
833
+ const seen = /* @__PURE__ */ new Set();
834
+ const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));
835
+ const pairs = entries.map((p) => `${quote(p.name, msg.singleQuote)}: ${code.slice(p.start, p.end)}`).join(", ");
836
+ if (msg.jsx) {
837
+ const values = entries.length ? ` values={{ ${pairs} }}` : "";
838
+ const components = msg.jsx.components.length ? ` components={{ ${msg.jsx.components.map((c) => `${JSON.stringify(c.name)}: ${c.source}`).join(", ")} }}` : "";
839
+ s.overwrite(msg.start, msg.end, `<${msg.jsx.name} id=${JSON.stringify(msg.key)}${values}${components} />`);
840
+ continue;
841
+ }
842
+ const tagSource = code.slice(msg.tagStart, msg.tagEnd);
843
+ const args = entries.length ? `, { ${pairs} }` : "";
844
+ s.overwrite(msg.start, msg.end, `${tagSource}(${quote(msg.key, msg.singleQuote)}${args})`);
845
+ }
846
+ return {
847
+ code: s.toString(),
848
+ map: s.generateMap({ hires: true })
849
+ };
850
+ }
851
+ //#endregion
825
852
  //#region src/plugin.ts
826
853
  const RESOLVED_VIRTUAL_ID = "\0virtual:verbaly";
827
854
  const LOCALE_MODULE_PREFIX = `${RESOLVED_VIRTUAL_ID}/locale/`;
@@ -836,7 +863,16 @@ function loadVirtualModule(id, cfg, catalogs) {
836
863
  function isTransformTarget(id) {
837
864
  return SOURCE_FILE_RE.test(id) && !id.includes("node_modules") && !id.startsWith("\0");
838
865
  }
839
- function runBuildGate(cfg, registry) {
866
+ function transformSource(code, id, registry) {
867
+ const analysis = analyzeFile(code, id);
868
+ registry.update(id, analysis);
869
+ return {
870
+ analysis,
871
+ result: transformCode(code, id, analysis) ?? null
872
+ };
873
+ }
874
+ function runBuildGate(cfg, registry, failOnMissing) {
875
+ if (failOnMissing === false) return;
840
876
  const result = check(cfg, loadCatalogs(cfg), registry);
841
877
  if (!result.ok) throw new Error(`[verbaly] build blocked\n${formatCheckResult(result)}\nRun \`npx verbaly extract\` and fill the missing translations.`);
842
878
  }
@@ -855,7 +891,7 @@ var MessageRegistry = class {
855
891
  for (const analysis of this.files.values()) for (const msg of analysis.tagged) {
856
892
  const existing = out.get(msg.key);
857
893
  if (existing) {
858
- if (existing.message !== msg.message) console.warn(`[verbaly] key collision "${msg.key}": ${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)} second dropped.`);
894
+ if (existing.message !== msg.message) console.warn(`[verbaly] key collision "${msg.key}": ${JSON.stringify(existing.message)} vs ${JSON.stringify(msg.message)}, second dropped.`);
859
895
  continue;
860
896
  }
861
897
  out.set(msg.key, msg);
@@ -987,27 +1023,27 @@ function init(options = {}) {
987
1023
  const PREVIEW = 5;
988
1024
  async function doctor(cfg) {
989
1025
  const entries = [];
990
- const ok = (check, message) => entries.push({
1026
+ const ok = (name, message) => entries.push({
991
1027
  level: "ok",
992
- check,
1028
+ check: name,
993
1029
  message
994
1030
  });
995
- const warn = (check, message, fix) => entries.push({
1031
+ const warn = (name, message, fix) => entries.push({
996
1032
  level: "warn",
997
- check,
1033
+ check: name,
998
1034
  message,
999
1035
  fix
1000
1036
  });
1001
- const error = (check, message, fix) => entries.push({
1037
+ const error = (name, message, fix) => entries.push({
1002
1038
  level: "error",
1003
- check,
1039
+ check: name,
1004
1040
  message,
1005
1041
  fix
1006
1042
  });
1007
1043
  const rel = (path) => relative(cfg.root, path).replaceAll("\\", "/");
1008
1044
  const configFile = findConfigFile(cfg.root);
1009
1045
  if (configFile) ok("config", `${configFile} found`);
1010
- else warn("config", "no config file running on defaults", "run `npx verbaly init`");
1046
+ else warn("config", "no config file, running on defaults", "run `npx verbaly init`");
1011
1047
  const catalogs = {};
1012
1048
  let catalogsHealthy = true;
1013
1049
  if (!existsSync(cfg.dir)) {
@@ -1040,14 +1076,14 @@ async function doctor(cfg) {
1040
1076
  const deps = readDeps(cfg.root);
1041
1077
  const bundler = detectBundler(cfg.root);
1042
1078
  const wired = deps["@verbaly/vite"] || deps["@verbaly/unplugin"];
1043
- if (!bundler) ok("plugin", "no bundler detected CLI flow (extract/check) applies");
1079
+ if (!bundler) ok("plugin", "no bundler detected, the CLI flow (extract/check) applies");
1044
1080
  else if (wired) ok("plugin", `${deps["@verbaly/vite"] ? "@verbaly/vite" : "@verbaly/unplugin"} installed for ${bundler}`);
1045
1081
  else if (bundler === "vite") warn("plugin", "vite detected but @verbaly/vite is not installed", "pnpm add -D @verbaly/vite and add verbaly() to the plugins in vite.config");
1046
1082
  else warn("plugin", `${bundler} detected but @verbaly/unplugin is not installed`, `pnpm add -D @verbaly/unplugin and add the verbaly ${bundler} plugin to your build config`);
1047
1083
  if (source) {
1048
1084
  const dtsPath = join(cfg.root, "verbaly.d.ts");
1049
1085
  if (!existsSync(dtsPath)) warn("types", "verbaly.d.ts has not been generated", "run `npx verbaly extract`");
1050
- else if (readFileSync(dtsPath, "utf8") !== generateDts(new Map(Object.entries(source)))) warn("types", "verbaly.d.ts is stale", "run `npx verbaly extract`");
1086
+ else if (readFileSync(dtsPath, "utf8") !== generateDts(source)) warn("types", "verbaly.d.ts is stale", "run `npx verbaly extract`");
1051
1087
  else ok("types", "verbaly.d.ts is up to date");
1052
1088
  }
1053
1089
  const registry = await extractProject(cfg);
@@ -1060,10 +1096,10 @@ async function doctor(cfg) {
1060
1096
  }
1061
1097
  if (catalogsHealthy) {
1062
1098
  const result = check(cfg, catalogs, registry);
1063
- if (result.unknown.length > 0) error("keys", `${result.unknown.length} unknown ${result.unknown.length === 1 ? "key" : "keys"} used in code (${preview(result.unknown.map((u) => u.key))})`, "fix the key or add it to the catalogs `npx verbaly check` for details");
1099
+ if (result.unknown.length > 0) error("keys", `${result.unknown.length} unknown ${result.unknown.length === 1 ? "key" : "keys"} used in code (${preview(result.unknown.map((u) => u.key))})`, "fix the key or add it to the catalogs (`npx verbaly check` for details)");
1064
1100
  if (result.missing.length > 0) {
1065
1101
  const locales = [...new Set(result.missing.map((m) => m.locale))];
1066
- warn("translations", `${result.missing.length} missing ${result.missing.length === 1 ? "translation" : "translations"} (${locales.join(", ")})`, "run `npx verbaly translate` or fill the catalogs `npx verbaly check` for details");
1102
+ warn("translations", `${result.missing.length} missing ${result.missing.length === 1 ? "translation" : "translations"} (${locales.join(", ")})`, "run `npx verbaly translate` or fill the catalogs (`npx verbaly check` for details)");
1067
1103
  }
1068
1104
  if (result.ok) ok("translations", "all translations complete");
1069
1105
  }
@@ -1155,19 +1191,20 @@ function exportCatalogs(cfg, catalogs, options = {}) {
1155
1191
  mkdirSync(dir, { recursive: true });
1156
1192
  for (const locale of targets) {
1157
1193
  const catalog = catalogs[locale] ?? {};
1158
- let entries = Object.keys(source).filter((key) => source[key]).sort().map((key) => ({
1194
+ const all = Object.keys(source).filter((key) => source[key]).sort().map((key) => ({
1159
1195
  key,
1160
1196
  source: source[key],
1161
1197
  target: catalog[key] ?? ""
1162
1198
  }));
1163
- if (options.missing) entries = entries.filter((entry) => !entry.target);
1199
+ const untranslated = all.filter((entry) => !entry.target);
1200
+ const entries = options.missing ? untranslated : all;
1164
1201
  const path = join(dir, `${locale}.${format === "csv" ? "csv" : "xlf"}`);
1165
1202
  writeFileSync(path, format === "csv" ? toCsv(entries) : toXliff(cfg.sourceLocale, locale, entries));
1166
1203
  files.push({
1167
1204
  locale,
1168
1205
  path,
1169
1206
  total: entries.length,
1170
- untranslated: entries.filter((entry) => !entry.target).length
1207
+ untranslated: untranslated.length
1171
1208
  });
1172
1209
  }
1173
1210
  return {
@@ -1187,8 +1224,8 @@ function importCatalogs(cfg, catalogs, files, options = {}) {
1187
1224
  for (const file of files) {
1188
1225
  const parsed = parseExchangeFile(file, options.locale);
1189
1226
  const locale = parsed.locale;
1190
- if (!/^[a-zA-Z]{2,3}([-_][a-zA-Z0-9]+)*$/.test(locale)) throw new Error(`[verbaly] ${file}: "${locale}" doesn't look like a locale pass --locale <id>.`);
1191
- if (locale === cfg.sourceLocale) throw new Error(`[verbaly] ${file} targets the source locale "${locale}" import fills translations, not the source. Pass --locale if the detection is wrong.`);
1227
+ if (!/^[a-zA-Z]{2,3}([-_][a-zA-Z0-9]+)*$/.test(locale)) throw new Error(`[verbaly] ${file}: "${locale}" doesn't look like a locale, pass --locale <id>.`);
1228
+ if (locale === cfg.sourceLocale) throw new Error(`[verbaly] ${file} targets the source locale "${locale}": import fills translations, not the source. Pass --locale if the detection is wrong.`);
1192
1229
  const catalog = catalogs[locale] ??= {};
1193
1230
  for (const [key, text] of Object.entries(parsed.entries)) {
1194
1231
  if (!text.trim()) continue;
@@ -1215,7 +1252,7 @@ function parseExchangeFile(file, localeOverride) {
1215
1252
  if (/\.(xlf|xliff)$/i.test(file)) {
1216
1253
  const parsed = parseXliff(content);
1217
1254
  const locale = localeOverride ?? parsed.locale;
1218
- if (!locale) throw new Error(`[verbaly] ${file} has no trgLang/target-language pass --locale <id>.`);
1255
+ if (!locale) throw new Error(`[verbaly] ${file} has no trgLang/target-language, pass --locale <id>.`);
1219
1256
  return {
1220
1257
  locale,
1221
1258
  entries: parsed.entries
@@ -1225,7 +1262,7 @@ function parseExchangeFile(file, localeOverride) {
1225
1262
  locale: localeOverride ?? basename(file).replace(/\.csv$/i, ""),
1226
1263
  entries: parseCsv(content)
1227
1264
  };
1228
- throw new Error(`[verbaly] ${file}: unsupported format expected .xlf, .xliff or .csv.`);
1265
+ throw new Error(`[verbaly] ${file}: unsupported format, expected .xlf, .xliff or .csv.`);
1229
1266
  }
1230
1267
  function toXliff(sourceLocale, locale, entries) {
1231
1268
  const units = entries.map(({ key, source, target }) => [
@@ -1371,7 +1408,7 @@ async function loadSdk(load = () => import("@anthropic-ai/sdk")) {
1371
1408
  try {
1372
1409
  return (await load()).default;
1373
1410
  } catch (error) {
1374
- if (isModuleNotFound(error, "@anthropic-ai/sdk")) throw new Error("[verbaly] the claude translate provider needs @anthropic-ai/sdk install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY", { cause: error });
1411
+ if (isModuleNotFound(error, "@anthropic-ai/sdk")) throw new Error("[verbaly] the claude translate provider needs @anthropic-ai/sdk: install it as a dev dependency (e.g. `pnpm add -D @anthropic-ai/sdk` / `npm i -D @anthropic-ai/sdk`) and set ANTHROPIC_API_KEY", { cause: error });
1375
1412
  throw error;
1376
1413
  }
1377
1414
  }
@@ -1646,35 +1683,6 @@ function decodeEntities(text) {
1646
1683
  return text.replace(/&quot;/g, "\"").replace(/&#39;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
1647
1684
  }
1648
1685
  //#endregion
1649
- //#region src/transform.ts
1650
- function quote(text, single) {
1651
- if (!single) return JSON.stringify(text);
1652
- return `'${text.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1653
- }
1654
- function transformCode(code, file, analysis) {
1655
- const { tagged } = analysis ?? analyzeFile(code, file);
1656
- if (tagged.length === 0) return null;
1657
- const s = new MagicString(code);
1658
- for (const msg of tagged) {
1659
- const seen = /* @__PURE__ */ new Set();
1660
- const entries = msg.params.filter((p) => !seen.has(p.name) && seen.add(p.name));
1661
- const pairs = entries.map((p) => `${quote(p.name, msg.singleQuote)}: ${code.slice(p.start, p.end)}`).join(", ");
1662
- if (msg.jsx) {
1663
- const values = entries.length ? ` values={{ ${pairs} }}` : "";
1664
- const components = msg.jsx.components.length ? ` components={{ ${msg.jsx.components.map((c) => `${JSON.stringify(c.name)}: ${c.source}`).join(", ")} }}` : "";
1665
- s.overwrite(msg.start, msg.end, `<${msg.jsx.name} id=${JSON.stringify(msg.key)}${values}${components} />`);
1666
- continue;
1667
- }
1668
- const tagSource = code.slice(msg.tagStart, msg.tagEnd);
1669
- const args = entries.length ? `, { ${pairs} }` : "";
1670
- s.overwrite(msg.start, msg.end, `${tagSource}(${quote(msg.key, msg.singleQuote)}${args})`);
1671
- }
1672
- return {
1673
- code: s.toString(),
1674
- map: s.generateMap({ hires: true })
1675
- };
1676
- }
1677
- //#endregion
1678
- export { LOCALE_MODULE_PREFIX, MessageRegistry, PSEUDO_LOCALE, RESOLVED_VIRTUAL_ID, SFC_FILE_RE, SOURCE_FILE_RE, VIRTUAL_ID, analyze, analyzeFile, analyzeSfc, catalogPath, check, claudeProvider, collectParams, detectBundler, doctor, exportCatalogs, extractProject, findConfigFile, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, importCatalogs, init, isTransformTarget, loadCatalogs, loadConfig, loadConfigFile, loadVirtualModule, parseExchangeFile, pruneCatalogs, pseudoCatalogs, pseudoLocalize, readCatalog, renderHtml, renderParamType, renderSite, resolveConfig, resolveVirtualId, runBuildGate, serializeCatalog, stableKey, structureMatches, syncCatalogs, targetLocales, transformCode, translateCatalogs, writeCatalog, writeDts };
1686
+ export { LOCALE_MODULE_PREFIX, MessageRegistry, PSEUDO_LOCALE, RESOLVED_VIRTUAL_ID, SFC_FILE_RE, SOURCE_FILE_RE, VIRTUAL_ID, analyze, analyzeFile, analyzeSfc, catalogPath, check, claudeProvider, collectParams, detectBundler, doctor, exportCatalogs, extractProject, findConfigFile, formatCheckResult, generateDts, generateLocaleModule, generateRuntimeModule, importCatalogs, init, isTransformTarget, loadCatalogs, loadConfig, loadConfigFile, loadVirtualModule, parseExchangeFile, pruneCatalogs, pseudoCatalogs, pseudoLocalize, readCatalog, renderHtml, renderParamType, renderSite, resolveConfig, resolveVirtualId, runBuildGate, serializeCatalog, stableKey, structureMatches, syncCatalogs, targetLocales, transformCode, transformSource, translateCatalogs, writeCatalog, writeDts };
1679
1687
 
1680
1688
  //# sourceMappingURL=index.js.map