prosebook 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +357 -26
  2. package/dist/bind-BIZNv8nU.js +62 -0
  3. package/dist/bind-BIZNv8nU.js.map +1 -0
  4. package/dist/bind-ZS86jHXk.d.ts +31 -0
  5. package/dist/bind-ZS86jHXk.d.ts.map +1 -0
  6. package/dist/bun.d.ts +12 -3
  7. package/dist/bun.d.ts.map +1 -1
  8. package/dist/bun.js +77 -26
  9. package/dist/bun.js.map +1 -1
  10. package/dist/{bundle-KD-DiiFa.d.ts → bundle-CLHY_97D.d.ts} +3 -3
  11. package/dist/bundle-CLHY_97D.d.ts.map +1 -0
  12. package/dist/bundle-Ut6Y2X6N.js +217 -0
  13. package/dist/bundle-Ut6Y2X6N.js.map +1 -0
  14. package/dist/cli.js +55 -12
  15. package/dist/cli.js.map +1 -1
  16. package/dist/{config-BPSmoYel.js → config-7T4hX_f7.js} +13 -5
  17. package/dist/{config-BPSmoYel.js.map → config-7T4hX_f7.js.map} +1 -1
  18. package/dist/{config-DdMkKt_N.d.ts → config-CzaJJQ60.d.ts} +19 -5
  19. package/dist/{config-DdMkKt_N.d.ts.map → config-CzaJJQ60.d.ts.map} +1 -1
  20. package/dist/config.d.ts +1 -1
  21. package/dist/config.js +1 -1
  22. package/dist/{copy-B3V_QRkT.d.ts → copy-BXn0aYQn.d.ts} +37 -3
  23. package/dist/copy-BXn0aYQn.d.ts.map +1 -0
  24. package/dist/copy-Bv6S-Hr2.js +235 -0
  25. package/dist/copy-Bv6S-Hr2.js.map +1 -0
  26. package/dist/endpoints-Dhgowovz.js +217 -0
  27. package/dist/endpoints-Dhgowovz.js.map +1 -0
  28. package/dist/generate-1BeQAd1O.d.ts +72 -0
  29. package/dist/generate-1BeQAd1O.d.ts.map +1 -0
  30. package/dist/{generate-9pR3i5Bj.js → generate-CKv86Iki.js} +1724 -627
  31. package/dist/generate-CKv86Iki.js.map +1 -0
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +2 -2
  34. package/dist/next-loader.d.ts +9 -2
  35. package/dist/next-loader.d.ts.map +1 -1
  36. package/dist/next-loader.js +12 -3
  37. package/dist/next-loader.js.map +1 -1
  38. package/dist/next.d.ts +11 -3
  39. package/dist/next.d.ts.map +1 -1
  40. package/dist/next.js +94 -21
  41. package/dist/next.js.map +1 -1
  42. package/dist/preact/index.d.ts +34 -0
  43. package/dist/preact/index.d.ts.map +1 -0
  44. package/dist/preact/index.js +72 -0
  45. package/dist/preact/index.js.map +1 -0
  46. package/dist/react/index.d.ts +5 -14
  47. package/dist/react/index.d.ts.map +1 -1
  48. package/dist/react/index.js +9 -33
  49. package/dist/react/index.js.map +1 -1
  50. package/dist/react/server.d.ts +1 -1
  51. package/dist/react/server.js +1 -1
  52. package/dist/{sarif-CWyTAQ0y.js → sarif-BnwYC78_.js} +8 -98
  53. package/dist/sarif-BnwYC78_.js.map +1 -0
  54. package/dist/{scan-Bx0_yDSi.js → scan-C0ap4Mpp.js} +145 -20
  55. package/dist/scan-C0ap4Mpp.js.map +1 -0
  56. package/dist/solid/index.d.ts +29 -0
  57. package/dist/solid/index.d.ts.map +1 -0
  58. package/dist/solid/index.js +48 -0
  59. package/dist/solid/index.js.map +1 -0
  60. package/dist/svelte/Text.svelte +13 -0
  61. package/dist/svelte/Text.svelte.d.ts +20 -0
  62. package/dist/svelte/index.d.ts +31 -0
  63. package/dist/svelte/index.d.ts.map +1 -0
  64. package/dist/svelte/index.js +35 -0
  65. package/dist/svelte/index.js.map +1 -0
  66. package/dist/tools.d.ts +44 -65
  67. package/dist/tools.d.ts.map +1 -1
  68. package/dist/tools.js +5 -5
  69. package/dist/vite.d.ts +13 -3
  70. package/dist/vite.d.ts.map +1 -1
  71. package/dist/vite.js +82 -25
  72. package/dist/vite.js.map +1 -1
  73. package/dist/vue/index.d.ts +43 -0
  74. package/dist/vue/index.d.ts.map +1 -0
  75. package/dist/vue/index.js +76 -0
  76. package/dist/vue/index.js.map +1 -0
  77. package/package.json +63 -7
  78. package/src/bun.ts +77 -20
  79. package/src/cli.ts +62 -8
  80. package/src/index.ts +1 -1
  81. package/src/next-loader.ts +17 -5
  82. package/src/next.ts +120 -16
  83. package/src/preact/index.ts +91 -0
  84. package/src/react/index.tsx +12 -42
  85. package/src/runtime/bind.ts +96 -0
  86. package/src/runtime/copy.ts +148 -24
  87. package/src/solid/index.ts +67 -0
  88. package/src/svelte/Text.svelte +13 -0
  89. package/src/svelte/Text.svelte.d.ts +20 -0
  90. package/src/svelte/index.ts +65 -0
  91. package/src/tools/bundle.ts +143 -2
  92. package/src/tools/catalog.ts +10 -1
  93. package/src/tools/chunks.ts +142 -0
  94. package/src/tools/config.ts +25 -7
  95. package/src/tools/entry.ts +19 -20
  96. package/src/tools/exchange.ts +275 -0
  97. package/src/tools/extract.ts +2 -2
  98. package/src/tools/generate.ts +7 -1
  99. package/src/tools/index.ts +2 -1
  100. package/src/tools/native/android.ts +186 -0
  101. package/src/tools/native/apple.ts +201 -0
  102. package/src/tools/native/date.ts +219 -0
  103. package/src/tools/native/index.ts +17 -5
  104. package/src/tools/native/kotlin.ts +47 -6
  105. package/src/tools/native/model.ts +67 -29
  106. package/src/tools/native/printf.ts +137 -0
  107. package/src/tools/native/rust.ts +86 -5
  108. package/src/tools/native/swift.ts +62 -6
  109. package/src/tools/scan.ts +146 -25
  110. package/src/tools/todo.ts +6 -1
  111. package/src/vite.ts +121 -28
  112. package/src/vue/index.ts +117 -0
  113. package/dist/bundle-DboItAre.js +0 -111
  114. package/dist/bundle-DboItAre.js.map +0 -1
  115. package/dist/bundle-KD-DiiFa.d.ts.map +0 -1
  116. package/dist/compile-xF3F4oIp.d.ts +0 -16
  117. package/dist/compile-xF3F4oIp.d.ts.map +0 -1
  118. package/dist/copy-B3V_QRkT.d.ts.map +0 -1
  119. package/dist/copy-Bo_FIoLy.js +0 -154
  120. package/dist/copy-Bo_FIoLy.js.map +0 -1
  121. package/dist/endpoints-CnUfCkIk.js +0 -96
  122. package/dist/endpoints-CnUfCkIk.js.map +0 -1
  123. package/dist/generate-9pR3i5Bj.js.map +0 -1
  124. package/dist/sarif-CWyTAQ0y.js.map +0 -1
  125. package/dist/scan-Bx0_yDSi.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-C0ap4Mpp.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/** Where `<Text>` comes from: each framework binding's. */\nconst TEXT_SOURCES = new Set(['prosebook/react', 'prosebook/preact', 'prosebook/solid', 'prosebook/vue', 'prosebook/svelte']);\n\n/** Single-file components, whose reads are in their scripts and their markup. */\nexport const SFC = /\\.(?:vue|svelte)$/;\n\n/** Code inside a file, parsed apart: a script block, or an expression in markup, at its offset. */\ninterface Piece {\n code: string;\n at: number;\n}\n\n/** A single-file component's script blocks, each at its offset. */\nexport const scriptBlocks = (code: string): Piece[] =>\n [...code.matchAll(/(<script\\b[^>]*>)([\\s\\S]*?)<\\/script>/g)].map((m) => ({ code: m[2]!, at: m.index + m[1]!.length }));\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 a prosebook binding. In a `.vue` or `.svelte` file,\n * its scripts and the expressions in its markup are read. Lines marked `prosebook-ignore` are skipped.\n */\nexport function unseenReads(file: string, code: string): Reads {\n if (!SFC.test(file)) return readsIn(file, code, file, [{ code, at: 0 }]);\n const vue = file.endsWith('.vue');\n const markup = blank(code, /<script\\b[\\s\\S]*?<\\/script>|<style\\b[\\s\\S]*?<\\/style>|<!--[\\s\\S]*?-->/g);\n const scripts = scriptBlocks(code);\n const expressions = vue ? vueExpressions(markup) : svelteExpressions(markup);\n // Scripts and markup alike may be TypeScript.\n return readsIn(file, code, 'component.ts', [...scripts, ...expressions], (names, report) => {\n for (const tag of markupTexts(markup, names, vue)) report(tag.at, tag.end, 'Text');\n });\n}\n\ntype Report = (start: number, end: number, name: string) => void;\n\nfunction readsIn(file: string, code: string, as: string, pieces: Piece[], markup?: (texts: Set<string>, report: Report) => void): Reads {\n const programs = pieces.map((piece) => ({ at: piece.at, program: parseSync(as, piece.code).program as unknown as Node }));\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 { program } of programs) {\n for (const statement of program.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' && TEXT_SOURCES.has(String((statement.source as Node).value))) texts.add(local);\n else imported.set(local, name);\n }\n }\n }\n const report: Report = (start, end, name) => {\n const at = position(start);\n if (ignored(at.line)) return;\n const text = code.slice(start, 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: [start: number, end: number, name: string][] = [];\n const visit = (node: Node, at: number) => {\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([at + node.start, at + node.end, callee.name as string]);\n else if (callee.type === 'MemberExpression' && !callee.computed && READERS.has(nameOf(callee.property as Node) ?? ''))\n report(at + node.start, at + node.end, 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(at + node.start, at + node.end, '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, at);\n } else if (isNode(value)) {\n visit(value, at);\n }\n }\n };\n for (const { program, at } of programs) visit(program, at);\n markup?.(texts, report);\n for (const [start, end, local] of calls) {\n if (READERS.has(local) || aliases.has(local)) report(start, end, local);\n else if (imported.has(local)) report(start, end, 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\n/** `code` with each match of `pattern` blanked to spaces, its lines kept, so offsets still point into the file. */\nconst blank = (code: string, pattern: RegExp) => code.replace(pattern, (m) => m.replace(/[^\\n]/g, ' '));\n\n/** A Vue template's expressions: `{{ ... }}`, and the values of bound attributes and directives. */\nfunction vueExpressions(markup: string): Piece[] {\n const pieces: Piece[] = [];\n for (const m of markup.matchAll(/\\{\\{([\\s\\S]*?)\\}\\}/g)) pieces.push({ code: m[1]!, at: m.index + 2 });\n for (const m of markup.matchAll(/\\s((?::|@|v-)[^\\s=>/\"']*)\\s*=\\s*([\"'])([\\s\\S]*?)\\2/g)) {\n // A loop's and a slot's values bind names; they read nothing.\n if (/^v-(?:for|slot)\\b/.test(m[1]!)) continue;\n pieces.push({ code: m[3]!, at: m.index + m[0].length - m[3]!.length - 1 });\n }\n return pieces;\n}\n\n/** Where the `}` closing the `{` at `start` is, past strings and nested braces; -1 if none does. */\nfunction closing(text: string, start: number): number {\n let depth = 0;\n for (let i = start; i < text.length; i++) {\n const c = text[i]!;\n if (c === '\"' || c === \"'\" || c === '`') {\n const end = text.indexOf(c, i + 1);\n if (end === -1) return -1;\n i = end;\n } else if (c === '{') {\n depth++;\n } else if (c === '}' && --depth === 0) {\n return i;\n }\n }\n return -1;\n}\n\n/** A Svelte template's expressions: each `{...}`, a block's or tag's past its keyword. */\nfunction svelteExpressions(markup: string): Piece[] {\n const pieces: Piece[] = [];\n for (let start = markup.indexOf('{'); start !== -1;) {\n const end = closing(markup, start);\n if (end === -1) break;\n const at = start + 1;\n const inner = markup.slice(at, end);\n const tag = /^\\s*([#:@/])(\\w*)(\\s+if\\b)?/.exec(inner);\n if (tag === null) {\n // A spread, `{...props}`, reads as the object it spreads.\n pieces.push({ code: inner.replace('...', ' '), at });\n } else {\n const [head, sigil, word, elseIf] = tag;\n // `{/if}`, `{:else}` and `{:then x}` hold no expression, and `{#snippet}` names parameters.\n const reads = sigil !== '/' && word !== 'snippet' && word !== 'then' && word !== 'catch' && (word !== 'else' || elseIf !== undefined);\n // `{@const x = ...}` declares; `{#each xs as x}` and `{#await p then x}` bind names after the expression.\n if (reads && word === 'const') pieces.push({ code: `var${inner.slice(head.length)}`, at: at + head.length - 3 });\n else if (reads) pieces.push({ code: inner.slice(head.length).split(/\\s+(?:as|then)\\s/)[0]!, at: at + head.length });\n }\n start = markup.indexOf('{', end + 1);\n }\n return pieces;\n}\n\n/**\n * Each `<Text>` in markup whose key the build can't see: Vue's `:id=\"...\"`, Svelte's `id={...}`,\n * `{id}` or `id=\"text {with} a placeholder\"`. An `id` written as plain text is a key `keyRefs` reads.\n */\nfunction markupTexts(markup: string, names: Set<string>, vue: boolean): { at: number; end: number }[] {\n if (names.size === 0) return [];\n const found: { at: number; end: number }[] = [];\n const tags = new RegExp(`<(?:${[...names].join('|')})(?=[\\\\s/>])((?:[^>\"']|\"[^\"]*\"|'[^']*')*)>`, 'g');\n for (const m of markup.matchAll(tags)) {\n const attributes = m[1]!;\n let id: string | undefined;\n if (vue) {\n id = /(?:^|\\s)(?::|v-bind:)id\\s*=\\s*([\"'])([\\s\\S]*?)\\1/.exec(attributes)?.[2];\n } else {\n const bound = /(?:^|\\s)id\\s*=\\s*\\{/.exec(attributes);\n if (bound !== null) {\n const open = bound.index + bound[0].length - 1;\n id = attributes.slice(open + 1, closing(attributes, open));\n } else if (/(?:^|\\s)\\{\\s*id\\s*\\}/.test(attributes) || /(?:^|\\s)id\\s*=\\s*([\"'])[^\"']*\\{/.test(attributes)) {\n id = 'id';\n }\n }\n if (id === undefined) continue;\n const [statement] = parseSync('id.ts', `(${id})`).program.body as unknown as Node[];\n if (!keySeen(statement?.type === 'ExpressionStatement' ? (statement.expression as Node) : undefined))\n found.push({ at: m.index, end: m.index + m[0].length });\n }\n return found;\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { r as TextProps$1, t as BoundCopy } from "../bind-ZS86jHXk.js";
2
+ import { T as TagFn, b as RegisteredLocale, n as Copy, x as RegisteredMessages } from "../copy-BXn0aYQn.js";
3
+ import { Accessor, JSX } from "solid-js";
4
+ type M = RegisteredMessages;
5
+ /** `Copy`, with `rich` returning a `JSX.Element`, whose reads are tracked. */
6
+ export type SolidCopy = BoundCopy<JSX.Element>;
7
+ type Tags = {
8
+ readonly [tag: string]: TagFn<JSX.Element>;
9
+ };
10
+ export interface CopyProviderProps {
11
+ /** Read once: give a provider a new key to change the copy under it. */
12
+ copy: Copy;
13
+ /** Tag renderers every rich read under this provider gets, beside the copy's own. */
14
+ tags?: Tags;
15
+ children?: JSX.Element;
16
+ }
17
+ export declare function CopyProvider(props: CopyProviderProps): JSX.Element;
18
+ /** The catalog, in the current locale. What reads it updates when the locale changes, or in dev the catalog. */
19
+ export declare function useCopy(): SolidCopy;
20
+ /** The current locale, as an accessor. */
21
+ export declare function useLocale(): Accessor<RegisteredLocale>;
22
+ export type TextProps<K extends Extract<keyof M, string>> = TextProps$1<K, JSX.Element>;
23
+ /**
24
+ * A message as an element: `<Text id="cart.items" values={{ count }} />`. Rich messages render their
25
+ * tags through the provider's renderers and any given in `values`.
26
+ */
27
+ export declare function Text<K extends Extract<keyof M, string>>(props: TextProps<K>): JSX.Element;
28
+ export {};
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":["Props"],"sources":["../../src/solid/index.ts"],"mappings":";;;KAaK,IAAI;;YAGG,YAAY,UAAU,IAAI;KAEjC;YAAmB,cAAc,MAAM,IAAI;;iBAI/B;;EAEf,MAAM;;EAEN,OAAO;EACP,WAAW,IAAI;;wBAGD,aAAa,OAAO,oBAAoB,IAAI;;wBAc5C,WAAW;;wBAOX,aAAa,SAAS;YAK1B,UAAU,UAAU,cAAc,cAAcA,YAAM,GAAG,IAAI;;;;;wBAMzD,KAAK,UAAU,cAAc,YAAY,OAAO,UAAU,KAAK,IAAI"}
@@ -0,0 +1,48 @@
1
+ import { t as bindCopy } from "../bind-BIZNv8nU.js";
2
+ import { createComponent, createContext, createMemo, createSignal, onCleanup, useContext } from "solid-js";
3
+ // src/solid/index.ts
4
+ /**
5
+ * `prosebook/solid`: the catalog in Solid components.
6
+ *
7
+ * Every read tracks a signal the copy sets on each change, so JSX that reads a message updates in
8
+ * place when the locale switches; components never run again. `rich` returns what its tag renderers
9
+ * made, beside the text, ready to put in JSX. Written without JSX, so it needs no Solid compiler.
10
+ */
11
+ const CopyContext = createContext();
12
+ function CopyProvider(props) {
13
+ const { copy, tags = {} } = props;
14
+ const [version, setVersion] = createSignal(copy.version);
15
+ onCleanup(copy.subscribe(() => setVersion(copy.version)));
16
+ const value = bindCopy(copy, tags, {
17
+ track: version,
18
+ whole: (parts) => parts
19
+ });
20
+ return createComponent(CopyContext.Provider, {
21
+ value,
22
+ get children() {
23
+ return props.children;
24
+ }
25
+ });
26
+ }
27
+ /** The catalog, in the current locale. What reads it updates when the locale changes, or in dev the catalog. */
28
+ function useCopy() {
29
+ const reader = useContext(CopyContext);
30
+ if (reader === void 0) throw new Error("prosebook: useCopy() needs a <CopyProvider> above it");
31
+ return reader;
32
+ }
33
+ /** The current locale, as an accessor. */
34
+ function useLocale() {
35
+ const reader = useCopy();
36
+ return () => reader.locale;
37
+ }
38
+ /**
39
+ * A message as an element: `<Text id="cart.items" values={{ count }} />`. Rich messages render their
40
+ * tags through the provider's renderers and any given in `values`.
41
+ */
42
+ function Text(props) {
43
+ const copy = useCopy();
44
+ return createMemo(() => copy.rich(props.id, props.values));
45
+ }
46
+ export { CopyProvider, Text, useCopy, useLocale };
47
+
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/solid/index.ts"],"sourcesContent":["/**\n * `prosebook/solid`: the catalog in Solid components.\n *\n * Every read tracks a signal the copy sets on each change, so JSX that reads a message updates in\n * place when the locale switches; components never run again. `rich` returns what its tag renderers\n * made, beside the text, ready to put in JSX. Written without JSX, so it needs no Solid compiler.\n */\nimport { createComponent, createContext, createMemo, createSignal, onCleanup, useContext, type Accessor, type JSX } from 'solid-js';\n\nimport { bindCopy, type BoundCopy, type TextProps as Props } from '#src/runtime/bind.ts';\nimport type { Copy } from '#src/runtime/copy.ts';\nimport type { RegisteredLocale, RegisteredMessages, TagFn } from '#src/runtime/types.ts';\n\ntype M = RegisteredMessages;\n\n/** `Copy`, with `rich` returning a `JSX.Element`, whose reads are tracked. */\nexport type SolidCopy = BoundCopy<JSX.Element>;\n\ntype Tags = { readonly [tag: string]: TagFn<JSX.Element> };\n\nconst CopyContext = createContext<SolidCopy>();\n\nexport interface CopyProviderProps {\n /** Read once: give a provider a new key to change the copy under it. */\n copy: Copy;\n /** Tag renderers every rich read under this provider gets, beside the copy's own. */\n tags?: Tags;\n children?: JSX.Element;\n}\n\nexport function CopyProvider(props: CopyProviderProps): JSX.Element {\n const { copy, tags = {} } = props;\n const [version, setVersion] = createSignal(copy.version);\n onCleanup(copy.subscribe(() => setVersion(copy.version)));\n const value = bindCopy<JSX.Element, JSX.Element>(copy, tags, { track: version, whole: (parts) => parts as JSX.Element });\n return createComponent(CopyContext.Provider, {\n value,\n get children() {\n return props.children;\n },\n });\n}\n\n/** The catalog, in the current locale. What reads it updates when the locale changes, or in dev the catalog. */\nexport function useCopy(): SolidCopy {\n const reader = useContext(CopyContext);\n if (reader === undefined) throw new Error('prosebook: useCopy() needs a <CopyProvider> above it');\n return reader;\n}\n\n/** The current locale, as an accessor. */\nexport function useLocale(): Accessor<RegisteredLocale> {\n const reader = useCopy();\n return () => reader.locale;\n}\n\nexport type TextProps<K extends Extract<keyof M, string>> = Props<K, JSX.Element>;\n\n/**\n * A message as an element: `<Text id=\"cart.items\" values={{ count }} />`. Rich messages render their\n * tags through the provider's renderers and any given in `values`.\n */\nexport function Text<K extends Extract<keyof M, string>>(props: TextProps<K>): JSX.Element {\n const copy = useCopy();\n // An accessor is something JSX renders, and runs again when the message or its values change.\n return createMemo(() => copy.rich(props.id as never, (props as { values?: object }).values as never)) as unknown as JSX.Element;\n}\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,cAAc,cAAyB;AAU7C,SAAgB,aAAa,OAAuC;CAClE,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM;CAC5B,MAAM,CAAC,SAAS,cAAc,aAAa,KAAK,OAAO;CACvD,UAAU,KAAK,gBAAgB,WAAW,KAAK,OAAO,CAAC,CAAC;CACxD,MAAM,QAAQ,SAAmC,MAAM,MAAM;EAAE,OAAO;EAAS,QAAQ,UAAU;CAAqB,CAAC;CACvH,OAAO,gBAAgB,YAAY,UAAU;EAC3C;EACA,IAAI,WAAW;GACb,OAAO,MAAM;EACf;CACF,CAAC;AACH;;AAGA,SAAgB,UAAqB;CACnC,MAAM,SAAS,WAAW,WAAW;CACrC,IAAI,WAAW,KAAA,GAAW,MAAM,IAAI,MAAM,sDAAsD;CAChG,OAAO;AACT;;AAGA,SAAgB,YAAwC;CACtD,MAAM,SAAS,QAAQ;CACvB,aAAa,OAAO;AACtB;;;;;AAQA,SAAgB,KAAyC,OAAkC;CACzF,MAAM,OAAO,QAAQ;CAErB,OAAO,iBAAiB,KAAK,KAAK,MAAM,IAAc,MAA8B,MAAe,CAAC;AACtG"}
@@ -0,0 +1,13 @@
1
+ <!--
2
+ A message, its tags rendered by snippets of the same name: `<Text id="cart.items" values={{ count }} />`.
3
+ Given `chunks` instead, as a tag's snippet is, renders what the tag wraps.
4
+ -->
5
+ <script lang="ts">
6
+ import { getCopy, type RichPart } from 'prosebook/svelte';
7
+
8
+ let { id, values, chunks, ...snippets }: { id?: string; values?: object; chunks?: RichPart[]; [tag: string]: unknown } = $props();
9
+ const copy = getCopy();
10
+ const parts = $derived(chunks ?? copy.rich(id as never, { ...snippets, ...values } as never));
11
+ </script>
12
+
13
+ {#each parts as part}{#if typeof part === 'string'}{part}{:else}{@render part.snippet(part.chunks)}{/if}{/each}
@@ -0,0 +1,20 @@
1
+ // Beside `Text.svelte` in src and in dist, so it names prosebook by package rather than by path.
2
+ import type { RegisteredMessages } from 'prosebook';
3
+ import type { RichPart, TagSnippet } from 'prosebook/svelte';
4
+ import type { Component } from 'svelte';
5
+
6
+ type M = RegisteredMessages;
7
+
8
+ /**
9
+ * A message's props: its key, `values` once it takes arguments, and a snippet per tag no default renders.
10
+ * Per key, so a key Svelte's tooling widens to the union still needs its own `values`.
11
+ */
12
+ export type TextProps<K extends Extract<keyof M, string>> = K extends unknown
13
+ ? { id: K } & ({} extends M[K]['args'] ? { values?: M[K]['args'] } : { values: M[K]['args'] }) & { [T in M[K]['tags']]?: TagSnippet }
14
+ : never;
15
+
16
+ /** A message as markup, or with `chunks`, what a tag wraps. */
17
+ declare const Text: <K extends Extract<keyof M, string>>(
18
+ ...args: Parameters<Component<TextProps<K> | { chunks: RichPart[] }>>
19
+ ) => ReturnType<Component<TextProps<K> | { chunks: RichPart[] }>>;
20
+ export default Text;
@@ -0,0 +1,31 @@
1
+ import { t as BoundCopy } from "../bind-ZS86jHXk.js";
2
+ import { S as RichKey, f as DefaultTags, g as MessageShape, n as Copy, x as RegisteredMessages } from "../copy-BXn0aYQn.js";
3
+ import { Snippet } from "svelte";
4
+ import Text from "./Text.svelte";
5
+ type M = RegisteredMessages;
6
+ /** What a rich read returns: text, and each tag as the snippet that renders it and what it wraps. */
7
+ export type RichPart = string | {
8
+ snippet: TagSnippet;
9
+ chunks: RichPart[];
10
+ };
11
+ /** Renders a tag: `{#snippet link(chunks)}<a href="/terms"><Text {chunks} /></a>{/snippet}`. */
12
+ export type TagSnippet = Snippet<[chunks: RichPart[]]>;
13
+ type Tags = {
14
+ readonly [tag: string]: TagSnippet;
15
+ };
16
+ /** The arguments of a rich read, and a snippet for each tag no default renders. */
17
+ export type RichSnippets<S extends MessageShape> = S['args'] & { [T in Exclude<S['tags'], DefaultTags>]: TagSnippet; } & { [T in Extract<S['tags'], DefaultTags>]?: TagSnippet; };
18
+ /** `Copy`, whose reads are reactive, with `rich` taking snippets and returning parts for `<Text>`. */
19
+ export interface SvelteCopy extends Omit<BoundCopy<never>, 'rich'> {
20
+ rich<K extends RichKey<M>>(this: void, key: K, ...values: {} extends RichSnippets<M[K]> ? [values?: RichSnippets<M[K]>] : [values: RichSnippets<M[K]>]): RichPart[];
21
+ }
22
+ export interface CopyOptions {
23
+ /** Snippets every rich read gets for these tags, beside the copy's own renderers. */
24
+ tags?: Tags;
25
+ }
26
+ /** Gives this component and those under it the copy, and returns it as `getCopy()` would. Call it while the component initialises. */
27
+ export declare function setCopy(copy: Copy, options?: CopyOptions): SvelteCopy;
28
+ /** The catalog, in the current locale. What reads it runs again when the locale changes, or in dev the catalog. */
29
+ export declare function getCopy(): SvelteCopy;
30
+ export { Text };
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/svelte/index.ts"],"mappings":";;;;KAgBK,IAAI;;YAGG;EAAsB,SAAS;EAAY,QAAQ;;;YAGnD,aAAa,SAAS,QAAQ;KAErC;YAAmB,cAAc;;;YAG1B,aAAa,UAAU,gBAAgB,eAAe,KAAK,QAAQ,WAAW,eAAe,mBACtG,KAAK,QAAQ,WAAW,gBAAgB;;iBAI1B,mBAAmB,KAAK;EACvC,KAAK,UAAU,QAAQ,IACrB,YACA,KAAK,MACF,mBAAmB,aAAa,EAAE,OAAO,SAAS,aAAa,EAAE,QAAQ,QAAQ,aAAa,EAAE,OAClG;;iBAGY;;EAEf,OAAO;;;wBAMO,QAAQ,MAAM,MAAM,UAAS,cAAmB;;wBAYhD,WAAW"}
@@ -0,0 +1,35 @@
1
+ import { t as bindCopy } from "../bind-BIZNv8nU.js";
2
+ import { getContext, setContext } from "svelte";
3
+ import { createSubscriber } from "svelte/reactivity";
4
+ import Text from "./Text.svelte";
5
+ // src/svelte/index.ts
6
+ /**
7
+ * `prosebook/svelte`: the catalog in Svelte 5 components.
8
+ *
9
+ * Every read goes through a subscriber the copy updates on each change, so markup, `$derived` and
10
+ * effects that read a message run again when the locale switches. Tags render through snippets:
11
+ * a rich read returns text and `{ snippet, chunks }` parts, which `<Text>` renders.
12
+ */
13
+ const KEY = Symbol("prosebook");
14
+ /** Gives this component and those under it the copy, and returns it as `getCopy()` would. Call it while the component initialises. */
15
+ function setCopy(copy, options = {}) {
16
+ const track = createSubscriber((update) => copy.subscribe(update));
17
+ const reader = bindCopy(copy, options.tags ?? {}, {
18
+ track,
19
+ tag: (snippet, chunks) => ({
20
+ snippet,
21
+ chunks
22
+ }),
23
+ whole: (parts) => parts
24
+ });
25
+ return setContext(KEY, reader);
26
+ }
27
+ /** The catalog, in the current locale. What reads it runs again when the locale changes, or in dev the catalog. */
28
+ function getCopy() {
29
+ const reader = getContext(KEY);
30
+ if (reader === void 0) throw new Error("prosebook: getCopy() needs setCopy(copy) in a component above it");
31
+ return reader;
32
+ }
33
+ export { Text, getCopy, setCopy };
34
+
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/svelte/index.ts"],"sourcesContent":["/**\n * `prosebook/svelte`: the catalog in Svelte 5 components.\n *\n * Every read goes through a subscriber the copy updates on each change, so markup, `$derived` and\n * effects that read a message run again when the locale switches. Tags render through snippets:\n * a rich read returns text and `{ snippet, chunks }` parts, which `<Text>` renders.\n */\nimport { getContext, setContext, type Snippet } from 'svelte';\nimport { createSubscriber } from 'svelte/reactivity';\n\nimport { bindCopy, type BoundCopy } from '#src/runtime/bind.ts';\nimport type { Copy } from '#src/runtime/copy.ts';\nimport type { DefaultTags, MessageShape, RegisteredMessages, RichKey } from '#src/runtime/types.ts';\n\nexport { default as Text } from './Text.svelte';\n\ntype M = RegisteredMessages;\n\n/** What a rich read returns: text, and each tag as the snippet that renders it and what it wraps. */\nexport type RichPart = string | { snippet: TagSnippet; chunks: RichPart[] };\n\n/** Renders a tag: `{#snippet link(chunks)}<a href=\"/terms\"><Text {chunks} /></a>{/snippet}`. */\nexport type TagSnippet = Snippet<[chunks: RichPart[]]>;\n\ntype Tags = { readonly [tag: string]: TagSnippet };\n\n/** The arguments of a rich read, and a snippet for each tag no default renders. */\nexport type RichSnippets<S extends MessageShape> = S['args'] & { [T in Exclude<S['tags'], DefaultTags>]: TagSnippet } & {\n [T in Extract<S['tags'], DefaultTags>]?: TagSnippet;\n};\n\n/** `Copy`, whose reads are reactive, with `rich` taking snippets and returning parts for `<Text>`. */\nexport interface SvelteCopy extends Omit<BoundCopy<never>, 'rich'> {\n rich<K extends RichKey<M>>(\n this: void,\n key: K,\n ...values: {} extends RichSnippets<M[K]> ? [values?: RichSnippets<M[K]>] : [values: RichSnippets<M[K]>]\n ): RichPart[];\n}\n\nexport interface CopyOptions {\n /** Snippets every rich read gets for these tags, beside the copy's own renderers. */\n tags?: Tags;\n}\n\nconst KEY = Symbol('prosebook');\n\n/** Gives this component and those under it the copy, and returns it as `getCopy()` would. Call it while the component initialises. */\nexport function setCopy(copy: Copy, options: CopyOptions = {}): SvelteCopy {\n const track = createSubscriber((update) => copy.subscribe(update));\n const reader = bindCopy<never, RichPart[]>(copy, options.tags ?? {}, {\n track,\n // A snippet renders only inside markup, so a tag's parts wait there for `<Text>`.\n tag: (snippet, chunks) => ({ snippet, chunks }),\n whole: (parts) => parts as RichPart[],\n });\n return setContext(KEY, reader as unknown as SvelteCopy);\n}\n\n/** The catalog, in the current locale. What reads it runs again when the locale changes, or in dev the catalog. */\nexport function getCopy(): SvelteCopy {\n const reader = getContext<SvelteCopy | undefined>(KEY);\n if (reader === undefined) throw new Error('prosebook: getCopy() needs setCopy(copy) in a component above it');\n return reader;\n}\n"],"mappings":";;;;;;;;;;;;AA6CA,MAAM,MAAM,OAAO,WAAW;;AAG9B,SAAgB,QAAQ,MAAY,UAAuB,CAAC,GAAe;CACzE,MAAM,QAAQ,kBAAkB,WAAW,KAAK,UAAU,MAAM,CAAC;CACjE,MAAM,SAAS,SAA4B,MAAM,QAAQ,QAAQ,CAAC,GAAG;EACnE;EAEA,MAAM,SAAS,YAAY;GAAE;GAAS;EAAO;EAC7C,QAAQ,UAAU;CACpB,CAAC;CACD,OAAO,WAAW,KAAK,MAA+B;AACxD;;AAGA,SAAgB,UAAsB;CACpC,MAAM,SAAS,WAAmC,GAAG;CACrD,IAAI,WAAW,KAAA,GAAW,MAAM,IAAI,MAAM,kEAAkE;CAC5G,OAAO;AACT"}
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,5 @@
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";
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-CzaJJQ60.js";
2
+ import { _ as Entry, a as localesOf, b as localeModule, c as Lock, d as emptyLock, f as hash, g as CompiledLocale, h as serializeLock, i as localeEntries, l as StaleTranslation, m as reconcile, n as catalogSchema, o as render, p as readLock, r as generate, s as writeFiles, t as Generated, u as changeOf, v as compileLocale, y as localeExpression } from "./generate-1BeQAd1O.js";
3
3
  export declare function sourceFiles(dirs: string[], extensions: string[], skip?: string[]): string[];
4
4
  interface KeyRefs {
5
5
  exact: Set<string>;
@@ -52,8 +52,8 @@ interface Reads {
52
52
  * the build would leave the message out, and the key would show in its place. A build has no types,
53
53
  * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,
54
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.
55
+ * reader. `<Text>` counts where it is imported from a prosebook binding. In a `.vue` or `.svelte` file,
56
+ * its scripts and the expressions in its markup are read. Lines marked `prosebook-ignore` are skipped.
57
57
  */
58
58
  export declare function unseenReads(file: string, code: string): Reads;
59
59
  interface Extraction {
@@ -195,68 +195,12 @@ interface Imported {
195
195
  problems: Problem[];
196
196
  }
197
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.
198
+ * Translations into `locale` from `prosebook todo`'s YAML filled in, by a person or a model, or from
199
+ * a translation tool's XLIFF or JSON (`readReturned`), each written to the file its locale and area
200
+ * put it in. An empty value is one left for later. An entry that would break the catalog is left out
201
+ * with its problem, so a reply with one bad entry still lands the others.
202
202
  */
203
203
  export declare function importTranslations(config: ResolvedConfig, locale: string, reply: string): Imported;
204
- interface Lock {
205
- version: 2;
206
- /** Source text by hash: every text some translation is anchored to. */
207
- sources: Record<string, string>;
208
- /** Per locale, per key: `<source hash>:<translation hash>`. */
209
- translations: Record<string, Record<string, string>>;
210
- }
211
- export declare function hash(text: string): string;
212
- export declare function emptyLock(): Lock;
213
- /** The lock at `path`. A version 1 lock, which held only the translations, reads with no source texts. */
214
- export declare function readLock(path: string): Lock;
215
- export declare function serializeLock(lock: Lock): string;
216
- interface StaleTranslation {
217
- /** The source the translation was made from, where the lock recorded it. */
218
- was: string | undefined;
219
- /** The source now. */
220
- now: string;
221
- /** The translation, made from `was`. */
222
- translation: string;
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;
226
- interface Reconciled {
227
- lock: Lock;
228
- /** Per locale, per key: the translations made from a source that has changed since. */
229
- stale: Map<string, Map<string, StaleTranslation>>;
230
- }
231
- /**
232
- * The lock brought up to date with the catalog, and what is stale. `accept` re-anchors the given
233
- * translations (per locale, keys, or `true` for all of them) to the current source.
234
- */
235
- export declare function reconcile(catalog: Catalog, previous: Lock, accept?: Map<string, Set<string> | true>): Reconciled;
236
- /** Every locale the app can switch to, in order: the source, the translations, the pseudo-locale. */
237
- export declare function localesOf(catalog: Catalog, config: ResolvedConfig): string[];
238
- /** Per locale, the messages its module holds. A translated locale holds only what it translates. */
239
- export declare function localeEntries(catalog: Catalog, config: ResolvedConfig, stale: Map<string, Map<string, StaleTranslation>>): Map<string, Entry[]>;
240
- /** The JSON Schema an editor reads to complete and explain catalog entries. */
241
- export declare function catalogSchema(config: ResolvedConfig): object;
242
- interface Generated {
243
- catalog: Catalog;
244
- /** Absolute path to content. */
245
- files: Map<string, string>;
246
- lock: Lock;
247
- stale: Map<string, Map<string, StaleTranslation>>;
248
- entries: Map<string, Entry[]>;
249
- }
250
- /** Everything `generate` writes, in memory. */
251
- export declare function render(config: ResolvedConfig, accept?: Map<string, Set<string> | true>): Generated;
252
- /** Writes what changed. With `check`, writes nothing and returns what would have changed. */
253
- export declare function writeFiles(files: Map<string, string>, check?: boolean): string[];
254
- export declare function generate(config: ResolvedConfig, options?: {
255
- check?: boolean;
256
- accept?: Map<string, Set<string> | true>;
257
- }): Generated & {
258
- changed: string[];
259
- };
260
204
  /**
261
205
  * A word diff, for showing a translator what changed in the source since they translated it. Words,
262
206
  * runs of whitespace and single punctuation marks are the units, so an ICU brace or a period changes
@@ -341,6 +285,41 @@ interface Todo {
341
285
  text: string;
342
286
  }
343
287
  export declare function todoOf(config: ResolvedConfig, catalog: Catalog, stale: ReadonlyMap<string, StaleTranslation>, locale: string, prompt?: boolean): Todo;
288
+ /** The entries a locale wants a translation of: those it lacks, and those gone stale. */
289
+ export declare function todoMessages(catalog: Catalog, stale: ReadonlyMap<string, StaleTranslation>, locale: string): Message[];
290
+ type ExchangeFormat = 'xliff' | 'json';
291
+ interface Exported {
292
+ /** How many entries the file holds. */
293
+ count: number;
294
+ text: string;
295
+ }
296
+ /**
297
+ * The entries `todo` would list for `locale`, or every one with `all`, as a file a translation tool
298
+ * reads. The source locale exports every entry, untranslated, for a tool to take as its source.
299
+ */
300
+ export declare function exportOf(config: ResolvedConfig, catalog: Catalog, stale: ReadonlyMap<string, StaleTranslation>, locale: string, format: ExchangeFormat, all?: boolean): Exported;
301
+ /** A translation as a file gives it back, before it is checked against the catalog. */
302
+ interface Returned {
303
+ key: string;
304
+ /** Null or empty is left for later. Anything but text or a number is refused. */
305
+ value: unknown;
306
+ /** Someone looked at it, so one given back unchanged still holds. A YAML or JSON reply always has. */
307
+ checked: boolean;
308
+ /** The source it was made from, where the file says. */
309
+ source?: string;
310
+ /** Why the file's entry can't be read, which leaves it out. */
311
+ problem?: string;
312
+ }
313
+ /**
314
+ * The translations in a file `import` was given: XLIFF 2.0 when it starts as XML, otherwise YAML,
315
+ * which reads JSON too. Text around fenced blocks is left aside, as a model's reply has it. A nested
316
+ * mapping, as some tools write JSON, joins its keys with dots.
317
+ */
318
+ export declare function readReturned(text: string, locale: string, source: string): {
319
+ returned: Returned[];
320
+ } | {
321
+ problem: string;
322
+ };
344
323
  interface SarifResult {
345
324
  /** A rule the report describes, or one of a lint plugin's. */
346
325
  rule: string;
@@ -366,5 +345,5 @@ export declare class SarifReport {
366
345
  add(result: SarifResult): void;
367
346
  toJSON(root: string, tool: SarifTool): object;
368
347
  }
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 };
348
+ 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 ExchangeFormat, type ExistenceRule, type Exported, 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 Returned, 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, catalogSchema, changeOf, compileLocale, compileRule, defineConfig, definePlugin, emptyLock, entryLocation, generate, hash, intlDate, intlNumber, lint, loadCatalog, loadConfig, localeEntries, localeExpression, localeModule, localesOf, parseMf2, phrase, previewOf, readLock, reconcile, render, resolveConfig, sentences, serializeLock, singularOf, toMf2, whereOf, words, writeFiles };
370
349
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","names":["Edit"],"sources":["../src/tools/scan.ts","../src/tools/extract.ts","../src/tools/entry.ts","../src/tools/lock.ts","../src/tools/generate.ts","../src/tools/diff.ts","../src/tools/pseudo.ts","../src/tools/mf1.ts","../src/tools/syntax.ts","../src/tools/migrate.ts","../src/tools/types.ts","../src/tools/todo.ts","../src/tools/sarif.ts"],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","names":["Edit"],"sources":["../src/tools/scan.ts","../src/tools/extract.ts","../src/tools/entry.ts","../src/tools/diff.ts","../src/tools/pseudo.ts","../src/tools/mf1.ts","../src/tools/syntax.ts","../src/tools/migrate.ts","../src/tools/types.ts","../src/tools/todo.ts","../src/tools/exchange.ts","../src/tools/sarif.ts"],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -1,5 +1,5 @@
1
- import { A as writeMessage, C as entryLocation, D as whereOf, E as previewOf, F as toMf1, M as intlNumber, N as parseMf2, O as SYNTAX_NAME, P as toMf2, S as KEY_PATTERN, T as loadCatalog, _ as applyFixes, a as render, b as readEntry, c as pseudoText, d as hash, f as readLock, g as wordDiff, h as formatDiff, i as localesOf, j as intlDate, k as readMessage, l as changeOf, m as serializeLock, n as generate, o as writeFiles, p as reconcile, r as localeEntries, s as pseudoAst, t as catalogSchema, u as emptyLock, v as editEntry, x as CatalogError, y as importTranslations } from "./generate-9pR3i5Bj.js";
2
- import { a as looksLikeCode, c as unseenReads, d as localeModule, i as keyRefs, l as compileLocale, n as hardcodedText, o as scan, r as isReferenced, s as sourceFiles, t as READERS, u as localeExpression } from "./scan-Bx0_yDSi.js";
3
- import { a as applyExtract, c as planExtract, d as typescriptOracle, i as planMigrate, l as slugOf, n as todoOf, o as areaOf, r as applyMigrate, s as draftPlurals, t as SarifReport, u as syntaxKind } from "./sarif-CWyTAQ0y.js";
4
- import { _ as words, a as DEFAULT_KINDS, c as CORE_RULES, d as compileRule, f as definePlugin, g as singularOf, h as sentences, i as resolveConfig, l as HOLE, m as phrase, n as defineConfig, o as DEFAULT_SURFACES, p as lint, r as loadConfig, s as STRICT_LISTS, u as applyEdits } from "./config-BPSmoYel.js";
5
- export { CORE_RULES, CatalogError, DEFAULT_KINDS, DEFAULT_SURFACES, HOLE, KEY_PATTERN, READERS, STRICT_LISTS, SYNTAX_NAME, SarifReport, applyEdits, applyExtract, applyFixes, applyMigrate, areaOf, catalogSchema, changeOf, compileLocale, compileRule, defineConfig, definePlugin, draftPlurals, editEntry, emptyLock, entryLocation, formatDiff, generate, hardcodedText, hash, importTranslations, intlDate, intlNumber, isReferenced, keyRefs, lint, loadCatalog, loadConfig, localeEntries, localeExpression, localeModule, localesOf, looksLikeCode, parseMf2, phrase, planExtract, planMigrate, previewOf, pseudoAst, pseudoText, readEntry, readLock, readMessage, reconcile, render, resolveConfig, scan, sentences, serializeLock, singularOf, slugOf, sourceFiles, syntaxKind, toMf1, toMf2, todoOf, typescriptOracle, unseenReads, whereOf, wordDiff, words, writeFiles, writeMessage };
1
+ import { A as loadCatalog, B as toMf1, C as formatDiff, E as KEY_PATTERN, F as writeMessage, I as intlDate, L as intlNumber, M as whereOf, N as SYNTAX_NAME, O as entryLocation, P as readMessage, R as parseMf2, S as serializeLock, T as CatalogError, _ as changeOf, a as render, b as readLock, c as pseudoText, d as importTranslations, f as readEntry, g as todoOf, h as todoMessages, i as localesOf, j as previewOf, l as applyFixes, m as readReturned, n as generate, o as writeFiles, p as exportOf, r as localeEntries, s as pseudoAst, t as catalogSchema, u as editEntry, v as emptyLock, w as wordDiff, x as reconcile, y as hash, z as toMf2 } from "./generate-CKv86Iki.js";
2
+ import { a as keyRefs, d as compileLocale, f as localeExpression, i as isReferenced, l as sourceFiles, o as looksLikeCode, p as localeModule, r as hardcodedText, s as scan, t as READERS, u as unseenReads } from "./scan-C0ap4Mpp.js";
3
+ import { a as areaOf, c as slugOf, i as applyExtract, l as syntaxKind, n as applyMigrate, o as draftPlurals, r as planMigrate, s as planExtract, t as SarifReport, u as typescriptOracle } from "./sarif-BnwYC78_.js";
4
+ import { _ as words, a as DEFAULT_KINDS, c as CORE_RULES, d as compileRule, f as definePlugin, g as singularOf, h as sentences, i as resolveConfig, l as HOLE, m as phrase, n as defineConfig, o as DEFAULT_SURFACES, p as lint, r as loadConfig, s as STRICT_LISTS, u as applyEdits } from "./config-7T4hX_f7.js";
5
+ export { CORE_RULES, CatalogError, DEFAULT_KINDS, DEFAULT_SURFACES, HOLE, KEY_PATTERN, READERS, STRICT_LISTS, SYNTAX_NAME, SarifReport, applyEdits, applyExtract, applyFixes, applyMigrate, areaOf, catalogSchema, changeOf, compileLocale, compileRule, defineConfig, definePlugin, draftPlurals, editEntry, emptyLock, entryLocation, exportOf, formatDiff, generate, hardcodedText, hash, importTranslations, intlDate, intlNumber, isReferenced, keyRefs, lint, loadCatalog, loadConfig, localeEntries, localeExpression, localeModule, localesOf, looksLikeCode, parseMf2, phrase, planExtract, planMigrate, previewOf, pseudoAst, pseudoText, readEntry, readLock, readMessage, readReturned, reconcile, render, resolveConfig, scan, sentences, serializeLock, singularOf, slugOf, sourceFiles, syntaxKind, toMf1, toMf2, todoMessages, todoOf, typescriptOracle, unseenReads, whereOf, wordDiff, words, writeFiles, writeMessage };
package/dist/vite.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { t as Prune } from "./bundle-KD-DiiFa.js";
2
- import { i as ProsebookConfig } from "./config-DdMkKt_N.js";
1
+ import { t as Prune } from "./bundle-CLHY_97D.js";
2
+ import { i as ProsebookConfig } from "./config-CzaJJQ60.js";
3
3
  import { Plugin } from "vite";
4
4
  export interface ProsebookPluginOptions {
5
5
  /** The config, inline. Without it, `prosebook.config.*` at `root` is read. */
@@ -15,8 +15,18 @@ export interface ProsebookPluginOptions {
15
15
  * pruned. `loose` prunes without that check; `false` ships every message.
16
16
  */
17
17
  prune?: Prune;
18
- /** Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. */
18
+ /**
19
+ * Keys a build always keeps, exactly or as `prefix.*`: for keys built at runtime from data. With
20
+ * `split`, their areas load with the catalog.
21
+ */
19
22
  keep?: string[];
23
+ /**
24
+ * Split each locale by area, so a lazy route loads only the messages it reads: a module that names
25
+ * an area's keys imports that area, which the bundler places beside it, and a lazy locale loads
26
+ * each area as it is needed. In the browser only: a server render loads whole locales, and its
27
+ * catalog lists each area's files, which a copy's `preload` hands on as the render reads them.
28
+ */
29
+ split?: boolean;
20
30
  /** In dev, Alt-click a string on the page to open its entry in the editor. On by default. */
21
31
  inspect?: boolean;
22
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;iBA8BiB;;EAEf,SAAS;;;;;EAKT;;;;;;EAMA,QAAQ;;EAER;;EAEA;;wBAcc,UAAU,UAAS,yBAA8B"}
1
+ {"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;iBA4DiB;;EAEf,SAAS;;;;;EAKT;;;;;;EAMA,QAAQ;;;;;EAKR;;;;;;;EAOA;;EAEA;;wBAsBc,UAAU,UAAS,yBAA8B"}