prosebook 0.1.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 (114) hide show
  1. package/README.md +311 -57
  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-CxVMn19f.js → config-BPSmoYel.js} +82 -196
  15. package/dist/config-BPSmoYel.js.map +1 -0
  16. package/dist/{config-CF7HziDa.d.ts → config-DdMkKt_N.d.ts} +85 -7
  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-PkPvsB07.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 -64
  61. package/dist/tools.d.ts.map +1 -1
  62. package/dist/tools.js +5 -4
  63. package/dist/vite.d.ts +14 -4
  64. package/dist/vite.d.ts.map +1 -1
  65. package/dist/vite.js +60 -114
  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 +61 -13
  83. package/src/tools/config.ts +58 -3
  84. package/src/tools/endpoints.ts +121 -0
  85. package/src/tools/entry.ts +77 -0
  86. package/src/tools/generate.ts +32 -32
  87. package/src/tools/icu.ts +93 -8
  88. package/src/tools/index.ts +20 -4
  89. package/src/tools/lint.ts +49 -12
  90. package/src/tools/lock.ts +7 -0
  91. package/src/tools/mf1.ts +21 -7
  92. package/src/tools/mf2.ts +249 -57
  93. package/src/tools/migrate.ts +3 -2
  94. package/src/tools/native/emit.ts +87 -0
  95. package/src/tools/native/index.ts +36 -0
  96. package/src/tools/native/kotlin.ts +269 -0
  97. package/src/tools/native/model.ts +280 -0
  98. package/src/tools/native/number.ts +210 -0
  99. package/src/tools/native/plurals.ts +103 -0
  100. package/src/tools/native/rust.ts +367 -0
  101. package/src/tools/native/swift.ts +326 -0
  102. package/src/tools/scan.ts +134 -4
  103. package/src/tools/syntax.ts +3 -3
  104. package/src/tools/todo.ts +118 -0
  105. package/src/vite.ts +74 -116
  106. package/dist/config-CF7HziDa.d.ts.map +0 -1
  107. package/dist/config-CxVMn19f.js.map +0 -1
  108. package/dist/copy-DXnzXsJa.d.ts.map +0 -1
  109. package/dist/helpers-BPuPGjQP.js +0 -61
  110. package/dist/helpers-BPuPGjQP.js.map +0 -1
  111. package/dist/index.js.map +0 -1
  112. package/dist/sarif-PkPvsB07.js.map +0 -1
  113. package/dist/scan-B0tZ7y-D.js +0 -2447
  114. package/dist/scan-B0tZ7y-D.js.map +0 -1
