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
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/react/index.tsx"],"sourcesContent":["/**\n * `prosebook/react`: the catalog in components.\n *\n * `useCopy()` re-renders its component when the locale changes, and its `rich` returns a ready\n * `ReactNode`: elements are keyed and tag functions receive keyed children, so a sentence with a\n * link inside it is one catalog entry and one call, with no wrapper to remember.\n */\nimport { createContext, Fragment, isValidElement, use, useMemo, useSyncExternalStore, type ReactNode } from 'react';\n\nimport type { Copy } from '#src/runtime/copy.ts';\nimport type { MessageShape, RegisteredLocale, RegisteredMessages, RichKey, RichParam, TagFn } from '#src/runtime/types.ts';\n\ntype M = RegisteredMessages;\ntype L = RegisteredLocale;\n\n/** `Copy`, with `rich` returning a `ReactNode`. */\nexport interface ReactCopy extends Omit<Copy, 'rich'> {\n rich<K extends RichKey<M>>(this: void, key: K, ...values: RichParam<M[K], ReactNode>): ReactNode;\n}\n\nconst CopyContext = createContext<{ copy: Copy; tags: Tags } | null>(null);\n\ntype Tags = { readonly [tag: string]: TagFn<ReactNode> };\n\nexport interface CopyProviderProps {\n copy: Copy;\n /** Tag renderers every rich read under this provider gets, beside the copy's own. */\n tags?: Tags;\n children?: ReactNode;\n}\n\nexport function CopyProvider({ copy, tags, children }: CopyProviderProps) {\n const value = useMemo(() => ({ copy: copy, tags: { ...(copy.tags as unknown as Tags), ...tags } }), [copy, tags]);\n return <CopyContext value={value}>{children}</CopyContext>;\n}\n\n/** The catalog, in the current locale. Re-renders the component when the locale changes, or in dev the catalog. */\nexport function useCopy(): ReactCopy {\n const context = use(CopyContext);\n if (context === null) throw new Error('prosebook: useCopy() needs a <CopyProvider> above it');\n const { copy, tags } = context;\n const version = useSyncExternalStore(\n copy.subscribe,\n () => copy.version,\n () => copy.version,\n );\n return useMemo(() => readerFor(copy, tags, version), [copy, tags, version]);\n}\n\n/** The current locale alone, for components that only format or switch. */\nexport function useLocale(): L {\n const context = use(CopyContext);\n if (context === null) throw new Error('prosebook: useLocale() needs a <CopyProvider> above it');\n return useSyncExternalStore(\n context.copy.subscribe,\n () => context.copy.locale,\n () => context.copy.locale,\n );\n}\n\n// Takes the version only so the reader is rebuilt, and its identity changes, when what it reads does.\nfunction readerFor(copy: Copy, tags: Tags, version: number): ReactCopy {\n return {\n locale: copy.locale,\n version,\n locales: copy.locales,\n source: copy.source,\n tags: copy.tags,\n ready: copy.ready,\n t: copy.t,\n has: copy.has,\n setLocale: copy.setLocale,\n subscribe: copy.subscribe,\n number: copy.number,\n date: copy.date,\n list: copy.list,\n relative: copy.relative,\n rich(key, ...values) {\n const given = (values[0] ?? {}) as Record<string, unknown>;\n const merged: Record<string, unknown> = { ...tags, ...given };\n for (const [name, value] of Object.entries(merged))\n if (typeof value === 'function') merged[name] = (chunks: unknown[]) => (value as TagFn<ReactNode>)(keyed(chunks));\n\n return <>{keyed(copy.rich(key as never, merged as never))}</>;\n },\n };\n}\n\n/** Text joined where it can be, and each element under its own key, so React never warns about a list. */\nfunction keyed(parts: unknown[]): (string | ReactNode)[] {\n if (parts.every((p) => typeof p === 'string')) return [parts.join('')];\n return parts.map((p, i) =>\n // oxlint-disable-next-line react/no-array-index-key -- the parts of one message never reorder, so positions are stable keys\n isValidElement(p) || typeof p === 'object' ? <Fragment key={i}>{p as ReactNode}</Fragment> : (p as ReactNode),\n );\n}\n\ntype ValuesProp<S extends MessageShape> = {} extends S['args'] & { [T in S['tags']]: unknown }\n ? { values?: S['args'] & { [T in S['tags']]?: TagFn<ReactNode> } }\n : { values: RichParam<S, ReactNode>[0] };\n\nexport type TextProps<K extends Extract<keyof M, string>> = { id: K } & ValuesProp<M[K]>;\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>) {\n const copy = useCopy();\n const values = (props as { values?: object }).values;\n return <>{copy.rich(props.id, values as never)}</>;\n}\n"],"mappings":";;;;;;;;;;AAoBA,MAAM,cAAc,cAAiD,IAAI;AAWzE,SAAgB,aAAa,EAAE,MAAM,MAAM,YAA+B;CACxE,MAAM,QAAQ,eAAe;EAAQ;EAAM,MAAM;GAAE,GAAI,KAAK;GAA0B,GAAG;EAAK;CAAE,IAAI,CAAC,MAAM,IAAI,CAAC;CAChH,OAAO,oBAAC,aAAD;EAAoB;EAAQ;CAAsB,CAAA;AAC3D;;AAGA,SAAgB,UAAqB;CACnC,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,sDAAsD;CAC5F,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,UAAU,qBACd,KAAK,iBACC,KAAK,eACL,KAAK,OACb;CACA,OAAO,cAAc,UAAU,MAAM,MAAM,OAAO,GAAG;EAAC;EAAM;EAAM;CAAO,CAAC;AAC5E;;AAGA,SAAgB,YAAe;CAC7B,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,wDAAwD;CAC9F,OAAO,qBACL,QAAQ,KAAK,iBACP,QAAQ,KAAK,cACb,QAAQ,KAAK,MACrB;AACF;AAGA,SAAS,UAAU,MAAY,MAAY,SAA4B;CACrE,OAAO;EACL,QAAQ,KAAK;EACb;EACA,SAAS,KAAK;EACd,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,GAAG,KAAK;EACR,KAAK,KAAK;EACV,WAAW,KAAK;EAChB,WAAW,KAAK;EAChB,QAAQ,KAAK;EACb,MAAM,KAAK;EACX,MAAM,KAAK;EACX,UAAU,KAAK;EACf,KAAK,KAAK,GAAG,QAAQ;GACnB,MAAM,QAAS,OAAO,MAAM,CAAC;GAC7B,MAAM,SAAkC;IAAE,GAAG;IAAM,GAAG;GAAM;GAC5D,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,GAC/C,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,WAAuB,MAA2B,MAAM,MAAM,CAAC;GAElH,OAAO,oBAAA,YAAA,EAAA,UAAG,MAAM,KAAK,KAAK,KAAc,MAAe,CAAC,EAAI,CAAA;EAC9D;CACF;AACF;;AAGA,SAAS,MAAM,OAA0C;CACvD,IAAI,MAAM,OAAO,MAAM,OAAO,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC;CACrE,OAAO,MAAM,KAAK,GAAG,MAEnB,eAAe,CAAC,KAAK,OAAO,MAAM,WAAW,oBAAC,UAAD,EAAA,UAAmB,EAAyB,GAA7B,CAA6B,IAAK,CAChG;AACF;;;;;AAYA,SAAgB,KAAyC,OAAqB;CAC5E,MAAM,OAAO,QAAQ;CACrB,MAAM,SAAU,MAA8B;CAC9C,OAAO,oBAAA,YAAA,EAAA,UAAG,KAAK,KAAK,MAAM,IAAI,MAAe,EAAI,CAAA;AACnD"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/react/index.tsx"],"sourcesContent":["/**\n * `prosebook/react`: the catalog in components.\n *\n * `useCopy()` re-renders its component when the locale changes, and its `rich` returns a ready\n * `ReactNode`: elements are keyed and tag functions receive keyed children, so a sentence with a\n * link inside it is one catalog entry and one call, with no wrapper to remember.\n */\nimport { createContext, Fragment, isValidElement, use, useMemo, useSyncExternalStore, type ReactNode } from 'react';\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 `ReactNode`. */\nexport type ReactCopy = BoundCopy<ReactNode>;\n\nconst CopyContext = createContext<{ copy: Copy; tags: Tags } | null>(null);\n\ntype Tags = { readonly [tag: string]: TagFn<ReactNode> };\n\nexport interface CopyProviderProps {\n copy: Copy;\n /** Tag renderers every rich read under this provider gets, beside the copy's own. */\n tags?: Tags;\n children?: ReactNode;\n}\n\nexport function CopyProvider({ copy, tags = {}, children }: CopyProviderProps) {\n const value = useMemo(() => ({ copy, tags }), [copy, tags]);\n return <CopyContext value={value}>{children}</CopyContext>;\n}\n\n/** The catalog, in the current locale. Re-renders the component when the locale changes, or in dev the catalog. */\nexport function useCopy(): ReactCopy {\n const context = use(CopyContext);\n if (context === null) throw new Error('prosebook: useCopy() needs a <CopyProvider> above it');\n const { copy, tags } = context;\n const version = useSyncExternalStore(\n copy.subscribe,\n () => copy.version,\n () => copy.version,\n );\n // Rebuilt on each version, so its identity changes when what it reads does.\n return useMemo(() => bindCopy(copy, tags, { tag, whole }), [copy, tags, version]);\n}\n\n/** The current locale alone, for components that only format or switch. */\nexport function useLocale(): RegisteredLocale {\n const context = use(CopyContext);\n if (context === null) throw new Error('prosebook: useLocale() needs a <CopyProvider> above it');\n return useSyncExternalStore(\n context.copy.subscribe,\n () => context.copy.locale,\n () => context.copy.locale,\n );\n}\n\nconst tag = (render: (chunks: never) => unknown, chunks: unknown[]) => render(keyed(chunks) as never);\nconst whole = (parts: unknown[]): ReactNode => <>{keyed(parts)}</>;\n\n/** Text joined where it can be, and each element under its own key, so React never warns about a list. */\nfunction keyed(parts: unknown[]): (string | ReactNode)[] {\n if (parts.every((p) => typeof p === 'string')) return [parts.join('')];\n return parts.map((p, i) =>\n // oxlint-disable-next-line react/no-array-index-key -- the parts of one message never reorder, so positions are stable keys\n isValidElement(p) || typeof p === 'object' ? <Fragment key={i}>{p as ReactNode}</Fragment> : (p as ReactNode),\n );\n}\n\nexport type TextProps<K extends Extract<keyof M, string>> = Props<K, ReactNode>;\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>) {\n const copy = useCopy();\n const values = (props as { values?: object }).values;\n return <>{copy.rich(props.id as never, values as never)}</>;\n}\n"],"mappings":";;;;;;;;;;;AAkBA,MAAM,cAAc,cAAiD,IAAI;AAWzE,SAAgB,aAAa,EAAE,MAAM,OAAO,CAAC,GAAG,YAA+B;CAC7E,MAAM,QAAQ,eAAe;EAAE;EAAM;CAAK,IAAI,CAAC,MAAM,IAAI,CAAC;CAC1D,OAAO,oBAAC,aAAD;EAAoB;EAAQ;CAAsB,CAAA;AAC3D;;AAGA,SAAgB,UAAqB;CACnC,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,sDAAsD;CAC5F,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,UAAU,qBACd,KAAK,iBACC,KAAK,eACL,KAAK,OACb;CAEA,OAAO,cAAc,SAAS,MAAM,MAAM;EAAE;EAAK;CAAM,CAAC,GAAG;EAAC;EAAM;EAAM;CAAO,CAAC;AAClF;;AAGA,SAAgB,YAA8B;CAC5C,MAAM,UAAU,IAAI,WAAW;CAC/B,IAAI,YAAY,MAAM,MAAM,IAAI,MAAM,wDAAwD;CAC9F,OAAO,qBACL,QAAQ,KAAK,iBACP,QAAQ,KAAK,cACb,QAAQ,KAAK,MACrB;AACF;AAEA,MAAM,OAAO,QAAoC,WAAsB,OAAO,MAAM,MAAM,CAAU;AACpG,MAAM,SAAS,UAAgC,oBAAA,YAAA,EAAA,UAAG,MAAM,KAAK,EAAI,CAAA;;AAGjE,SAAS,MAAM,OAA0C;CACvD,IAAI,MAAM,OAAO,MAAM,OAAO,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC;CACrE,OAAO,MAAM,KAAK,GAAG,MAEnB,eAAe,CAAC,KAAK,OAAO,MAAM,WAAW,oBAAC,UAAD,EAAA,UAAmB,EAAyB,GAA7B,CAA6B,IAAK,CAChG;AACF;;;;;AAQA,SAAgB,KAAyC,OAAqB;CAC5E,MAAM,OAAO,QAAQ;CACrB,MAAM,SAAU,MAA8B;CAC9C,OAAO,oBAAA,YAAA,EAAA,UAAG,KAAK,KAAK,MAAM,IAAa,MAAe,EAAI,CAAA;AAC5D"}
@@ -1,4 +1,4 @@
1
- import { g as MessageShapes, n as Copy, r as CopyOptions, t as Catalog } from "../copy-B3V_QRkT.js";
1
+ import { _ as MessageShapes, n as Copy, r as CopyOptions, t as Catalog } from "../copy-BXn0aYQn.js";
2
2
  /** What a request asks for: a locale, or the locales it prefers, most preferred first. */
