@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.
- package/README.md +3 -1
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/alias-chain.js +35 -9
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +274 -496
- package/dist/src/augmentations.js +64 -3
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/bindings.js +382 -0
- package/dist/src/commands/held.js +499 -0
- package/dist/src/commands/read.js +161 -0
- package/dist/src/commands/shell.js +169 -0
- package/dist/src/commands/tools.js +1847 -0
- package/dist/src/component-files.js +3 -2
- package/dist/src/component-layer.js +4 -2
- package/dist/src/config.js +6 -82
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +10 -38
- package/dist/src/contextual-uses.js +57 -26
- package/dist/src/contract/config.generated.js +561 -0
- package/dist/src/contract/contract.generated.js +1370 -0
- package/dist/src/contract/report.generated.js +866 -0
- package/dist/src/dependencies.js +165 -122
- package/dist/src/destructuring.js +1 -19
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +181 -168
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
- package/dist/src/entries/conventions.js +740 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +650 -0
- package/dist/src/entries/workflow-steps.js +345 -0
- package/dist/src/exemptions/argument-places.js +131 -0
- package/dist/src/exemptions/calls.js +338 -0
- package/dist/src/exemptions/classes.js +13 -0
- package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
- package/dist/src/exemptions/detectors.js +48 -0
- package/dist/src/{enum-group.js → exemptions/enum-group.js} +9 -16
- package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +7 -11
- package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +76 -54
- package/dist/src/exemptions/generated-file.js +241 -0
- package/dist/src/{injection-container.js → exemptions/injection-container.js} +20 -26
- package/dist/src/exemptions/installed-globals.js +173 -0
- package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
- package/dist/src/exemptions/lifecycle.js +150 -0
- package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
- package/dist/src/exemptions/serialization-contract.js +1276 -0
- package/dist/src/exemptions/sites.js +182 -0
- package/dist/src/{template-field.js → exemptions/template-field.js} +13 -23
- package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
- package/dist/src/exposure.js +400 -0
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +3 -26
- package/dist/src/findings/completion.js +109 -56
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +27 -41
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +16 -13
- package/dist/src/findings/non-code-artifacts.js +17 -12
- package/dist/src/findings/reads-and-writes.js +86 -27
- package/dist/src/findings/self-check.js +44 -30
- package/dist/src/findings/unused-declarations.js +29 -45
- package/dist/src/findings/visibility-narrowing.js +23 -31
- package/dist/src/findings-pass.js +13 -21
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +67 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index/values.js +124 -0
- package/dist/src/index.js +2 -2
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory/commonjs.js +507 -0
- package/dist/src/inventory/enumerate.js +167 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +323 -724
- package/dist/src/javascript-file.js +48 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +126 -107
- package/dist/src/kinds.js +3 -366
- package/dist/src/{components.js → liveness/components.js} +2 -3
- package/dist/src/{graph.js → liveness/graph.js} +52 -28
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/classify.js +289 -0
- package/dist/src/load/diagnostics.js +87 -0
- package/dist/src/load/discover.js +285 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +113 -0
- package/dist/src/load/synthesize.js +152 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +82 -16
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -0
- package/dist/src/position.js +43 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +95 -0
- package/dist/src/ref.js +12 -20
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +117 -1074
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +246 -730
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
- package/dist/src/roots/manifest.js +216 -0
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +487 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/session.js +37 -29
- package/dist/src/source-files.js +6 -10
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +36 -83
- package/dist/src/suppress.js +50 -71
- package/dist/src/symbol-keys.js +92 -88
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +5 -12
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +298 -0
- package/dist/src/text/yaml.js +684 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/vendor/unbash/ansi-c.js +132 -0
- package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
- package/dist/src/vendor/unbash/arithmetic.js +591 -0
- package/dist/src/vendor/unbash/assignment.js +131 -0
- package/dist/src/vendor/unbash/chars.js +48 -0
- package/dist/src/vendor/unbash/command.js +41 -0
- package/dist/src/vendor/unbash/expansion.js +80 -0
- package/dist/src/vendor/unbash/glob.js +268 -0
- package/dist/src/vendor/unbash/heredoc.js +27 -0
- package/dist/src/vendor/unbash/internal-types.js +1 -0
- package/dist/src/vendor/unbash/lexer.js +4274 -0
- package/dist/src/vendor/unbash/limits.js +3 -0
- package/dist/src/vendor/unbash/parser.js +1366 -0
- package/dist/src/vendor/unbash/parts.js +57 -0
- package/dist/src/vendor/unbash/substitution-context.js +8 -0
- package/dist/src/vendor/unbash/traversal.js +212 -0
- package/dist/src/vendor/unbash/types.js +1 -0
- package/dist/src/vendor/unbash/word-value.js +61 -0
- package/dist/src/vendor/unbash/word.js +40 -0
- package/dist/src/verbs/analyze.js +194 -127
- package/dist/src/verbs/describe.js +13 -18
- package/dist/src/verbs/explain.js +85 -139
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +30 -6
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/jsr.json +2 -1
- package/package.json +14 -8
- package/src/alias-chain.ts +40 -11
- package/src/amd.ts +130 -0
- package/src/analysis.ts +426 -733
- package/src/augmentations.ts +93 -4
- package/src/automatic-types.ts +12 -30
- package/src/bindings.ts +460 -0
- package/src/commands/held.ts +632 -0
- package/src/commands/read.ts +242 -0
- package/src/commands/shell.ts +228 -0
- package/src/commands/tools.ts +2382 -0
- package/src/component-files.ts +4 -2
- package/src/component-layer.ts +4 -2
- package/src/config.ts +31 -302
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +16 -66
- package/src/contextual-uses.ts +66 -34
- package/src/contract/config.generated.ts +821 -0
- package/src/contract/contract.generated.ts +1453 -0
- package/src/contract/report.generated.ts +1241 -0
- package/src/dependencies.ts +223 -128
- package/src/destructuring.ts +1 -31
- package/src/edges.ts +41 -140
- package/src/element-access.ts +190 -187
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
- package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
- package/src/{conventions.ts → entries/conventions.ts} +367 -583
- package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +835 -0
- package/src/entries/workflow-steps.ts +432 -0
- package/src/exemptions/argument-places.ts +198 -0
- package/src/exemptions/calls.ts +483 -0
- package/src/exemptions/classes.ts +49 -0
- package/src/exemptions/decorator.ts +56 -0
- package/src/exemptions/detectors.ts +75 -0
- package/src/{enum-group.ts → exemptions/enum-group.ts} +10 -19
- package/src/{erased-stores.ts → exemptions/erased-stores.ts} +7 -14
- package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +85 -66
- package/src/exemptions/generated-file.ts +318 -0
- package/src/{injection-container.ts → exemptions/injection-container.ts} +29 -34
- package/src/exemptions/installed-globals.ts +255 -0
- package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
- package/src/exemptions/lifecycle.ts +286 -0
- package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
- package/src/exemptions/serialization-contract.ts +1729 -0
- package/src/exemptions/sites.ts +293 -0
- package/src/{template-field.ts → exemptions/template-field.ts} +22 -32
- package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
- package/src/exposure.ts +492 -0
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +15 -25
- package/src/finding.ts +10 -10
- package/src/findings/boundary.ts +11 -0
- package/src/findings/completion.ts +133 -64
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +9 -10
- package/src/findings/emitters.ts +38 -52
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
- package/src/findings/interfaces.ts +7 -9
- package/src/findings/intra-function.ts +16 -14
- package/src/findings/non-code-artifacts.ts +24 -15
- package/src/findings/reads-and-writes.ts +118 -41
- package/src/findings/self-check.ts +51 -38
- package/src/findings/unused-declarations.ts +31 -49
- package/src/findings/visibility-narrowing.ts +36 -40
- package/src/findings-pass.ts +15 -31
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +96 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +50 -26
- package/src/host.ts +12 -5
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index/values.ts +170 -0
- package/src/index.ts +2 -2
- package/src/indexed-access.ts +570 -527
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory/commonjs.ts +696 -0
- package/src/inventory/enumerate.ts +203 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +442 -895
- package/src/javascript-file.ts +54 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +133 -122
- package/src/kinds.ts +5 -413
- package/src/{components.ts → liveness/components.ts} +4 -6
- package/src/{graph.ts → liveness/graph.ts} +100 -35
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/classify.ts +433 -0
- package/src/load/diagnostics.ts +154 -0
- package/src/load/discover.ts +471 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +194 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/package-scope.ts +74 -0
- package/src/load/plan.ts +513 -0
- package/src/load/specifiers.ts +171 -0
- package/src/load/synthesize.ts +230 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/partial.ts +3 -2
- package/src/paths.ts +92 -16
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- package/src/position.ts +44 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +201 -10
- package/src/ref.ts +12 -20
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +187 -1450
- package/src/report-schema.ts +68 -0
- package/src/report.ts +103 -226
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +284 -1115
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +26 -29
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
- package/src/roots/manifest.ts +303 -0
- package/src/roots/published-reach.ts +486 -0
- package/src/{roots.ts → roots/root-set.ts} +286 -469
- package/src/roots/unseen-loads.ts +345 -0
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/session.ts +87 -55
- package/src/source-files.ts +6 -11
- package/src/stores.ts +15 -3
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +55 -91
- package/src/suppress.ts +78 -79
- package/src/symbol-keys.ts +104 -107
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +15 -21
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.ts +388 -0
- package/src/text/yaml.ts +747 -0
- package/src/type-errors.ts +18 -288
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/vendor/unbash/LICENSE +15 -0
- package/src/vendor/unbash/VENDORED.md +16 -0
- package/src/vendor/unbash/ansi-c.ts +132 -0
- package/src/vendor/unbash/arithmetic-parts.ts +53 -0
- package/src/vendor/unbash/arithmetic.ts +641 -0
- package/src/vendor/unbash/assignment.ts +163 -0
- package/src/vendor/unbash/chars.ts +48 -0
- package/src/vendor/unbash/command.ts +47 -0
- package/src/vendor/unbash/expansion.ts +144 -0
- package/src/vendor/unbash/glob.ts +284 -0
- package/src/vendor/unbash/heredoc.ts +34 -0
- package/src/vendor/unbash/internal-types.ts +94 -0
- package/src/vendor/unbash/lexer.ts +4352 -0
- package/src/vendor/unbash/limits.ts +3 -0
- package/src/vendor/unbash/parser.ts +1612 -0
- package/src/vendor/unbash/parts.ts +96 -0
- package/src/vendor/unbash/substitution-context.ts +20 -0
- package/src/vendor/unbash/traversal.ts +182 -0
- package/src/vendor/unbash/types.ts +660 -0
- package/src/vendor/unbash/word-value.ts +64 -0
- package/src/vendor/unbash/word.ts +50 -0
- package/src/verbs/analyze.ts +255 -164
- package/src/verbs/describe.ts +19 -17
- package/src/verbs/explain.ts +93 -169
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +36 -6
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/dist/src/base-url.js +0 -18
- package/dist/src/calls.js +0 -155
- package/dist/src/conventions.js +0 -937
- package/dist/src/discover.js +0 -468
- package/dist/src/entry-points.js +0 -529
- package/dist/src/exempt-classes.js +0 -75
- package/dist/src/exempt.js +0 -209
- package/dist/src/generated-file.js +0 -95
- package/dist/src/glob.js +0 -65
- package/dist/src/manifest.js +0 -165
- package/dist/src/matrix.js +0 -246
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/roots.js +0 -672
- package/dist/src/serialization-contract.js +0 -611
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/sweep.js +0 -224
- package/dist/src/test-joins.js +0 -101
- package/dist/src/value-flow.js +0 -74
- package/dist/src/version.js +0 -7
- package/dist/src/workflow-steps.js +0 -185
- package/src/base-url.ts +0 -24
- package/src/calls.ts +0 -225
- package/src/decorator.ts +0 -78
- package/src/discover.ts +0 -653
- package/src/entry-points.ts +0 -687
- package/src/exempt-classes.ts +0 -123
- package/src/exempt.ts +0 -323
- package/src/generated-file.ts +0 -111
- package/src/glob.ts +0 -67
- package/src/manifest.ts +0 -234
- package/src/matrix.ts +0 -423
- package/src/pnpm-workspace.ts +0 -135
- package/src/serialization-contract.ts +0 -849
- package/src/setup-failure.ts +0 -39
- package/src/sweep.ts +0 -347
- package/src/test-joins.ts +0 -120
- package/src/value-flow.ts +0 -104
- package/src/version.ts +0 -7
- package/src/workflow-steps.ts +0 -216
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a module or a configuration document writes out, read without running it, as one
|
|
3
|
+
* value model every entry source reads: a default export in each of its forms, the options
|
|
4
|
+
* a call of one module's export passes, and the top-level values names stand for. A value
|
|
5
|
+
* the text does not state is opaque, so no reader guesses it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
isArrayLiteralExpression,
|
|
10
|
+
isBindingElement,
|
|
11
|
+
isExternalModuleReference,
|
|
12
|
+
isImportClause,
|
|
13
|
+
isImportEqualsDeclaration,
|
|
14
|
+
isImportSpecifier,
|
|
15
|
+
isStringLiteralLikeNode,
|
|
16
|
+
isCallExpression,
|
|
17
|
+
isClassDeclaration,
|
|
18
|
+
isClassExpression,
|
|
19
|
+
isComputedPropertyName,
|
|
20
|
+
isExportAssignment,
|
|
21
|
+
isExportDeclaration,
|
|
22
|
+
isExpressionStatement,
|
|
23
|
+
isFunctionDeclaration,
|
|
24
|
+
isFunctionExpression,
|
|
25
|
+
isFunctionLikeDeclaration,
|
|
26
|
+
isIdentifier,
|
|
27
|
+
isImportDeclaration,
|
|
28
|
+
isNamedExports,
|
|
29
|
+
isNamespaceImport,
|
|
30
|
+
isNoSubstitutionTemplateLiteral,
|
|
31
|
+
isNumericLiteral,
|
|
32
|
+
isObjectBindingPattern,
|
|
33
|
+
isObjectLiteralExpression,
|
|
34
|
+
isPropertyAccessExpression,
|
|
35
|
+
isPropertyAssignment,
|
|
36
|
+
isSpreadAssignment,
|
|
37
|
+
isStringLiteral,
|
|
38
|
+
isVariableDeclaration,
|
|
39
|
+
isVariableStatement,
|
|
40
|
+
isSourceFile,
|
|
41
|
+
isBlock,
|
|
42
|
+
isModuleBlock,
|
|
43
|
+
isCaseBlock,
|
|
44
|
+
isCatchClause,
|
|
45
|
+
isForStatement,
|
|
46
|
+
isForInStatement,
|
|
47
|
+
isForOfStatement,
|
|
48
|
+
isVariableDeclarationList,
|
|
49
|
+
isMetaProperty,
|
|
50
|
+
isAssignmentOperator,
|
|
51
|
+
isBinaryExpression,
|
|
52
|
+
isParenthesizedExpression,
|
|
53
|
+
isPostfixUnaryExpression,
|
|
54
|
+
isPrefixUnaryExpression,
|
|
55
|
+
isShorthandPropertyAssignment,
|
|
56
|
+
isSpreadElement,
|
|
57
|
+
NodeFlags,
|
|
58
|
+
SyntaxKind,
|
|
59
|
+
type CallExpression,
|
|
60
|
+
type Node,
|
|
61
|
+
type SourceFile,
|
|
62
|
+
} from "@typescript/native/ast";
|
|
63
|
+
import { commonJSModuleValue, type ModuleValue } from "../inventory/commonjs.ts";
|
|
64
|
+
import { callsPlatformBy, lexicalBinding, writtenName, type BindingOf } from "../platform-calls.ts";
|
|
65
|
+
import { startsRelativePath } from "../paths.ts";
|
|
66
|
+
import { unwrapped } from "../unwrap.ts";
|
|
67
|
+
|
|
68
|
+
/** Where a value's text starts in its file, or undefined in a document read with no positions. */
|
|
69
|
+
type At = number | undefined;
|
|
70
|
+
|
|
71
|
+
/** One member an object writes: its key, or undefined where a spread or a computed key hides it. */
|
|
72
|
+
interface Member {
|
|
73
|
+
readonly key: string | undefined;
|
|
74
|
+
readonly value: Value;
|
|
75
|
+
readonly at: At;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** One value a module's text states, or an opaque one it does not. */
|
|
79
|
+
export type Value =
|
|
80
|
+
| { readonly kind: "text"; readonly text: string; readonly at: At }
|
|
81
|
+
| { readonly kind: "boolean"; readonly value: boolean; readonly at: At }
|
|
82
|
+
| { readonly kind: "array"; readonly elements: readonly Value[]; readonly at: At }
|
|
83
|
+
| { readonly kind: "object"; readonly members: readonly Member[]; readonly at: At }
|
|
84
|
+
| {
|
|
85
|
+
/** A `resolve` or `join` of the module's own directory and literal segments. */
|
|
86
|
+
readonly kind: "directory";
|
|
87
|
+
readonly segments: readonly string[];
|
|
88
|
+
readonly at: At;
|
|
89
|
+
}
|
|
90
|
+
| { readonly kind: "opaque"; readonly at: At };
|
|
91
|
+
|
|
92
|
+
/** A JSON or YAML document's value, which carries no positions. */
|
|
93
|
+
export function documentValue(value: unknown): Value {
|
|
94
|
+
if (typeof value === "string") {
|
|
95
|
+
return { kind: "text", text: value, at: undefined };
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === "boolean") {
|
|
98
|
+
return { kind: "boolean", value, at: undefined };
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
return { kind: "array", elements: value.map(documentValue), at: undefined };
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === "object" && value !== null) {
|
|
104
|
+
return {
|
|
105
|
+
kind: "object",
|
|
106
|
+
members: Object.entries(value).map(([key, held]) => ({
|
|
107
|
+
key,
|
|
108
|
+
value: documentValue(held),
|
|
109
|
+
at: undefined,
|
|
110
|
+
})),
|
|
111
|
+
at: undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return { kind: "opaque", at: undefined };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The node that declares a name through one binding: the holder itself for a name, or the
|
|
119
|
+
* binding element of an object or array pattern that binds it.
|
|
120
|
+
*/
|
|
121
|
+
function boundAt(holder: Node, name: Node | undefined, text: string): Node | undefined {
|
|
122
|
+
if (name === undefined) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
if (isIdentifier(name)) {
|
|
126
|
+
return name.text === text ? holder : undefined;
|
|
127
|
+
}
|
|
128
|
+
let found: Node | undefined;
|
|
129
|
+
name.forEachChild((element) => {
|
|
130
|
+
found ??= boundAt(element, (element as { readonly name?: Node }).name, text);
|
|
131
|
+
});
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** The declaration of a name one statement makes in the block it is written in. */
|
|
136
|
+
function blockDeclaration(statement: Node, name: string): Node | undefined {
|
|
137
|
+
if (
|
|
138
|
+
(isFunctionDeclaration(statement) || isClassDeclaration(statement)) &&
|
|
139
|
+
statement.name?.text === name
|
|
140
|
+
) {
|
|
141
|
+
return statement;
|
|
142
|
+
}
|
|
143
|
+
if (
|
|
144
|
+
isVariableStatement(statement) &&
|
|
145
|
+
(statement.declarationList.flags & NodeFlags.BlockScoped) !== 0
|
|
146
|
+
) {
|
|
147
|
+
return statement.declarationList.declarations
|
|
148
|
+
.map((one) => boundAt(one, one.name, name))
|
|
149
|
+
.find((one) => one !== undefined);
|
|
150
|
+
}
|
|
151
|
+
if (isImportEqualsDeclaration(statement)) {
|
|
152
|
+
return statement.name.text === name ? statement : undefined;
|
|
153
|
+
}
|
|
154
|
+
if (isImportDeclaration(statement)) {
|
|
155
|
+
const clause = statement.importClause;
|
|
156
|
+
if (clause?.name?.text === name) {
|
|
157
|
+
return clause;
|
|
158
|
+
}
|
|
159
|
+
const bindings = clause?.namedBindings;
|
|
160
|
+
if (bindings !== undefined && isNamespaceImport(bindings)) {
|
|
161
|
+
return bindings.name.text === name ? bindings : undefined;
|
|
162
|
+
}
|
|
163
|
+
return bindings?.elements.find((one) => one.name.text === name);
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The `var` declaration of a name anywhere below one scope, outside every function nested in it. */
|
|
169
|
+
function varDeclaration(scope: Node, name: string): Node | undefined {
|
|
170
|
+
let found: Node | undefined;
|
|
171
|
+
const visit = (node: Node): void => {
|
|
172
|
+
if (found !== undefined || isFunctionLikeDeclaration(node) || isClassLike(node)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (isVariableDeclaration(node) && (node.parent.flags & NodeFlags.BlockScoped) === 0) {
|
|
176
|
+
found = boundAt(node, node.name, name);
|
|
177
|
+
if (found !== undefined) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
node.forEachChild(visit);
|
|
182
|
+
};
|
|
183
|
+
scope.forEachChild(visit);
|
|
184
|
+
return found;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function isClassLike(node: Node): boolean {
|
|
188
|
+
return isClassDeclaration(node) || isClassExpression(node);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The statements a scope holds directly, for a block, a case block or a file. */
|
|
192
|
+
function statementsOf(scope: Node): readonly Node[] {
|
|
193
|
+
if (isSourceFile(scope) || isBlock(scope) || isModuleBlock(scope)) {
|
|
194
|
+
return scope.statements;
|
|
195
|
+
}
|
|
196
|
+
if (isCaseBlock(scope)) {
|
|
197
|
+
return scope.clauses.flatMap((clause) => [...clause.statements]);
|
|
198
|
+
}
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** The declaration one scope makes of a name, ECMAScript's lexical and variable environments read from the syntax. */
|
|
203
|
+
function scopeDeclaration(scope: Node, name: string): Node | undefined {
|
|
204
|
+
if (isFunctionLikeDeclaration(scope)) {
|
|
205
|
+
const own =
|
|
206
|
+
(isFunctionExpression(scope) || isFunctionDeclaration(scope)) && scope.name?.text === name
|
|
207
|
+
? isFunctionExpression(scope)
|
|
208
|
+
? scope
|
|
209
|
+
: undefined
|
|
210
|
+
: undefined;
|
|
211
|
+
const parameter = scope.parameters
|
|
212
|
+
.map((one) => boundAt(one, one.name, name))
|
|
213
|
+
.find((one) => one !== undefined);
|
|
214
|
+
const body = scope.body;
|
|
215
|
+
return (
|
|
216
|
+
parameter ??
|
|
217
|
+
(body === undefined
|
|
218
|
+
? undefined
|
|
219
|
+
: (statementsOf(body)
|
|
220
|
+
.map((one) => blockDeclaration(one, name))
|
|
221
|
+
.find(Boolean) ?? varDeclaration(body, name))) ??
|
|
222
|
+
own
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (isClassExpression(scope) && scope.name?.text === name) {
|
|
226
|
+
return scope;
|
|
227
|
+
}
|
|
228
|
+
if (isCatchClause(scope)) {
|
|
229
|
+
const caught = scope.variableDeclaration;
|
|
230
|
+
return caught === undefined ? undefined : boundAt(caught, caught.name, name);
|
|
231
|
+
}
|
|
232
|
+
if (isForStatement(scope) || isForInStatement(scope) || isForOfStatement(scope)) {
|
|
233
|
+
const list = scope.initializer;
|
|
234
|
+
return list !== undefined &&
|
|
235
|
+
isVariableDeclarationList(list) &&
|
|
236
|
+
(list.flags & NodeFlags.BlockScoped) !== 0
|
|
237
|
+
? list.declarations
|
|
238
|
+
.map((one) => boundAt(one, one.name, name))
|
|
239
|
+
.find((one) => one !== undefined)
|
|
240
|
+
: undefined;
|
|
241
|
+
}
|
|
242
|
+
const declared = statementsOf(scope)
|
|
243
|
+
.map((one) => blockDeclaration(one, name))
|
|
244
|
+
.find(Boolean);
|
|
245
|
+
return declared ?? (isSourceFile(scope) ? varDeclaration(scope, name) : undefined);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The declaration an identifier written in a module resolves to by the scopes that enclose
|
|
250
|
+
* it, nearest first, or undefined where none of them, the file included, declares it, as
|
|
251
|
+
* for a global. A module read outside every program has no checker to ask.
|
|
252
|
+
*/
|
|
253
|
+
export function declarationOf(occurrence: Node & { readonly text: string }): Node | undefined {
|
|
254
|
+
for (let scope = occurrence.parent; ; scope = scope.parent) {
|
|
255
|
+
const found = scopeDeclaration(scope, occurrence.text);
|
|
256
|
+
if (found !== undefined || isSourceFile(scope)) {
|
|
257
|
+
return found;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** The text a property name writes: a name, a literal, or a computed literal; undefined where it computes one. */
|
|
263
|
+
function keyOf(name: Node): string | undefined {
|
|
264
|
+
if (
|
|
265
|
+
isIdentifier(name) ||
|
|
266
|
+
isStringLiteral(name) ||
|
|
267
|
+
isNoSubstitutionTemplateLiteral(name) ||
|
|
268
|
+
isNumericLiteral(name)
|
|
269
|
+
) {
|
|
270
|
+
return name.text;
|
|
271
|
+
}
|
|
272
|
+
if (isComputedPropertyName(name)) {
|
|
273
|
+
const key = unwrapped(name.expression);
|
|
274
|
+
if (isStringLiteral(key) || isNoSubstitutionTemplateLiteral(key) || isNumericLiteral(key)) {
|
|
275
|
+
return key.text;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Whether one expression is the module's own directory: a free `__dirname`, or `import.meta.dirname`. */
|
|
282
|
+
function isOwnDirectory(node: Node): boolean {
|
|
283
|
+
const held = unwrapped(node);
|
|
284
|
+
if (isIdentifier(held)) {
|
|
285
|
+
return held.text === "__dirname" && declarationOf(held) === undefined;
|
|
286
|
+
}
|
|
287
|
+
return (
|
|
288
|
+
isPropertyAccessExpression(held) &&
|
|
289
|
+
isMetaProperty(held.expression) &&
|
|
290
|
+
held.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
291
|
+
held.name.text === "dirname"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** The literal segments a `resolve` or `join` call of the module's own directory writes, or undefined. */
|
|
296
|
+
function directorySegments(node: Node): readonly string[] | undefined {
|
|
297
|
+
if (!isCallExpression(node)) {
|
|
298
|
+
return undefined;
|
|
299
|
+
}
|
|
300
|
+
const callee = unwrapped(node.expression);
|
|
301
|
+
const name = isPropertyAccessExpression(callee) ? callee.name : callee;
|
|
302
|
+
const [first, ...rest] = node.arguments;
|
|
303
|
+
if (
|
|
304
|
+
!isIdentifier(name) ||
|
|
305
|
+
(name.text !== "resolve" && name.text !== "join") ||
|
|
306
|
+
first === undefined ||
|
|
307
|
+
!isOwnDirectory(first)
|
|
308
|
+
) {
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
const segments = rest.map((one) => {
|
|
312
|
+
const part = unwrapped(one);
|
|
313
|
+
return isStringLiteral(part) || isNoSubstitutionTemplateLiteral(part) ? part.text : undefined;
|
|
314
|
+
});
|
|
315
|
+
return segments.every((one) => one !== undefined) ? segments : undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** The value one expression writes out, parentheses, `as`, `satisfies` and `!` read through. */
|
|
319
|
+
export function writtenValue(node: Node): Value {
|
|
320
|
+
const held = unwrapped(node);
|
|
321
|
+
const at = held.getStart();
|
|
322
|
+
const segments = directorySegments(held);
|
|
323
|
+
if (segments !== undefined) {
|
|
324
|
+
return { kind: "directory", segments, at };
|
|
325
|
+
}
|
|
326
|
+
if (isStringLiteral(held) || isNoSubstitutionTemplateLiteral(held)) {
|
|
327
|
+
return { kind: "text", text: held.text, at };
|
|
328
|
+
}
|
|
329
|
+
if (held.kind === SyntaxKind.TrueKeyword || held.kind === SyntaxKind.FalseKeyword) {
|
|
330
|
+
return { kind: "boolean", value: held.kind === SyntaxKind.TrueKeyword, at };
|
|
331
|
+
}
|
|
332
|
+
if (isArrayLiteralExpression(held)) {
|
|
333
|
+
return {
|
|
334
|
+
kind: "array",
|
|
335
|
+
elements: held.elements.map((element) =>
|
|
336
|
+
element.kind === SyntaxKind.SpreadElement
|
|
337
|
+
? { kind: "opaque", at: element.getStart() }
|
|
338
|
+
: writtenValue(element),
|
|
339
|
+
),
|
|
340
|
+
at,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (isObjectLiteralExpression(held)) {
|
|
344
|
+
return {
|
|
345
|
+
kind: "object",
|
|
346
|
+
members: held.properties.map((member): Member => {
|
|
347
|
+
const start = member.getStart();
|
|
348
|
+
if (isSpreadAssignment(member)) {
|
|
349
|
+
return { key: undefined, value: { kind: "opaque", at: start }, at: start };
|
|
350
|
+
}
|
|
351
|
+
const key = keyOf(member.name);
|
|
352
|
+
return {
|
|
353
|
+
key,
|
|
354
|
+
value: isPropertyAssignment(member)
|
|
355
|
+
? writtenValue(member.initializer)
|
|
356
|
+
: { kind: "opaque", at: start },
|
|
357
|
+
at: start,
|
|
358
|
+
};
|
|
359
|
+
}),
|
|
360
|
+
at,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
return { kind: "opaque", at };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* The initializer of a top-level `const`, `let` or `var` declaration of the file that
|
|
368
|
+
* declares a name and that the file assigns nowhere else, so the initializer is the value
|
|
369
|
+
* the name holds.
|
|
370
|
+
*/
|
|
371
|
+
function topLevelValue(declaration: Node | undefined, file: SourceFile): Node | undefined {
|
|
372
|
+
if (
|
|
373
|
+
declaration === undefined ||
|
|
374
|
+
!isVariableDeclaration(declaration) ||
|
|
375
|
+
!isIdentifier(declaration.name) ||
|
|
376
|
+
declaration.initializer === undefined
|
|
377
|
+
) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
const statement = declaration.parent.parent;
|
|
381
|
+
if (!isVariableStatement(statement) || statement.parent !== file) {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
const constant = (declaration.parent.flags & NodeFlags.Const) !== 0;
|
|
385
|
+
return constant || !assigned(file, declaration, declaration.name.text)
|
|
386
|
+
? declaration.initializer
|
|
387
|
+
: undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Whether the file writes a name that resolves to one declaration anywhere but its initializer. */
|
|
391
|
+
function assigned(file: SourceFile, declaration: Node, name: string): boolean {
|
|
392
|
+
let found = false;
|
|
393
|
+
const visit = (node: Node): void => {
|
|
394
|
+
if (found) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
if (isIdentifier(node)) {
|
|
398
|
+
found = node.text === name && isWriteTarget(node) && declarationOf(node) === declaration;
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
node.forEachChild(visit);
|
|
402
|
+
};
|
|
403
|
+
file.forEachChild(visit);
|
|
404
|
+
return found;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Whether one identifier is written: the target of an assignment, a destructuring
|
|
409
|
+
* assignment's element included, the operand of `++` or `--`, or a `for`-`in` or
|
|
410
|
+
* `for`-`of` target.
|
|
411
|
+
*/
|
|
412
|
+
function isWriteTarget(node: Node): boolean {
|
|
413
|
+
let at = node;
|
|
414
|
+
for (let parent = at.parent; ; at = parent, parent = parent.parent) {
|
|
415
|
+
if (isParenthesizedExpression(parent) || isSpreadElement(parent)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (isShorthandPropertyAssignment(parent) && parent.name === at) {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (
|
|
422
|
+
(isPropertyAssignment(parent) && parent.initializer === at) ||
|
|
423
|
+
isArrayLiteralExpression(parent) ||
|
|
424
|
+
isObjectLiteralExpression(parent)
|
|
425
|
+
) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
const parent = at.parent;
|
|
431
|
+
if (isBinaryExpression(parent)) {
|
|
432
|
+
return parent.left === at && isAssignmentOperator(parent.operatorToken.kind);
|
|
433
|
+
}
|
|
434
|
+
if (isPrefixUnaryExpression(parent) || isPostfixUnaryExpression(parent)) {
|
|
435
|
+
return (
|
|
436
|
+
at === node &&
|
|
437
|
+
(parent.operator === SyntaxKind.PlusPlusToken ||
|
|
438
|
+
parent.operator === SyntaxKind.MinusMinusToken)
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
return (isForInStatement(parent) || isForOfStatement(parent)) && parent.initializer === at;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* The values one expression stands for: a name of a top-level binding read through to its
|
|
446
|
+
* initializer, and, where `throughCalls`, a call read through to every argument it passes.
|
|
447
|
+
* A `require` call's argument names a module, which is no value the call returns.
|
|
448
|
+
*/
|
|
449
|
+
function readThrough(
|
|
450
|
+
node: Node,
|
|
451
|
+
file: SourceFile,
|
|
452
|
+
throughCalls: boolean,
|
|
453
|
+
seen: ReadonlySet<Node> = new Set(),
|
|
454
|
+
): readonly Value[] {
|
|
455
|
+
const held = unwrapped(node);
|
|
456
|
+
if (isIdentifier(held)) {
|
|
457
|
+
const value = topLevelValue(declarationOf(held), file);
|
|
458
|
+
if (value !== undefined && !seen.has(value)) {
|
|
459
|
+
return readThrough(value, file, throughCalls, new Set([...seen, value]));
|
|
460
|
+
}
|
|
461
|
+
} else if (throughCalls && isCallExpression(held) && !isRequireCall(held)) {
|
|
462
|
+
return held.arguments.flatMap((argument) => readThrough(argument, file, throughCalls, seen));
|
|
463
|
+
}
|
|
464
|
+
return [writtenValue(held)];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** The local names one module binds to another module's exports at its top level. */
|
|
468
|
+
interface Bindings {
|
|
469
|
+
/** Each local name bound to one export, by the export's name; `default` for the module's value. */
|
|
470
|
+
readonly named: ReadonlyMap<Node, string>;
|
|
471
|
+
/** The declarations of each local name bound to the whole module. */
|
|
472
|
+
readonly namespaces: ReadonlySet<Node>;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** A written name's binding by the scopes that enclose it, {@link declarationOf} answering. */
|
|
476
|
+
const scopeBinding = lexicalBinding(declarationOf);
|
|
477
|
+
|
|
478
|
+
/** Whether one call is a `require()` call, read through `bindingOf`, the file's own scopes where no checker answers. */
|
|
479
|
+
function isRequireCall(call: CallExpression, bindingOf: BindingOf = scopeBinding): boolean {
|
|
480
|
+
const callee = writtenName(call.expression);
|
|
481
|
+
return callee !== undefined && callsPlatformBy("require", callee, bindingOf);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** The literal one call's one argument writes, where it is a string literal or a template literal with no substitution. */
|
|
485
|
+
function loadedSpecifier(call: CallExpression): string | undefined {
|
|
486
|
+
const [argument, ...rest] = call.arguments;
|
|
487
|
+
return argument !== undefined &&
|
|
488
|
+
rest.length === 0 &&
|
|
489
|
+
(isStringLiteral(argument) || isNoSubstitutionTemplateLiteral(argument))
|
|
490
|
+
? argument.text
|
|
491
|
+
: undefined;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** Whether one expression is a `require()` call of a module `isModule` accepts, its specifier the one literal argument. */
|
|
495
|
+
function requires(
|
|
496
|
+
node: Node,
|
|
497
|
+
isModule: (specifier: string) => boolean,
|
|
498
|
+
bindingOf: BindingOf = scopeBinding,
|
|
499
|
+
): boolean {
|
|
500
|
+
const held = unwrapped(node);
|
|
501
|
+
if (!isCallExpression(held)) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
const specifier = loadedSpecifier(held);
|
|
505
|
+
return specifier !== undefined && isModule(specifier) && isRequireCall(held, bindingOf);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** What {@link boundExports} lists for a name bound to the module itself. */
|
|
509
|
+
export const NAMESPACE = "*";
|
|
510
|
+
|
|
511
|
+
/** Whether one import or re-export names a module `isModule` accepts. */
|
|
512
|
+
function fromModule(declaration: Node, isModule: (specifier: string) => boolean): boolean {
|
|
513
|
+
return (
|
|
514
|
+
isImportDeclaration(declaration) &&
|
|
515
|
+
isStringLiteral(declaration.moduleSpecifier) &&
|
|
516
|
+
isModule(declaration.moduleSpecifier.text)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* What one declaration binds of a module `isModule` accepts: the export each name names,
|
|
522
|
+
* `default` for the module's value, and {@link NAMESPACE} for the module itself. A default
|
|
523
|
+
* import, an import assignment and a name a `require()` call initializes bind the value and
|
|
524
|
+
* the module both, as interop reads them; an object binding pattern of such a call binds each
|
|
525
|
+
* export it lists, and a member read of one binds that member.
|
|
526
|
+
*/
|
|
527
|
+
export function boundExports(
|
|
528
|
+
declaration: Node,
|
|
529
|
+
isModule: (specifier: string) => boolean,
|
|
530
|
+
bindingOf: BindingOf = scopeBinding,
|
|
531
|
+
): readonly string[] {
|
|
532
|
+
if (isImportClause(declaration)) {
|
|
533
|
+
return declaration.name !== undefined && fromModule(declaration.parent, isModule)
|
|
534
|
+
? ["default", NAMESPACE]
|
|
535
|
+
: [];
|
|
536
|
+
}
|
|
537
|
+
if (isNamespaceImport(declaration)) {
|
|
538
|
+
return fromModule(declaration.parent.parent, isModule) ? [NAMESPACE] : [];
|
|
539
|
+
}
|
|
540
|
+
if (isImportSpecifier(declaration)) {
|
|
541
|
+
return fromModule(declaration.parent.parent.parent, isModule)
|
|
542
|
+
? [(declaration.propertyName ?? declaration.name).text]
|
|
543
|
+
: [];
|
|
544
|
+
}
|
|
545
|
+
if (isImportEqualsDeclaration(declaration)) {
|
|
546
|
+
const reference = declaration.moduleReference;
|
|
547
|
+
return isExternalModuleReference(reference) &&
|
|
548
|
+
isStringLiteralLikeNode(reference.expression) &&
|
|
549
|
+
isModule(reference.expression.text)
|
|
550
|
+
? ["default", NAMESPACE]
|
|
551
|
+
: [];
|
|
552
|
+
}
|
|
553
|
+
if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) {
|
|
554
|
+
const holder = declaration.parent.parent;
|
|
555
|
+
const exported = declaration.propertyName ?? declaration.name;
|
|
556
|
+
const key = exported === undefined ? undefined : keyOf(exported);
|
|
557
|
+
return declaration.dotDotDotToken === undefined &&
|
|
558
|
+
declaration.name !== undefined &&
|
|
559
|
+
isIdentifier(declaration.name) &&
|
|
560
|
+
key !== undefined &&
|
|
561
|
+
isVariableDeclaration(holder) &&
|
|
562
|
+
holder.initializer !== undefined &&
|
|
563
|
+
requires(holder.initializer, isModule, bindingOf)
|
|
564
|
+
? [key]
|
|
565
|
+
: [];
|
|
566
|
+
}
|
|
567
|
+
if (!isVariableDeclaration(declaration) || declaration.initializer === undefined) {
|
|
568
|
+
return [];
|
|
569
|
+
}
|
|
570
|
+
const value = unwrapped(declaration.initializer);
|
|
571
|
+
if (isIdentifier(declaration.name) && requires(value, isModule, bindingOf)) {
|
|
572
|
+
return ["default", NAMESPACE];
|
|
573
|
+
}
|
|
574
|
+
return isIdentifier(declaration.name) &&
|
|
575
|
+
isPropertyAccessExpression(value) &&
|
|
576
|
+
requires(value.expression, isModule, bindingOf)
|
|
577
|
+
? [value.name.text]
|
|
578
|
+
: [];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* The relative specifiers one module's imports and re-exports name, and those its
|
|
583
|
+
* `require()` calls and `import()` calls name by a literal, in the order the file writes
|
|
584
|
+
* them. A module read outside every program answers it so, with no checker to ask.
|
|
585
|
+
*/
|
|
586
|
+
export function relativeSpecifiers(file: SourceFile): readonly string[] {
|
|
587
|
+
const known = specifiersOf.get(file);
|
|
588
|
+
if (known !== undefined) {
|
|
589
|
+
return known;
|
|
590
|
+
}
|
|
591
|
+
const found: string[] = [];
|
|
592
|
+
const named = (specifier: string | undefined): void => {
|
|
593
|
+
if (specifier !== undefined && startsRelativePath(specifier)) {
|
|
594
|
+
found.push(specifier);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const visit = (node: Node): void => {
|
|
598
|
+
if (
|
|
599
|
+
(isImportDeclaration(node) || isExportDeclaration(node)) &&
|
|
600
|
+
node.moduleSpecifier !== undefined &&
|
|
601
|
+
isStringLiteral(node.moduleSpecifier)
|
|
602
|
+
) {
|
|
603
|
+
named(node.moduleSpecifier.text);
|
|
604
|
+
} else if (
|
|
605
|
+
isCallExpression(node) &&
|
|
606
|
+
(node.expression.kind === SyntaxKind.ImportKeyword || isRequireCall(node))
|
|
607
|
+
) {
|
|
608
|
+
named(loadedSpecifier(node));
|
|
609
|
+
}
|
|
610
|
+
node.forEachChild(visit);
|
|
611
|
+
};
|
|
612
|
+
file.forEachChild(visit);
|
|
613
|
+
specifiersOf.set(file, found);
|
|
614
|
+
return found;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/** What {@link relativeSpecifiers} found in each file, which every project of a run reads alike. */
|
|
618
|
+
const specifiersOf = new WeakMap<SourceFile, readonly string[]>();
|
|
619
|
+
|
|
620
|
+
/** The declarations one top-level statement makes that may bind another module. */
|
|
621
|
+
function bindingDeclarations(statement: Node): readonly Node[] {
|
|
622
|
+
if (isImportDeclaration(statement)) {
|
|
623
|
+
const clause = statement.importClause;
|
|
624
|
+
const bindings = clause?.namedBindings;
|
|
625
|
+
return [
|
|
626
|
+
...(clause === undefined ? [] : [clause]),
|
|
627
|
+
...(bindings === undefined
|
|
628
|
+
? []
|
|
629
|
+
: isNamespaceImport(bindings)
|
|
630
|
+
? [bindings]
|
|
631
|
+
: bindings.elements),
|
|
632
|
+
];
|
|
633
|
+
}
|
|
634
|
+
if (isImportEqualsDeclaration(statement)) {
|
|
635
|
+
return [statement];
|
|
636
|
+
}
|
|
637
|
+
if (!isVariableStatement(statement)) {
|
|
638
|
+
return [];
|
|
639
|
+
}
|
|
640
|
+
return statement.declarationList.declarations.flatMap((declaration) =>
|
|
641
|
+
isObjectBindingPattern(declaration.name)
|
|
642
|
+
? [declaration, ...declaration.name.elements]
|
|
643
|
+
: [declaration],
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/** The top-level bindings one file makes of a module, as {@link boundExports} reads each. */
|
|
648
|
+
function bindingsOf(file: SourceFile, module: string): Bindings {
|
|
649
|
+
const named = new Map<Node, string>();
|
|
650
|
+
const namespaces = new Set<Node>();
|
|
651
|
+
const isModule = (specifier: string): boolean => specifier === module;
|
|
652
|
+
for (const declaration of file.statements.flatMap(bindingDeclarations)) {
|
|
653
|
+
for (const exported of boundExports(declaration, isModule)) {
|
|
654
|
+
if (exported === NAMESPACE) {
|
|
655
|
+
namespaces.add(declaration);
|
|
656
|
+
} else {
|
|
657
|
+
named.set(declaration, exported);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return { named, namespaces };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** One call of a module's export the file makes, and the export it calls. */
|
|
665
|
+
interface ExportCall {
|
|
666
|
+
readonly call: CallExpression;
|
|
667
|
+
readonly export: string;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Every call one file makes of one module's exports: through a name an import or a
|
|
672
|
+
* `require` binding gives it, a namespace's member, a member of a `require` of the
|
|
673
|
+
* module, or that `require`'s value called itself, each where no scope enclosing the
|
|
674
|
+
* call declares the name otherwise.
|
|
675
|
+
*/
|
|
676
|
+
function callsOf(file: SourceFile, module: string): readonly ExportCall[] {
|
|
677
|
+
const { named, namespaces } = bindingsOf(file, module);
|
|
678
|
+
const found: ExportCall[] = [];
|
|
679
|
+
const visit = (node: Node): void => {
|
|
680
|
+
if (isCallExpression(node)) {
|
|
681
|
+
const callee = unwrapped(node.expression);
|
|
682
|
+
if (isIdentifier(callee)) {
|
|
683
|
+
const declaration = declarationOf(callee);
|
|
684
|
+
const exported = declaration === undefined ? undefined : named.get(declaration);
|
|
685
|
+
if (exported !== undefined) {
|
|
686
|
+
found.push({ call: node, export: exported });
|
|
687
|
+
}
|
|
688
|
+
} else if (requires(callee, (specifier) => specifier === module)) {
|
|
689
|
+
found.push({ call: node, export: "default" });
|
|
690
|
+
} else if (isPropertyAccessExpression(callee)) {
|
|
691
|
+
const holder = unwrapped(callee.expression);
|
|
692
|
+
const declaration = isIdentifier(holder) ? declarationOf(holder) : undefined;
|
|
693
|
+
if (
|
|
694
|
+
(declaration !== undefined && namespaces.has(declaration)) ||
|
|
695
|
+
requires(holder, (specifier) => specifier === module)
|
|
696
|
+
) {
|
|
697
|
+
found.push({ call: node, export: callee.name.text });
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
node.forEachChild(visit);
|
|
702
|
+
};
|
|
703
|
+
file.forEachChild(visit);
|
|
704
|
+
return found;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/** What one module writes out, read without running it. */
|
|
708
|
+
export interface StaticModule {
|
|
709
|
+
/** Every value the module exports as its default, in each form, read through names and calls. */
|
|
710
|
+
readonly defaultExport: readonly Value[];
|
|
711
|
+
/** The first argument of each call of one module's export, read through a top-level name. */
|
|
712
|
+
options(module: string, exported: string): readonly Value[];
|
|
713
|
+
/** Every call of one module's exports the module makes. */
|
|
714
|
+
calls(module: string): readonly ExportCall[];
|
|
715
|
+
/** The first argument one call passes, read through a top-level name. */
|
|
716
|
+
argument(call: CallExpression): readonly Value[];
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** One parsed module, or a JSON document parsed as one, read as {@link StaticModule}. */
|
|
720
|
+
export function staticModule(file: SourceFile): StaticModule {
|
|
721
|
+
const argument = (call: CallExpression): readonly Value[] => {
|
|
722
|
+
const [first] = call.arguments;
|
|
723
|
+
return first === undefined ? [] : readThrough(first, file, false);
|
|
724
|
+
};
|
|
725
|
+
return {
|
|
726
|
+
defaultExport: defaultExportOf(file),
|
|
727
|
+
options: (module, exported) =>
|
|
728
|
+
callsOf(file, module).flatMap((one) => (one.export === exported ? argument(one.call) : [])),
|
|
729
|
+
calls: (module) => callsOf(file, module),
|
|
730
|
+
argument,
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/** A YAML document's value read as {@link StaticModule}: its default export, and no call. */
|
|
735
|
+
export function documentModule(value: unknown): StaticModule {
|
|
736
|
+
return {
|
|
737
|
+
defaultExport: [documentValue(value)],
|
|
738
|
+
options: () => [],
|
|
739
|
+
calls: () => [],
|
|
740
|
+
argument: () => [],
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** The value a CommonJS module leaves, its later member writes and any hidden member on it. */
|
|
745
|
+
function moduleValueOf(written: ModuleValue, file: SourceFile): readonly Value[] {
|
|
746
|
+
const members: Member[] = written.members.map((member) => {
|
|
747
|
+
const at = member.node.getStart();
|
|
748
|
+
return {
|
|
749
|
+
key: member.key,
|
|
750
|
+
value: member.value === undefined ? { kind: "opaque", at } : writtenValue(member.value),
|
|
751
|
+
at,
|
|
752
|
+
};
|
|
753
|
+
});
|
|
754
|
+
if (written.open) {
|
|
755
|
+
members.push({ key: undefined, value: { kind: "opaque", at: undefined }, at: undefined });
|
|
756
|
+
}
|
|
757
|
+
if (written.whole === undefined) {
|
|
758
|
+
return members.length === 0 ? [] : [{ kind: "object", members, at: members[0]?.at }];
|
|
759
|
+
}
|
|
760
|
+
return readThrough(written.whole, file, true).map((value) =>
|
|
761
|
+
value.kind === "object" ? { ...value, members: [...value.members, ...members] } : value,
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
/**
|
|
766
|
+
* The values a module's default export forms write: `export default`, `export =`, a
|
|
767
|
+
* `default` specifier, a JSON document, and, in a file Node runs as CommonJS, the module
|
|
768
|
+
* value as Node leaves it. A default function or class writes no value to read.
|
|
769
|
+
*/
|
|
770
|
+
function defaultExportOf(file: SourceFile): readonly Value[] {
|
|
771
|
+
if (file.fileName.endsWith(".json")) {
|
|
772
|
+
return file.statements.flatMap((statement) =>
|
|
773
|
+
isExpressionStatement(statement) ? [writtenValue(statement.expression)] : [],
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
const commonJS = commonJSModuleValue(file, declarationOf);
|
|
777
|
+
if (commonJS !== undefined) {
|
|
778
|
+
return moduleValueOf(commonJS, file);
|
|
779
|
+
}
|
|
780
|
+
const found: Value[] = [];
|
|
781
|
+
for (const statement of file.statements) {
|
|
782
|
+
if (isExportAssignment(statement)) {
|
|
783
|
+
found.push(...readThrough(statement.expression, file, true));
|
|
784
|
+
} else if (
|
|
785
|
+
(isFunctionDeclaration(statement) || isClassDeclaration(statement)) &&
|
|
786
|
+
statement.modifiers?.some((one) => one.kind === SyntaxKind.DefaultKeyword) === true
|
|
787
|
+
) {
|
|
788
|
+
found.push({ kind: "opaque", at: statement.getStart() });
|
|
789
|
+
} else if (
|
|
790
|
+
isExportDeclaration(statement) &&
|
|
791
|
+
statement.moduleSpecifier === undefined &&
|
|
792
|
+
statement.exportClause !== undefined &&
|
|
793
|
+
isNamedExports(statement.exportClause)
|
|
794
|
+
) {
|
|
795
|
+
for (const element of statement.exportClause.elements) {
|
|
796
|
+
if (element.name.text === "default") {
|
|
797
|
+
found.push(...readThrough(element.propertyName ?? element.name, file, true));
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return found;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* The values one object writes for a key after its last spread or computed key, the last
|
|
807
|
+
* written last, or undefined where none follows one, which may write the key unseen. A
|
|
808
|
+
* value that is no object writes none.
|
|
809
|
+
*/
|
|
810
|
+
export function membersNamed(value: Value, key: string): readonly Value[] | undefined {
|
|
811
|
+
if (value.kind !== "object") {
|
|
812
|
+
return [];
|
|
813
|
+
}
|
|
814
|
+
const hidden = value.members.findLastIndex((member) => member.key === undefined);
|
|
815
|
+
const after = value.members.slice(hidden + 1).filter((member) => member.key === key);
|
|
816
|
+
return hidden >= 0 && after.length === 0 ? undefined : after.map((member) => member.value);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* The texts one value writes: itself where it is text, or, where `elements`, each element
|
|
821
|
+
* of an array of texts. Undefined for any other value.
|
|
822
|
+
*/
|
|
823
|
+
export function textsOf(value: Value, elements: boolean): readonly string[] | undefined {
|
|
824
|
+
if (value.kind === "text") {
|
|
825
|
+
return [value.text];
|
|
826
|
+
}
|
|
827
|
+
if (!elements || value.kind !== "array") {
|
|
828
|
+
return undefined;
|
|
829
|
+
}
|
|
830
|
+
const texts = value.elements.map((element) =>
|
|
831
|
+
element.kind === "text" ? element.text : undefined,
|
|
832
|
+
);
|
|
833
|
+
return texts.every((text) => text !== undefined) ? texts : undefined;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* The values at one path of keys below each value, whatever else the path holds: a key
|
|
838
|
+
* reads an object's members of that name, and `*` every element of an array and every
|
|
839
|
+
* member of an object.
|
|
840
|
+
*/
|
|
841
|
+
export function valuesAt(values: readonly Value[], path: readonly string[]): readonly Value[] {
|
|
842
|
+
const [key, ...rest] = path;
|
|
843
|
+
if (key === undefined) {
|
|
844
|
+
return values;
|
|
845
|
+
}
|
|
846
|
+
const next = values.flatMap((value): readonly Value[] => {
|
|
847
|
+
if (key === "*" && value.kind === "array") {
|
|
848
|
+
return value.elements;
|
|
849
|
+
}
|
|
850
|
+
if (value.kind !== "object") {
|
|
851
|
+
return [];
|
|
852
|
+
}
|
|
853
|
+
return value.members
|
|
854
|
+
.filter((member) => key === "*" || member.key === key)
|
|
855
|
+
.map((member) => member.value);
|
|
856
|
+
});
|
|
857
|
+
return valuesAt(next, rest);
|
|
858
|
+
}
|