@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
@@ -1,12 +1,26 @@
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
7
 
8
+ import {
9
+ isIdentifier,
10
+ isImportDeclaration,
11
+ isPropertyAssignment,
12
+ isStringLiteralLikeNode,
13
+ isVariableStatement,
14
+ NodeFlags,
15
+ type Node,
16
+ type SourceFile,
17
+ } from "@typescript/native/ast";
18
+ import { GENERATORS } from "./contract/contract.generated.ts";
8
19
  import type { DetectorInput, Evidence } from "./exempt.ts";
20
+ import { minifiedBy } from "./javascript-file.ts";
9
21
  import type { SourceFiles } from "./source-files.ts";
22
+ import { bytewise } from "./text/compare.ts";
23
+ import { satisfies } from "./version-range.ts";
10
24
 
11
25
  /** The generated-code header, matched against each line of a leading comment. */
12
26
  const HEADER_LINE = /^\/\/ Code generated .* DO NOT EDIT\.$/u;
@@ -23,9 +37,44 @@ const GENERATED_WORD = /\bgenerated\b/iu;
23
37
  /** The phrases a generator's notice asks a reader not to edit the file by. */
24
38
  const KEEP_OUT = /\b(?:do not edit|will be overwritten|should not make any changes)\b/iu;
25
39
 