3
3
  type Asked<L extends string> = L | (string & {}) | readonly string[];
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as createCopy } from "../copy-Bo_FIoLy.js";
1
+ import { n as createCopy } from "../copy-Bv6S-Hr2.js";
2
2
  import { cache } from "react";
3
3
  // src/react/server.ts
4
4
  /**
@@ -1,11 +1,11 @@
1
1
  import { n as flatten, t as createHelpers } from "./helpers-BWGcD0kS.js";
2
- import { c as parseMessage, f as tagsOf, n as argsOf, p as walk, t as TYPE } from "./icu-Dej0mNsF.js";
3
- import { a as looksLikeCode, l as compileLocale, u as localeExpression } from "./scan-Bx0_yDSi.js";
4
- import { A as writeMessage, O as SYNTAX_NAME, S as KEY_PATTERN, T as loadCatalog, k as readMessage, l as changeOf, x as CatalogError } from "./generate-9pR3i5Bj.js";
5
- import { g as singularOf } from "./config-BPSmoYel.js";
2
+ import { c as parseMessage, f as tagsOf, n as argsOf } from "./icu-Dej0mNsF.js";
3
+ import { d as compileLocale, f as localeExpression, o as looksLikeCode } from "./scan-C0ap4Mpp.js";
4
+ import { A as loadCatalog, D as SCHEMA_LINE, E as KEY_PATTERN, F as writeMessage, P as readMessage, T as CatalogError } from "./generate-CKv86Iki.js";
5
+ import { g as singularOf } from "./config-7T4hX_f7.js";
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
7
  import { basename, dirname, extname, join, relative, resolve } from "node:path";
7
8
  import { pathToFileURL } from "node:url";
8
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
9
  import { parseSync } from "oxc-parser";
10
10
  import { Scalar, isMap, parseDocument, stringify } from "yaml";
11
11
  // src/tools/types.ts
@@ -745,7 +745,7 @@ function applyExtract(config, plan) {
745
745
  })}`;
746
746
  });
747
747
  mkdirSync(config.catalog, { recursive: true });
748
- const current = existsSync(path) ? readFileSync(path, "utf8") : "# yaml-language-server: $schema=./catalog.schema.json\n";
748
+ const current = existsSync(path) ? readFileSync(path, "utf8") : `${SCHEMA_LINE}\n`;
749
749
  writeFileSync(path, `${current.replace(/\n*$/, "\n")}\n${blocks.join("\n")}`);
750
750
  written.push(path);
751
751
  }
@@ -894,96 +894,6 @@ function firstDifference(a, b, locale) {
894
894
  };
895
895
  }
896
896
  }
897
- // src/tools/todo.ts
898
- /**
899
- * `prosebook todo`: a locale's missing and stale entries, each with what a translator needs above it,
900
- * as the YAML to fill in. `prosebook import` reads it back filled.
901
- *
902
- * With `prompt`, it leads with what a model must be told to reply in that form. prosebook writes the
903
- * prompt and checks the reply; which model answers it, and how, is left to whoever runs it.
904
- */
905
- function todoOf(config, catalog, stale, locale, prompt = false) {
906
- const wanted = catalog.messages.filter((m) => !m.translations.has(locale) || stale.has(m.key));
907
- if (wanted.length === 0) return {
908
- count: 0,
909
- text: `# ${locale} is complete`
910
- };
911
- const byArea = /* @__PURE__ */ new Map();
912
- for (const m of wanted) {
913
- const lines = byArea.get(m.area) ?? [];
914
- lines.push("");
915
- if (m.context) lines.push(`# ${m.context}`);
916
- const what = [m.kind && `kind: ${m.kind}`, m.surface && `surface: ${m.surface}`].filter(Boolean).join(", ");
917
- if (what) lines.push(`# ${what}`);
918
- for (const [arg, said] of m.argDocs) lines.push(`# {${arg}}: ${said}`);
919
- lines.push(`# ${config.source}: ${m.text.replaceAll("\n", " ")}`);
920
- const change = stale.get(m.key);
921
- if (change !== void 0) lines.push(`# stale, the source changed: ${changeOf(change)}`);
922
- lines.push(stringify({ [m.key]: m.translations.get(locale)?.text ?? "" }, { lineWidth: 0 }).trimEnd());
923
- byArea.set(m.area, lines);
924
- }
925
- const entries = [...byArea].map(([area, lines]) => `\n# ── ${area}.yaml ──\n${lines.join("\n")}`).join("\n");
926
- const count = wanted.length;
927
- if (prompt) return {
928
- count,
929
- text: `${promptFor(config, wanted, locale, wanted.some((m) => stale.has(m.key)))}\n${entries.trimStart()}\n\`\`\``
930
- };
931
- return {
932
- count,
933
- text: `${`# prosebook todo: ${count} entr${count === 1 ? "y" : "ies"} for ${locale}. Fill them in, then \`prosebook import ${locale} <this file>\`.`}\n${entries}`
934
- };
935
- }
936
- function promptFor(config, wanted, locale, anyStale) {
937
- const mf2 = config.messageFormat === 2;
938
- const [target, source] = [nameOf(locale), nameOf(config.source)];
939
- const plurals = {
940
- cardinal: false,
941
- ordinal: false
942
- };
943
- for (const m of wanted) walk(m.ast, (el) => {
944
- if (el.type === TYPE.plural) plurals[el.pluralType === "ordinal" ? "ordinal" : "cardinal"] = true;
945
- });
946
- const categories = (type) => {
947
- const forms = new Intl.PluralRules(locale, { type }).resolvedOptions().pluralCategories;
948
- return forms.length === 1 ? `only the form ${forms[0]}` : `the forms ${new Intl.ListFormat("en").format(forms)}`;
949
- };
950
- const exact = mf2 ? "`0`" : "`=0`";
951
- const rules = [
952
- mf2 ? `Each message is in MessageFormat 2. Keep every {$placeholder}, every markup tag ({#b}…{/b}) around the same words, and every .input, .local and .match line; translate the text, not the names.` : `Each message is in ICU MessageFormat. Keep every {placeholder}, and every <tag> around the same words; translate the text inside plural and select arms, not the arm or argument names.`,
953
- plurals.cardinal && `A plural in ${target} takes ${categories("cardinal")}: give each its own ${mf2 ? "variant" : "arm"}, and keep exact ones such as ${exact} where the ${source} has them.`,
954
- plurals.ordinal && `An ordinal in ${target} takes ${categories("ordinal")}.`,
955
- "The comments above an entry say where it appears, what kind of string it is, and what each placeholder holds. Hold each to its kind, as described below: a button stays as short as the source.",
956
- anyStale && "An entry marked stale has a translation made from an earlier source. The diff shows what changed in the source ({+added+}, [-removed-]): change the translation only as much as that needs, and give it back unchanged if it still holds.",
957
- "Quote a value in single quotes when it starts with { or holds \": \", as YAML needs, and double any single quote inside it."
958
- ].filter((r) => typeof r === "string");
959
- const kinds = new Set(wanted.flatMap((m) => m.kind === void 0 ? [] : [m.kind]));
960
- const surfaces = new Set(wanted.flatMap((m) => m.surface === void 0 ? [] : [m.surface]));
961
- const legend = [
962
- ...kinds.size > 0 ? ["", "Kinds of string:"] : [],
963
- ...[...kinds].map((k) => `- ${k}: ${config.kinds[k]?.doc ?? ""}`),
964
- ...surfaces.size > 0 ? ["", "Surfaces:"] : [],
965
- ...[...surfaces].map((s) => `- ${s}: ${config.surfaces[s] ?? ""}`)
966
- ];
967
- return [
968
- `Translate the entries below from ${source} (${config.source}) into ${target} (${locale}). They are the text of an app's interface.`,
969
- "",
970
- "Reply with one YAML code block holding every key below, each with its translation as the value, and nothing else in the block.",
971
- "",
972
- ...rules.map((r) => `- ${r}`),
973
- ...legend,
974
- "",
975
- `The entries, in ${SYNTAX_NAME[config.messageFormat]}:`,
976
- "",
977
- "```yaml"
978
- ].join("\n");
979
- }
980
- function nameOf(locale) {
981
- try {
982
- return new Intl.DisplayNames(["en"], { type: "language" }).of(locale) ?? locale;
983
- } catch {
984
- return locale;
985
- }
986
- }
987
897
  // src/tools/sarif.ts
