prosebook 0.4.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.
Files changed (122) hide show
  1. package/README.md +288 -28
  2. package/dist/bind-BIZNv8nU.js +62 -0
  3. package/dist/bind-BIZNv8nU.js.map +1 -0
  4. package/dist/bind-ZS86jHXk.d.ts +31 -0
  5. package/dist/bind-ZS86jHXk.d.ts.map +1 -0
  6. package/dist/bun.d.ts +11 -2
  7. package/dist/bun.d.ts.map +1 -1
  8. package/dist/bun.js +77 -26
  9. package/dist/bun.js.map +1 -1
  10. package/dist/{bundle-CpUIWb9j.d.ts → bundle-CLHY_97D.d.ts} +2 -2
  11. package/dist/bundle-CLHY_97D.d.ts.map +1 -0
  12. package/dist/bundle-Ut6Y2X6N.js +217 -0
  13. package/dist/bundle-Ut6Y2X6N.js.map +1 -0
  14. package/dist/cli.js +55 -12
  15. package/dist/cli.js.map +1 -1
  16. package/dist/{config-nWZf9QmF.js → config-7T4hX_f7.js} +5 -3
  17. package/dist/{config-nWZf9QmF.js.map → config-7T4hX_f7.js.map} +1 -1
  18. package/dist/config-CzaJJQ60.d.ts.map +1 -1
  19. package/dist/config.js +1 -1
  20. package/dist/{copy-B3V_QRkT.d.ts → copy-BXn0aYQn.d.ts} +37 -3
  21. package/dist/copy-BXn0aYQn.d.ts.map +1 -0
  22. package/dist/copy-Bv6S-Hr2.js +235 -0
  23. package/dist/copy-Bv6S-Hr2.js.map +1 -0
  24. package/dist/endpoints-Dhgowovz.js +217 -0
  25. package/dist/endpoints-Dhgowovz.js.map +1 -0
  26. package/dist/generate-1BeQAd1O.d.ts +72 -0
  27. package/dist/generate-1BeQAd1O.d.ts.map +1 -0
  28. package/dist/{generate-BW3N8EGb.js → generate-CKv86Iki.js} +1091 -309
  29. package/dist/generate-CKv86Iki.js.map +1 -0
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.js +2 -2
  32. package/dist/next-loader.d.ts +9 -2
  33. package/dist/next-loader.d.ts.map +1 -1
  34. package/dist/next-loader.js +12 -3
  35. package/dist/next-loader.js.map +1 -1
  36. package/dist/next.d.ts +10 -2
  37. package/dist/next.d.ts.map +1 -1
  38. package/dist/next.js +94 -21
  39. package/dist/next.js.map +1 -1
  40. package/dist/preact/index.d.ts +34 -0
  41. package/dist/preact/index.d.ts.map +1 -0
  42. package/dist/preact/index.js +72 -0
  43. package/dist/preact/index.js.map +1 -0
  44. package/dist/react/index.d.ts +5 -14
  45. package/dist/react/index.d.ts.map +1 -1
  46. package/dist/react/index.js +9 -33
  47. package/dist/react/index.js.map +1 -1
  48. package/dist/react/server.d.ts +1 -1
  49. package/dist/react/server.js +1 -1
  50. package/dist/{sarif-B5nrMqrT.js → sarif-BnwYC78_.js} +8 -98
  51. package/dist/sarif-BnwYC78_.js.map +1 -0
  52. package/dist/{scan-Bx0_yDSi.js → scan-C0ap4Mpp.js} +145 -20
  53. package/dist/scan-C0ap4Mpp.js.map +1 -0
  54. package/dist/solid/index.d.ts +29 -0
  55. package/dist/solid/index.d.ts.map +1 -0
  56. package/dist/solid/index.js +48 -0
  57. package/dist/solid/index.js.map +1 -0
  58. package/dist/svelte/Text.svelte +13 -0
  59. package/dist/svelte/Text.svelte.d.ts +20 -0
  60. package/dist/svelte/index.d.ts +31 -0
  61. package/dist/svelte/index.d.ts.map +1 -0
  62. package/dist/svelte/index.js +35 -0
  63. package/dist/svelte/index.js.map +1 -0
  64. package/dist/tools.d.ts +43 -64
  65. package/dist/tools.d.ts.map +1 -1
  66. package/dist/tools.js +5 -5
  67. package/dist/vite.d.ts +12 -2
  68. package/dist/vite.d.ts.map +1 -1
  69. package/dist/vite.js +82 -25
  70. package/dist/vite.js.map +1 -1
  71. package/dist/vue/index.d.ts +43 -0
  72. package/dist/vue/index.d.ts.map +1 -0
  73. package/dist/vue/index.js +76 -0
  74. package/dist/vue/index.js.map +1 -0
  75. package/package.json +63 -7
  76. package/src/bun.ts +77 -20
  77. package/src/cli.ts +62 -8
  78. package/src/index.ts +1 -1
  79. package/src/next-loader.ts +17 -5
  80. package/src/next.ts +120 -16
  81. package/src/preact/index.ts +91 -0
  82. package/src/react/index.tsx +12 -42
  83. package/src/runtime/bind.ts +96 -0
  84. package/src/runtime/copy.ts +148 -24
  85. package/src/solid/index.ts +67 -0
  86. package/src/svelte/Text.svelte +13 -0
  87. package/src/svelte/Text.svelte.d.ts +20 -0
  88. package/src/svelte/index.ts +65 -0
  89. package/src/tools/bundle.ts +143 -2
  90. package/src/tools/catalog.ts +10 -1
  91. package/src/tools/chunks.ts +142 -0
  92. package/src/tools/config.ts +1 -1
  93. package/src/tools/entry.ts +19 -20
  94. package/src/tools/exchange.ts +275 -0
  95. package/src/tools/extract.ts +2 -2
  96. package/src/tools/generate.ts +7 -1
  97. package/src/tools/index.ts +2 -1
  98. package/src/tools/native/android.ts +10 -25
  99. package/src/tools/native/apple.ts +70 -8
  100. package/src/tools/native/date.ts +219 -0
  101. package/src/tools/native/kotlin.ts +47 -6
  102. package/src/tools/native/model.ts +42 -16
  103. package/src/tools/native/printf.ts +46 -4
  104. package/src/tools/native/rust.ts +86 -5
  105. package/src/tools/native/swift.ts +62 -6
  106. package/src/tools/scan.ts +146 -25
  107. package/src/tools/todo.ts +6 -1
  108. package/src/vite.ts +121 -28
  109. package/src/vue/index.ts +117 -0
  110. package/dist/bundle-CpUIWb9j.d.ts.map +0 -1
  111. package/dist/bundle-DboItAre.js +0 -111
  112. package/dist/bundle-DboItAre.js.map +0 -1
  113. package/dist/compile-ClK9htKr.d.ts +0 -16
  114. package/dist/compile-ClK9htKr.d.ts.map +0 -1
  115. package/dist/copy-B3V_QRkT.d.ts.map +0 -1
  116. package/dist/copy-Bo_FIoLy.js +0 -154
  117. package/dist/copy-Bo_FIoLy.js.map +0 -1
  118. package/dist/endpoints-BgG_5Vpm.js +0 -96
  119. package/dist/endpoints-BgG_5Vpm.js.map +0 -1
  120. package/dist/generate-BW3N8EGb.js.map +0 -1
  121. package/dist/sarif-B5nrMqrT.js.map +0 -1
  122. package/dist/scan-Bx0_yDSi.js.map +0 -1
