prosebook 0.3.0 → 0.5.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 +357 -26
- package/dist/bind-BIZNv8nU.js +62 -0
- package/dist/bind-BIZNv8nU.js.map +1 -0
- package/dist/bind-ZS86jHXk.d.ts +31 -0
- package/dist/bind-ZS86jHXk.d.ts.map +1 -0
- package/dist/bun.d.ts +12 -3
- package/dist/bun.d.ts.map +1 -1
- package/dist/bun.js +77 -26
- package/dist/bun.js.map +1 -1
- package/dist/{bundle-KD-DiiFa.d.ts → bundle-CLHY_97D.d.ts} +3 -3
- package/dist/bundle-CLHY_97D.d.ts.map +1 -0
- package/dist/bundle-Ut6Y2X6N.js +217 -0
- package/dist/bundle-Ut6Y2X6N.js.map +1 -0
- package/dist/cli.js +55 -12
- package/dist/cli.js.map +1 -1
- package/dist/{config-BPSmoYel.js → config-7T4hX_f7.js} +13 -5
- package/dist/{config-BPSmoYel.js.map → config-7T4hX_f7.js.map} +1 -1
- package/dist/{config-DdMkKt_N.d.ts → config-CzaJJQ60.d.ts} +19 -5
- package/dist/{config-DdMkKt_N.d.ts.map → config-CzaJJQ60.d.ts.map} +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/{copy-B3V_QRkT.d.ts → copy-BXn0aYQn.d.ts} +37 -3
- package/dist/copy-BXn0aYQn.d.ts.map +1 -0
- package/dist/copy-Bv6S-Hr2.js +235 -0
- package/dist/copy-Bv6S-Hr2.js.map +1 -0
- package/dist/endpoints-Dhgowovz.js +217 -0
- package/dist/endpoints-Dhgowovz.js.map +1 -0
- package/dist/generate-1BeQAd1O.d.ts +72 -0
- package/dist/generate-1BeQAd1O.d.ts.map +1 -0
- package/dist/{generate-9pR3i5Bj.js → generate-CKv86Iki.js} +1724 -627
- package/dist/generate-CKv86Iki.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/next-loader.d.ts +9 -2
- package/dist/next-loader.d.ts.map +1 -1
- package/dist/next-loader.js +12 -3
- package/dist/next-loader.js.map +1 -1
- package/dist/next.d.ts +11 -3
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +94 -21
- package/dist/next.js.map +1 -1
- package/dist/preact/index.d.ts +34 -0
- package/dist/preact/index.d.ts.map +1 -0
- package/dist/preact/index.js +72 -0
- package/dist/preact/index.js.map +1 -0
- package/dist/react/index.d.ts +5 -14
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +9 -33
- package/dist/react/index.js.map +1 -1
- package/dist/react/server.d.ts +1 -1
- package/dist/react/server.js +1 -1
- package/dist/{sarif-CWyTAQ0y.js → sarif-BnwYC78_.js} +8 -98
- package/dist/sarif-BnwYC78_.js.map +1 -0
- package/dist/{scan-Bx0_yDSi.js → scan-C0ap4Mpp.js} +145 -20
- package/dist/scan-C0ap4Mpp.js.map +1 -0
- package/dist/solid/index.d.ts +29 -0
- package/dist/solid/index.d.ts.map +1 -0
- package/dist/solid/index.js +48 -0
- package/dist/solid/index.js.map +1 -0
- package/dist/svelte/Text.svelte +13 -0
- package/dist/svelte/Text.svelte.d.ts +20 -0
- package/dist/svelte/index.d.ts +31 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +35 -0
- package/dist/svelte/index.js.map +1 -0
- package/dist/tools.d.ts +44 -65
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +5 -5
- package/dist/vite.d.ts +13 -3
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +82 -25
- package/dist/vite.js.map +1 -1
- package/dist/vue/index.d.ts +43 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +76 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +63 -7
- package/src/bun.ts +77 -20
- package/src/cli.ts +62 -8
- package/src/index.ts +1 -1
- package/src/next-loader.ts +17 -5
- package/src/next.ts +120 -16
- package/src/preact/index.ts +91 -0
- package/src/react/index.tsx +12 -42
- package/src/runtime/bind.ts +96 -0
- package/src/runtime/copy.ts +148 -24
- package/src/solid/index.ts +67 -0
- package/src/svelte/Text.svelte +13 -0
- package/src/svelte/Text.svelte.d.ts +20 -0
- package/src/svelte/index.ts +65 -0
- package/src/tools/bundle.ts +143 -2
- package/src/tools/catalog.ts +10 -1
- package/src/tools/chunks.ts +142 -0
- package/src/tools/config.ts +25 -7
- package/src/tools/entry.ts +19 -20
- package/src/tools/exchange.ts +275 -0
- package/src/tools/extract.ts +2 -2
- package/src/tools/generate.ts +7 -1
- package/src/tools/index.ts +2 -1
- package/src/tools/native/android.ts +186 -0
- package/src/tools/native/apple.ts +201 -0
- package/src/tools/native/date.ts +219 -0
- package/src/tools/native/index.ts +17 -5
- package/src/tools/native/kotlin.ts +47 -6
- package/src/tools/native/model.ts +67 -29
- package/src/tools/native/printf.ts +137 -0
- package/src/tools/native/rust.ts +86 -5
- package/src/tools/native/swift.ts +62 -6
- package/src/tools/scan.ts +146 -25
- package/src/tools/todo.ts +6 -1
- package/src/vite.ts +121 -28
- package/src/vue/index.ts +117 -0
- package/dist/bundle-DboItAre.js +0 -111
- package/dist/bundle-DboItAre.js.map +0 -1
- package/dist/bundle-KD-DiiFa.d.ts.map +0 -1
- package/dist/compile-xF3F4oIp.d.ts +0 -16
- package/dist/compile-xF3F4oIp.d.ts.map +0 -1
- package/dist/copy-B3V_QRkT.d.ts.map +0 -1
- package/dist/copy-Bo_FIoLy.js +0 -154
- package/dist/copy-Bo_FIoLy.js.map +0 -1
- package/dist/endpoints-CnUfCkIk.js +0 -96
- package/dist/endpoints-CnUfCkIk.js.map +0 -1
- package/dist/generate-9pR3i5Bj.js.map +0 -1
- package/dist/sarif-CWyTAQ0y.js.map +0 -1
- package/dist/scan-Bx0_yDSi.js.map +0 -1
package/dist/vite.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { n as generate,
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { C as unseenMessage, S as splitIndex, _ as membersOf, a as areaModule, c as beside, d as hotLocale, f as importingAreas, g as lazyAreas, h as keeper, m as installInspector, n as HOT_INDEX, o as areasNamed, p as inAreas, r as HOT_SET, t as AREA, w as unseenOf } from "./bundle-Ut6Y2X6N.js";
|
|
2
|
+
import { T as CatalogError, n as generate, o as writeFiles } from "./generate-CKv86Iki.js";
|
|
3
|
+
import { i as areaGroups, n as nodeMiddleware, r as redirect, t as inspectorEndpoints } from "./endpoints-Dhgowovz.js";
|
|
4
|
+
import { a as keyRefs, d as compileLocale, f as localeExpression, n as SFC, u as unseenReads } from "./scan-C0ap4Mpp.js";
|
|
5
|
+
import { r as loadConfig } from "./config-7T4hX_f7.js";
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { join, relative, resolve } from "node:path";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
9
|
// src/vite.ts
|
|
9
10
|
/**
|
|
@@ -14,6 +15,15 @@ import { fileURLToPath } from "node:url";
|
|
|
14
15
|
* code names: each locale module is swapped for its messages less the keys no module references
|
|
15
16
|
* (see `tools/scan.ts` for how a reference is found, and why it errs towards keeping).
|
|
16
17
|
*
|
|
18
|
+
* With `split`, a client's catalog has no messages of its own: each module naming an area's keys
|
|
19
|
+
* imports a module for that area, which the bundler places beside it, and which registers the area
|
|
20
|
+
* (`addArea`) and waits until every copy holds it in its locale. Areas are found by the same
|
|
21
|
+
* references pruning reads, so a read pruning can't see is one splitting can't place either. A lazy
|
|
22
|
+
* locale's part is a file per group of areas the bundler will put in one chunk (`tools/chunks.ts`),
|
|
23
|
+
* read from the source when the build starts, as an area loads before every module naming it has.
|
|
24
|
+
* The client build writes down the file each area loads from per lazy locale, and the server build
|
|
25
|
+
* after it gives its catalog that list (`areaFiles`), so a server render can link what it read.
|
|
26
|
+
*
|
|
17
27
|
* In dev an edit doesn't reload the page: each generated locale module accepts its own update and
|
|
18
28
|
* hands the new messages to every copy (`HotHook`), which re-renders in the locale and state it was in.
|
|
19
29
|
* The index accepts its own too, since its doc comments change with the source text, and reloads the
|
|
@@ -24,10 +34,15 @@ import { fileURLToPath } from "node:url";
|
|
|
24
34
|
* launches.
|
|
25
35
|
*/
|
|
26
36
|
const PLACEHOLDER = /__PROSEBOOK_LOCALE_(\d+)__/g;
|
|
27
|
-
const SCRIPT = /\.[cm]?[jt]sx
|
|
37
|
+
const SCRIPT = /\.(?:[cm]?[jt]sx?|vue|svelte)$/;
|
|
28
38
|
const DEV = "virtual:prosebook/dev";
|
|
29
39
|
const DEV_ID = "\0prosebook/dev";
|
|
40
|
+
const AREA_ID = "\0prosebook/area/";
|
|
30
41
|
const normalize = (path) => resolve(path).replaceAll("\\", "/");
|
|
42
|
+
/** Where a split client build leaves its area files for the server build: apart from the source and the output. */
|
|
43
|
+
const areaFilesPath = (root) => join(root, "node_modules", ".prosebook", "area-files.json");
|
|
44
|
+
/** The index with `areaFiles` among its catalog's fields. */
|
|
45
|
+
const withAreaFiles = (index, files) => index.replace(/defineCatalog(?:<[^>]*>)?\(\{/, (call) => `${call}\n areaFiles: ${JSON.stringify(files)},`);
|
|
31
46
|
const inspectorPath = beside(import.meta.url, "inspector");
|
|
32
47
|
const ownDir = normalize(fileURLToPath(new URL(".", import.meta.url)));
|
|
33
48
|
function prosebook(options = {}) {
|
|
@@ -44,6 +59,7 @@ function prosebook(options = {}) {
|
|
|
44
59
|
const reads = /* @__PURE__ */ new Map();
|
|
45
60
|
const prune = options.prune ?? "strict";
|
|
46
61
|
const placeholders = [];
|
|
62
|
+
let groups = /* @__PURE__ */ new Map();
|
|
47
63
|
const shipped = {
|
|
48
64
|
total: 0,
|
|
49
65
|
kept: 0
|
|
@@ -61,6 +77,11 @@ function prosebook(options = {}) {
|
|
|
61
77
|
const out = normalize(config.out);
|
|
62
78
|
return path === `${out}/index.ts` || path === `${out}/index.js`;
|
|
63
79
|
};
|
|
80
|
+
const splitting = (ssr) => options.split === true && ssr !== true && generated !== void 0;
|
|
81
|
+
const placeholder = (locale, areas) => `__PROSEBOOK_LOCALE_${placeholders.push({
|
|
82
|
+
locale,
|
|
83
|
+
areas
|
|
84
|
+
}) - 1}__`;
|
|
64
85
|
return {
|
|
65
86
|
name: "prosebook",
|
|
66
87
|
async configResolved(resolved) {
|
|
@@ -70,7 +91,9 @@ function prosebook(options = {}) {
|
|
|
70
91
|
config = await loadConfig(options.root === void 0 ? resolved.root : resolve(resolved.root, options.root), options.config);
|
|
71
92
|
},
|
|
72
93
|
resolveId(id) {
|
|
73
|
-
|
|
94
|
+
if (id === DEV) return DEV_ID;
|
|
95
|
+
if (id.startsWith("virtual:prosebook/area/")) return AREA_ID + id.slice(AREA.length);
|
|
96
|
+
return null;
|
|
74
97
|
},
|
|
75
98
|
buildStart() {
|
|
76
99
|
try {
|
|
@@ -82,8 +105,9 @@ function prosebook(options = {}) {
|
|
|
82
105
|
exact: /* @__PURE__ */ new Set(),
|
|
83
106
|
prefixes: /* @__PURE__ */ new Set()
|
|
84
107
|
};
|
|
85
|
-
kept = keeper(refs, options.keep);
|
|
108
|
+
kept = prune === false ? () => true : keeper(refs, options.keep);
|
|
86
109
|
reads.clear();
|
|
110
|
+
groups = building && options.split === true && generated !== void 0 ? areaGroups(config, generated, options.keep) : /* @__PURE__ */ new Map();
|
|
87
111
|
placeholders.length = 0;
|
|
88
112
|
shipped.total = shipped.kept = 0;
|
|
89
113
|
},
|
|
@@ -95,6 +119,7 @@ function prosebook(options = {}) {
|
|
|
95
119
|
if (!path.endsWith(".yaml") || !dirs.some((d) => path.startsWith(`${d}/`))) return;
|
|
96
120
|
try {
|
|
97
121
|
generated = generate(config);
|
|
122
|
+
for (const [id, mod] of server.moduleGraph.idToModuleMap) if (id.startsWith(AREA_ID)) server.moduleGraph.invalidateModule(mod);
|
|
98
123
|
server.config.logger.info(`prosebook: regenerated from ${relative(config.root, path).replaceAll("\\", "/")}`, { timestamp: true });
|
|
99
124
|
} catch (e) {
|
|
100
125
|
server.config.logger.error(describe(e), { timestamp: true });
|
|
@@ -118,38 +143,56 @@ function prosebook(options = {}) {
|
|
|
118
143
|
open: editor
|
|
119
144
|
})));
|
|
120
145
|
},
|
|
121
|
-
load(id) {
|
|
146
|
+
load(id, loadOptions) {
|
|
122
147
|
if (id === DEV_ID) {
|
|
123
148
|
if (options.inspect === false) return HOT_SET;
|
|
124
149
|
return HOT_SET + installInspector(inspectorPath, base);
|
|
125
150
|
}
|
|
151
|
+
if (id.startsWith(AREA_ID) && generated !== void 0) {
|
|
152
|
+
const [area = "", locale] = id.slice(16).split("/");
|
|
153
|
+
if (locale !== void 0) return `export default ${placeholder(locale, membersOf(groups, area))};\n`;
|
|
154
|
+
return areaModule(config, generated, area, building ? (l) => placeholder(l, [area]) : void 0, void 0, groups);
|
|
155
|
+
}
|
|
156
|
+
if (splitting(loadOptions?.ssr) && isIndex(id)) return splitIndex(config, generated, options.keep);
|
|
157
|
+
if (building && options.split === true && loadOptions?.ssr === true && isIndex(id)) {
|
|
158
|
+
const path = areaFilesPath(viteRoot);
|
|
159
|
+
if (existsSync(path)) return withAreaFiles(readFileSync(id.split("?", 1)[0], "utf8"), JSON.parse(readFileSync(path, "utf8")));
|
|
160
|
+
this.warn("no split client build to link area files from: build the client before the server, or `preload` is handed nothing");
|
|
161
|
+
}
|
|
126
162
|
if (!building || prune === false || generated === void 0) return null;
|
|
127
163
|
const locale = localeOf(id);
|
|
128
164
|
if (locale === void 0) return null;
|
|
129
|
-
|
|
130
|
-
return `export default __PROSEBOOK_LOCALE_${placeholders.length - 1}__;\n`;
|
|
165
|
+
return `export default ${placeholder(locale)};\n`;
|
|
131
166
|
},
|
|
132
167
|
transform: {
|
|
133
168
|
order: "pre",
|
|
134
169
|
handler(code, id, transformOptions) {
|
|
170
|
+
const ssr = transformOptions?.ssr;
|
|
135
171
|
if (!building) {
|
|
136
172
|
const locale = localeOf(id);
|
|
137
173
|
if (locale !== void 0) return {
|
|
138
174
|
code: code + hotLocale(locale),
|
|
139
175
|
map: null
|
|
140
176
|
};
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
code: (transformOptions?.ssr === true ? "" : `import ${JSON.stringify(DEV)};\n`) + code + HOT_INDEX,
|
|
177
|
+
if (isIndex(id)) return {
|
|
178
|
+
code: (ssr === true ? "" : `import ${JSON.stringify(DEV)};\n`) + code + HOT_INDEX,
|
|
144
179
|
map: null
|
|
145
180
|
};
|
|
146
|
-
}
|
|
147
|
-
if (
|
|
148
|
-
if (localeOf(id) !== void 0 || isIndex(id)) return null;
|
|
149
|
-
keyRefs(code, refs);
|
|
181
|
+
} else if (localeOf(id) !== void 0 || isIndex(id)) return null;
|
|
182
|
+
if (id.includes("/node_modules/") || id.startsWith("\0")) return null;
|
|
150
183
|
const path = id.split("?", 1)[0];
|
|
151
|
-
|
|
152
|
-
|
|
184
|
+
const part = SFC.test(path) && path !== id;
|
|
185
|
+
const own = normalize(path).startsWith(`${ownDir}/`);
|
|
186
|
+
if (building && prune !== false) {
|
|
187
|
+
keyRefs(code, refs);
|
|
188
|
+
if (prune === "strict" && SCRIPT.test(path) && !part && !own) reads.set(id, unseenReads(path, code));
|
|
189
|
+
}
|
|
190
|
+
if (!splitting(ssr) || !SCRIPT.test(path) || part || own) return null;
|
|
191
|
+
const areas = areasNamed(code, lazyAreas(generated, options.keep));
|
|
192
|
+
return areas.length === 0 ? null : {
|
|
193
|
+
code: importingAreas(code, path, areas),
|
|
194
|
+
map: null
|
|
195
|
+
};
|
|
153
196
|
}
|
|
154
197
|
},
|
|
155
198
|
buildEnd(error) {
|
|
@@ -163,8 +206,9 @@ function prosebook(options = {}) {
|
|
|
163
206
|
const entries = generated.entries;
|
|
164
207
|
return {
|
|
165
208
|
code: code.replace(PLACEHOLDER, (_match, index) => {
|
|
166
|
-
const locale = placeholders[Number(index)];
|
|
167
|
-
const
|
|
209
|
+
const { locale, areas } = placeholders[Number(index)];
|
|
210
|
+
const all = entries.get(locale) ?? [];
|
|
211
|
+
const list = areas === void 0 ? all : inAreas(all, areas);
|
|
168
212
|
const compiled = compileLocale(list, kept);
|
|
169
213
|
shipped.total += list.length;
|
|
170
214
|
shipped.kept += compiled.rows.length;
|
|
@@ -173,9 +217,22 @@ function prosebook(options = {}) {
|
|
|
173
217
|
map: null
|
|
174
218
|
};
|
|
175
219
|
},
|
|
176
|
-
generateBundle() {
|
|
220
|
+
generateBundle(_output, bundle) {
|
|
221
|
+
if (building && options.split === true && this.environment.config.consumer === "client") {
|
|
222
|
+
const files = {};
|
|
223
|
+
for (const chunk of Object.values(bundle)) {
|
|
224
|
+
if (chunk.type !== "chunk" || chunk.facadeModuleId?.startsWith(AREA_ID) !== true) continue;
|
|
225
|
+
const [lead = "", locale] = chunk.facadeModuleId.slice(16).split("/");
|
|
226
|
+
if (locale === void 0) continue;
|
|
227
|
+
const own = generated?.entries.get(locale) ?? [];
|
|
228
|
+
for (const area of membersOf(groups, lead)) if (inAreas(own, [area]).length > 0) (files[area] ??= {})[locale] = base + chunk.fileName;
|
|
229
|
+
}
|
|
230
|
+
writeFiles(/* @__PURE__ */ new Map([[areaFilesPath(viteRoot), `${JSON.stringify(files, null, 2)}\n`]]));
|
|
231
|
+
}
|
|
177
232
|
if (shipped.total === 0) return;
|
|
178
|
-
|
|
233
|
+
const locales = new Set(placeholders.map((p) => p.locale)).size;
|
|
234
|
+
const pruned = prune === false ? "" : `; pruned ${shipped.total - shipped.kept} no code names`;
|
|
235
|
+
this.info(`shipped ${shipped.kept} of ${shipped.total} messages across ${locales} locale(s)${pruned}`);
|
|
179
236
|
}
|
|
180
237
|
};
|
|
181
238
|
}
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * `prosebook/vite`: the catalog as part of the build.\n *\n * Generates on start and again whenever a catalog or translation file changes, so dev never runs on\n * stale modules and a broken entry shows in the error overlay. A build ships only the messages the\n * code names: each locale module is swapped for its messages less the keys no module references\n * (see `tools/scan.ts` for how a reference is found, and why it errs towards keeping).\n *\n * In dev an edit doesn't reload the page: each generated locale module accepts its own update and\n * hands the new messages to every copy (`HotHook`), which re-renders in the locale and state it was in.\n * The index accepts its own too, since its doc comments change with the source text, and reloads the\n * page only when a locale was added or removed. In the browser the index imports the dev module first,\n * which sets up the hooks and the inspector (`inspector.ts`) before any copy exists, so frameworks that\n * render their own HTML (TanStack Start, React Router) get them too. The server opens the entry the\n * inspector asks for through Vite's own `/__open-in-editor`, so the editor is whichever Vite already\n * launches.\n */\nimport { relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { Plugin } from 'vite';\n\nimport { beside, HOT_INDEX, HOT_SET, hotLocale, installInspector, keeper, unseenMessage, unseenOf, type Prune } from './tools/bundle.ts';\nimport { CatalogError, type EntryLocation } from './tools/catalog.ts';\nimport { compileLocale, localeExpression } from './tools/compile.ts';\nimport { loadConfig, type ProsebookConfig, type ResolvedConfig } from './tools/config.ts';\nimport { inspectorEndpoints, nodeMiddleware, redirect } from './tools/endpoints.ts';\nimport { generate, type Generated } from './tools/generate.ts';\nimport { keyRefs, unseenReads, type KeyRefs, type Reads } from './tools/scan.ts';\n\nexport interface ProsebookPluginOptions {\n /** The config, inline. Without it, `prosebook.config.*` at `root` is read. */\n config?: ProsebookConfig;\n /**\n * The folder the config's paths resolve from, and where its file is found: the Vite root when\n * unset. Apps that share one catalog name its folder, so each build generates the same files.\n */\n root?: string;\n /**\n * Ship only the messages the code names. `strict`, the default, also fails the build on a read\n * whose key it can't see, such as `t(item.key)`, which would show the key where its message was\n * pruned. `loose` prunes without that check; `false` ships every message.\n */\n prune?: Prune;\n /** Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. */\n keep?: string[];\n /** In dev, Alt-click a string on the page to open its entry in the editor. On by default. */\n inspect?: boolean;\n}\n\nconst PLACEHOLDER = /__PROSEBOOK_LOCALE_(\\d+)__/g;\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\nconst DEV = 'virtual:prosebook/dev';\nconst DEV_ID = '\\0prosebook/dev';\n\nconst normalize = (path: string) => resolve(path).replaceAll('\\\\', '/');\n\nconst inspectorPath = beside(import.meta.url, 'inspector');\n// prosebook's own modules, which read keys handed to them: `<Text>` calls `rich(props.id)`.\nconst ownDir = normalize(fileURLToPath(new URL('.', import.meta.url)));\n\nexport function prosebook(options: ProsebookPluginOptions = {}): Plugin {\n let config: ResolvedConfig;\n let generated: Generated | undefined;\n let building = false;\n let base = '/';\n let viteRoot = '';\n let kept: (key: string) => boolean = () => true;\n let refs: KeyRefs = { exact: new Set(), prefixes: new Set() };\n const reads = new Map<string, Reads>();\n const prune = options.prune ?? 'strict';\n const placeholders: string[] = [];\n const shipped = { total: 0, kept: 0 };\n\n const describe = (e: unknown) => (e instanceof CatalogError ? e.message : e instanceof Error ? (e.stack ?? e.message) : String(e));\n\n const localeOf = (id: string): string | undefined => {\n const path = normalize(id.split('?', 1)[0]!);\n const out = normalize(config.out);\n if (!path.startsWith(`${out}/`)) return undefined;\n const name = path.slice(out.length + 1).replace(/\\.[jt]s$/, '');\n return generated?.entries.has(name) === true ? name : undefined;\n };\n\n const isIndex = (id: string) => {\n const path = normalize(id.split('?', 1)[0]!);\n const out = normalize(config.out);\n return path === `${out}/index.ts` || path === `${out}/index.js`;\n };\n\n return {\n name: 'prosebook',\n\n async configResolved(resolved) {\n building = resolved.command === 'build';\n base = resolved.base;\n viteRoot = resolved.root;\n config = await loadConfig(options.root === undefined ? resolved.root : resolve(resolved.root, options.root), options.config);\n },\n\n resolveId(id) {\n return id === DEV ? DEV_ID : null;\n },\n\n buildStart() {\n try {\n generated = generate(config);\n } catch (e) {\n this.error(describe(e));\n }\n refs = { exact: new Set(), prefixes: new Set() };\n kept = keeper(refs, options.keep);\n reads.clear();\n placeholders.length = 0;\n shipped.total = shipped.kept = 0;\n },\n\n configureServer(server) {\n const dirs = [normalize(config.catalog), normalize(config.locales)];\n server.watcher.add(dirs);\n const onChange = (file: string) => {\n const path = normalize(file);\n if (!path.endsWith('.yaml') || !dirs.some((d) => path.startsWith(`${d}/`))) return;\n try {\n generated = generate(config);\n server.config.logger.info(`prosebook: regenerated from ${relative(config.root, path).replaceAll('\\\\', '/')}`, {\n timestamp: true,\n });\n } catch (e) {\n server.config.logger.error(describe(e), { timestamp: true });\n server.ws.send({ type: 'error', err: { message: describe(e), stack: '', plugin: 'prosebook' } });\n }\n };\n server.watcher.on('change', onChange);\n server.watcher.on('add', onChange);\n server.watcher.on('unlink', onChange);\n if (options.inspect === false) return;\n\n // Each entry opens through Vite's own `/__open-in-editor`, so the editor is whichever Vite launches.\n const editor = ({ file, line, column }: EntryLocation) =>\n redirect(`${base}__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`);\n server.middlewares.use(nodeMiddleware(inspectorEndpoints(config, { base, open: editor })));\n },\n\n load(id) {\n if (id === DEV_ID) {\n if (options.inspect === false) return HOT_SET;\n return HOT_SET + installInspector(inspectorPath, base);\n }\n if (!building || prune === false || generated === undefined) return null;\n const locale = localeOf(id);\n if (locale === undefined) return null;\n placeholders.push(locale);\n return `export default __PROSEBOOK_LOCALE_${placeholders.length - 1}__;\\n`;\n },\n\n // Before other plugins, so a read is found in the source as written: `<Text id>` still JSX, and\n // the line it is reported at, and its `prosebook-ignore`, where the author wrote them.\n transform: {\n order: 'pre',\n handler(code, id, transformOptions) {\n if (!building) {\n const locale = localeOf(id);\n if (locale !== undefined) return { code: code + hotLocale(locale), map: null };\n if (!isIndex(id)) return null;\n // A server render has no page to inspect, and its copies are made per request.\n const dev = transformOptions?.ssr === true ? '' : `import ${JSON.stringify(DEV)};\\n`;\n return { code: dev + code + HOT_INDEX, map: null };\n }\n if (prune === false || id.includes('/node_modules/') || id.startsWith('\\0')) return null;\n // The index's `Messages` type names every key, and is read here before its types are stripped.\n if (localeOf(id) !== undefined || isIndex(id)) return null;\n keyRefs(code, refs);\n const path = id.split('?', 1)[0]!;\n if (prune === 'strict' && SCRIPT.test(path) && !normalize(path).startsWith(`${ownDir}/`)) reads.set(id, unseenReads(path, code));\n return null;\n },\n },\n\n buildEnd(error) {\n if (error !== undefined || prune !== 'strict') return;\n const live = new Set(this.getModuleIds());\n const unseen = unseenOf([...reads].filter(([id]) => live.has(id)).map(([, r]) => r));\n if (unseen.length > 0) this.error(unseenMessage(unseen, viteRoot));\n },\n\n renderChunk(code) {\n if (!code.includes('__PROSEBOOK_LOCALE_') || generated === undefined) return null;\n const entries = generated.entries;\n const replaced = code.replace(PLACEHOLDER, (_match, index: string) => {\n const locale = placeholders[Number(index)]!;\n const list = entries.get(locale) ?? [];\n const compiled = compileLocale(list, kept);\n shipped.total += list.length;\n shipped.kept += compiled.rows.length;\n return localeExpression(compiled);\n });\n return { code: replaced, map: null };\n },\n\n generateBundle() {\n if (shipped.total === 0) return;\n this.info(\n `shipped ${shipped.kept} of ${shipped.total} messages across ${placeholders.length} locale(s); pruned ${shipped.total - shipped.kept} no code names`,\n );\n },\n };\n}\n\nexport default prosebook;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["/**\n * `prosebook/vite`: the catalog as part of the build.\n *\n * Generates on start and again whenever a catalog or translation file changes, so dev never runs on\n * stale modules and a broken entry shows in the error overlay. A build ships only the messages the\n * code names: each locale module is swapped for its messages less the keys no module references\n * (see `tools/scan.ts` for how a reference is found, and why it errs towards keeping).\n *\n * With `split`, a client's catalog has no messages of its own: each module naming an area's keys\n * imports a module for that area, which the bundler places beside it, and which registers the area\n * (`addArea`) and waits until every copy holds it in its locale. Areas are found by the same\n * references pruning reads, so a read pruning can't see is one splitting can't place either. A lazy\n * locale's part is a file per group of areas the bundler will put in one chunk (`tools/chunks.ts`),\n * read from the source when the build starts, as an area loads before every module naming it has.\n * The client build writes down the file each area loads from per lazy locale, and the server build\n * after it gives its catalog that list (`areaFiles`), so a server render can link what it read.\n *\n * In dev an edit doesn't reload the page: each generated locale module accepts its own update and\n * hands the new messages to every copy (`HotHook`), which re-renders in the locale and state it was in.\n * The index accepts its own too, since its doc comments change with the source text, and reloads the\n * page only when a locale was added or removed. In the browser the index imports the dev module first,\n * which sets up the hooks and the inspector (`inspector.ts`) before any copy exists, so frameworks that\n * render their own HTML (TanStack Start, React Router) get them too. The server opens the entry the\n * inspector asks for through Vite's own `/__open-in-editor`, so the editor is whichever Vite already\n * launches.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { Plugin } from 'vite';\n\nimport {\n AREA,\n areaModule,\n areasNamed,\n lazyAreas,\n beside,\n HOT_INDEX,\n HOT_SET,\n hotLocale,\n importingAreas,\n inAreas,\n installInspector,\n keeper,\n membersOf,\n splitIndex,\n unseenMessage,\n unseenOf,\n type Groups,\n type Prune,\n} from './tools/bundle.ts';\nimport { CatalogError, type EntryLocation } from './tools/catalog.ts';\nimport { areaGroups } from './tools/chunks.ts';\nimport { compileLocale, localeExpression } from './tools/compile.ts';\nimport { loadConfig, type ProsebookConfig, type ResolvedConfig } from './tools/config.ts';\nimport { inspectorEndpoints, nodeMiddleware, redirect } from './tools/endpoints.ts';\nimport { generate, writeFiles, type Generated } from './tools/generate.ts';\nimport { keyRefs, SFC, unseenReads, type KeyRefs, type Reads } from './tools/scan.ts';\n\nexport interface ProsebookPluginOptions {\n /** The config, inline. Without it, `prosebook.config.*` at `root` is read. */\n config?: ProsebookConfig;\n /**\n * The folder the config's paths resolve from, and where its file is found: the Vite root when\n * unset. Apps that share one catalog name its folder, so each build generates the same files.\n */\n root?: string;\n /**\n * Ship only the messages the code names. `strict`, the default, also fails the build on a read\n * whose key it can't see, such as `t(item.key)`, which would show the key where its message was\n * pruned. `loose` prunes without that check; `false` ships every message.\n */\n prune?: Prune;\n /**\n * Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. With\n * `split`, their areas load with the catalog.\n */\n keep?: string[];\n /**\n * Split each locale by area, so a lazy route loads only the messages it reads: a module that names\n * an area's keys imports that area, which the bundler places beside it, and a lazy locale loads\n * each area as it is needed. In the browser only: a server render loads whole locales, and its\n * catalog lists each area's files, which a copy's `preload` hands on as the render reads them.\n */\n split?: boolean;\n /** In dev, Alt-click a string on the page to open its entry in the editor. On by default. */\n inspect?: boolean;\n}\n\nconst PLACEHOLDER = /__PROSEBOOK_LOCALE_(\\d+)__/g;\nconst SCRIPT = /\\.(?:[cm]?[jt]sx?|vue|svelte)$/;\nconst DEV = 'virtual:prosebook/dev';\nconst DEV_ID = '\\0prosebook/dev';\nconst AREA_ID = '\\0prosebook/area/';\n\nconst normalize = (path: string) => resolve(path).replaceAll('\\\\', '/');\n\n/** Where a split client build leaves its area files for the server build: apart from the source and the output. */\nconst areaFilesPath = (root: string) => join(root, 'node_modules', '.prosebook', 'area-files.json');\n\n/** The index with `areaFiles` among its catalog's fields. */\nconst withAreaFiles = (index: string, files: unknown) =>\n index.replace(/defineCatalog(?:<[^>]*>)?\\(\\{/, (call) => `${call}\\n areaFiles: ${JSON.stringify(files)},`);\n\nconst inspectorPath = beside(import.meta.url, 'inspector');\n// prosebook's own modules, which read keys handed to them: `<Text>` calls `rich(props.id)`.\nconst ownDir = normalize(fileURLToPath(new URL('.', import.meta.url)));\n\nexport function prosebook(options: ProsebookPluginOptions = {}): Plugin {\n let config: ResolvedConfig;\n let generated: Generated | undefined;\n let building = false;\n let base = '/';\n let viteRoot = '';\n let kept: (key: string) => boolean = () => true;\n let refs: KeyRefs = { exact: new Set(), prefixes: new Set() };\n const reads = new Map<string, Reads>();\n const prune = options.prune ?? 'strict';\n // What each placeholder stands for: a locale's messages, or with `split`, some areas' of them.\n const placeholders: { locale: string; areas?: string[] }[] = [];\n let groups: Groups = new Map();\n const shipped = { total: 0, kept: 0 };\n\n const describe = (e: unknown) => (e instanceof CatalogError ? e.message : e instanceof Error ? (e.stack ?? e.message) : String(e));\n\n const localeOf = (id: string): string | undefined => {\n const path = normalize(id.split('?', 1)[0]!);\n const out = normalize(config.out);\n if (!path.startsWith(`${out}/`)) return undefined;\n const name = path.slice(out.length + 1).replace(/\\.[jt]s$/, '');\n return generated?.entries.has(name) === true ? name : undefined;\n };\n\n const isIndex = (id: string) => {\n const path = normalize(id.split('?', 1)[0]!);\n const out = normalize(config.out);\n return path === `${out}/index.ts` || path === `${out}/index.js`;\n };\n\n // Split in the browser only: a server render's copies, one per request, load whole locales.\n const splitting = (ssr: boolean | undefined) => options.split === true && ssr !== true && generated !== undefined;\n const placeholder = (locale: string, areas?: string[]) => `__PROSEBOOK_LOCALE_${placeholders.push({ locale, areas }) - 1}__`;\n\n return {\n name: 'prosebook',\n\n async configResolved(resolved) {\n building = resolved.command === 'build';\n base = resolved.base;\n viteRoot = resolved.root;\n config = await loadConfig(options.root === undefined ? resolved.root : resolve(resolved.root, options.root), options.config);\n },\n\n resolveId(id) {\n if (id === DEV) return DEV_ID;\n if (id.startsWith(AREA)) return AREA_ID + id.slice(AREA.length);\n return null;\n },\n\n buildStart() {\n try {\n generated = generate(config);\n } catch (e) {\n this.error(describe(e));\n }\n refs = { exact: new Set(), prefixes: new Set() };\n kept = prune === false ? () => true : keeper(refs, options.keep);\n reads.clear();\n groups = building && options.split === true && generated !== undefined ? areaGroups(config, generated, options.keep) : new Map();\n placeholders.length = 0;\n shipped.total = shipped.kept = 0;\n },\n\n configureServer(server) {\n const dirs = [normalize(config.catalog), normalize(config.locales)];\n server.watcher.add(dirs);\n const onChange = (file: string) => {\n const path = normalize(file);\n if (!path.endsWith('.yaml') || !dirs.some((d) => path.startsWith(`${d}/`))) return;\n try {\n generated = generate(config);\n // An area's module names the locales, which an edit may have added to.\n for (const [id, mod] of server.moduleGraph.idToModuleMap) if (id.startsWith(AREA_ID)) server.moduleGraph.invalidateModule(mod);\n server.config.logger.info(`prosebook: regenerated from ${relative(config.root, path).replaceAll('\\\\', '/')}`, {\n timestamp: true,\n });\n } catch (e) {\n server.config.logger.error(describe(e), { timestamp: true });\n server.ws.send({ type: 'error', err: { message: describe(e), stack: '', plugin: 'prosebook' } });\n }\n };\n server.watcher.on('change', onChange);\n server.watcher.on('add', onChange);\n server.watcher.on('unlink', onChange);\n if (options.inspect === false) return;\n\n // Each entry opens through Vite's own `/__open-in-editor`, so the editor is whichever Vite launches.\n const editor = ({ file, line, column }: EntryLocation) =>\n redirect(`${base}__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`);\n server.middlewares.use(nodeMiddleware(inspectorEndpoints(config, { base, open: editor })));\n },\n\n load(id, loadOptions) {\n if (id === DEV_ID) {\n if (options.inspect === false) return HOT_SET;\n return HOT_SET + installInspector(inspectorPath, base);\n }\n if (id.startsWith(AREA_ID) && generated !== undefined) {\n const [area = '', locale] = id.slice(AREA_ID.length).split('/');\n if (locale !== undefined) return `export default ${placeholder(locale, membersOf(groups, area))};\\n`;\n return areaModule(config, generated, area, building ? (l) => placeholder(l, [area]) : undefined, undefined, groups);\n }\n if (splitting(loadOptions?.ssr) && isIndex(id)) return splitIndex(config, generated!, options.keep);\n if (building && options.split === true && loadOptions?.ssr === true && isIndex(id)) {\n const path = areaFilesPath(viteRoot);\n if (existsSync(path)) return withAreaFiles(readFileSync(id.split('?', 1)[0]!, 'utf8'), JSON.parse(readFileSync(path, 'utf8')));\n this.warn('no split client build to link area files from: build the client before the server, or `preload` is handed nothing');\n }\n if (!building || prune === false || generated === undefined) return null;\n const locale = localeOf(id);\n if (locale === undefined) return null;\n return `export default ${placeholder(locale)};\\n`;\n },\n\n // Before other plugins, so a read is found in the source as written: `<Text id>` still JSX, and\n // the line it is reported at, and its `prosebook-ignore`, where the author wrote them.\n transform: {\n order: 'pre',\n handler(code, id, transformOptions) {\n const ssr = transformOptions?.ssr;\n if (!building) {\n const locale = localeOf(id);\n if (locale !== undefined) return { code: code + hotLocale(locale), map: null };\n if (isIndex(id)) {\n // A server render has no page to inspect, and its copies are made per request.\n const dev = ssr === true ? '' : `import ${JSON.stringify(DEV)};\\n`;\n return { code: dev + code + HOT_INDEX, map: null };\n }\n // The index's `Messages` type names every key, and is read here before its types are stripped.\n } else if (localeOf(id) !== undefined || isIndex(id)) {\n return null;\n }\n if (id.includes('/node_modules/') || id.startsWith('\\0')) return null;\n const path = id.split('?', 1)[0]!;\n // A component file is read whole, as written; the parts its compiler splits off (`?vue&type=script`) are not again.\n const part = SFC.test(path) && path !== id;\n const own = normalize(path).startsWith(`${ownDir}/`);\n if (building && prune !== false) {\n keyRefs(code, refs);\n if (prune === 'strict' && SCRIPT.test(path) && !part && !own) reads.set(id, unseenReads(path, code));\n }\n if (!splitting(ssr) || !SCRIPT.test(path) || part || own) return null;\n const areas = areasNamed(code, lazyAreas(generated!, options.keep));\n return areas.length === 0 ? null : { code: importingAreas(code, path, areas), map: null };\n },\n },\n\n buildEnd(error) {\n if (error !== undefined || prune !== 'strict') return;\n const live = new Set(this.getModuleIds());\n const unseen = unseenOf([...reads].filter(([id]) => live.has(id)).map(([, r]) => r));\n if (unseen.length > 0) this.error(unseenMessage(unseen, viteRoot));\n },\n\n renderChunk(code) {\n if (!code.includes('__PROSEBOOK_LOCALE_') || generated === undefined) return null;\n const entries = generated.entries;\n const replaced = code.replace(PLACEHOLDER, (_match, index: string) => {\n const { locale, areas } = placeholders[Number(index)]!;\n const all = entries.get(locale) ?? [];\n const list = areas === undefined ? all : inAreas(all, areas);\n const compiled = compileLocale(list, kept);\n shipped.total += list.length;\n shipped.kept += compiled.rows.length;\n return localeExpression(compiled);\n });\n return { code: replaced, map: null };\n },\n\n generateBundle(_output, bundle) {\n if (building && options.split === true && this.environment.config.consumer === 'client') {\n // Each lazy locale's part of a group, for the areas in it that locale translates.\n const files: Record<string, Record<string, string>> = {};\n for (const chunk of Object.values(bundle)) {\n if (chunk.type !== 'chunk' || chunk.facadeModuleId?.startsWith(AREA_ID) !== true) continue;\n const [lead = '', locale] = chunk.facadeModuleId.slice(AREA_ID.length).split('/');\n if (locale === undefined) continue;\n const own = generated?.entries.get(locale) ?? [];\n for (const area of membersOf(groups, lead))\n if (inAreas(own, [area]).length > 0) (files[area] ??= {})[locale] = base + chunk.fileName;\n }\n writeFiles(new Map([[areaFilesPath(viteRoot), `${JSON.stringify(files, null, 2)}\\n`]]));\n }\n if (shipped.total === 0) return;\n const locales = new Set(placeholders.map((p) => p.locale)).size;\n const pruned = prune === false ? '' : `; pruned ${shipped.total - shipped.kept} no code names`;\n this.info(`shipped ${shipped.kept} of ${shipped.total} messages across ${locales} locale(s)${pruned}`);\n },\n };\n}\n\nexport default prosebook;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { n as LooseTextProps, t as BoundCopy } from "../bind-ZS86jHXk.js";
|
|
2
|
+
import { T as TagFn, b as RegisteredLocale, n as Copy, x as RegisteredMessages } from "../copy-BXn0aYQn.js";
|
|
3
|
+
import { ComputedRef, Plugin, Slot, VNode, VNodeChild } from "vue";
|
|
4
|
+
type M = RegisteredMessages;
|
|
5
|
+
/** `Copy`, with `rich` returning a `VNode`, whose reads re-render what calls them. */
|
|
6
|
+
export type VueCopy = BoundCopy<VNodeChild, VNode>;
|
|
7
|
+
type Tags = {
|
|
8
|
+
readonly [tag: string]: TagFn<VNodeChild>;
|
|
9
|
+
};
|
|
10
|
+
export interface CopyOptions {
|
|
11
|
+
/** Tag renderers every rich read gets, beside the copy's own: `link: (chunks) => h('a', { href }, chunks)`. */
|
|
12
|
+
tags?: Tags;
|
|
13
|
+
}
|
|
14
|
+
/** The copy for the whole app: `app.use(copyPlugin(copy, { tags }))`. */
|
|
15
|
+
export declare function copyPlugin(copy: Copy, options?: CopyOptions): Plugin;
|
|
16
|
+
/** The copy for this component and those under it, in place of the app's. Call it in `setup`. */
|
|
17
|
+
export declare function provideCopy(copy: Copy, options?: CopyOptions): VueCopy;
|
|
18
|
+
/** The catalog, in the current locale. A template or `computed` reading it runs again when the locale changes, or in dev the catalog. */
|
|
19
|
+
export declare function useCopy(): VueCopy;
|
|
20
|
+
/** The current locale, as a ref. */
|
|
21
|
+
export declare function useLocale(): ComputedRef<RegisteredLocale>;
|
|
22
|
+
/** A slot per tag: each gets what the tag wraps as `chunks`, a `VNode` to render with `<component :is="chunks" />`. */
|
|
23
|
+
export type TextSlots = {
|
|
24
|
+
[tag: string]: Slot<{
|
|
25
|
+
chunks: VNode;
|
|
26
|
+
}> | undefined;
|
|
27
|
+
};
|
|
28
|
+
export type TextProps<K extends Extract<keyof M, string>> = LooseTextProps<K, VNodeChild>;
|
|
29
|
+
/**
|
|
30
|
+
* A message in a template: `<Text id="cart.items" :values="{ count }" />`. Its tags render through
|
|
31
|
+
* a slot of the same name, a function in `values` or the provider's renderers, in that order.
|
|
32
|
+
*
|
|
33
|
+
* ```vue
|
|
34
|
+
* <Text id="cart.terms">
|
|
35
|
+
* <template #link="{ chunks }"><a href="/terms"><component :is="chunks" /></a></template>
|
|
36
|
+
* </Text>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export declare const Text: <K extends Extract<keyof M, string>>(props: TextProps<K>, context: {
|
|
40
|
+
slots: TextSlots;
|
|
41
|
+
}) => VNode;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/vue/index.ts"],"mappings":";;;KA+BK,IAAI;;YAGG,UAAU,UAAU,YAAY;KAEvC;YAAmB,cAAc,MAAM;;iBAE3B;;EAEf,OAAO;;;wBAmBO,WAAW,MAAM,MAAM,UAAS,cAAmB;;wBAWnD,YAAY,MAAM,MAAM,UAAS,cAAmB;;wBAQpD,WAAW;;wBAOX,aAAa,YAAY;;YAM7B;GAAe,cAAc;IAAO,QAAQ;;;YAE5C,UAAU,UAAU,cAAc,cAAc,eAAe,GAAG;;;;;;;;;;;qBAYjE,OAWI,UAAU,cAAc,YAAY,OAAO,UAAU,IAAI;EAAW,OAAO;MAAgB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { t as bindCopy } from "../bind-BIZNv8nU.js";
|
|
2
|
+
import { Fragment, computed, defineComponent, h, inject, onScopeDispose, provide, shallowRef } from "vue";
|
|
3
|
+
// src/vue/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* `prosebook/vue`: the catalog in Vue components.
|
|
6
|
+
*
|
|
7
|
+
* `useCopy()` reads through a ref the copy bumps on every change, so a template or `computed` that
|
|
8
|
+
* reads a message runs again when the locale switches, with nothing to watch by hand. `rich` returns a
|
|
9
|
+
* `VNode`, which a render function returns as it is and a template renders with
|
|
10
|
+
* `<component :is="...">`; `<Text>` renders one in a template, its tags from slots.
|
|
11
|
+
*/
|
|
12
|
+
const KEY = Symbol("prosebook");
|
|
13
|
+
/** The reader and what stops it following the copy. */
|
|
14
|
+
function bound(copy, tags = {}) {
|
|
15
|
+
const version = shallowRef(copy.version);
|
|
16
|
+
const stop = copy.subscribe(() => {
|
|
17
|
+
version.value = copy.version;
|
|
18
|
+
});
|
|
19
|
+
return [bindCopy(copy, tags, {
|
|
20
|
+
track: () => void version.value,
|
|
21
|
+
whole: (parts) => h(Fragment, null, parts)
|
|
22
|
+
}), stop];
|
|
23
|
+
}
|
|
24
|
+
/** The copy for the whole app: `app.use(copyPlugin(copy, { tags }))`. */
|
|
25
|
+
function copyPlugin(copy, options = {}) {
|
|
26
|
+
return { install(app) {
|
|
27
|
+
const [reader, stop] = bound(copy, options.tags);
|
|
28
|
+
app.provide(KEY, reader);
|
|
29
|
+
app.onUnmount(stop);
|
|
30
|
+
} };
|
|
31
|
+
}
|
|
32
|
+
/** The copy for this component and those under it, in place of the app's. Call it in `setup`. */
|
|
33
|
+
function provideCopy(copy, options = {}) {
|
|
34
|
+
const [reader, stop] = bound(copy, options.tags);
|
|
35
|
+
provide(KEY, reader);
|
|
36
|
+
onScopeDispose(stop);
|
|
37
|
+
return reader;
|
|
38
|
+
}
|
|
39
|
+
/** The catalog, in the current locale. A template or `computed` reading it runs again when the locale changes, or in dev the catalog. */
|
|
40
|
+
function useCopy() {
|
|
41
|
+
const reader = inject(KEY, null);
|
|
42
|
+
if (reader === null) throw new Error("prosebook: useCopy() needs copyPlugin(copy) installed, or provideCopy(copy) above it");
|
|
43
|
+
return reader;
|
|
44
|
+
}
|
|
45
|
+
/** The current locale, as a ref. */
|
|
46
|
+
function useLocale() {
|
|
47
|
+
const reader = useCopy();
|
|
48
|
+
return computed(() => reader.locale);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A message in a template: `<Text id="cart.items" :values="{ count }" />`. Its tags render through
|
|
52
|
+
* a slot of the same name, a function in `values` or the provider's renderers, in that order.
|
|
53
|
+
*
|
|
54
|
+
* ```vue
|
|
55
|
+
* <Text id="cart.terms">
|
|
56
|
+
* <template #link="{ chunks }"><a href="/terms"><component :is="chunks" /></a></template>
|
|
57
|
+
* </Text>
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
const Text = defineComponent((props, { slots }) => {
|
|
61
|
+
const copy = useCopy();
|
|
62
|
+
return () => {
|
|
63
|
+
const fromSlots = {};
|
|
64
|
+
for (const [name, slot] of Object.entries(slots)) if (slot !== void 0) fromSlots[name] = (chunks) => slot({ chunks: h(Fragment, null, chunks) });
|
|
65
|
+
return copy.rich(props.id, {
|
|
66
|
+
...fromSlots,
|
|
67
|
+
...props.values
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
}, {
|
|
71
|
+
name: "Text",
|
|
72
|
+
props: ["id", "values"]
|
|
73
|
+
});
|
|
74
|
+
export { Text, copyPlugin, provideCopy, useCopy, useLocale };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/vue/index.ts"],"sourcesContent":["/**\n * `prosebook/vue`: the catalog in Vue components.\n *\n * `useCopy()` reads through a ref the copy bumps on every change, so a template or `computed` that\n * reads a message runs again when the locale switches, with nothing to watch by hand. `rich` returns a\n * `VNode`, which a render function returns as it is and a template renders with\n * `<component :is=\"...\">`; `<Text>` renders one in a template, its tags from slots.\n */\nimport {\n computed,\n defineComponent,\n Fragment,\n h,\n inject,\n onScopeDispose,\n provide,\n shallowRef,\n type App,\n type ComputedRef,\n type InjectionKey,\n type Plugin,\n type SetupContext,\n type Slot,\n type VNode,\n type VNodeChild,\n} from 'vue';\n\nimport { bindCopy, type BoundCopy, type LooseTextProps } from '#src/runtime/bind.ts';\nimport type { Copy } from '#src/runtime/copy.ts';\nimport type { RegisteredLocale, RegisteredMessages, TagFn } from '#src/runtime/types.ts';\n\ntype M = RegisteredMessages;\n\n/** `Copy`, with `rich` returning a `VNode`, whose reads re-render what calls them. */\nexport type VueCopy = BoundCopy<VNodeChild, VNode>;\n\ntype Tags = { readonly [tag: string]: TagFn<VNodeChild> };\n\nexport interface CopyOptions {\n /** Tag renderers every rich read gets, beside the copy's own: `link: (chunks) => h('a', { href }, chunks)`. */\n tags?: Tags;\n}\n\nconst KEY: InjectionKey<VueCopy> = Symbol('prosebook');\n\n/** The reader and what stops it following the copy. */\nfunction bound(copy: Copy, tags: Tags = {}): [VueCopy, () => void] {\n const version = shallowRef(copy.version);\n const stop = copy.subscribe(() => {\n version.value = copy.version;\n });\n const reader = bindCopy<VNodeChild, VNode>(copy, tags, {\n track: () => void version.value,\n whole: (parts) => h(Fragment, null, parts as VNodeChild[]),\n });\n return [reader, stop];\n}\n\n/** The copy for the whole app: `app.use(copyPlugin(copy, { tags }))`. */\nexport function copyPlugin(copy: Copy, options: CopyOptions = {}): Plugin {\n return {\n install(app: App) {\n const [reader, stop] = bound(copy, options.tags);\n app.provide(KEY, reader);\n app.onUnmount(stop);\n },\n };\n}\n\n/** The copy for this component and those under it, in place of the app's. Call it in `setup`. */\nexport function provideCopy(copy: Copy, options: CopyOptions = {}): VueCopy {\n const [reader, stop] = bound(copy, options.tags);\n provide(KEY, reader);\n onScopeDispose(stop);\n return reader;\n}\n\n/** The catalog, in the current locale. A template or `computed` reading it runs again when the locale changes, or in dev the catalog. */\nexport function useCopy(): VueCopy {\n const reader = inject(KEY, null);\n if (reader === null) throw new Error('prosebook: useCopy() needs copyPlugin(copy) installed, or provideCopy(copy) above it');\n return reader;\n}\n\n/** The current locale, as a ref. */\nexport function useLocale(): ComputedRef<RegisteredLocale> {\n const reader = useCopy();\n return computed(() => reader.locale);\n}\n\n/** A slot per tag: each gets what the tag wraps as `chunks`, a `VNode` to render with `<component :is=\"chunks\" />`. */\nexport type TextSlots = { [tag: string]: Slot<{ chunks: VNode }> | undefined };\n\nexport type TextProps<K extends Extract<keyof M, string>> = LooseTextProps<K, VNodeChild>;\n\n/**\n * A message in a template: `<Text id=\"cart.items\" :values=\"{ count }\" />`. Its tags render through\n * a slot of the same name, a function in `values` or the provider's renderers, in that order.\n *\n * ```vue\n * <Text id=\"cart.terms\">\n * <template #link=\"{ chunks }\"><a href=\"/terms\"><component :is=\"chunks\" /></a></template>\n * </Text>\n * ```\n */\nexport const Text = defineComponent(\n (props: { id: string; values?: object }, { slots }: SetupContext) => {\n const copy = useCopy();\n return () => {\n const fromSlots: Record<string, unknown> = {};\n for (const [name, slot] of Object.entries(slots))\n if (slot !== undefined) fromSlots[name] = (chunks: VNodeChild[]) => slot({ chunks: h(Fragment, null, chunks) });\n return copy.rich(props.id as never, { ...fromSlots, ...props.values } as never);\n };\n },\n { name: 'Text', props: ['id', 'values'] },\n) as unknown as <K extends Extract<keyof M, string>>(props: TextProps<K>, context: { slots: TextSlots }) => VNode;\n"],"mappings":";;;;;;;;;;;AA2CA,MAAM,MAA6B,OAAO,WAAW;;AAGrD,SAAS,MAAM,MAAY,OAAa,CAAC,GAA0B;CACjE,MAAM,UAAU,WAAW,KAAK,OAAO;CACvC,MAAM,OAAO,KAAK,gBAAgB;EAChC,QAAQ,QAAQ,KAAK;CACvB,CAAC;CAKD,OAAO,CAJQ,SAA4B,MAAM,MAAM;EACrD,aAAa,KAAK,QAAQ;EAC1B,QAAQ,UAAU,EAAE,UAAU,MAAM,KAAqB;CAC3D,CACa,GAAG,IAAI;AACtB;;AAGA,SAAgB,WAAW,MAAY,UAAuB,CAAC,GAAW;CACxE,OAAO,EACL,QAAQ,KAAU;EAChB,MAAM,CAAC,QAAQ,QAAQ,MAAM,MAAM,QAAQ,IAAI;EAC/C,IAAI,QAAQ,KAAK,MAAM;EACvB,IAAI,UAAU,IAAI;CACpB,EACF;AACF;;AAGA,SAAgB,YAAY,MAAY,UAAuB,CAAC,GAAY;CAC1E,MAAM,CAAC,QAAQ,QAAQ,MAAM,MAAM,QAAQ,IAAI;CAC/C,QAAQ,KAAK,MAAM;CACnB,eAAe,IAAI;CACnB,OAAO;AACT;;AAGA,SAAgB,UAAmB;CACjC,MAAM,SAAS,OAAO,KAAK,IAAI;CAC/B,IAAI,WAAW,MAAM,MAAM,IAAI,MAAM,sFAAsF;CAC3H,OAAO;AACT;;AAGA,SAAgB,YAA2C;CACzD,MAAM,SAAS,QAAQ;CACvB,OAAO,eAAe,OAAO,MAAM;AACrC;;;;;;;;;;;AAiBA,MAAa,OAAO,iBACjB,OAAwC,EAAE,YAA0B;CACnE,MAAM,OAAO,QAAQ;CACrB,aAAa;EACX,MAAM,YAAqC,CAAC;EAC5C,KAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,KAAK,GAC7C,IAAI,SAAS,KAAA,GAAW,UAAU,SAAS,WAAyB,KAAK,EAAE,QAAQ,EAAE,UAAU,MAAM,MAAM,EAAE,CAAC;EAChH,OAAO,KAAK,KAAK,MAAM,IAAa;GAAE,GAAG;GAAW,GAAG,MAAM;EAAO,CAAU;CAChF;AACF,GACA;CAAE,MAAM;CAAQ,OAAO,CAAC,MAAM,QAAQ;AAAE,CAC1C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prosebook",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Every string your app shows, in one typed catalog: ICU messages compiled to plain functions, for React and any JavaScript.",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Every string your app shows, in one typed catalog: ICU messages compiled to plain functions, for React, Preact, Vue, Svelte, Solid and any JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
7
7
|
"i18n",
|
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
"messageformat",
|
|
13
13
|
"messageformat2",
|
|
14
14
|
"nextjs",
|
|
15
|
+
"preact",
|
|
15
16
|
"react",
|
|
17
|
+
"solid",
|
|
18
|
+
"svelte",
|
|
16
19
|
"translation",
|
|
17
20
|
"typescript",
|
|
18
21
|
"ux-writing",
|
|
19
|
-
"vite"
|
|
22
|
+
"vite",
|
|
23
|
+
"vue"
|
|
20
24
|
],
|
|
21
25
|
"homepage": "https://gkurt.com/prosebook/",
|
|
22
26
|
"bugs": {
|
|
@@ -72,6 +76,38 @@
|
|
|
72
76
|
"default": "./dist/react/server.js"
|
|
73
77
|
}
|
|
74
78
|
},
|
|
79
|
+
"./preact": {
|
|
80
|
+
"prosebook@dev": "./src/preact/index.ts",
|
|
81
|
+
"import": {
|
|
82
|
+
"types": "./dist/preact/index.d.ts",
|
|
83
|
+
"default": "./dist/preact/index.js"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"./vue": {
|
|
87
|
+
"prosebook@dev": "./src/vue/index.ts",
|
|
88
|
+
"import": {
|
|
89
|
+
"types": "./dist/vue/index.d.ts",
|
|
90
|
+
"default": "./dist/vue/index.js"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"./solid": {
|
|
94
|
+
"prosebook@dev": "./src/solid/index.ts",
|
|
95
|
+
"import": {
|
|
96
|
+
"types": "./dist/solid/index.d.ts",
|
|
97
|
+
"default": "./dist/solid/index.js"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"./svelte": {
|
|
101
|
+
"prosebook@dev": "./src/svelte/index.ts",
|
|
102
|
+
"svelte": {
|
|
103
|
+
"types": "./dist/svelte/index.d.ts",
|
|
104
|
+
"default": "./dist/svelte/index.js"
|
|
105
|
+
},
|
|
106
|
+
"import": {
|
|
107
|
+
"types": "./dist/svelte/index.d.ts",
|
|
108
|
+
"default": "./dist/svelte/index.js"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
75
111
|
"./vite": {
|
|
76
112
|
"prosebook@dev": "./src/vite.ts",
|
|
77
113
|
"import": {
|
|
@@ -145,30 +181,50 @@
|
|
|
145
181
|
"oxfmt": "^0.75.0",
|
|
146
182
|
"oxlint": "^1.90.0",
|
|
147
183
|
"oxlint-tsgolint": "^7.0.2005",
|
|
184
|
+
"preact": "^11.1.1",
|
|
148
185
|
"react": "^19.3.1",
|
|
149
186
|
"react-dom": "^19.3.1",
|
|
187
|
+
"solid-js": "^1.9.18",
|
|
188
|
+
"svelte": "^5.57.3",
|
|
150
189
|
"tegami": "^1.5.0",
|
|
151
190
|
"tsdown": "^0.23.4",
|
|
152
191
|
"typescript": "^7.0.2",
|
|
153
|
-
"vite": "^8.3.5"
|
|
192
|
+
"vite": "^8.3.5",
|
|
193
|
+
"vue": "^3.5.44"
|
|
154
194
|
},
|
|
155
195
|
"peerDependencies": {
|
|
156
196
|
"next": ">=16",
|
|
197
|
+
"preact": ">=10",
|
|
157
198
|
"react": ">=18",
|
|
199
|
+
"solid-js": ">=1.8",
|
|
200
|
+
"svelte": ">=5.7",
|
|
158
201
|
"typescript": ">=7",
|
|
159
|
-
"vite": ">=6"
|
|
202
|
+
"vite": ">=6",
|
|
203
|
+
"vue": ">=3.5"
|
|
160
204
|
},
|
|
161
205
|
"peerDependenciesMeta": {
|
|
206
|
+
"next": {
|
|
207
|
+
"optional": true
|
|
208
|
+
},
|
|
209
|
+
"preact": {
|
|
210
|
+
"optional": true
|
|
211
|
+
},
|
|
162
212
|
"react": {
|
|
163
213
|
"optional": true
|
|
164
214
|
},
|
|
165
|
-
"
|
|
215
|
+
"solid-js": {
|
|
216
|
+
"optional": true
|
|
217
|
+
},
|
|
218
|
+
"svelte": {
|
|
166
219
|
"optional": true
|
|
167
220
|
},
|
|
168
221
|
"typescript": {
|
|
169
222
|
"optional": true
|
|
170
223
|
},
|
|
171
|
-
"
|
|
224
|
+
"vite": {
|
|
225
|
+
"optional": true
|
|
226
|
+
},
|
|
227
|
+
"vue": {
|
|
172
228
|
"optional": true
|
|
173
229
|
}
|
|
174
230
|
},
|