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

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 (431) 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 +35 -9
  6. package/dist/src/amd.js +81 -0
  7. package/dist/src/analysis.js +274 -496
  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/commands/held.js +499 -0
  12. package/dist/src/commands/read.js +161 -0
  13. package/dist/src/commands/shell.js +169 -0
  14. package/dist/src/commands/tools.js +1847 -0
  15. package/dist/src/component-files.js +3 -2
  16. package/dist/src/component-layer.js +4 -2
  17. package/dist/src/config.js +6 -82
  18. package/dist/src/conformance-record.js +2 -2
  19. package/dist/src/consumers.js +10 -38
  20. package/dist/src/contextual-uses.js +57 -26
  21. package/dist/src/contract/config.generated.js +561 -0
  22. package/dist/src/contract/contract.generated.js +1370 -0
  23. package/dist/src/contract/report.generated.js +866 -0
  24. package/dist/src/dependencies.js +165 -122
  25. package/dist/src/destructuring.js +1 -19
  26. package/dist/src/edges.js +38 -122
  27. package/dist/src/element-access.js +181 -168
  28. package/dist/src/entries/build-scripts.js +222 -0
  29. package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
  30. package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
  31. package/dist/src/entries/conventions.js +740 -0
  32. package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
  33. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
  34. package/dist/src/entries/run-directories.js +77 -0
  35. package/dist/src/entries/sources.js +144 -0
  36. package/dist/src/entries/static-module.js +670 -0
  37. package/dist/src/entries/test-runners.js +650 -0
  38. package/dist/src/entries/workflow-steps.js +345 -0
  39. package/dist/src/exemptions/argument-places.js +131 -0
  40. package/dist/src/exemptions/calls.js +338 -0
  41. package/dist/src/exemptions/classes.js +13 -0
  42. package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
  43. package/dist/src/exemptions/detectors.js +48 -0
  44. package/dist/src/{enum-group.js → exemptions/enum-group.js} +9 -16
  45. package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +7 -11
  46. package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +76 -54
  47. package/dist/src/exemptions/generated-file.js +241 -0
  48. package/dist/src/{injection-container.js → exemptions/injection-container.js} +20 -26
  49. package/dist/src/exemptions/installed-globals.js +173 -0
  50. package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
  51. package/dist/src/exemptions/lifecycle.js +150 -0
  52. package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
  53. package/dist/src/exemptions/serialization-contract.js +1276 -0
  54. package/dist/src/exemptions/sites.js +182 -0
  55. package/dist/src/{template-field.js → exemptions/template-field.js} +13 -23
  56. package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
  57. package/dist/src/exposure.js +400 -0
  58. package/dist/src/extensions.js +37 -0
  59. package/dist/src/facts.js +38 -0
  60. package/dist/src/failure.js +36 -0
  61. package/dist/src/file-facts.js +3 -26
  62. package/dist/src/findings/completion.js +109 -56
  63. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  64. package/dist/src/findings/emitters.js +27 -41
  65. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  66. package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
  67. package/dist/src/findings/interfaces.js +4 -5
  68. package/dist/src/findings/intra-function.js +16 -13
  69. package/dist/src/findings/non-code-artifacts.js +17 -12
  70. package/dist/src/findings/reads-and-writes.js +86 -27
  71. package/dist/src/findings/self-check.js +44 -30
  72. package/dist/src/findings/unused-declarations.js +29 -45
  73. package/dist/src/findings/visibility-narrowing.js +23 -31
  74. package/dist/src/findings-pass.js +13 -21
  75. package/dist/src/fs/document.js +52 -0
  76. package/dist/src/fs/manifest.js +67 -0
  77. package/dist/src/fs/tree.js +88 -0
  78. package/dist/src/git-ignore.js +39 -26
  79. package/dist/src/import-bindings.js +73 -28
  80. package/dist/src/index/sites.js +557 -0
  81. package/dist/src/index/values.js +124 -0
  82. package/dist/src/index.js +2 -2
  83. package/dist/src/indexed-access.js +523 -478
  84. package/dist/src/intra-function-parts.js +63 -43
  85. package/dist/src/inventory/commonjs.js +507 -0
  86. package/dist/src/inventory/enumerate.js +167 -0
  87. package/dist/src/inventory/syntax.js +236 -0
  88. package/dist/src/inventory.js +323 -724
  89. package/dist/src/javascript-file.js +48 -0
  90. package/dist/src/json-document.js +198 -67
  91. package/dist/src/keyof-comparisons.js +126 -107
  92. package/dist/src/kinds.js +3 -366
  93. package/dist/src/{components.js → liveness/components.js} +2 -3
  94. package/dist/src/{graph.js → liveness/graph.js} +52 -28
  95. package/dist/src/liveness/matrix.js +99 -0
  96. package/dist/src/liveness/sweep.js +558 -0
  97. package/dist/src/load/classify.js +289 -0
  98. package/dist/src/load/diagnostics.js +87 -0
  99. package/dist/src/load/discover.js +285 -0
  100. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  101. package/dist/src/load/expected-module.js +136 -0
  102. package/dist/src/load/load.js +27 -0
  103. package/dist/src/load/module-lookup.js +37 -0
  104. package/dist/src/load/package-scope.js +56 -0
  105. package/dist/src/load/plan.js +313 -0
  106. package/dist/src/load/specifiers.js +113 -0
  107. package/dist/src/load/synthesize.js +152 -0
  108. package/dist/src/load/test-globals.js +207 -0
  109. package/dist/src/load/test-joins.js +82 -0
  110. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  111. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  112. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
  113. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  114. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  115. package/dist/src/module-declarations.js +70 -85
  116. package/dist/src/overrides.js +134 -112
  117. package/dist/src/partial.js +2 -1
  118. package/dist/src/paths.js +82 -16
  119. package/dist/src/platform-calls.js +78 -0
  120. package/dist/src/platform-symbols.js +134 -0
  121. package/dist/src/position.js +43 -1
  122. package/dist/src/print.js +11 -79
  123. package/dist/src/product.js +13 -0
  124. package/dist/src/query.js +95 -0
  125. package/dist/src/ref.js +12 -20
  126. package/dist/src/references/read-rule.js +1 -0
  127. package/dist/src/references/read-rules.js +48 -0
  128. package/dist/src/references/sink.js +237 -0
  129. package/dist/src/references/sources.js +450 -0
  130. package/dist/src/references.js +117 -1074
  131. package/dist/src/report-schema.js +51 -0
  132. package/dist/src/report.js +57 -41
  133. package/dist/src/reporters/annotations.js +15 -8
  134. package/dist/src/reporters/sarif.js +16 -10
  135. package/dist/src/reporters/template.js +4 -19
  136. package/dist/src/resolve.js +246 -730
  137. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
  138. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
  139. package/dist/src/roots/manifest.js +216 -0
  140. package/dist/src/roots/published-reach.js +412 -0
  141. package/dist/src/roots/root-set.js +487 -0
  142. package/dist/src/roots/unseen-loads.js +236 -0
  143. package/dist/src/run.js +66 -77
  144. package/dist/src/schema.js +320 -343
  145. package/dist/src/scope.js +38 -115
  146. package/dist/src/session.js +37 -29
  147. package/dist/src/source-files.js +6 -10
  148. package/dist/src/stores.js +4 -2
  149. package/dist/src/suppress-file.js +25 -28
  150. package/dist/src/suppress-inline.js +36 -83
  151. package/dist/src/suppress.js +50 -71
  152. package/dist/src/symbol-keys.js +92 -88
  153. package/dist/src/tests/file-class.js +76 -0
  154. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  155. package/dist/src/{test-support.js → tests/support.js} +5 -12
  156. package/dist/src/text/compare.js +26 -0
  157. package/dist/src/text/glob.js +194 -0
  158. package/dist/src/text/named-files.js +298 -0
  159. package/dist/src/text/yaml.js +684 -0
  160. package/dist/src/type-errors.js +11 -232
  161. package/dist/src/type-parameter-reads.js +178 -139
  162. package/dist/src/unimplemented-callees.js +262 -242
  163. package/dist/src/unwrap.js +23 -0
  164. package/dist/src/usage.js +10 -0
  165. package/dist/src/vendor/unbash/ansi-c.js +132 -0
  166. package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
  167. package/dist/src/vendor/unbash/arithmetic.js +591 -0
  168. package/dist/src/vendor/unbash/assignment.js +131 -0
  169. package/dist/src/vendor/unbash/chars.js +48 -0
  170. package/dist/src/vendor/unbash/command.js +41 -0
  171. package/dist/src/vendor/unbash/expansion.js +80 -0
  172. package/dist/src/vendor/unbash/glob.js +268 -0
  173. package/dist/src/vendor/unbash/heredoc.js +27 -0
  174. package/dist/src/vendor/unbash/internal-types.js +1 -0
  175. package/dist/src/vendor/unbash/lexer.js +4274 -0
  176. package/dist/src/vendor/unbash/limits.js +3 -0
  177. package/dist/src/vendor/unbash/parser.js +1366 -0
  178. package/dist/src/vendor/unbash/parts.js +57 -0
  179. package/dist/src/vendor/unbash/substitution-context.js +8 -0
  180. package/dist/src/vendor/unbash/traversal.js +212 -0
  181. package/dist/src/vendor/unbash/types.js +1 -0
  182. package/dist/src/vendor/unbash/word-value.js +61 -0
  183. package/dist/src/vendor/unbash/word.js +40 -0
  184. package/dist/src/verbs/analyze.js +194 -127
  185. package/dist/src/verbs/describe.js +13 -18
  186. package/dist/src/verbs/explain.js +85 -139
  187. package/dist/src/verbs/print-config.js +2 -1
  188. package/dist/src/verbs/print-projects.js +6 -4
  189. package/dist/src/verbs/print-retained.js +30 -6
  190. package/dist/src/verbs/print-roots.js +4 -2
  191. package/dist/src/verbs/verb.js +1 -5
  192. package/dist/src/verbs/version.js +14 -3
  193. package/jsr.json +2 -1
  194. package/package.json +14 -8
  195. package/src/alias-chain.ts +40 -11
  196. package/src/amd.ts +130 -0
  197. package/src/analysis.ts +426 -733
  198. package/src/augmentations.ts +93 -4
  199. package/src/automatic-types.ts +12 -30
  200. package/src/bindings.ts +460 -0
  201. package/src/commands/held.ts +632 -0
  202. package/src/commands/read.ts +242 -0
  203. package/src/commands/shell.ts +228 -0
  204. package/src/commands/tools.ts +2382 -0
  205. package/src/component-files.ts +4 -2
  206. package/src/component-layer.ts +4 -2
  207. package/src/config.ts +31 -302
  208. package/src/conformance-record.ts +2 -2
  209. package/src/consumers.ts +16 -66
  210. package/src/contextual-uses.ts +66 -34
  211. package/src/contract/config.generated.ts +821 -0
  212. package/src/contract/contract.generated.ts +1453 -0
  213. package/src/contract/report.generated.ts +1241 -0
  214. package/src/dependencies.ts +223 -128
  215. package/src/destructuring.ts +1 -31
  216. package/src/edges.ts +41 -140
  217. package/src/element-access.ts +190 -187
  218. package/src/entries/build-scripts.ts +292 -0
  219. package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
  220. package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
  221. package/src/{conventions.ts → entries/conventions.ts} +367 -583
  222. package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
  223. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
  224. package/src/entries/run-directories.ts +108 -0
  225. package/src/entries/sources.ts +266 -0
  226. package/src/entries/static-module.ts +858 -0
  227. package/src/entries/test-runners.ts +835 -0
  228. package/src/entries/workflow-steps.ts +432 -0
  229. package/src/exemptions/argument-places.ts +198 -0
  230. package/src/exemptions/calls.ts +483 -0
  231. package/src/exemptions/classes.ts +49 -0
  232. package/src/exemptions/decorator.ts +56 -0
  233. package/src/exemptions/detectors.ts +75 -0
  234. package/src/{enum-group.ts → exemptions/enum-group.ts} +10 -19
  235. package/src/{erased-stores.ts → exemptions/erased-stores.ts} +7 -14
  236. package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +85 -66
  237. package/src/exemptions/generated-file.ts +318 -0
  238. package/src/{injection-container.ts → exemptions/injection-container.ts} +29 -34
  239. package/src/exemptions/installed-globals.ts +255 -0
  240. package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
  241. package/src/exemptions/lifecycle.ts +286 -0
  242. package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
  243. package/src/exemptions/serialization-contract.ts +1729 -0
  244. package/src/exemptions/sites.ts +293 -0
  245. package/src/{template-field.ts → exemptions/template-field.ts} +22 -32
  246. package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
  247. package/src/exposure.ts +492 -0
  248. package/src/extensions.ts +45 -0
  249. package/src/facts.ts +46 -0
  250. package/src/failure.ts +63 -0
  251. package/src/file-facts.ts +15 -25
  252. package/src/finding.ts +10 -10
  253. package/src/findings/boundary.ts +11 -0
  254. package/src/findings/completion.ts +133 -64
  255. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  256. package/src/findings/emitter.ts +9 -10
  257. package/src/findings/emitters.ts +38 -52
  258. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  259. package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
  260. package/src/findings/interfaces.ts +7 -9
  261. package/src/findings/intra-function.ts +16 -14
  262. package/src/findings/non-code-artifacts.ts +24 -15
  263. package/src/findings/reads-and-writes.ts +118 -41
  264. package/src/findings/self-check.ts +51 -38
  265. package/src/findings/unused-declarations.ts +31 -49
  266. package/src/findings/visibility-narrowing.ts +36 -40
  267. package/src/findings-pass.ts +15 -31
  268. package/src/fs/document.ts +70 -0
  269. package/src/fs/manifest.ts +96 -0
  270. package/src/fs/tree.ts +110 -0
  271. package/src/git-ignore.ts +50 -26
  272. package/src/host.ts +12 -5
  273. package/src/import-bindings.ts +98 -29
  274. package/src/index/sites.ts +781 -0
  275. package/src/index/values.ts +170 -0
  276. package/src/index.ts +2 -2
  277. package/src/indexed-access.ts +570 -527
  278. package/src/intra-function-parts.ts +73 -44
  279. package/src/inventory/commonjs.ts +696 -0
  280. package/src/inventory/enumerate.ts +203 -0
  281. package/src/inventory/syntax.ts +311 -0
  282. package/src/inventory.ts +442 -895
  283. package/src/javascript-file.ts +54 -0
  284. package/src/json-document.ts +241 -76
  285. package/src/keyof-comparisons.ts +133 -122
  286. package/src/kinds.ts +5 -413
  287. package/src/{components.ts → liveness/components.ts} +4 -6
  288. package/src/{graph.ts → liveness/graph.ts} +100 -35
  289. package/src/liveness/matrix.ts +162 -0
  290. package/src/liveness/sweep.ts +855 -0
  291. package/src/load/classify.ts +433 -0
  292. package/src/load/diagnostics.ts +154 -0
  293. package/src/load/discover.ts +471 -0
  294. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  295. package/src/load/expected-module.ts +194 -0
  296. package/src/load/load.ts +73 -0
  297. package/src/load/module-lookup.ts +50 -0
  298. package/src/load/package-scope.ts +74 -0
  299. package/src/load/plan.ts +513 -0
  300. package/src/load/specifiers.ts +171 -0
  301. package/src/load/synthesize.ts +230 -0
  302. package/src/load/test-globals.ts +251 -0
  303. package/src/load/test-joins.ts +98 -0
  304. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  305. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  306. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
  307. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  308. package/src/{workspace.ts → load/workspace.ts} +90 -134
  309. package/src/module-declarations.ts +74 -98
  310. package/src/overrides.ts +162 -134
  311. package/src/partial.ts +3 -2
  312. package/src/paths.ts +92 -16
  313. package/src/platform-calls.ts +119 -0
  314. package/src/platform-symbols.ts +189 -0
  315. package/src/position.ts +44 -1
  316. package/src/print.ts +12 -92
  317. package/src/product.ts +18 -0
  318. package/src/query.ts +201 -10
  319. package/src/ref.ts +12 -20
  320. package/src/references/read-rule.ts +60 -0
  321. package/src/references/read-rules.ts +55 -0
  322. package/src/references/sink.ts +314 -0
  323. package/src/references/sources.ts +632 -0
  324. package/src/references.ts +187 -1450
  325. package/src/report-schema.ts +68 -0
  326. package/src/report.ts +103 -226
  327. package/src/reporters/annotations.ts +22 -11
  328. package/src/reporters/json.ts +1 -1
  329. package/src/reporters/reporter.ts +2 -4
  330. package/src/reporters/reporters.ts +1 -1
  331. package/src/reporters/sarif.ts +29 -16
  332. package/src/reporters/template.ts +4 -20
  333. package/src/reporters/text.ts +3 -3
  334. package/src/resolve.ts +284 -1115
  335. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +26 -29
  336. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
  337. package/src/roots/manifest.ts +303 -0
  338. package/src/roots/published-reach.ts +486 -0
  339. package/src/{roots.ts → roots/root-set.ts} +286 -469
  340. package/src/roots/unseen-loads.ts +345 -0
  341. package/src/run.ts +75 -91
  342. package/src/schema.ts +391 -388
  343. package/src/scope.ts +46 -138
  344. package/src/session.ts +87 -55
  345. package/src/source-files.ts +6 -11
  346. package/src/stores.ts +15 -3
  347. package/src/suppress-file.ts +24 -33
  348. package/src/suppress-inline.ts +55 -91
  349. package/src/suppress.ts +78 -79
  350. package/src/symbol-keys.ts +104 -107
  351. package/src/tests/file-class.ts +135 -0
  352. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  353. package/src/{test-support.ts → tests/support.ts} +15 -21
  354. package/src/text/compare.ts +27 -0
  355. package/src/text/glob.ts +210 -0
  356. package/src/text/named-files.ts +388 -0
  357. package/src/text/yaml.ts +747 -0
  358. package/src/type-errors.ts +18 -288
  359. package/src/type-parameter-reads.ts +187 -158
  360. package/src/unimplemented-callees.ts +297 -280
  361. package/src/unwrap.ts +34 -0
  362. package/src/usage.ts +10 -0
  363. package/src/vendor/unbash/LICENSE +15 -0
  364. package/src/vendor/unbash/VENDORED.md +16 -0
  365. package/src/vendor/unbash/ansi-c.ts +132 -0
  366. package/src/vendor/unbash/arithmetic-parts.ts +53 -0
  367. package/src/vendor/unbash/arithmetic.ts +641 -0
  368. package/src/vendor/unbash/assignment.ts +163 -0
  369. package/src/vendor/unbash/chars.ts +48 -0
  370. package/src/vendor/unbash/command.ts +47 -0
  371. package/src/vendor/unbash/expansion.ts +144 -0
  372. package/src/vendor/unbash/glob.ts +284 -0
  373. package/src/vendor/unbash/heredoc.ts +34 -0
  374. package/src/vendor/unbash/internal-types.ts +94 -0
  375. package/src/vendor/unbash/lexer.ts +4352 -0
  376. package/src/vendor/unbash/limits.ts +3 -0
  377. package/src/vendor/unbash/parser.ts +1612 -0
  378. package/src/vendor/unbash/parts.ts +96 -0
  379. package/src/vendor/unbash/substitution-context.ts +20 -0
  380. package/src/vendor/unbash/traversal.ts +182 -0
  381. package/src/vendor/unbash/types.ts +660 -0
  382. package/src/vendor/unbash/word-value.ts +64 -0
  383. package/src/vendor/unbash/word.ts +50 -0
  384. package/src/verbs/analyze.ts +255 -164
  385. package/src/verbs/describe.ts +19 -17
  386. package/src/verbs/explain.ts +93 -169
  387. package/src/verbs/print-config.ts +2 -1
  388. package/src/verbs/print-projects.ts +6 -4
  389. package/src/verbs/print-retained.ts +36 -6
  390. package/src/verbs/print-roots.ts +4 -2
  391. package/src/verbs/verb.ts +2 -8
  392. package/src/verbs/version.ts +15 -3
  393. package/dist/src/base-url.js +0 -18
  394. package/dist/src/calls.js +0 -155
  395. package/dist/src/conventions.js +0 -937
  396. package/dist/src/discover.js +0 -468
  397. package/dist/src/entry-points.js +0 -529
  398. package/dist/src/exempt-classes.js +0 -75
  399. package/dist/src/exempt.js +0 -209
  400. package/dist/src/generated-file.js +0 -95
  401. package/dist/src/glob.js +0 -65
  402. package/dist/src/manifest.js +0 -165
  403. package/dist/src/matrix.js +0 -246
  404. package/dist/src/pnpm-workspace.js +0 -120
  405. package/dist/src/roots.js +0 -672
  406. package/dist/src/serialization-contract.js +0 -611
  407. package/dist/src/setup-failure.js +0 -23
  408. package/dist/src/sweep.js +0 -224
  409. package/dist/src/test-joins.js +0 -101
  410. package/dist/src/value-flow.js +0 -74
  411. package/dist/src/version.js +0 -7
  412. package/dist/src/workflow-steps.js +0 -185
  413. package/src/base-url.ts +0 -24
  414. package/src/calls.ts +0 -225
  415. package/src/decorator.ts +0 -78
  416. package/src/discover.ts +0 -653
  417. package/src/entry-points.ts +0 -687
  418. package/src/exempt-classes.ts +0 -123
  419. package/src/exempt.ts +0 -323
  420. package/src/generated-file.ts +0 -111
  421. package/src/glob.ts +0 -67
  422. package/src/manifest.ts +0 -234
  423. package/src/matrix.ts +0 -423
  424. package/src/pnpm-workspace.ts +0 -135
  425. package/src/serialization-contract.ts +0 -849
  426. package/src/setup-failure.ts +0 -39
  427. package/src/sweep.ts +0 -347
  428. package/src/test-joins.ts +0 -120
  429. package/src/value-flow.ts +0 -104
  430. package/src/version.ts +0 -7
  431. package/src/workflow-steps.ts +0 -216
