@cplieger/deadset-ts 8.0.0-dev.2 → 8.0.0-dev.20

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 (402) hide show
  1. package/README.md +3 -1
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/dist/bin/mapper-connection.js +2 -1
  4. package/dist/bin/node-host.js +24 -5
  5. package/dist/src/alias-chain.js +2 -7
  6. package/dist/src/amd.js +81 -0
  7. package/dist/src/analysis.js +269 -432
  8. package/dist/src/augmentations.js +64 -3
  9. package/dist/src/automatic-types.js +10 -29
  10. package/dist/src/bindings.js +382 -0
  11. package/dist/src/calls.js +279 -43
  12. package/dist/src/commands/held.js +499 -0
  13. package/dist/src/commands/read.js +161 -0
  14. package/dist/src/commands/shell.js +169 -0
  15. package/dist/src/commands/tools.js +1847 -0
  16. package/dist/src/component-files.js +3 -2
  17. package/dist/src/component-layer.js +4 -2
  18. package/dist/src/config.js +6 -82
  19. package/dist/src/conformance-record.js +1 -1
  20. package/dist/src/consumers.js +10 -38
  21. package/dist/src/contextual-uses.js +57 -26
  22. package/dist/src/contract/config.generated.js +561 -0
  23. package/dist/src/contract/contract.generated.js +1369 -0
  24. package/dist/src/contract/report.generated.js +866 -0
  25. package/dist/src/dependencies.js +164 -121
  26. package/dist/src/destructuring.js +1 -19
  27. package/dist/src/edges.js +38 -122
  28. package/dist/src/element-access.js +181 -168
  29. package/dist/src/entries/build-scripts.js +222 -0
  30. package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
  31. package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
  32. package/dist/src/entries/conventions.js +740 -0
  33. package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
  34. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
  35. package/dist/src/entries/run-directories.js +77 -0
  36. package/dist/src/entries/sources.js +144 -0
  37. package/dist/src/entries/static-module.js +670 -0
  38. package/dist/src/entries/test-runners.js +650 -0
  39. package/dist/src/entries/workflow-steps.js +345 -0
  40. package/dist/src/enum-group.js +3 -10
  41. package/dist/src/erased-stores.js +4 -6
  42. package/dist/src/exempt-classes.js +1 -63
  43. package/dist/src/exempt.js +21 -66
  44. package/dist/src/exposure.js +400 -0
  45. package/dist/src/extensions.js +37 -0
  46. package/dist/src/facts.js +38 -0
  47. package/dist/src/failure.js +36 -0
  48. package/dist/src/file-facts.js +3 -26
  49. package/dist/src/findings/completion.js +109 -56
  50. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  51. package/dist/src/findings/emitters.js +27 -41
  52. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  53. package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
  54. package/dist/src/findings/interfaces.js +4 -5
  55. package/dist/src/findings/intra-function.js +16 -13
  56. package/dist/src/findings/non-code-artifacts.js +17 -12
  57. package/dist/src/findings/reads-and-writes.js +86 -27
  58. package/dist/src/findings/self-check.js +44 -30
  59. package/dist/src/findings/unused-declarations.js +29 -45
  60. package/dist/src/findings/visibility-narrowing.js +23 -31
  61. package/dist/src/findings-pass.js +13 -21
  62. package/dist/src/framework-lifecycle.js +46 -19
  63. package/dist/src/fs/document.js +52 -0
  64. package/dist/src/fs/manifest.js +67 -0
  65. package/dist/src/fs/tree.js +88 -0
  66. package/dist/src/generated-file.js +174 -28
  67. package/dist/src/git-ignore.js +39 -26
  68. package/dist/src/import-bindings.js +73 -28
  69. package/dist/src/index/sites.js +557 -0
  70. package/dist/src/index.js +2 -2
  71. package/dist/src/indexed-access.js +523 -478
  72. package/dist/src/injection-container.js +2 -2
  73. package/dist/src/interface-satisfaction.js +1 -1
  74. package/dist/src/intra-function-parts.js +63 -43
  75. package/dist/src/inventory/commonjs.js +507 -0
  76. package/dist/src/inventory/enumerate.js +167 -0
  77. package/dist/src/inventory/syntax.js +236 -0
  78. package/dist/src/inventory.js +323 -724
  79. package/dist/src/javascript-file.js +48 -0
  80. package/dist/src/json-document.js +198 -67
  81. package/dist/src/keyof-comparisons.js +126 -107
  82. package/dist/src/kinds.js +3 -366
  83. package/dist/src/{components.js → liveness/components.js} +2 -3
  84. package/dist/src/{graph.js → liveness/graph.js} +52 -28
  85. package/dist/src/liveness/matrix.js +99 -0
  86. package/dist/src/liveness/sweep.js +558 -0
  87. package/dist/src/load/classify.js +289 -0
  88. package/dist/src/load/diagnostics.js +87 -0
  89. package/dist/src/load/discover.js +285 -0
  90. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  91. package/dist/src/load/expected-module.js +136 -0
  92. package/dist/src/load/load.js +27 -0
  93. package/dist/src/load/module-lookup.js +37 -0
  94. package/dist/src/load/package-scope.js +56 -0
  95. package/dist/src/load/plan.js +313 -0
  96. package/dist/src/load/specifiers.js +113 -0
  97. package/dist/src/load/synthesize.js +152 -0
  98. package/dist/src/load/test-globals.js +207 -0
  99. package/dist/src/load/test-joins.js +82 -0
  100. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  101. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  102. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
  103. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  104. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  105. package/dist/src/module-declarations.js +70 -85
  106. package/dist/src/overrides.js +134 -112
  107. package/dist/src/partial.js +2 -1
  108. package/dist/src/paths.js +82 -16
  109. package/dist/src/platform-calls.js +78 -0
  110. package/dist/src/platform-symbols.js +134 -0
  111. package/dist/src/position.js +43 -1
  112. package/dist/src/print.js +11 -79
  113. package/dist/src/product.js +13 -0
  114. package/dist/src/query.js +47 -0
  115. package/dist/src/ref.js +12 -20
  116. package/dist/src/references/read-rule.js +1 -0
  117. package/dist/src/references/read-rules.js +48 -0
  118. package/dist/src/references/sink.js +237 -0
  119. package/dist/src/references/sources.js +450 -0
  120. package/dist/src/references.js +117 -1074
  121. package/dist/src/report-schema.js +51 -0
  122. package/dist/src/report.js +57 -41
  123. package/dist/src/reporters/annotations.js +15 -8
  124. package/dist/src/reporters/sarif.js +16 -10
  125. package/dist/src/reporters/template.js +4 -19
  126. package/dist/src/resolve.js +246 -730
  127. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
  128. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
  129. package/dist/src/roots/manifest.js +216 -0
  130. package/dist/src/roots/published-reach.js +412 -0
  131. package/dist/src/roots/root-set.js +487 -0
  132. package/dist/src/roots/unseen-loads.js +236 -0
  133. package/dist/src/run.js +66 -77
  134. package/dist/src/schema.js +320 -343
  135. package/dist/src/scope.js +38 -115
  136. package/dist/src/serialization-contract.js +601 -22
  137. package/dist/src/session.js +37 -29
  138. package/dist/src/source-files.js +6 -10
  139. package/dist/src/stores.js +4 -2
  140. package/dist/src/suppress-file.js +25 -28
  141. package/dist/src/suppress-inline.js +36 -83
  142. package/dist/src/suppress.js +50 -71
  143. package/dist/src/symbol-keys.js +92 -88
  144. package/dist/src/template-field.js +9 -19
  145. package/dist/src/tests/file-class.js +76 -0
  146. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  147. package/dist/src/{test-support.js → tests/support.js} +5 -12
  148. package/dist/src/text/compare.js +26 -0
  149. package/dist/src/text/glob.js +194 -0
  150. package/dist/src/text/named-files.js +298 -0
  151. package/dist/src/text/yaml.js +684 -0
  152. package/dist/src/type-errors.js +11 -232
  153. package/dist/src/type-parameter-reads.js +178 -139
  154. package/dist/src/unimplemented-callees.js +262 -242
  155. package/dist/src/unwrap.js +23 -0
  156. package/dist/src/usage.js +10 -0
  157. package/dist/src/vendor/unbash/ansi-c.js +132 -0
  158. package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
  159. package/dist/src/vendor/unbash/arithmetic.js +591 -0
  160. package/dist/src/vendor/unbash/assignment.js +131 -0
  161. package/dist/src/vendor/unbash/chars.js +48 -0
  162. package/dist/src/vendor/unbash/command.js +41 -0
  163. package/dist/src/vendor/unbash/expansion.js +80 -0
  164. package/dist/src/vendor/unbash/glob.js +268 -0
  165. package/dist/src/vendor/unbash/heredoc.js +27 -0
  166. package/dist/src/vendor/unbash/internal-types.js +1 -0
  167. package/dist/src/vendor/unbash/lexer.js +4274 -0
  168. package/dist/src/vendor/unbash/limits.js +3 -0
  169. package/dist/src/vendor/unbash/parser.js +1366 -0
  170. package/dist/src/vendor/unbash/parts.js +57 -0
  171. package/dist/src/vendor/unbash/substitution-context.js +8 -0
  172. package/dist/src/vendor/unbash/traversal.js +212 -0
  173. package/dist/src/vendor/unbash/types.js +1 -0
  174. package/dist/src/vendor/unbash/word-value.js +61 -0
  175. package/dist/src/vendor/unbash/word.js +40 -0
  176. package/dist/src/verbs/analyze.js +194 -127
  177. package/dist/src/verbs/describe.js +13 -18
  178. package/dist/src/verbs/explain.js +85 -139
  179. package/dist/src/verbs/print-config.js +2 -1
  180. package/dist/src/verbs/print-projects.js +6 -4
  181. package/dist/src/verbs/print-retained.js +7 -5
  182. package/dist/src/verbs/print-roots.js +4 -2
  183. package/dist/src/verbs/verb.js +1 -5
  184. package/dist/src/verbs/version.js +14 -3
  185. package/jsr.json +2 -1
  186. package/package.json +14 -8
  187. package/src/alias-chain.ts +2 -9
  188. package/src/amd.ts +130 -0
  189. package/src/analysis.ts +424 -650
  190. package/src/augmentations.ts +93 -4
  191. package/src/automatic-types.ts +12 -30
  192. package/src/bindings.ts +460 -0
  193. package/src/calls.ts +338 -46
  194. package/src/commands/held.ts +632 -0
  195. package/src/commands/read.ts +242 -0
  196. package/src/commands/shell.ts +228 -0
  197. package/src/commands/tools.ts +2382 -0
  198. package/src/component-files.ts +4 -2
  199. package/src/component-layer.ts +4 -2
  200. package/src/config.ts +31 -302
  201. package/src/conformance-record.ts +1 -1
  202. package/src/consumers.ts +16 -66
  203. package/src/contextual-uses.ts +66 -34
  204. package/src/contract/config.generated.ts +821 -0
  205. package/src/contract/contract.generated.ts +1452 -0
  206. package/src/contract/report.generated.ts +1241 -0
  207. package/src/dependencies.ts +222 -127
  208. package/src/destructuring.ts +1 -31
  209. package/src/edges.ts +41 -140
  210. package/src/element-access.ts +190 -187
  211. package/src/entries/build-scripts.ts +292 -0
  212. package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
  213. package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
  214. package/src/{conventions.ts → entries/conventions.ts} +367 -583
  215. package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
  216. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
  217. package/src/entries/run-directories.ts +108 -0
  218. package/src/entries/sources.ts +266 -0
  219. package/src/entries/static-module.ts +858 -0
  220. package/src/entries/test-runners.ts +835 -0
  221. package/src/entries/workflow-steps.ts +432 -0
  222. package/src/enum-group.ts +2 -11
  223. package/src/erased-stores.ts +3 -7
  224. package/src/exempt-classes.ts +1 -75
  225. package/src/exempt.ts +70 -98
  226. package/src/exposure.ts +492 -0
  227. package/src/extensions.ts +45 -0
  228. package/src/facts.ts +46 -0
  229. package/src/failure.ts +63 -0
  230. package/src/file-facts.ts +15 -25
  231. package/src/finding.ts +10 -10
  232. package/src/findings/boundary.ts +11 -0
  233. package/src/findings/completion.ts +133 -64
  234. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  235. package/src/findings/emitter.ts +9 -10
  236. package/src/findings/emitters.ts +38 -52
  237. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  238. package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
  239. package/src/findings/interfaces.ts +7 -9
  240. package/src/findings/intra-function.ts +16 -14
  241. package/src/findings/non-code-artifacts.ts +24 -15
  242. package/src/findings/reads-and-writes.ts +118 -41
  243. package/src/findings/self-check.ts +51 -38
  244. package/src/findings/unused-declarations.ts +31 -49
  245. package/src/findings/visibility-narrowing.ts +36 -40
  246. package/src/findings-pass.ts +15 -31
  247. package/src/framework-lifecycle.ts +47 -19
  248. package/src/fs/document.ts +70 -0
  249. package/src/fs/manifest.ts +96 -0
  250. package/src/fs/tree.ts +110 -0
  251. package/src/generated-file.ts +242 -35
  252. package/src/git-ignore.ts +50 -26
  253. package/src/host.ts +12 -5
  254. package/src/import-bindings.ts +98 -29
  255. package/src/index/sites.ts +781 -0
  256. package/src/index.ts +2 -2
  257. package/src/indexed-access.ts +570 -527
  258. package/src/injection-container.ts +2 -2
  259. package/src/interface-satisfaction.ts +2 -2
  260. package/src/intra-function-parts.ts +73 -44
  261. package/src/inventory/commonjs.ts +696 -0
  262. package/src/inventory/enumerate.ts +203 -0
  263. package/src/inventory/syntax.ts +311 -0
  264. package/src/inventory.ts +442 -895
  265. package/src/javascript-file.ts +54 -0
  266. package/src/json-document.ts +241 -76
  267. package/src/keyof-comparisons.ts +133 -122
  268. package/src/kinds.ts +5 -413
  269. package/src/{components.ts → liveness/components.ts} +4 -6
  270. package/src/{graph.ts → liveness/graph.ts} +100 -35
  271. package/src/liveness/matrix.ts +162 -0
  272. package/src/liveness/sweep.ts +855 -0
  273. package/src/load/classify.ts +433 -0
  274. package/src/load/diagnostics.ts +154 -0
  275. package/src/load/discover.ts +471 -0
  276. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  277. package/src/load/expected-module.ts +194 -0
  278. package/src/load/load.ts +73 -0
  279. package/src/load/module-lookup.ts +50 -0
  280. package/src/load/package-scope.ts +74 -0
  281. package/src/load/plan.ts +513 -0
  282. package/src/load/specifiers.ts +171 -0
  283. package/src/load/synthesize.ts +230 -0
  284. package/src/load/test-globals.ts +251 -0
  285. package/src/load/test-joins.ts +98 -0
  286. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  287. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  288. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
  289. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  290. package/src/{workspace.ts → load/workspace.ts} +90 -134
  291. package/src/module-declarations.ts +74 -98
  292. package/src/overrides.ts +162 -134
  293. package/src/partial.ts +3 -2
  294. package/src/paths.ts +92 -16
  295. package/src/platform-calls.ts +119 -0
  296. package/src/platform-symbols.ts +189 -0
  297. package/src/position.ts +44 -1
  298. package/src/print.ts +12 -92
  299. package/src/product.ts +18 -0
  300. package/src/query.ts +92 -0
  301. package/src/ref.ts +12 -20
  302. package/src/references/read-rule.ts +60 -0
  303. package/src/references/read-rules.ts +55 -0
  304. package/src/references/sink.ts +314 -0
  305. package/src/references/sources.ts +632 -0
  306. package/src/references.ts +187 -1450
  307. package/src/report-schema.ts +68 -0
  308. package/src/report.ts +103 -226
  309. package/src/reporters/annotations.ts +22 -11
  310. package/src/reporters/json.ts +1 -1
  311. package/src/reporters/reporter.ts +2 -4
  312. package/src/reporters/reporters.ts +1 -1
  313. package/src/reporters/sarif.ts +29 -16
  314. package/src/reporters/template.ts +4 -20
  315. package/src/reporters/text.ts +3 -3
  316. package/src/resolve.ts +284 -1115
  317. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +27 -29
  318. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
  319. package/src/roots/manifest.ts +303 -0
  320. package/src/roots/published-reach.ts +486 -0
  321. package/src/{roots.ts → roots/root-set.ts} +286 -469
  322. package/src/roots/unseen-loads.ts +345 -0
  323. package/src/run.ts +75 -91
  324. package/src/schema.ts +391 -388
  325. package/src/scope.ts +46 -138
  326. package/src/serialization-contract.ts +769 -23
  327. package/src/session.ts +87 -55
  328. package/src/source-files.ts +6 -11
  329. package/src/stores.ts +15 -3
  330. package/src/suppress-file.ts +24 -33
  331. package/src/suppress-inline.ts +55 -91
  332. package/src/suppress.ts +78 -79
  333. package/src/symbol-keys.ts +104 -107
  334. package/src/template-field.ts +15 -25
  335. package/src/tests/file-class.ts +135 -0
  336. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  337. package/src/{test-support.ts → tests/support.ts} +15 -21
  338. package/src/text/compare.ts +27 -0
  339. package/src/text/glob.ts +210 -0
  340. package/src/text/named-files.ts +388 -0
  341. package/src/text/yaml.ts +747 -0
  342. package/src/type-errors.ts +18 -288
  343. package/src/type-parameter-reads.ts +187 -158
  344. package/src/unimplemented-callees.ts +297 -280
  345. package/src/unwrap.ts +34 -0
  346. package/src/usage.ts +10 -0
  347. package/src/vendor/unbash/LICENSE +15 -0
  348. package/src/vendor/unbash/VENDORED.md +16 -0
  349. package/src/vendor/unbash/ansi-c.ts +132 -0
  350. package/src/vendor/unbash/arithmetic-parts.ts +53 -0
  351. package/src/vendor/unbash/arithmetic.ts +641 -0
  352. package/src/vendor/unbash/assignment.ts +163 -0
  353. package/src/vendor/unbash/chars.ts +48 -0
  354. package/src/vendor/unbash/command.ts +47 -0
  355. package/src/vendor/unbash/expansion.ts +144 -0
  356. package/src/vendor/unbash/glob.ts +284 -0
  357. package/src/vendor/unbash/heredoc.ts +34 -0
  358. package/src/vendor/unbash/internal-types.ts +94 -0
  359. package/src/vendor/unbash/lexer.ts +4352 -0
  360. package/src/vendor/unbash/limits.ts +3 -0
  361. package/src/vendor/unbash/parser.ts +1612 -0
  362. package/src/vendor/unbash/parts.ts +96 -0
  363. package/src/vendor/unbash/substitution-context.ts +20 -0
  364. package/src/vendor/unbash/traversal.ts +182 -0
  365. package/src/vendor/unbash/types.ts +660 -0
  366. package/src/vendor/unbash/word-value.ts +64 -0
  367. package/src/vendor/unbash/word.ts +50 -0
  368. package/src/verbs/analyze.ts +255 -164
  369. package/src/verbs/describe.ts +19 -17
  370. package/src/verbs/explain.ts +93 -169
  371. package/src/verbs/print-config.ts +2 -1
  372. package/src/verbs/print-projects.ts +6 -4
  373. package/src/verbs/print-retained.ts +7 -5
  374. package/src/verbs/print-roots.ts +4 -2
  375. package/src/verbs/verb.ts +2 -8
  376. package/src/verbs/version.ts +15 -3
  377. package/dist/src/base-url.js +0 -18
  378. package/dist/src/conventions.js +0 -937
  379. package/dist/src/discover.js +0 -468
  380. package/dist/src/entry-points.js +0 -529
  381. package/dist/src/glob.js +0 -65
  382. package/dist/src/manifest.js +0 -165
  383. package/dist/src/matrix.js +0 -246
  384. package/dist/src/pnpm-workspace.js +0 -120
  385. package/dist/src/roots.js +0 -672
  386. package/dist/src/setup-failure.js +0 -23
  387. package/dist/src/sweep.js +0 -224
  388. package/dist/src/test-joins.js +0 -101
  389. package/dist/src/version.js +0 -7
  390. package/dist/src/workflow-steps.js +0 -185
  391. package/src/base-url.ts +0 -24
  392. package/src/discover.ts +0 -653
  393. package/src/entry-points.ts +0 -687
  394. package/src/glob.ts +0 -67
  395. package/src/manifest.ts +0 -234
  396. package/src/matrix.ts +0 -423
  397. package/src/pnpm-workspace.ts +0 -135
  398. package/src/setup-failure.ts +0 -39
  399. package/src/sweep.ts +0 -347
  400. package/src/test-joins.ts +0 -120
  401. package/src/version.ts +0 -7
  402. package/src/workflow-steps.ts +0 -216
