prosebook 0.2.0 → 0.4.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 (115) hide show
  1. package/README.md +345 -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-CpUIWb9j.d.ts +6 -0
  7. package/dist/bundle-CpUIWb9j.d.ts.map +1 -0
  8. package/dist/bundle-DboItAre.js +111 -0
  9. package/dist/bundle-DboItAre.js.map +1 -0
  10. package/dist/cli.js +51 -56
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compile-ClK9htKr.d.ts +16 -0
  13. package/dist/compile-ClK9htKr.d.ts.map +1 -0
  14. package/dist/{config-_4HfnJQu.d.ts → config-CzaJJQ60.d.ts} +75 -2
  15. package/dist/config-CzaJJQ60.d.ts.map +1 -0
  16. package/dist/{config-D--RITk2.js → config-nWZf9QmF.js} +42 -212
  17. package/dist/config-nWZf9QmF.js.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-BgG_5Vpm.js +96 -0
  25. package/dist/endpoints-BgG_5Vpm.js.map +1 -0
  26. package/dist/generate-BW3N8EGb.js +5488 -0
  27. package/dist/generate-BW3N8EGb.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-B5nrMqrT.js} +103 -87
  57. package/dist/sarif-B5nrMqrT.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 +57 -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/android.ts +201 -0
  94. package/src/tools/native/apple.ts +139 -0
  95. package/src/tools/native/emit.ts +87 -0
  96. package/src/tools/native/index.ts +48 -0
  97. package/src/tools/native/kotlin.ts +269 -0
  98. package/src/tools/native/model.ts +292 -0
  99. package/src/tools/native/number.ts +210 -0
  100. package/src/tools/native/plurals.ts +103 -0
  101. package/src/tools/native/printf.ts +95 -0
  102. package/src/tools/native/rust.ts +367 -0
  103. package/src/tools/native/swift.ts +326 -0
  104. package/src/tools/syntax.ts +3 -3
  105. package/src/tools/todo.ts +118 -0
  106. package/src/vite.ts +28 -114
  107. package/dist/config-D--RITk2.js.map +0 -1
  108. package/dist/config-_4HfnJQu.d.ts.map +0 -1
  109. package/dist/copy-DXnzXsJa.d.ts.map +0 -1
  110. package/dist/helpers-BPuPGjQP.js +0 -61
  111. package/dist/helpers-BPuPGjQP.js.map +0 -1
  112. package/dist/index.js.map +0 -1
  113. package/dist/sarif-CI8HOokm.js.map +0 -1
  114. package/dist/scan-Ch2BfIUD.js +0 -2561
  115. package/dist/scan-Ch2BfIUD.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":";;;;;;;;;;;;;;;;;;;AA6BA,IAAM,UAAN,MAAc;CACZ,yBAA0B,IAAI,IAAoB;;CAGlD,IAAI,SAAqC;EAEvC,IAAI,YAAY,KAAA,KAAa,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAAG,OAAO;EACvE,MAAM,OAAO,KAAK,UAAU,OAAO;EACnC,IAAI,OAAO,KAAK,OAAO,IAAI,IAAI;EAC/B,IAAI,SAAS,KAAA,GAAW,KAAK,OAAO,IAAI,MAAO,OAAO,IAAI,KAAK,OAAO,MAAO;EAC7E,OAAO,KAAK;CACd;CAEA,eAAyB;EACvB,OAAO,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,SAAS,KAAK,KAAK,KAAK,EAAE;CAC1E;AACF;AAEA,SAAS,OAAO,MAAsB;CACpC,OAAO,qBAAqB,KAAK,IAAI,IAAI,KAAK,SAAS,KAAK,KAAK,UAAU,IAAI,EAAE;AACnF;AAKA,SAAS,MAAM,UAAkC,SAAkB,OAAsB;CACvF,IAAI,SAAS,WAAW,GAAG,OAAO;CAClC,MAAM,QAAQ,SAAS,KAAK,OAAO,KAAK,IAAI,SAAS,KAAK,CAAC;CAC3D,OAAO,MAAM,WAAW,IAAI,MAAM,KAAM,IAAI,MAAM,KAAK,IAAI,EAAE;AAC/D;AAEA,SAAS,KAAK,SAA4D,SAAkB,OAAsB;CAEhH,OAAO,KADM,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,GAAG,KAAK,UAAU,GAAG,EAAE,UAAU,MAAM,OAAO,OAAO,SAAS,KAAK,GAChH,CAAC,CAAC,KAAK,IAAI,EAAE;AAC9B;;AAGA,SAAS,WAAW,SAA6B,MAA8B,SAA0B;CACvG,MAAM,QAAQ,OAAO,QAAQ,QAAQ,CAAC,CAAC;CACvC,MAAM,QAAQ,QAAQ,IAAI,OAAO;CACjC,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,OAAO,GAAG,SAAS,cAAc,MAAM,MAAM,KAAK,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,IAAI,OAAO,GAAG,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;AAClH;;AAGA,SAAS,KAAK,OAAuB;CACnC,OAAO,KAAK,UAAU,KAAK,CAAC,CAAC,QAAQ,qBAAqB,MAAM,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG;AACtG;;;;;AAMA,SAAS,OAAO,IAA0B,MAAc,OAAuB;CAC7E,MAAM,EAAE,UAAU,YAAY,cAAc,IAAI,IAAI;CACpD,MAAM,QAAQ,GAAG,MAAM,UAAU,KAAK,QAAQ,EAAE;CAChD,OAAO,YAAY,KAAA,IAAY,QAAQ,IAAI,KAAK,QAAQ,EAAE,EAAE,IAAI,MAAM,IAAI,KAAK,QAAQ,EAAE,EAAE;AAC7F;AAEA,SAAS,KAAK,IAA0B,SAAkB,OAAsB;CAC9E,MAAM,aAAa,QAAgB,MAAc,YAC/C,OAAO,IAAI,MAAM,KAAK,OAAO,GAAG,OAAO,IAAI,IAAI,WAAW,SAAS,aAAa,EAAE,GAAG,OAAO,EAAE,EAAE;CAClG,IAAI,gBAAgB,EAAE,GAAG,OAAO,UAAU,KAAK,GAAG,OAAO,cAAc,EAAE,CAAC;CAC1E,IAAI,cAAc,EAAE,KAAK,cAAc,EAAE,GAAG,OAAO,UAAU,KAAK,GAAG,OAAO,YAAY,EAAE,CAAC;CAC3F,QAAQ,GAAG,MAAX;EACE,KAAKA,OAAK,SACR,OAAO,KAAK,UAAU,GAAG,KAAK;EAChC,KAAKA,OAAK,UACR,OAAO,OAAO,IAAI,GAAG,OAAO,OAAO,GAAG,KAAK,CAAC;EAC9C,KAAKA,OAAK;GAER,IAAI,UAAU,MAAM,OAAO;GAC3B,OAAO,OAAO,IAAI,MAAM,MAAM,OAAO,OAAO,MAAM,IAAI,IAAI,MAAM,WAAW,IAAI,MAAM,MAAM,WAAW,GAAG,EAAE;EAC7G,KAAKA,OAAK,QACR,OAAO,OAAO,OAAO,GAAG,KAAK,EAAE,IAAI,KAAK,GAAG,SAAS,SAAS,KAAK,EAAE;EACtE,KAAKA,OAAK,QAAQ;GAEhB,MAAM,YAAY,YAAY,EAAE;GAChC,MAAM,QAAQ,OAAO,GAAG,KAAK;GAC7B,MAAM,KAAK,WAAW,UAAU,UAAU,cAAc,MAAM,KAAK,WAAW,UAAU,YAAY,GAAG,MAAM,UAAU;GACvH,MAAM,QAAQ,cAAc,KAAA,IAAY,KAAK,WAAW,UAAU,SAAS,UAAU,MAAM,OAAO;GAClG,MAAM,MAAM;IAAE,MAAM,GAAG;IAAO,QAAQ,GAAG;GAAO;GAChD,OAAO,OAAO,GAAG,IAAI,GAAG,OAAO,IAAI,GAAG,eAAe,UAAU,IAAI,KAAK,GAAG,SAAS,SAAS,GAAG,IAAI,MAAM;EAC5G;EACA,KAAKA,OAAK,KACR,OAAO,UAAU,KAAK,UAAU,GAAG,KAAK,EAAE,IAAI,MAAM,GAAG,UAAU,SAAS,KAAK,EAAE;EACnF,SACE,MAAM,IAAI,MAAM,+CAAgD,GAA4B,MAAM;CACtG;AACF;AAEA,SAAS,QAAQ,KAA6B,SAA0B;CACtE,MAAM,UAAU,cAAc,GAAG;CACjC,IAAI,YAAY,KAAA,GAAW,OAAO,KAAK,UAAU,OAAO;CACxD,OAAO,aAAa,MAAM,KAAK,SAAS,IAAI;AAC9C;;AAUA,SAAgB,cAAc,SAA0B,MAAiD;CACvG,MAAM,UAAU,IAAI,QAAQ;CAC5B,MAAM,OAAiB,CAAC;CACxB,KAAK,MAAM,CAAC,KAAK,QAAQ,SAAS;EAChC,IAAI,QAAQ,CAAC,KAAK,GAAG,GAAG;EACxB,KAAK,KAAK,KAAK,KAAK,UAAU,GAAG,EAAE,IAAI,QAAQ,KAAK,OAAO,EAAE,EAAE;CACjE;CACA,OAAO;EAAE,cAAc,QAAQ,aAAa;EAAG;CAAK;AACtD;;AAGA,SAAgB,aAAa,UAA0B,QAAgB,IAAqB;CAC1F,MAAM,OAAO,KAAK,GAAG,OAAO,0DAA0D,GAAG,OAAO;CAEhG,MAAM,eAAe,KAAK,SAAS,aAAa,KAAK,MAAM,EAAE,QAAQ,MAAM,YAAY,CAAC,IAAI,SAAS;CACrG,MAAM,SAAS,aAAa,SAAS,IAAI,KAAK,aAAa,KAAK,IAAI,EAAE,MAAM;CAC5E,MAAM,QAAQ,KAAK,kCAAkC;CACrD,OAAO,GAAG,OAAO,OAAO,sBAAsB,SAAS,KAAK,KAAK,IAAI,EAAE,IAAI,MAAM;AACnF;;AAGA,SAAgB,iBAAiB,UAAkC;CACjE,OAAO,aAAa,SAAS,aAAa,KAAK,IAAI,EAAE,cAAc,SAAS,KAAK,KAAK,IAAI,EAAE;AAC9F;;;;;;;;;;AChJA,MAAM,4BAAY,IAAI,IAAI;CAAC;CAAgB;CAAQ;CAAS;CAAO;CAAY;AAAM,CAAC;AAEtF,SAAgB,YAAY,MAAgB,YAAsB,OAAiB,CAAC,GAAa;CAC/F,MAAM,QAAkB,CAAC;CACzB,MAAM,SAAS,QAAgB;EAC7B,IAAI;EACJ,IAAI;GACF,QAAQ,YAAY,GAAG;EACzB,QAAQ;GACN;EACF;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,OAAO,KAAK,KAAK,IAAI;GAC3B,IAAI,KAAK,MAAM,MAAM,SAAS,KAAK,KAAK,WAAW,GAAG,EAAE,EAAE,KAAK,KAAK,WAAW,GAAG,EAAE,GAAG,CAAC,GAAG;GAC3F,IAAI,SAAS,IAAI,CAAC,CAAC,YAAY,GACzB;QAAA,CAAC,UAAU,IAAI,IAAI,KAAK,CAAC,KAAK,WAAW,GAAG,GAAG,MAAM,IAAI;GAAA,OACxD,IAAI,WAAW,SAAS,QAAQ,IAAI,CAAC,KAAK,CAAC,KAAK,SAAS,OAAO,GACrE,MAAM,KAAK,IAAI;EAEnB;CACF;CACA,KAAK,MAAM,OAAO,MAAM,MAAM,GAAG;CACjC,OAAO,MAAM,KAAK;AACpB;AAQA,MAAM,cAAc;AACpB,MAAM,aAAa;AAEnB,SAAgB,QAAQ,MAAc,OAAgB;CAAE,uBAAO,IAAI,IAAI;CAAG,0BAAU,IAAI,IAAI;AAAE,GAAY;CACxG,KAAK,MAAM,KAAK,KAAK,SAAS,WAAW,GAAG,KAAK,MAAM,IAAI,EAAE,EAAG;CAChE,KAAK,MAAM,KAAK,KAAK,SAAS,UAAU,GAAG,KAAK,SAAS,IAAI,EAAE,EAAG;CAClE,OAAO;AACT;AAEA,SAAgB,aAAa,KAAa,MAAwB;CAChE,IAAI,KAAK,MAAM,IAAI,GAAG,GAAG,OAAO;CAChC,KAAK,MAAM,UAAU,KAAK,UAAU,IAAI,IAAI,WAAW,MAAM,GAAG,OAAO;CACvE,OAAO;AACT;;;;;;AAkBA,SAAgB,cAAc,MAAuB;CACnD,MAAM,QAAQ,KAAK,KAAK,CAAC,CAAC,QAAQ,cAAc,EAAE;CAClD,OAAO,UAAU,MAAM,CAAC,KAAK,KAAK,KAAK,KAAK,qBAAqB,KAAK,KAAK;AAC7E;AAEA,MAAM,qCAAqB,IAAI,IAAI;CAAC;CAAQ;CAAO;CAAO;CAAQ;CAAU;CAAS;CAAO;AAAM,CAAC;AACnG,MAAM,SAAS;;AAGf,SAAS,UAAU,MAAc;CAC/B,MAAM,QAAQ,KAAK,MAAM,IAAI;CAC7B,MAAM,aAAuB,CAAC;CAC9B,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,OAAO;EACxB,WAAW,KAAK,MAAM;EACtB,UAAU,KAAK,SAAS;CAC1B;CACA,MAAM,YAAY,OAAe;EAC/B,IAAI,KAAK;EACT,IAAI,KAAK,WAAW,SAAS;EAC7B,OAAO,KAAK,IAAI;GACd,MAAM,MAAO,KAAK,KAAK,KAAM;GAC7B,IAAI,WAAW,QAAS,IAAI,KAAK;QAC5B,KAAK,MAAM;EAClB;EACA,OAAO;GAAE,MAAM,KAAK;GAAG,QAAQ,KAAK,WAAW,MAAO;EAAE;CAC1D;CACA,MAAM,WAAW,UACd,MAAM,OAAO,MAAM,GAAA,CAAI,SAAS,kBAAkB,MAAM,MAAM,OAAO,MAAM,GAAA,CAAI,SAAS,kBAAkB;CAC7G,OAAO;EAAE;EAAU;CAAQ;AAC7B;;AAGA,SAAgB,cAAc,MAAc,MAAc,YAAmC;CAC3F,MAAM,EAAE,YAAY,UAAU,MAAM,IAAI;CACxC,MAAM,QAAqB,CAAC;CAC5B,MAAM,EAAE,UAAU,YAAY,UAAU,IAAI;CAC5C,MAAM,UAAU,MAAY,MAAc,UAAkB;EAC1D,MAAM,UAAU,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;EAC/C,IAAI,CAAC,OAAO,KAAK,OAAO,KAAK,cAAc,OAAO,GAAG;EACrD,MAAM,KAAK,SAAS,KAAK,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;EAC3F,IAAI,QAAQ,GAAG,IAAI,GAAG;EACtB,MAAM,KAAK;GAAE;GAAM,GAAG;GAAI,MAAM;GAAS;EAAM,CAAC;CAClD;CAEA,MAAM,SAAS,MAAY,YAAqB;EAC9C,IAAI,QAAQ;EACZ,IAAI,KAAK,SAAS,cAAc;GAC9B,MAAM,UAAU,KAAK;GACrB,IAAI,QAAQ,KAAK,SAAS,mBAAmB,mBAAmB,IAAI,QAAQ,KAAK,QAAQ,EAAE,GAAG,QAAQ;EACxG;EACA,IAAI,KAAK,SAAS,aAAa,OAAO,OAAO,MAAM,KAAK,OAAiB,MAAM;EAC/E,IAAI,KAAK,SAAS,4BAA4B,OAAO;GACnD,MAAM,OAAO,KAAK;GAClB,IAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU,OAAO,MAAM,KAAK,OAAO,MAAM;GAC9F,IAAI,KAAK,SAAS,qBAAsB,KAAK,YAAuB,WAAW,GAC7E,OAAO,MAAM,OAAQ,KAAK,OAA2C,EAAE,EAAE,MAAM,MAAM,GAAG,MAAM;EAClG;EACA,IAAI,KAAK,SAAS,gBAAgB;GAChC,MAAM,OAAO,KAAK;GAClB,MAAM,QAAQ,KAAK;GACnB,MAAM,OAAO,KAAK,SAAS,kBAAmB,KAAK,QAAQ,KAAM;GACjE,IAAI,WAAW,SAAS,IAAI,KAAK,UAAU,MAAM;IAC/C,IAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UAAU,OAAO,OAAO,MAAM,OAAO,IAAI;IAChG,IAAI,MAAM,SAAS,0BAA0B;KAC3C,MAAM,OAAO,MAAM;KACnB,IAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,UAAU,OAAO,MAAM,KAAK,OAAO,IAAI;IAC9F;GACF;GACA;EACF;EACA,KAAK,MAAM,SAAS,OAAO,OAAO,IAAI,GACpC,IAAI,MAAM,QAAQ,KAAK,GAChB;QAAA,MAAM,SAAS,OAAO,IAAI,OAAO,KAAK,GAAG,MAAM,OAAO,KAAK;EAAA,OAC3D,IAAI,OAAO,KAAK,GACrB,MAAM,OAAO,KAAK;CAGxB;CACA,MAAM,SAA4B,IAAI;CACtC,OAAO;AACT;AAEA,SAAS,OAAO,OAA+B;CAC7C,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAQ,MAAe,SAAS;AACxF;AAQA,SAAgB,KAAK,MAAgB,YAAsB,YAAsB,MAAc,OAAiB,CAAC,GAAe;CAC9H,MAAM,OAAgB;EAAE,uBAAO,IAAI,IAAI;EAAG,0BAAU,IAAI,IAAI;CAAE;CAC9D,MAAM,YAAyB,CAAC;CAChC,MAAM,QAAQ,YAAY,MAAM,YAAY,IAAI;CAChD,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,QAAQ,MAAM,IAAI;EAClB,IAAI,YAAY,KAAK,IAAI,GAAG,UAAU,KAAK,GAAG,cAAc,SAAS,MAAM,IAAI,CAAC,CAAC,WAAW,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC;CAC3H;CACA,OAAO;EAAE;EAAW;EAAM,OAAO,MAAM;CAAO;AAChD;;AAGA,MAAa,0BAAU,IAAI,IAAI,CAAC,KAAK,MAAM,CAAC;AAmB5C,MAAM,SAAS;AACf,MAAM,2BAAW,IAAI,IAAI;CAAC;CAA2B;CAAkB;CAAyB;CAAuB;AAAiB,CAAC;;AAGzI,SAAS,QAAQ,MAAiC;CAChD,IAAI,SAAS,KAAA,GAAW,OAAO;CAC/B,IAAI,SAAS,IAAI,KAAK,IAAI,GAAG,OAAO,QAAQ,KAAK,UAAkB;CACnE,QAAQ,KAAK,MAAb;EACE,KAAK,WACH,OAAO,OAAO,KAAK,UAAU;EAC/B,KAAK,mBAAmB;GACtB,MAAM,CAAC,QAAQ,KAAK;GACpB,OAAQ,KAAK,YAAuB,WAAW,KAAK,OAAO,KAAK,MAAM,MAAM,UAAU,EAAE;EAC1F;EACA,KAAK,oBAAoB;GACvB,MAAM,OAAO,KAAK;GAClB,OAAO,KAAK,aAAa,OAAO,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,KAAK,KAAK;EACrH;EACA,KAAK,yBACH,OAAO,QAAQ,KAAK,UAAkB,KAAK,QAAQ,KAAK,SAAiB;EAC3E,KAAK,qBACH,OAAO,QAAQ,KAAK,IAAY,KAAK,QAAQ,KAAK,KAAa;EACjE,SACE,OAAO;CACX;AACF;AAEA,MAAM,UAAU,SACd,MAAM,SAAS,gBAAgB,MAAM,SAAS,kBACzC,KAAK,OACN,MAAM,SAAS,YACb,OAAO,KAAK,KAAK,IACjB,KAAA;;;;;;;;;AAUR,SAAgB,YAAY,MAAc,MAAqB;CAC7D,MAAM,EAAE,YAAY,UAAU,MAAM,IAAI;CACxC,MAAM,EAAE,UAAU,YAAY,UAAU,IAAI;CAC5C,MAAM,SAAuB,CAAC;CAC9B,MAAM,0BAAU,IAAI,IAAY;;CAEhC,MAAM,2BAAW,IAAI,IAAoB;CACzC,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,aAAc,QAA4B,MAAgB;EACnE,IAAI,UAAU,SAAS,qBAAqB;EAC5C,KAAK,MAAM,KAAK,UAAU,YAAsB;GAC9C,MAAM,OAAO,EAAE,SAAS,oBAAoB,OAAO,EAAE,QAAgB,IAAI,KAAA;GACzE,MAAM,QAAQ,OAAO,EAAE,KAAa;GACpC,IAAI,SAAS,KAAA,KAAa,UAAU,KAAA,GAAW;GAC/C,IAAI,SAAS,UAAW,UAAU,OAAgB,UAAU,mBAAmB,MAAM,IAAI,KAAK;QACzF,SAAS,IAAI,OAAO,IAAI;EAC/B;CACF;CACA,MAAM,UAAU,MAAY,SAAiB;EAC3C,MAAM,KAAK,SAAS,KAAK,KAAK;EAC9B,IAAI,QAAQ,GAAG,IAAI,GAAG;EACtB,MAAM,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC,CAAC,QAAQ,QAAQ,GAAG;EACjE,OAAO,KAAK;GAAE;GAAM,GAAG;GAAI;GAAM,MAAM,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,OAAO;EAAK,CAAC;CAC9F;CAEA,MAAM,QAA0B,CAAC;CACjC,MAAM,SAAS,SAAe;EAC5B,IAAI,KAAK,SAAS,iBACX;QAAA,MAAM,KAAK,KAAK,YACnB,IAAI,EAAE,SAAS,cAAc,QAAQ,IAAI,OAAO,EAAE,GAAW,KAAK,EAAE,KAAM,EAAE,MAAe,SAAS,cAClG,QAAQ,IAAI,OAAO,EAAE,KAAa,CAAE;EAAA;EAG1C,IAAI,KAAK,SAAS,oBAAoB,CAAC,QAAS,KAAK,UAAqB,EAAE,GAAG;GAC7E,MAAM,SAAS,KAAK;GACpB,IAAI,OAAO,SAAS,cAAc,MAAM,KAAK,CAAC,MAAM,OAAO,IAAc,CAAC;QACrE,IAAI,OAAO,SAAS,sBAAsB,CAAC,OAAO,YAAY,QAAQ,IAAI,OAAO,OAAO,QAAgB,KAAK,EAAE,GAClH,OAAO,MAAM,OAAO,OAAO,QAAgB,CAAE;EACjD;EACA,IAAI,KAAK,SAAS,uBAAuB,MAAM,IAAI,OAAO,KAAK,IAAY,KAAK,EAAE,GAAG;GAEnF,MAAM,QADM,KAAK,WAAsB,MAAM,MAAM,EAAE,SAAS,kBAAkB,OAAO,EAAE,IAAY,MAAM,IAC5F,CAAC,EAAE;GAClB,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,CAAC,QAAQ,MAAM,SAAS,2BAA4B,MAAM,aAAsB,KAAK,GAChI,OAAO,MAAM,MAAM;EACvB;EACA,KAAK,MAAM,SAAS,OAAO,OAAO,IAAI,GACpC,IAAI,MAAM,QAAQ,KAAK,GAChB;QAAA,MAAM,SAAS,OAAO,IAAI,OAAO,KAAK,GAAG,MAAM,KAAK;EAAA,OACpD,IAAI,OAAO,KAAK,GACrB,MAAM,KAAK;CAGjB;CACA,MAAM,OAA0B;CAChC,KAAK,MAAM,CAAC,MAAM,UAAU,OAC1B,IAAI,QAAQ,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,GAAG,OAAO,MAAM,KAAK;MAC3D,IAAI,SAAS,IAAI,KAAK,GAAG,OAAO,MAAM,SAAS,IAAI,KAAK,CAAE;CAEjE,OAAO,MAAM,GAAG,MAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,IAAK;CACjF,OAAO;EAAE;EAAQ;CAAQ;AAC3B"}