@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
@@ -3,27 +3,20 @@
3
3
  * applied and the declared edges evaluated, and the stale suppressions, the totals and the
4
4
  * verdict a report carries beside them.
5
5
  */
6
+ import { EXIT_CLEAN, EXIT_FINDINGS, EXIT_PENDING } from "./contract/contract.generated.js";
6
7
  import { fails } from "./config.js";
7
8
  import { decidedFindings } from "./findings/emitters.js";
8
9
  import { fileRefsOf, staleSuppressions, } from "./findings/self-check.js";
9
- import { isDependencySubject, isRowSubject, pairs, suppressionCounts, } from "./suppress.js";
10
- import { EXIT_CLEAN, EXIT_FINDINGS } from "./verbs/verb.js";
11
- /** The exit code of a report holding at least one pending finding. */
12
- export const EXIT_PENDING = 4;
13
- /** Two strings ordered bytewise. */
14
- function compare(a, b) {
15
- if (a === b) {
16
- return 0;
17
- }
18
- return a < b ? -1 : 1;
19
- }
10
+ import { namesKind, pairs, suppressionCounts } from "./suppress.js";
11
+ import { bytewise } from "./text/compare.js";
12
+ import { PRODUCT } from "./product.js";
20
13
  /** The canonical order: path, line, column, code, then the symbol reference. */
21
14
  function canonical(a, b) {
22
- return (compare(a.position.path, b.position.path) ||
15
+ return (bytewise(a.position.path, b.position.path) ||
23
16
  a.position.line - b.position.line ||
24
17
  a.position.column - b.position.column ||
25
- compare(a.code, b.code) ||
26
- compare(a.symbol.ref, b.symbol.ref));
18
+ bytewise(a.code, b.code) ||
19
+ bytewise(a.symbol.ref, b.symbol.ref));
27
20
  }
28
21
  /**
29
22
  * The pass over one run. The emitter table decides every record against every finding the
@@ -73,20 +66,19 @@ export function verdictOf(result, config) {
73
66
  if (result.totals.staleSuppressions > 0) {
74
67
  return EXIT_FINDINGS;
75
68
  }
76
- const failing = result.findings.some((finding) => fails(finding.severity, config.reporters.failOn));
69
+ const failing = result.findings.some((finding) => fails(finding.severity, config.reporters.fail_on));
77
70
  return failing ? EXIT_FINDINGS : EXIT_CLEAN;
78
71
  }
79
72
  /**
80
73
  * The rows one round of a baseline write records from its findings, in their order. A
81
- * finding about a row of a document other than a dependency row is not recorded: no record
82
- * withholds one, so a row recording it would be stale on the first read back. Two findings
83
- * that would record the same row, two parts of one declaration under one code, record it
84
- * once.
74
+ * finding no baseline row can name is not recorded: a row recording it would be stale on
75
+ * the first read back. Two findings that would record the same row, two parts of one
76
+ * declaration under one code, record it once.
85
77
  */