26
- /** The comments before a text's first token, a leading hashbang line skipped. */
27
- function leadingComments(text: string): readonly string[] {
28
- const comments: string[] = [];
40
+ /** What decided a file generated, as the facts document's `evidence` names it. */
41
+ type GeneratedEvidence = "header" | "tag" | "notice" | "convention" | "minified";
42
+
43
+ /** The generator row one generated file matches, with the version and the schema files its output states. */
44
+ interface GeneratorMatch {
45
+ readonly row: string;
46
+ readonly version?: string;
47
+ readonly schemaFiles: readonly string[];
48
+ }
49
+
50
+ /** Why one file is generated, and which generator wrote it. */
51
+ export interface GeneratedFact {
52
+ readonly evidence: GeneratedEvidence;
53
+ /** The 1-based line of the comment line that marks the file; 1 for a row or a minified file. */
54
+ readonly line: number;
55
+ /** The marking line's text, the convention row's name, or what decided the file minified. */
56
+ readonly detail: string;
57
+ readonly generator?: GeneratorMatch;
58
+ }
59
+
60
+ /** One line of a leading comment, with its 1-based line in the file. */
61
+ interface CommentLine {
62
+ readonly text: string;
63
+ readonly line: number;
64
+ }
65
+
66
+ /** The comments before a text's first token, a leading hashbang line skipped, each as its lines. */
67
+ function leadingComments(text: string): readonly (readonly CommentLine[])[] {
68
+ const comments: CommentLine[][] = [];
69
+ const lineAt = (offset: number): number =>
70
+ text.slice(0, offset).split(/\r\n|[\n\r\u2028\u2029]/u).length;
71
+ const lines = (from: number, to: number): CommentLine[] => {
72
+ const first = lineAt(from);
73
+ return text
74
+ .slice(from, to)
75
+ .split(/\r\n|[\n\r\u2028\u2029]/u)
76
+ .map((one, index) => ({ text: one, line: first + index }));
77
+ };
29
78
  let at = text.startsWith("\uFEFF") ? 1 : 0;
30
79
  if (text.startsWith("#!", at)) {
31
80
  const end = text.indexOf("\n", at);
@@ -38,14 +87,14 @@ function leadingComments(text: string): readonly string[] {
38
87
  if (text.startsWith("//", at)) {
39
88
  const end = text.slice(at).search(/[\r\n\u2028\u2029]/u);
40
89
  const stop = end === -1 ? text.length : at + end;
41
- comments.push(text.slice(at, stop));
90
+ comments.push(lines(at, stop));
42
91
  at = stop;
43
92
  } else if (text.startsWith("/*", at)) {
44
93
  const end = text.indexOf("*/", at + 2);
45
94
  if (end === -1) {
46
95
  return comments;
47
96
  }
48
- comments.push(text.slice(at, end + 2));
97
+ comments.push(lines(at, end + 2));
49
98
  at = end + 2;
50
99
  } else {
51
100
  return comments;
@@ -53,49 +102,201 @@ function leadingComments(text: string): readonly string[] {
53
102
  }
54
103
  }
55
104
 
56
- /** The detail a file's leading comments mark it generated by, or undefined where they mark nothing. */
57
- export function generatedByComment(text: string): string | undefined {
58
- const comments = leadingComments(text);
59
- if (comments.some((one) => one.split(/\r?\n/u).some((line) => HEADER_LINE.test(line)))) {
60
- return "generated by the header line its leading comments hold";
105
+ /** The mark one file's leading comments make it generated by, or undefined where they mark nothing. */
106
+ export function generatedMark(text: string): Omit<GeneratedFact, "generator"> | undefined {
107
+ const lines = leadingComments(text).flat();
108
+ const header = lines.find((one) => HEADER_LINE.test(one.text));
109
+ if (header !== undefined) {
110
+ return { evidence: "header", line: header.line, detail: header.text };
111
+ }
112
+ const tag = lines.find((one) => GENERATED_TAG.test(one.text));
113
+ if (tag !== undefined) {
114
+ return { evidence: "tag", line: tag.line, detail: tag.text };
61
115
  }
62
- if (comments.some((one) => GENERATED_TAG.test(one))) {
63
- return "generated by the @generated tag its leading comments hold";
116
+ const [first] = lines;
117
+ if (first !== undefined && GENERATED_BY.test(first.text)) {
118
+ return { evidence: "notice", line: first.line, detail: first.text };
64
119
  }
65
- const notice = comments.join("\n");
66
- if (
67
- GENERATED_BY.test(comments[0] ?? "") ||
68
- (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice))
69
- ) {
70
- return "generated by the notice its leading comments hold";
120
+ const notice = lines.map((one) => one.text).join("\n");
121
+ if (GENERATED_WORD.test(notice) && KEEP_OUT.test(notice)) {
122
+ const marked = lines.find((one) => GENERATED_WORD.test(one.text) || KEEP_OUT.test(one.text));
123
+ return marked === undefined
124
+ ? undefined
125
+ : { evidence: "notice", line: marked.line, detail: marked.text };
126
+ }
127
+ return undefined;
128
+ }
129
+
130
+ /** What one form's first group captures, for each line the form matches. */
131
+ function after(lines: readonly CommentLine[], form: RegExp): readonly string[] {
132
+ return lines.flatMap((one) => form.exec(one.text)?.[1] ?? []);
133
+ }
134
+
135
+ /**
136
+ * Where each plugin's output states its version and its schema files, as `references-ts.36`
137
+ * states: a plugin missing here states no version, and one with no schema form names none.
138
+ */
139
+ const STATEMENTS: Readonly<Record<string, { version?: RegExp; schemaFiles?: RegExp }>> = {
140
+ "protoc-gen-es": {
141
+ version: /^\/\/ @generated by protoc-gen-es (\S+)/u,
142
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
143
+ },
144
+ "protoc-gen-connect-es": {
145
+ version: /^\/\/ @generated by protoc-gen-connect-es (\S+)/u,
146
+ schemaFiles: /^\/\/ @generated from file (\S+)/u,
147
+ },
148
+ "protobuf-ts": {
149
+ version: /^\/\/ @generated by protobuf-ts (\S+)/u,
150
+ schemaFiles: /^\/\/ @generated from protobuf file "([^"]+)"/u,
151
+ },
152
+ "ts-proto": {
153
+ version: /^\/\/\s+protoc-gen-ts_proto\s+(\S+)$/u,
154
+ schemaFiles: /^\/\/ source: (.+)$/u,
155
+ },
156
+ "protoc-gen-grpc-web": {
157
+ version: /^\/\/\s+protoc-gen-grpc-web\s+(\S+)$/u,
158
+ schemaFiles: /^\/\/ source: (.+)$/u,
159
+ },
160
+ "protoc-gen-js": { schemaFiles: /^\/\/ source: (.+)$/u },
161
+ "ts-protoc-gen": { schemaFiles: /^\/\/ file: (.+)$/u },
162
+ };
163
+
164
+ /** A ts-proto service path literal: `/<package>.<service>/<method>`. */
165
+ const SERVICE_PATH = /^\/(?:\w+\.)+\w+\/\w+$/u;
166
+
167
+ /** Whether one ts-proto file writes a service path, as `references-ts.36` reads one. */
168
+ function writesServicePath(file: SourceFile): boolean {
169
+ const named = file.statements.some(
170
+ (statement) =>
171
+ isVariableStatement(statement) &&
172
+ (statement.declarationList.flags & NodeFlags.Const) !== 0 &&
173
+ statement.declarationList.declarations.some(
174
+ (one) =>
175
+ isIdentifier(one.name) &&
176
+ one.name.text.endsWith("ServiceName") &&
177
+ one.initializer !== undefined &&
178
+ isStringLiteralLikeNode(one.initializer),
179
+ ),
180
+ );
181
+ let found = named;
182
+ const visit = (node: Node): void => {
183
+ if (found) {
184
+ return;
185
+ }
186
+ if (
187
+ isPropertyAssignment(node) &&
188
+ isIdentifier(node.name) &&
189
+ node.name.text === "path" &&
190
+ isStringLiteralLikeNode(node.initializer) &&
191
+ SERVICE_PATH.test(node.initializer.text)
192
+ ) {
193
+ found = true;
194
+ return;
195
+ }
196
+ node.forEachChild(visit);
197
+ };
198
+ file.forEachChild(visit);
199
+ return found;
200
+ }
201
+
202
+ /** Whether one file imports a module by its specifier. */
203
+ function imports(file: SourceFile, module: string): boolean {
204
+ return file.statements.some(
205
+ (statement) =>
206
+ isImportDeclaration(statement) &&
207
+ isStringLiteralLikeNode(statement.moduleSpecifier) &&
208
+ statement.moduleSpecifier.text === module,
209
+ );
210
+ }
211
+
212
+ /** The option sets the rows of `references-ts.36` name, by row; every other row's is met by any output. */
213
+ const OPTION_SETS: Readonly<Record<string, (file: SourceFile) => boolean>> = {
214
+ "ts-proto-2-output-schema": (file) => imports(file, "ts-proto-descriptors"),
215
+ "ts-proto-2": writesServicePath,
216
+ "ts-proto-2-types": (file) => !writesServicePath(file),
217
+ };
218
+
219
+ /** The first generator row one file matches, in `generators.json`'s order, as `bindings.3` reads it. */
220
+ export function generatorOf(file: SourceFile): GeneratorMatch | undefined {
221
+ const lines = leadingComments(file.text).flat();
222
+ for (const row of GENERATORS) {
223
+ const header = new RegExp(row.header, "u");
224
+ if (!lines.some((one) => header.test(one.text))) {
225
+ continue;
226
+ }
227
+ const statements = STATEMENTS[row.plugin] ?? {};
228
+ const [version] = statements.version === undefined ? [] : after(lines, statements.version);
229
+ if (
230
+ "versions" in row &&
231
+ (version === undefined ||
232
+ !satisfies(
233
+ version.replace(/^v/u, ""),
234
+ `>=${row.versions.min.replace(/^v/u, "")} <=${row.versions.max.replace(/^v/u, "")}`,
235
+ ))
236
+ ) {
237
+ continue;
238
+ }
239
+ if (OPTION_SETS[row.id]?.(file) === false) {
240
+ continue;
241
+ }
242
+ const schemaFiles =
243
+ statements.schemaFiles === undefined
244
+ ? []
245
+ : [
246
+ ...new Set(
247
+ after(lines, statements.schemaFiles).filter((one) => one.endsWith(".proto")),
248
+ ),
249
+ ].sort(bytewise);
250
+ return { row: row.id, ...(version === undefined ? {} : { version }), schemaFiles };
71
251
  }
72
252
  return undefined;
73
253
  }
74
254
 
75
255
  /**
76
- * Every own file of one project that is generated, by its path below the target root,
77
- * with the detail a retention in it names: its leading comments' mark, else the row
78
- * naming its directory as generated.
256
+ * Every own file of one project that is generated, by its path below the target root, with
257
+ * what decided it: its leading comments' mark, else the row naming its directory as
258
+ * generated, else its minified name or text; and the generator row it matches.
79
259
  */
80
260
  export function generatedFilesOf(
81
261
  files: SourceFiles,
82
262
  byRow: ReadonlyMap<string, string>,
83
- ): ReadonlyMap<string, string> {
84
- const found = new Map<string, string>();
263
+ ): ReadonlyMap<string, GeneratedFact> {
264
+ const found = new Map<string, GeneratedFact>();
85
265
  for (const [path, file] of files.byPath) {
86
266
  const row = byRow.get(path);
87
- const detail =
88
- generatedByComment(file.text) ??
267
+ const minified = row === undefined ? minifiedBy(file.fileName, file.text) : undefined;
268
+ const fact =
269
+ generatedMark(file.text) ??
89
270
  (row === undefined
90
- ? undefined
91
- : `generated below a directory the convention row ${row} names`);
92
- if (detail !== undefined) {
93
- found.set(path, detail);
271
+ ? minified === undefined
272
+ ? undefined
273
+ : { evidence: "minified" as const, line: 1, detail: minified }
274
+ : { evidence: "convention" as const, line: 1, detail: row });
275
+ if (fact === undefined) {
276
+ continue;
94
277
  }
278
+ const generator = generatorOf(file);
279
+ found.set(path, generator === undefined ? fact : { ...fact, generator });
95
280
  }
96
281
  return found;
97
282
  }
98
283
 
284
+ /** The detail a retention in a generated file names, from what decided the file. */
285
+ function retentionDetail(fact: GeneratedFact): string {
286
+ switch (fact.evidence) {
287
+ case "header":
288
+ return "generated by the header line its leading comments hold";
289
+ case "tag":
290
+ return "generated by the @generated tag its leading comments hold";
291
+ case "notice":
292
+ return "generated by the notice its leading comments hold";
293
+ case "convention":
294
+ return `generated below a directory the convention row ${fact.detail} names`;
295
+ case "minified":
296
+ return "generated as a minified JavaScript file";
297
+ }
298
+ }
299
+
99
300
  /** The generated-file detector, recording each file's first line. */
100
301
  export function generatedFile<Brand>(input: DetectorInput<Brand>): readonly Evidence[] {
101
302
  const generated = input.generated;
@@ -103,9 +304,15 @@ export function generatedFile<Brand>(input: DetectorInput<Brand>): readonly Evid
103
304
  return [];
104
305
  }
105
306
  return input.held.symbols.flatMap((symbol): Evidence[] => {
106
- const detail = generated.get(symbol.position.path);
107
- return symbol.kind === "file" || detail === undefined
307
+ const fact = generated.get(symbol.position.path);
308
+ return symbol.kind === "file" || fact === undefined
108
309
  ? []
109
- : [{ id: symbol.id, detail, site: { path: symbol.position.path, line: 1, column: 1 } }];
310
+ : [
311
+ {
312
+ id: symbol.id,
313
+ detail: retentionDetail(fact),
314
+ site: { path: symbol.position.path, line: 1, column: 1 },
315
+ },
316
+ ];
110
317
  });
111
318
  }
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
  }
package/src/host.ts CHANGED
@@ -1,5 +1,5 @@
1
- /** What one path names. */
2
- export type PathKind = "file" | "directory" | "absent";
1
+ /** What one path names: `"other"` is anything else there, a named pipe, a device or a socket. */
2
+ export type PathKind = "file" | "directory" | "other" | "absent";
3
3
 
4
4
  /** One entry of a directory. */
5
5
  export interface DirectoryEntry {
@@ -17,11 +17,18 @@ export interface DirectoryEntry {
17
17
  export interface Host {
18
18
  /** The directory a relative path resolves against. */
19
19
  workingDirectory(): string;
20
- /** The text of one file, or a throw naming why it cannot be read. */
21
- readFile(path: string): string;
20
+ /**
21
+ * The text of one file, or a throw naming why it cannot be read. Given `limit`, the
22
+ * text of no more than the file's first `limit` bytes, so a caller bounding a document
23
+ * asks for one byte past its bound and never holds a larger file whole.
24
+ */
25
+ readFile(path: string, limit?: number): string;
22
26
  /** The entries of one directory, or a throw naming why it cannot be read. */
23
27
  readDirectory(path: string): readonly DirectoryEntry[];
24
- /** What one path names, `"absent"` where nothing is there. */
28
+ /**
29
+ * What one path names once every symbolic link along it is followed, `"absent"` where
30
+ * nothing is there, or a throw naming why it cannot be known, as for a link loop.
31
+ */
25
32
  kindOf(path: string): PathKind;
26
33
  /**
27
34
  * The path one path names once every symbolic link along it is followed, or a throw
@@ -4,17 +4,20 @@
4
4
  */
5
5
 
6
6
  import {
7
+ isExportDeclaration,
7
8
  isExportSpecifier,
8
9
  isExternalModuleReference,
9
- isIdentifier,
10
10
  isImportDeclaration,
11
11
  isImportEqualsDeclaration,
12
12
  isJsxElement,
13
13
  isJsxFragment,
14
14
  isJsxSelfClosingElement,
15
+ isNamedExports,
15
16
  isNamespaceImport,
16
17
  SyntaxKind,
17
18
  type Identifier,
19
+ type ImportDeclaration,
20
+ type ImportEqualsDeclaration,
18
21
  type Node,
19
22
  type SourceFile,
20
23
  } from "@typescript/native/ast";
@@ -41,42 +44,108 @@ export interface JsxOptions {
41
44
  readonly jsxFragmentFactory?: unknown;
42
45
  }
43
46
 
47
+ /** One name a file's top-level import declaration binds. */
48
+ interface ImportBinding {
49
+ readonly name: Identifier;
50
+ /** The export name it binds, `default` for a default import; absent where it binds a whole module. */
51
+ readonly imported?: string;
52
+ /** An `import x = …` declaration: whether it is exported, and whether it names an external module. */
53
+ readonly equals?: { readonly exported: boolean; readonly external: boolean };
54
+ }
55
+
56
+ const bindingsCache = new WeakMap<SourceFile, readonly ImportBinding[]>();
57
+
44
58
  /**
45
- * The names one file's import declarations bind: the default, named and namespace names of
46
- * an `import` declaration, and the name of an `import x = require()` declaration that is
47
- * not exported. None is judged in a declaration file or a component file.
59
+ * Every name one import declaration binds, in source order: the default, named and
60
+ * namespace names of an `import` declaration and the name of an `import x = …` declaration.
61
+ * A namespace import and an import assignment bind a whole module.
48
62
  */
49
- export function importBindings(file: SourceFile): readonly Identifier[] {
50
- if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
51
- return [];
63
+ export function declarationBindings(
64
+ declaration: ImportDeclaration | ImportEqualsDeclaration,
65
+ ): readonly ImportBinding[] {
66
+ if (isImportEqualsDeclaration(declaration)) {
67
+ return [
68
+ {
69
+ name: declaration.name,
70
+ equals: {
71
+ exported: (declaration.modifiers ?? []).some(
72
+ (modifier) => modifier.kind === SyntaxKind.ExportKeyword,
73
+ ),
74
+ external: isExternalModuleReference(declaration.moduleReference),
75
+ },
76
+ },
77
+ ];
52
78
  }
53
- const found: Identifier[] = [];
54
- for (const statement of file.statements) {
55
- if (isImportDeclaration(statement)) {
56
- const clause = statement.importClause;
57
- if (clause?.name !== undefined) {
58
- found.push(clause.name);
59
- }
60
- const named = clause?.namedBindings;
61
- if (named !== undefined && isNamespaceImport(named)) {
62
- found.push(named.name);
63
- } else if (named !== undefined) {
64
- for (const element of named.elements) {
65
- found.push(element.name);
66
- }
67
- }
68
- } else if (
69
- isImportEqualsDeclaration(statement) &&
70
- isExternalModuleReference(statement.moduleReference) &&
71
- isIdentifier(statement.name) &&
72
- !(statement.modifiers ?? []).some((modifier) => modifier.kind === SyntaxKind.ExportKeyword)
73
- ) {
74
- found.push(statement.name);
79
+ const clause = declaration.importClause;
80
+ const found: ImportBinding[] = [];
81
+ if (clause?.name !== undefined) {
82
+ found.push({ name: clause.name, imported: "default" });
83
+ }
84
+ const named = clause?.namedBindings;
85
+ if (named !== undefined && isNamespaceImport(named)) {
86
+ found.push({ name: named.name });
87
+ } else if (named !== undefined) {
88
+ for (const element of named.elements) {
89
+ found.push({ name: element.name, imported: element.propertyName?.text ?? element.name.text });
75
90
  }
76
91
  }
77
92
  return found;
78
93
  }
79
94
 
95
+ /** Every name one file's top-level import declarations bind, in source order. */
96
+ export function bindingsOf(file: SourceFile): readonly ImportBinding[] {
97
+ const known = bindingsCache.get(file);
98
+ if (known !== undefined) {
99
+ return known;
100
+ }
101
+ const found = file.statements.flatMap((statement) =>
102
+ isImportDeclaration(statement) || isImportEqualsDeclaration(statement)
103
+ ? declarationBindings(statement)
104
+ : [],
105
+ );
106
+ bindingsCache.set(file, found);
107
+ return found;
108
+ }
109
+
110
+ /**
111
+ * The export names an import or a re-export declaration reads from the module it names,
112
+ * `undefined` standing for the whole module where a namespace or a star reads it; none for
113
+ * any other node.
114
+ */
115
+ export function importedNames(node: Node): readonly (string | undefined)[] {
116
+ if (isImportDeclaration(node)) {
117
+ return declarationBindings(node).map((binding) => binding.imported);
118
+ }
119
+ if (!isExportDeclaration(node)) {
120
+ return [];
121
+ }
122
+ const clause = node.exportClause;
123
+ return clause === undefined || !isNamedExports(clause)
124
+ ? [undefined]
125
+ : clause.elements.map((one) => (one.propertyName ?? one.name).text);
126
+ }
127
+
128
+ /** The export names {@link importedNames} gives one node, without the whole module. */
129
+ export function namesImported(node: Node): readonly string[] {
130
+ return importedNames(node).flatMap((name) => name ?? []);
131
+ }
132
+
133
+ /**
134
+ * The bindings of one file that are judged as imports: those of {@link bindingsOf} but an
135
+ * `import x = …` that is exported or names no external module. None is judged in a
136
+ * declaration file or a component file.
137
+ */
138
+ export function importBindings(file: SourceFile): readonly Identifier[] {
139
+ if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
140
+ return [];
141
+ }
142
+ return bindingsOf(file).flatMap((binding) =>
143
+ binding.equals !== undefined && (binding.equals.exported || !binding.equals.external)
144
+ ? []
145
+ : [binding.name],
146
+ );
147
+ }
148
+
80
149
  /** Whether one identifier names a binding in a local export, which reads it whatever it resolves to. */
81
150
  export function exportsLocally(use: Identifier): boolean {
82
151
  return isExportSpecifier(use.parent);