@@ -1,17 +1,15 @@
1
+ import { CODE } from "../contract/contract.generated.ts";
1
2
  import type { Finding } from "../finding.ts";
2
- import { OUTSIDE, type Graph } from "../graph.ts";
3
- import type { InventorySymbol, SymbolKind } from "../inventory.ts";
4
- import type { MatrixCandidate } from "../matrix.ts";
3
+ import { OUTSIDE, type Graph } from "../liveness/graph.ts";
4
+ import { WORDS, type InventorySymbol } from "../inventory.ts";
5
+ import type { Candidate } from "../liveness/sweep.ts";
5
6
  import { declarationKey } from "../ref.ts";
6
- import type { RootKind } from "../roots.ts";
7
- import { runnerCalls } from "../sweep.ts";
7
+ import type { RootKind } from "../roots/root-set.ts";
8
+ import { runnerCalls } from "../liveness/sweep.ts";
9
+ import { exposedTypes, exposes } from "../exposure.ts";
8
10
  import type { Emitter, EmitterInput } from "./emitter.ts";
9
11
  import { writeOnlyDeclarations } from "./reads-and-writes.ts";
10
12
 
11
- const UNNECESSARY_EXPORT = "DS1101";
12
- const UNREACHABLE_EXPORT = "DS1103";
13
- const REDUNDANT_EXPORT_KEYWORD = "DS1104";
14
-
15
13
  /** How far the references to one declaration reach, from the narrowest. */