988
898
  /**
989
899
  * Findings as SARIF 2.1.0, the static-analysis format code scanning reads (GitHub's among them), so
@@ -1044,6 +954,6 @@ function location(file, line, column) {
1044
954
  } }
1045
955
  } };
1046
956
  }
1047
- export { applyExtract as a, planExtract as c, typescriptOracle as d, planMigrate as i, slugOf as l, todoOf as n, areaOf as o, applyMigrate as r, draftPlurals as s, SarifReport as t, syntaxKind as u };
957
+ export { areaOf as a, slugOf as c, applyExtract as i, syntaxKind as l, applyMigrate as n, draftPlurals as o, planMigrate as r, planExtract as s, SarifReport as t, typescriptOracle as u };
1048
958
 
1049
- //# sourceMappingURL=sarif-CWyTAQ0y.js.map
959
+ //# sourceMappingURL=sarif-BnwYC78_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif-BnwYC78_.js","names":[],"sources":["../src/tools/types.ts","../src/tools/extract.ts","../src/tools/migrate.ts","../src/tools/sarif.ts"],"sourcesContent":["/**\n * What kind of value an expression in JSX holds, so `extract` can write `{n, number}` for a number and\n * `{d, date, medium}` for a date instead of a bare `{x}`, which is text and prints a number unformatted.\n *\n * The project's own TypeScript (7 or later, through its `unstable/async` API) answers where it can; the\n * syntax of the expression answers where it can't: a JavaScript project, a file outside any tsconfig,\n * or no TypeScript installed. Not the `unstable/sync` API: on Linux and macOS it reads the compiler's\n * pipes through a Node internal (`stdout._handle.fd`) that Bun doesn't have, so under Bun it throws.\n */\nimport { existsSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\n\nimport type * as TsAsync from 'typescript/unstable/async';\nimport type { Checker, Type } from 'typescript/unstable/async';\n\n// oxlint-disable-next-line typescript/no-explicit-any -- a loose ESTree node: the walk reads whichever fields each node type has\ntype Node = { type: string; start: number; end: number; [field: string]: any };\n\ntype TsNode = { pos: number; end: number; forEachChild(visit: (child: TsNode) => unknown): unknown };\n\n/** `node` is markup or an object, which a message can't hold as text. */\nexport type ValueKind = 'string' | 'number' | 'date' | 'node' | 'unknown';\n\nexport interface Span {\n start: number;\n end: number;\n}\n\nexport interface TypeOracle {\n /** Where the answers come from, for the report. */\n readonly source: string;\n /** The kind of each span's expression in `file`, or nothing where the oracle can't say. */\n kinds(file: string, spans: readonly Span[]): Promise<(ValueKind | undefined)[]>;\n close(): Promise<void>;\n}\n\n/** The nearest `tsconfig.json` at or above the file's folder. */\nfunction tsconfigFor(file: string): string | undefined {\n for (let dir = dirname(resolve(file)); ; dir = dirname(dir)) {\n const candidate = join(dir, 'tsconfig.json');\n if (existsSync(candidate)) return candidate;\n if (dirname(dir) === dir) return undefined;\n }\n}\n\n// TypeFlags, as the TypeScript 7 API numbers them.\nconst ANY_OR_UNKNOWN = 3;\nconst NUMBER_LIKE = 67648 | 4224;\nconst STRING_LIKE = 12583968;\nconst UNION = 134217728;\nconst OBJECT = 1048576;\n\n/**\n * An oracle over the projects the files belong to, or nothing where TypeScript 7 isn't installed or\n * none of the files has a tsconfig. One compiler process serves every project and is closed by `close`.\n */\nexport async function typescriptOracle(root: string, files: readonly string[]): Promise<TypeOracle | undefined> {\n const configs = [...new Set(files.map(tsconfigFor).filter((c) => c !== undefined))];\n if (configs.length === 0) return undefined;\n let ts: typeof TsAsync;\n try {\n ts = await import('typescript/unstable/async');\n } catch {\n return undefined;\n }\n let api: InstanceType<typeof ts.API>;\n let snapshot: Awaited<ReturnType<InstanceType<typeof ts.API>['updateSnapshot']>>;\n try {\n api = new ts.API({ cwd: root });\n snapshot = await api.updateSnapshot({ openProjects: configs });\n } catch {\n return undefined;\n }\n\n const classify = async (checker: Checker, type: Type | undefined): Promise<ValueKind | undefined> => {\n if (type === undefined) return undefined;\n const t = (await checker.getNonNullableType(type)) ?? type;\n if ((t.flags & ANY_OR_UNKNOWN) !== 0) return 'unknown';\n if ((t.flags & UNION) !== 0) {\n const members = (await (t as Type & { getTypes(): Promise<readonly Type[] | undefined> }).getTypes()) ?? [];\n const kinds = new Set(await Promise.all(members.map((m) => classify(checker, m))));\n if (kinds.size === 1) return [...kinds][0];\n return kinds.has('node') ? 'node' : 'unknown';\n }\n if ((t.flags & NUMBER_LIKE) !== 0) return 'number';\n if ((t.flags & STRING_LIKE) !== 0) return 'string';\n if ((await checker.typeToString(t)) === 'Date') return 'date';\n if ((t.flags & OBJECT) !== 0) return 'node';\n return 'unknown';\n };\n\n return {\n source: 'TypeScript',\n async kinds(file, spans) {\n const project = await snapshot.getDefaultProjectForFile(resolve(file));\n const sourceFile = await project?.program.getSourceFile(resolve(file));\n if (project === undefined || sourceFile === undefined) return spans.map(() => undefined);\n // The smallest node covering each span is the expression itself.\n const nodes = spans.map(({ start, end }) => {\n let node: TsNode = sourceFile;\n for (;;) {\n let next: TsNode | undefined;\n node.forEachChild((child) => {\n if (next === undefined && child.pos <= start && child.end >= end) next = child;\n });\n if (next === undefined) return node;\n node = next;\n }\n });\n const types = await project.checker.getTypeAtLocation(nodes as never[]);\n return Promise.all(types.map((type) => classify(project.checker, type)));\n },\n close() {\n return api.close();\n },\n };\n}\n\n/** The kind an expression's syntax gives away, where it does: `items.length`, `a * b`, `new Date()`. */\nexport function syntaxKind(expr: Node): ValueKind | undefined {\n switch (expr.type) {\n case 'Literal':\n return typeof expr.value === 'number' ? 'number' : typeof expr.value === 'string' ? 'string' : undefined;\n case 'TemplateLiteral':\n return 'string';\n case 'MemberExpression':\n return !expr.computed && ['length', 'size', 'count'].includes(expr.property?.name as string) ? 'number' : undefined;\n case 'BinaryExpression':\n return ['-', '*', '/', '%', '**'].includes(expr.operator as string) ? 'number' : undefined;\n case 'NewExpression':\n return expr.callee?.type === 'Identifier' && expr.callee.name === 'Date' ? 'date' : undefined;\n case 'CallExpression':\n return expr.callee?.type === 'MemberExpression' && expr.callee.object?.name === 'Math' ? 'number' : undefined;\n default:\n return undefined;\n }\n}\n","/**\n * `prosebook extract`: moves hardcoded JSX text into the catalog and rewrites the code to read it.\n *\n * It works on runs, not fragments. The children of an element that together make one sentence, text,\n * simple expressions and inline elements holding only text, become one message: `Hi {user.name}, <a\n * href=\"/x\">go</a>!` is `Hi {name}, <a>go</a>!`, so a translator can reorder the whole sentence. A run\n * becomes `<Text id=… />`, which needs no hook; a readable attribute becomes `t('…')`, with `useCopy()`\n * added to its component. Keys, kinds, surfaces and contexts are suggestions, marked for review.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, extname, join, relative } from 'node:path';\n\nimport { parseSync } from 'oxc-parser';\nimport { stringify } from 'yaml';\n\nimport { CatalogError, KEY_PATTERN, loadCatalog, SCHEMA_LINE } from './catalog.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport { parseMessage } from './icu.ts';\nimport { singularOf } from './lint.ts';\nimport { looksLikeCode } from './scan.ts';\nimport { writeMessage } from './syntax.ts';\nimport { syntaxKind, typescriptOracle, type Span, type ValueKind } from './types.ts';\n\n// oxlint-disable-next-line typescript/no-explicit-any -- a loose ESTree node: the walk reads whichever fields each node type has\ntype Node = { type: string; start: number; end: number; [field: string]: any };\n\nexport interface Extraction {\n file: string;\n line: number;\n column: number;\n key: string;\n /** The ICU message. */\n message: string;\n /** Whether an entry with this key and message already exists, so none is added. */\n reused: boolean;\n where: string;\n /** The code that replaces the hardcoded text. */\n replacement: string;\n}\n\nexport interface NewEntry {\n key: string;\n area: string;\n message: string;\n kind?: string;\n surface?: string;\n context: string;\n args: Record<string, string>;\n}\n\ninterface Edit {\n start: number;\n end: number;\n text: string;\n}\n\nexport interface FilePlan {\n path: string;\n file: string;\n code: string;\n edits: Edit[];\n extractions: Extraction[];\n warnings: string[];\n}\n\nexport interface ExtractPlan {\n files: FilePlan[];\n entries: NewEntry[];\n /** What said which placeholders are numbers and dates: `TypeScript`, or `syntax` alone. */\n types: string;\n}\n\nconst MODULE = 'prosebook/react';\nconst NON_PROSE = new Set(['code', 'pre', 'kbd', 'samp', 'script', 'style', 'svg', 'path', 'Text']);\nconst LETTER = /\\p{L}/u;\n\n/** Elements that sit inside a sentence, and so become a tag in its message rather than ending it. */\nconst INLINE = new Set([\n 'a',\n 'abbr',\n 'b',\n 'bdi',\n 'cite',\n 'del',\n 'dfn',\n 'em',\n 'i',\n 'ins',\n 'kbd',\n 'mark',\n 'q',\n 's',\n 'small',\n 'span',\n 'strong',\n 'sub',\n 'sup',\n 'time',\n 'u',\n 'var',\n]);\nconst isInline = (name: string) => INLINE.has(name) || /^[A-Z]\\w*Link$|^Link$/.test(name);\n\n/** Elements whose text has an obvious kind and surface. */\nconst ROLES: Record<string, [kind: string, surface: string]> = {\n button: ['action', 'action'],\n h1: ['name', 'heading'],\n h2: ['name', 'heading'],\n h3: ['name', 'heading'],\n h4: ['name', 'heading'],\n h5: ['name', 'heading'],\n h6: ['name', 'heading'],\n th: ['name', 'heading'],\n legend: ['name', 'heading'],\n caption: ['name', 'heading'],\n label: ['name', 'label'],\n option: ['name', 'choice'],\n title: ['name', 'title'],\n};\n\n/** Readable attributes: the kind and surface their value has, and the key suffix that says which it is. */\nconst ATTRIBUTE_ROLES: Record<string, [kind: string | undefined, surface: string, suffix: string]> = {\n placeholder: [undefined, 'placeholder', 'placeholder'],\n title: ['name', 'tooltip', 'tooltip'],\n 'aria-label': ['name', 'aria', 'aria'],\n 'aria-description': ['description', 'aria', 'aria-description'],\n 'aria-placeholder': [undefined, 'aria', 'aria-placeholder'],\n alt: [undefined, 'aria', 'alt'],\n label: ['name', 'label', 'label'],\n};\n\n/** The area a file's strings go in: its name in kebab case, or its folder's for an `index`. */\nexport function areaOf(file: string): string {\n let name = basename(file, extname(file));\n if (['index', 'page', 'layout', 'route', 'main'].includes(name)) name = basename(dirname(file));\n const kebab = name\n .replace(/([a-z0-9])([A-Z])/g, '$1-$2')\n .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return kebab === '' ? 'app' : kebab;\n}\n\n/** A key segment from the words of a message: its first four, plain ASCII, lowercase. */\nexport function slugOf(message: string): string {\n const words = message\n .replace(/<\\/?[\\w-]+>|\\{[^}]*\\}/g, ' ')\n .replace(/ı/g, 'i')\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, ' ')\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, 4);\n return words.length === 0 ? 'text' : words.join('-');\n}\n\n/**\n * A count followed by a plural noun, `{n, number} files`, as the plural it needs: `{n, plural, one {#\n * file} other {# files}}`. English only, and a draft: the TODO above the entry asks for a look.\n */\nexport function draftPlurals(message: string): string {\n return message.replace(/\\{(\\w+), number\\} (\\p{L}+)(?![\\p{L}'])/gu, (whole, name: string, word: string) => {\n const one = singularOf(word);\n return one === undefined ? whole : `{${name}, plural, one {# ${one}} other {# ${word}}}`;\n });\n}\n\n/** A message built as MF1, as the catalog writes messages: an MF2 catalog gets it in MF2. */\nfunction inSyntax(config: ResolvedConfig, mf1: string): string {\n return config.messageFormat === 2 ? writeMessage(2, parseMessage(mf1), config.source) : mf1;\n}\n\n/** Text written literally inside an ICU message. */\nfunction escapeIcu(text: string): string {\n return text.replace(/[{}<]/g, (c) => `'${c}'`);\n}\n\n/** JSX text as React renders it: lines trimmed where they meet a line break, blank lines dropped. */\nfunction jsxText(value: string): string {\n const lines = value.split(/\\r\\n|\\n|\\r/);\n // As Babel does: a value with no visible characters still keeps its one line, so a lone space renders.\n let lastNonEmpty = 0;\n lines.forEach((line, i) => {\n if (/[^ \\t]/.test(line)) lastNonEmpty = i;\n });\n let out = '';\n lines.forEach((line, i) => {\n let trimmed = line.replace(/\\t/g, ' ');\n if (i !== 0) trimmed = trimmed.replace(/^ +/, '');\n if (i !== lines.length - 1) trimmed = trimmed.replace(/ +$/, '');\n if (trimmed === '') return;\n out += i === lastNonEmpty ? trimmed : `${trimmed} `;\n });\n return out;\n}\n\nconst SIMPLE_EXPRESSIONS = new Set([\n 'Identifier',\n 'MemberExpression',\n 'CallExpression',\n 'TemplateLiteral',\n 'Literal',\n 'BinaryExpression',\n 'TSNonNullExpression',\n 'ChainExpression',\n]);\n\n/** A value that reads as a placeholder: no JSX and no functions inside it, which would be markup or logic. */\nfunction simple(expr: Node): boolean {\n if (!SIMPLE_EXPRESSIONS.has(expr.type)) return false;\n let found = false;\n const walk = (node: Node) => {\n if (found) return;\n if (/^JSX|FunctionExpression$/.test(node.type)) found = true;\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) value.forEach((v) => isNode(v) && walk(v));\n else if (isNode(value)) walk(value);\n }\n };\n walk(expr);\n return !found;\n}\n\n/** Every name bound inside a node: declarations, parameters, catch clauses, imports. With `deep` false, only its own scope's. */\nfunction bindingsIn(root: Node, deep = true): Set<string> {\n const names = new Set<string>();\n const pattern = (p: Node | null | undefined) => {\n if (!p) return;\n if (p.type === 'Identifier') names.add(p.name as string);\n else if (p.type === 'ObjectPattern')\n for (const prop of p.properties as Node[]) pattern(prop.type === 'RestElement' ? prop : prop.value);\n else if (p.type === 'ArrayPattern') for (const el of p.elements as (Node | null)[]) pattern(el);\n else if (p.type === 'AssignmentPattern') pattern(p.left);\n else if (p.type === 'RestElement') pattern(p.argument);\n };\n const walk = (node: Node, top: boolean) => {\n if (node.type === 'VariableDeclarator') pattern(node.id);\n if (node.type === 'ImportSpecifier' || node.type === 'ImportDefaultSpecifier' || node.type === 'ImportNamespaceSpecifier')\n pattern(node.local);\n if ((node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') && node.id) pattern(node.id);\n if (node.type === 'CatchClause') pattern(node.param);\n const isFunction = /Function(?:Declaration|Expression)$|^ArrowFunctionExpression$/.test(node.type);\n if (isFunction && (deep || top)) for (const param of node.params as Node[]) pattern(param);\n if (isFunction && !deep && !top) return;\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) if (isNode(child)) walk(child, false);\n } else if (isNode(value)) {\n walk(value, false);\n }\n }\n };\n walk(root, true);\n return names;\n}\n\nfunction elementName(el: Node): string | undefined {\n const name = el.openingElement?.name as Node | undefined;\n return name?.type === 'JSXIdentifier' ? (name.name as string) : undefined;\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && typeof (value as Node).type === 'string';\n}\n\ninterface Context {\n config: ResolvedConfig;\n area: string;\n /** Every key's source message, the catalog's and those added in this run. */\n known: Map<string, string>;\n entries: NewEntry[];\n /** The kind of value each JSX expression holds, by `start:end`. */\n kinds: Map<string, ValueKind>;\n}\n\n/** Plans the extraction from one file: the edits and the entries, without writing anything. */\nfunction planFile(path: string, ctx: Context): FilePlan {\n const code = readFileSync(path, 'utf8');\n const file = relative(ctx.config.root, path).replaceAll('\\\\', '/');\n const plan: FilePlan = { path, file, code, edits: [], extractions: [], warnings: [] };\n const { program } = parseSync(file, code);\n const topLevel = bindingsIn(program, false);\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 line = lineStarts.findIndex((s) => s > at);\n line = line === -1 ? lineStarts.length : line;\n return { line, column: at - lineStarts[line - 1]! + 1 };\n };\n const ignored = (at: number) => {\n const { line } = position(at);\n return (lines[line - 1] ?? '').includes('prosebook-ignore') || (lines[line - 2] ?? '').includes('prosebook-ignore');\n };\n\n let needText = false;\n let needUseCopy = false;\n /** Per component, by where it starts: the call that reads the catalog there. */\n const hooked = new Map<number, string>();\n /** Children now inside a `<Text>`, which the walk does not enter. */\n const consumed = new Set<Node>();\n /** Enclosing functions, innermost last: the component each belongs to, and whether it is one. */\n const components: { fn: Node; name: string | undefined; isComponent: boolean }[] = [];\n\n const componentName = (fn: Node, parent: Node | undefined): string | undefined => {\n if (fn.id?.name) return fn.id.name as string;\n if (parent?.type === 'VariableDeclarator' && parent.id?.type === 'Identifier') return parent.id.name as string;\n return undefined;\n };\n\n const entryFor = (\n message: string,\n args: Record<string, string>,\n role: [string | undefined, string | undefined],\n context: string,\n suffix?: string,\n ) => {\n for (const [key, text] of ctx.known) if (text === message && key.startsWith(`${ctx.area}.`)) return { key, reused: true };\n\n const base = `${ctx.area}.${slugOf(message)}${suffix ? `-${suffix}` : ''}`;\n let key = base;\n for (let n = 2; ctx.known.has(key); n++) key = `${base}-${n}`;\n ctx.known.set(key, message);\n const [kind, surface] = role;\n ctx.entries.push({\n key,\n area: ctx.area,\n message,\n ...(kind !== undefined && kind in ctx.config.kinds ? { kind } : {}),\n ...(surface !== undefined && surface in ctx.config.surfaces ? { surface } : {}),\n context,\n args,\n });\n return { key, reused: false };\n };\n\n const where = (element: string | undefined) => {\n const component = components.at(-1)?.name;\n return `${component ? `In ${component}` : `In ${file}`}${element ? `, the text of a <${element}>` : ''}.`;\n };\n\n /** The children of one element, split into runs at anything that cannot sit inside a sentence. */\n const extractChildren = (parent: Node, children: Node[]) => {\n const parentName = parent.type === 'JSXElement' ? elementName(parent) : undefined;\n let run: Node[] = [];\n const flush = () => {\n if (run.length > 0) extractRun(run, parentName);\n run = [];\n };\n for (const child of children) {\n if (inlineable(child)) run.push(child);\n else flush();\n }\n flush();\n };\n\n const inlineable = (child: Node): boolean => {\n if (child.type === 'JSXText') return true;\n if (child.type === 'JSXExpressionContainer') return simple(child.expression as Node);\n if (child.type === 'JSXElement') {\n const name = elementName(child);\n if (name === undefined || child.openingElement.selfClosing || !isInline(name)) return false;\n return (child.children as Node[]).every(inlineable);\n }\n return false;\n };\n\n const extractRun = (run: Node[], parentName: string | undefined) => {\n // Whitespace-only text at either end stays where it is, so the layout around the run is unchanged.\n const blank = (n: Node) => n.type === 'JSXText' && jsxText(n.value as string).trim() === '';\n const from = run.findIndex((n) => !blank(n));\n if (from === -1) return;\n const nodes = run.slice(from, run.findLastIndex((n) => !blank(n)) + 1);\n // Elements with nothing but whitespace between them are layout, not a sentence: each one's text is\n // its own, as `<li><a href>Home</a></li>` is \"Home\".\n if (nodes.every((n) => n.type === 'JSXElement' || blank(n))) return;\n const first = nodes[0]!;\n const last = nodes.at(-1)!;\n const values = new Map<string, string>();\n const argDocs: Record<string, string> = {};\n const names = new Map<string, string>();\n const tags = new Set<string>();\n\n const argName = (expr: Node): string => {\n const source = code.slice(expr.start, expr.end);\n const known = names.get(source);\n if (known !== undefined) return known;\n let inner = expr;\n while (inner.type === 'TSNonNullExpression' || inner.type === 'ChainExpression') inner = inner.expression;\n let name =\n inner.type === 'Identifier'\n ? (inner.name as string)\n : inner.type === 'MemberExpression' && !inner.computed && inner.property?.type === 'Identifier'\n ? (inner.property.name as string)\n : 'value';\n const base = name;\n for (let n = 2; values.has(name) || tags.has(name); n++) name = `${base}${n}`;\n names.set(source, name);\n values.set(name, source);\n argDocs[name] = `TODO: what \\`${source}\\` holds`;\n return name;\n };\n\n const piece = (node: Node): string => {\n if (node.type === 'JSXText') return escapeIcu(jsxText(node.value as string));\n if (node.type === 'JSXElement') {\n let tag = elementName(node)!.toLowerCase();\n const base = tag;\n for (let n = 2; tags.has(tag) || values.has(tag); n++) tag = `${base}${n}`;\n tags.add(tag);\n const open = code.slice(node.openingElement.start, node.openingElement.end);\n const close = code.slice(node.closingElement.start, node.closingElement.end);\n values.set(tag, `(chunks) => ${open}{chunks}${close}`);\n return `<${tag}>${(node.children as Node[]).map(piece).join('')}</${tag}>`;\n }\n const expr = node.expression as Node;\n if (expr.type === 'Literal' && typeof expr.value === 'string') return escapeIcu(expr.value);\n if (expr.type === 'TemplateLiteral' && expr.expressions.length === 0) return escapeIcu(expr.quasis[0].value.cooked as string);\n const kind = ctx.kinds.get(`${expr.start}:${expr.end}`) ?? syntaxKind(expr);\n if (kind === 'node') markup.push(code.slice(expr.start, expr.end));\n return `{${argName(expr)}${kind === 'number' ? ', number' : kind === 'date' ? ', date, medium' : ''}}`;\n };\n\n const markup: string[] = [];\n let message = nodes.map(piece).join('').trim();\n if (markup.length > 0) {\n const at = position(first.start);\n plan.warnings.push(\n `${file}:${at.line}:${at.column} {${markup.join('}, {')}} holds markup, not text, so this sentence was left as it is: make it a tag, or give the text around it entries of its own`,\n );\n return;\n }\n if (/^en\\b/.test(ctx.config.source)) message = draftPlurals(message);\n // A run of spaces, placeholders and punctuation is layout, not words.\n const words = message.replace(/\\{[^}]*\\}|<\\/?[\\w-]+>|'[{}<]'/g, '');\n if (!LETTER.test(words) || (values.size === 0 && looksLikeCode(message))) return;\n // The whitespace at either end stays in the code as it was: a space still renders as one, and a\n // line break still renders as nothing.\n const start = first.type === 'JSXText' ? first.start + (/^\\s*/.exec(first.value as string)?.[0].length ?? 0) : first.start;\n const end = last.type === 'JSXText' ? last.end - (/\\s*$/.exec(last.value as string)?.[0].length ?? 0) : last.end;\n if (ignored(start)) return;\n\n message = inSyntax(ctx.config, message);\n const role = parentName !== undefined ? ROLES[parentName] : undefined;\n const { key, reused } = entryFor(message, argDocs, role ?? [undefined, undefined], where(parentName));\n const props = [...values].map(([name, source]) => `${/^[A-Za-z_$][\\w$]*$/.test(name) ? name : JSON.stringify(name)}: ${source}`);\n const replacement = `<Text id=${JSON.stringify(key)}${props.length > 0 ? ` values={{ ${props.join(', ')} }}` : ''} />`;\n plan.edits.push({ start, end, text: replacement });\n for (const n of nodes) consumed.add(n);\n plan.extractions.push({ file, ...position(start), key, message, reused, where: 'text', replacement });\n needText = true;\n };\n\n const extractAttribute = (attr: Node, element: Node) => {\n const name = attr.name?.type === 'JSXIdentifier' ? (attr.name.name as string) : '';\n if (!ctx.config.scan.attributes.includes(name) || attr.value === null) return;\n const value = attr.value as Node;\n let text: string | undefined;\n if (value.type === 'Literal' && typeof value.value === 'string') {\n text = value.value;\n } else if (value.type === 'JSXExpressionContainer') {\n const expr = value.expression as Node;\n if (expr.type === 'Literal' && typeof expr.value === 'string') text = expr.value;\n if (expr.type === 'TemplateLiteral' && expr.expressions.length === 0) text = expr.quasis[0].value.cooked as string;\n }\n if (text === undefined || !LETTER.test(text) || looksLikeCode(text) || ignored(attr.start)) return;\n const at = position(value.start);\n const fn = components.findLast((c) => c.isComponent)?.fn;\n if (fn === undefined || fn.body?.type !== 'BlockStatement') {\n plan.warnings.push(\n `${file}:${at.line}:${at.column} ${name}=\"${text}\" is not inside a component with a body, so it has no t() to read; left as it is`,\n );\n return;\n }\n const call = hookFor(fn);\n if (call === undefined) {\n plan.warnings.push(\n `${file}:${at.line}:${at.column} ${name}=\"${text}\": its component binds both \\`t\\` and \\`copy\\` already; left as it is`,\n );\n return;\n }\n const [kind, surface, suffix] = ATTRIBUTE_ROLES[name] ?? [undefined, undefined, name];\n const tag = elementName(element) ?? 'element';\n const component = components.at(-1)?.name;\n const context = `${component ? `In ${component}` : `In ${file}`}, the ${name} of a <${tag}>.`;\n const message = inSyntax(ctx.config, escapeIcu(text));\n const { key, reused } = entryFor(message, {}, [kind, surface], context, suffix);\n const replacement = `{${call}(${JSON.stringify(key)})}`;\n plan.edits.push({ start: value.start, end: value.end, text: replacement });\n plan.extractions.push({ file, ...at, key, message, reused, where: name, replacement });\n };\n\n /**\n * Makes sure the component can read the catalog, and returns the call that does: `t` from\n * `useCopy()`, or `copy.t` where the component or the module already binds a `t` of its own.\n */\n const hookFor = (fn: Node): string | undefined => {\n const chosen = hooked.get(fn.start);\n if (chosen !== undefined) return chosen;\n const taken = new Set([...topLevel, ...bindingsIn(fn)]);\n const body = code.slice(fn.body.start, fn.body.end);\n const insert = (text: string) => plan.edits.push({ start: fn.body.start + 1, end: fn.body.start + 1, text });\n const firstStatement = fn.body.body[0] as Node | undefined;\n const indent = firstStatement ? ' '.repeat(position(firstStatement.start).column - 1) : ' ';\n let call: string | undefined;\n const destructured = /\\{([^{}]*)\\}\\s*=\\s*useCopy\\(\\)/.exec(body);\n const whole = /\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*useCopy\\(\\)/.exec(body);\n if (destructured !== null && /(^|[\\s,])t(\\s*[,}]|\\s*$)/.test(destructured[1]!)) {\n call = 't';\n } else if (whole !== null) {\n call = `${whole[1]}.t`;\n } else if (destructured !== null && !taken.has('t')) {\n const at = fn.body.start + destructured.index + 1;\n plan.edits.push({ start: at, end: at, text: ' t,' });\n call = 't';\n } else if (!taken.has('t')) {\n insert(`\n${indent}const { t } = useCopy();`);\n call = 't';\n } else if (!taken.has('copy')) {\n insert(`\n${indent}const copy = useCopy();`);\n call = 'copy.t';\n }\n if (call !== undefined && destructured === null && whole === null) needUseCopy = true;\n if (call !== undefined) hooked.set(fn.start, call);\n return call;\n };\n\n const visit = (node: Node, parent: Node | undefined) => {\n const isFunction = node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';\n if (isFunction) {\n const own = componentName(node, parent);\n const isComponent = own !== undefined && /^(?:[A-Z]|use[A-Z])/.test(own);\n components.push({ fn: node, name: isComponent ? own : components.at(-1)?.name, isComponent });\n }\n if (node.type === 'JSXElement' || node.type === 'JSXFragment') {\n const name = node.type === 'JSXElement' ? elementName(node) : undefined;\n if (node.type === 'JSXElement') {\n for (const attr of node.openingElement.attributes as Node[]) {\n if (attr.type !== 'JSXAttribute') continue;\n extractAttribute(attr, node);\n // `render={() => <p>Hi</p>}` holds JSX of its own.\n if (attr.value?.type === 'JSXExpressionContainer') visit(attr.value as Node, attr);\n }\n }\n if (name === undefined || !NON_PROSE.has(name)) {\n extractChildren(node, node.children as Node[]);\n for (const child of node.children as Node[]) if (!consumed.has(child)) visit(child, node);\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, node);\n } else if (isNode(value)) {\n visit(value, node);\n }\n }\n if (isFunction) components.pop();\n };\n\n visit(program, undefined);\n plan.extractions.sort((a, b) => (a.line !== b.line ? a.line - b.line : a.column - b.column));\n if (needText || needUseCopy) importEdit(program, code, plan, [...(needText ? ['Text'] : []), ...(needUseCopy ? ['useCopy'] : [])]);\n return plan;\n}\n\n/** Adds the names to an existing `prosebook/react` import, or a new import after the last one. */\nfunction importEdit(program: Node, code: string, plan: FilePlan, wanted: string[]) {\n const imports = (program.body as Node[]).filter((s) => s.type === 'ImportDeclaration');\n const existing = imports.find((s) => s.source.value === MODULE && s.importKind !== 'type');\n if (existing) {\n const named = (existing.specifiers as Node[]).filter((s) => s.type === 'ImportSpecifier');\n const have = new Set(named.map((s) => s.local.name as string));\n const missing = wanted.filter((w) => !have.has(w));\n if (missing.length === 0) return;\n if (named.length > 0) {\n plan.edits.push({ start: named.at(-1)!.end, end: named.at(-1)!.end, text: `, ${missing.join(', ')}` });\n return;\n }\n }\n const line = `import { ${wanted.join(', ')} } from '${MODULE}';`;\n const last = imports.at(-1);\n if (last) {\n plan.edits.push({ start: last.end, end: last.end, text: `\\n${line}` });\n } else {\n const directive = /^(?:\\s*(['\"])use \\w+\\1;?\\s*\\n)*/.exec(code)?.[0] ?? '';\n plan.edits.push({ start: directive.length, end: directive.length, text: `${line}\\n${directive === '' ? '\\n' : ''}` });\n }\n}\n\n/** The spans of every expression inside JSX, which are the ones that may become placeholders. */\nfunction jsxExpressions(file: string, code: string): Span[] {\n const spans: Span[] = [];\n const walk = (node: Node) => {\n if (node.type === 'JSXExpressionContainer' && node.expression.type !== 'JSXEmptyExpression')\n spans.push({ start: node.expression.start as number, end: node.expression.end as number });\n\n for (const value of Object.values(node)) {\n if (Array.isArray(value)) {\n for (const child of value) if (isNode(child)) walk(child);\n } else if (isNode(value)) {\n walk(value);\n }\n }\n };\n walk(parseSync(file, code).program);\n return spans;\n}\n\n/**\n * Plans an extraction over the files in `paths` against the current catalog. With `types` (the\n * default), the project's TypeScript says which placeholders are numbers and dates.\n */\nexport async function planExtract(\n config: ResolvedConfig,\n paths: string[],\n options: { area?: string; types?: boolean } = {},\n): Promise<ExtractPlan> {\n if (options.area !== undefined && !KEY_PATTERN.test(`${options.area}.x`))\n throw new Error(`prosebook: \"${options.area}\" is not an area: lowercase words joined by -`);\n const known = new Map<string, string>();\n if (existsSync(config.catalog)) {\n try {\n for (const m of loadCatalog(config).messages) known.set(m.key, m.text);\n } catch (e) {\n if (!(e instanceof CatalogError)) throw e;\n throw new Error(`prosebook: fix the catalog before extracting into it:\\n${e.message}`, { cause: e });\n }\n }\n const entries: NewEntry[] = [];\n const oracle = options.types === false ? undefined : await typescriptOracle(config.root, paths);\n try {\n // One file at a time: each plan sees the entries the ones before it added.\n const files: FilePlan[] = [];\n for (const path of paths) {\n const kinds = new Map<string, ValueKind>();\n if (oracle) {\n const spans = jsxExpressions(path, readFileSync(path, 'utf8'));\n (await oracle.kinds(path, spans)).forEach((kind, i) => {\n if (kind !== undefined && kind !== 'unknown') kinds.set(`${spans[i]!.start}:${spans[i]!.end}`, kind);\n });\n }\n files.push(planFile(path, { config, area: options.area ?? areaOf(path), known, entries, kinds }));\n }\n return { files, entries, types: oracle?.source ?? 'syntax' };\n } finally {\n await oracle?.close();\n }\n}\n\n/** Applies a plan: rewrites the files and appends the new entries to their area's catalog file. */\nexport function applyExtract(config: ResolvedConfig, plan: ExtractPlan): string[] {\n const written: string[] = [];\n for (const file of plan.files) {\n if (file.edits.length === 0) continue;\n let code = file.code;\n for (const edit of [...file.edits].sort((a, b) => (a.start !== b.start ? b.start - a.start : b.end - a.end)))\n code = code.slice(0, edit.start) + edit.text + code.slice(edit.end);\n\n writeFileSync(file.path, code);\n written.push(file.path);\n }\n const byArea = new Map<string, NewEntry[]>();\n for (const entry of plan.entries) byArea.set(entry.area, [...(byArea.get(entry.area) ?? []), entry]);\n for (const [area, entries] of byArea) {\n const path = join(config.catalog, `${area}.yaml`);\n // Written as YAML 1.1, which quotes `n`, `yes` and `on`: they then read the same under 1.1 and 1.2,\n // as the catalog requires.\n const blocks = entries.map((e) => {\n const body: Record<string, unknown> = {};\n if (e.kind) body.kind = e.kind;\n if (e.surface) body.surface = e.surface;\n body.context = e.context;\n if (Object.keys(e.args).length > 0) body.args = e.args;\n body[config.source] = e.message;\n return `# TODO(prosebook extract): check the key, kind and context.\\n${stringify({ [e.key]: body }, { lineWidth: 0, version: '1.1' })}`;\n });\n mkdirSync(config.catalog, { recursive: true });\n const current = existsSync(path) ? readFileSync(path, 'utf8') : `${SCHEMA_LINE}\\n`;\n writeFileSync(path, `${current.replace(/\\n*$/, '\\n')}\\n${blocks.join('\\n')}`);\n written.push(path);\n }\n return written;\n}\n","/**\n * `prosebook migrate`: a catalog and its translations rewritten from one syntax to the other. Each\n * message is written in the target syntax, read back, and rendered both ways over sample values, so\n * one that can't be written, or would come out different, is reported before anything changes.\n */\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { isMap, parseDocument, Scalar } from 'yaml';\n\nimport { createHelpers, flatten } from '#src/runtime/helpers.ts';\nimport type { CompiledMessages } from '#src/runtime/types.ts';\n\nimport { loadCatalog } from './catalog.ts';\nimport { compileLocale, localeExpression } from './compile.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport { argsOf, tagsOf, type MessageFormatElement } from './icu.ts';\nimport { readMessage, writeMessage, type MessageFormatVersion } from './syntax.ts';\n\ninterface Located {\n key: string;\n locale: string;\n /** The file, relative to the project root. */\n file: string;\n}\n\nexport interface MigratePlan {\n to: MessageFormatVersion;\n rewrites: (Located & { before: string; after: string })[];\n /** Messages the target syntax can't say, and why. Nothing is written while there are any. */\n blocked: (Located & { reason: string })[];\n /** Messages that would render differently, with the first values that show it. */\n changed: (Located & { values: string; before: string; after: string })[];\n}\n\nexport function planMigrate(config: ResolvedConfig, to: MessageFormatVersion): MigratePlan {\n // Both syntaxes read without isolation marks, which MF1 never has, so their renders compare.\n const catalog = loadCatalog({ ...config, isolate: false });\n const plan: MigratePlan = { to, rewrites: [], blocked: [], changed: [] };\n for (const m of catalog.messages) {\n const versions = [\n { locale: config.source, text: m.text, ast: m.ast, file: m.file },\n ...[...m.translations].map(([locale, t]) => ({ locale, text: t.text, ast: t.ast, file: t.file })),\n ];\n for (const { locale, text, ast, file } of versions) {\n const at = { key: m.key, locale, file };\n let after: string;\n let back: MessageFormatElement[];\n try {\n after = writeMessage(to, ast, locale);\n back = readMessage(to, after, locale, false);\n } catch (e) {\n plan.blocked.push({ ...at, reason: (e as Error).message });\n continue;\n }\n const difference = firstDifference(ast, back, locale);\n if (difference !== undefined) plan.changed.push({ ...at, ...difference });\n if (after !== text) plan.rewrites.push({ ...at, before: text, after });\n }\n }\n return plan;\n}\n\n/** Writes the plan's rewrites into the YAML files, keeping their comments and layout. Returns the files written. */\nexport function applyMigrate(config: ResolvedConfig, plan: MigratePlan): string[] {\n if (plan.blocked.length > 0) throw new Error('prosebook: some messages have no equivalent in the target syntax; nothing was written');\n const byFile = Map.groupBy(plan.rewrites, (r) => r.file);\n for (const [file, rewrites] of byFile) {\n const path = join(config.root, file);\n // As YAML 1.1, so a new value that 1.1 would read differently, like `n`, is quoted.\n const doc = parseDocument(readFileSync(path, 'utf8'), { version: '1.1' });\n for (const r of rewrites) {\n const value = doc.createNode(r.after);\n if (r.after.includes('\\n')) value.type = Scalar.BLOCK_LITERAL;\n const entry = doc.get(r.key, true);\n if (isMap(entry)) entry.set(config.source, value);\n else doc.set(r.key, value);\n }\n writeFileSync(path, doc.toString({ lineWidth: 0 }));\n }\n return [...byFile.keys()];\n}\n\nconst NUMBERS = [0, 1, 2, 3, 5, 11, 21, 101, 1.5, 1234.5];\nconst DATE = new Date(Date.UTC(2026, 0, 5, 14, 30, 15));\n\nfunction samplesOf(type: string): unknown[] {\n if (type === 'number') return NUMBERS;\n if (type === 'Date | number') return [DATE];\n const named = [...type.matchAll(/\"((?:[^\"\\\\]|\\\\.)*)\"/g)].map((m) => JSON.parse(`\"${m[1]}\"`) as string);\n return named.length > 0 ? [...named, 'zzz'] : ['Ada'];\n}\n\n/** Every combination of sample values, or each argument varied alone where there are too many. */\nfunction* samples(a: MessageFormatElement[], b: MessageFormatElement[]): Generator<Record<string, unknown>> {\n const types = new Map<string, Set<unknown>>();\n for (const [name, type] of [...argsOf(a), ...argsOf(b)]) {\n const set = types.get(name) ?? new Set();\n for (const value of samplesOf(type)) set.add(value);\n types.set(name, set);\n }\n const tags = Object.fromEntries(\n [...new Set([...tagsOf(a), ...tagsOf(b)])].map((t) => [t, (c: unknown[]) => `<${t}>${c.join('')}</${t}>`]),\n );\n const names = [...types.keys()];\n const lists = names.map((n) => [...types.get(n)!]);\n if (lists.reduce((n, l) => n * l.length, 1) <= 1000) {\n let combos: unknown[][] = [[]];\n for (const list of lists) combos = combos.flatMap((c) => list.map((v) => [...c, v]));\n for (const combo of combos) yield { ...tags, ...Object.fromEntries(names.map((n, i) => [n, combo[i]])) };\n return;\n }\n const base = Object.fromEntries(names.map((n, i) => [n, lists[i]![0]]));\n for (const [i, n] of names.entries()) for (const value of lists[i]!) yield { ...tags, ...base, [n]: value };\n}\n\nfunction renderer(ast: MessageFormatElement[], locale: string): (values: Record<string, unknown>) => string {\n // oxlint-disable-next-line no-new-func, typescript/no-implied-eval -- evaluating our own compiled message, as the bundle would\n const messages = new Function(`return ${localeExpression(compileLocale([['m', ast]]))};`)() as CompiledMessages;\n const message = messages.m!;\n const h = createHelpers(locale);\n return (values) => (typeof message === 'string' ? message : flatten(message(values, h)).join(''));\n}\n\nfunction firstDifference(a: MessageFormatElement[], b: MessageFormatElement[], locale: string) {\n const before = renderer(a, locale);\n const after = renderer(b, locale);\n for (const values of samples(a, b)) {\n const x = before(values);\n const y = after(values);\n if (x !== y) {\n const shown = Object.entries(values)\n .filter(([, v]) => typeof v !== 'function')\n .map(([k, v]) => `${k}: ${v instanceof Date ? v.toISOString() : JSON.stringify(v)}`);\n return { values: `{ ${shown.join(', ')} }`, before: x, after: y };\n }\n }\n return undefined;\n}\n","/**\n * Findings as SARIF 2.1.0, the static-analysis format code scanning reads (GitHub's among them), so\n * `lint`, `scan`, `status` and `check` can annotate a pull request on the line the string is written.\n *\n * Paths are relative to the project root under `%SRCROOT%`, which the log maps to the root itself:\n * a project that isn't at the top of its repository still resolves to the right files.\n */\nimport { pathToFileURL } from 'node:url';\n\nexport interface SarifResult {\n /** A rule the report describes, or one of a lint plugin's. */\n rule: string;\n level: 'error' | 'warning';\n message: string;\n /** Relative to the project root, with forward slashes; none for a problem that isn't in a file. */\n file?: string;\n /** From 1; none for the file as a whole. */\n line?: number;\n column?: number;\n}\n\nexport interface SarifTool {\n name: string;\n version: string;\n informationUri: string;\n}\n\n/** Collects what a command found, then writes it as one SARIF run. */\nexport class SarifReport {\n readonly results: SarifResult[] = [];\n private readonly rules = new Map<string, string | undefined>();\n\n /** Describes a rule in one sentence; the first description given for an id stands. */\n rule(id: string, description?: string): void {\n if (this.rules.get(id) === undefined) this.rules.set(id, description);\n }\n\n add(result: SarifResult): void {\n if (!this.rules.has(result.rule)) this.rules.set(result.rule, undefined);\n this.results.push(result);\n }\n\n toJSON(root: string, tool: SarifTool): object {\n const ids = [...this.rules.keys()];\n return {\n $schema: 'https://json.schemastore.org/sarif-2.1.0.json',\n version: '2.1.0',\n runs: [\n {\n tool: {\n driver: {\n ...tool,\n rules: ids.map((id) => {\n const description = this.rules.get(id);\n return description === undefined ? { id } : { id, shortDescription: { text: description } };\n }),\n },\n },\n originalUriBaseIds: { '%SRCROOT%': { uri: pathToFileURL(root).href.replace(/\\/?$/, '/') } },\n results: this.results.map((r) => ({\n ruleId: r.rule,\n ruleIndex: ids.indexOf(r.rule),\n level: r.level,\n message: { text: r.message },\n locations: r.file === undefined ? [] : [location(r.file, r.line, r.column)],\n })),\n },\n ],\n };\n }\n}\n\nfunction location(file: string, line: number | undefined, column: number | undefined): object {\n return {\n physicalLocation: {\n artifactLocation: { uri: encodeURI(file), uriBaseId: '%SRCROOT%' },\n ...(line === undefined ? {} : { region: { startLine: line, startColumn: column ?? 1 } }),\n },\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
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
2
  import { readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { extname, join, relative } from "node:path";
4
4
  import { isDateElement, isNumberElement, isTimeElement } from "@formatjs/icu-messageformat-parser";
5
5
  import { parseSync } from "oxc-parser";
6
6
  // src/tools/compile.ts
@@ -318,36 +318,67 @@ function keySeen(node) {
318
318
  }
319
319
  }
320
320
  const nameOf = (node) => node?.type === "Identifier" || node?.type === "JSXIdentifier" ? node.name : node?.type === "Literal" ? String(node.value) : void 0;
321
+ /** Where `<Text>` comes from: each framework binding's. */
322
+ const TEXT_SOURCES = /* @__PURE__ */ new Set([
323
+ "prosebook/react",
324
+ "prosebook/preact",
325
+ "prosebook/solid",
326
+ "prosebook/vue",
327
+ "prosebook/svelte"
328
+ ]);
329
+ /** Single-file components, whose reads are in their scripts and their markup. */
330
+ const SFC = /\.(?:vue|svelte)$/;
331
+ /** A single-file component's script blocks, each at its offset. */
332
+ const scriptBlocks = (code) => [...code.matchAll(/(<script\b[^>]*>)([\s\S]*?)<\/script>/g)].map((m) => ({
333
+ code: m[2],
334
+ at: m.index + m[1].length
335
+ }));
321
336
  /**
322
337
  * The reads in a module whose key a pruned build can't see, as in `t(item.key)` or `<Text id={id} />`:
323
338
  * the build would leave the message out, and the key would show in its place. A build has no types,
324
339
  * so readers are known by name: `t` and `rich` called alone or as methods, and any name imported here,
325
340
  * 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.
341
+ * reader. `<Text>` counts where it is imported from a prosebook binding. In a `.vue` or `.svelte` file,
342
+ * its scripts and the expressions in its markup are read. Lines marked `prosebook-ignore` are skipped.
328
343
  */
329
344
  function unseenReads(file, code) {
330
- const { program } = parseSync(file, code);
345
+ if (!SFC.test(file)) return readsIn(file, code, file, [{
346
+ code,
347
+ at: 0
348
+ }]);
349
+ const vue = file.endsWith(".vue");
350
+ const markup = blank(code, /<script\b[\s\S]*?<\/script>|<style\b[\s\S]*?<\/style>|<!--[\s\S]*?-->/g);
351
+ const scripts = scriptBlocks(code);
352
+ const expressions = vue ? vueExpressions(markup) : svelteExpressions(markup);
353
+ return readsIn(file, code, "component.ts", [...scripts, ...expressions], (names, report) => {
354
+ for (const tag of markupTexts(markup, names, vue)) report(tag.at, tag.end, "Text");
355
+ });
356
+ }
357
+ function readsIn(file, code, as, pieces, markup) {
358
+ const programs = pieces.map((piece) => ({
359
+ at: piece.at,
360
+ program: parseSync(as, piece.code).program
361
+ }));
331
362
  const { position, ignored } = lineIndex(code);
332
363
  const unseen = [];
333
364
  const aliases = /* @__PURE__ */ new Set();
334
365
  /** Local name to the name it was imported by. */
335
366
  const imported = /* @__PURE__ */ new Map();
336
367
  const texts = /* @__PURE__ */ new Set();
337
- for (const statement of program.body) {
368
+ for (const { program } of programs) for (const statement of program.body) {
338
369
  if (statement.type !== "ImportDeclaration") continue;
339
370
  for (const s of statement.specifiers) {
340
371
  const name = s.type === "ImportSpecifier" ? nameOf(s.imported) : void 0;
341
372
  const local = nameOf(s.local);
342
373
  if (name === void 0 || local === void 0) continue;
343
- if (name === "Text" && statement.source.value === "prosebook/react") texts.add(local);
374
+ if (name === "Text" && TEXT_SOURCES.has(String(statement.source.value))) texts.add(local);
344
375
  else imported.set(local, name);
345
376
  }
346
377
  }
347
- const report = (node, name) => {
348
- const at = position(node.start);
378
+ const report = (start, end, name) => {
379
+ const at = position(start);
349
380
  if (ignored(at.line)) return;
350
- const text = code.slice(node.start, node.end).replace(/\s+/g, " ");
381
+ const text = code.slice(start, end).replace(/\s+/g, " ");
351
382
  unseen.push({
352
383
  file,
353
384
  ...at,
@@ -356,32 +387,126 @@ function unseenReads(file, code) {
356
387
  });
357
388
  };
358
389
  const calls = [];
359
- const visit = (node) => {
390
+ const visit = (node, at) => {
360
391
  if (node.type === "ObjectPattern") {
361
392
  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
393
  }
363
394
  if (node.type === "CallExpression" && !keySeen(node.arguments[0])) {
364
395
  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));
396
+ if (callee.type === "Identifier") calls.push([
397
+ at + node.start,
398
+ at + node.end,
399
+ callee.name
400
+ ]);
401
+ else if (callee.type === "MemberExpression" && !callee.computed && READERS.has(nameOf(callee.property) ?? "")) report(at + node.start, at + node.end, nameOf(callee.property));
367
402
  }
368
403
  if (node.type === "JSXOpeningElement" && texts.has(nameOf(node.name) ?? "")) {
369
404
  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");
405
+ if (value !== null && value !== void 0 && !keySeen(value.type === "JSXExpressionContainer" ? value.expression : value)) report(at + node.start, at + node.end, "Text");
371
406
  }
372
407
  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);
408
+ for (const child of value) if (isNode(child)) visit(child, at);
409
+ } else if (isNode(value)) visit(value, at);
375
410
  };
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));
411
+ for (const { program, at } of programs) visit(program, at);
412
+ markup?.(texts, report);
413
+ for (const [start, end, local] of calls) if (READERS.has(local) || aliases.has(local)) report(start, end, local);
414
+ else if (imported.has(local)) report(start, end, imported.get(local));
379
415
  unseen.sort((a, b) => a.line === b.line ? a.column - b.column : a.line - b.line);