@@ -0,0 +1,117 @@
1
+ /**
2
+ * `prosebook/vue`: the catalog in Vue components.
3
+ *
4
+ * `useCopy()` reads through a ref the copy bumps on every change, so a template or `computed` that
5
+ * reads a message runs again when the locale switches, with nothing to watch by hand. `rich` returns a
6
+ * `VNode`, which a render function returns as it is and a template renders with
7
+ * `<component :is="...">`; `<Text>` renders one in a template, its tags from slots.
8
+ */
9
+ import {
10
+ computed,
11
+ defineComponent,
12
+ Fragment,
13
+ h,
14
+ inject,
15
+ onScopeDispose,
16
+ provide,
17
+ shallowRef,
18
+ type App,
19
+ type ComputedRef,
20
+ type InjectionKey,
21
+ type Plugin,
22
+ type SetupContext,
23
+ type Slot,
24
+ type VNode,
25
+ type VNodeChild,
26
+ } from 'vue';
27
+
28
+ import { bindCopy, type BoundCopy, type LooseTextProps } from '#src/runtime/bind.ts';
29
+ import type { Copy } from '#src/runtime/copy.ts';
30
+ import type { RegisteredLocale, RegisteredMessages, TagFn } from '#src/runtime/types.ts';
31
+
32
+ type M = RegisteredMessages;
33
+
34
+ /** `Copy`, with `rich` returning a `VNode`, whose reads re-render what calls them. */
35
+ export type VueCopy = BoundCopy<VNodeChild, VNode>;
36
+
37
+ type Tags = { readonly [tag: string]: TagFn<VNodeChild> };
38
+
39
+ export interface CopyOptions {
40
+ /** Tag renderers every rich read gets, beside the copy's own: `link: (chunks) => h('a', { href }, chunks)`. */
41
+ tags?: Tags;
42
+ }
43
+
44
+ const KEY: InjectionKey<VueCopy> = Symbol('prosebook');
45
+
46
+ /** The reader and what stops it following the copy. */
47
+ function bound(copy: Copy, tags: Tags = {}): [VueCopy, () => void] {
48
+ const version = shallowRef(copy.version);
49
+ const stop = copy.subscribe(() => {
50
+ version.value = copy.version;
51
+ });
52
+ const reader = bindCopy<VNodeChild, VNode>(copy, tags, {
53
+ track: () => void version.value,
54
+ whole: (parts) => h(Fragment, null, parts as VNodeChild[]),
55
+ });
56
+ return [reader, stop];
57
+ }
58
+
59
+ /** The copy for the whole app: `app.use(copyPlugin(copy, { tags }))`. */
60
+ export function copyPlugin(copy: Copy, options: CopyOptions = {}): Plugin {
61
+ return {
62
+ install(app: App) {
63
+ const [reader, stop] = bound(copy, options.tags);
64
+ app.provide(KEY, reader);
65
+ app.onUnmount(stop);
66
+ },
67
+ };
68
+ }
69
+
70
+ /** The copy for this component and those under it, in place of the app's. Call it in `setup`. */
71
+ export function provideCopy(copy: Copy, options: CopyOptions = {}): VueCopy {
72
+ const [reader, stop] = bound(copy, options.tags);
73
+ provide(KEY, reader);
74
+ onScopeDispose(stop);
75
+ return reader;
76
+ }
77
+
78
+ /** The catalog, in the current locale. A template or `computed` reading it runs again when the locale changes, or in dev the catalog. */
79
+ export function useCopy(): VueCopy {
80
+ const reader = inject(KEY, null);
81
+ if (reader === null) throw new Error('prosebook: useCopy() needs copyPlugin(copy) installed, or provideCopy(copy) above it');
82
+ return reader;
83
+ }
84
+
85
+ /** The current locale, as a ref. */
86
+ export function useLocale(): ComputedRef<RegisteredLocale> {
87
+ const reader = useCopy();
88
+ return computed(() => reader.locale);
89
+ }
90
+
91
+ /** A slot per tag: each gets what the tag wraps as `chunks`, a `VNode` to render with `<component :is="chunks" />`. */
92
+ export type TextSlots = { [tag: string]: Slot<{ chunks: VNode }> | undefined };
93
+
94
+ export type TextProps<K extends Extract<keyof M, string>> = LooseTextProps<K, VNodeChild>;
95
+
96
+ /**
97
+ * A message in a template: `<Text id="cart.items" :values="{ count }" />`. Its tags render through
98
+ * a slot of the same name, a function in `values` or the provider's renderers, in that order.
99
+ *
100
+ * ```vue
101
+ * <Text id="cart.terms">
102
+ * <template #link="{ chunks }"><a href="/terms"><component :is="chunks" /></a></template>
103
+ * </Text>
104
+ * ```
105
+ */
106
+ export const Text = defineComponent(
107
+ (props: { id: string; values?: object }, { slots }: SetupContext) => {
108
+ const copy = useCopy();
109
+ return () => {
110
+ const fromSlots: Record<string, unknown> = {};
111
+ for (const [name, slot] of Object.entries(slots))
112
+ if (slot !== undefined) fromSlots[name] = (chunks: VNodeChild[]) => slot({ chunks: h(Fragment, null, chunks) });
113
+ return copy.rich(props.id as never, { ...fromSlots, ...props.values } as never);
114
+ };
115
+ },
116
+ { name: 'Text', props: ['id', 'values'] },
117
+ ) as unknown as <K extends Extract<keyof M, string>>(props: TextProps<K>, context: { slots: TextSlots }) => VNode;
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundle-CpUIWb9j.d.ts","names":[],"sources":["../src/tools/bundle.ts"],"mappings":";;;KAiBY"}
@@ -1,111 +0,0 @@
1
- import { c as unseenReads, d as localeModule, i as keyRefs, l as compileLocale, r as isReferenced, s as sourceFiles, t as READERS } from "./scan-Bx0_yDSi.js";
2
- import { relative } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { existsSync, readFileSync, watch } from "node:fs";
5
- // src/tools/bundle.ts
6
- /**
7
- * What the bundler integrations share: which messages a build ships, the reads that would show a key
8
- * once their message is pruned, and the code a dev server appends so an edit swaps messages in place.
9
- *
10
- * Vite sees every module before it writes a chunk, so it prunes after the fact (`vite.ts`). Next's
11
- * loaders and Bun's `onLoad` hand over a locale module before the code that reads it has been seen,
12
- * so they read the source under `scan.dirs` first (`scanSources`) and load the locale already pruned.
13
- */
14
- /** Whether a build keeps a key: some module names it, or `keep` lists it, exactly or as `prefix.*`. */
15
- function keeper(refs, keep = []) {
16
- return (key) => isReferenced(key, refs) || keep.some((k) => k.endsWith(".*") ? key.startsWith(k.slice(0, -1)) : k === key);
17
- }
18
- /** The unseen reads that are a copy's: a name only counts once some module binds a reader to it. */
19
- function unseenOf(reads) {
20
- const all = [...reads];
21
- const readers = /* @__PURE__ */ new Set([...READERS, ...all.flatMap((r) => [...r.aliases])]);
22
- return all.flatMap((r) => r.unseen).filter((u) => u.name === "Text" || readers.has(u.name));
23
- }
24
- /** The error a strict build fails with, each read at its path from `root`. */
25
- function unseenMessage(unseen, root) {
26
- const at = (file) => relative(root, file).replaceAll("\\", "/");
27
- return [
28
- `${unseen.length} read(s) whose key the build can't see, so their messages would be pruned and the key shown instead:`,
29
- ...unseen.map((u) => ` ${at(u.file)}:${u.line}:${u.column} ${u.code}`),
30
- "Write each key at the read, as a literal or `area.${id}`; or, where every key it reads is written elsewhere or listed in `keep`, mark the line `prosebook-ignore`. `prune: \"loose\"` turns this check off."
31
- ].join("\n");
32
- }
33
- /**
34
- * Every key the app's source names and, with `strict`, its unseen reads. The generated modules are
35
- * left out, since they name every key.
36
- */
37
- function scanSources(config, strict) {
38
- const refs = {
39
- exact: /* @__PURE__ */ new Set(),
40
- prefixes: /* @__PURE__ */ new Set()
41
- };
42
- const reads = [];
43
- for (const file of sourceFiles(config.scan.dirs, config.scan.extensions, [config.out])) {
44
- const code = readFileSync(file, "utf8");
45
- keyRefs(code, refs);
46
- if (strict) reads.push(unseenReads(file, code));
47
- }
48
- return {
49
- refs,
50
- unseen: unseenOf(reads)
51
- };
52
- }
53
- /** Calls `change` once a burst of edits to the catalog or its translations settles. Returns the stop. */
54
- function watchCatalog(config, change) {
55
- let timer;
56
- const watchers = [config.catalog, config.locales].filter((dir) => existsSync(dir)).map((dir) => watch(dir, { recursive: true }, (_event, file) => {
57
- if (file === null || !file.endsWith(".yaml")) return;
58
- clearTimeout(timer);
59
- timer = setTimeout(change, 50);
60
- }));
61
- return () => {
62
- clearTimeout(timer);
63
- for (const w of watchers) w.close();
64
- };
65
- }
66
- /** A locale's module holding only the keys `kept`, as JavaScript, for a bundler to load in its place. */
67
- function prunedModule(entries, kept) {
68
- return localeModule(compileLocale(entries, kept), "// Pruned by prosebook: only the messages the code names.", false);
69
- }
70
- /**
71
- * Appended to a locale module in dev. Accepts its own update and hands the new messages to every
72
- * copy (`HotHook`); a page loaded before the hooks existed reloads instead.
73
- */
74
- const hotLocale = (locale) => `
75
- if (import.meta.hot) {
76
- import.meta.hot.accept((next) => {
77
- const hot = globalThis.__PROSEBOOK_HOT__;
78
- if (next === undefined || hot === undefined) return import.meta.hot.invalidate();
79
- for (const update of hot) update(${JSON.stringify(locale)}, next.default);
80
- });
81
- }
82
- `;
83
- /** Appended to the index in dev: its doc comments change with every edit; only a new locale reloads. */
84
- const HOT_INDEX = `
85
- if (import.meta.hot) {
86
- import.meta.hot.accept((next) => {
87
- if (next === undefined || next.catalog.locales.join() !== catalog.locales.join()) import.meta.hot.invalidate();
88
- });
89
- }
90
- `;
91
- /**
92
- * A locale module in dev under a bundler that runs it again when it changes, as Turbopack's Fast
93
- * Refresh does, but has no `import.meta.hot`: each run hands its messages to every copy. The first
94
- * run finds no copy holding the locale yet, so it changes nothing.
95
- */
96
- const notifyingLocale = (code, locale) => `${code.replace(/^export default /m, "const messages = ")}
97
- export default messages;
98
- globalThis.__PROSEBOOK_HOT__?.forEach((update) => update(${JSON.stringify(locale)}, messages));
99
- `;
100
- /** Prepended to the index in dev, so the set every copy adds itself to exists before any copy does. */
101
- const HOT_SET = "globalThis.__PROSEBOOK_HOT__ ??= new Set();\n";
102
- /**
103
- * A module of prosebook's own beside the one at `url`, which passes its `import.meta.url`: the source
104
- * when run from source, the built file in dist.
105
- */
106
- const beside = (url, name) => fileURLToPath(new URL(`./${name}${url.endsWith(".ts") ? ".ts" : ".js"}`, url)).replaceAll("\\", "/");
107
- /** Code that starts the inspector (`inspector.ts`, imported from `path`) on the page, its endpoints under `base`. */
108
- const installInspector = (path, base) => `import { install as installInspector } from ${JSON.stringify(path)};\ninstallInspector(${JSON.stringify({ base })});\n`;
109
- export { installInspector as a, prunedModule as c, unseenOf as d, watchCatalog as f, hotLocale as i, scanSources as l, HOT_SET as n, keeper as o, beside as r, notifyingLocale as s, HOT_INDEX as t, unseenMessage as u };
110
-
111
- //# sourceMappingURL=bundle-DboItAre.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bundle-DboItAre.js","names":[],"sources":["../src/tools/bundle.ts"],"sourcesContent":["/**\n * What the bundler integrations share: which messages a build ships, the reads that would show a key\n * once their message is pruned, and the code a dev server appends so an edit swaps messages in place.\n *\n * Vite sees every module before it writes a chunk, so it prunes after the fact (`vite.ts`). Next's\n * loaders and Bun's `onLoad` hand over a locale module before the code that reads it has been seen,\n * so they read the source under `scan.dirs` first (`scanSources`) and load the locale already pruned.\n */\nimport { existsSync, readFileSync, watch } from 'node:fs';\nimport { relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { compileLocale, localeModule, type Entry } from './compile.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport { isReferenced, keyRefs, READERS, sourceFiles, unseenReads, type KeyRefs, type Reads, type UnseenRead } from './scan.ts';\n\n/** How a build prunes: `strict` also fails on a read whose key it can't see. */\nexport type Prune = 'strict' | 'loose' | false;\n\n/** Whether a build keeps a key: some module names it, or `keep` lists it, exactly or as `prefix.*`. */\nexport function keeper(refs: KeyRefs, keep: string[] = []): (key: string) => boolean {\n return (key) => isReferenced(key, refs) || keep.some((k) => (k.endsWith('.*') ? key.startsWith(k.slice(0, -1)) : k === key));\n}\n\n/** The unseen reads that are a copy's: a name only counts once some module binds a reader to it. */\nexport function unseenOf(reads: Iterable<Reads>): UnseenRead[] {\n const all = [...reads];\n const readers = new Set([...READERS, ...all.flatMap((r) => [...r.aliases])]);\n return all.flatMap((r) => r.unseen).filter((u) => u.name === 'Text' || readers.has(u.name));\n}\n\n/** The error a strict build fails with, each read at its path from `root`. */\nexport function unseenMessage(unseen: UnseenRead[], root: string): string {\n const at = (file: string) => relative(root, file).replaceAll('\\\\', '/');\n return [\n `${unseen.length} read(s) whose key the build can't see, so their messages would be pruned and the key shown instead:`,\n ...unseen.map((u) => ` ${at(u.file)}:${u.line}:${u.column} ${u.code}`),\n 'Write each key at the read, as a literal or `area.${id}`; or, where every key it reads is written elsewhere or listed in `keep`, mark the line `prosebook-ignore`. `prune: \"loose\"` turns this check off.',\n ].join('\\n');\n}\n\nexport interface SourceScan {\n refs: KeyRefs;\n unseen: UnseenRead[];\n}\n\n/**\n * Every key the app's source names and, with `strict`, its unseen reads. The generated modules are\n * left out, since they name every key.\n */\nexport function scanSources(config: ResolvedConfig, strict: boolean): SourceScan {\n const refs: KeyRefs = { exact: new Set(), prefixes: new Set() };\n const reads: Reads[] = [];\n for (const file of sourceFiles(config.scan.dirs, config.scan.extensions, [config.out])) {\n const code = readFileSync(file, 'utf8');\n keyRefs(code, refs);\n if (strict) reads.push(unseenReads(file, code));\n }\n return { refs, unseen: unseenOf(reads) };\n}\n\n/** Calls `change` once a burst of edits to the catalog or its translations settles. Returns the stop. */\nexport function watchCatalog(config: ResolvedConfig, change: () => void): () => void {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watchers = [config.catalog, config.locales]\n .filter((dir) => existsSync(dir))\n .map((dir) =>\n watch(dir, { recursive: true }, (_event, file) => {\n if (file === null || !file.endsWith('.yaml')) return;\n clearTimeout(timer);\n timer = setTimeout(change, 50);\n }),\n );\n return () => {\n clearTimeout(timer);\n for (const w of watchers) w.close();\n };\n}\n\n/** A locale's module holding only the keys `kept`, as JavaScript, for a bundler to load in its place. */\nexport function prunedModule(entries: Entry[], kept: (key: string) => boolean): string {\n return localeModule(compileLocale(entries, kept), '// Pruned by prosebook: only the messages the code names.', false);\n}\n\n/**\n * Appended to a locale module in dev. Accepts its own update and hands the new messages to every\n * copy (`HotHook`); a page loaded before the hooks existed reloads instead.\n */\nexport const hotLocale = (locale: string) => `\nif (import.meta.hot) {\n import.meta.hot.accept((next) => {\n const hot = globalThis.__PROSEBOOK_HOT__;\n if (next === undefined || hot === undefined) return import.meta.hot.invalidate();\n for (const update of hot) update(${JSON.stringify(locale)}, next.default);\n });\n}\n`;\n\n/** Appended to the index in dev: its doc comments change with every edit; only a new locale reloads. */\nexport const HOT_INDEX = `\nif (import.meta.hot) {\n import.meta.hot.accept((next) => {\n if (next === undefined || next.catalog.locales.join() !== catalog.locales.join()) import.meta.hot.invalidate();\n });\n}\n`;\n\n/**\n * A locale module in dev under a bundler that runs it again when it changes, as Turbopack's Fast\n * Refresh does, but has no `import.meta.hot`: each run hands its messages to every copy. The first\n * run finds no copy holding the locale yet, so it changes nothing.\n */\nexport const notifyingLocale = (code: string, locale: string) =>\n `${code.replace(/^export default /m, 'const messages = ')}\nexport default messages;\nglobalThis.__PROSEBOOK_HOT__?.forEach((update) => update(${JSON.stringify(locale)}, messages));\n`;\n\n/** Prepended to the index in dev, so the set every copy adds itself to exists before any copy does. */\nexport const HOT_SET = 'globalThis.__PROSEBOOK_HOT__ ??= new Set();\\n';\n\n/**\n * A module of prosebook's own beside the one at `url`, which passes its `import.meta.url`: the source\n * when run from source, the built file in dist.\n */\nexport const beside = (url: string, name: string) =>\n fileURLToPath(new URL(`./${name}${url.endsWith('.ts') ? '.ts' : '.js'}`, url)).replaceAll('\\\\', '/');\n\n/** Code that starts the inspector (`inspector.ts`, imported from `path`) on the page, its endpoints under `base`. */\nexport const installInspector = (path: string, base: string) =>\n `import { install as installInspector } from ${JSON.stringify(path)};\\ninstallInspector(${JSON.stringify({ base })});\\n`;\n"],"mappings":";;;;;;;;;;;;;;AAoBA,SAAgB,OAAO,MAAe,OAAiB,CAAC,GAA6B;CACnF,QAAQ,QAAQ,aAAa,KAAK,IAAI,KAAK,KAAK,MAAM,MAAO,EAAE,SAAS,IAAI,IAAI,IAAI,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM,GAAI;AAC7H;;AAGA,SAAgB,SAAS,OAAsC;CAC7D,MAAM,MAAM,CAAC,GAAG,KAAK;CACrB,MAAM,0BAAU,IAAI,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,SAAS,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;CAC3E,OAAO,IAAI,SAAS,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,UAAU,QAAQ,IAAI,EAAE,IAAI,CAAC;AAC5F;;AAGA,SAAgB,cAAc,QAAsB,MAAsB;CACxE,MAAM,MAAM,SAAiB,SAAS,MAAM,IAAI,CAAC,CAAC,WAAW,MAAM,GAAG;CACtE,OAAO;EACL,GAAG,OAAO,OAAO;EACjB,GAAG,OAAO,KAAK,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,OAAO,IAAI,EAAE,MAAM;EACvE;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;;AAWA,SAAgB,YAAY,QAAwB,QAA6B;CAC/E,MAAM,OAAgB;EAAE,uBAAO,IAAI,IAAI;EAAG,0BAAU,IAAI,IAAI;CAAE;CAC9D,MAAM,QAAiB,CAAC;CACxB,KAAK,MAAM,QAAQ,YAAY,OAAO,KAAK,MAAM,OAAO,KAAK,YAAY,CAAC,OAAO,GAAG,CAAC,GAAG;EACtF,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,QAAQ,MAAM,IAAI;EAClB,IAAI,QAAQ,MAAM,KAAK,YAAY,MAAM,IAAI,CAAC;CAChD;CACA,OAAO;EAAE;EAAM,QAAQ,SAAS,KAAK;CAAE;AACzC;;AAGA,SAAgB,aAAa,QAAwB,QAAgC;CACnF,IAAI;CACJ,MAAM,WAAW,CAAC,OAAO,SAAS,OAAO,OAAO,CAAC,CAC9C,QAAQ,QAAQ,WAAW,GAAG,CAAC,CAAC,CAChC,KAAK,QACJ,MAAM,KAAK,EAAE,WAAW,KAAK,IAAI,QAAQ,SAAS;EAChD,IAAI,SAAS,QAAQ,CAAC,KAAK,SAAS,OAAO,GAAG;EAC9C,aAAa,KAAK;EAClB,QAAQ,WAAW,QAAQ,EAAE;CAC/B,CAAC,CACH;CACF,aAAa;EACX,aAAa,KAAK;EAClB,KAAK,MAAM,KAAK,UAAU,EAAE,MAAM;CACpC;AACF;;AAGA,SAAgB,aAAa,SAAkB,MAAwC;CACrF,OAAO,aAAa,cAAc,SAAS,IAAI,GAAG,6DAA6D,KAAK;AACtH;;;;;AAMA,MAAa,aAAa,WAAmB;;;;;uCAKN,KAAK,UAAU,MAAM,EAAE;;;;;AAM9D,MAAa,YAAY;;;;;;;;;;;;AAazB,MAAa,mBAAmB,MAAc,WAC5C,GAAG,KAAK,QAAQ,qBAAqB,mBAAmB,EAAE;;2DAED,KAAK,UAAU,MAAM,EAAE;;;AAIlF,MAAa,UAAU;;;;;AAMvB,MAAa,UAAU,KAAa,SAClC,cAAc,IAAI,IAAI,KAAK,OAAO,IAAI,SAAS,KAAK,IAAI,QAAQ,SAAS,GAAG,CAAC,CAAC,CAAC,WAAW,MAAM,GAAG;;AAGrG,MAAa,oBAAoB,MAAc,SAC7C,+CAA+C,KAAK,UAAU,IAAI,EAAE,sBAAsB,KAAK,UAAU,EAAE,KAAK,CAAC,EAAE"}
@@ -1,16 +0,0 @@
1
- import { nt as MessageFormatElement } from "./config-CzaJJQ60.js";
2
- type Entry = readonly [key: string, ast: MessageFormatElement[]];
3
- interface CompiledLocale {
4
- /** `const oN = {…};` lines the messages refer to. */
5
- declarations: string[];
6
- /** `"key": message,` lines. */
7
- rows: string[];
8
- }
9
- /** One locale's messages as code, keeping only `keep` when given. */
10
- declare function compileLocale(entries: Iterable<Entry>, keep?: (key: string) => boolean): CompiledLocale;
11
- /** The locale as an ES module whose default export is its messages. */
12
- declare function localeModule(compiled: CompiledLocale, banner: string, ts: boolean): string;
13
- /** The locale as one expression, for a bundler to inline in place of the module. */
14
- declare function localeExpression(compiled: CompiledLocale): string;
15
- export { localeModule as a, localeExpression as i, Entry as n, compileLocale as r, CompiledLocale as t };
16
- //# sourceMappingURL=compile-ClK9htKr.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compile-ClK9htKr.d.ts","names":[],"sources":["../src/tools/compile.ts"],"mappings":";KA0BY,kBAAkB,aAAa,KAAK;UAoG/B;;EAEf;;EAEA;;;iBAIc,cAAc,SAAS,SAAS,QAAQ,QAAQ,0BAA0B;;iBAW1E,aAAa,UAAU,gBAAgB,gBAAgB;;iBAUvD,iBAAiB,UAAU"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"copy-B3V_QRkT.d.ts","names":[],"sources":["../src/runtime/types.ts","../src/runtime/copy.ts"],"mappings":";;;;;;;;UASiB;EACf;EACA;;;KAIU;GAAmB,cAAc;;;;;;;;;;;;;;;UAgB5B;KAEL,qBAAqB;EAAmB,gBAAgB,UAAU;IAAkB,IAAI;KACxF,mBAAmB;EAAmB,cAAc;IAAqB;;KAEzE,cAAc;EAAmB,YAAY;IAAqB;;;;;UAM7D;WACN;;;;;EAKT,EAAE,eAAe,UAAU,KAAK,qBAAqB,QAAQ,KAAK;;EAElE,EAAE,OAAO,eAAe,UAAU,KAAK,uBAAuB,QAAQ,KAAK;;EAE3E,EAAE;;;;;EAKF,EACE,eACA,gBACA,kBACA,MAAM,MACN,UAAU,KAAK,oBACf,QAAQ,KAAK;;EAGf,EAAE,gBAAgB,MAAM;;EAExB,EAAE,QAAQ,QAAQ,aAAa;;KAGrB;GAAU;;KAIV;YAAqB;;;;;;;KAOrB,YAAY,aAAa,cAAc;;KAGvC,WAAW,gBAAgB,UAAU;;;;;KAMrC;EAAa,qBAAqB;EAAU,oBAAoB,IAAI;;;KAGpE,sBAAsB,QAAQ,QAAQ,GAAG;;KAGzC;YAA+B,cAAc;;;KAG7C,OAAO,eAAe;KACtB,MAAM,MAAM,QAAQ,OAAO,OAAO;;KAElC,YAAY;KAEnB,OAAO,gBAAgB;;KAGhB,UAAU,UAAU,gBAAgB,OAAO,2BAA2B,OAAO,cAAc,MAAM;KAExG,UAAU,UAAU,cAAc,QAAQ,KAAK,QAAQ,WAAW,eAAe,MAAM,WACzF,KAAK,QAAQ,WAAW,gBAAgB,MAAM;;KAIrC,WAAW,UAAU,cAAc,KAAK,YAAY,UAAU,GAAG;KACjE,UAAU,UAAU,cAAc,KAC5C,OAAO,WAAW,GAAG,oBAAoB,SAAS,WAAW,GAAG,OAAO,QAAQ,WAAW,GAAG;;KAGnF,QAAQ,UAAU,oBAAoB,WAAW,KAAK,EAAE,sCAAsC,WAAU;KCxG/G,eAAe;EAAU,SAAS;;;UAGtB,QAAQ,UAAU,gBAAgB,eAAe;;WAEvD,QAAQ;WACR,kBAAkB;;WAElB,sBAAsB,UAAU,KAAK;;WAErC,qBAAqB,UAAU,KAAK;;WAEpC,cAAc;;;iBAIT,cAAc,UAAU,eAAe,2BACrD,SAAS,KAAK,QAAQ,GAAG,mBACxB,QAAQ,GAAG;UAIG,YAAY;;EAE3B,SAAS;;EAET;KAAU,cAAc;;;EAExB,aAAa,aAAa,QAAQ;;;;;EAKlC,WAAW,OAAO,OAAO,aAAa,QAAQ;;;UAI/B,KAAK,UAAU,gBAAgB,eAAe;;WAEpD,QAAQ;WACR,kBAAkB;WAClB,QAAQ;;WAER;cAAkB,cAAc;;;WAEhC,OAAO;;EAEhB,EAAE,gBAAgB,YAAY,YAAY,KAAK,MAAM,MAAM,UAAU,EAAE;;EAEvE,KAAK,UAAU,QAAQ,IAAI,aAAa,YAAY,KAAK,MAAM,QAAQ,UAAU,EAAE,IAAI,KAAK,OAAO;EACnG,IAAI,YAAY,cAAc,aAAa;;EAE3C,UAAU,YAAY,MAAM,wCAAwC;;WAE3D;;EAET,UAAU,YAAY;;EAEtB,OAAO,YAAY,wBAAwB,UAAU,KAAK;;EAE1D,KAAK,YAAY,OAAO,eAAe,UAAU,KAAK;;EAEtD,KAAK,YAAY,OAAO,kBAAkB,UAAU,KAAK;;EAEzD,SAAS,YAAY,eAAe,MAAM,KAAK,wBAAwB,UAAU,KAAK;;;iBAaxE,WAAW,UAAU,eAAe,kBAAkB,SAAS,QAAQ,GAAG,IAAI,UAAS,YAAY,KAAU,KAAK,GAAG"}
@@ -1,154 +0,0 @@
1
- import { n as flatten, t as createHelpers } from "./helpers-BWGcD0kS.js";
2
- import { n as chainOf, r as negotiate } from "./locale-DpQA1pqH.js";
3
- // src/runtime/copy.ts
4
- /** Ties a generated catalog to its message shapes. Called by generated code. */
5
- function defineCatalog(catalog) {
6
- return catalog;
7
- }
8
- /** A message as the text it puts on screen: rendered again with its tags left as their text. */
9
- function textOf(message, values, h) {
10
- if (typeof message === "string") return message;
11
- const plain = Object.fromEntries(Object.entries(values).filter(([, v]) => typeof v !== "function"));
12
- return flatten(message(plain, h)).join("");
13
- }
14
- /** A catalog reader with a current locale. Create one per app, or one per request on a server. */
15
- function createCopy(catalog, options = {}) {
16
- const loaded = /* @__PURE__ */ new Map();
17
- const pending = /* @__PURE__ */ new Map();
18
- const listeners = /* @__PURE__ */ new Set();
19
- const defaultTags = options.tags ?? {};
20
- const formatters = /* @__PURE__ */ new Map();
21
- let reading;
22
- const helpers = (l) => createHelpers(l, (error) => {
23
- if (reading === void 0) return;
24
- if (options.onError) options.onError(error, reading, l);
25
- else console.error(`prosebook: ${reading} in ${l}: ${error.message}`);
26
- });
27
- for (const [locale, messages] of Object.entries(catalog.messages)) loaded.set(locale, {
28
- messages,
29
- h: helpers(locale)
30
- });
31
- let locale = catalog.source;
32
- let chain = layers(chainOf(locale, catalog.locales, catalog.source));
33
- let latest = 0;
34
- let version = 0;
35
- function changed() {
36
- version++;
37
- for (const listener of listeners) listener();
38
- }
39
- globalThis.__PROSEBOOK_HOT__?.add((l, messages) => {
40
- const layer = loaded.get(l);
41
- if (layer === void 0) return;
42
- layer.messages = messages;
43
- changed();
44
- });
45
- function layers(locales) {
46
- return locales.flatMap((l) => loaded.get(l) ?? []);
47
- }
48
- function load(l) {
49
- const done = loaded.get(l);
50
- if (done) return Promise.resolve(done);
51
- let p = pending.get(l);
52
- if (p === void 0) {
53
- const loader = catalog.loaders[l];
54
- if (loader === void 0) return Promise.reject(/* @__PURE__ */ new Error(`prosebook: no messages for locale "${l}"`));
55
- p = loader().then((mod) => {
56
- const layer = {
57
- messages: mod.default,
58
- h: helpers(l)
59
- };
60
- loaded.set(l, layer);
61
- pending.delete(l);
62
- return layer;
63
- });
64
- p.catch(() => pending.delete(l));
65
- pending.set(l, p);
66
- }
67
- return p;
68
- }
69
- async function setLocale(next) {
70
- const target = negotiate(next, catalog.locales, catalog.source);
71
- const ticket = ++latest;
72
- const locales = chainOf(target, catalog.locales, catalog.source);
73
- await Promise.all(locales.map(load));
74
- if (ticket !== latest || target === locale) return;
75
- locale = target;
76
- chain = layers(locales);
77
- formatters.clear();
78
- changed();
79
- }
80
- function find(key) {
81
- for (const layer of chain) {
82
- const message = layer.messages[key];
83
- if (message !== void 0) return [message, layer.h];
84
- }
85
- }
86
- function render(key, values) {
87
- const found = find(key);
88
- if (found === void 0) return options.onMissing ? options.onMissing(key, locale) : key;
89
- const [message, h] = found;
90
- reading = key;
91
- let out;
92
- try {
93
- out = typeof message === "string" ? message : message(values, h);
94
- } finally {
95
- reading = void 0;
96
- }
97
- const read = globalThis.__PROSEBOOK_READ__;
98
- if (read !== void 0) read(key, typeof out === "string" ? out : textOf(message, values, h), h.locale);
99
- return out;
100
- }
101
- function formatter(kind, options, make) {
102
- const id = `${kind}${JSON.stringify(options ?? {})}`;
103
- let made = formatters.get(id);
104
- if (made === void 0) formatters.set(id, made = make());
105
- return made;
106
- }
107
- const ready = options.locale === void 0 ? Promise.resolve() : setLocale(options.locale);
108
- return {
109
- get locale() {
110
- return locale;
111
- },
112
- get version() {
113
- return version;
114
- },
115
- locales: catalog.locales,
116
- source: catalog.source,
117
- tags: defaultTags,
118
- ready,
119
- t(key, ...args) {
120
- const out = render(key, args[0] ?? {});
121
- return typeof out === "string" ? out : flatten(out).join("");
122
- },
123
- rich(key, ...values) {
124
- const out = render(key, {
125
- ...defaultTags,
126
- ...values[0]
127
- });
128
- return flatten(out);
129
- },
130
- has(key) {
131
- return find(key) !== void 0;
132
- },
133
- setLocale,
134
- subscribe(listener) {
135
- listeners.add(listener);
136
- return () => listeners.delete(listener);
137
- },
138
- number(value, opts) {
139
- return formatter("n", opts, () => new Intl.NumberFormat(locale, opts)).format(value);
140
- },
141
- date(value, opts) {
142
- return formatter("d", opts, () => new Intl.DateTimeFormat(locale, opts)).format(value);
143
- },
144
- list(items, opts) {
145
- return formatter("l", opts, () => new Intl.ListFormat(locale, opts)).format(items);
146
- },
147
- relative(value, unit, opts) {
148
- return formatter("r", opts, () => new Intl.RelativeTimeFormat(locale, opts)).format(value, unit);
149
- }
150
- };
151
- }
152
- export { defineCatalog as n, createCopy as t };
153
-
154
- //# sourceMappingURL=copy-Bo_FIoLy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"copy-Bo_FIoLy.js","names":[],"sources":["../src/runtime/copy.ts"],"sourcesContent":["import { createHelpers, flatten } from './helpers.ts';\nimport { chainOf, negotiate } from './locale.ts';\nimport type {\n AnyTagFn,\n ArgsParam,\n Chunks,\n Compiled,\n CompiledMessages,\n Helpers,\n DevHooks,\n MessageShapes,\n RichKey,\n RichParam,\n Values,\n} from './types.ts';\n\ntype Loader = () => Promise<{ default: CompiledMessages }>;\n\n/** A generated catalog: which locales exist, which ship in the bundle and how to fetch the rest. */\nexport interface Catalog<M extends MessageShapes = MessageShapes, L extends string = string> {\n /** The locale the catalog is written in, which every other falls back to. */\n readonly source: L;\n readonly locales: readonly L[];\n /** Locales whose messages ship in the bundle. Always holds the source. */\n readonly messages: { readonly [locale in L]?: CompiledMessages };\n /** Locales fetched when first asked for. */\n readonly loaders: { readonly [locale in L]?: Loader };\n /** Type-only: carries the message shapes from the generated module to `createCopy`. */\n readonly '~messages'?: M;\n}\n\n/** Ties a generated catalog to its message shapes. Called by generated code. */\nexport function defineCatalog<M extends MessageShapes, L extends string = string>(\n catalog: Omit<Catalog<M, L>, '~messages'>,\n): Catalog<M, L> {\n return catalog;\n}\n\nexport interface CopyOptions<L extends string> {\n /** The locale to start in, or the locales a reader prefers, most preferred first. Defaults to the source. */\n locale?: L | (string & {}) | readonly string[];\n /** Tag renderers every rich read gets unless it supplies its own, such as `b` or `link`. */\n tags?: { [tag: string]: AnyTagFn };\n /** What a key the catalog lacks reads as. Defaults to the key itself. */\n onMissing?: (key: string, locale: L) => string;\n /**\n * Told of a placeholder that had no value, or one `Intl` refused (`currency: 'euro'`): the message\n * still renders, showing `{name}` there. Defaults to `console.error`.\n */\n onError?: (error: Error, key: string, locale: L) => void;\n}\n\n/** Its methods don't use `this`, so they can be passed around and destructured: `const { t } = copy`. */\nexport interface Copy<M extends MessageShapes = MessageShapes, L extends string = string> {\n /** The locale messages are coming back in. */\n readonly locale: L;\n readonly locales: readonly L[];\n readonly source: L;\n /** The tag renderers rich reads fall back to, from `CopyOptions.tags`. */\n readonly tags: { readonly [tag: string]: AnyTagFn };\n /** Settles once the starting locale's messages are loaded. */\n readonly ready: Promise<void>;\n /** A message as plain text. Tags render as the text they wrap. */\n t<K extends keyof M & string>(this: void, key: K, ...args: ArgsParam<M[K]>): string;\n /** A message with elements inside it: each tag's function turns what it wraps into a node. */\n rich<K extends RichKey<M>, N = unknown>(this: void, key: K, ...values: RichParam<M[K], N>): Chunks<N>;\n has(this: void, key: string): key is keyof M & string;\n /** Switches locale once its messages (and its fallbacks') are loaded. */\n setLocale(this: void, next: L | (string & {}) | readonly string[]): Promise<void>;\n /** Counts changes to what messages read as: each locale switch, and each catalog edit in dev. */\n readonly version: number;\n /** Called after each locale switch, and in dev after each catalog edit. Returns the unsubscribe. */\n subscribe(this: void, listener: () => void): () => void;\n /** Formats a number in the current locale. */\n number(this: void, value: number | bigint, options?: Intl.NumberFormatOptions): string;\n /** Formats a date in the current locale. */\n date(this: void, value: Date | number, options?: Intl.DateTimeFormatOptions): string;\n /** Joins items as a list in the current locale: `a, b and c`. */\n list(this: void, items: Iterable<string>, options?: Intl.ListFormatOptions): string;\n /** `in 3 days`, `2 hours ago`, in the current locale. */\n relative(this: void, value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions): string;\n}\n\ntype Layer = { messages: CompiledMessages; h: Helpers };\n\n/** A message as the text it puts on screen: rendered again with its tags left as their text. */\nfunction textOf(message: Compiled, values: Values, h: Helpers): string {\n if (typeof message === 'string') return message;\n const plain = Object.fromEntries(Object.entries(values).filter(([, v]) => typeof v !== 'function'));\n return flatten(message(plain, h)).join('');\n}\n\n/** A catalog reader with a current locale. Create one per app, or one per request on a server. */\nexport function createCopy<M extends MessageShapes, L extends string>(catalog: Catalog<M, L>, options: CopyOptions<L> = {}): Copy<M, L> {\n const loaded = new Map<L, Layer>();\n const pending = new Map<L, Promise<Layer>>();\n const listeners = new Set<() => void>();\n const defaultTags = options.tags ?? {};\n const formatters = new Map<string, Intl.NumberFormat | Intl.DateTimeFormat | Intl.ListFormat | Intl.RelativeTimeFormat>();\n // The key being rendered, which a helper's report names; none while the inspector renders it again.\n let reading: string | undefined;\n const helpers = (l: L) =>\n createHelpers(l, (error) => {\n if (reading === undefined) return;\n if (options.onError) options.onError(error, reading, l);\n else console.error(`prosebook: ${reading} in ${l}: ${error.message}`);\n });\n\n for (const [locale, messages] of Object.entries(catalog.messages) as [L, CompiledMessages][])\n loaded.set(locale, { messages, h: helpers(locale) });\n\n let locale = catalog.source;\n let chain: Layer[] = layers(chainOf(locale, catalog.locales, catalog.source));\n let latest = 0;\n let version = 0;\n\n function changed() {\n version++;\n for (const listener of listeners) listener();\n }\n\n // A layer's messages are swapped in place, so the chain that holds it reads the new ones.\n (globalThis as DevHooks).__PROSEBOOK_HOT__?.add((l, messages) => {\n const layer = loaded.get(l as L);\n if (layer === undefined) return;\n layer.messages = messages;\n changed();\n });\n\n function layers(locales: L[]): Layer[] {\n return locales.flatMap((l) => loaded.get(l) ?? []);\n }\n\n function load(l: L): Promise<Layer> {\n const done = loaded.get(l);\n if (done) return Promise.resolve(done);\n let p = pending.get(l);\n if (p === undefined) {\n // oxlint-disable-next-line typescript/no-unsafe-enum-assignment -- `L` is a string union, not an enum; the rule misreads the mapped type\n const loader = catalog.loaders[l];\n if (loader === undefined) return Promise.reject(new Error(`prosebook: no messages for locale \"${l}\"`));\n p = loader().then((mod) => {\n const layer = { messages: mod.default, h: helpers(l) };\n loaded.set(l, layer);\n pending.delete(l);\n return layer;\n });\n p.catch(() => pending.delete(l));\n pending.set(l, p);\n }\n return p;\n }\n\n async function setLocale(next: L | (string & {}) | readonly string[]): Promise<void> {\n const target = negotiate(next, catalog.locales, catalog.source);\n const ticket = ++latest;\n const locales = chainOf(target, catalog.locales, catalog.source);\n await Promise.all(locales.map(load));\n // A slower load must not undo a later switch.\n if (ticket !== latest || target === locale) return;\n locale = target;\n chain = layers(locales);\n formatters.clear();\n changed();\n }\n\n function find(key: string): [Compiled, Helpers] | undefined {\n for (const layer of chain) {\n const message = layer.messages[key];\n if (message !== undefined) return [message, layer.h];\n }\n return undefined;\n }\n\n function render(key: string, values: Values): unknown {\n const found = find(key);\n if (found === undefined) return options.onMissing ? options.onMissing(key, locale) : key;\n const [message, h] = found;\n reading = key;\n let out: unknown;\n try {\n out = typeof message === 'string' ? message : message(values, h);\n } finally {\n reading = undefined;\n }\n const read = (globalThis as DevHooks).__PROSEBOOK_READ__;\n if (read !== undefined) read(key, typeof out === 'string' ? out : textOf(message, values, h), h.locale);\n return out;\n }\n\n function formatter<F>(kind: string, options: object | undefined, make: () => F): F {\n const id = `${kind}${JSON.stringify(options ?? {})}`;\n let made = formatters.get(id) as F | undefined;\n if (made === undefined) formatters.set(id, (made = make()) as never);\n return made;\n }\n\n const ready = options.locale === undefined ? Promise.resolve() : setLocale(options.locale);\n\n return {\n get locale() {\n return locale;\n },\n get version() {\n return version;\n },\n locales: catalog.locales,\n source: catalog.source,\n tags: defaultTags,\n ready,\n t(key, ...args) {\n const out = render(key, (args[0] as Values | undefined) ?? {});\n return typeof out === 'string' ? out : flatten(out).join('');\n },\n rich(key, ...values) {\n const out = render(key, { ...defaultTags, ...(values[0] as Values | undefined) });\n return flatten(out) as Chunks<never>;\n },\n has(key): key is keyof M & string {\n return find(key) !== undefined;\n },\n setLocale,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n number(value, opts) {\n return formatter('n', opts, () => new Intl.NumberFormat(locale, opts)).format(value);\n },\n date(value, opts) {\n return formatter('d', opts, () => new Intl.DateTimeFormat(locale, opts)).format(value);\n },\n list(items, opts) {\n return formatter('l', opts, () => new Intl.ListFormat(locale, opts)).format(items);\n },\n relative(value, unit, opts) {\n return formatter('r', opts, () => new Intl.RelativeTimeFormat(locale, opts)).format(value, unit);\n },\n };\n}\n"],"mappings":"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"}
@@ -1,96 +0,0 @@
1
- import { C as entryLocation, T as loadCatalog, b as readEntry, v as editEntry, x as CatalogError } from "./generate-BW3N8EGb.js";
2
- // src/tools/endpoints.ts
3
- /** Answers the inspector's requests, and nothing for any other path. */
4
- function inspectorEndpoints(config, { base = "/", open }) {
5
- return async (req) => {
6
- const url = new URL(req.url);
7
- if (url.pathname === `${base}__prosebook/open`) return openEntry(await config, url, open);
8
- if (url.pathname === `${base}__prosebook/entry`) return entry(await config, req, url);
9
- };
10
- }
11
- /**
12
- * `?key=cart.items&locale=de`: the translation's entry, or the catalog's with `catalog` or when the
13
- * locale has none. Read from disk, so a key added since the page loaded is found.
14
- */
15
- async function openEntry(config, url, open) {
16
- const key = url.searchParams.get("key") ?? "";
17
- let message;
18
- try {
19
- message = loadCatalog(config).messages.find((m) => m.key === key);
20
- } catch (e) {
21
- return failed(e);
22
- }
23
- if (message === void 0) return new Response(`no entry for "${key}" in the catalog: has it been renamed since the page loaded?`, { status: 404 });
24
- const locale = url.searchParams.has("catalog") ? void 0 : url.searchParams.get("locale") ?? void 0;
25
- return open(entryLocation(config, message, locale));
26
- }
27
- /**
28
- * The inspector's dialog: GET `?key=…` reads an entry in every locale, POST `{ key, edit, write }`
29
- * applies an edit (`entry.ts`). A POST writes files, so only the page itself may send one: same origin,
30
- * and JSON, which a form on another site can't send without asking first.
31
- */
32
- async function entry(config, req, url) {
33
- if (req.method === "GET") try {
34
- return Response.json(readEntry(config, url.searchParams.get("key") ?? ""));
35
- } catch (e) {
36
- return failed(e);
37
- }
38
- const origin = req.headers.get("origin");
39
- const sameOrigin = origin === null || new URL(origin).host === url.host;
40
- if (req.method !== "POST" || !sameOrigin || req.headers.get("content-type")?.startsWith("application/json") !== true) return Response.json({ problems: [{
41
- severity: "error",
42
- message: "edits come from the page, as JSON"
43
- }] }, { status: 403 });
44
- try {
45
- const { key, edit, write } = await req.json();
46
- const result = editEntry(config, key, edit, write === true);
47
- return Response.json(result, { status: result.problems.length > 0 ? 422 : 200 });
48
- } catch (e) {
49
- return failed(e);
50
- }
51
- }
52
- /** A redirect within the page's origin, to a launcher the dev server answers itself. */
53
- const redirect = (path) => new Response(null, {
54
- status: 307,
55
- headers: { Location: path }
56
- });
57
- function failed(e) {
58
- return Response.json({ problems: e instanceof CatalogError ? e.problems : [{
59
- severity: "error",
60
- message: e instanceof Error ? e.message : String(e)
61
- }] }, { status: e instanceof CatalogError ? 409 : 400 });
62
- }
63
- /**
64
- * The endpoints as Node middleware, for Vite's server and Next's. The request's host is the one the
65
- * page was served from: a proxy's `x-forwarded-host`, as Next's rewrite sets, before its own.
66
- */
67
- function nodeMiddleware(handle) {
68
- return (req, res, next) => {
69
- if (req.url?.includes("/__prosebook/") !== true) return next();
70
- const forwarded = req.headers["x-forwarded-host"];
71
- const host = (Array.isArray(forwarded) ? forwarded[0] : forwarded) ?? req.headers.host ?? "localhost";
72
- const headers = new Headers();
73
- for (const [name, value] of Object.entries(req.headers)) for (const v of Array.isArray(value) ? value : value === void 0 ? [] : [value]) headers.append(name, v);
74
- const chunks = [];
75
- req.on("data", (chunk) => chunks.push(chunk));
76
- req.on("end", () => {
77
- const body = req.method === "GET" || req.method === "HEAD" ? void 0 : Buffer.concat(chunks);
78
- handle(new Request(`http://${host}${req.url}`, {
79
- method: req.method,
80
- headers,
81
- body
82
- })).then(async (response) => {
83
- if (response === void 0) return next();
84
- res.statusCode = response.status;
85
- response.headers.forEach((value, name) => res.setHeader(name, value));
86
- res.end(Buffer.from(await response.arrayBuffer()));
87
- }).catch((e) => {
88
- res.statusCode = 500;
89
- res.end(e instanceof Error ? e.message : String(e));
90
- });
91
- });
92
- };
93
- }
94
- export { nodeMiddleware as n, redirect as r, inspectorEndpoints as t };
95
-
96
- //# sourceMappingURL=endpoints-BgG_5Vpm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"endpoints-BgG_5Vpm.js","names":[],"sources":["../src/tools/endpoints.ts"],"sourcesContent":["/**\n * The dev server's half of the inspector (`inspector.ts`, `inspector-dialog.ts`): `__prosebook/open`,\n * which opens an entry in the editor, and `__prosebook/entry`, which reads and edits one. Written over\n * `Request` and `Response`, so each integration mounts them its own way: Vite as middleware, Bun as\n * `Bun.serve` routes, Next behind a rewrite to a server of their own. Each opens the editor its dev\n * server already launches.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport { CatalogError, entryLocation, loadCatalog, type EntryLocation } from './catalog.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport { editEntry, readEntry, type EntryEdit } from './entry.ts';\n\nexport interface EndpointOptions {\n /** The path the endpoints sit under, ending in `/`: `/` by default. */\n base?: string;\n /** Opens an entry in the editor: a redirect to the dev server's own launcher, or a reply once it has. */\n open: (at: EntryLocation) => Response | Promise<Response>;\n}\n\n/** Answers the inspector's requests, and nothing for any other path. */\nexport function inspectorEndpoints(\n config: ResolvedConfig | Promise<ResolvedConfig>,\n { base = '/', open }: EndpointOptions,\n): (req: Request) => Promise<Response | undefined> {\n return async (req) => {\n const url = new URL(req.url);\n if (url.pathname === `${base}__prosebook/open`) return openEntry(await config, url, open);\n if (url.pathname === `${base}__prosebook/entry`) return entry(await config, req, url);\n return undefined;\n };\n}\n\n/**\n * `?key=cart.items&locale=de`: the translation's entry, or the catalog's with `catalog` or when the\n * locale has none. Read from disk, so a key added since the page loaded is found.\n */\nasync function openEntry(config: ResolvedConfig, url: URL, open: EndpointOptions['open']): Promise<Response> {\n const key = url.searchParams.get('key') ?? '';\n let message;\n try {\n message = loadCatalog(config).messages.find((m) => m.key === key);\n } catch (e) {\n return failed(e);\n }\n if (message === undefined)\n return new Response(`no entry for \"${key}\" in the catalog: has it been renamed since the page loaded?`, { status: 404 });\n const locale = url.searchParams.has('catalog') ? undefined : (url.searchParams.get('locale') ?? undefined);\n return open(entryLocation(config, message, locale));\n}\n\n/**\n * The inspector's dialog: GET `?key=…` reads an entry in every locale, POST `{ key, edit, write }`\n * applies an edit (`entry.ts`). A POST writes files, so only the page itself may send one: same origin,\n * and JSON, which a form on another site can't send without asking first.\n */\nasync function entry(config: ResolvedConfig, req: Request, url: URL): Promise<Response> {\n if (req.method === 'GET') {\n try {\n return Response.json(readEntry(config, url.searchParams.get('key') ?? ''));\n } catch (e) {\n return failed(e);\n }\n }\n const origin = req.headers.get('origin');\n const sameOrigin = origin === null || new URL(origin).host === url.host;\n if (req.method !== 'POST' || !sameOrigin || req.headers.get('content-type')?.startsWith('application/json') !== true)\n return Response.json({ problems: [{ severity: 'error', message: 'edits come from the page, as JSON' }] }, { status: 403 });\n try {\n const { key, edit, write } = (await req.json()) as { key: string; edit: EntryEdit; write?: boolean };\n const result = editEntry(config, key, edit, write === true);\n return Response.json(result, { status: result.problems.length > 0 ? 422 : 200 });\n } catch (e) {\n return failed(e);\n }\n}\n\n/** A redirect within the page's origin, to a launcher the dev server answers itself. */\nexport const redirect = (path: string) => new Response(null, { status: 307, headers: { Location: path } });\n\nfunction failed(e: unknown): Response {\n return Response.json(\n { problems: e instanceof CatalogError ? e.problems : [{ severity: 'error', message: e instanceof Error ? e.message : String(e) }] },\n { status: e instanceof CatalogError ? 409 : 400 },\n );\n}\n\n/**\n * The endpoints as Node middleware, for Vite's server and Next's. The request's host is the one the\n * page was served from: a proxy's `x-forwarded-host`, as Next's rewrite sets, before its own.\n */\nexport function nodeMiddleware(\n handle: (req: Request) => Promise<Response | undefined>,\n): (req: IncomingMessage, res: ServerResponse, next: () => void) => void {\n return (req, res, next) => {\n // Before reading the body, which a request for anything else leaves to whoever answers it.\n if (req.url?.includes('/__prosebook/') !== true) return next();\n const forwarded = req.headers['x-forwarded-host'];\n const host = (Array.isArray(forwarded) ? forwarded[0] : forwarded) ?? req.headers.host ?? 'localhost';\n const headers = new Headers();\n for (const [name, value] of Object.entries(req.headers))\n for (const v of Array.isArray(value) ? value : value === undefined ? [] : [value]) headers.append(name, v);\n\n const chunks: Buffer[] = [];\n req.on('data', (chunk: Buffer) => chunks.push(chunk));\n req.on('end', () => {\n const body = req.method === 'GET' || req.method === 'HEAD' ? undefined : Buffer.concat(chunks);\n handle(new Request(`http://${host}${req.url}`, { method: req.method, headers, body }))\n .then(async (response) => {\n if (response === undefined) return next();\n res.statusCode = response.status;\n response.headers.forEach((value, name) => res.setHeader(name, value));\n res.end(Buffer.from(await response.arrayBuffer()));\n })\n .catch((e: unknown) => {\n res.statusCode = 500;\n res.end(e instanceof Error ? e.message : String(e));\n });\n });\n };\n}\n"],"mappings":"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"}