86
78
  export function recordedFindings(findings) {
87
79
  const rows = new Map();
88
80
  for (const finding of findings) {
89
- if (isRowSubject(finding.symbol.kind) && !isDependencySubject(finding.symbol.kind)) {
81
+ if (!namesKind(finding.symbol.kind, "baseline")) {
90
82
  continue;
91
83
  }
92
84
  const row = { code: finding.code, symbol: finding.symbol.ref, path: finding.position.path };
@@ -100,5 +92,5 @@ export function rowKey(row) {
100
92
  }
101
93
  /** The analyzer identity a baseline this analyzer writes names as its rows' provenance. */
102
94
  export function analyzerProvenance(version) {
103
- return { analyzer: "deadset-ts", version };
95
+ return { analyzer: PRODUCT, version };
104
96
  }
@@ -1,23 +1,47 @@
1
1
  /**
2
- * The framework-lifecycle class: a member a `ts.lifecycle_contracts` entry lists is held
3
- * back on each class that is the entry's component, being a class a decorator or a call
4
- * the entry's `components` name is attached to or passes, or one extending, directly or
5
- * through a chain, a class its `bases` name. A component's members are its type's, so a
6
- * member a base class declares answers for every subclass.
2
+ * The framework-lifecycle class: a member a lifecycle contract lists is held back on each
3
+ * class that is the contract's component, being a class a decorator or a call the
4
+ * contract's `components` name is attached to or passes, or one extending, directly or
5
+ * through a chain, a class its `bases` name. The contracts are the platform's custom
6
+ * elements and each `ts.lifecycle_contracts` entry. A component's members are its type's,
7
+ * so a member a base class declares answers for every subclass.
7
8
  */
8
9
  import { isClassDeclaration, isClassExpression, isHeritageClause, SyntaxKind, } from "@typescript/native/ast";
9
10
  import { aliasChains } from "./alias-chain.js";
10
11
  import { calleeName, callsOf, classesPassed, decoratedClasses, decoratorCallee } from "./calls.js";
11
12
  import { classMembers, memberComponent, typesAt } from "./class-members.js";
12
- import { entriesAt, names, namesADeclaration, resolveEntries, spelledEntry, } from "./configured-declarations.js";
13
+ import { entriesAt, names, namesADeclaration, resolveEntries, spelledEntry, } from "./roots/configured-declarations.js";
13
14
  import { nodeKey } from "./inventory.js";
14
15
  import { renderPosition } from "./position.js";
15
16
  import { must } from "./query.js";
17
+ /**
18
+ * The platform's own contract (references-ts.38): `CustomElementRegistry.define` reads
19
+ * these members off the class it registers, its constructor's and its prototype's
20
+ * (https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-define),
21
+ * and calls each callback as a custom element reaction.
22
+ */
23
+ const CUSTOM_ELEMENTS = {
24
+ components: [{ global: "CustomElementRegistry.define" }],
25
+ members: [
26
+ "observedAttributes:static",
27
+ "disabledFeatures:static",
28
+ "formAssociated:static",
29
+ "connectedCallback",
30
+ "disconnectedCallback",
31
+ "connectedMoveCallback",
32
+ "adoptedCallback",
33
+ "attributeChangedCallback",
34
+ "formAssociatedCallback",
35
+ "formResetCallback",
36
+ "formDisabledCallback",
37
+ "formStateRestoreCallback",
38
+ ],
39
+ };
16
40
  /** Every contract's components and bases, resolved in one pass over the entries. */
17
41
  function resolveContracts(project, held, contracts) {
18
42
  const entries = contracts.flatMap((contract) => [
19
- ...contract.components,
20
- ...contract.bases,
43
+ ...(contract.components ?? []),
44
+ ...(contract.bases ?? []),
21
45
  ]);
22
46
  const resolved = resolveEntries(project, held, entries);
23
47
  let next = 0;
@@ -28,8 +52,8 @@ function resolveContracts(project, held, contracts) {
28
52
  };
29
53
  return contracts.map((contract) => ({
30
54
  contract,
31
- components: take(contract.components.length),
32
- bases: take(contract.bases.length),
55
+ components: take(contract.components?.length ?? 0),
56
+ bases: take(contract.bases?.length ?? 0),
33
57
  }));
34
58
  }
35
59
  /** The classes a decorator or a call the contracts' components name makes a component. */
@@ -155,16 +179,17 @@ function byBases(project, held, chains, contracts) {
155
179
  }
156
180
  /**
157
181
  * The framework-lifecycle detector: each member a contract lists, on each class that
158
- * is a component of that contract's framework, recorded where the class was found to
159
- * be one.
182
+ * is a component of that contract's framework, recorded once per place where the class
183
+ * was found to be one.
160
184
  */
161
185
  export function frameworkLifecycle(input) {
162
186
  const { project, held, targetRoot } = input;
163
- if (input.ts.lifecycleContracts.length === 0) {
187
+ const declared = [CUSTOM_ELEMENTS, ...input.ts.lifecycle_contracts];
188
+ const contracts = resolveContracts(project, held, declared);
189
+ if (contracts.every((one) => one.components.length === 0 && one.bases.length === 0)) {
164
190
  return [];
165
191
  }
166
192
  const chains = aliasChains(project, held);
167
- const contracts = resolveContracts(project, held, input.ts.lifecycleContracts);
168
193
  const components = [
169
194
  ...byComponents(project, held, chains, contracts),
170
195
  ...byBases(project, held, chains, contracts),
@@ -173,22 +198,24 @@ export function frameworkLifecycle(input) {
173
198
  return [];
174
199
  }
175
200
  const ids = new Set(components.map((one) => one.id));
176
- const sides = input.ts.lifecycleContracts.some((contract) => contract.members.some((name) => name.endsWith(":static")))
201
+ const sides = declared.some((contract) => contract.members.some((name) => name.endsWith(":static")))
177
202
  ? ["instance", "static"]
178
203
  : ["instance"];
179
204
  const members = sides.map((side) => classMembers(project, held, ids, side));
180
205
  const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
181
- const found = [];
206
+ const found = new Map();
182
207
  for (const component of components) {
183
208
  const listed = new Set(component.contract.members);
184
209
  const site = renderPosition(component.site.getSourceFile(), targetRoot, component.site.getStart());
185
210
  for (const member of members.flatMap((side) => side.get(component.id) ?? [])) {
186
211
  const symbol = byId.get(member.id);
187
212
  const spelled = symbol === undefined ? undefined : memberComponent(symbol, byId);
188
- if (spelled !== undefined && listed.has(spelled)) {
189
- found.push({ id: member.id, detail: component.detail, site });
213
+ // A configured contract may name the platform's registry too; one record answers both.
214
+ const key = `${member.id}\0${component.detail}\0${site.path}:${String(site.line)}:${String(site.column)}`;
215
+ if (spelled !== undefined && listed.has(spelled) && !found.has(key)) {
216
+ found.set(key, { id: member.id, detail: component.detail, site });
190
217
  }
191
218
  }
192
219
  }
193
- return found;
220
+ return [...found.values()];
194
221
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The one read of a document the target holds at its root, which a checkout under analysis
3
+ * controls: the repository configuration, the ignore file, the baseline and the edges
4
+ * document. The read stays inside the target root, takes a regular file alone, and holds no
5
+ * more of it than the Contract's document bound.
6
+ */
7
+ import { CONFIG_MAX_BYTES } from "../contract/config.generated.js";
8
+ import { failureExit } from "../failure.js";
9
+ import { joinPath, relativePath } from "../paths.js";
10
+ import { message } from "../product.js";
11
+ import { utf8Bytes } from "../reporters/sha256.js";
12
+ import { UsageError } from "../usage.js";
13
+ function unreadable(label, reason) {
14
+ return failureExit("unreadable-document", `${label}: ${reason}: make it a regular file inside the target root that the run can read, or remove it`);
15
+ }
16
+ /** Refuses a document past the Contract's bound with the usage code, naming it `label`. */
17
+ export function refuseOverBound(label, text) {
18
+ if (utf8Bytes(text).length > CONFIG_MAX_BYTES) {
19
+ throw new UsageError(`${label}: holds more than ${String(CONFIG_MAX_BYTES)} bytes`);
20
+ }
21
+ }
22
+ /**
23
+ * The text of the document `name` at the absolute `targetRoot`, undefined where nothing is
24
+ * there. One whose name resolves outside the target root or to anything but a regular file,
25
+ * or whose read fails, is refused by `refuse` naming it `label`, nothing read from it. A
26
+ * document past the bound is read one byte past it and refused as {@link refuseOverBound} does.
27
+ */
28
+ export function readTargetDocument(host, targetRoot, name, label = name, refuse = unreadable) {
29
+ const attempt = (read) => {
30
+ try {
31
+ return read();
32
+ }
33
+ catch (error) {
34
+ throw refuse(label, message(error));
35
+ }
36
+ };
37
+ const path = joinPath(targetRoot, name);
38
+ const kind = attempt(() => host.kindOf(path));
39
+ if (kind === "absent") {
40
+ return undefined;
41
+ }
42
+ const real = attempt(() => host.realPath(path));
43
+ if (relativePath(attempt(() => host.realPath(targetRoot)), real) === undefined) {
44
+ throw refuse(label, `resolves to ${real}, outside the target root`);
45
+ }
46
+ if (kind !== "file") {
47
+ throw refuse(label, "is not a regular file");
48
+ }
49
+ const text = attempt(() => host.readFile(real, CONFIG_MAX_BYTES + 1));
50
+ refuseOverBound(label, text);
51
+ return text;
52
+ }
@@ -0,0 +1,67 @@
1
+ import { isRecord } from "../json-document.js";
2
+ import { joinPath } from "../paths.js";
3
+ import { message } from "../product.js";
4
+ /** The file a package's manifest is read from. */
5
+ export const MANIFEST = "package.json";
6
+ /** The directory a package manager installs packages into, whose files are no project's own. */
7
+ export const INSTALLED_PACKAGES = "node_modules";
8
+ /** The manifest members a package declares the packages it depends on under. */
9
+ export const DEPENDENCY_SECTIONS = [
10
+ "dependencies",
11
+ "devDependencies",
12
+ "peerDependencies",
13
+ "optionalDependencies",
14
+ ];
15
+ /** Whether one path passes through an installed-packages directory. */
16
+ export function inInstalledPackages(path) {
17
+ return path.split("/").includes(INSTALLED_PACKAGES);
18
+ }
19
+ /** One JSON file, read and parsed, as {@link JSONRead} tells its states apart. */
20
+ export function readJSONFile(host, path) {
21
+ let text;
22
+ try {
23
+ text = host.readFile(path);
24
+ }
25
+ catch (error) {
26
+ return { kind: "unread", reason: message(error) };
27
+ }
28
+ try {
29
+ return { kind: "read", text, value: JSON.parse(text) };
30
+ }
31
+ catch {
32
+ return { kind: "malformed" };
33
+ }
34
+ }
35
+ /** One JSON file's text and decoded value, or undefined where it cannot be read or parsed. */
36
+ export function readDocument(host, path) {
37
+ const read = readJSONFile(host, path);
38
+ return read.kind === "read" ? { text: read.text, value: read.value } : undefined;
39
+ }
40
+ /** One JSON file's decoded value, or undefined where it cannot be read or parsed. */
41
+ export function readJSON(host, path) {
42
+ return readDocument(host, path)?.value;
43
+ }
44
+ /**
45
+ * The manifest in one directory, decoded, or undefined where none reads as an object. A
46
+ * manifest this program cannot read declares nothing, the same answer as an absent one:
47
+ * the package manager owns the refusal of a manifest it rejects.
48
+ */
49
+ export function manifestAt(host, dir) {
50
+ const value = readJSON(host, joinPath(dir, MANIFEST));
51
+ return isRecord(value) ? value : undefined;
52
+ }
53
+ /** The manifest in one directory, as {@link ManifestRead} tells its states apart. */
54
+ export function readManifestAt(host, dir) {
55
+ if (!holdsManifest(host, dir)) {
56
+ return { kind: "absent" };
57
+ }
58
+ const read = readJSONFile(host, joinPath(dir, MANIFEST));
59
+ if (read.kind !== "read") {
60
+ return read;
61
+ }
62
+ return isRecord(read.value) ? { kind: "read", manifest: read.value } : { kind: "malformed" };
63
+ }
64
+ /** Whether one directory holds a manifest file. */
65
+ export function holdsManifest(host, dir) {
66
+ return host.kindOf(joinPath(dir, MANIFEST)) === "file";
67
+ }
@@ -0,0 +1,88 @@
1
+ import { ancestors, joinPath, resolvePath } from "../paths.js";
2
+ import { LANGUAGE_ROWS } from "../contract/contract.generated.js";
3
+ import { LANGUAGE } from "../product.js";
4
+ import { bytewise } from "../text/compare.js";
5
+ import { globExpression } from "../text/glob.js";
6
+ /** The directories no walk of the tree enters, by the patterns the language's row names. */
7
+ const SKIPPED_DIRECTORIES = LANGUAGE_ROWS[LANGUAGE].skippedDirectories.map(globExpression);
8
+ function skipped(name) {
9
+ return SKIPPED_DIRECTORIES.some((pattern) => pattern.test(name));
10
+ }
11
+ /** One file's text without its line ending, or undefined where it cannot be read. */
12
+ function lineOf(host, path) {
13
+ try {
14
+ return host.readFile(path).replace(/\r?\n$/u, "");
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ /**
21
+ * The repository whose working tree holds `dir`: the nearest directory at or above it with a
22
+ * `.git`. A `.git` file, which a linked worktree or a submodule holds, names the repository's
23
+ * directory on its `gitdir:` line; that directory's `commondir` file, where it has one, names
24
+ * the directory shared with the main worktree, which holds `info/exclude`:
25
+ * https://git-scm.com/docs/gitrepository-layout
26
+ */
27
+ export function repositoryOf(host, dir) {
28
+ for (const root of ancestors(dir)) {
29
+ const dotGit = joinPath(root, ".git");
30
+ const kind = host.kindOf(dotGit);
31
+ if (kind === "directory") {
32
+ return { root, exclude: joinPath(dotGit, "info/exclude") };
33
+ }
34
+ if (kind === "file") {
35
+ const named = /^gitdir: (.+)$/u.exec(lineOf(host, dotGit) ?? "")?.[1];
36
+ const gitDir = named === undefined ? undefined : resolvePath(root, named);
37
+ const common = gitDir === undefined ? undefined : lineOf(host, joinPath(gitDir, "commondir"));
38
+ const shared = gitDir === undefined || common === undefined ? gitDir : resolvePath(gitDir, common);
39
+ return { root, exclude: shared === undefined ? undefined : joinPath(shared, "info/exclude") };
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ /** The entries of one directory, none where it cannot be read. */
45
+ export function entriesOf(host, dir) {
46
+ try {
47
+ return host.readDirectory(dir);
48
+ }
49
+ catch {
50
+ return [];
51
+ }
52
+ }
53
+ /** The names of the files directly in `dir` whose name `accepts`, in bytewise order. */
54
+ export function filesIn(host, dir, accepts) {
55
+ return entriesOf(host, dir)
56
+ .filter((entry) => !entry.directory && accepts(entry.name))
57
+ .map((entry) => entry.name)
58
+ .sort(bytewise);
59
+ }
60
+ /** The names of the directories directly in `dir` a walk enters, in bytewise order. */
61
+ export function directoriesIn(host, dir) {
62
+ return entriesOf(host, dir)
63
+ .filter((entry) => entry.directory && !skipped(entry.name))
64
+ .map((entry) => entry.name)
65
+ .sort(bytewise);
66
+ }
67
+ /**
68
+ * Every file below `root` whose name `accepts`, by its path relative to `root` in bytewise
69
+ * order, read in no skipped directory.
70
+ */
71
+ export function filesBelow(host, root, accepts) {
72
+ const found = [];
73
+ const pending = [""];
74
+ for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
75
+ for (const entry of entriesOf(host, joinPath(root, dir))) {
76
+ const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
77
+ if (!entry.directory) {
78
+ if (accepts(entry.name)) {
79
+ found.push(path);
80
+ }
81
+ }
82
+ else if (!skipped(entry.name)) {
83
+ pending.push(path);
84
+ }
85
+ }
86
+ }
87
+ return found.sort(bytewise);
88
+ }
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * The generated-file class on TypeScript: every declaration in a file that marks itself
3
- * generated, or below a directory an applied convention row names as generated, is
4
- * retained, because a generator owns the file and a deletion there is undone at the next
5
- * generation.
3
+ * generated, below a directory an applied convention row names as generated, or minified,
4
+ * is retained, because a generator owns the file and a deletion there is undone at the next
5
+ * generation. The same decision is each `generated` entry of the facts document.
6
6
  */
7
+ import { isIdentifier, isImportDeclaration, isPropertyAssignment, isStringLiteralLikeNode, isVariableStatement, NodeFlags, } from "@typescript/native/ast";
8
+ import { GENERATORS } from "./contract/contract.generated.js";
9
+ import { minifiedBy } from "./javascript-file.js";
10
+ import { bytewise } from "./text/compare.js";
11
+ import { satisfies } from "./version-range.js";
7
12
  /** The generated-code header, matched against each line of a leading comment. */
8
13
  const HEADER_LINE = /^\/\/ Code generated .* DO NOT EDIT\.$/u;
9
14
  /** The documentation tag `@generated` as a word of a comment. */
@@ -14,9 +19,17 @@ const GENERATED_BY = /^\/\/ Generated by \S/u;
14
19
  const GENERATED_WORD = /\bgenerated\b/iu;
15
20
  /** The phrases a generator's notice asks a reader not to edit the file by. */
16
21
  const KEEP_OUT = /\b(?:do not edit|will be overwritten|should not make any changes)\b/iu;
17
- /** The comments before a text's first token, a leading hashbang line skipped. */
22
+ /** The comments before a text's first token, a leading hashbang line skipped, each as its lines. */
18
23
  function leadingComments(text) {
19
24
  const comments = [];
25
+ const lineAt = (offset) => text.slice(0, offset).split(/\r\n|[\n\r\u2028\u2029]/u).length;
26
+ const lines = (from, to) => {
27
+ const first = lineAt(from);
28
+ return text
29
+ .slice(from, to)
30
+ .split(/\r\n|[\n\r\u2028\u2029]/u)
31
+ .map((one, index) => ({ text: one, line: first + index }));
32
+ };
20
33
  let at = text.startsWith("\uFEFF") ? 1 : 0;
21
34
  if (text.startsWith("#!", at)) {
22
35
  const end = text.indexOf("\n", at);
@@ -29,7 +42,7 @@ function leadingComments(text) {
29
42
  if (text.startsWith("//", at)) {
30
43
  const end = text.slice(at).search(/[\r\n\u2028\u2029]/u);
31
44
  const stop = end === -1 ? text.length : at + end;
32
- comments.push(text.slice(at, stop));
45
+ comments.push(lines(at, stop));
33
46
  at = stop;
34
47
  }
35
48
  else if (text.startsWith("/*", at)) {
@@ -37,7 +50,7 @@ function leadingComments(text) {
37
50
  if (end === -1) {
38
51
  return comments;
39
52
  }
40
- comments.push(text.slice(at, end + 2));
53
+ comments.push(lines(at, end + 2));
41
54
  at = end + 2;
42
55
  }
43
56
  else {
@@ -45,41 +58,168 @@ function leadingComments(text) {
45
58
  }
46
59
  }
47
60
  }
48
- /** The detail a file's leading comments mark it generated by, or undefined where they mark nothing. */
49
- export function generatedByComment(text) {
50
- const comments = leadingComments(text);
51
- if (comments.some((one) => one.split(/\r?\n/u).some((line) => HEADER_LINE.test(line)))) {
52
- return "generated by the header line its leading comments hold";
61
+ /** The mark one file's leading comments make it generated by, or undefined where they mark nothing. */
62
+ export function generatedMark(text) {
63
+ const lines = leadingComments(text).flat();
64
+ const header = lines.find((one) => HEADER_LINE.test(one.text));
65
+ if (header !== undefined) {
66
+ return { evidence: "header", line: header.line, detail: header.text };
53
67
  }
54
- if (comments.some((one) => GENERATED_TAG.test(one))) {
55
- return "generated by the @generated tag its leading comments hold";
68
+ const tag = lines.find((one) => GENERATED_TAG.test(one.text));
69
+ if (tag !== undefined) {
70
+ return { evidence: "tag", line: tag.line, detail: tag.text };
56
71
  }
57
- const notice = comments.join("\n");
58
- if (GENERATED_BY.test(comments[0] ?? "") ||
59
- (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice))) {
60
- return "generated by the notice its leading comments hold";
72
+ const [first] = lines;
73
+ if (first !== undefined && GENERATED_BY.test(first.text)) {
74
+ return { evidence: "notice", line: first.line, detail: first.text };
75
+ }
76
+ const notice = lines.map((one) => one.text).join("\n");
77
+ if (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice)) {
78
+ const marked = lines.find((one) => GENERATED_WORD.test(one.text) || KEEP_OUT.test(one.text));
79
+ return marked === undefined
80
+ ? undefined
81
+ : { evidence: "notice", line: marked.line, detail: marked.text };
82
+ }
83
+ return undefined;
84
+ }
85
+ /** What one form's first group captures, for each line the form matches. */
86
+ function after(lines, form) {
87
+ return lines.flatMap((one) => form.exec(one.text)?.[1] ?? []);
88
+ }
89
+ /**
90
+ * Where each plugin's output states its version and its schema files, as `references-ts.36`
91
+ * states: a plugin missing here states no version, and one with no schema form names none.
92
+ */
93
+ const STATEMENTS = {
94
+ "protoc-gen-es": {
95
+ version: /^\/\/ @generated by protoc-gen-es (\S+)/u,
96
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
97
+ },
98
+ "protoc-gen-connect-es": {
99
+ version: /^\/\/ @generated by protoc-gen-connect-es (\S+)/u,
100
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
101
+ },
102
+ "protobuf-ts": {
103
+ version: /^\/\/ @generated by protobuf-ts (\S+)/u,
104
+ schemaFiles: /^\/\/ @generated from protobuf file "([^"]+)"/u,
105
+ },
106
+ "ts-proto": {
107
+ version: /^\/\/\s+protoc-gen-ts_proto\s+(\S+)$/u,
108
+ schemaFiles: /^\/\/ source: (.+)$/u,
109
+ },
110
+ "protoc-gen-grpc-web": {
111
+ version: /^\/\/\s+protoc-gen-grpc-web\s+(\S+)$/u,
112
+ schemaFiles: /^\/\/ source: (.+)$/u,
113
+ },
114
+ "protoc-gen-js": { schemaFiles: /^\/\/ source: (.+)$/u },
115
+ "ts-protoc-gen": { schemaFiles: /^\/\/ file: (.+)$/u },
116
+ };
117
+ /** A ts-proto service path literal: `/<package>.<service>/<method>`. */
118
+ const SERVICE_PATH = /^\/(?:\w+\.)+\w+\/\w+$/u;
119
+ /** Whether one ts-proto file writes a service path, as `references-ts.36` reads one. */
120
+ function writesServicePath(file) {
121
+ const named = file.statements.some((statement) => isVariableStatement(statement) &&
122
+ (statement.declarationList.flags & NodeFlags.Const) !== 0 &&
123
+ statement.declarationList.declarations.some((one) => isIdentifier(one.name) &&
124
+ one.name.text.endsWith("ServiceName") &&
125
+ one.initializer !== undefined &&
126
+ isStringLiteralLikeNode(one.initializer)));
127
+ let found = named;
128
+ const visit = (node) => {
129
+ if (found) {
130
+ return;
131
+ }
132
+ if (isPropertyAssignment(node) &&
133
+ isIdentifier(node.name) &&
134
+ node.name.text === "path" &&
135
+ isStringLiteralLikeNode(node.initializer) &&
136
+ SERVICE_PATH.test(node.initializer.text)) {
137
+ found = true;
138
+ return;
139
+ }
140
+ node.forEachChild(visit);
141
+ };
142
+ file.forEachChild(visit);
143
+ return found;
144
+ }
145
+ /** Whether one file imports a module by its specifier. */
146
+ function imports(file, module) {
147
+ return file.statements.some((statement) => isImportDeclaration(statement) &&
148
+ isStringLiteralLikeNode(statement.moduleSpecifier) &&
149
+ statement.moduleSpecifier.text === module);
150
+ }
151
+ /** The option sets the rows of `references-ts.36` name, by row; every other row's is met by any output. */
152
+ const OPTION_SETS = {
153
+ "ts-proto-2-output-schema": (file) => imports(file, "ts-proto-descriptors"),
154
+ "ts-proto-2": writesServicePath,
155
+ "ts-proto-2-types": (file) => !writesServicePath(file),
156
+ };
157
+ /** The first generator row one file matches, in `generators.json`'s order, as `bindings.3` reads it. */
158
+ export function generatorOf(file) {
159
+ const lines = leadingComments(file.text).flat();
160
+ for (const row of GENERATORS) {
161
+ const header = new RegExp(row.header, "u");
162
+ if (!lines.some((one) => header.test(one.text))) {
163
+ continue;
164
+ }
165
+ const statements = STATEMENTS[row.plugin] ?? {};
166
+ const [version] = statements.version === undefined ? [] : after(lines, statements.version);
167
+ if ("versions" in row &&
168
+ (version === undefined ||
169
+ !satisfies(version.replace(/^v/u, ""), `>=${row.versions.min.replace(/^v/u, "")} <=${row.versions.max.replace(/^v/u, "")}`))) {
170
+ continue;
171
+ }
172
+ if (OPTION_SETS[row.id]?.(file) === false) {
173
+ continue;
174
+ }
175
+ const schemaFiles = statements.schemaFiles === undefined
176
+ ? []
177
+ : [
178
+ ...new Set(after(lines, statements.schemaFiles).filter((one) => one.endsWith(".proto"))),
179
+ ].sort(bytewise);
180
+ return { row: row.id, ...(version === undefined ? {} : { version }), schemaFiles };
61
181
  }
62
182
  return undefined;
63
183
  }
64
184
  /**
65
- * Every own file of one project that is generated, by its path below the target root,
66
- * with the detail a retention in it names: its leading comments' mark, else the row
67
- * naming its directory as generated.
185
+ * Every own file of one project that is generated, by its path below the target root, with
186
+ * what decided it: its leading comments' mark, else the row naming its directory as
187
+ * generated, else its minified name or text; and the generator row it matches.
68
188
  */
69
189
  export function generatedFilesOf(files, byRow) {
70
190
  const found = new Map();
71
191
  for (const [path, file] of files.byPath) {
72
192
  const row = byRow.get(path);
73
- const detail = generatedByComment(file.text) ??
193
+ const minified = row === undefined ? minifiedBy(file.fileName, file.text) : undefined;
194
+ const fact = generatedMark(file.text) ??
74
195
  (row === undefined
75
- ? undefined
76
- : `generated below a directory the convention row ${row} names`);
77
- if (detail !== undefined) {
78
- found.set(path, detail);
196
+ ? minified === undefined
197
+ ? undefined
198
+ : { evidence: "minified", line: 1, detail: minified }
199
+ : { evidence: "convention", line: 1, detail: row });
200
+ if (fact === undefined) {
201
+ continue;
79
202
  }
203
+ const generator = generatorOf(file);
204
+ found.set(path, generator === undefined ? fact : { ...fact, generator });
80
205
  }
81
206
  return found;
82
207
  }
208
+ /** The detail a retention in a generated file names, from what decided the file. */
209
+ function retentionDetail(fact) {
210
+ switch (fact.evidence) {
211
+ case "header":
212
+ return "generated by the header line its leading comments hold";
213
+ case "tag":
214
+ return "generated by the @generated tag its leading comments hold";
215
+ case "notice":
216
+ return "generated by the notice its leading comments hold";
217
+ case "convention":
218
+ return `generated below a directory the convention row ${fact.detail} names`;
219
+ case "minified":
220
+ return "generated as a minified JavaScript file";
221
+ }
222
+ }
83
223
  /** The generated-file detector, recording each file's first line. */
84
224
  export function generatedFile(input) {
85
225
  const generated = input.generated;
@@ -87,9 +227,15 @@ export function generatedFile(input) {
87
227
  return [];
88
228
  }
89
229
  return input.held.symbols.flatMap((symbol) => {
90
- const detail = generated.get(symbol.position.path);
91
- return symbol.kind === "file" || detail === undefined
230
+ const fact = generated.get(symbol.position.path);
231
+ return symbol.kind === "file" || fact === undefined
92
232
  ? []
93
- : [{ id: symbol.id, detail, site: { path: symbol.position.path, line: 1, column: 1 } }];
233
+ : [
234
+ {
235
+ id: symbol.id,
236
+ detail: retentionDetail(fact),
237
+ site: { path: symbol.position.path, line: 1, column: 1 },
238
+ },
239
+ ];
94
240
  });
95
241
  }