parseman 0.45.0 → 0.47.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 (333) hide show
  1. package/README.md +8 -3
  2. package/dist/analysis/choice-cost-gate.d.ts +158 -0
  3. package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
  4. package/dist/analysis/choice-cost-render.d.ts +96 -0
  5. package/dist/analysis/choice-cost-render.d.ts.map +1 -0
  6. package/dist/analysis/choice-cost.d.ts +383 -0
  7. package/dist/analysis/choice-cost.d.ts.map +1 -0
  8. package/dist/analysis/commitment.d.ts +131 -0
  9. package/dist/analysis/commitment.d.ts.map +1 -0
  10. package/dist/analysis/corpus.d.ts +73 -0
  11. package/dist/analysis/corpus.d.ts.map +1 -0
  12. package/dist/analysis/diagnose-render.d.ts +65 -0
  13. package/dist/analysis/diagnose-render.d.ts.map +1 -0
  14. package/dist/analysis/diagnose.d.ts +15 -0
  15. package/dist/analysis/diagnose.d.ts.map +1 -1
  16. package/dist/analysis/diagnostics.cjs +13736 -0
  17. package/dist/analysis/diagnostics.cjs.map +6 -0
  18. package/dist/analysis/diagnostics.d.ts +27 -0
  19. package/dist/analysis/diagnostics.d.ts.map +1 -0
  20. package/dist/analysis/diagnostics.js +13713 -0
  21. package/dist/analysis/diagnostics.js.map +6 -0
  22. package/dist/analysis/duplication.d.ts.map +1 -1
  23. package/dist/analysis/fix-render.d.ts +43 -0
  24. package/dist/analysis/fix-render.d.ts.map +1 -0
  25. package/dist/analysis/fix.d.ts +172 -0
  26. package/dist/analysis/fix.d.ts.map +1 -0
  27. package/dist/analysis/format-number.d.ts +23 -0
  28. package/dist/analysis/format-number.d.ts.map +1 -0
  29. package/dist/analysis/gating.d.ts +47 -2
  30. package/dist/analysis/gating.d.ts.map +1 -1
  31. package/dist/analysis/rebuild.d.ts +61 -0
  32. package/dist/analysis/rebuild.d.ts.map +1 -0
  33. package/dist/analysis/terminal.d.ts +145 -0
  34. package/dist/analysis/terminal.d.ts.map +1 -0
  35. package/dist/cli/index.cjs +14837 -0
  36. package/dist/cli/index.cjs.map +6 -0
  37. package/dist/cli/index.d.ts +3 -0
  38. package/dist/cli/index.d.ts.map +1 -0
  39. package/dist/cli/index.js +14814 -0
  40. package/dist/cli/index.js.map +6 -0
  41. package/dist/combinators/adjacency.d.ts +100 -0
  42. package/dist/combinators/adjacency.d.ts.map +1 -0
  43. package/dist/combinators/choice.d.ts.map +1 -1
  44. package/dist/combinators/completions.d.ts.map +1 -1
  45. package/dist/combinators/dispatch.d.ts +8 -1
  46. package/dist/combinators/dispatch.d.ts.map +1 -1
  47. package/dist/combinators/expect.d.ts.map +1 -1
  48. package/dist/combinators/first-set.d.ts.map +1 -1
  49. package/dist/combinators/gate.d.ts.map +1 -1
  50. package/dist/combinators/grammar.d.ts +22 -0
  51. package/dist/combinators/grammar.d.ts.map +1 -1
  52. package/dist/combinators/literal.d.ts +8 -0
  53. package/dist/combinators/literal.d.ts.map +1 -1
  54. package/dist/combinators/map.d.ts +0 -1
  55. package/dist/combinators/map.d.ts.map +1 -1
  56. package/dist/combinators/node.d.ts +13 -3
  57. package/dist/combinators/node.d.ts.map +1 -1
  58. package/dist/combinators/parser.d.ts +1 -1
  59. package/dist/combinators/parser.d.ts.map +1 -1
  60. package/dist/combinators/ref.d.ts.map +1 -1
  61. package/dist/combinators/regex.d.ts +1 -16
  62. package/dist/combinators/regex.d.ts.map +1 -1
  63. package/dist/combinators/repeat.d.ts +30 -2
  64. package/dist/combinators/repeat.d.ts.map +1 -1
  65. package/dist/combinators/scanTo.d.ts +71 -1
  66. package/dist/combinators/scanTo.d.ts.map +1 -1
  67. package/dist/combinators/sequence.d.ts.map +1 -1
  68. package/dist/combinators/token.d.ts.map +1 -1
  69. package/dist/combinators/trivia-boundary.d.ts +26 -0
  70. package/dist/combinators/trivia-boundary.d.ts.map +1 -0
  71. package/dist/combinators/trivia-skip.d.ts +40 -1
  72. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  73. package/dist/combinators/withCtx.d.ts.map +1 -1
  74. package/dist/compiler/build-arity.d.ts.map +1 -1
  75. package/dist/compiler/compile-linkable-table.d.ts +104 -0
  76. package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
  77. package/dist/compiler/degradation.d.ts +0 -2
  78. package/dist/compiler/degradation.d.ts.map +1 -1
  79. package/dist/compiler/fields.d.ts +13 -0
  80. package/dist/compiler/fields.d.ts.map +1 -1
  81. package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
  82. package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
  83. package/dist/compiler/ir-serialize.d.ts.map +1 -1
  84. package/dist/compiler/linker.d.ts +79 -47
  85. package/dist/compiler/linker.d.ts.map +1 -1
  86. package/dist/compiler/token-alphabet.d.ts +81 -0
  87. package/dist/compiler/token-alphabet.d.ts.map +1 -0
  88. package/dist/compiler/token-dispatch.d.ts +95 -0
  89. package/dist/compiler/token-dispatch.d.ts.map +1 -0
  90. package/dist/compiler/token-scanner.d.ts +53 -0
  91. package/dist/compiler/token-scanner.d.ts.map +1 -0
  92. package/dist/compiler/value-usage.d.ts.map +1 -1
  93. package/dist/coverage.d.ts +1 -1
  94. package/dist/coverage.d.ts.map +1 -1
  95. package/dist/cst/capture-buffer.d.ts +35 -0
  96. package/dist/cst/capture-buffer.d.ts.map +1 -1
  97. package/dist/cst/reflection-attach.d.ts +8 -0
  98. package/dist/cst/reflection-attach.d.ts.map +1 -0
  99. package/dist/cst/reflection-symbols.d.ts +10 -0
  100. package/dist/cst/reflection-symbols.d.ts.map +1 -0
  101. package/dist/cst/reflection.d.ts +2 -4
  102. package/dist/cst/reflection.d.ts.map +1 -1
  103. package/dist/cst/root-trivia-scope.d.ts +26 -0
  104. package/dist/cst/root-trivia-scope.d.ts.map +1 -0
  105. package/dist/cst/trivia-charscan.d.ts +18 -0
  106. package/dist/cst/trivia-charscan.d.ts.map +1 -0
  107. package/dist/cst/trivia-kinds.d.ts +19 -0
  108. package/dist/cst/trivia-kinds.d.ts.map +1 -1
  109. package/dist/functional/doc.d.ts.map +1 -1
  110. package/dist/functional/run-tabled.d.ts +12 -0
  111. package/dist/functional/run-tabled.d.ts.map +1 -0
  112. package/dist/functional/run.d.ts +25 -42
  113. package/dist/functional/run.d.ts.map +1 -1
  114. package/dist/grammar-metadata.d.ts +3 -0
  115. package/dist/grammar-metadata.d.ts.map +1 -0
  116. package/dist/index.cjs +10499 -7478
  117. package/dist/index.cjs.map +3 -4
  118. package/dist/index.d.ts +24 -9
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +10499 -7478
  121. package/dist/index.js.map +3 -4
  122. package/dist/language-service/index.cjs +311 -121
  123. package/dist/language-service/index.cjs.map +3 -4
  124. package/dist/language-service/index.js +311 -121
  125. package/dist/language-service/index.js.map +3 -4
  126. package/dist/oracle/index.cjs.map +0 -1
  127. package/dist/oracle/index.js.map +0 -1
  128. package/dist/parse-context.d.ts +49 -0
  129. package/dist/parse-context.d.ts.map +1 -0
  130. package/dist/plugin/evaluator.d.ts +41 -2
  131. package/dist/plugin/evaluator.d.ts.map +1 -1
  132. package/dist/plugin/free-identifiers.d.ts +23 -0
  133. package/dist/plugin/free-identifiers.d.ts.map +1 -0
  134. package/dist/plugin/index.cjs +11381 -9442
  135. package/dist/plugin/index.cjs.map +3 -4
  136. package/dist/plugin/index.d.ts.map +1 -1
  137. package/dist/plugin/index.js +11381 -9442
  138. package/dist/plugin/index.js.map +3 -4
  139. package/dist/plugin/reducer-resolver.d.ts +17 -1
  140. package/dist/plugin/reducer-resolver.d.ts.map +1 -1
  141. package/dist/regex/classes.d.ts +28 -0
  142. package/dist/regex/classes.d.ts.map +1 -1
  143. package/dist/regex/first-set.d.ts +14 -0
  144. package/dist/regex/first-set.d.ts.map +1 -1
  145. package/dist/run/index.cjs +136 -51
  146. package/dist/run/index.cjs.map +2 -3
  147. package/dist/run/index.js +136 -51
  148. package/dist/run/index.js.map +2 -3
  149. package/dist/spec/index.cjs +2898 -1622
  150. package/dist/spec/index.cjs.map +3 -4
  151. package/dist/spec/index.js +2898 -1622
  152. package/dist/spec/index.js.map +3 -4
  153. package/dist/spec/model.d.ts +8 -0
  154. package/dist/spec/model.d.ts.map +1 -1
  155. package/dist/table/assemble.d.ts +307 -0
  156. package/dist/table/assemble.d.ts.map +1 -0
  157. package/dist/table/cell.d.ts +42 -0
  158. package/dist/table/cell.d.ts.map +1 -0
  159. package/dist/table/child-slots.d.ts +30 -0
  160. package/dist/table/child-slots.d.ts.map +1 -0
  161. package/dist/table/compile-rule-map.d.ts +136 -0
  162. package/dist/table/compile-rule-map.d.ts.map +1 -0
  163. package/dist/table/compile.d.ts +56 -0
  164. package/dist/table/compile.d.ts.map +1 -0
  165. package/dist/table/duplication-hook.d.ts +32 -0
  166. package/dist/table/duplication-hook.d.ts.map +1 -0
  167. package/dist/table/emit-assembly.d.ts +139 -0
  168. package/dist/table/emit-assembly.d.ts.map +1 -0
  169. package/dist/table/emit.d.ts +113 -0
  170. package/dist/table/emit.d.ts.map +1 -0
  171. package/dist/table/encode-baseline.d.ts +16 -0
  172. package/dist/table/encode-baseline.d.ts.map +1 -0
  173. package/dist/table/encode.d.ts +71 -0
  174. package/dist/table/encode.d.ts.map +1 -0
  175. package/dist/table/exec-baseline.d.ts +9 -0
  176. package/dist/table/exec-baseline.d.ts.map +1 -0
  177. package/dist/table/exec.d.ts +72 -0
  178. package/dist/table/exec.d.ts.map +1 -0
  179. package/dist/table/fold.d.ts +25 -0
  180. package/dist/table/fold.d.ts.map +1 -0
  181. package/dist/table/index.cjs +11375 -0
  182. package/dist/table/index.cjs.map +6 -0
  183. package/dist/table/index.d.ts +100 -0
  184. package/dist/table/index.d.ts.map +1 -0
  185. package/dist/table/index.js +11352 -0
  186. package/dist/table/index.js.map +6 -0
  187. package/dist/table/inspect.d.ts +17 -0
  188. package/dist/table/inspect.d.ts.map +1 -0
  189. package/dist/table/ops.d.ts +446 -0
  190. package/dist/table/ops.d.ts.map +1 -0
  191. package/dist/table/program.d.ts +493 -0
  192. package/dist/table/program.d.ts.map +1 -0
  193. package/dist/table/run-support.d.ts +38 -0
  194. package/dist/table/run-support.d.ts.map +1 -0
  195. package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
  196. package/dist/table/scan-shapes.d.ts.map +1 -0
  197. package/dist/table/site-labels.d.ts +58 -0
  198. package/dist/table/site-labels.d.ts.map +1 -0
  199. package/dist/table/stamp.d.ts +70 -0
  200. package/dist/table/stamp.d.ts.map +1 -0
  201. package/dist/types.d.ts +80 -24
  202. package/dist/types.d.ts.map +1 -1
  203. package/dist/version.d.ts +1 -1
  204. package/package.json +41 -3
  205. package/src/analysis/choice-cost-gate.ts +359 -0
  206. package/src/analysis/choice-cost-render.ts +293 -0
  207. package/src/analysis/choice-cost.ts +1008 -0
  208. package/src/analysis/commitment.ts +557 -0
  209. package/src/analysis/corpus.ts +130 -0
  210. package/src/analysis/diagnose-render.ts +625 -0
  211. package/src/analysis/diagnose.ts +317 -0
  212. package/src/analysis/diagnostics.ts +33 -0
  213. package/src/analysis/duplication.ts +1955 -0
  214. package/src/analysis/fix-render.ts +217 -0
  215. package/src/analysis/fix.ts +669 -0
  216. package/src/analysis/format-number.ts +31 -0
  217. package/src/analysis/gating.ts +792 -0
  218. package/src/analysis/grammar.ts +72 -0
  219. package/src/analysis/rebuild.ts +287 -0
  220. package/src/analysis/terminal.ts +336 -0
  221. package/src/cli/index.ts +413 -0
  222. package/src/combinators/adjacency.ts +171 -0
  223. package/src/combinators/attempt.ts +41 -0
  224. package/src/combinators/case-fold.ts +90 -0
  225. package/src/combinators/choice.ts +466 -0
  226. package/src/combinators/completions.ts +64 -0
  227. package/src/combinators/dispatch.ts +358 -0
  228. package/src/combinators/expect.ts +159 -0
  229. package/src/combinators/first-set.ts +322 -0
  230. package/src/combinators/gate.ts +50 -0
  231. package/src/combinators/grammar.ts +272 -0
  232. package/src/combinators/keywords.ts +145 -0
  233. package/src/combinators/literal.ts +102 -0
  234. package/src/combinators/map.ts +106 -0
  235. package/src/combinators/node.ts +345 -0
  236. package/src/combinators/not.ts +55 -0
  237. package/src/combinators/parser.ts +266 -0
  238. package/src/combinators/peek.ts +63 -0
  239. package/src/combinators/probe.ts +25 -0
  240. package/src/combinators/ref.ts +109 -0
  241. package/src/combinators/regex.ts +170 -0
  242. package/src/combinators/repeat.ts +561 -0
  243. package/src/combinators/scanTo.ts +393 -0
  244. package/src/combinators/sequence.ts +221 -0
  245. package/src/combinators/token.ts +120 -0
  246. package/src/combinators/trivia-boundary.ts +48 -0
  247. package/src/combinators/trivia-skip.ts +821 -0
  248. package/src/combinators/withCtx.ts +45 -0
  249. package/src/compiler/build-arity.ts +330 -0
  250. package/src/compiler/compile-linkable-table.ts +209 -0
  251. package/src/compiler/degradation.ts +266 -0
  252. package/src/compiler/fields.ts +191 -0
  253. package/src/compiler/grammar-coverage-ids.ts +129 -0
  254. package/src/compiler/ir-serialize.ts +615 -0
  255. package/src/compiler/linker.ts +954 -0
  256. package/src/compiler/token-alphabet.ts +209 -0
  257. package/src/compiler/token-dispatch.ts +353 -0
  258. package/src/compiler/token-scanner.ts +318 -0
  259. package/src/compiler/value-usage.ts +147 -0
  260. package/src/coverage.ts +383 -0
  261. package/src/cst/capture-buffer.ts +297 -0
  262. package/src/cst/host-mode.ts +69 -0
  263. package/src/cst/reflection-attach.ts +11 -0
  264. package/src/cst/reflection-symbols.ts +9 -0
  265. package/src/cst/reflection.ts +149 -0
  266. package/src/cst/relative-spans.ts +207 -0
  267. package/src/cst/root-trivia-scope.ts +30 -0
  268. package/src/cst/trivia-charscan.ts +312 -0
  269. package/src/cst/trivia-entries.ts +285 -0
  270. package/src/cst/trivia-index.ts +131 -0
  271. package/src/cst/trivia-kinds.ts +358 -0
  272. package/src/cst/types.ts +76 -0
  273. package/src/cst/walk.ts +101 -0
  274. package/src/functional/doc.ts +959 -0
  275. package/src/functional/run-tabled.ts +153 -0
  276. package/src/functional/run.ts +558 -0
  277. package/src/grammar-metadata.ts +2 -0
  278. package/src/index.ts +189 -0
  279. package/src/language-service/index.ts +221 -0
  280. package/src/line-index.ts +131 -0
  281. package/src/oracle/digest.ts +620 -0
  282. package/src/oracle/index.ts +55 -0
  283. package/src/parse-context.ts +91 -0
  284. package/src/plugin/direct-builder-static.ts +102 -0
  285. package/src/plugin/evaluator.ts +1505 -0
  286. package/src/plugin/free-identifiers.ts +338 -0
  287. package/src/plugin/index.ts +2281 -0
  288. package/src/plugin/reducer-resolver.ts +606 -0
  289. package/src/recovery/scan.ts +155 -0
  290. package/src/regex/classes.ts +183 -0
  291. package/src/regex/first-set.ts +316 -0
  292. package/src/run/index.ts +31 -0
  293. package/src/spec/ebnf.ts +128 -0
  294. package/src/spec/index.ts +54 -0
  295. package/src/spec/model.ts +636 -0
  296. package/src/spec/railroad-lib.ts +559 -0
  297. package/src/spec/railroad.ts +256 -0
  298. package/src/table/assemble.ts +2994 -0
  299. package/src/table/cell.ts +42 -0
  300. package/src/table/child-slots.ts +113 -0
  301. package/src/table/compile-rule-map.ts +336 -0
  302. package/src/table/compile.ts +244 -0
  303. package/src/table/duplication-hook.ts +89 -0
  304. package/src/table/emit-assembly.ts +1766 -0
  305. package/src/table/emit.ts +418 -0
  306. package/src/table/encode-baseline.ts +340 -0
  307. package/src/table/encode.ts +1569 -0
  308. package/src/table/exec-baseline.ts +537 -0
  309. package/src/table/exec.ts +1672 -0
  310. package/src/table/fold.ts +38 -0
  311. package/src/table/index.ts +108 -0
  312. package/src/table/inspect.ts +52 -0
  313. package/src/table/ops.ts +459 -0
  314. package/src/table/program.ts +855 -0
  315. package/src/table/run-support.ts +66 -0
  316. package/src/table/scan-shapes.ts +1578 -0
  317. package/src/table/site-labels.ts +228 -0
  318. package/src/table/stamp.ts +229 -0
  319. package/src/types.ts +563 -0
  320. package/src/version.ts +16 -0
  321. package/dist/compiler/codegen.d.ts +0 -334
  322. package/dist/compiler/codegen.d.ts.map +0 -1
  323. package/dist/compiler/inline-build.d.ts +0 -11
  324. package/dist/compiler/inline-build.d.ts.map +0 -1
  325. package/dist/compiler/inline-callback.d.ts +0 -21
  326. package/dist/compiler/inline-callback.d.ts.map +0 -1
  327. package/dist/compiler/line-index.d.ts +0 -2
  328. package/dist/compiler/line-index.d.ts.map +0 -1
  329. package/dist/compiler/scannable-run.d.ts.map +0 -1
  330. package/dist/compiler/scannable-terminal.d.ts +0 -23
  331. package/dist/compiler/scannable-terminal.d.ts.map +0 -1
  332. package/dist/compiler/trivia-fast-path.d.ts +0 -38
  333. package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
