@menapo/vi18n 0.0.0-stage → 0.1.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/LICENSE +49 -0
- package/README.md +110 -2
- package/dist/apply-Bre-lY9J.js +228 -0
- package/dist/captured-Bj8bO6vd.js +1034 -0
- package/dist/check-D34P-S--.js +1 -0
- package/dist/check-DyrAbv73.js +201 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +41 -0
- package/dist/codec-DuvU2VQy.js +207 -0
- package/dist/collect-Cx4_SKOe.js +13 -0
- package/dist/collisions-CfpZU8e4.js +217 -0
- package/dist/compile-B5Mz6WJm.js +245 -0
- package/dist/compile-Cc97JaW2.js +2502 -0
- package/dist/concurrency-C9UUWuDC.js +96 -0
- package/dist/concurrency-DONL7t4V.js +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.js +39 -0
- package/dist/dir-zw6bGMKQ.js +290 -0
- package/dist/duplicates-yuRljQ6h.js +185 -0
- package/dist/engine-B7p40M__.js +531 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +1241 -0
- package/dist/exclude-Du-Zd_qI.js +26 -0
- package/dist/export-DGsMbcTZ.js +103 -0
- package/dist/extract-Dujlsgnk.js +298 -0
- package/dist/formats-KPIg-08V.js +781 -0
- package/dist/i18next-CrUnKFfh.js +1596 -0
- package/dist/i18next-Rgo-v9l0.js +1 -0
- package/dist/import-Cwdhcywt.js +225 -0
- package/dist/index.d.ts +156 -0
- package/dist/index.js +239 -0
- package/dist/init-BecCTvKi.js +792 -0
- package/dist/library-D5CWvTQs.js +1882 -0
- package/dist/lingui--PcOOYry.js +833 -0
- package/dist/lint.d.ts +105 -0
- package/dist/lint.js +971 -0
- package/dist/load-CQRz4PpM.js +2950 -0
- package/dist/load-gW-oJt1Q.js +747 -0
- package/dist/locales-DX4MOBFE.js +827 -0
- package/dist/lsp-Cq1Nuu-Q.js +31 -0
- package/dist/match-BGr8EXB3.js +89 -0
- package/dist/mcp-Cf0T7qPW.js +28 -0
- package/dist/merge-driver-CpiiSVIc.js +422 -0
- package/dist/messages-Cxa-5tR0.js +166 -0
- package/dist/migrate-CntqvrW4.js +1 -0
- package/dist/migrate-dPgq9uzy.js +1484 -0
- package/dist/openai-CTaMDcl-.js +346 -0
- package/dist/output-DsaHKHEt.js +164 -0
- package/dist/parse.d.ts +62 -0
- package/dist/parse.js +156 -0
- package/dist/pipeline-C7g11THL.js +2031 -0
- package/dist/pipeline-wxS0SApG.js +1 -0
- package/dist/project-Dwx7iPm6.js +381 -0
- package/dist/project-pXipT8iG.js +1046 -0
- package/dist/pseudo-DRv30iN5.js +145 -0
- package/dist/raw-C4Y6vyGY.js +4708 -0
- package/dist/react-intl-CaBVwJ9u.js +519 -0
- package/dist/restore-Btq66Tid.js +175 -0
- package/dist/rewrite-CiTRAICl.js +803 -0
- package/dist/rich-DCUKl5HM.js +97 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/run-DYD_VXKy.js +694 -0
- package/dist/runtime-DTMmliT1.js +64 -0
- package/dist/scan-worker.d.ts +62 -0
- package/dist/scan-worker.js +1 -0
- package/dist/scoped-BMYpSthj.js +44 -0
- package/dist/server-BDkykMjb.js +11823 -0
- package/dist/server-qv3mz70d.js +10485 -0
- package/dist/server.d.ts +169 -0
- package/dist/server.js +1234 -0
- package/dist/sitemap-DV3o8QD7.js +159 -0
- package/dist/sources-D19czDSA.js +2242 -0
- package/dist/status-DZArW5aE.js +149 -0
- package/dist/stdio-CVaJfxA7.js +29 -0
- package/dist/stdio-FPOKQAgu.js +95 -0
- package/dist/sync-CcVzger_.js +658 -0
- package/dist/t-DC-c9zE5.js +454 -0
- package/dist/test.d.ts +116 -0
- package/dist/test.js +319 -0
- package/dist/translate-DZR3MkSA.js +299 -0
- package/dist/ts-BaU5yOf0.js +4863 -0
- package/dist/types-CgUCkFU1.d.ts +707 -0
- package/dist/types-D3QGM8XO.js +146 -0
- package/dist/types-DsdWBG0b.d.ts +558 -0
- package/dist/types-DtvQ7UdD.js +1948 -0
- package/dist/types-wSh9fCBY.js +11 -0
- package/dist/units-CQ5Q99RC.js +150 -0
- package/dist/url-CRfCOhUI.js +172 -0
- package/dist/url-CsBkRaJy.js +225 -0
- package/dist/validate-BxpupdAf.js +2615 -0
- package/dist/validate-D6DJepZv.js +365 -0
- package/dist/version-BlyLGlOa.js +28 -0
- package/dist/vite-scan-B_YW4X_a.js +242 -0
- package/dist/vite.d.ts +34 -0
- package/dist/vite.js +7558 -0
- package/dist/vue-i18n-CEMnpv7k.js +651 -0
- package/dist/which-C6PFI1Dm.js +116 -0
- package/dist/worker-84U6uYB3.js +1656 -0
- package/package.json +97 -3
- package/schemas/catalog.json +57 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { i as toRuntimeConfig } from "./runtime-DTMmliT1.js";
|
|
2
|
+
import { a as assetCatalogsModule, i as SERVER_FILES, o as serverCatalogVersion, s as serverCatalogsDir, t as compileLocales } from "./locales-DX4MOBFE.js";
|
|
3
|
+
import { t as findCollisions } from "./collisions-CfpZU8e4.js";
|
|
4
|
+
import { a as table, i as rootRelative, r as plural } from "./output-DsaHKHEt.js";
|
|
5
|
+
import { t as EXIT } from "./types-wSh9fCBY.js";
|
|
6
|
+
import { a as loadCatalogs, f as scan, i as hasErrors, n as compareText, o as loadProject, r as computeDrift, s as occurrencesOf, u as readErrors } from "./project-Dwx7iPm6.js";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { randomBytes } from "node:crypto";
|
|
9
|
+
import { mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
10
|
+
//#region src/cli/commands/compile.ts
|
|
11
|
+
/**
|
|
12
|
+
* `vi18n compile [--out dir] [--assets dir] [--strict] [--pseudo] [--check]`
|
|
13
|
+
* (INTERNALS §2.12, PLAN §5.3): compiled catalogs for code Vite does not
|
|
14
|
+
* build (a wrangler Worker, e-mail rendering), loaded with
|
|
15
|
+
* `registerCatalogs`. It refuses to run while `extract --check` reports
|
|
16
|
+
* drift, so what ships always matches the code. A committed `--out`
|
|
17
|
+
* directory can still lag behind the catalogs (a translation edited,
|
|
18
|
+
* `extract` run without `compile`): `--check` compares it with what would be
|
|
19
|
+
* written, for CI.
|
|
20
|
+
*
|
|
21
|
+
* `--assets` is the Workers layout of `ssr.catalogs: "assets"` (spike S12):
|
|
22
|
+
* the catalogs go to the static-assets directory under the server message
|
|
23
|
+
* set's version, and `<out>/catalogs.js` reads them through `env.ASSETS`, so
|
|
24
|
+
* the Worker bundles none and only the locales requests use take memory.
|
|
25
|
+
*/
|
|
26
|
+
async function compile(ctx, flags) {
|
|
27
|
+
const project = await loadProject(ctx);
|
|
28
|
+
const { config, codec } = project;
|
|
29
|
+
const data = {
|
|
30
|
+
out: null,
|
|
31
|
+
assets: null,
|
|
32
|
+
files: [],
|
|
33
|
+
missing: {},
|
|
34
|
+
drift: []
|
|
35
|
+
};
|
|
36
|
+
const scanned = await scan(project);
|
|
37
|
+
ctx.out.report(scanned.diagnostics.filter((d) => d.severity === "error"));
|
|
38
|
+
const loaded = await loadCatalogs(project);
|
|
39
|
+
const unreadable = readErrors(loaded.diagnostics);
|
|
40
|
+
if (unreadable.length > 0) {
|
|
41
|
+
ctx.out.report(unreadable);
|
|
42
|
+
return {
|
|
43
|
+
exitCode: EXIT.failure,
|
|
44
|
+
data
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const collided = findCollisions(occurrencesOf(scanned), loaded.set, codec, { referenced: new Set(config.extraction.keep) });
|
|
48
|
+
if (hasErrors(collided)) {
|
|
49
|
+
ctx.out.report(collided);
|
|
50
|
+
return {
|
|
51
|
+
exitCode: EXIT.failure,
|
|
52
|
+
data
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const refusals = (await computeDrift(project, scanned.messages, loaded.set)).diagnostics.filter((d) => d.code === "catalog-drift" || d.code === "id-scheme-mismatch");
|
|
56
|
+
if (refusals.length > 0) {
|
|
57
|
+
ctx.out.report(refusals);
|
|
58
|
+
ctx.out.note("vi18n compile: the catalogs do not match the code; run vi18n extract first");
|
|
59
|
+
return {
|
|
60
|
+
exitCode: EXIT.failure,
|
|
61
|
+
data
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const pseudo = flags.pseudo ? config.pseudoLocales : [];
|
|
65
|
+
const locales = [...config.locales, ...pseudo];
|
|
66
|
+
const compiled = compileLocales({
|
|
67
|
+
config,
|
|
68
|
+
codec,
|
|
69
|
+
messages: scanned.messages,
|
|
70
|
+
keep: config.extraction.keep,
|
|
71
|
+
catalogs: loaded.set,
|
|
72
|
+
locales
|
|
73
|
+
});
|
|
74
|
+
let missingTotal = 0;
|
|
75
|
+
for (const locale of config.locales) {
|
|
76
|
+
const result = compiled.get(locale);
|
|
77
|
+
ctx.out.report(result.diagnostics);
|
|
78
|
+
if (locale === config.sourceLocale) continue;
|
|
79
|
+
let count = 0;
|
|
80
|
+
for (const status of result.status.values()) if (status.state === "missing") count++;
|
|
81
|
+
data.missing[locale] = count;
|
|
82
|
+
missingTotal += count;
|
|
83
|
+
}
|
|
84
|
+
if (flags.out !== null) {
|
|
85
|
+
const out = resolve(ctx.global.cwd, flags.out);
|
|
86
|
+
data.out = rootRelative(config.root, out);
|
|
87
|
+
if (!flags.check) await mkdir(out, { recursive: true });
|
|
88
|
+
const files = [];
|
|
89
|
+
const runtime = toRuntimeConfig(config, {
|
|
90
|
+
base: "/",
|
|
91
|
+
pseudo: flags.pseudo
|
|
92
|
+
});
|
|
93
|
+
const assets = flags.assets === null ? null : resolve(ctx.global.cwd, flags.assets);
|
|
94
|
+
let version = null;
|
|
95
|
+
if (assets === null) for (const locale of locales) {
|
|
96
|
+
const messages = compiled.get(locale).messages;
|
|
97
|
+
files.push([join(out, `${locale}.json`), bundleJson(messages)]);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
data.assets = rootRelative(config.root, assets);
|
|
101
|
+
version = serverCatalogVersion({
|
|
102
|
+
idScheme: codec.idScheme,
|
|
103
|
+
v: null,
|
|
104
|
+
ids: compiled.get(config.sourceLocale).messages.keys(),
|
|
105
|
+
sourceHashOf: (id) => scanned.messages.get(id)?.sourceHash ?? ""
|
|
106
|
+
});
|
|
107
|
+
for (const locale of locales) {
|
|
108
|
+
const messages = compiled.get(locale).messages;
|
|
109
|
+
const path = join(assets, serverCatalogsDir(version), locale, SERVER_FILES.catalog);
|
|
110
|
+
files.push([path, bundleJson(messages)]);
|
|
111
|
+
}
|
|
112
|
+
runtime.ssr.maxLoadedLocales = config.ssr.maxLoadedLocales;
|
|
113
|
+
}
|
|
114
|
+
files.push([join(out, "sources.json"), sourcesJson(scanned.messages)]);
|
|
115
|
+
files.push([join(out, "config.json"), JSON.stringify(runtime) + "\n"]);
|
|
116
|
+
if (version !== null) {
|
|
117
|
+
files.push([join(out, "catalogs.js"), assetCatalogsModule({
|
|
118
|
+
locales,
|
|
119
|
+
dir: serverCatalogsDir(version),
|
|
120
|
+
deploy: `deploy ${flags.assets} (written by vi18n compile --assets) with this Worker`,
|
|
121
|
+
modules: "compile without --assets to bundle the catalogs as modules"
|
|
122
|
+
})]);
|
|
123
|
+
files.push([join(out, "catalogs.d.ts"), CATALOGS_DTS]);
|
|
124
|
+
}
|
|
125
|
+
const fix = `vi18n compile --out ${flags.out}${assets === null ? "" : ` --assets ${flags.assets}`}${flags.pseudo ? " --pseudo" : ""}`;
|
|
126
|
+
for (const [path, content] of files) {
|
|
127
|
+
const file = rootRelative(config.root, path);
|
|
128
|
+
data.files.push(file);
|
|
129
|
+
if (!flags.check) {
|
|
130
|
+
await mkdir(dirname(path), { recursive: true });
|
|
131
|
+
await writeIfChanged(path, content);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const current = await readIfExists(path);
|
|
135
|
+
if (current !== null && current.replace(/\r\n/g, "\n") === content) continue;
|
|
136
|
+
data.drift.push(file);
|
|
137
|
+
ctx.out.report([{
|
|
138
|
+
code: "catalog-drift",
|
|
139
|
+
severity: "error",
|
|
140
|
+
message: current === null ? `${file} does not exist yet; run ${fix}` : `${file} is out of date; run ${fix}`,
|
|
141
|
+
file
|
|
142
|
+
}]);
|
|
143
|
+
}
|
|
144
|
+
if (version !== null && assets !== null && !flags.check) {
|
|
145
|
+
const locales = [...config.locales, ...config.pseudoLocales];
|
|
146
|
+
const removed = await removeOtherVersions(assets, version, locales);
|
|
147
|
+
if (removed > 0) ctx.out.note(`removed ${plural(removed, "earlier catalog version")} from ${ctx.out.display(rootRelative(config.root, join(assets, "vi18n")))}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const where = data.out === null ? "" : ctx.out.display(data.out) + (data.assets === null ? "" : ` and ${ctx.out.display(data.assets)}`);
|
|
151
|
+
const rows = Object.entries(data.missing).map(([locale, count]) => [locale, count]);
|
|
152
|
+
if (rows.length > 0) ctx.out.result(table(["locale", "missing"], rows));
|
|
153
|
+
if (data.out !== null && flags.check) ctx.out.result(data.drift.length > 0 ? `vi18n compile --check: ${data.drift.length} out-of-date file(s) in ${where}` : `vi18n compile --check: ${where} ${data.assets === null ? "is" : "are"} up to date`);
|
|
154
|
+
else if (data.out !== null) ctx.out.result(`wrote ${plural(data.files.length, "file")} to ${where}`);
|
|
155
|
+
const failed = hasErrors(ctx.out.diagnostics) || flags.strict && missingTotal > 0;
|
|
156
|
+
if (flags.strict && missingTotal > 0) ctx.out.note(`vi18n compile --strict: ${plural(missingTotal, "missing translation")}`);
|
|
157
|
+
return {
|
|
158
|
+
exitCode: failed ? EXIT.failure : EXIT.ok,
|
|
159
|
+
data
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/** Types of the generated `catalogs.js` for TypeScript apps (no `allowJs` needed). */
|
|
163
|
+
const CATALOGS_DTS = `// Generated by vi18n compile --assets: catalog loaders for registerCatalogs().
|
|
164
|
+
declare const catalogs: { [locale: string]: () => Promise<{ [id: string]: unknown }> };
|
|
165
|
+
export default catalogs;
|
|
166
|
+
`;
|
|
167
|
+
/** A version directory name (`hash8`): base64url, 8 characters. */
|
|
168
|
+
const VERSION_NAME = /^[\w-]{8}$/;
|
|
169
|
+
/**
|
|
170
|
+
* Removes the versions of `<assets>/vi18n/` other than `version` that this
|
|
171
|
+
* command wrote: directories named like a version holding only
|
|
172
|
+
* `<locale>/catalog.json` for configured locales. Anything else there (a
|
|
173
|
+
* Vite build's stable manifests, the app's own files) is left alone.
|
|
174
|
+
*/
|
|
175
|
+
async function removeOtherVersions(assets, version, locales) {
|
|
176
|
+
const root = join(assets, "vi18n");
|
|
177
|
+
let removed = 0;
|
|
178
|
+
for (const entry of await readdir(root, { withFileTypes: true }).catch(() => [])) {
|
|
179
|
+
if (!entry.isDirectory() || entry.name === version || !VERSION_NAME.test(entry.name)) continue;
|
|
180
|
+
const dir = join(root, entry.name);
|
|
181
|
+
if (!await onlyCatalogs(dir, locales)) continue;
|
|
182
|
+
await rm(dir, {
|
|
183
|
+
recursive: true,
|
|
184
|
+
force: true
|
|
185
|
+
});
|
|
186
|
+
removed++;
|
|
187
|
+
}
|
|
188
|
+
return removed;
|
|
189
|
+
}
|
|
190
|
+
async function onlyCatalogs(dir, locales) {
|
|
191
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
192
|
+
if (!entry.isDirectory() || !locales.includes(entry.name)) return false;
|
|
193
|
+
if ((await readdir(join(dir, entry.name), { withFileTypes: true })).some((file) => !file.isFile() || file.name !== SERVER_FILES.catalog)) return false;
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
/** `BundleData` with keys in code-unit order and no whitespace: byte-stable for diffs and caches. */
|
|
198
|
+
function bundleJson(messages) {
|
|
199
|
+
const ids = [...messages.keys()].sort(compareText);
|
|
200
|
+
const data = Object.create(null);
|
|
201
|
+
for (const id of ids) data[id] = messages.get(id);
|
|
202
|
+
return JSON.stringify(data) + "\n";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Every exact source literal of a generated-id message → its id, so
|
|
206
|
+
* untransformed code (`t("Hello {name}")` in a Worker) finds the compiled
|
|
207
|
+
* message (PLAN §5.3). Manual ids are looked up by id and need no entry.
|
|
208
|
+
*/
|
|
209
|
+
function sourcesJson(messages) {
|
|
210
|
+
const pairs = [];
|
|
211
|
+
for (const message of messages.values()) {
|
|
212
|
+
if (message.manual) continue;
|
|
213
|
+
for (const source of message.sources) pairs.push([source, message.id]);
|
|
214
|
+
}
|
|
215
|
+
pairs.sort((a, b) => compareText(a[0], b[0]));
|
|
216
|
+
const data = Object.create(null);
|
|
217
|
+
for (const [source, id] of pairs) data[source] = id;
|
|
218
|
+
return JSON.stringify(data) + "\n";
|
|
219
|
+
}
|
|
220
|
+
async function readIfExists(path) {
|
|
221
|
+
try {
|
|
222
|
+
return await readFile(path, "utf8");
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error.code === "ENOENT") return null;
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Unchanged files keep their mtime (watchers, incremental deploys); others are replaced atomically. */
|
|
229
|
+
async function writeIfChanged(path, content) {
|
|
230
|
+
try {
|
|
231
|
+
if (await readFile(path, "utf8") === content) return;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error.code !== "ENOENT") throw error;
|
|
234
|
+
}
|
|
235
|
+
const temp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
236
|
+
try {
|
|
237
|
+
await writeFile(temp, content);
|
|
238
|
+
await rename(temp, path);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
await rm(temp, { force: true });
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
export { compile };
|