@verbaly/vite 0.74.0 → 0.76.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
@@ -17,6 +17,8 @@ The Vite plugin for [Verbaly](https://github.com/AronSoto/verbaly). Write natura
17
17
 
18
18
  ```bash
19
19
  pnpm add verbaly @verbaly/vite
20
+ pnpm add -D @verbaly/compiler # the verbaly command: init, extract, check
21
+ npx verbaly init
20
22
  ```
21
23
 
22
24
  ```ts
@@ -24,10 +26,15 @@ pnpm add verbaly @verbaly/vite
24
26
  import verbaly from '@verbaly/vite';
25
27
 
26
28
  export default {
27
- plugins: [verbaly({ sourceLocale: 'en', locales: ['en', 'es', 'pt'] })],
29
+ plugins: [verbaly()],
28
30
  };
29
31
  ```
30
32
 
33
+ ```ts
34
+ // verbaly.config.ts, created by npx verbaly init: list your languages, the build and `verbaly check` read them
35
+ export default { sourceLocale: 'en', locales: ['en', 'es', 'pt'] };
36
+ ```
37
+
31
38
  ```ts
32
39
  // anywhere in your app, extracted + typed on save
33
40
  import { t, setLocale } from 'virtual:verbaly';
package/dist/index.d.ts CHANGED
@@ -4,5 +4,4 @@ import { Plugin } from "vite";
4
4
  export type ViteVerbalyOptions = PluginOptions;
5
5
  export default function verbaly(options?: ViteVerbalyOptions): Plugin;
6
6
  //#endregion
7
- export type { VerbalyConfig };
8
- //# sourceMappingURL=index.d.ts.map
7
+ export type { VerbalyConfig };
package/dist/index.js CHANGED
@@ -59,7 +59,7 @@ function verbaly(options = {}) {
59
59
  stateBroken = Boolean(result.stateProblem);
60
60
  }
61
61
  async function writeBack(catalogEdit) {
62
- if (catalogEdit) cfg = await loadConfig(cfg.root, options);
62
+ if (catalogEdit) cfg = await loadConfig(cfg.root, options, { plugin: true });
63
63
  const result = await syncProject(cfg, {
64
64
  registry,
65
65
  catalogs: loadCatalogs(cfg),
@@ -83,7 +83,7 @@ function verbaly(options = {}) {
83
83
  enforce: "pre",
84
84
  async configResolved(viteConfig) {
85
85
  isBuild = viteConfig.command === "build";
86
- cfg = await loadConfig(options.root ?? viteConfig.root, options);
86
+ cfg = await loadConfig(options.root ?? viteConfig.root, options, { plugin: true });
87
87
  catalogs = loadCatalogs(cfg);
88
88
  included = createSourceFilter(cfg);
89
89
  if (!isBuild) flushDts();
@@ -159,5 +159,3 @@ function verbaly(options = {}) {
159
159
  }
160
160
  //#endregion
161
161
  export { verbaly as default };
162
-
163
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verbaly/vite",
3
- "version": "0.74.0",
3
+ "version": "0.76.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.74.0"
41
+ "@verbaly/compiler": "0.76.0"
42
42
  },
43
43
  "peerDependencies": {
44
- "verbaly": "^0.74.0",
44
+ "verbaly": "^0.76.0",
45
45
  "vite": "^7.0.0 || ^8.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "verbaly": "^0.74.0",
49
- "vite": "^8.3.4"
48
+ "verbaly": "^0.76.0",
49
+ "vite": "^8.3.5"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsdown",
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
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 reportOutsideInclude,\n isTransformTarget,\n loadCatalogs,\n loadConfig,\n loadVirtualModule,\n outdatedTranslations,\n resolveVirtualId,\n runBuildGate,\n SOURCE_FILE_RE,\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\n// built from the constant, so the NUL that marks a resolved virtual id never sits in the source\nconst VIRTUAL_ID_FILTER = new RegExp(`^${RESOLVED_VIRTUAL_ID.slice(1)}(?:/|$)`);\nconst RESOLVED_ID_FILTER = new RegExp(`^${RESOLVED_VIRTUAL_ID}(?:/|$)`);\nconst ANY_RESOLVED_ID = new RegExp(`^${RESOLVED_VIRTUAL_ID.charAt(0)}`);\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 // a build's scan: each file it read unchanged reaches transform already parsed\n let scan: MessageRegistry | undefined;\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 const lock: Finding[] = [];\n for (const finding of result.findings) {\n if (finding.kind === 'collision' || finding.kind === 'renamed') code.push(finding);\n else if (finding.kind === 'lock') lock.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 reporter.report('lock', lock);\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 an older state file: 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 // dev and build alike: a file outside include writes a t`…` that no build translates\n await reportOutsideInclude(cfg);\n if (!isBuild) return;\n scan = await extractProject(cfg, { keepSource: true });\n syncCatalogs(cfg, catalogs, scan);\n },\n\n // filtered: without them Vite 8 hands every module to JS, which tripled a real app's build\n resolveId: {\n filter: { id: VIRTUAL_ID_FILTER },\n handler(id) {\n return resolveVirtualId(id);\n },\n },\n\n load: {\n filter: { id: RESOLVED_ID_FILTER },\n handler(id) {\n return loadVirtualModule(id, cfg, catalogs);\n },\n },\n\n transform: {\n filter: { id: { include: SOURCE_FILE_RE, exclude: [/node_modules/, ANY_RESOLVED_ID] } },\n handler(code, id) {\n if (!isTransformTarget(id) || !included(id)) return undefined;\n const { messages, result } = transformSource(code, id, registry, scan);\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\n buildEnd() {\n if (!isBuild) return;\n scan = undefined;\n runBuildGate(cfg, registry, options.failOnMissing);\n },\n };\n}\n"],"mappings":"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"}