@@ -0,0 +1,338 @@
1
+ /**
2
+ * Emit-time scope check: does the module parseman is about to hand back name
3
+ * anything that nothing binds?
4
+ *
5
+ * Lowering DELETES code — the `rules(...)` call sites, the `x.define(...)`
6
+ * statements, and finally the macro import itself. Every one of those deletions
7
+ * is only safe because the text that referenced the deleted binding was deleted
8
+ * too. When a shape slips through where it wasn't, the artifact still builds,
9
+ * still imports, and throws `ReferenceError` the first time a consumer touches
10
+ * the binding — in the consumer's process, long after publish.
11
+ *
12
+ * The specific shapes are refused where they are recognised (an exported
13
+ * `rules()` factory is thrown on in index.ts before we ever get here). This is
14
+ * the net beneath them: it does not know or care HOW a name escaped, only that
15
+ * the emitted module reads it and nothing in the module binds it.
16
+ *
17
+ * Two deliberate design points:
18
+ *
19
+ * 1. SCOPE ANALYSIS, NOT GREP. A text scan for the macro names reports `node`,
20
+ * `rule`, `not` and `field` in hand-written host modules — they are ordinary
21
+ * parameter and property names — and a check that cries wolf is a check
22
+ * people learn to pass with a comment. Only an identifier in a READ position
23
+ * with no binding counts.
24
+ *
25
+ * 2. FLAT BINDINGS, NOT A SCOPE TREE. A name is considered bound if ANYTHING in
26
+ * the module binds it, in any scope. That is deliberately weaker than real
27
+ * scope resolution: it cannot see a name that is bound in a sibling scope and
28
+ * free in this one. It is also structurally incapable of the false positive
29
+ * that would make this check worthless, and the failure it exists to catch —
30
+ * a name whose ONLY binding was the deleted import — is caught exactly.
31
+ *
32
+ * Build-time only. Nothing here runs in, or is emitted into, the artifact.
33
+ */
34
+ import { parseSync } from 'oxc-parser'
35
+
36
+ export type FreeIdentifier = {
37
+ /** The identifier that is read but bound by nothing. */
38
+ name: string
39
+ /** 1-based line in the EMITTED module. */
40
+ line: number
41
+ /** 1-based column in the emitted module. */
42
+ column: number
43
+ /**
44
+ * The top-level declaration the read sits inside — `export const cssFactory`,
45
+ * `function build`, … — or null if it is at statement level. This is what turns
46
+ * "free identifier `sequence`" into a place to go and look.
47
+ */
48
+ enclosing: string | null
49
+ }
50
+
51
+ /** Per-top-level-statement facts, for the reachability filter. */
52
+ type TopLevel = {
53
+ /** Names this statement binds at module level. */
54
+ declared: Set<string>
55
+ /** `export`ed, so the module system can reach it however unreferenced it looks. */
56
+ exported: boolean
57
+ /** A binding whose value is only produced by CALLING it — a function/arrow initializer. */
58
+ declaresOnlyFunctions: boolean
59
+ label: string | null
60
+ }
61
+
62
+ type AnyNode = { type: string } & Record<string, unknown>
63
+
64
+ const isNode = (v: unknown): v is AnyNode =>
65
+ typeof v === 'object' && v !== null && typeof (v as { type?: unknown }).type === 'string'
66
+
67
+ /**
68
+ * Type-land keys. The emitted module is TypeScript, and a type annotation names
69
+ * types — `Combinator`, `Record`, `HostMode` — which are not value bindings and
70
+ * must never be read as references.
71
+ */
72
+ const TYPE_KEYS = new Set([
73
+ 'typeAnnotation', 'returnType', 'typeParameters', 'typeArguments',
74
+ 'superTypeArguments', 'typeName', 'implements', 'extends', 'accessibility',
75
+ ])
76
+
77
+ /**
78
+ * TS wrapper nodes that DO contain a real value expression. Everything else whose
79
+ * type starts with `TS` is pure type-land and is skipped whole.
80
+ */
81
+ const TS_VALUE_WRAPPERS = new Set([
82
+ 'TSAsExpression', 'TSSatisfiesExpression', 'TSNonNullExpression',
83
+ 'TSInstantiationExpression', 'TSTypeAssertion',
84
+ ])
85
+
86
+ /**
87
+ * Names that are legitimately free in a module: the host environment provides
88
+ * them. Taken from the live `globalThis` rather than a hand-kept list — the check
89
+ * runs in the build's own Node process, so this is the set that actually exists —
90
+ * plus the implicit bindings and the browser/CJS names a Node build host lacks but
91
+ * a published artifact may legitimately expect.
92
+ */
93
+ const HOST_GLOBALS = new Set<string>([
94
+ ...Object.getOwnPropertyNames(globalThis),
95
+ 'undefined', 'NaN', 'Infinity', 'globalThis', 'arguments',
96
+ 'window', 'document', 'self', 'navigator', 'location', 'top', 'parent',
97
+ 'require', 'module', 'exports', '__dirname', '__filename',
98
+ ])
99
+
100
+ /** Collect every name the pattern BINDS. Default values inside it are references, not bindings. */
101
+ function bindPattern(pattern: unknown, out: Set<string>): void {
102
+ if (!isNode(pattern)) return
103
+ switch (pattern.type) {
104
+ case 'Identifier':
105
+ case 'BindingIdentifier':
106
+ out.add(String(pattern.name))
107
+ return
108
+ case 'ObjectPattern':
109
+ for (const prop of (pattern.properties as unknown[]) ?? []) {
110
+ if (!isNode(prop)) continue
111
+ if (prop.type === 'RestElement' || prop.type === 'BindingRestElement') bindPattern(prop.argument, out)
112
+ else bindPattern(prop.value, out)
113
+ }
114
+ return
115
+ case 'ArrayPattern':
116
+ for (const el of (pattern.elements as unknown[]) ?? []) bindPattern(el, out)
117
+ return
118
+ case 'AssignmentPattern':
119
+ bindPattern(pattern.left, out)
120
+ return
121
+ case 'RestElement':
122
+ case 'BindingRestElement':
123
+ bindPattern(pattern.argument, out)
124
+ return
125
+ case 'TSParameterProperty':
126
+ bindPattern(pattern.parameter, out)
127
+ return
128
+ default:
129
+ return
130
+ }
131
+ }
132
+
133
+ /** Every name bound ANYWHERE in the module, in any scope. See the header for why flat. */
134
+ function collectBindings(node: unknown, out: Set<string>): void {
135
+ if (Array.isArray(node)) {
136
+ for (const child of node) collectBindings(child, out)
137
+ return
138
+ }
139
+ if (!isNode(node)) return
140
+
141
+ switch (node.type) {
142
+ case 'ImportSpecifier':
143
+ case 'ImportDefaultSpecifier':
144
+ case 'ImportNamespaceSpecifier':
145
+ if (isNode(node.local)) out.add(String(node.local.name))
146
+ break
147
+ case 'VariableDeclarator':
148
+ bindPattern(node.id, out)
149
+ break
150
+ case 'FunctionDeclaration':
151
+ case 'FunctionExpression':
152
+ case 'TSDeclareFunction':
153
+ case 'ArrowFunctionExpression':
154
+ if (isNode(node.id)) out.add(String(node.id.name))
155
+ for (const p of ((node.params as AnyNode | undefined)?.items as unknown[] | undefined)
156
+ ?? (node.params as unknown[] | undefined) ?? []) bindPattern(p, out)
157
+ if (isNode(node.params) && node.params.rest) bindPattern(node.params.rest, out)
158
+ break
159
+ case 'ClassDeclaration':
160
+ case 'ClassExpression':
161
+ if (isNode(node.id)) out.add(String(node.id.name))
162
+ break
163
+ case 'CatchClause':
164
+ if (node.param) {
165
+ // oxc wraps it: CatchParameter { pattern }. ESTree puts the pattern directly.
166
+ bindPattern(isNode(node.param) && node.param.pattern ? node.param.pattern : node.param, out)
167
+ }
168
+ break
169
+ default:
170
+ break
171
+ }
172
+
173
+ for (const key of Object.keys(node)) {
174
+ if (key === 'type' || key === 'start' || key === 'end') continue
175
+ collectBindings(node[key], out)
176
+ }
177
+ }
178
+
179
+ const FUNCTION_TYPES = new Set(['ArrowFunctionExpression', 'FunctionExpression', 'FunctionDeclaration'])
180
+
181
+ /** What a top-level statement declares, exports, and is called, for the diagnostic and the filter. */
182
+ function describeTopLevel(stmt: AnyNode): TopLevel {
183
+ const declared = new Set<string>()
184
+ let target: AnyNode = stmt
185
+ let prefix = ''
186
+ let exported = false
187
+ if (stmt.type === 'ExportNamedDeclaration' || stmt.type === 'ExportDefaultDeclaration') {
188
+ exported = true
189
+ prefix = 'export '
190
+ if (!isNode(stmt.declaration)) return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}{ … }` }
191
+ target = stmt.declaration
192
+ }
193
+
194
+ if (target.type === 'VariableDeclaration') {
195
+ const decls = ((target.declarations as unknown[]) ?? []).filter(isNode)
196
+ for (const d of decls) bindPattern(d.id, declared)
197
+ const declaresOnlyFunctions = decls.length > 0
198
+ && decls.every(d => isNode(d.init) && FUNCTION_TYPES.has(d.init.type))
199
+ const name = [...declared][0]
200
+ return {
201
+ declared, exported, declaresOnlyFunctions,
202
+ label: name ? `${prefix}${String(target.kind)} ${name}` : null,
203
+ }
204
+ }
205
+ if (target.type === 'FunctionDeclaration' && isNode(target.id)) {
206
+ declared.add(String(target.id.name))
207
+ return { declared, exported, declaresOnlyFunctions: true, label: `${prefix}function ${String(target.id.name)}` }
208
+ }
209
+ if (target.type === 'ClassDeclaration' && isNode(target.id)) {
210
+ declared.add(String(target.id.name))
211
+ return { declared, exported, declaresOnlyFunctions: false, label: `${prefix}class ${String(target.id.name)}` }
212
+ }
213
+ return { declared, exported, declaresOnlyFunctions: false, label: null }
214
+ }
215
+
216
+ /**
217
+ * Every identifier the emitted module READS but nothing binds.
218
+ *
219
+ * Returns an empty array when the module can't be parsed: a check that can't see
220
+ * the code has nothing to say about it, and refusing to emit on a parse failure
221
+ * would turn this into a second, worse syntax error.
222
+ */
223
+ export function findFreeIdentifiers(code: string, filename: string): FreeIdentifier[] {
224
+ let parsed: ReturnType<typeof parseSync>
225
+ try {
226
+ parsed = parseSync(filename, code)
227
+ } catch {
228
+ return []
229
+ }
230
+ if (parsed.errors.length > 0) return []
231
+
232
+ const bound = new Set<string>()
233
+ collectBindings(parsed.program.body, bound)
234
+
235
+ // Line starts, so a byte offset becomes line:column without rescanning the file.
236
+ const lineStarts = [0]
237
+ for (let i = 0; i < code.length; i++) if (code.charCodeAt(i) === 10) lineStarts.push(i + 1)
238
+ const positionOf = (offset: number): { line: number; column: number } => {
239
+ let lo = 0
240
+ let hi = lineStarts.length - 1
241
+ while (lo < hi) {
242
+ const mid = (lo + hi + 1) >> 1
243
+ if (lineStarts[mid]! <= offset) lo = mid
244
+ else hi = mid - 1
245
+ }
246
+ return { line: lo + 1, column: offset - lineStarts[lo]! + 1 }
247
+ }
248
+
249
+ const statements = (parsed.program.body as unknown[]).filter(isNode)
250
+ const tops = statements.map(describeTopLevel)
251
+
252
+ type Candidate = FreeIdentifier & { stmt: number; insideFunction: boolean }
253
+ const candidates: Candidate[] = []
254
+ const seen = new Set<string>()
255
+ /** name -> indices of the top-level statements that READ it. Drives the reachability filter. */
256
+ const readIn = new Map<string, Set<number>>()
257
+
258
+ const visit = (node: unknown, stmt: number, insideFunction: boolean): void => {
259
+ if (Array.isArray(node)) {
260
+ for (const child of node) visit(child, stmt, insideFunction)
261
+ return
262
+ }
263
+ if (!isNode(node)) return
264
+
265
+ const type = node.type
266
+
267
+ // Import declarations bind; nothing in them reads.
268
+ if (type === 'ImportDeclaration' || type === 'ExportAllDeclaration') return
269
+ // `import.meta` / `new.target` — neither half is an identifier reference.
270
+ if (type === 'MetaProperty' || type === 'PrivateIdentifier') return
271
+ // `export { x } from './m'` re-exports; `x` is the OTHER module's name, not a local read.
272
+ if (type === 'ExportNamedDeclaration' && node.source) return
273
+ // Type-land. The wrappers carry a real expression; everything else is types only.
274
+ if (type.startsWith('TS')) {
275
+ if (TS_VALUE_WRAPPERS.has(type)) visit(node.expression, stmt, insideFunction)
276
+ return
277
+ }
278
+
279
+ if (type === 'Identifier' || type === 'IdentifierReference') {
280
+ const name = String(node.name)
281
+ let readers = readIn.get(name)
282
+ if (!readers) readIn.set(name, readers = new Set())
283
+ readers.add(stmt)
284
+ if (bound.has(name) || HOST_GLOBALS.has(name)) return
285
+ const offset = typeof node.start === 'number' ? node.start : 0
286
+ const { line, column } = positionOf(offset)
287
+ const key = `${name}@${line}:${column}`
288
+ if (seen.has(key)) return
289
+ seen.add(key)
290
+ candidates.push({ name, line, column, enclosing: tops[stmt]!.label, stmt, insideFunction })
291
+ return
292
+ }
293
+
294
+ const nowInsideFunction = insideFunction || FUNCTION_TYPES.has(type)
295
+
296
+ for (const key of Object.keys(node)) {
297
+ if (key === 'type' || key === 'start' || key === 'end') continue
298
+ if (TYPE_KEYS.has(key)) continue
299
+
300
+ // A non-computed key is a NAME, not a read: `{ node: 1 }`, `x.field`, `class { rule() {} }`.
301
+ if (key === 'key' && node.computed !== true) continue
302
+ if (key === 'property' && node.computed !== true) continue
303
+ // Labels: `outer: for (…) { break outer }` — neither position is a value.
304
+ if (key === 'label') continue
305
+ // `export { local as exported }` — `exported` is a name in the export list.
306
+ if (key === 'exported') continue
307
+ // `import { imported as local }` is already returned above; belt and braces.
308
+ if (key === 'imported') continue
309
+
310
+ visit(node[key], stmt, nowInsideFunction)
311
+ }
312
+ }
313
+
314
+ for (let i = 0; i < statements.length; i++) visit(statements[i], i, false)
315
+
316
+ /*
317
+ * REACHABILITY. A `const factory = (g) => ({ node(…) })` that lowering left as text is
318
+ * not a runtime failure and must not be reported: it is not exported, nothing else in
319
+ * the module names it, and its free names sit inside a function body that is therefore
320
+ * never entered — dead code a bundler drops and an unbundled module simply never
321
+ * evaluates. That is the documented two-artifact pattern (a LOCAL factory const), and
322
+ * it is exactly the contrast that makes the exported case fatal.
323
+ *
324
+ * All three conditions are load-bearing. Drop `insideFunction` and `const x = node(…)`
325
+ * at statement level stops being caught, though it throws the moment the module is
326
+ * imported. Drop the unreferenced test and a factory something still calls is missed.
327
+ * Drop `exported` and the shape 8995b1c refuses walks straight through.
328
+ */
329
+ return candidates.filter(c => {
330
+ const top = tops[c.stmt]!
331
+ if (!c.insideFunction || top.exported || !top.declaresOnlyFunctions || top.declared.size === 0) return true
332
+ for (const name of top.declared) {
333
+ const readers = readIn.get(name)
334
+ if (readers && [...readers].some(i => i !== c.stmt)) return true
335
+ }
336
+ return false
337
+ }).map(({ name, line, column, enclosing }) => ({ name, line, column, enclosing }))
338
+ }