@@ -0,0 +1,387 @@
1
+ import { d as staticLiteral, l as placeholderOf, o as numberOptions, r as dateOptions, s as optionArgsOf, t as TYPE$1, u as selectionOf } from "./icu-Dej0mNsF.js";
2
+ import { extname, join, relative } from "node:path";
3
+ import { readFileSync, readdirSync, statSync } from "node:fs";
4
+ import { isDateElement, isNumberElement, isTimeElement } from "@formatjs/icu-messageformat-parser";
5
+ import { parseSync } from "oxc-parser";
6
+ // src/tools/compile.ts
7
+ /**
8
+ * Compiles ICU ASTs to JavaScript.
9
+ *
10
+ * A message with no placeholders becomes its string. Any other becomes `(a, h) => …`, where `a` holds
11
+ * the arguments and `h` the locale's helpers (see `runtime/helpers.ts`): a plural is a call picking an
12
+ * arm, a number is a call to a cached `Intl.NumberFormat`. A placeholder without a value, or with one
13
+ * `Intl` refuses, shows as `{name}` and is reported (`h.e`) rather than throwing, so every such
14
+ * message uses `h`. Formatting options are resolved here and
15
+ * hoisted to module constants, so a skeleton costs nothing at runtime and its formatter is built once.
16
+ * Options an argument gives (MF2's `currency=$code`) follow them as an object built per call, and the
17
+ * runtime keeps a formatter per value.
18
+ */
19
+ /** Module-level constants for formatting options, one per distinct object. */
20
+ var Hoisted = class {
21
+ byJson = /* @__PURE__ */ new Map();
22
+ /** `, oN` naming the hoisted options, or nothing when there are none. */
23
+ arg(options) {
24
+ if (options === void 0 || Object.keys(options).length === 0) return "";
25
+ const json = JSON.stringify(options);
26
+ let name = this.byJson.get(json);
27
+ if (name === void 0) this.byJson.set(json, name = `o${this.byJson.size}`);
28
+ return `, ${name}`;
29
+ }
30
+ declarations() {
31
+ return [...this.byJson].map(([json, name]) => `const ${name} = ${json};`);
32
+ }
33
+ };
34
+ function access(name) {
35
+ return /^[A-Za-z_$][\w$]*$/.test(name) ? `a.${name}` : `a[${JSON.stringify(name)}]`;
36
+ }
37
+ function nodes(elements, hoisted, pound) {
38
+ if (elements.length === 0) return "\"\"";
39
+ const parts = elements.map((el) => node(el, hoisted, pound));
40
+ return parts.length === 1 ? parts[0] : `[${parts.join(", ")}]`;
41
+ }
42
+ function arms(options, hoisted, pound) {
43
+ return `{ ${Object.entries(options).map(([arm, option]) => `${JSON.stringify(arm)}: () => ${nodes(option.value, hoisted, pound)}`).join(", ")} }`;
44
+ }
45
+ /** The hoisted options, then the options arguments give as `{ currency: a.code }`, where there are any. */
46
+ function formatting(options, args, hoisted) {
47
+ const given = Object.entries(args ?? {});
48
+ const fixed = hoisted.arg(options);
49
+ if (given.length === 0) return fixed;
50
+ return `${fixed || ", undefined"}, { ${given.map(([option, { arg }]) => `${option}: ${access(arg)}`).join(", ")} }`;
51
+ }
52
+ /** A string literal, its invisible direction marks escaped so the generated code shows them. */
53
+ function text(value) {
54
+ return JSON.stringify(value).replace(/[\u2066-\u2069]/g, (c) => `\\u${c.charCodeAt(0).toString(16)}`);
55
+ }
56
+ /**
57
+ * A placeholder's value, or its fallback (`{name}`, reported through `h.e`) where it has none or
58
+ * `Intl` refuses it, between the marks isolating its direction where it has them.
59
+ */
60
+ function placed(el, name, value) {
61
+ const { fallback, isolate } = placeholderOf(el, name);
62
+ const shown = `${value} ?? h.e(${text(fallback)})`;
63
+ return isolate === void 0 ? shown : `[${text(isolate[0])}, ${shown}, ${text(isolate[1])}]`;
64
+ }
65
+ function node(el, hoisted, pound) {
66
+ const formatted = (helper, name, options) => placed(el, name, `h.${helper}(${access(name)}${formatting(options, optionArgsOf(el), hoisted)})`);
67
+ if (isNumberElement(el)) return formatted("n", el.value, numberOptions(el));
68
+ if (isDateElement(el) || isTimeElement(el)) return formatted("d", el.value, dateOptions(el));
69
+ switch (el.type) {
70
+ case TYPE$1.literal: return JSON.stringify(el.value);
71
+ case TYPE$1.argument: return placed(el, el.value, access(el.value));
72
+ case TYPE$1.pound:
73
+ if (pound === null) return "\"#\"";
74
+ return placed(el, pound.name, `h.n(${access(pound.name)}${pound.offset !== 0 ? ` - ${pound.offset}` : ""})`);
75
+ case TYPE$1.select: return `h.s(${access(el.value)}, ${arms(el.options, hoisted, pound)})`;
76
+ case TYPE$1.plural: {
77
+ const selection = selectionOf(el);
78
+ const value = access(el.value);
79
+ const on = selection?.value === "round" ? `Math.round(${value})` : selection?.value === "percent" ? `${value} * 100` : value;
80
+ const rules = selection === void 0 ? "" : formatting(selection.options, selection.args, hoisted);
81
+ const own = {
82
+ name: el.value,
83
+ offset: el.offset
84
+ };
85
+ return `h.p(${on}, ${el.offset}, ${el.pluralType === "ordinal"}, ${arms(el.options, hoisted, own)}${rules})`;
86
+ }
87
+ case TYPE$1.tag: return `h.t(a, ${JSON.stringify(el.value)}, ${nodes(el.children, hoisted, pound)})`;
88
+ default: throw new Error(`prosebook: no compiler for ICU element type ${el.type}`);
89
+ }
90
+ }
91
+ function message(ast, hoisted) {
92
+ const literal = staticLiteral(ast);
93
+ if (literal !== void 0) return JSON.stringify(literal);
94
+ return `(a, h) => ${nodes(ast, hoisted, null)}`;
95
+ }
96
+ /** One locale's messages as code, keeping only `keep` when given. */
97
+ function compileLocale(entries, keep) {
98
+ const hoisted = new Hoisted();
99
+ const rows = [];
100
+ for (const [key, ast] of entries) {
101
+ if (keep && !keep(key)) continue;
102
+ rows.push(` ${JSON.stringify(key)}: ${message(ast, hoisted)},`);
103
+ }
104
+ return {
105
+ declarations: hoisted.declarations(),
106
+ rows
107
+ };
108
+ }
109
+ /** The locale as an ES module whose default export is its messages. */
110
+ function localeModule(compiled, banner, ts) {
111
+ const head = ts ? `${banner}\nimport type { CompiledMessages } from 'prosebook';\n` : `${banner}\n`;
112
+ const declarations = ts ? compiled.declarations.map((d) => d.replace(/;$/, " as const;")) : compiled.declarations;
113
+ const consts = declarations.length > 0 ? `\n${declarations.join("\n")}\n` : "";
114
+ const close = ts ? "} satisfies CompiledMessages;" : "};";
115
+ return `${head}${consts}\nexport default {\n${compiled.rows.join("\n")}\n${close}\n`;
116
+ }
117
+ /** The locale as one expression, for a bundler to inline in place of the module. */
118
+ function localeExpression(compiled) {
119
+ return `(() => {\n${compiled.declarations.join("\n")}\nreturn {\n${compiled.rows.join("\n")}\n};\n})()`;
120
+ }
121
+ // src/tools/scan.ts
122
+ /**
123
+ * What the code says: text a person reads that is still written in a component, and which catalog
124
+ * keys the code names.
125
+ *
126
+ * Key references are found by reading string literals, not by tracing calls, so a key kept in an
127
+ * array or a map still counts, and `nav.${id}` or `'nav.' + id` keeps every key under `nav.`. That
128
+ * errs towards keeping a key, which is the safe direction both for `unused` and for pruning a bundle.
129
+ */
130
+ const SKIP_DIRS = /* @__PURE__ */ new Set([
131
+ "node_modules",
132
+ "dist",
133
+ "build",
134
+ "out",
135
+ "coverage",
136
+ ".git"
137
+ ]);
138
+ function sourceFiles(dirs, extensions, skip = []) {
139
+ const files = [];
140
+ const visit = (dir) => {
141
+ let names;
142
+ try {
143
+ names = readdirSync(dir);
144
+ } catch {
145
+ return;
146
+ }
147
+ for (const name of names) {
148
+ const path = join(dir, name);
149
+ if (skip.some((s) => path === s || path.startsWith(`${s}/`) || path.startsWith(`${s}\\`))) continue;
150
+ if (statSync(path).isDirectory()) {
151
+ if (!SKIP_DIRS.has(name) && !name.startsWith(".")) visit(path);
152
+ } else if (extensions.includes(extname(name)) && !name.endsWith(".d.ts")) files.push(path);
153
+ }
154
+ };
155
+ for (const dir of dirs) visit(dir);
156
+ return files.sort();
157
+ }
158
+ const LITERAL_KEY = /(['"`])([a-z0-9][\w-]*(?:\.[\w-]+)+)\1/g;
159
+ const KEY_PREFIX = /(['"`])([a-z0-9][\w-]*(?:\.[\w-]+)*\.)(?:\1|\$\{)/g;
160
+ function keyRefs(code, into = {
161
+ exact: /* @__PURE__ */ new Set(),
162
+ prefixes: /* @__PURE__ */ new Set()
163
+ }) {
164
+ for (const m of code.matchAll(LITERAL_KEY)) into.exact.add(m[2]);
165
+ for (const m of code.matchAll(KEY_PREFIX)) into.prefixes.add(m[2]);
166
+ return into;
167
+ }
168
+ function isReferenced(key, refs) {
169
+ if (refs.exact.has(key)) return true;
170
+ for (const prefix of refs.prefixes) if (key.startsWith(prefix)) return true;
171
+ return false;
172
+ }
173
+ /**
174
+ * Text that reads as code rather than prose: one token holding a path, a call, an identifier with an
175
+ * underscore or a dotted name, as in `form.json()` or `src/index.ts`. Trailing punctuation doesn't
176
+ * count, so `Done.` is still prose.
177
+ */
178
+ function looksLikeCode(text) {
179
+ const token = text.trim().replace(/[.!?:,…]+$/, "");
180
+ return token !== "" && !/\s/.test(token) && /[/()_=<>{}[\].]|::/.test(token);
181
+ }
182
+ const NON_PROSE_ELEMENTS = /* @__PURE__ */ new Set([
183
+ "code",
184
+ "pre",
185
+ "kbd",
186
+ "samp",
187
+ "script",
188
+ "style",
189
+ "svg",
190
+ "path"
191
+ ]);
192
+ const LETTER = /\p{L}/u;
193
+ /** Offsets in `code` as lines and columns, and whether a line is marked `prosebook-ignore`, on it or the line above. */
194
+ function lineIndex(code) {
195
+ const lines = code.split("\n");
196
+ const lineStarts = [];
197
+ let offset = 0;
198
+ for (const line of lines) {
199
+ lineStarts.push(offset);
200
+ offset += line.length + 1;
201
+ }
202
+ const position = (at) => {
203
+ let lo = 0;
204
+ let hi = lineStarts.length - 1;
205
+ while (lo < hi) {
206
+ const mid = lo + hi + 1 >> 1;
207
+ if (lineStarts[mid] <= at) lo = mid;
208
+ else hi = mid - 1;
209
+ }
210
+ return {
211
+ line: lo + 1,
212
+ column: at - lineStarts[lo] + 1
213
+ };
214
+ };
215
+ const ignored = (line) => (lines[line - 1] ?? "").includes("prosebook-ignore") || (lines[line - 2] ?? "").includes("prosebook-ignore");
216
+ return {
217
+ position,
218
+ ignored
219
+ };
220
+ }
221
+ /** Literal text in JSX that a person reads, where it should be a catalog entry. */
222
+ function hardcodedText(file, code, attributes) {
223
+ const { program } = parseSync(file, code);
224
+ const found = [];
225
+ const { position, ignored } = lineIndex(code);
226
+ const report = (node, text, where) => {
227
+ const trimmed = text.replace(/\s+/g, " ").trim();
228
+ if (!LETTER.test(trimmed) || looksLikeCode(trimmed)) return;
229
+ const at = position(node.start + Math.max(0, code.slice(node.start, node.end).search(/\S/)));
230
+ if (ignored(at.line)) return;
231
+ found.push({
232
+ file,
233
+ ...at,
234
+ text: trimmed,
235
+ where
236
+ });
237
+ };
238
+ const visit = (node, inProse) => {
239
+ let prose = inProse;
240
+ if (node.type === "JSXElement") {
241
+ const opening = node.openingElement;
242
+ if (opening.name.type === "JSXIdentifier" && NON_PROSE_ELEMENTS.has(opening.name.name ?? "")) prose = false;
243
+ }
244
+ if (node.type === "JSXText" && prose) report(node, node.value, "text");
245
+ if (node.type === "JSXExpressionContainer" && prose) {
246
+ const expr = node.expression;
247
+ if (expr.type === "Literal" && typeof expr.value === "string") report(expr, expr.value, "text");
248
+ if (expr.type === "TemplateLiteral" && expr.expressions.length === 0) report(expr, String(expr.quasis[0]?.value.cooked), "text");
249
+ }
250
+ if (node.type === "JSXAttribute") {
251
+ const name = node.name;
252
+ const value = node.value;
253
+ const attr = name.type === "JSXIdentifier" ? name.name ?? "" : "";
254
+ if (attributes.includes(attr) && value !== null) {
255
+ if (value.type === "Literal" && typeof value.value === "string") report(value, value.value, attr);
256
+ if (value.type === "JSXExpressionContainer") {
257
+ const expr = value.expression;
258
+ if (expr.type === "Literal" && typeof expr.value === "string") report(expr, expr.value, attr);
259
+ }
260
+ }
261
+ return;
262
+ }
263
+ for (const value of Object.values(node)) if (Array.isArray(value)) {
264
+ for (const child of value) if (isNode(child)) visit(child, prose);
265
+ } else if (isNode(value)) visit(value, prose);
266
+ };
267
+ visit(program, true);
268
+ return found;
269
+ }
270
+ function isNode(value) {
271
+ return typeof value === "object" && value !== null && typeof value.type === "string";
272
+ }
273
+ function scan(dirs, extensions, attributes, root, skip = []) {
274
+ const refs = {
275
+ exact: /* @__PURE__ */ new Set(),
276
+ prefixes: /* @__PURE__ */ new Set()
277
+ };
278
+ const hardcoded = [];
279
+ const files = sourceFiles(dirs, extensions, skip);
280
+ for (const path of files) {
281
+ const code = readFileSync(path, "utf8");
282
+ keyRefs(code, refs);
283
+ if (/\.[jt]sx$/.test(path)) hardcoded.push(...hardcodedText(relative(root, path).replaceAll("\\", "/"), code, attributes));
284
+ }
285
+ return {
286
+ hardcoded,
287
+ refs,
288
+ files: files.length
289
+ };
290
+ }
291
+ /** A copy's methods that take a key first. */
292
+ const READERS = /* @__PURE__ */ new Set(["t", "rich"]);
293
+ const PREFIX = /^[a-z0-9][\w-]*(?:\.[\w-]+)*\.$/;
294
+ const WRAPPERS = /* @__PURE__ */ new Set([
295
+ "ParenthesizedExpression",
296
+ "TSAsExpression",
297
+ "TSSatisfiesExpression",
298
+ "TSNonNullExpression",
299
+ "TSTypeAssertion"
300
+ ]);
301
+ /** Whether `keyRefs` finds every key an expression can be: a literal, or a template or sum after a key prefix. */
302
+ function keySeen(node) {
303
+ if (node === void 0) return false;
304
+ if (WRAPPERS.has(node.type)) return keySeen(node.expression);
305
+ switch (node.type) {
306
+ case "Literal": return typeof node.value === "string";
307
+ case "TemplateLiteral": {
308
+ const [head] = node.quasis;
309
+ return node.expressions.length === 0 || PREFIX.test(head?.value.cooked ?? "");
310
+ }
311
+ case "BinaryExpression": {
312
+ const left = node.left;
313
+ return node.operator === "+" && left.type === "Literal" && typeof left.value === "string" && PREFIX.test(left.value);
314
+ }
315
+ case "ConditionalExpression": return keySeen(node.consequent) && keySeen(node.alternate);
316
+ case "LogicalExpression": return keySeen(node.left) && keySeen(node.right);
317
+ default: return false;
318
+ }
319
+ }
320
+ const nameOf = (node) => node?.type === "Identifier" || node?.type === "JSXIdentifier" ? node.name : node?.type === "Literal" ? String(node.value) : void 0;
321
+ /**
322
+ * The reads in a module whose key a pruned build can't see, as in `t(item.key)` or `<Text id={id} />`:
323
+ * the build would leave the message out, and the key would show in its place. A build has no types,
324
+ * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,
325
+ * which may be bound to one elsewhere (`aliases`); the caller keeps those whose name turns out to be a
326
+ * reader. `<Text>` counts where it is imported from `prosebook/react`. Lines marked `prosebook-ignore`
327
+ * are skipped.
328
+ */
329
+ function unseenReads(file, code) {
330
+ const { program } = parseSync(file, code);
331
+ const { position, ignored } = lineIndex(code);
332
+ const unseen = [];
333
+ const aliases = /* @__PURE__ */ new Set();
334
+ /** Local name to the name it was imported by. */
335
+ const imported = /* @__PURE__ */ new Map();
336
+ const texts = /* @__PURE__ */ new Set();
337
+ for (const statement of program.body) {
338
+ if (statement.type !== "ImportDeclaration") continue;
339
+ for (const s of statement.specifiers) {
340
+ const name = s.type === "ImportSpecifier" ? nameOf(s.imported) : void 0;
341
+ const local = nameOf(s.local);
342
+ if (name === void 0 || local === void 0) continue;
343
+ if (name === "Text" && statement.source.value === "prosebook/react") texts.add(local);
344
+ else imported.set(local, name);
345
+ }
346
+ }
347
+ const report = (node, name) => {
348
+ const at = position(node.start);
349
+ if (ignored(at.line)) return;
350
+ const text = code.slice(node.start, node.end).replace(/\s+/g, " ");
351
+ unseen.push({
352
+ file,
353
+ ...at,
354
+ name,
355
+ code: text.length > 80 ? `${text.slice(0, 77)}...` : text
356
+ });
357
+ };
358
+ const calls = [];
359
+ const visit = (node) => {
360
+ if (node.type === "ObjectPattern") {
361
+ for (const p of node.properties) if (p.type === "Property" && READERS.has(nameOf(p.key) ?? "") && p.value.type === "Identifier") aliases.add(nameOf(p.value));
362
+ }
363
+ if (node.type === "CallExpression" && !keySeen(node.arguments[0])) {
364
+ const callee = node.callee;
365
+ if (callee.type === "Identifier") calls.push([node, callee.name]);
366
+ else if (callee.type === "MemberExpression" && !callee.computed && READERS.has(nameOf(callee.property) ?? "")) report(node, nameOf(callee.property));
367
+ }
368
+ if (node.type === "JSXOpeningElement" && texts.has(nameOf(node.name) ?? "")) {
369
+ const value = node.attributes.find((a) => a.type === "JSXAttribute" && nameOf(a.name) === "id")?.value;
370
+ if (value !== null && value !== void 0 && !keySeen(value.type === "JSXExpressionContainer" ? value.expression : value)) report(node, "Text");
371
+ }
372
+ for (const value of Object.values(node)) if (Array.isArray(value)) {
373
+ for (const child of value) if (isNode(child)) visit(child);
374
+ } else if (isNode(value)) visit(value);
375
+ };
376
+ visit(program);
377
+ for (const [node, local] of calls) if (READERS.has(local) || aliases.has(local)) report(node, local);
378
+ else if (imported.has(local)) report(node, imported.get(local));
379
+ unseen.sort((a, b) => a.line === b.line ? a.column - b.column : a.line - b.line);
380
+ return {
381
+ unseen,
382
+ aliases
383
+ };
384
+ }
385
+ export { looksLikeCode as a, unseenReads as c, localeModule as d, keyRefs as i, compileLocale as l, hardcodedText as n, scan as o, isReferenced as r, sourceFiles as s, READERS as t, localeExpression as u };
386
+
387
+ //# sourceMappingURL=scan-Bx0_yDSi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-Bx0_yDSi.js","names":["TYPE"],"sources":["../src/tools/compile.ts","../src/tools/scan.ts"],"sourcesContent":["/**\n * Compiles ICU ASTs to JavaScript.\n *\n * A message with no placeholders becomes its string. Any other becomes `(a, h) => …`, where `a` holds\n * the arguments and `h` the locale's helpers (see `runtime/helpers.ts`): a plural is a call picking an\n * arm, a number is a call to a cached `Intl.NumberFormat`. A placeholder without a value, or with one\n * `Intl` refuses, shows as `{name}` and is reported (`h.e`) rather than throwing, so every such\n * message uses `h`. Formatting options are resolved here and\n * hoisted to module constants, so a skeleton costs nothing at runtime and its formatter is built once.\n * Options an argument gives (MF2's `currency=$code`) follow them as an object built per call, and the\n * runtime keeps a formatter per value.\n */\nimport { isDateElement, isNumberElement, isTimeElement } from '@formatjs/icu-messageformat-parser';\n\nimport {\n dateOptions,\n numberOptions,\n optionArgsOf,\n placeholderOf,\n selectionOf,\n staticLiteral,\n TYPE,\n type MessageFormatElement,\n type OptionArgs,\n} from './icu.ts';\n\nexport type Entry = readonly [key: string, ast: MessageFormatElement[]];\n\n/** Module-level constants for formatting options, one per distinct object. */\nclass Hoisted {\n private readonly byJson = new Map<string, string>();\n\n /** `, oN` naming the hoisted options, or nothing when there are none. */\n arg(options: object | undefined): string {\n // `{}` (a bare `:number`) formats as no options do, so it needs no constant.\n if (options === undefined || Object.keys(options).length === 0) return '';\n const json = JSON.stringify(options);\n let name = this.byJson.get(json);\n if (name === undefined) this.byJson.set(json, (name = `o${this.byJson.size}`));\n return `, ${name}`;\n }\n\n declarations(): string[] {\n return [...this.byJson].map(([json, name]) => `const ${name} = ${json};`);\n }\n}\n\nfunction access(name: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(name) ? `a.${name}` : `a[${JSON.stringify(name)}]`;\n}\n\n/** The plural a `#` prints the value of. */\ntype Pound = { name: string; offset: number } | null;\n\nfunction nodes(elements: MessageFormatElement[], hoisted: Hoisted, pound: Pound): string {\n if (elements.length === 0) return '\"\"';\n const parts = elements.map((el) => node(el, hoisted, pound));\n return parts.length === 1 ? parts[0]! : `[${parts.join(', ')}]`;\n}\n\nfunction arms(options: Record<string, { value: MessageFormatElement[] }>, hoisted: Hoisted, pound: Pound): string {\n const body = Object.entries(options).map(([arm, option]) => `${JSON.stringify(arm)}: () => ${nodes(option.value, hoisted, pound)}`);\n return `{ ${body.join(', ')} }`;\n}\n\n/** The hoisted options, then the options arguments give as `{ currency: a.code }`, where there are any. */\nfunction formatting(options: object | undefined, args: OptionArgs | undefined, hoisted: Hoisted): string {\n const given = Object.entries(args ?? {});\n const fixed = hoisted.arg(options);\n if (given.length === 0) return fixed;\n return `${fixed || ', undefined'}, { ${given.map(([option, { arg }]) => `${option}: ${access(arg)}`).join(', ')} }`;\n}\n\n/** A string literal, its invisible direction marks escaped so the generated code shows them. */\nfunction text(value: string): string {\n return JSON.stringify(value).replace(/[\\u2066-\\u2069]/g, (c) => `\\\\u${c.charCodeAt(0).toString(16)}`);\n}\n\n/**\n * A placeholder's value, or its fallback (`{name}`, reported through `h.e`) where it has none or\n * `Intl` refuses it, between the marks isolating its direction where it has them.\n */\nfunction placed(el: MessageFormatElement, name: string, value: string): string {\n const { fallback, isolate } = placeholderOf(el, name);\n const shown = `${value} ?? h.e(${text(fallback)})`;\n return isolate === undefined ? shown : `[${text(isolate[0])}, ${shown}, ${text(isolate[1])}]`;\n}\n\nfunction node(el: MessageFormatElement, hoisted: Hoisted, pound: Pound): string {\n const formatted = (helper: string, name: string, options: object | undefined) =>\n placed(el, name, `h.${helper}(${access(name)}${formatting(options, optionArgsOf(el), hoisted)})`);\n if (isNumberElement(el)) return formatted('n', el.value, numberOptions(el));\n if (isDateElement(el) || isTimeElement(el)) return formatted('d', el.value, dateOptions(el));\n switch (el.type) {\n case TYPE.literal:\n return JSON.stringify(el.value);\n case TYPE.argument:\n return placed(el, el.value, access(el.value));\n case TYPE.pound:\n // `#` is its plural's own value less its offset, formatted as a number.\n if (pound === null) return '\"#\"';\n return placed(el, pound.name, `h.n(${access(pound.name)}${pound.offset !== 0 ? ` - ${pound.offset}` : ''})`);\n case TYPE.select:\n return `h.s(${access(el.value)}, ${arms(el.options, hoisted, pound)})`;\n case TYPE.plural: {\n // Read from MF2, a plural selects on the number as formatted, and on what `:integer` and `:percent` make of it.\n const selection = selectionOf(el);\n const value = access(el.value);\n const on = selection?.value === 'round' ? `Math.round(${value})` : selection?.value === 'percent' ? `${value} * 100` : value;\n const rules = selection === undefined ? '' : formatting(selection.options, selection.args, hoisted);\n const own = { name: el.value, offset: el.offset };\n return `h.p(${on}, ${el.offset}, ${el.pluralType === 'ordinal'}, ${arms(el.options, hoisted, own)}${rules})`;\n }\n case TYPE.tag:\n return `h.t(a, ${JSON.stringify(el.value)}, ${nodes(el.children, hoisted, pound)})`;\n default:\n throw new Error(`prosebook: no compiler for ICU element type ${(el as MessageFormatElement).type}`);\n }\n}\n\nfunction message(ast: MessageFormatElement[], hoisted: Hoisted): string {\n const literal = staticLiteral(ast);\n if (literal !== undefined) return JSON.stringify(literal);\n return `(a, h) => ${nodes(ast, hoisted, null)}`;\n}\n\nexport interface CompiledLocale {\n /** `const oN = {…};` lines the messages refer to. */\n declarations: string[];\n /** `\"key\": message,` lines. */\n rows: string[];\n}\n\n/** One locale's messages as code, keeping only `keep` when given. */\nexport function compileLocale(entries: Iterable<Entry>, keep?: (key: string) => boolean): CompiledLocale {\n const hoisted = new Hoisted();\n const rows: string[] = [];\n for (const [key, ast] of entries) {\n if (keep && !keep(key)) continue;\n rows.push(` ${JSON.stringify(key)}: ${message(ast, hoisted)},`);\n }\n return { declarations: hoisted.declarations(), rows };\n}\n\n/** The locale as an ES module whose default export is its messages. */\nexport function localeModule(compiled: CompiledLocale, banner: string, ts: boolean): string {\n const head = ts ? `${banner}\\nimport type { CompiledMessages } from 'prosebook';\\n` : `${banner}\\n`;\n // `as const` keeps `style: \"currency\"` a literal, which is what `Intl`'s option types ask for.\n const declarations = ts ? compiled.declarations.map((d) => d.replace(/;$/, ' as const;')) : compiled.declarations;\n const consts = declarations.length > 0 ? `\\n${declarations.join('\\n')}\\n` : '';\n const close = ts ? '} satisfies CompiledMessages;' : '};';\n return `${head}${consts}\\nexport default {\\n${compiled.rows.join('\\n')}\\n${close}\\n`;\n}\n\n/** The locale as one expression, for a bundler to inline in place of the module. */\nexport function localeExpression(compiled: CompiledLocale): string {\n return `(() => {\\n${compiled.declarations.join('\\n')}\\nreturn {\\n${compiled.rows.join('\\n')}\\n};\\n})()`;\n}\n","/**\n * What the code says: text a person reads that is still written in a component, and which catalog\n * keys the code names.\n *\n * Key references are found by reading string literals, not by tracing calls, so a key kept in an\n * array or a map still counts, and `nav.${id}` or `'nav.' + id` keeps every key under `nav.`. That\n * errs towards keeping a key, which is the safe direction both for `unused` and for pruning a bundle.\n */\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { extname, join, relative } from 'node:path';\n\nimport { parseSync } from 'oxc-parser';\n\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', '.git']);\n\nexport function sourceFiles(dirs: string[], extensions: string[], skip: string[] = []): string[] {\n const files: string[] = [];\n const visit = (dir: string) => {\n let names: string[];\n try {\n names = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of names) {\n const path = join(dir, name);\n if (skip.some((s) => path === s || path.startsWith(`${s}/`) || path.startsWith(`${s}\\\\`))) continue;\n if (statSync(path).isDirectory()) {\n if (!SKIP_DIRS.has(name) && !name.startsWith('.')) visit(path);\n } else if (extensions.includes(extname(name)) && !name.endsWith('.d.ts')) {\n files.push(path);\n }\n }\n };\n for (const dir of dirs) visit(dir);\n return files.sort();\n}\n\nexport interface KeyRefs {\n exact: Set<string>;\n /** `nav.` from `nav.${id}` or `'nav.' + id`: every key under it may be read. */\n prefixes: Set<string>;\n}\n\nconst LITERAL_KEY = /(['\"`])([a-z0-9][\\w-]*(?:\\.[\\w-]+)+)\\1/g;\nconst KEY_PREFIX = /(['\"`])([a-z0-9][\\w-]*(?:\\.[\\w-]+)*\\.)(?:\\1|\\$\\{)/g;\n\nexport function keyRefs(code: string, into: KeyRefs = { exact: new Set(), prefixes: new Set() }): KeyRefs {\n for (const m of code.matchAll(LITERAL_KEY)) into.exact.add(m[2]!);\n for (const m of code.matchAll(KEY_PREFIX)) into.prefixes.add(m[2]!);\n return into;\n}\n\nexport function isReferenced(key: string, refs: KeyRefs): boolean {\n if (refs.exact.has(key)) return true;\n for (const prefix of refs.prefixes) if (key.startsWith(prefix)) return true;\n return false;\n}\n\nexport interface Hardcoded {\n file: string;\n line: number;\n column: number;\n text: string;\n /** Where it sits: `text`, or the attribute it is the value of. */\n where: string;\n}\n\ntype Node = { type: string; start: number; end: number; [field: string]: unknown };\n\n/**\n * Text that reads as code rather than prose: one token holding a path, a call, an identifier with an\n * underscore or a dotted name, as in `form.json()` or `src/index.ts`. Trailing punctuation doesn't\n * count, so `Done.` is still prose.\n */\nexport function looksLikeCode(text: string): boolean {\n const token = text.trim().replace(/[.!?:,…]+$/, '');\n return token !== '' && !/\\s/.test(token) && /[/()_=<>{}[\\].]|::/.test(token);\n}\n\nconst NON_PROSE_ELEMENTS = new Set(['code', 'pre', 'kbd', 'samp', 'script', 'style', 'svg', 'path']);\nconst LETTER = /\\p{L}/u;\n\n/** Offsets in `code` as lines and columns, and whether a line is marked `prosebook-ignore`, on it or the line above. */\nfunction lineIndex(code: string) {\n const lines = code.split('\\n');\n const lineStarts: number[] = [];\n let offset = 0;\n for (const line of lines) {\n lineStarts.push(offset);\n offset += line.length + 1;\n }\n const position = (at: number) => {\n let lo = 0;\n let hi = lineStarts.length - 1;\n while (lo < hi) {\n const mid = (lo + hi + 1) >> 1;\n if (lineStarts[mid]! <= at) lo = mid;\n else hi = mid - 1;\n }\n return { line: lo + 1, column: at - lineStarts[lo]! + 1 };\n };\n const ignored = (line: number) =>\n (lines[line - 1] ?? '').includes('prosebook-ignore') || (lines[line - 2] ?? '').includes('prosebook-ignore');\n return { position, ignored };\n}\n\n/** Literal text in JSX that a person reads, where it should be a catalog entry. */\nexport function hardcodedText(file: string, code: string, attributes: string[]): Hardcoded[] {\n const { program } = parseSync(file, code);\n const found: Hardcoded[] = [];\n const { position, ignored } = lineIndex(code);\n const report = (node: Node, text: string, where: string) => {\n const trimmed = text.replace(/\\s+/g, ' ').trim();\n if (!LETTER.test(trimmed) || looksLikeCode(trimmed)) return;\n const at = position(node.start + Math.max(0, code.slice(node.start, node.end).search(/\\S/)));\n if (ignored(at.line)) return;\n found.push({ file, ...at, text: trimmed, where });\n };\n\n const visit = (node: Node, inProse: boolean) => {\n let prose = inProse;\n if (node.type === 'JSXElement') {\n const opening = node.openingElement as Node & { name: Node & { name?: string } };\n if (opening.name.type === 'JSXIdentifier' && NON_PROSE_ELEMENTS.has(opening.name.name ?? '')) prose = false;\n }\n if (node.type === 'JSXText' && prose) report(node, node.value as string, 'text');\n if (node.type === 'JSXExpressionContainer' && prose) {\n const expr = node.expression as Node;\n if (expr.type === 'Literal' && typeof expr.value === 'string') report(expr, expr.value, 'text');\n if (expr.type === 'TemplateLiteral' && (expr.expressions as Node[]).length === 0)\n report(expr, String((expr.quasis as { value: { cooked: string } }[])[0]?.value.cooked), 'text');\n }\n if (node.type === 'JSXAttribute') {\n const name = node.name as Node & { name?: string };\n const value = node.value as Node | null;\n const attr = name.type === 'JSXIdentifier' ? (name.name ?? '') : '';\n if (attributes.includes(attr) && value !== null) {\n if (value.type === 'Literal' && typeof value.value === 'string') report(value, value.value, attr);\n if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression as Node;\n if (expr.type === 'Literal' && typeof expr.value === 'string') report(expr, expr.value, attr);\n }\n }\n return;\n }\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) if (isNode(child)) visit(child, prose);\n } else if (isNode(value)) {\n visit(value, prose);\n }\n }\n };\n visit(program as unknown as Node, true);\n return found;\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && typeof (value as Node).type === 'string';\n}\n\nexport interface ScanResult {\n hardcoded: Hardcoded[];\n refs: KeyRefs;\n files: number;\n}\n\nexport function scan(dirs: string[], extensions: string[], attributes: string[], root: string, skip: string[] = []): ScanResult {\n const refs: KeyRefs = { exact: new Set(), prefixes: new Set() };\n const hardcoded: Hardcoded[] = [];\n const files = sourceFiles(dirs, extensions, skip);\n for (const path of files) {\n const code = readFileSync(path, 'utf8');\n keyRefs(code, refs);\n if (/\\.[jt]sx$/.test(path)) hardcoded.push(...hardcodedText(relative(root, path).replaceAll('\\\\', '/'), code, attributes));\n }\n return { hardcoded, refs, files: files.length };\n}\n\n/** A copy's methods that take a key first. */\nexport const READERS = new Set(['t', 'rich']);\n\nexport interface UnseenRead {\n file: string;\n line: number;\n column: number;\n /** The read as written, on one line. */\n code: string;\n /** What it calls: a reader, the name it was imported by, or `Text`. */\n name: string;\n}\n\nexport interface Reads {\n /** Reads whose key a build can't see, in calls that may be to a copy's `t` or `rich`. */\n unseen: UnseenRead[];\n /** Names a copy's readers are bound to here, as in `const { rich: tRich } = copy`. */\n aliases: Set<string>;\n}\n\nconst PREFIX = /^[a-z0-9][\\w-]*(?:\\.[\\w-]+)*\\.$/;\nconst WRAPPERS = new Set(['ParenthesizedExpression', 'TSAsExpression', 'TSSatisfiesExpression', 'TSNonNullExpression', 'TSTypeAssertion']);\n\n/** Whether `keyRefs` finds every key an expression can be: a literal, or a template or sum after a key prefix. */\nfunction keySeen(node: Node | undefined): boolean {\n if (node === undefined) return false;\n if (WRAPPERS.has(node.type)) return keySeen(node.expression as Node);\n switch (node.type) {\n case 'Literal':\n return typeof node.value === 'string';\n case 'TemplateLiteral': {\n const [head] = node.quasis as { value: { cooked: string | null } }[];\n return (node.expressions as Node[]).length === 0 || PREFIX.test(head?.value.cooked ?? '');\n }\n case 'BinaryExpression': {\n const left = node.left as Node;\n return node.operator === '+' && left.type === 'Literal' && typeof left.value === 'string' && PREFIX.test(left.value);\n }\n case 'ConditionalExpression':\n return keySeen(node.consequent as Node) && keySeen(node.alternate as Node);\n case 'LogicalExpression':\n return keySeen(node.left as Node) && keySeen(node.right as Node);\n default:\n return false;\n }\n}\n\nconst nameOf = (node: Node | undefined): string | undefined =>\n node?.type === 'Identifier' || node?.type === 'JSXIdentifier'\n ? (node.name as string)\n : node?.type === 'Literal'\n ? String(node.value)\n : undefined;\n\n/**\n * The reads in a module whose key a pruned build can't see, as in `t(item.key)` or `<Text id={id} />`:\n * the build would leave the message out, and the key would show in its place. A build has no types,\n * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,\n * which may be bound to one elsewhere (`aliases`); the caller keeps those whose name turns out to be a\n * reader. `<Text>` counts where it is imported from `prosebook/react`. Lines marked `prosebook-ignore`\n * are skipped.\n */\nexport function unseenReads(file: string, code: string): Reads {\n const { program } = parseSync(file, code);\n const { position, ignored } = lineIndex(code);\n const unseen: UnseenRead[] = [];\n const aliases = new Set<string>();\n /** Local name to the name it was imported by. */\n const imported = new Map<string, string>();\n const texts = new Set<string>();\n for (const statement of (program as unknown as Node).body as Node[]) {\n if (statement.type !== 'ImportDeclaration') continue;\n for (const s of statement.specifiers as Node[]) {\n const name = s.type === 'ImportSpecifier' ? nameOf(s.imported as Node) : undefined;\n const local = nameOf(s.local as Node);\n if (name === undefined || local === undefined) continue;\n if (name === 'Text' && (statement.source as Node).value === 'prosebook/react') texts.add(local);\n else imported.set(local, name);\n }\n }\n const report = (node: Node, name: string) => {\n const at = position(node.start);\n if (ignored(at.line)) return;\n const text = code.slice(node.start, node.end).replace(/\\s+/g, ' ');\n unseen.push({ file, ...at, name, code: text.length > 80 ? `${text.slice(0, 77)}...` : text });\n };\n // Calls by a plain name, kept until every alias in the module is known.\n const calls: [Node, string][] = [];\n const visit = (node: Node) => {\n if (node.type === 'ObjectPattern') {\n for (const p of node.properties as Node[]) {\n if (p.type === 'Property' && READERS.has(nameOf(p.key as Node) ?? '') && (p.value as Node).type === 'Identifier')\n aliases.add(nameOf(p.value as Node)!);\n }\n }\n if (node.type === 'CallExpression' && !keySeen((node.arguments as Node[])[0])) {\n const callee = node.callee as Node;\n if (callee.type === 'Identifier') calls.push([node, callee.name as string]);\n else if (callee.type === 'MemberExpression' && !callee.computed && READERS.has(nameOf(callee.property as Node) ?? ''))\n report(node, nameOf(callee.property as Node)!);\n }\n if (node.type === 'JSXOpeningElement' && texts.has(nameOf(node.name as Node) ?? '')) {\n const id = (node.attributes as Node[]).find((a) => a.type === 'JSXAttribute' && nameOf(a.name as Node) === 'id');\n const value = id?.value as Node | null | undefined;\n if (value !== null && value !== undefined && !keySeen(value.type === 'JSXExpressionContainer' ? (value.expression as Node) : value))\n report(node, 'Text');\n }\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) if (isNode(child)) visit(child);\n } else if (isNode(value)) {\n visit(value);\n }\n }\n };\n visit(program as unknown as Node);\n for (const [node, local] of calls) {\n if (READERS.has(local) || aliases.has(local)) report(node, local);\n else if (imported.has(local)) report(node, imported.get(local)!);\n }\n unseen.sort((a, b) => (a.line === b.line ? a.column - b.column : a.line - b.line));\n return { unseen, aliases };\n}\n"],"mappings":"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"}
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-CF7HziDa.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,35 +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
- 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 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 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 };
351
370
  //# sourceMappingURL=tools.d.ts.map