@verbaly/vite 0.67.0 → 0.68.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
@@ -33,7 +33,7 @@ export default {
33
33
  import { t, setLocale } from 'virtual:verbaly';
34
34
  ```
35
35
 
36
- - **Live extraction** with debounced catalog writes + HMR, from `.js/.ts/.jsx/.tsx` and `.svelte`/`.vue` files (script and markup).
36
+ - **Live extraction** with debounced catalog writes + HMR, from `.js/.ts/.jsx/.tsx` and `.svelte`/`.vue` files (script and markup). Every write starts from the files on disk, so a checkout or an `extract` run in another terminal is never undone with an older text, and a catalog saved half-typed is reported in one line while the server keeps running. The terminal says what `next dev` says, once each: a hand edit of a text your code owns, a key with two texts, a `t` under another name, a translation older than its source.
37
37
  - **Per-locale code-splitting**: `setLocale` lazy-loads only what's used.
38
38
  - **Build gate**: missing translations stop the build (same as `verbaly check`); `failOnMissing: false` opts out.
39
39
 
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
- import { LOCALE_MODULE_PREFIX, MessageRegistry, RESOLVED_VIRTUAL_ID, collisionEntries, createSourceFilter, extractProject, formatCollision, isTransformTarget, loadCatalogs, loadConfig, loadVirtualModule, resolveVirtualId, runBuildGate, syncCatalogs, transformSource, updateState, writeCatalog, writeDts } from "@verbaly/compiler";
1
+ import { LOCALE_MODULE_PREFIX, MessageRegistry, RESOLVED_VIRTUAL_ID, createDevReporter, createSourceFilter, extractProject, formatCliError, isTransformTarget, loadCatalogs, loadConfig, loadVirtualModule, outdatedTranslations, resolveVirtualId, runBuildGate, syncCatalogs, syncProject, transformSource, writeDts } from "@verbaly/compiler";
2
2
  import { readFileSync } from "node:fs";
3
- import { relative } from "node:path";
4
3
  //#region src/index.ts
5
4
  function safeRead(file) {
6
5
  try {
@@ -9,12 +8,6 @@ function safeRead(file) {
9
8
  return;
10
9
  }
11
10
  }
12
- const said = /* @__PURE__ */ new Set();
13
- function once(id, line) {
14
- if (said.has(id)) return;
15
- said.add(id);
16
- console.warn(`[verbaly] ${line}`);
17
- }
18
11
  function verbaly(options = {}) {
19
12
  let cfg;
20
13
  let catalogs;
@@ -22,6 +15,9 @@ function verbaly(options = {}) {
22
15
  let server;
23
16
  let writeTimer;
24
17
  let included;
18
+ let reporter;
19
+ let stateBroken = false;
20
+ let writing = Promise.resolve();
25
21
  const registry = new MessageRegistry();
26
22
  const selfWrites = /* @__PURE__ */ new Map();
27
23
  function invalidateVirtual() {
@@ -36,38 +32,44 @@ function verbaly(options = {}) {
36
32
  function flushDts() {
37
33
  writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});
38
34
  }
39
- function flushCatalogs() {
40
- syncCatalogs(cfg, catalogs, registry);
41
- for (const locale of cfg.locales) {
42
- const serialized = writeCatalog(cfg, locale, catalogs[locale] ?? {});
43
- selfWrites.set(locale, serialized);
44
- }
45
- flushDts();
46
- try {
47
- updateState(cfg, catalogs);
48
- } catch (error) {
49
- once("state", `${error.message.replace(/^\[verbaly\] /, "")}, so drafts are not tracked`);
50
- }
51
- for (const entry of collisionEntries(registry)) once(`collision:${entry.key}`, formatCollision(entry, cfg.root).trim());
35
+ function report(result, catalogEdit) {
36
+ reporter ??= createDevReporter(cfg.root);
37
+ const replaced = [];
38
+ const code = [];
39
+ for (const finding of result.findings) if (finding.kind === "collision" || finding.kind === "renamed") code.push(finding);
40
+ else if (finding.kind === "replaced" && catalogEdit) replaced.push({
41
+ ...finding,
42
+ edited: true
43
+ });
44
+ reporter.report("replaced", replaced);
45
+ reporter.report("code", code);
46
+ const outdated = result.state ? outdatedTranslations(cfg, result.catalogs, result.state.fingerprints) : [];
47
+ reporter.report("outdated", outdated.map((entry) => ({
48
+ kind: "outdated",
49
+ ...entry
50
+ })));
51
+ if (result.stateProblem && !stateBroken) console.warn(`[verbaly] ${result.stateProblem}, so drafts are not tracked`);
52
+ stateBroken = Boolean(result.stateProblem);
53
+ }
54
+ async function writeBack(catalogEdit) {
55
+ if (catalogEdit) cfg = await loadConfig(cfg.root, options);
56
+ const result = await syncProject(cfg, {
57
+ registry,
58
+ catalogs: loadCatalogs(cfg),
59
+ confirm: true,
60
+ write: "changed"
61
+ });
62
+ catalogs = result.catalogs;
63
+ for (const [locale, serialized] of Object.entries(result.written)) selfWrites.set(locale, serialized);
64
+ report(result, catalogEdit);
52
65
  invalidateVirtual();
53
66
  }
67
+ function queueWriteBack(catalogEdit) {
68
+ writing = writing.then(() => writeBack(catalogEdit)).catch((error) => console.warn(formatCliError(error)));
69
+ }
54
70
  function scheduleFlush() {
55
71
  clearTimeout(writeTimer);
56
- writeTimer = setTimeout(flushCatalogs, 50);
57
- }
58
- async function reloadFromDisk() {
59
- cfg = await loadConfig(cfg.root, options);
60
- catalogs = loadCatalogs(cfg);
61
- const { added, replaced } = syncCatalogs(cfg, catalogs, registry);
62
- const messages = registry.messages();
63
- for (const key of replaced) {
64
- const file = messages.get(key)?.file;
65
- if (!file) continue;
66
- const place = relative(cfg.root, file).replaceAll("\\", "/");
67
- once(`replaced:${key}`, `${cfg.sourceLocale}.json: your edit of "${key}" was replaced, its text lives in ${place}: change it there`);
68
- }
69
- if (Object.keys(added).length > 0) flushCatalogs();
70
- else invalidateVirtual();
72
+ writeTimer = setTimeout(() => queueWriteBack(false), 50);
71
73
  }
72
74
  return {
73
75
  name: "verbaly",
@@ -91,7 +93,7 @@ function verbaly(options = {}) {
91
93
  selfWrites.delete(locale);
92
94
  if (safeRead(file) === expected) return;
93
95
  }
94
- reloadFromDisk();
96
+ queueWriteBack(true);
95
97
  };
96
98
  devServer.watcher.on("change", onCatalogFile);
97
99
  devServer.watcher.on("add", onCatalogFile);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n LOCALE_MODULE_PREFIX,\n MessageRegistry,\n RESOLVED_VIRTUAL_ID,\n collisionEntries,\n createSourceFilter,\n extractProject,\n formatCollision,\n isTransformTarget,\n loadCatalogs,\n loadConfig,\n loadVirtualModule,\n resolveVirtualId,\n runBuildGate,\n syncCatalogs,\n transformSource,\n updateState,\n writeCatalog,\n writeDts,\n type Catalogs,\n type PluginOptions,\n type ResolvedConfig,\n} from '@verbaly/compiler';\nimport { readFileSync } from 'node:fs';\nimport { relative } from 'node:path';\nimport type { Plugin, ViteDevServer } from 'vite';\n\nfunction safeRead(file: string): string | undefined {\n try {\n return readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type ViteVerbalyOptions = PluginOptions;\n\n// a dev server re-runs per keystroke: keyed on what it is about, never on the text being typed\nconst said = new Set<string>();\n\nfunction once(id: string, line: string): void {\n if (said.has(id)) return;\n said.add(id);\n console.warn(`[verbaly] ${line}`);\n}\n\nexport default function verbaly(options: ViteVerbalyOptions = {}): Plugin {\n let cfg: ResolvedConfig;\n let catalogs: Catalogs;\n let isBuild = false;\n let server: ViteDevServer | undefined;\n let writeTimer: ReturnType<typeof setTimeout> | undefined;\n let included: (id: string) => boolean;\n const registry = new MessageRegistry();\n const selfWrites = new Map<string, string>();\n\n function invalidateVirtual(): void {\n if (!server) return;\n const ids = [\n RESOLVED_VIRTUAL_ID,\n ...cfg.locales.map((locale) => LOCALE_MODULE_PREFIX + locale),\n ];\n for (const id of ids) {\n const mod = server.moduleGraph.getModuleById(id);\n if (mod) server.moduleGraph.invalidateModule(mod);\n }\n server.ws.send({ type: 'full-reload' });\n }\n\n // cfg.dts is the merged option: @verbaly/astro fills its override before Vite resolves config\n function flushDts(): void {\n writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n }\n\n function flushCatalogs(): void {\n syncCatalogs(cfg, catalogs, registry);\n for (const locale of cfg.locales) {\n const serialized = writeCatalog(cfg, locale, catalogs[locale] ?? {});\n selfWrites.set(locale, serialized);\n }\n flushDts();\n try {\n // a pruned key takes its draft along, and an edited source text marks its translations\n updateState(cfg, catalogs);\n } catch (error) {\n once('state', `${(error as Error).message.replace(/^\\[verbaly\\] /, '')}, so drafts are not tracked`);\n }\n for (const entry of collisionEntries(registry)) {\n once(`collision:${entry.key}`, formatCollision(entry, cfg.root).trim());\n }\n invalidateVirtual();\n }\n\n function scheduleFlush(): void {\n clearTimeout(writeTimer);\n writeTimer = setTimeout(flushCatalogs, 50);\n }\n\n async function reloadFromDisk(): Promise<void> {\n cfg = await loadConfig(cfg.root, options);\n catalogs = loadCatalogs(cfg);\n // the code owns the texts it writes, as in a build: a hand edit of one is replaced, and said\n const { added, replaced } = syncCatalogs(cfg, catalogs, registry);\n const messages = registry.messages();\n for (const key of replaced) {\n const file = messages.get(key)?.file;\n if (!file) continue;\n const place = relative(cfg.root, file).replaceAll('\\\\', '/');\n once(`replaced:${key}`, `${cfg.sourceLocale}.json: your edit of \"${key}\" was replaced, its text lives in ${place}: change it there`);\n }\n // the flush writes the code's texts back and reloads the tabs itself: one reload, not two\n if (Object.keys(added).length > 0) flushCatalogs();\n else invalidateVirtual();\n }\n\n return {\n name: 'verbaly',\n enforce: 'pre',\n\n async configResolved(viteConfig) {\n isBuild = viteConfig.command === 'build';\n cfg = await loadConfig(options.root ?? viteConfig.root, options);\n catalogs = loadCatalogs(cfg);\n included = createSourceFilter(cfg);\n if (!isBuild) {\n flushDts();\n }\n },\n\n configureServer(devServer) {\n server = devServer;\n devServer.watcher.add(cfg.dir);\n const onCatalogFile = (file: string): void => {\n if (!file.startsWith(cfg.dir) || !file.endsWith('.json')) return;\n const locale = file.split(/[\\\\/]/).pop()!.slice(0, -5);\n // a dotfile there is the drafts sidecar: it changes no message, so no tab has to reload\n if (locale.startsWith('.')) return;\n const expected = selfWrites.get(locale);\n if (expected !== undefined) {\n selfWrites.delete(locale);\n // content compare: a stale entry must not swallow an external edit\n if (safeRead(file) === expected) return;\n }\n void reloadFromDisk();\n };\n devServer.watcher.on('change', onCatalogFile);\n devServer.watcher.on('add', onCatalogFile);\n devServer.watcher.on('unlink', (file) => {\n if (isTransformTarget(file) && included(file)) {\n registry.remove(file);\n scheduleFlush();\n }\n });\n },\n\n // the code's text ships, as in dev: the whole scan runs before any module is emitted\n async buildStart() {\n if (!isBuild) return;\n syncCatalogs(cfg, catalogs, await extractProject(cfg));\n },\n\n resolveId(id) {\n return resolveVirtualId(id);\n },\n\n load(id) {\n return loadVirtualModule(id, cfg, catalogs);\n },\n\n transform(code, id) {\n if (!isTransformTarget(id) || !included(id)) return undefined;\n const { messages, result } = transformSource(code, id, registry);\n\n const found = Object.entries(messages);\n if (!isBuild && found.length > 0) {\n const source = (catalogs[cfg.sourceLocale] ??= {});\n let changed = false;\n for (const [key, message] of found) {\n if (source[key] !== message) {\n source[key] = message;\n changed = true;\n }\n }\n if (changed) scheduleFlush();\n }\n\n return result ?? undefined;\n },\n\n buildEnd() {\n if (!isBuild) return;\n runBuildGate(cfg, registry, options.failOnMissing);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import {\n LOCALE_MODULE_PREFIX,\n MessageRegistry,\n RESOLVED_VIRTUAL_ID,\n createDevReporter,\n createSourceFilter,\n extractProject,\n formatCliError,\n isTransformTarget,\n loadCatalogs,\n loadConfig,\n loadVirtualModule,\n outdatedTranslations,\n resolveVirtualId,\n runBuildGate,\n syncCatalogs,\n syncProject,\n transformSource,\n writeDts,\n type Catalogs,\n type DevReporter,\n type Finding,\n type PluginOptions,\n type ResolvedConfig,\n type SyncProjectResult,\n} from '@verbaly/compiler';\nimport { readFileSync } from 'node:fs';\nimport type { Plugin, ViteDevServer } from 'vite';\n\nfunction safeRead(file: string): string | undefined {\n try {\n return readFileSync(file, 'utf8');\n } catch {\n return undefined;\n }\n}\n\nexport type { VerbalyConfig } from '@verbaly/compiler';\nexport type ViteVerbalyOptions = PluginOptions;\n\nexport default function verbaly(options: ViteVerbalyOptions = {}): Plugin {\n let cfg: ResolvedConfig;\n let catalogs: Catalogs;\n let isBuild = false;\n let server: ViteDevServer | undefined;\n let writeTimer: ReturnType<typeof setTimeout> | undefined;\n let included: (id: string) => boolean;\n let reporter: DevReporter | undefined;\n let stateBroken = false;\n // one write-back at a time: a save and a catalog edit landing together must not interleave\n let writing: Promise<void> = Promise.resolve();\n const registry = new MessageRegistry();\n const selfWrites = new Map<string, string>();\n\n function invalidateVirtual(): void {\n if (!server) return;\n const ids = [\n RESOLVED_VIRTUAL_ID,\n ...cfg.locales.map((locale) => LOCALE_MODULE_PREFIX + locale),\n ];\n for (const id of ids) {\n const mod = server.moduleGraph.getModuleById(id);\n if (mod) server.moduleGraph.invalidateModule(mod);\n }\n server.ws.send({ type: 'full-reload' });\n }\n\n // cfg.dts is the merged option: @verbaly/astro fills its override before Vite resolves config\n function flushDts(): void {\n writeDts(cfg, catalogs[cfg.sourceLocale] ?? {});\n }\n\n // what the dev server says, through the same reporter and wording as next dev\n function report(result: SyncProjectResult, catalogEdit: boolean): void {\n reporter ??= createDevReporter(cfg.root);\n const replaced: Finding[] = [];\n const code: Finding[] = [];\n for (const finding of result.findings) {\n if (finding.kind === 'collision' || finding.kind === 'renamed') code.push(finding);\n // a change to the catalog that the code undid was a person's edit; one to the code is not\n else if (finding.kind === 'replaced' && catalogEdit)\n replaced.push({ ...finding, edited: true });\n }\n reporter.report('replaced', replaced);\n reporter.report('code', code);\n const outdated = result.state\n ? outdatedTranslations(cfg, result.catalogs, result.state.fingerprints)\n : [];\n reporter.report(\n 'outdated',\n outdated.map((entry): Finding => ({ kind: 'outdated', ...entry })),\n );\n if (result.stateProblem && !stateBroken) {\n console.warn(`[verbaly] ${result.stateProblem}, so drafts are not tracked`);\n }\n stateBroken = Boolean(result.stateProblem);\n }\n\n // every write in dev starts from the disk, and re-reads the files behind each text that changes\n async function writeBack(catalogEdit: boolean): Promise<void> {\n if (catalogEdit) cfg = await loadConfig(cfg.root, options);\n const result = await syncProject(cfg, {\n registry,\n catalogs: loadCatalogs(cfg),\n confirm: true,\n write: 'changed',\n });\n catalogs = result.catalogs;\n for (const [locale, serialized] of Object.entries(result.written)) {\n selfWrites.set(locale, serialized);\n }\n report(result, catalogEdit);\n invalidateVirtual();\n }\n\n function queueWriteBack(catalogEdit: boolean): void {\n // a catalog saved half-typed is broken JSON for a moment, and that must never end the server\n writing = writing\n .then(() => writeBack(catalogEdit))\n .catch((error: unknown) => console.warn(formatCliError(error)));\n }\n\n function scheduleFlush(): void {\n clearTimeout(writeTimer);\n writeTimer = setTimeout(() => queueWriteBack(false), 50);\n }\n\n return {\n name: 'verbaly',\n enforce: 'pre',\n\n async configResolved(viteConfig) {\n isBuild = viteConfig.command === 'build';\n cfg = await loadConfig(options.root ?? viteConfig.root, options);\n catalogs = loadCatalogs(cfg);\n included = createSourceFilter(cfg);\n if (!isBuild) {\n flushDts();\n }\n },\n\n configureServer(devServer) {\n server = devServer;\n devServer.watcher.add(cfg.dir);\n const onCatalogFile = (file: string): void => {\n if (!file.startsWith(cfg.dir) || !file.endsWith('.json')) return;\n const locale = file.split(/[\\\\/]/).pop()!.slice(0, -5);\n // a dotfile there is the state sidecar: it changes no message, so no tab has to reload\n if (locale.startsWith('.')) return;\n const expected = selfWrites.get(locale);\n if (expected !== undefined) {\n selfWrites.delete(locale);\n // content compare: a stale entry must not swallow an external edit\n if (safeRead(file) === expected) return;\n }\n queueWriteBack(true);\n };\n devServer.watcher.on('change', onCatalogFile);\n devServer.watcher.on('add', onCatalogFile);\n devServer.watcher.on('unlink', (file) => {\n if (isTransformTarget(file) && included(file)) {\n registry.remove(file);\n scheduleFlush();\n }\n });\n },\n\n // the code's text ships, as in dev: the whole scan runs before any module is emitted\n async buildStart() {\n if (!isBuild) return;\n syncCatalogs(cfg, catalogs, await extractProject(cfg));\n },\n\n resolveId(id) {\n return resolveVirtualId(id);\n },\n\n load(id) {\n return loadVirtualModule(id, cfg, catalogs);\n },\n\n transform(code, id) {\n if (!isTransformTarget(id) || !included(id)) return undefined;\n const { messages, result } = transformSource(code, id, registry);\n\n const found = Object.entries(messages);\n if (!isBuild && found.length > 0) {\n const source = (catalogs[cfg.sourceLocale] ??= {});\n let changed = false;\n for (const [key, message] of found) {\n if (source[key] !== message) {\n source[key] = message;\n changed = true;\n }\n }\n if (changed) scheduleFlush();\n }\n\n return result ?? undefined;\n },\n\n buildEnd() {\n if (!isBuild) return;\n runBuildGate(cfg, registry, options.failOnMissing);\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/vite",
3
- "version": "0.67.0",
3
+ "version": "0.68.0",
4
4
  "description": "Zero-config Vite plugin for Verbaly: extraction, codegen and HMR.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -38,15 +38,15 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@verbaly/compiler": "0.67.0"
41
+ "@verbaly/compiler": "0.68.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "verbaly": "^0.67.0",
44
+ "verbaly": "^0.68.0",
45
45
  "vite": "^7.0.0 || ^8.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "verbaly": "^0.67.0",
49
- "vite": "^8.3.2"
48
+ "verbaly": "^0.68.0",
49
+ "vite": "^8.3.3"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsdown",