prosebook 0.2.0 → 0.3.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 (112) hide show
  1. package/README.md +274 -29
  2. package/dist/bun.d.ts +34 -0
  3. package/dist/bun.d.ts.map +1 -0
  4. package/dist/bun.js +98 -0
  5. package/dist/bun.js.map +1 -0
  6. package/dist/bundle-DboItAre.js +111 -0
  7. package/dist/bundle-DboItAre.js.map +1 -0
  8. package/dist/bundle-KD-DiiFa.d.ts +6 -0
  9. package/dist/bundle-KD-DiiFa.d.ts.map +1 -0
  10. package/dist/cli.js +51 -56
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compile-xF3F4oIp.d.ts +16 -0
  13. package/dist/compile-xF3F4oIp.d.ts.map +1 -0
  14. package/dist/{config-D--RITk2.js → config-BPSmoYel.js} +36 -212
  15. package/dist/config-BPSmoYel.js.map +1 -0
  16. package/dist/{config-_4HfnJQu.d.ts → config-DdMkKt_N.d.ts} +61 -2
  17. package/dist/config-DdMkKt_N.d.ts.map +1 -0
  18. package/dist/config.d.ts +2 -2
  19. package/dist/config.js +1 -1
  20. package/dist/{copy-DXnzXsJa.d.ts → copy-B3V_QRkT.d.ts} +20 -7
  21. package/dist/copy-B3V_QRkT.d.ts.map +1 -0
  22. package/dist/copy-Bo_FIoLy.js +154 -0
  23. package/dist/copy-Bo_FIoLy.js.map +1 -0
  24. package/dist/endpoints-CnUfCkIk.js +96 -0
  25. package/dist/endpoints-CnUfCkIk.js.map +1 -0
  26. package/dist/generate-9pR3i5Bj.js +5173 -0
  27. package/dist/generate-9pR3i5Bj.js.map +1 -0
  28. package/dist/helpers-BWGcD0kS.js +96 -0
  29. package/dist/helpers-BWGcD0kS.js.map +1 -0
  30. package/dist/icu-Dej0mNsF.js +218 -0
  31. package/dist/icu-Dej0mNsF.js.map +1 -0
  32. package/dist/index.d.ts +12 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +4 -188
  35. package/dist/inspector.d.ts +4 -1
  36. package/dist/inspector.d.ts.map +1 -1
  37. package/dist/inspector.js +7 -2
  38. package/dist/inspector.js.map +1 -1
  39. package/dist/locale-DpQA1pqH.js +70 -0
  40. package/dist/locale-DpQA1pqH.js.map +1 -0
  41. package/dist/next-loader.d.ts +11 -0
  42. package/dist/next-loader.d.ts.map +1 -0
  43. package/dist/next-loader.js +17 -0
  44. package/dist/next-loader.js.map +1 -0
  45. package/dist/next.d.ts +30 -0
  46. package/dist/next.d.ts.map +1 -0
  47. package/dist/next.js +160 -0
  48. package/dist/next.js.map +1 -0
  49. package/dist/react/index.d.ts +3 -3
  50. package/dist/react/index.d.ts.map +1 -1
  51. package/dist/react/index.js.map +1 -1
  52. package/dist/react/server.d.ts +20 -0
  53. package/dist/react/server.d.ts.map +1 -0
  54. package/dist/react/server.js +34 -0
  55. package/dist/react/server.js.map +1 -0
  56. package/dist/{sarif-CI8HOokm.js → sarif-CWyTAQ0y.js} +103 -87
  57. package/dist/sarif-CWyTAQ0y.js.map +1 -0
  58. package/dist/scan-Bx0_yDSi.js +387 -0
  59. package/dist/scan-Bx0_yDSi.js.map +1 -0
  60. package/dist/tools.d.ts +83 -90
  61. package/dist/tools.d.ts.map +1 -1
  62. package/dist/tools.js +5 -4
  63. package/dist/vite.d.ts +3 -2
  64. package/dist/vite.d.ts.map +1 -1
  65. package/dist/vite.js +29 -109
  66. package/dist/vite.js.map +1 -1
  67. package/package.json +37 -4
  68. package/src/bun.ts +132 -0
  69. package/src/cli.ts +60 -58
  70. package/src/index.ts +1 -1
  71. package/src/inspector.ts +8 -3
  72. package/src/next-loader.ts +16 -0
  73. package/src/next.ts +188 -0
  74. package/src/react/index.tsx +6 -6
  75. package/src/react/server.ts +37 -0
  76. package/src/runtime/copy.ts +22 -3
  77. package/src/runtime/helpers.ts +54 -18
  78. package/src/runtime/locale.ts +19 -0
  79. package/src/runtime/types.ts +21 -6
  80. package/src/tools/bundle.ts +131 -0
  81. package/src/tools/catalog.ts +2 -2
  82. package/src/tools/compile.ts +60 -23
  83. package/src/tools/config.ts +39 -0
  84. package/src/tools/endpoints.ts +121 -0
  85. package/src/tools/entry.ts +77 -0
  86. package/src/tools/generate.ts +2 -0
  87. package/src/tools/icu.ts +63 -7
  88. package/src/tools/index.ts +6 -3
  89. package/src/tools/lock.ts +7 -0
  90. package/src/tools/mf1.ts +21 -7
  91. package/src/tools/mf2.ts +249 -57
  92. package/src/tools/migrate.ts +3 -2
  93. package/src/tools/native/emit.ts +87 -0
  94. package/src/tools/native/index.ts +36 -0
  95. package/src/tools/native/kotlin.ts +269 -0
  96. package/src/tools/native/model.ts +280 -0
  97. package/src/tools/native/number.ts +210 -0
  98. package/src/tools/native/plurals.ts +103 -0
  99. package/src/tools/native/rust.ts +367 -0
  100. package/src/tools/native/swift.ts +326 -0
  101. package/src/tools/syntax.ts +3 -3
  102. package/src/tools/todo.ts +118 -0
  103. package/src/vite.ts +28 -114
  104. package/dist/config-D--RITk2.js.map +0 -1
  105. package/dist/config-_4HfnJQu.d.ts.map +0 -1
  106. package/dist/copy-DXnzXsJa.d.ts.map +0 -1
  107. package/dist/helpers-BPuPGjQP.js +0 -61
  108. package/dist/helpers-BPuPGjQP.js.map +0 -1
  109. package/dist/index.js.map +0 -1
  110. package/dist/sarif-CI8HOokm.js.map +0 -1
  111. package/dist/scan-Ch2BfIUD.js +0 -2561
  112. package/dist/scan-Ch2BfIUD.js.map +0 -1
package/dist/tools.d.ts CHANGED
@@ -1,18 +1,61 @@
1
- import { A as sentences, B as EntryLocation, C as RuleContext, D as definePlugin, E as compileRule, F as KindRule, G as entryLocation, H as Message, I as STRICT_LISTS, J as whereOf, K as loadCatalog, L as Severity, M as words, N as DEFAULT_KINDS, O as lint, P as DEFAULT_SURFACES, R as Catalog, S as Rule, T as applyEdits, U as Problem, V as KEY_PATTERN, W as Translation, Y as MessageFormatElement, _ as HOLE, b as Readings, c as resolveConfig, d as CheckRule, f as ConditionalRule, g as Finding, h as ExistenceRule, i as ResolvedConfig, j as singularOf, k as phrase, l as CORE_RULES, m as Edit, o as defineConfig, p as CoreRule, q as previewOf, r as ProsebookConfig, s as loadConfig, u as CapitalizationRule, v as OccurrenceRule, w as SubstitutionRule, x as Report, y as Plugin, z as CatalogError } from "./config-_4HfnJQu.js";
2
- import "@formatjs/icu-messageformat-parser";
3
- type Entry = readonly [key: string, ast: MessageFormatElement[]];
4
- interface CompiledLocale {
5
- /** `const oN = {…};` lines the messages refer to. */
6
- declarations: string[];
7
- /** `"key": message,` lines. */
8
- rows: string[];
1
+ import { $ as loadCatalog, A as RuleContext, B as DEFAULT_KINDS, C as Finding, D as Readings, E as Plugin, F as lint, G as Catalog, H as KindRule, I as phrase, J as KEY_PATTERN, K as CatalogError, L as sentences, M as applyEdits, N as compileRule, O as Report, P as definePlugin, Q as entryLocation, R as singularOf, S as ExistenceRule, T as OccurrenceRule, U as STRICT_LISTS, V as DEFAULT_SURFACES, W as Severity, X as Problem, Y as Message, Z as Translation, _ as CapitalizationRule, a as ResolvedConfig, b as CoreRule, c as loadConfig, d as Mf2Function, et as previewOf, f as intlDate, g as CORE_RULES, h as toMf2, i as ProsebookConfig, j as SubstitutionRule, k as Rule, l as resolveConfig, m as parseMf2, nt as MessageFormatElement, p as intlNumber, q as EntryLocation, r as NativeTarget, s as defineConfig, tt as whereOf, u as Isolate, v as CheckRule, w as HOLE, x as Edit, y as ConditionalRule, z as words } from "./config-DdMkKt_N.js";
2
+ import { a as localeModule, i as localeExpression, n as Entry, r as compileLocale, t as CompiledLocale } from "./compile-xF3F4oIp.js";
3
+ export declare function sourceFiles(dirs: string[], extensions: string[], skip?: string[]): string[];
4
+ interface KeyRefs {
5
+ exact: Set<string>;
6
+ /** `nav.` from `nav.${id}` or `'nav.' + id`: every key under it may be read. */
7
+ prefixes: Set<string>;
8
+ }
9
+ export declare function keyRefs(code: string, into?: KeyRefs): KeyRefs;
10
+ export declare function isReferenced(key: string, refs: KeyRefs): boolean;
11
+ interface Hardcoded {
12
+ file: string;
13
+ line: number;
14
+ column: number;
15
+ text: string;
16
+ /** Where it sits: `text`, or the attribute it is the value of. */
17
+ where: string;
9
18
  }
10
- /** One locale's messages as code, keeping only `keep` when given. */
11
- export declare function compileLocale(entries: Iterable<Entry>, keep?: (key: string) => boolean): CompiledLocale;
12
- /** The locale as an ES module whose default export is its messages. */
13
- export declare function localeModule(compiled: CompiledLocale, banner: string, ts: boolean): string;
14
- /** The locale as one expression, for a bundler to inline in place of the module. */
15
- export declare function localeExpression(compiled: CompiledLocale): string;
19
+ /**
20
+ * Text that reads as code rather than prose: one token holding a path, a call, an identifier with an
21
+ * underscore or a dotted name, as in `form.json()` or `src/index.ts`. Trailing punctuation doesn't
22
+ * count, so `Done.` is still prose.
23
+ */
24
+ export declare function looksLikeCode(text: string): boolean;
25
+ /** Literal text in JSX that a person reads, where it should be a catalog entry. */
26
+ export declare function hardcodedText(file: string, code: string, attributes: string[]): Hardcoded[];
27
+ interface ScanResult {
28
+ hardcoded: Hardcoded[];
29
+ refs: KeyRefs;
30
+ files: number;
31
+ }
32
+ export declare function scan(dirs: string[], extensions: string[], attributes: string[], root: string, skip?: string[]): ScanResult;
33
+ /** A copy's methods that take a key first. */
34
+ export declare const READERS: Set<string>;
35
+ interface UnseenRead {
36
+ file: string;
37
+ line: number;
38
+ column: number;
39
+ /** The read as written, on one line. */
40
+ code: string;
41
+ /** What it calls: a reader, the name it was imported by, or `Text`. */
42
+ name: string;
43
+ }
44
+ interface Reads {
45
+ /** Reads whose key a build can't see, in calls that may be to a copy's `t` or `rich`. */
46
+ unseen: UnseenRead[];
47
+ /** Names a copy's readers are bound to here, as in `const { rich: tRich } = copy`. */
48
+ aliases: Set<string>;
49
+ }
50
+ /**
51
+ * The reads in a module whose key a pruned build can't see, as in `t(item.key)` or `<Text id={id} />`:
52
+ * the build would leave the message out, and the key would show in its place. A build has no types,
53
+ * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,
54
+ * which may be bound to one elsewhere (`aliases`); the caller keeps those whose name turns out to be a
55
+ * reader. `<Text>` counts where it is imported from `prosebook/react`. Lines marked `prosebook-ignore`
56
+ * are skipped.
57
+ */
58
+ export declare function unseenReads(file: string, code: string): Reads;
16
59
  interface Extraction {
17
60
  file: string;
18
61
  line: number;
@@ -143,6 +186,21 @@ export declare function applyFixes(config: ResolvedConfig, catalog: Catalog, fin
143
186
  fixed: number;
144
187
  problems: Problem[];
145
188
  };
189
+ interface Imported {
190
+ /** Keys whose translation was written. */
191
+ written: string[];
192
+ /** Keys given back as they were: each still holds, so a stale one can be accepted. */
193
+ unchanged: string[];
194
+ /** What was left out, and why. The rest was written regardless. */
195
+ problems: Problem[];
196
+ }
197
+ /**
198
+ * Translations into `locale` from `prosebook todo`'s YAML filled in, by a person or a model, each
199
+ * written to the file its locale and area put it in. Text around a fenced block is left aside, as a
200
+ * model's reply has it, and an empty value is one left for later. An entry that would break the
201
+ * catalog is left out with its problem, so a reply with one bad entry still lands the others.
202
+ */
203
+ export declare function importTranslations(config: ResolvedConfig, locale: string, reply: string): Imported;
146
204
  interface Lock {
147
205
  version: 2;
148
206
  /** Source text by hash: every text some translation is anchored to. */
@@ -163,6 +221,8 @@ interface StaleTranslation {
163
221
  /** The translation, made from `was`. */
164
222
  translation: string;
165
223
  }
224
+ /** What changed in the source of a stale translation, as a word diff, or why that isn't known. */
225
+ export declare function changeOf(stale: StaleTranslation): string;
166
226
  interface Reconciled {
167
227
  lock: Lock;
168
228
  /** Per locale, per key: the translations made from a source that has changed since. */
@@ -211,24 +271,6 @@ export declare function wordDiff(before: string, after: string): DiffPart[];
211
271
  export declare function formatDiff(parts: DiffPart[]): string;
212
272
  export declare function pseudoText(text: string): string;
213
273
  export declare function pseudoAst(ast: MessageFormatElement[]): MessageFormatElement[];
214
- type Element$1 = MessageFormatElement;
215
- /** A function as written: `:number minimumFractionDigits=2`. Kept on the styles it produces, to write back. */
216
- interface Mf2Function {
217
- name: string;
218
- options: Record<string, string>;
219
- }
220
- /** An MF2 message as the element tree. Throws, with the fix, where it can't be read or compiled. */
221
- export declare function parseMf2(text: string, locale: string): Element$1[];
222
- /** The `Intl.NumberFormat` options a numeric function formats with. */
223
- export declare function intlNumber(fn: Mf2Function): Intl.NumberFormatOptions;
224
- /**
225
- * The `Intl.DateTimeFormat` options a date or time function formats with. MF2's date and time
226
- * functions are still a draft; this follows the spec's semantic options as the `messageformat`
227
- * package does, so a message reads the same in both.
228
- */
229
- export declare function intlDate(fn: Mf2Function): Intl.DateTimeFormatOptions;
230
- /** A message tree as MF2 text. Throws, saying what has no MF2 equivalent, where it can't be written. */
231
- export declare function toMf2(ast: Element$1[], locale: string): string;
232
274
  type Element = MessageFormatElement;
233
275
  export declare function toMf1(ast: Element[]): string;
234
276
  type MessageFormatVersion = 1 | 2;
@@ -237,7 +279,7 @@ export declare const SYNTAX_NAME: Record<MessageFormatVersion, string>;
237
279
  * A message as the element tree. MF2 is read per locale: which variants a value can reach depends
238
280
  * on the locale's plural rules (see `matchTree` in mf2.ts).
239
281
  */
240
- export declare function readMessage(version: MessageFormatVersion, text: string, locale: string): MessageFormatElement[];
282
+ export declare function readMessage(version: MessageFormatVersion, text: string, locale: string, isolate?: Isolate): MessageFormatElement[];
241
283
  /** A tree as text in the given syntax. Throws, saying what the syntax can't express, where it can't. */
242
284
  export declare function writeMessage(version: MessageFormatVersion, ast: MessageFormatElement[], locale: string): string;
243
285
  interface Located {
@@ -292,6 +334,13 @@ interface TypeOracle {
292
334
  export declare function typescriptOracle(root: string, files: readonly string[]): Promise<TypeOracle | undefined>;
293
335
  /** The kind an expression's syntax gives away, where it does: `items.length`, `a * b`, `new Date()`. */
294
336
  export declare function syntaxKind(expr: Node): ValueKind | undefined;
337
+ interface Todo {
338
+ /** How many entries want a translation. */
339
+ count: number;
340
+ /** The entries as YAML, by area, with the prompt first when one was asked for. */
341
+ text: string;
342
+ }
343
+ export declare function todoOf(config: ResolvedConfig, catalog: Catalog, stale: ReadonlyMap<string, StaleTranslation>, locale: string, prompt?: boolean): Todo;
295
344
  interface SarifResult {
296
345
  /** A rule the report describes, or one of a lint plugin's. */
297
346
  rule: string;
@@ -317,61 +366,5 @@ export declare class SarifReport {
317
366
  add(result: SarifResult): void;
318
367
  toJSON(root: string, tool: SarifTool): object;
319
368
  }
320
- export declare function sourceFiles(dirs: string[], extensions: string[], skip?: string[]): string[];
321
- interface KeyRefs {
322
- exact: Set<string>;
323
- /** `nav.` from `nav.${id}` or `'nav.' + id`: every key under it may be read. */
324
- prefixes: Set<string>;
325
- }
326
- export declare function keyRefs(code: string, into?: KeyRefs): KeyRefs;
327
- export declare function isReferenced(key: string, refs: KeyRefs): boolean;
328
- interface Hardcoded {
329
- file: string;
330
- line: number;
331
- column: number;
332
- text: string;
333
- /** Where it sits: `text`, or the attribute it is the value of. */
334
- where: string;
335
- }
336
- /**
337
- * Text that reads as code rather than prose: one token holding a path, a call, an identifier with an
338
- * underscore or a dotted name, as in `form.json()` or `src/index.ts`. Trailing punctuation doesn't
339
- * count, so `Done.` is still prose.
340
- */
341
- export declare function looksLikeCode(text: string): boolean;
342
- /** Literal text in JSX that a person reads, where it should be a catalog entry. */
343
- export declare function hardcodedText(file: string, code: string, attributes: string[]): Hardcoded[];
344
- interface ScanResult {
345
- hardcoded: Hardcoded[];
346
- refs: KeyRefs;
347
- files: number;
348
- }
349
- export declare function scan(dirs: string[], extensions: string[], attributes: string[], root: string, skip?: string[]): ScanResult;
350
- /** A copy's methods that take a key first. */
351
- export declare const READERS: Set<string>;
352
- interface UnseenRead {
353
- file: string;
354
- line: number;
355
- column: number;
356
- /** The read as written, on one line. */
357
- code: string;
358
- /** What it calls: a reader, the name it was imported by, or `Text`. */
359
- name: string;
360
- }
361
- interface Reads {
362
- /** Reads whose key a build can't see, in calls that may be to a copy's `t` or `rich`. */
363
- unseen: UnseenRead[];
364
- /** Names a copy's readers are bound to here, as in `const { rich: tRich } = copy`. */
365
- aliases: Set<string>;
366
- }
367
- /**
368
- * The reads in a module whose key a pruned build can't see, as in `t(item.key)` or `<Text id={id} />`:
369
- * the build would leave the message out, and the key would show in its place. A build has no types,
370
- * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,
371
- * which may be bound to one elsewhere (`aliases`); the caller keeps those whose name turns out to be a
372
- * reader. `<Text>` counts where it is imported from `prosebook/react`. Lines marked `prosebook-ignore`
373
- * are skipped.
374
- */
375
- export declare function unseenReads(file: string, code: string): Reads;
376
- export { CORE_RULES, type CapitalizationRule, type Catalog, CatalogError, type CheckRule, type CompiledLocale, type ConditionalRule, type CoreRule, DEFAULT_KINDS, DEFAULT_SURFACES, type DiffPart, type Edit, type Entry, type EntryEdit, type EntryFields, type EntryFinding, type EntryLocation, type EntryView, type EntryYaml, type ExistenceRule, type ExtractPlan, type Extraction, type FilePlan, type Finding, type Generated, HOLE, type Hardcoded, KEY_PATTERN, type KeyRefs, type KindRule, type Lock, type Message, type MessageFormatVersion, type Mf2Function, type MigratePlan, type NewEntry, type OccurrenceRule, type Plugin, type Problem, type ProsebookConfig, type Readings, type Reads, type Report, type ResolvedConfig, type Rule, type RuleContext, STRICT_LISTS, type SarifResult, type SarifTool, type Severity, type Span, type StaleTranslation, type SubstitutionRule, type Translation, type TypeOracle, type UnseenRead, type ValueKind, applyEdits, compileRule, defineConfig, definePlugin, entryLocation, lint, loadCatalog, loadConfig, phrase, previewOf, resolveConfig, sentences, singularOf, whereOf, words };
369
+ export { CORE_RULES, type CapitalizationRule, type Catalog, CatalogError, type CheckRule, type CompiledLocale, type ConditionalRule, type CoreRule, DEFAULT_KINDS, DEFAULT_SURFACES, type DiffPart, type Edit, type Entry, type EntryEdit, type EntryFields, type EntryFinding, type EntryLocation, type EntryView, type EntryYaml, type ExistenceRule, type ExtractPlan, type Extraction, type FilePlan, type Finding, type Generated, HOLE, type Hardcoded, type Imported, type Isolate, KEY_PATTERN, type KeyRefs, type KindRule, type Lock, type Message, type MessageFormatVersion, type Mf2Function, type MigratePlan, type NativeTarget, type NewEntry, type OccurrenceRule, type Plugin, type Problem, type ProsebookConfig, type Readings, type Reads, type Report, type ResolvedConfig, type Rule, type RuleContext, STRICT_LISTS, type SarifResult, type SarifTool, type Severity, type Span, type StaleTranslation, type SubstitutionRule, type Todo, type Translation, type TypeOracle, type UnseenRead, type ValueKind, applyEdits, compileLocale, compileRule, defineConfig, definePlugin, entryLocation, intlDate, intlNumber, lint, loadCatalog, loadConfig, localeExpression, localeModule, parseMf2, phrase, previewOf, resolveConfig, sentences, singularOf, toMf2, whereOf, words };
377
370
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","names":["Edit","Element"],"sources":["../src/tools/compile.ts","../src/tools/extract.ts","../src/tools/entry.ts","../src/tools/lock.ts","../src/tools/generate.ts","../src/tools/diff.ts","../src/tools/pseudo.ts","../src/tools/mf2.ts","../src/tools/mf1.ts","../src/tools/syntax.ts","../src/tools/migrate.ts","../src/tools/types.ts","../src/tools/sarif.ts","../src/tools/scan.ts"],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","names":["Edit"],"sources":["../src/tools/scan.ts","../src/tools/extract.ts","../src/tools/entry.ts","../src/tools/lock.ts","../src/tools/generate.ts","../src/tools/diff.ts","../src/tools/pseudo.ts","../src/tools/mf1.ts","../src/tools/syntax.ts","../src/tools/migrate.ts","../src/tools/types.ts","../src/tools/todo.ts","../src/tools/sarif.ts"],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -1,4 +1,5 @@
1
- import { A as entryLocation, B as parseMf2, C as editEntry, D as localeModule, E as localeExpression, F as SYNTAX_NAME, H as toMf1, I as readMessage, L as writeMessage, M as loadCatalog, N as previewOf, O as CatalogError, P as whereOf, R as intlDate, S as applyFixes, T as compileLocale, V as toMf2, _ as emptyLock, a as looksLikeCode, b as reconcile, c as unseenReads, d as localeEntries, f as localesOf, g as pseudoText, h as pseudoAst, i as keyRefs, k as KEY_PATTERN, l as catalogSchema, m as writeFiles, n as hardcodedText, o as scan, p as render, r as isReferenced, s as sourceFiles, t as READERS, u as generate, v as hash, w as readEntry, x as serializeLock, y as readLock, z as intlNumber } from "./scan-Ch2BfIUD.js";
2
- import { a as wordDiff, c as draftPlurals, d as syntaxKind, f as typescriptOracle, i as formatDiff, l as planExtract, n as applyMigrate, o as applyExtract, r as planMigrate, s as areaOf, t as SarifReport, u as slugOf } from "./sarif-CI8HOokm.js";
3
- import { _ as words, a as DEFAULT_KINDS, c as CORE_RULES, d as compileRule, f as definePlugin, g as singularOf, h as sentences, i as resolveConfig, l as HOLE, m as phrase, n as defineConfig, o as DEFAULT_SURFACES, p as lint, r as loadConfig, s as STRICT_LISTS, u as applyEdits } from "./config-D--RITk2.js";
4
- export { CORE_RULES, CatalogError, DEFAULT_KINDS, DEFAULT_SURFACES, HOLE, KEY_PATTERN, READERS, STRICT_LISTS, SYNTAX_NAME, SarifReport, applyEdits, applyExtract, applyFixes, applyMigrate, areaOf, catalogSchema, compileLocale, compileRule, defineConfig, definePlugin, draftPlurals, editEntry, emptyLock, entryLocation, formatDiff, generate, hardcodedText, hash, intlDate, intlNumber, isReferenced, keyRefs, lint, loadCatalog, loadConfig, localeEntries, localeExpression, localeModule, localesOf, looksLikeCode, parseMf2, phrase, planExtract, planMigrate, previewOf, pseudoAst, pseudoText, readEntry, readLock, readMessage, reconcile, render, resolveConfig, scan, sentences, serializeLock, singularOf, slugOf, sourceFiles, syntaxKind, toMf1, toMf2, typescriptOracle, unseenReads, whereOf, wordDiff, words, writeFiles, writeMessage };
1
+ import { A as writeMessage, C as entryLocation, D as whereOf, E as previewOf, F as toMf1, M as intlNumber, N as parseMf2, O as SYNTAX_NAME, P as toMf2, S as KEY_PATTERN, T as loadCatalog, _ as applyFixes, a as render, b as readEntry, c as pseudoText, d as hash, f as readLock, g as wordDiff, h as formatDiff, i as localesOf, j as intlDate, k as readMessage, l as changeOf, m as serializeLock, n as generate, o as writeFiles, p as reconcile, r as localeEntries, s as pseudoAst, t as catalogSchema, u as emptyLock, v as editEntry, x as CatalogError, y as importTranslations } from "./generate-9pR3i5Bj.js";
2
+ import { a as looksLikeCode, c as unseenReads, d as localeModule, i as keyRefs, l as compileLocale, n as hardcodedText, o as scan, r as isReferenced, s as sourceFiles, t as READERS, u as localeExpression } from "./scan-Bx0_yDSi.js";
3
+ import { a as applyExtract, c as planExtract, d as typescriptOracle, i as planMigrate, l as slugOf, n as todoOf, o as areaOf, r as applyMigrate, s as draftPlurals, t as SarifReport, u as syntaxKind } from "./sarif-CWyTAQ0y.js";
4
+ import { _ as words, a as DEFAULT_KINDS, c as CORE_RULES, d as compileRule, f as definePlugin, g as singularOf, h as sentences, i as resolveConfig, l as HOLE, m as phrase, n as defineConfig, o as DEFAULT_SURFACES, p as lint, r as loadConfig, s as STRICT_LISTS, u as applyEdits } from "./config-BPSmoYel.js";
5
+ export { CORE_RULES, CatalogError, DEFAULT_KINDS, DEFAULT_SURFACES, HOLE, KEY_PATTERN, READERS, STRICT_LISTS, SYNTAX_NAME, SarifReport, applyEdits, applyExtract, applyFixes, applyMigrate, areaOf, catalogSchema, changeOf, compileLocale, compileRule, defineConfig, definePlugin, draftPlurals, editEntry, emptyLock, entryLocation, formatDiff, generate, hardcodedText, hash, importTranslations, intlDate, intlNumber, isReferenced, keyRefs, lint, loadCatalog, loadConfig, localeEntries, localeExpression, localeModule, localesOf, looksLikeCode, parseMf2, phrase, planExtract, planMigrate, previewOf, pseudoAst, pseudoText, readEntry, readLock, readMessage, reconcile, render, resolveConfig, scan, sentences, serializeLock, singularOf, slugOf, sourceFiles, syntaxKind, toMf1, toMf2, todoOf, typescriptOracle, unseenReads, whereOf, wordDiff, words, writeFiles, writeMessage };
package/dist/vite.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { r as ProsebookConfig } from "./config-_4HfnJQu.js";
1
+ import { t as Prune } from "./bundle-KD-DiiFa.js";
2
+ import { i as ProsebookConfig } from "./config-DdMkKt_N.js";
2
3
  import { Plugin } from "vite";
3
4
  export interface ProsebookPluginOptions {
4
5
  /** The config, inline. Without it, `prosebook.config.*` at `root` is read. */
@@ -13,7 +14,7 @@ export interface ProsebookPluginOptions {
13
14
  * whose key it can't see, such as `t(item.key)`, which would show the key where its message was
14
15
  * pruned. `loose` prunes without that check; `false` ships every message.
15
16
  */
16
- prune?: 'strict' | 'loose' | false;
17
+ prune?: Prune;
17
18
  /** Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. */
18
19
  keep?: string[];
19
20
  /** In dev, Alt-click a string on the page to open its entry in the editor. On by default. */
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;iBA2BiB;;EAEf,SAAS;;;;;EAKT;;;;;;EAMA;;EAEA;;EAEA;;wBAgCc,UAAU,UAAS,yBAA8B"}
1
+ {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;iBA8BiB;;EAEf,SAAS;;;;;EAKT;;;;;;EAMA,QAAQ;;EAER;;EAEA;;wBAcc,UAAU,UAAS,yBAA8B"}
package/dist/vite.js CHANGED
@@ -1,5 +1,8 @@
1
- import { A as entryLocation, C as editEntry, E as localeExpression, O as CatalogError, T as compileLocale, c as unseenReads, i as keyRefs, r as isReferenced, t as READERS, u as generate, w as readEntry } from "./scan-Ch2BfIUD.js";
2
- import { r as loadConfig } from "./config-D--RITk2.js";
1
+ import { a as installInspector, d as unseenOf, i as hotLocale, n as HOT_SET, o as keeper, r as beside, t as HOT_INDEX, u as unseenMessage } from "./bundle-DboItAre.js";
2
+ import { n as generate, x as CatalogError } from "./generate-9pR3i5Bj.js";
3
+ import { c as unseenReads, i as keyRefs, l as compileLocale, u as localeExpression } from "./scan-Bx0_yDSi.js";
4
+ import { r as loadConfig } from "./config-BPSmoYel.js";
5
+ import { n as nodeMiddleware, r as redirect, t as inspectorEndpoints } from "./endpoints-CnUfCkIk.js";
3
6
  import { relative, resolve } from "node:path";
4
7
  import { fileURLToPath } from "node:url";
5
8
  // src/vite.ts
@@ -14,31 +17,18 @@ import { fileURLToPath } from "node:url";
14
17
  * In dev an edit doesn't reload the page: each generated locale module accepts its own update and
15
18
  * hands the new messages to every copy (`HotHook`), which re-renders in the locale and state it was in.
16
19
  * The index accepts its own too, since its doc comments change with the source text, and reloads the
17
- * page only when a locale was added or removed. The page also gets the inspector (`inspector.ts`), and
18
- * the server opens the entry it asks for through Vite's own `/__open-in-editor`, so the editor is
19
- * whichever Vite already launches.
20
+ * page only when a locale was added or removed. In the browser the index imports the dev module first,
21
+ * which sets up the hooks and the inspector (`inspector.ts`) before any copy exists, so frameworks that
22
+ * render their own HTML (TanStack Start, React Router) get them too. The server opens the entry the
23
+ * inspector asks for through Vite's own `/__open-in-editor`, so the editor is whichever Vite already
24
+ * launches.
20
25
  */
21
26
  const PLACEHOLDER = /__PROSEBOOK_LOCALE_(\d+)__/g;
22
27
  const SCRIPT = /\.[cm]?[jt]sx?$/;
28
+ const DEV = "virtual:prosebook/dev";
23
29
  const DEV_ID = "\0prosebook/dev";
24
- const hotLocale = (locale) => `
25
- if (import.meta.hot) {
26
- import.meta.hot.accept((next) => {
27
- const hot = globalThis.__PROSEBOOK_HOT__;
28
- if (next === undefined || hot === undefined) return import.meta.hot.invalidate();
29
- for (const update of hot) update(${JSON.stringify(locale)}, next.default);
30
- });
31
- }
32
- `;
33
- const HOT_INDEX = `
34
- if (import.meta.hot) {
35
- import.meta.hot.accept((next) => {
36
- if (next === undefined || next.catalog.locales.join() !== catalog.locales.join()) import.meta.hot.invalidate();
37
- });
38
- }
39
- `;
40
30
  const normalize = (path) => resolve(path).replaceAll("\\", "/");
41
- const inspectorPath = normalize(fileURLToPath(new URL(`./inspector${import.meta.url.endsWith(".ts") ? ".ts" : ".js"}`, import.meta.url)));
31
+ const inspectorPath = beside(import.meta.url, "inspector");
42
32
  const ownDir = normalize(fileURLToPath(new URL(".", import.meta.url)));
43
33
  function prosebook(options = {}) {
44
34
  let config;
@@ -46,6 +36,7 @@ function prosebook(options = {}) {
46
36
  let building = false;
47
37
  let base = "/";
48
38
  let viteRoot = "";
39
+ let kept = () => true;
49
40
  let refs = {
50
41
  exact: /* @__PURE__ */ new Set(),
51
42
  prefixes: /* @__PURE__ */ new Set()
@@ -70,7 +61,6 @@ function prosebook(options = {}) {
70
61
  const out = normalize(config.out);
71
62
  return path === `${out}/index.ts` || path === `${out}/index.js`;
72
63
  };
73
- const kept = (key) => isReferenced(key, refs) || (options.keep ?? []).some((k) => k.endsWith(".*") ? key.startsWith(k.slice(0, -1)) : k === key);
74
64
  return {
75
65
  name: "prosebook",
76
66
  async configResolved(resolved) {
@@ -79,19 +69,8 @@ function prosebook(options = {}) {
79
69
  viteRoot = resolved.root;
80
70
  config = await loadConfig(options.root === void 0 ? resolved.root : resolve(resolved.root, options.root), options.config);
81
71
  },
82
- transformIndexHtml() {
83
- if (building) return [];
84
- return [{
85
- tag: "script",
86
- attrs: {
87
- type: "module",
88
- src: `${base}@id/__x00__prosebook/dev`
89
- },
90
- injectTo: "head-prepend"
91
- }];
92
- },
93
72
  resolveId(id) {
94
- return id === DEV_ID ? id : null;
73
+ return id === DEV ? DEV_ID : null;
95
74
  },
96
75
  buildStart() {
97
76
  try {
@@ -103,6 +82,7 @@ function prosebook(options = {}) {
103
82
  exact: /* @__PURE__ */ new Set(),
104
83
  prefixes: /* @__PURE__ */ new Set()
105
84
  };
85
+ kept = keeper(refs, options.keep);
106
86
  reads.clear();
107
87
  placeholders.length = 0;
108
88
  shipped.total = shipped.kept = 0;
@@ -132,69 +112,16 @@ function prosebook(options = {}) {
132
112
  server.watcher.on("add", onChange);
133
113
  server.watcher.on("unlink", onChange);
134
114
  if (options.inspect === false) return;
135
- server.middlewares.use((req, res, next) => {
136
- const url = new URL(req.url ?? "/", "http://localhost");
137
- if (url.pathname !== `${base}__prosebook/open`) return next();
138
- const key = url.searchParams.get("key") ?? "";
139
- const message = generated?.catalog.messages.find((m) => m.key === key);
140
- if (message === void 0) {
141
- res.statusCode = 404;
142
- res.end(`no entry for "${key}" in the catalog: has it been renamed since the page loaded?`);
143
- return;
144
- }
145
- const locale = url.searchParams.has("catalog") ? void 0 : url.searchParams.get("locale") ?? void 0;
146
- const { file, line, column } = entryLocation(config, message, locale);
147
- req.url = `${base}__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`;
148
- next();
149
- });
150
- server.middlewares.use((req, res, next) => {
151
- const url = new URL(req.url ?? "/", "http://localhost");
152
- if (url.pathname !== `${base}__prosebook/entry`) return next();
153
- const reply = (status, body) => {
154
- res.statusCode = status;
155
- res.setHeader("Content-Type", "application/json");
156
- res.end(JSON.stringify(body));
157
- };
158
- const fail = (e) => reply(e instanceof CatalogError ? 409 : 400, { problems: e instanceof CatalogError ? e.problems : [{
159
- severity: "error",
160
- message: e instanceof Error ? e.message : String(e)
161
- }] });
162
- if (req.method === "GET") {
163
- try {
164
- reply(200, readEntry(config, url.searchParams.get("key") ?? ""));
165
- } catch (e) {
166
- fail(e);
167
- }
168
- return;
169
- }
170
- const origin = req.headers.origin;
171
- const sameOrigin = origin === void 0 || new URL(origin).host === req.headers.host;
172
- if (req.method !== "POST" || !sameOrigin || req.headers["content-type"]?.startsWith("application/json") !== true) {
173
- reply(403, { problems: [{
174
- severity: "error",
175
- message: "edits come from the page, as JSON"
176
- }] });
177
- return;
178
- }
179
- let body = "";
180
- req.setEncoding("utf8");
181
- req.on("data", (chunk) => body += chunk);
182
- req.on("end", () => {
183
- try {
184
- const { key, edit, write } = JSON.parse(body);
185
- const result = editEntry(config, key, edit, write === true);
186
- reply(result.problems.length > 0 ? 422 : 200, result);
187
- } catch (e) {
188
- fail(e);
189
- }
190
- });
191
- });
115
+ const editor = ({ file, line, column }) => redirect(`${base}__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`);
116
+ server.middlewares.use(nodeMiddleware(inspectorEndpoints(config, {
117
+ base,
118
+ open: editor
119
+ })));
192
120
  },
193
121
  load(id) {
194
122
  if (id === DEV_ID) {
195
- const hot = "globalThis.__PROSEBOOK_HOT__ ??= new Set();\n";
196
- if (options.inspect === false) return hot;
197
- return `import { install } from ${JSON.stringify(inspectorPath)};\n${hot}install(${JSON.stringify({ base })});\n`;
123
+ if (options.inspect === false) return HOT_SET;
124
+ return HOT_SET + installInspector(inspectorPath, base);
198
125
  }
199
126
  if (!building || prune === false || generated === void 0) return null;
200
127
  const locale = localeOf(id);
@@ -204,17 +131,18 @@ function prosebook(options = {}) {
204
131
  },
205
132
  transform: {
206
133
  order: "pre",
207
- handler(code, id) {
134
+ handler(code, id, transformOptions) {
208
135
  if (!building) {
209
136
  const locale = localeOf(id);
210
137
  if (locale !== void 0) return {
211
138
  code: code + hotLocale(locale),
212
139
  map: null
213
140
  };
214
- return isIndex(id) ? {
215
- code: code + HOT_INDEX,
141
+ if (!isIndex(id)) return null;
142
+ return {
143
+ code: (transformOptions?.ssr === true ? "" : `import ${JSON.stringify(DEV)};\n`) + code + HOT_INDEX,
216
144
  map: null
217
- } : null;
145
+ };
218
146
  }
219
147
  if (prune === false || id.includes("/node_modules/") || id.startsWith("\0")) return null;
220
148
  if (localeOf(id) !== void 0 || isIndex(id)) return null;
@@ -227,16 +155,8 @@ function prosebook(options = {}) {
227
155
  buildEnd(error) {
228
156
  if (error !== void 0 || prune !== "strict") return;
229
157
  const live = new Set(this.getModuleIds());
230
- const all = [...reads].filter(([id]) => live.has(id)).map(([, r]) => r);
231
- const readers = /* @__PURE__ */ new Set([...READERS, ...all.flatMap((r) => [...r.aliases])]);
232
- const unseen = all.flatMap((r) => r.unseen).filter((u) => u.name === "Text" || readers.has(u.name));
233
- if (unseen.length === 0) return;
234
- const at = (file) => relative(viteRoot, file).replaceAll("\\", "/");
235
- this.error([
236
- `${unseen.length} read(s) whose key the build can't see, so their messages would be pruned and the key shown instead:`,
237
- ...unseen.map((u) => ` ${at(u.file)}:${u.line}:${u.column} ${u.code}`),
238
- "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."
239
- ].join("\n"));
158
+ const unseen = unseenOf([...reads].filter(([id]) => live.has(id)).map(([, r]) => r));
159
+ if (unseen.length > 0) this.error(unseenMessage(unseen, viteRoot));
240
160
  },
241
161
  renderChunk(code) {
242
162
  if (!code.includes("__PROSEBOOK_LOCALE_") || generated === void 0) return null;
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. The page also gets the inspector (`inspector.ts`), and\n * the server opens the entry it asks for through Vite's own `/__open-in-editor`, so the editor is\n * whichever Vite already launches.\n */\nimport { relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { Plugin } from 'vite';\n\nimport { CatalogError, entryLocation } from './tools/catalog.ts';\nimport { compileLocale, localeExpression } from './tools/compile.ts';\nimport { loadConfig, type ProsebookConfig, type ResolvedConfig } from './tools/config.ts';\nimport { editEntry, readEntry, type EntryEdit } from './tools/entry.ts';\nimport { generate, type Generated } from './tools/generate.ts';\nimport { isReferenced, keyRefs, READERS, 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?: 'strict' | 'loose' | false;\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_ID = '\\0prosebook/dev';\n\nconst 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\nconst 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\nconst normalize = (path: string) => resolve(path).replaceAll('\\\\', '/');\n\n// Beside this module: the source when run from source, the built file in dist.\nconst inspectorPath = normalize(fileURLToPath(new URL(`./inspector${import.meta.url.endsWith('.ts') ? '.ts' : '.js'}`, import.meta.url)));\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 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 const kept = (key: string) =>\n isReferenced(key, refs) || (options.keep ?? []).some((k) => (k.endsWith('.*') ? key.startsWith(k.slice(0, -1)) : k === key));\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 // First in <head>, so the hot hooks are there before any copy is created.\n transformIndexHtml() {\n if (building) return [];\n return [{ tag: 'script', attrs: { type: 'module', src: `${base}@id/__x00__prosebook/dev` }, injectTo: 'head-prepend' }];\n },\n\n resolveId(id) {\n return id === DEV_ID ? 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 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 // `?key=cart.items&locale=de`: the translation's entry, or the catalog's with `catalog` or when\n // the locale has none. Handed on to Vite's `/__open-in-editor`, which comes later in the stack.\n server.middlewares.use((req, res, next) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== `${base}__prosebook/open`) return next();\n const key = url.searchParams.get('key') ?? '';\n const message = generated?.catalog.messages.find((m) => m.key === key);\n if (message === undefined) {\n res.statusCode = 404;\n res.end(`no entry for \"${key}\" in the catalog: has it been renamed since the page loaded?`);\n return;\n }\n const locale = url.searchParams.has('catalog') ? undefined : (url.searchParams.get('locale') ?? undefined);\n const { file, line, column } = entryLocation(config, message, locale);\n req.url = `${base}__open-in-editor?file=${encodeURIComponent(`${file}:${line}:${column}`)}`;\n next();\n });\n\n // The inspector's dialog: GET `?key=…` reads an entry in every locale, POST `{ key, edit, write }`\n // applies an edit (`tools/entry.ts`). A POST writes files, so only the page itself may send one:\n // same origin, and JSON, which a form on another site can't send without asking first.\n server.middlewares.use((req, res, next) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n if (url.pathname !== `${base}__prosebook/entry`) return next();\n const reply = (status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify(body));\n };\n const fail = (e: unknown) =>\n reply(e instanceof CatalogError ? 409 : 400, {\n problems: e instanceof CatalogError ? e.problems : [{ severity: 'error', message: e instanceof Error ? e.message : String(e) }],\n });\n if (req.method === 'GET') {\n try {\n reply(200, readEntry(config, url.searchParams.get('key') ?? ''));\n } catch (e) {\n fail(e);\n }\n return;\n }\n const origin = req.headers.origin;\n const sameOrigin = origin === undefined || new URL(origin).host === req.headers.host;\n if (req.method !== 'POST' || !sameOrigin || req.headers['content-type']?.startsWith('application/json') !== true) {\n reply(403, { problems: [{ severity: 'error', message: 'edits come from the page, as JSON' }] });\n return;\n }\n let body = '';\n req.setEncoding('utf8');\n req.on('data', (chunk: string) => (body += chunk));\n req.on('end', () => {\n try {\n const { key, edit, write } = JSON.parse(body) as { key: string; edit: EntryEdit; write?: boolean };\n const result = editEntry(config, key, edit, write === true);\n reply(result.problems.length > 0 ? 422 : 200, result);\n } catch (e) {\n fail(e);\n }\n });\n });\n },\n\n load(id) {\n if (id === DEV_ID) {\n const hot = 'globalThis.__PROSEBOOK_HOT__ ??= new Set();\\n';\n if (options.inspect === false) return hot;\n return `import { install } from ${JSON.stringify(inspectorPath)};\\n${hot}install(${JSON.stringify({ base })});\\n`;\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) {\n if (!building) {\n const locale = localeOf(id);\n if (locale !== undefined) return { code: code + hotLocale(locale), map: null };\n return isIndex(id) ? { code: code + HOT_INDEX, map: null } : 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 all = [...reads].filter(([id]) => live.has(id)).map(([, r]) => r);\n const readers = new Set([...READERS, ...all.flatMap((r) => [...r.aliases])]);\n const unseen = all.flatMap((r) => r.unseen).filter((u) => u.name === 'Text' || readers.has(u.name));\n if (unseen.length === 0) return;\n const at = (file: string) => relative(viteRoot, file).replaceAll('\\\\', '/');\n this.error(\n [\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 },\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 * 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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"}