380
416
  return {
381
417
  unseen,
382
418
  aliases
383
419
  };
384
420
  }
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 };
421
+ /** `code` with each match of `pattern` blanked to spaces, its lines kept, so offsets still point into the file. */
422
+ const blank = (code, pattern) => code.replace(pattern, (m) => m.replace(/[^\n]/g, " "));
423
+ /** A Vue template's expressions: `{{ ... }}`, and the values of bound attributes and directives. */
424
+ function vueExpressions(markup) {
425
+ const pieces = [];
426
+ for (const m of markup.matchAll(/\{\{([\s\S]*?)\}\}/g)) pieces.push({
427
+ code: m[1],
428
+ at: m.index + 2
429
+ });
430
+ for (const m of markup.matchAll(/\s((?::|@|v-)[^\s=>/"']*)\s*=\s*(["'])([\s\S]*?)\2/g)) {
431
+ if (/^v-(?:for|slot)\b/.test(m[1])) continue;
432
+ pieces.push({
433
+ code: m[3],
434
+ at: m.index + m[0].length - m[3].length - 1
435
+ });
436
+ }
437
+ return pieces;
438
+ }
439
+ /** Where the `}` closing the `{` at `start` is, past strings and nested braces; -1 if none does. */
440
+ function closing(text, start) {
441
+ let depth = 0;
442
+ for (let i = start; i < text.length; i++) {
443
+ const c = text[i];
444
+ if (c === "\"" || c === "'" || c === "`") {
445
+ const end = text.indexOf(c, i + 1);
446
+ if (end === -1) return -1;
447
+ i = end;
448
+ } else if (c === "{") depth++;
449
+ else if (c === "}" && --depth === 0) return i;
450
+ }
451
+ return -1;
452
+ }
453
+ /** A Svelte template's expressions: each `{...}`, a block's or tag's past its keyword. */
454
+ function svelteExpressions(markup) {
455
+ const pieces = [];
456
+ for (let start = markup.indexOf("{"); start !== -1;) {
457
+ const end = closing(markup, start);
458
+ if (end === -1) break;
459
+ const at = start + 1;
460
+ const inner = markup.slice(at, end);
461
+ const tag = /^\s*([#:@/])(\w*)(\s+if\b)?/.exec(inner);
462
+ if (tag === null) pieces.push({
463
+ code: inner.replace("...", " "),
464
+ at
465
+ });
466
+ else {
467
+ const [head, sigil, word, elseIf] = tag;
468
+ const reads = sigil !== "/" && word !== "snippet" && word !== "then" && word !== "catch" && (word !== "else" || elseIf !== void 0);
469
+ if (reads && word === "const") pieces.push({
470
+ code: `var${inner.slice(head.length)}`,
471
+ at: at + head.length - 3
472
+ });
473
+ else if (reads) pieces.push({
474
+ code: inner.slice(head.length).split(/\s+(?:as|then)\s/)[0],
475
+ at: at + head.length
476
+ });
477
+ }
478
+ start = markup.indexOf("{", end + 1);
479
+ }
480
+ return pieces;
481
+ }
482
+ /**
483
+ * Each `<Text>` in markup whose key the build can't see: Vue's `:id="..."`, Svelte's `id={...}`,
484
+ * `{id}` or `id="text {with} a placeholder"`. An `id` written as plain text is a key `keyRefs` reads.
485
+ */
486
+ function markupTexts(markup, names, vue) {
487
+ if (names.size === 0) return [];
488
+ const found = [];
489
+ const tags = new RegExp(`<(?:${[...names].join("|")})(?=[\\s/>])((?:[^>"']|"[^"]*"|'[^']*')*)>`, "g");
490
+ for (const m of markup.matchAll(tags)) {
491
+ const attributes = m[1];
492
+ let id;
493
+ if (vue) id = /(?:^|\s)(?::|v-bind:)id\s*=\s*(["'])([\s\S]*?)\1/.exec(attributes)?.[2];
494
+ else {
495
+ const bound = /(?:^|\s)id\s*=\s*\{/.exec(attributes);
496
+ if (bound !== null) {
497
+ const open = bound.index + bound[0].length - 1;
498
+ id = attributes.slice(open + 1, closing(attributes, open));
499
+ } else if (/(?:^|\s)\{\s*id\s*\}/.test(attributes) || /(?:^|\s)id\s*=\s*(["'])[^"']*\{/.test(attributes)) id = "id";
500
+ }
501
+ if (id === void 0) continue;
502
+ const [statement] = parseSync("id.ts", `(${id})`).program.body;
503
+ if (!keySeen(statement?.type === "ExpressionStatement" ? statement.expression : void 0)) found.push({
504
+ at: m.index,
505
+ end: m.index + m[0].length
506
+ });
507
+ }
508
+ return found;
509
+ }
510
+ export { keyRefs as a, scriptBlocks as c, compileLocale as d, localeExpression as f, isReferenced as i, sourceFiles as l, SFC as n, looksLikeCode as o, localeModule as p, hardcodedText as r, scan as s, READERS as t, unseenReads as u };
386
511
 
387
- //# sourceMappingURL=scan-Bx0_yDSi.js.map
512
+ //# sourceMappingURL=scan-C0ap4Mpp.js.map