16
14
  type Reach = "file" | "package" | "outside";
17
15
 
@@ -22,18 +20,6 @@ type Reach = "file" | "package" | "outside";
22
20
  */
23
21
  const PUBLISHING: ReadonlySet<RootKind> = new Set<RootKind>(["manifest-entry", "published-api"]);
24
22
 
25
- /** The reader's word for each kind of declaration this family reports. */
26
- const WORDS: Partial<Readonly<Record<SymbolKind, string>>> = {
27
- function: "function",
28
- class: "class",
29
- interface: "interface",
30
- type: "type alias",
31
- enum: "enum",
32
- namespace: "namespace",
33
- variable: "variable",
34
- "export-alias": "re-export",
35
- };
36
-
37
23
  /** Per declaration of the run, the facts both rules read. */
38
24
  interface Facts {
39
25
  readonly graph: Graph;
@@ -145,14 +131,10 @@ function topLevelExport(facts: Facts, at: number, symbol: InventorySymbol): bool
145
131
  return symbol.exported && parent !== OUTSIDE && facts.graph.symbols[parent]?.kind === "file";
146
132
  }
147
133
 
148
- function wordOf(symbol: InventorySymbol): string {
149
- return WORDS[symbol.kind] ?? symbol.kind;
150
- }
151
-
152
134
  /** The visibility each narrowing code names, which only the narrowing codes carry. */