@@ -0,0 +1,432 @@
1
+ /**
2
+ * The command strings beside a target root's packages: a CI workflow's `run:` steps, the git
3
+ * hooks, the hook managers' commands and scripts and the makefile's recipe lines, each with the
4
+ * directory it runs in. A workflow or hook manager file is read by `text/yaml.ts`, and one it does not
5
+ * read gives no command; each command is read by `commands/read.ts`.
6
+ */
7
+
8
+ import type { SourceFile } from "@typescript/native/ast";
9
+ import type { HeldText } from "../commands/held.ts";
10
+ import { pathsNamed, readCommands, type CommandReading } from "../commands/read.ts";
11
+ import { PART } from "../commands/shell.ts";
12
+ import type { Host } from "../host.ts";
13
+ import { isRecord } from "../json-document.ts";
14
+ import { joinPath, resolvePath } from "../paths.ts";
15
+ import type { SourceFiles } from "../source-files.ts";
16
+ import { filesIn } from "../fs/tree.ts";
17
+ import { yamlDocument } from "../text/yaml.ts";
18
+
19
+ /** One file a workflow step, git hook or recipe runs or names. */
20
+ interface WorkflowEntry {
21
+ readonly file: SourceFile;
22
+ /** The word or the path that named it, as the command writes it. */
23
+ readonly source: string;
24
+ }
25
+
26
+ /** One command string and the directory it runs in. */
27
+ interface CommandString {
28
+ readonly text: string;
29
+ /**
30
+ * The directory it runs in, absolute, or `undefined` where the file does not state what runs
31
+ * where: a directory holding a `${{ }}` expression, which GitHub Actions evaluates.
32
+ */
33
+ readonly dir: string | undefined;
34
+ }
35
+
36
+ const WORKFLOWS = ".github/workflows";
37
+
38
+ /** The directories whose every file is a git hook script, run as the shell runs it. */
39
+ const HOOK_DIRS = [".husky", ".githooks"] as const;
40
+
41
+ /** The key setting the directory a workflow step, or each step a `defaults.run` covers, runs in. */
42
+ const WORKING_DIRECTORY = "working-directory";
43
+
44
+ /** One hook manager's file. */
45
+ interface HookManager {
46
+ readonly name: string;
47
+ /** The key its commands are written under. */
48
+ readonly key: string;
49
+ /** The key of a command's directory: lefthook's `root`, https://lefthook.dev/configuration/root/ */
50
+ readonly dirKey?: string;
51
+ /** Whether its hooks name script files it runs, as lefthook's do. */
52
+ readonly scripts?: true;
53
+ }
54
+
55
+ const HOOK_MANAGERS: readonly HookManager[] = [
56
+ { name: ".pre-commit-config.yaml", key: "entry" },
57
+ ...["lefthook.yml", "lefthook.yaml", ".lefthook.yml"].map((name): HookManager => ({
58
+ name,
59
+ key: "run",
60
+ dirKey: "root",
61
+ scripts: true,
62
+ })),
63
+ ];
64
+
65
+ /**
66
+ * The directories lefthook searches for a hook's scripts after the file's `source_dir` and
67
+ * `source_dir_local`: https://github.com/evilmartians/lefthook/blob/master/internal/command/run.go
68
+ */
69
+ const LEFTHOOK_DIRS = [".config/lefthook", ".config/lefthook-local"] as const;
70
+
71
+ /** The names GNU make reads a makefile under, in the order it tries them. */
72
+ const MAKEFILES = ["GNUmakefile", "makefile", "Makefile"] as const;
73
+
74
+ function readText(host: Host, path: string): string | undefined {
75
+ try {
76
+ return host.readFile(path);
77
+ } catch {
78
+ return undefined;
79
+ }
80
+ }
81
+
82
+ /** The text a YAML scalar's value stands for, or `undefined` for a collection or a null. */
83
+ function scalarText(value: unknown): string | undefined {
84
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean"
85
+ ? String(value)
86
+ : undefined;
87
+ }
88
+
89
+ /** The `defaults.run.working-directory` one mapping writes, as YAML decodes it. */
90
+ function defaultDirectory(mapping: Record<string, unknown>): string | undefined {
91
+ const defaults = mapping["defaults"];
92
+ const run = isRecord(defaults) ? defaults["run"] : undefined;
93
+ return isRecord(run) ? scalarText(run[WORKING_DIRECTORY]) : undefined;
94
+ }
95
+
96
+ /**
97
+ * Every command one workflow or hook manager file writes under `key`, in every mapping of the
98
+ * document, each with the directory it runs in: its mapping's `dirKey` value, else, in a workflow
99
+ * file, the innermost `defaults.run.working-directory` of a mapping holding it, else `root`. A
100
+ * file `yamlDocument` does not read gives none.
101
+ */
102
+ function keyedCommands(
103
+ text: string,
104
+ root: string,
105
+ key: string,
106
+ dirKey: string | undefined,
107
+ ): readonly CommandString[] {
108
+ const found: CommandString[] = [];
109
+ const visit = (node: unknown, inherited: string | undefined): void => {
110
+ if (Array.isArray(node)) {
111
+ for (const item of node) {
112
+ visit(item, inherited);
113
+ }
114
+ return;
115
+ }
116
+ if (!isRecord(node)) {
117
+ return;
118
+ }
119
+ const here = (dirKey === WORKING_DIRECTORY ? defaultDirectory(node) : undefined) ?? inherited;
120
+ const command = scalarText(node[key]);
121
+ if (command !== undefined && command.trim() !== "") {
122
+ const written = (dirKey === undefined ? undefined : scalarText(node[dirKey])) ?? here;
123
+ found.push({
124
+ text: command,
125
+ dir:
126
+ written === undefined
127
+ ? root
128
+ : written.includes("${{")
129
+ ? undefined
130
+ : resolvePath(root, written),
131
+ });
132
+ }
133
+ for (const value of Object.values(node)) {
134
+ visit(value, here);
135
+ }
136
+ };
137
+ visit(yamlDocument(text), undefined);
138
+ return found;
139
+ }
140
+
141
+ /**
142
+ * The command lefthook runs for each script a lefthook file's hooks name, each key of a hook's
143
+ * `scripts` and each `script` of a job in its `jobs`, a group's among them, from each directory
144
+ * it searches: its `runner`, then the path `<dir>/<hook>/<name>` quoted as lefthook quotes it,
145
+ * run in the target root: https://lefthook.dev/configuration/Scripts/
146
+ */
147
+ function lefthookScripts(text: string, root: string): readonly CommandString[] {
148
+ const document = yamlDocument(text);
149
+ if (!isRecord(document)) {
150
+ return [];
151
+ }
152
+ const dirs = [
153
+ scalarText(document["source_dir"]) ?? ".lefthook/",
154
+ scalarText(document["source_dir_local"]) ?? ".lefthook-local/",
155
+ ...LEFTHOOK_DIRS,
156
+ ];
157
+ const found: CommandString[] = [];
158
+ const add = (hook: string, name: string, runner: string | undefined): void => {
159
+ for (const dir of dirs) {
160
+ const path = `'${joinPath(dir, hook, name).replaceAll("'", "'\\''")}'`;
161
+ found.push({
162
+ text: runner === undefined || runner.trim() === "" ? path : `${runner} ${path}`,
163
+ dir: root,
164
+ });
165
+ }
166
+ };
167
+ const jobs = (hook: string, list: unknown): void => {
168
+ for (const job of Array.isArray(list) ? list : []) {
169
+ const script = isRecord(job) ? scalarText(job["script"]) : undefined;
170
+ if (isRecord(job) && script !== undefined) {
171
+ add(hook, script, scalarText(job["runner"]));
172
+ }
173
+ const group = isRecord(job) ? job["group"] : undefined;
174
+ jobs(hook, isRecord(group) ? group["jobs"] : undefined);
175
+ }
176
+ };
177
+ for (const [hook, value] of Object.entries(document)) {
178
+ const scripts = isRecord(value) ? value["scripts"] : undefined;
179
+ for (const [name, options] of Object.entries(isRecord(scripts) ? scripts : {})) {
180
+ add(hook, name, isRecord(options) ? scalarText(options["runner"]) : undefined);
181
+ }
182
+ jobs(hook, isRecord(value) ? value["jobs"] : undefined);
183
+ }
184
+ return found;
185
+ }
186
+
187
+ /** The path of each git hook script at `root`, every file in {@link HOOK_DIRS}. */
188
+ function hookScripts(host: Host, root: string): readonly string[] {
189
+ return HOOK_DIRS.flatMap((dir) =>
190
+ filesIn(host, joinPath(root, dir), () => true).map((name) => joinPath(root, dir, name)),
191
+ );
192
+ }
193
+
194
+ /** The makefile make reads at `root`, the first of {@link MAKEFILES} there. */
195
+ function makefileAt(host: Host, root: string): string | undefined {
196
+ return MAKEFILES.map((name) => joinPath(root, name)).find(
197
+ (candidate) => host.kindOf(candidate) === "file",
198
+ );
199
+ }
200
+
201
+ /**
202
+ * One recipe line as make hands it to the shell: each `$$` a `$`, and each reference to a
203
+ * variable or a function, which make expands first, a part only the run knows:
204
+ * https://www.gnu.org/software/make/manual/html_node/Reference.html
205
+ */
206
+ function expandedByMake(line: string): string {
207
+ let expanded = "";
208
+ for (let at = 0; at < line.length; at += 1) {
209
+ const char = line.charAt(at);
210
+ const next = line.charAt(at + 1);
211
+ if (char !== "$") {
212
+ expanded += char;
213
+ } else if (next === "$") {
214
+ expanded += "$";
215
+ at += 1;
216
+ } else {
217
+ let end = at + 1;
218
+ if (next === "(" || next === "{") {
219
+ const close = next === "(" ? ")" : "}";
220
+ for (let depth = 0; end < line.length; end += 1) {
221
+ depth += line.charAt(end) === next ? 1 : line.charAt(end) === close ? -1 : 0;
222
+ if (depth === 0) {
223
+ break;
224
+ }
225
+ }
226
+ }
227
+ expanded += PART;
228
+ at = Math.min(end, line.length);
229
+ }
230
+ }
231
+ return expanded;
232
+ }
233
+
234
+ /**
235
+ * Each recipe line of the target root's makefile: a line that starts with a tab, joined with
236
+ * the lines a trailing `\` continues it onto, less the `@`, `-` and `+` make reads before it,
237
+ * as make expands it. Make runs each line in a shell of its own.
238
+ */
239
+ function recipeLines(host: Host, root: string): readonly CommandString[] {
240
+ const path = makefileAt(host, root);
241
+ const text = path === undefined ? undefined : readText(host, path);
242
+ return (text ?? "")
243
+ .replace(/\\\r?\n/gu, " ")
244
+ .split(/\r?\n/u)
245
+ .filter((line) => line.startsWith("\t"))
246
+ .map((line) => ({
247
+ text: expandedByMake(line.slice(1).replace(/^[@+-]+/u, "")),
248
+ dir: root,
249
+ }));
250
+ }
251
+
252
+ /**
253
+ * Every command the target root's workflow steps, git hooks, hook managers and makefile run:
254
+ * the `run:` steps of the workflow files in `.github/workflows`, each file in `.husky` and
255
+ * `.githooks` as one shell script, the `entry:` commands of `.pre-commit-config.yaml`, the
256
+ * `run:` commands and the scripts of a lefthook file and the makefile's recipe lines.
257
+ */
258
+ export function workflowCommands(host: Host, targetRoot: string): readonly CommandString[] {
259
+ const root = resolvePath(host.workingDirectory(), targetRoot);
260
+ const workflows = filesIn(host, joinPath(root, WORKFLOWS), (name) => /\.ya?ml$/u.test(name));
261
+ return [
262
+ ...workflows.flatMap((name) =>
263
+ keyedCommands(
264
+ readText(host, joinPath(root, WORKFLOWS, name)) ?? "",
265
+ root,
266
+ "run",
267
+ WORKING_DIRECTORY,
268
+ ),
269
+ ),
270
+ ...hookScripts(host, root).map((path): CommandString => ({
271
+ text: readText(host, path) ?? "",
272
+ dir: root,
273
+ })),
274
+ ...HOOK_MANAGERS.flatMap(({ name, key, dirKey, scripts }) => {
275
+ const text = readText(host, joinPath(root, name)) ?? "";
276
+ return [
277
+ ...keyedCommands(text, root, key, dirKey),
278
+ ...(scripts === true ? lefthookScripts(text, root) : []),
279
+ ];
280
+ }),
281
+ ...recipeLines(host, root),
282
+ ];
283
+ }
284
+
285
+ /**
286
+ * The whole text of each file {@link workflowCommands} reads a command from, each workflow file,
287
+ * git hook, hook manager file and makefile at the target root, which the dependency rule reads.
288
+ */
289
+ export function commandFileTexts(host: Host, targetRoot: string): readonly string[] {
290
+ return commandFiles(host, targetRoot).flatMap(({ path }) => readText(host, path) ?? []);
291
+ }
292
+
293
+ /** Each file {@link workflowCommands} reads a command from, with the key a YAML file writes its commands under. */
294
+ function commandFiles(
295
+ host: Host,
296
+ targetRoot: string,
297
+ ): readonly { readonly path: string; readonly key: string | undefined }[] {
298
+ const root = resolvePath(host.workingDirectory(), targetRoot);
299
+ return [
300
+ ...filesIn(host, joinPath(root, WORKFLOWS), (name) => /\.ya?ml$/u.test(name)).map((name) => ({
301
+ path: joinPath(root, WORKFLOWS, name),
302
+ key: "run",
303
+ })),
304
+ ...hookScripts(host, root).map((path) => ({ path, key: undefined })),
305
+ ...HOOK_MANAGERS.map(({ name, key }) => ({ path: joinPath(root, name), key })),
306
+ ...[makefileAt(host, root) ?? []].flat().map((path) => ({ path, key: undefined })),
307
+ ];
308
+ }
309
+
310
+ /**
311
+ * The filters of a workflow's `on` key, which name the files and refs whose change triggers it
312
+ * and run none: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onpushpull_requestpull_request_targetpathspaths-ignore
313
+ */
314
+ const TRIGGER_FILTERS = new Set([
315
+ "paths",
316
+ "paths-ignore",
317
+ "branches",
318
+ "branches-ignore",
319
+ "tags",
320
+ "tags-ignore",
321
+ ]);
322
+
323
+ /**
324
+ * The values of one YAML file that are no command, each with its key: every scalar but one
325
+ * written under `key`, which is read as a command string, and one of a trigger filter. A file
326
+ * `yamlDocument` does not read is one value, its whole text. No key and no comment is a value.
327
+ */
328
+ function yamlValues(text: string, key: string): readonly HeldText[] {
329
+ const document = yamlDocument(text);
330
+ if (document === undefined) {
331
+ return [{ text, shell: false }];
332
+ }
333
+ const found: HeldText[] = [];
334
+ // `trigger` is 1 for the value of the top-level `on`, a map of events, and 2 for an event's.
335
+ const visit = (node: unknown, under: string | undefined, trigger = 0): void => {
336
+ if (Array.isArray(node)) {
337
+ for (const item of node) {
338
+ visit(item, under);
339
+ }
340
+ } else if (isRecord(node)) {
341
+ for (const [name, value] of Object.entries(node)) {
342
+ const on = node === document && (name === "on" || name === "true");
343
+ if (
344
+ (name !== key || scalarText(value) === undefined) &&
345
+ !(trigger === 2 && TRIGGER_FILTERS.has(name))
346
+ ) {
347
+ visit(value, name, on ? 1 : trigger === 1 ? 2 : 0);
348
+ }
349
+ }
350
+ } else {
351
+ const value = scalarText(node);
352
+ if (value !== undefined) {
353
+ found.push({ text: value, shell: false, ...(under === undefined ? {} : { key: under }) });
354
+ }
355
+ }
356
+ };
357
+ visit(document, undefined);
358
+ return found;
359
+ }
360
+
361
+ /**
362
+ * The lines of a makefile that are no recipe line, joined with the lines a trailing `\`
363
+ * continues them onto, each without the comment a `#` make does not read as escaped starts.
364
+ */
365
+ function makefileValues(text: string): readonly HeldText[] {
366
+ return text
367
+ .replace(/\\\r?\n/gu, " ")
368
+ .split(/\r?\n/u)
369
+ .filter((line) => !line.startsWith("\t"))
370
+ .map((line) => line.replace(/(?<!\\)#.*$/u, ""))
371
+ .filter((line) => line.trim() !== "")
372
+ .map((line) => ({ text: line, shell: false }));
373
+ }
374
+
375
+ /**
376
+ * The texts layer 3 reads of the target root's command files: each command string, and each
377
+ * value of a workflow file, a hook manager file and the makefile that is no command.
378
+ */
379
+ function heldTexts(host: Host, targetRoot: string): readonly HeldText[] {
380
+ const makefile = makefileAt(host, resolvePath(host.workingDirectory(), targetRoot));
381
+ return [
382
+ ...workflowCommands(host, targetRoot).map(({ text }) => ({ text, shell: true })),
383
+ ...commandFiles(host, targetRoot).flatMap(({ path, key }) => {
384
+ const text = readText(host, path);
385
+ return text === undefined
386
+ ? []
387
+ : key !== undefined
388
+ ? yamlValues(text, key)
389
+ : path === makefile
390
+ ? makefileValues(text)
391
+ : [];
392
+ }),
393
+ ];
394
+ }
395
+
396
+ /** What the target root's workflow steps, git hooks, hook managers and makefile run, read once per run. */
397
+ export interface Workflows {
398
+ /** Each command read. */
399
+ readonly readings: readonly CommandReading[];
400
+ /** The whole text of each file a command is read from, which the dependency rule reads. */
401
+ readonly texts: readonly string[];
402
+ /** What layer 3 reads of them: each command string, and each value that is no command. */
403
+ readonly held: readonly HeldText[];
404
+ }
405
+
406
+ /** Reads the target root's workflow steps, git hooks, hook managers and makefile. */
407
+ export function readWorkflows(host: Host, targetRoot: string): Workflows {
408
+ return {
409
+ readings: workflowCommands(host, targetRoot).map(({ text, dir }) => readCommands(text, dir)),
410
+ texts: commandFileTexts(host, targetRoot),
411
+ held: heldTexts(host, targetRoot),
412
+ };
413
+ }
414
+
415
+ /**
416
+ * The own files the target root's workflow steps, git hooks and recipes run or name, as
417
+ * `pathsNamed` reads each command.
418
+ */
419
+ export function workflowEntries(
420
+ workflows: Workflows,
421
+ files: SourceFiles,
422
+ ): readonly WorkflowEntry[] {
423
+ const found: WorkflowEntry[] = [];
424
+ for (const reading of workflows.readings) {
425
+ for (const { path, written } of pathsNamed(reading)) {
426
+ for (const file of files.named(path)) {
427
+ found.push({ file, source: written });
428
+ }
429
+ }
430
+ }
431
+ return found;
432
+ }
package/src/enum-group.ts CHANGED
@@ -18,7 +18,6 @@ import {
18
18
  isIdentifier,
19
19
  isMethodDeclaration,
20
20
  isNoSubstitutionTemplateLiteral,
21
- isParenthesizedExpression,
22
21
  isParenthesizedTypeNode,
23
22
  isPropertyAccessExpression,
24
23
  isReturnStatement,
@@ -28,6 +27,7 @@ import {
28
27
  type Node,
29
28
  type SourceFile,
30
29
  } from "@typescript/native/ast";
30
+ import { parenthesesRemoved } from "./unwrap.ts";
31
31
  import { TypeFlags, type Type } from "@typescript/native/sync";
32
32
  import { aliasChains } from "./alias-chain.ts";
33
33
  import type { DetectorInput, Evidence } from "./exempt.ts";
@@ -55,15 +55,6 @@ interface Site {
55
55
  readonly at: Node;
56
56
  }
57
57
 
58
- /** The expression one node is, through however many parentheses are written around it. */
59
- function unparenthesized(node: Node): Node {
60
- let held = node;
61
- while (isParenthesizedExpression(held)) {
62
- held = held.expression;
63
- }
64
- return held;
65
- }
66
-
67
58
  /**
68
59
  * The name node a type node names its declaration by: the identifier of a type
69
60
  * reference, the right side of a qualified one, through parentheses. A type that is no
@@ -86,7 +77,7 @@ function typeName(type: Node | undefined): Node | undefined {
86
77
 
87
78
  /** The name node an expression names an object by: an identifier, or a property access's name. */
88
79
  function objectName(expression: Node): Node | undefined {
89
- const held = unparenthesized(expression);
80
+ const held = parenthesesRemoved(expression);
90
81
  if (isIdentifier(held)) {
91
82
  return held;
92
83
  }
@@ -16,7 +16,6 @@ import {
16
16
  isNoSubstitutionTemplateLiteral,
17
17
  isNumericLiteral,
18
18
  isObjectLiteralExpression,
19
- isParenthesizedExpression,
20
19
  isPropertyAccessExpression,
21
20
  isPropertyAssignment,
22
21
  isShorthandPropertyAssignment,
@@ -31,6 +30,7 @@ import { SymbolFlags, TypeFlags, type Type } from "@typescript/native/sync";
31
30
  import { must } from "./query.ts";
32
31
  import type { ProjectView } from "./session.ts";
33
32
  import { checkedTypes } from "./value-reach.ts";
33
+ import { parenthesesRemoved } from "./unwrap.ts";
34
34
 
35
35
  /** The types that keep nothing of the value they are given. */
36
36
  const ERASED = TypeFlags.Any | TypeFlags.Unknown;
@@ -81,10 +81,6 @@ function literalKey(node: Expression): string | undefined {
81
81
  : undefined;
82
82
  }
83
83
 
84
- function unwrapped(node: Expression): Expression {
85
- return isParenthesizedExpression(node) ? unwrapped(node.expression) : node;
86
- }
87
-
88
84
  /**
89
85
  * Whether a variable declaration's type can differ from what is stored in it: it carries an
90
86
  * annotation, or it has neither an annotation nor an initializer, so its type evolves.
@@ -150,8 +146,8 @@ function propertyAt(source: Expression, name: string): Expression | undefined {
150
146
  * here, and a default value is stored in its target too.
151
147
  */
152
148
  function patternTargets(pattern: Expression, from: Stored, out: Target[]): void {
153
- const target = unwrapped(pattern);
154
- const source = from.path.length === 0 ? unwrapped(from.value) : from.value;
149
+ const target = parenthesesRemoved(pattern);
150
+ const source = from.path.length === 0 ? parenthesesRemoved(from.value) : from.value;
155
151
  const into = (element: Expression, step: Step, literal: Expression | undefined): void => {
156
152
  patternTargets(
157
153
  element,
@@ -1,12 +1,4 @@
1
- /**
2
- * The exemption classes of the Contract's vocabulary, in its order: each class's name, its
3
- * languages and, for a TypeScript class, which private members it may retain. The rows are
4
- * written out, as the fixed severity codes are, and a test holds them equal to the Contract.
5
- * A class name is a type, so a table or a record naming a class outside the vocabulary, or
6
- * one of another language only, does not compile.
7
- */
8
-
9
- import type { Language } from "./config.ts";
1
+ import { EXEMPTION_CLASSES, type Language } from "./contract/contract.generated.ts";
10
2
 
11
3
  /**
12
4
  * Whether a class may retain a member declared with the `private` modifier, and one
@@ -19,67 +11,6 @@ export interface TypeScriptVisibility {
19
11
  readonly privateName: boolean;
20
12
  }
21
13
 
22
- /** One class of the vocabulary. */
23
- interface ExemptionClassRow {
24
- readonly class: string;
25
- readonly languages: readonly Language[];
26
- /** Present exactly on a class that runs on TypeScript. */
27
- readonly typescriptVisibility?: TypeScriptVisibility;
28
- }
29
-
30
- /** Every class of the vocabulary, in the order the Contract lists them. */
31
- export const EXEMPTION_CLASSES = [
32
- {
33
- class: "interface-satisfaction",
34
- languages: ["go", "ts"],
35
- typescriptVisibility: { private: false, privateName: false },
36
- },
37
- { class: "encoding-reflection", languages: ["go"] },
38
- { class: "format-verb-contract", languages: ["go"] },
39
- { class: "errors-duck-typing", languages: ["go"] },
40
- {
41
- class: "enum-group",
42
- languages: ["go", "ts"],
43
- typescriptVisibility: { private: false, privateName: false },
44
- },
45
- {
46
- class: "generated-file",
47
- languages: ["go", "ts"],
48
- typescriptVisibility: { private: true, privateName: false },
49
- },
50
- { class: "linkname-cgo-asm-plugin", languages: ["go"] },
51
- {
52
- class: "template-field",
53
- languages: ["go", "ts"],
54
- typescriptVisibility: { private: true, privateName: false },
55
- },
56
- {
57
- class: "reflective-lookup",
58
- languages: ["go", "ts"],
59
- typescriptVisibility: { private: true, privateName: false },
60
- },
61
- {
62
- class: "decorator",
63
- languages: ["ts"],
64
- typescriptVisibility: { private: true, privateName: false },
65
- },
66
- {
67
- class: "injection-container",
68
- languages: ["ts"],
69
- typescriptVisibility: { private: true, privateName: false },
70
- },
71
- {
72
- class: "framework-lifecycle",
73
- languages: ["ts"],
74
- typescriptVisibility: { private: true, privateName: false },
75
- },
76
- {
77
- class: "serialization-contract",
78
- languages: ["ts"],
79
- typescriptVisibility: { private: true, privateName: false },
80
- },
81
- ] as const satisfies readonly ExemptionClassRow[];
82
-
83
14
  type Row = (typeof EXEMPTION_CLASSES)[number];
84
15
 
85
16
  /** The name of a class of the vocabulary. */
@@ -116,8 +47,3 @@ const VISIBILITY: ReadonlyMap<TSExemptionClass, TypeScriptVisibility> = new Map(
116
47
  export function typescriptVisibilityOf(exemptionClass: TSExemptionClass): TypeScriptVisibility {
117
48
  return VISIBILITY.get(exemptionClass) ?? { private: false, privateName: false };
118
49
  }
119
-
120
- /** Whether one name is a class of the vocabulary, whichever language it runs on. */
121
- export function isExemptionClass(name: string): name is ExemptionClass {
122
- return EXEMPTION_CLASSES.some((row) => row.class === name);
123
- }