153
135
  const NARROWER: Readonly<Record<string, "file" | "package">> = {
154
- [REDUNDANT_EXPORT_KEYWORD]: "file",
155
- [UNNECESSARY_EXPORT]: "package",
136
+ [CODE.redundantExportKeyword]: "file",
137
+ [CODE.unnecessaryExport]: "package",
156
138
  };
157
139
 
158
140
  function findingOf(symbol: InventorySymbol, code: string, message: string): Finding {
@@ -184,7 +166,11 @@ function narrowing(
184
166
  facts: Facts,
185
167
  at: number,
186
168
  symbol: InventorySymbol,
187
- world: { readonly closed: boolean; readonly writeOnly: ReadonlySet<string> },
169
+ world: {
170
+ readonly closed: boolean;
171
+ readonly writeOnly: ReadonlySet<string>;
172
+ readonly exposed: ReadonlySet<string> | "every";
173
+ },
188
174
  ): Finding | undefined {
189
175
  const { closed } = world;
190
176
  const file = facts.fileOf[at] ?? OUTSIDE;
@@ -198,7 +184,7 @@ function narrowing(
198
184
  ) {
199
185
  return undefined;
200
186
  }
201
- const word = wordOf(symbol);
187
+ const word = WORDS[symbol.kind];
202
188
  switch (facts.reach[at]) {
203
189
  case "file":
204
190
  if (facts.published[file] === true && !closed) {
@@ -206,16 +192,16 @@ function narrowing(
206
192
  }
207
193
  return findingOf(
208
194
  symbol,
209
- REDUNDANT_EXPORT_KEYWORD,
195
+ CODE.redundantExportKeyword,
210
196
  `exported ${word} is referenced only inside the file that declares it`,
211
197
  );
212
198
  case "package":
213
- if (!closed || !publishes(facts.rootedBy[at])) {
199
+ if (!closed || !publishes(facts.rootedBy[at]) || exposes(world.exposed, symbol)) {
214
200
  return undefined;
215
201
  }
216
202
  return findingOf(
217
203
  symbol,
218
- UNNECESSARY_EXPORT,
204
+ CODE.unnecessaryExport,
219
205
  `exported ${word} is referenced only inside the package that declares it`,
220
206
  );
221
207
  default:
@@ -234,7 +220,7 @@ function unreachable(
234
220
  facts: Facts,
235
221
  at: number,
236
222
  symbol: InventorySymbol,
237
- candidate: MatrixCandidate,
223
+ candidate: Candidate,
238
224
  input: EmitterInput,
239
225
  ): Finding | undefined {
240
226
  if (
@@ -254,13 +240,13 @@ function unreachable(
254
240
  : "has no reference in the target";
255
241
  return findingOf(
256
242
  symbol,
257
- UNREACHABLE_EXPORT,
258
- `exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`,
243
+ CODE.unreachableExport,
244
+ `exported ${WORDS[symbol.kind]} ${found}, and nothing outside can import the file that declares it`,
259
245
  );
260
246
  }
261
247
 
262
248
  /**
263
- * The visibility-narrowing family over one run: `DS1101`, `DS1103` and `DS1104`. A finding
249
+ * The visibility-narrowing kinds over one run: `DS1501`, `DS1101` and `DS1503`. A finding
264
250
  * about a symbol a declared cross-language edge names is reported here like any other, and
265
251
  * the edge's evaluation then holds it pending, because the edge stands for a reference from
266
252
  * outside the file and the package whose use is decided in the other language. A
@@ -269,13 +255,23 @@ function unreachable(
269
255
  export function narrowings(input: EmitterInput): readonly Finding[] {
270
256
  const facts = factsOf(input.swept.matrix.union);
271
257
  const { declared, loaded } = input.boundary.consumers;
272
- const closed = input.config.consumersComplete && declared.every((id) => loaded.includes(id));
273
- const world = { closed, writeOnly: writeOnlyDeclarations(input) };
258
+ const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
259
+ const world = {
260
+ closed,
261
+ writeOnly: writeOnlyDeclarations(input),
262
+ exposed: closed
263
+ ? exposedTypes(
264
+ facts.graph,
265
+ facts.reach.map((reach) => reach === "outside"),
266
+ input.boundary.carriers,
267
+ )
268
+ : new Set<string>(),
269
+ };
274
270
  const candidates = new Map(
275
271
  input.swept.sweep.candidates.map((candidate) => [candidate.id, candidate]),
276
272
  );
277
273
  const generated =
278
- input.config.analysis.generatedFiles === "include" ? undefined : input.files.generated;
274
+ input.config.analysis.generated_files === "include" ? undefined : input.files.generated;
279
275
 
280
276
  const findings: Finding[] = [];
281
277
  facts.graph.symbols.forEach((symbol, at) => {
@@ -316,5 +312,5 @@ export function unreachableExports(input: EmitterInput): ReadonlySet<string> {
316
312
  return found;
317
313
  }
318
314
 
319
- /** The findings of the visibility-narrowing family, `DS1100` to `DS1199`. */
315
+ /** The findings of the visibility-narrowing kinds, `DS1501`, `DS1101` and `DS1503`. */
320
316
  export const visibilityNarrowing: Emitter = narrowings;
@@ -4,8 +4,9 @@
4
4
  * verdict a report carries beside them.
5
5
  */
6
6
 
7
+ import { EXIT_CLEAN, EXIT_FINDINGS, EXIT_PENDING } from "./contract/contract.generated.ts";
7
8
  import type { AnalysisInputs } from "./analysis.ts";
8
- import { fails, type Config } from "./config.ts";
9
+ import { fails, type ResolvedConfig } from "./config.ts";
9
10
  import type { EdgeEvaluation } from "./edges.ts";
10
11
  import type { CompletedFinding, Finding } from "./finding.ts";
11
12
  import { decidedFindings, type Withheld } from "./findings/emitters.ts";
@@ -15,15 +16,10 @@ import {
15
16
  type StaleRecord,
16
17
  type StaleSuppression,
17
18
  } from "./findings/self-check.ts";
18
- import {
19
- isDependencySubject,
20
- isRowSubject,
21
- pairs,
22
- suppressionCounts,
23
- type Ledger,
24
- } from "./suppress.ts";
19
+ import { namesKind, pairs, suppressionCounts, type Ledger } from "./suppress.ts";
25
20
  import type { Provenance, Recorded } from "./suppress-file.ts";
26
- import { EXIT_CLEAN, EXIT_FINDINGS } from "./verbs/verb.ts";
21
+ import { bytewise } from "./text/compare.ts";
22
+ import { PRODUCT } from "./product.ts";
27
23
 
28
24
  /** The counts a report's totals carry beside its findings. */
29
25
  interface PassTotals {
@@ -49,25 +45,14 @@ export interface PassResult {
49
45
  readonly ledger: Ledger;
50
46
  }
51
47
 
52
- /** The exit code of a report holding at least one pending finding. */
53
- export const EXIT_PENDING = 4;
54
-
55
- /** Two strings ordered bytewise. */
56
- function compare(a: string, b: string): number {
57
- if (a === b) {
58
- return 0;
59
- }
60
- return a < b ? -1 : 1;
61
- }
62
-
63
48
  /** The canonical order: path, line, column, code, then the symbol reference. */
64
49
  function canonical(a: Finding, b: Finding): number {
65
50
  return (
66
- compare(a.position.path, b.position.path) ||
51
+ bytewise(a.position.path, b.position.path) ||
67
52
  a.position.line - b.position.line ||
68
53
  a.position.column - b.position.column ||
69
- compare(a.code, b.code) ||
70
- compare(a.symbol.ref, b.symbol.ref)
54
+ bytewise(a.code, b.code) ||
55
+ bytewise(a.symbol.ref, b.symbol.ref)
71
56
  );
72
57
  }
73
58
 
@@ -118,7 +103,7 @@ export function findingsPass(inputs: AnalysisInputs): PassResult {
118
103
  * suppression is stale or a finding's severity reaches the configured failing severity;
119
104
  * clean otherwise.
120
105
  */
121
- export function verdictOf(result: PassResult, config: Config): number {
106
+ export function verdictOf(result: PassResult, config: ResolvedConfig): number {
122
107
  if (result.totals.pending > 0) {
123
108
  return EXIT_PENDING;
124
109
  }
@@ -126,7 +111,7 @@ export function verdictOf(result: PassResult, config: Config): number {
126
111
  return EXIT_FINDINGS;
127
112
  }
128
113
  const failing = result.findings.some((finding) =>
129
- fails(finding.severity, config.reporters.failOn),
114
+ fails(finding.severity, config.reporters.fail_on),
130
115
  );
131
116
  return failing ? EXIT_FINDINGS : EXIT_CLEAN;
132
117
  }
@@ -140,15 +125,14 @@ export interface RecordableFinding {
140
125
 
141
126
  /**
142
127
  * The rows one round of a baseline write records from its findings, in their order. A
143
- * finding about a row of a document other than a dependency row is not recorded: no record
144
- * withholds one, so a row recording it would be stale on the first read back. Two findings
145
- * that would record the same row, two parts of one declaration under one code, record it
146
- * once.
128
+ * finding no baseline row can name is not recorded: a row recording it would be stale on
129
+ * the first read back. Two findings that would record the same row, two parts of one
130
+ * declaration under one code, record it once.
147
131
  */
148
132
  export function recordedFindings(findings: readonly RecordableFinding[]): readonly Recorded[] {
149
133
  const rows = new Map<string, Recorded>();
150
134
  for (const finding of findings) {
151
- if (isRowSubject(finding.symbol.kind) && !isDependencySubject(finding.symbol.kind)) {
135
+ if (!namesKind(finding.symbol.kind, "baseline")) {
152
136
  continue;
153
137
  }
154
138
  const row = { code: finding.code, symbol: finding.symbol.ref, path: finding.position.path };
@@ -164,5 +148,5 @@ export function rowKey(row: Recorded): string {
164
148
 
165
149
  /** The analyzer identity a baseline this analyzer writes names as its rows' provenance. */
166
150
  export function analyzerProvenance(version: string): Provenance {
167
- return { analyzer: "deadset-ts", version };
151
+ return { analyzer: PRODUCT, version };
168
152
  }
@@ -0,0 +1,70 @@
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
+
8
+ import { CONFIG_MAX_BYTES } from "../contract/config.generated.ts";
9
+ import type { Host } from "../host.ts";
10
+ import { failureExit } from "../failure.ts";
11
+ import { joinPath, relativePath } from "../paths.ts";
12
+ import { message } from "../product.ts";
13
+ import { utf8Bytes } from "../reporters/sha256.ts";
14
+ import { UsageError } from "../usage.ts";
15
+
16
+ function unreadable(label: string, reason: string): Error {
17
+ return failureExit(
18
+ "unreadable-document",
19
+ `${label}: ${reason}: make it a regular file inside the target root that the run can read, or remove it`,
20
+ );
21
+ }
22
+
23
+ /** Refuses a document past the Contract's bound with the usage code, naming it `label`. */
24
+ export function refuseOverBound(label: string, text: string): void {
25
+ if (utf8Bytes(text).length > CONFIG_MAX_BYTES) {
26
+ throw new UsageError(`${label}: holds more than ${String(CONFIG_MAX_BYTES)} bytes`);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * The text of the document `name` at the absolute `targetRoot`, undefined where nothing is
32
+ * there. One whose name resolves outside the target root or to anything but a regular file,
33
+ * or whose read fails, is refused by `refuse` naming it `label`, nothing read from it. A
34
+ * document past the bound is read one byte past it and refused as {@link refuseOverBound} does.
35
+ */
36
+ export function readTargetDocument(
37
+ host: Host,
38
+ targetRoot: string,
39
+ name: string,
40
+ label = name,
41
+ refuse: (label: string, reason: string) => Error = unreadable,
42
+ ): string | undefined {
43
+ const attempt = <T>(read: () => T): T => {
44
+ try {
45
+ return read();
46
+ } catch (error: unknown) {
47
+ throw refuse(label, message(error));
48
+ }
49
+ };
50
+ const path = joinPath(targetRoot, name);
51
+ const kind = attempt(() => host.kindOf(path));
52
+ if (kind === "absent") {
53
+ return undefined;
54
+ }
55
+ const real = attempt(() => host.realPath(path));
56
+ if (
57
+ relativePath(
58
+ attempt(() => host.realPath(targetRoot)),
59
+ real,
60
+ ) === undefined
61
+ ) {
62
+ throw refuse(label, `resolves to ${real}, outside the target root`);
63
+ }
64
+ if (kind !== "file") {
65
+ throw refuse(label, "is not a regular file");
66
+ }
67
+ const text = attempt(() => host.readFile(real, CONFIG_MAX_BYTES + 1));
68
+ refuseOverBound(label, text);
69
+ return text;
70
+ }
@@ -0,0 +1,96 @@
1
+ import type { Host } from "../host.ts";
2
+ import { isRecord } from "../json-document.ts";
3
+ import { joinPath } from "../paths.ts";
4
+ import { message } from "../product.ts";
5
+
6
+ /** The file a package's manifest is read from. */
7
+ export const MANIFEST = "package.json";
8
+
9
+ /** The directory a package manager installs packages into, whose files are no project's own. */
10
+ export const INSTALLED_PACKAGES = "node_modules";
11
+
12
+ /** The manifest members a package declares the packages it depends on under. */
13
+ export const DEPENDENCY_SECTIONS = [
14
+ "dependencies",
15
+ "devDependencies",
16
+ "peerDependencies",
17
+ "optionalDependencies",
18
+ ] as const;
19
+
20
+ /** Whether one path passes through an installed-packages directory. */
21
+ export function inInstalledPackages(path: string): boolean {
22
+ return path.split("/").includes(INSTALLED_PACKAGES);
23
+ }
24
+
25
+ /** What one JSON file reads as: its text and decoded value, text that does not parse, or a file that cannot be read, with the read's error. */
26
+ type JSONRead =
27
+ | { readonly kind: "read"; readonly text: string; readonly value: unknown }
28
+ | { readonly kind: "malformed" }
29
+ | { readonly kind: "unread"; readonly reason: string };
30
+
31
+ /** One JSON file, read and parsed, as {@link JSONRead} tells its states apart. */
32
+ export function readJSONFile(host: Host, path: string): JSONRead {
33
+ let text: string;
34
+ try {
35
+ text = host.readFile(path);
36
+ } catch (error: unknown) {
37
+ return { kind: "unread", reason: message(error) };
38
+ }
39
+ try {
40
+ return { kind: "read", text, value: JSON.parse(text) as unknown };
41
+ } catch {
42
+ return { kind: "malformed" };
43
+ }
44
+ }
45
+
46
+ /** One JSON file's text and decoded value, or undefined where it cannot be read or parsed. */
47
+ export function readDocument(
48
+ host: Host,
49
+ path: string,
50
+ ): { text: string; value: unknown } | undefined {
51
+ const read = readJSONFile(host, path);
52
+ return read.kind === "read" ? { text: read.text, value: read.value } : undefined;
53
+ }
54
+
55
+ /** One JSON file's decoded value, or undefined where it cannot be read or parsed. */
56
+ export function readJSON(host: Host, path: string): unknown {
57
+ return readDocument(host, path)?.value;
58
+ }
59
+
60
+ /**
61
+ * The manifest in one directory, decoded, or undefined where none reads as an object. A
62
+ * manifest this program cannot read declares nothing, the same answer as an absent one:
63
+ * the package manager owns the refusal of a manifest it rejects.
64
+ */
65
+ export function manifestAt(host: Host, dir: string): Record<string, unknown> | undefined {
66
+ const value = readJSON(host, joinPath(dir, MANIFEST));
67
+ return isRecord(value) ? value : undefined;
68
+ }
69
+
70
+ /**
71
+ * What one directory's manifest reads as: none, an object, a file that does not parse as one,
72
+ * or a file that cannot be read, with the read's error. Either of the last two still bounds
73
+ * the package it would declare.
74
+ */
75
+ export type ManifestRead =
76
+ | { readonly kind: "absent" }
77
+ | { readonly kind: "read"; readonly manifest: Record<string, unknown> }
78
+ | { readonly kind: "malformed" }
79
+ | { readonly kind: "unread"; readonly reason: string };
80
+
81
+ /** The manifest in one directory, as {@link ManifestRead} tells its states apart. */
82
+ export function readManifestAt(host: Host, dir: string): ManifestRead {
83
+ if (!holdsManifest(host, dir)) {
84
+ return { kind: "absent" };
85
+ }
86
+ const read = readJSONFile(host, joinPath(dir, MANIFEST));
87
+ if (read.kind !== "read") {
88
+ return read;
89
+ }
90
+ return isRecord(read.value) ? { kind: "read", manifest: read.value } : { kind: "malformed" };
91
+ }
92
+
93
+ /** Whether one directory holds a manifest file. */
94
+ export function holdsManifest(host: Host, dir: string): boolean {
95
+ return host.kindOf(joinPath(dir, MANIFEST)) === "file";
96
+ }
package/src/fs/tree.ts ADDED
@@ -0,0 +1,110 @@
1
+ import type { DirectoryEntry, Host } from "../host.ts";
2
+ import { ancestors, joinPath, resolvePath } from "../paths.ts";
3
+ import { LANGUAGE_ROWS } from "../contract/contract.generated.ts";
4
+ import { LANGUAGE } from "../product.ts";
5
+ import { bytewise } from "../text/compare.ts";
6
+ import { globExpression } from "../text/glob.ts";
7
+
8
+ /** The directories no walk of the tree enters, by the patterns the language's row names. */
9
+ const SKIPPED_DIRECTORIES = LANGUAGE_ROWS[LANGUAGE].skippedDirectories.map(globExpression);
10
+
11
+ function skipped(name: string): boolean {
12
+ return SKIPPED_DIRECTORIES.some((pattern) => pattern.test(name));
13
+ }
14
+
15
+ /** A git repository's working tree root, and the `info/exclude` file git reads for it. */
16
+ interface Repository {
17
+ readonly root: string;
18
+ /** Undefined where a `.git` file names no repository directory. */
19
+ readonly exclude: string | undefined;
20
+ }
21
+
22
+ /** One file's text without its line ending, or undefined where it cannot be read. */
23
+ function lineOf(host: Host, path: string): string | undefined {
24
+ try {
25
+ return host.readFile(path).replace(/\r?\n$/u, "");
26
+ } catch {
27
+ return undefined;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * The repository whose working tree holds `dir`: the nearest directory at or above it with a
33
+ * `.git`. A `.git` file, which a linked worktree or a submodule holds, names the repository's
34
+ * directory on its `gitdir:` line; that directory's `commondir` file, where it has one, names
35
+ * the directory shared with the main worktree, which holds `info/exclude`:
36
+ * https://git-scm.com/docs/gitrepository-layout
37
+ */
38
+ export function repositoryOf(host: Host, dir: string): Repository | undefined {
39
+ for (const root of ancestors(dir)) {
40
+ const dotGit = joinPath(root, ".git");
41
+ const kind = host.kindOf(dotGit);
42
+ if (kind === "directory") {
43
+ return { root, exclude: joinPath(dotGit, "info/exclude") };
44
+ }
45
+ if (kind === "file") {
46
+ const named = /^gitdir: (.+)$/u.exec(lineOf(host, dotGit) ?? "")?.[1];
47
+ const gitDir = named === undefined ? undefined : resolvePath(root, named);
48
+ const common = gitDir === undefined ? undefined : lineOf(host, joinPath(gitDir, "commondir"));
49
+ const shared =
50
+ gitDir === undefined || common === undefined ? gitDir : resolvePath(gitDir, common);
51
+ return { root, exclude: shared === undefined ? undefined : joinPath(shared, "info/exclude") };
52
+ }
53
+ }
54
+ return undefined;
55
+ }
56
+
57
+ /** The entries of one directory, none where it cannot be read. */
58
+ export function entriesOf(host: Host, dir: string): readonly DirectoryEntry[] {
59
+ try {
60
+ return host.readDirectory(dir);
61
+ } catch {
62
+ return [];
63
+ }
64
+ }
65
+
66
+ /** The names of the files directly in `dir` whose name `accepts`, in bytewise order. */
67
+ export function filesIn(
68
+ host: Host,
69
+ dir: string,
70
+ accepts: (name: string) => boolean,
71
+ ): readonly string[] {
72
+ return entriesOf(host, dir)
73
+ .filter((entry) => !entry.directory && accepts(entry.name))
74
+ .map((entry) => entry.name)
75
+ .sort(bytewise);
76
+ }
77
+
78
+ /** The names of the directories directly in `dir` a walk enters, in bytewise order. */
79
+ export function directoriesIn(host: Host, dir: string): readonly string[] {
80
+ return entriesOf(host, dir)
81
+ .filter((entry) => entry.directory && !skipped(entry.name))
82
+ .map((entry) => entry.name)
83
+ .sort(bytewise);
84
+ }
85
+
86
+ /**
87
+ * Every file below `root` whose name `accepts`, by its path relative to `root` in bytewise
88
+ * order, read in no skipped directory.
89
+ */
90
+ export function filesBelow(
91
+ host: Host,
92
+ root: string,
93
+ accepts: (name: string) => boolean,
94
+ ): readonly string[] {
95
+ const found: string[] = [];
96
+ const pending = [""];
97
+ for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
98
+ for (const entry of entriesOf(host, joinPath(root, dir))) {
99
+ const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
100
+ if (!entry.directory) {
101
+ if (accepts(entry.name)) {
102
+ found.push(path);
103
+ }
104
+ } else if (!skipped(entry.name)) {
105
+ pending.push(path);
106
+ }
107
+ }
108
+ }
109
+ return found.sort(bytewise);
110
+ }
package/src/git-ignore.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  /**
2
- * Which files the `.gitignore` files of a tree ignore, read as git reads them:
2
+ * Which files git ignores below a tree's root, read as git reads its ignore files:
3
3
  * https://git-scm.com/docs/gitignore#_pattern_format
4
4
  */
5
5
 
6
- import { globExpression } from "./glob.ts";
7
6
  import type { Host } from "./host.ts";
8
- import { dirnamePath, joinPath, relativePath } from "./paths.ts";
7
+ import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.ts";
8
+ import { message } from "./product.ts";
9
+ import { gitignoreExpression } from "./text/glob.ts";
10
+ import { repositoryOf } from "./fs/tree.ts";
9
11
 
10
- /** One pattern line of a `.gitignore` file. */
12
+ /** One pattern line of an ignore file. */
11
13
  interface Rule {
12
14
  readonly negated: boolean;
13
15
  /** Whether the pattern ends with `/`, so it matches a directory alone. */
@@ -16,7 +18,7 @@ interface Rule {
16
18
  readonly expression: RegExp;
17
19
  }
18
20
 
19
- /** The rules one `.gitignore` text writes, in order. */
21
+ /** The rules one ignore file's text writes, in order. */
20
22
  function rulesOf(text: string): readonly Rule[] {
21
23
  const rules: Rule[] = [];
22
24
  for (const raw of text.split(/\r?\n/u)) {
@@ -25,7 +27,7 @@ function rulesOf(text: string): readonly Rule[] {
25
27
  continue;
26
28
  }
27
29
  const negated = line.startsWith("!");
28
- line = negated || line.startsWith("\\") ? line.slice(1) : line;
30
+ line = negated ? line.slice(1) : line;
29
31
  const directoryOnly = line.endsWith("/");
30
32
  line = line.replace(/\/+$/u, "");
31
33
  if (line === "") {
@@ -38,43 +40,65 @@ function rulesOf(text: string): readonly Rule[] {
38
40
  rules.push({
39
41
  negated,
40
42
  directoryOnly,
41
- expression: globExpression(anchored ? pattern : `**/${pattern}`),
43
+ expression: gitignoreExpression(anchored ? pattern : `**/${pattern}`),
42
44
  });
43
45
  }
44
46
  return rules;
45
47
  }
46
48
 
49
+ /** Told each ignore file that exists and cannot be read, with why. */
50
+ type UnreadIgnoreFile = (path: string, reason: string) => void;
51
+
52
+ function rulesIn(host: Host, path: string, unread: UnreadIgnoreFile): readonly Rule[] {
53
+ try {
54
+ return rulesOf(host.readFile(path));
55
+ } catch (error: unknown) {
56
+ if (host.kindOf(path) !== "absent") {
57
+ unread(path, message(error));
58
+ }
59
+ return [];
60
+ }
61
+ }
62
+
47
63
  /**
48
- * Whether the `.gitignore` files at and below `root` ignore one absolute path. A path
49
- * outside `root` is not ignored, and a file below an ignored directory stays ignored
50
- * whatever a later pattern says, as git cannot re-include it.
64
+ * Whether git ignores one absolute path below `root`: by the repository's `info/exclude`, then
65
+ * the `.gitignore` files from its root down to the path's directory, a later pattern overriding
66
+ * an earlier one, though a file below an ignored directory stays ignored, as git cannot re-include
67
+ * it. A path outside `root` is not ignored. With no repository, the files at and below `root` are
68
+ * read; with one, the directories above `root` give patterns but are not tested. An unreadable
69
+ * ignore file ignores nothing, and `unread` is told it.
51
70
  */
52
- export function gitIgnored(host: Host, root: string): (path: string) => boolean {
71
+ export function gitIgnored(
72
+ host: Host,
73
+ root: string,
74
+ unread: UnreadIgnoreFile = () => undefined,
75
+ ): (path: string) => boolean {
76
+ const found = repositoryOf(host, root);
77
+ const repository = found?.root;
78
+ const above =
79
+ repository === undefined ? [] : [...ancestors(dirnamePath(root), repository)].reverse();
80
+ const excluded = found?.exclude === undefined ? [] : rulesIn(host, found.exclude, unread);
53
81
  const read = new Map<string, readonly Rule[]>();
54
82
  const rulesAt = (dir: string): readonly Rule[] => {
55
83
  let rules = read.get(dir);
56
84
  if (rules === undefined) {
57
- try {
58
- rules = rulesOf(host.readFile(joinPath(dir, ".gitignore")));
59
- } catch {
60
- rules = [];
61
- }
85
+ rules = rulesIn(host, joinPath(dir, ".gitignore"), unread);
62
86
  read.set(dir, rules);
63
87
  }
64
88
  return rules;
65
89
  };
66
- /** Whether the rules of `root` and of each directory down to one path's own decide it ignored. */
67
90
  const decided = (path: string, directory: boolean): boolean => {
68
- const dirs: string[] = [];
69
- for (let at = dirnamePath(path); relativePath(root, at) !== undefined; at = dirnamePath(at)) {
70
- dirs.unshift(at);
71
- }
72
- dirs.unshift(root);
91
+ const sources: [string, readonly Rule[]][] = [
92
+ ...(repository === undefined ? [] : [[repository, excluded] as [string, readonly Rule[]]]),
93
+ ...[...above, ...[...ancestors(dirnamePath(path), root)].reverse()].map(
94
+ (dir): [string, readonly Rule[]] => [dir, rulesAt(dir)],
95
+ ),
96
+ ];
73
97
  let ignored = false;
74
- for (const dir of dirs) {
75
- const below = relativePath(dir, path);
76
- for (const rule of below === undefined ? [] : rulesAt(dir)) {
77
- if ((directory || !rule.directoryOnly) && rule.expression.test(below ?? "")) {
98
+ for (const [dir, rules] of sources) {
99
+ const below = relativePath(dir, path) ?? "";
100
+ for (const rule of rules) {
101
+ if ((directory || !rule.directoryOnly) && rule.expression.test(below)) {
78
102
  ignored = !rule.negated;
79
103
  }
80
104
  }