@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.
- 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 +2 -7
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +269 -432
- 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/calls.js +279 -43
- 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 +1 -1
- 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 +1369 -0
- package/dist/src/contract/report.generated.js +866 -0
- package/dist/src/dependencies.js +164 -121
- 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/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -66
- 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/framework-lifecycle.js +46 -19
- 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/generated-file.js +174 -28
- 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.js +2 -2
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/injection-container.js +2 -2
- package/dist/src/interface-satisfaction.js +1 -1
- 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 +47 -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/serialization-contract.js +601 -22
- 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/template-field.js +9 -19
- 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 +7 -5
- 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 +2 -9
- package/src/amd.ts +130 -0
- package/src/analysis.ts +424 -650
- package/src/augmentations.ts +93 -4
- package/src/automatic-types.ts +12 -30
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- 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 +1 -1
- 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 +1452 -0
- package/src/contract/report.generated.ts +1241 -0
- package/src/dependencies.ts +222 -127
- 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/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +70 -98
- 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/framework-lifecycle.ts +47 -19
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +96 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +242 -35
- 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.ts +2 -2
- package/src/indexed-access.ts +570 -527
- package/src/injection-container.ts +2 -2
- package/src/interface-satisfaction.ts +2 -2
- 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 +92 -0
- 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} +27 -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/serialization-contract.ts +769 -23
- 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/template-field.ts +15 -25
- 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 +7 -5
- 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/conventions.js +0 -937
- package/dist/src/discover.js +0 -468
- package/dist/src/entry-points.js +0 -529
- 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/setup-failure.js +0 -23
- package/dist/src/sweep.js +0 -224
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/dist/src/workflow-steps.js +0 -185
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/entry-points.ts +0 -687
- 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/setup-failure.ts +0 -39
- package/src/sweep.ts +0 -347
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
- package/src/workflow-steps.ts +0 -216
package/src/overrides.ts
CHANGED
|
@@ -15,14 +15,14 @@ import {
|
|
|
15
15
|
SyntaxKind,
|
|
16
16
|
type HeritageClauseElement,
|
|
17
17
|
type Node,
|
|
18
|
+
type SourceFile,
|
|
18
19
|
} from "@typescript/native/ast";
|
|
19
|
-
import type { AliasChains } from "./alias-chain.ts";
|
|
20
20
|
import { memberComponent } from "./class-members.ts";
|
|
21
|
-
import { nodeKey, type
|
|
21
|
+
import { nodeKey, type InventorySymbol } from "./inventory.ts";
|
|
22
22
|
import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
|
|
23
23
|
import { UNANSWERED } from "./query.ts";
|
|
24
24
|
import { nameComponent } from "./ref.ts";
|
|
25
|
-
import type {
|
|
25
|
+
import type { Read, ReadRule } from "./references/read-rule.ts";
|
|
26
26
|
import type { ProjectView } from "./session.ts";
|
|
27
27
|
|
|
28
28
|
const CLASS_DECLARATIONS: ReadonlySet<SyntaxKind> = new Set([
|
|
@@ -41,141 +41,157 @@ function nameOf(entry: HeritageClauseElement): Node {
|
|
|
41
41
|
return isQualifiedName(entry.typeName) ? entry.typeName.right : entry.typeName;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const found: { id: string; base: Node }[] = [];
|
|
50
|
-
for (const file of project.ownSourceFiles()) {
|
|
51
|
-
const visit = (node: Node): void => {
|
|
52
|
-
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
53
|
-
const id = held.declarations.get(nodeKey(file, node));
|
|
54
|
-
const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
|
|
55
|
-
const base = clause?.types[0];
|
|
56
|
-
if (id !== undefined && base !== undefined) {
|
|
57
|
-
found.push({ id, base: nameOf(base) });
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
node.forEachChild(visit);
|
|
61
|
-
};
|
|
62
|
-
file.forEachChild(visit);
|
|
63
|
-
}
|
|
64
|
-
return found;
|
|
44
|
+
/** One class whose `extends` clause names a class, with the name node and its file. */
|
|
45
|
+
interface Extending {
|
|
46
|
+
readonly node: Node;
|
|
47
|
+
readonly file: SourceFile;
|
|
48
|
+
readonly base: Node;
|
|
65
49
|
}
|
|
66
50
|
|
|
67
51
|
/**
|
|
68
|
-
* The
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
52
|
+
* The reads each base class member makes of its overrides, for the classes of the target that
|
|
53
|
+
* extend another class of the target, and each class makes of its members that override a
|
|
54
|
+
* member of a class declared outside the target. A member a nearer base already overrides is
|
|
55
|
+
* reached through that override. A private member is not overridden. Each read is placed at
|
|
56
|
+
* the override. A class whose base the checker leaves unanswered holds every member it could
|
|
57
|
+
* override.
|
|
73
58
|
*/
|
|
74
|
-
export
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
const own = members.get(symbol.parent) ?? new Map<string, InventorySymbol>();
|
|
97
|
-
own.set(component, symbol);
|
|
98
|
-
members.set(symbol.parent, own);
|
|
99
|
-
}
|
|
100
|
-
const bases = new Map<string, readonly string[]>();
|
|
101
|
-
const outsideBases = new Map<string, TSSymbol>();
|
|
102
|
-
const own = project.ownPaths();
|
|
103
|
-
const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
|
|
104
|
-
classes.forEach(({ id }, index) => {
|
|
105
|
-
const symbol = symbols[index];
|
|
106
|
-
if (symbol !== undefined && symbol !== UNANSWERED) {
|
|
107
|
-
const inTarget = chains
|
|
108
|
-
.chainOf(symbol)
|
|
109
|
-
.map((target) => target.id)
|
|
110
|
-
.filter((target) => byId.get(target)?.kind === "class" && target !== id);
|
|
111
|
-
bases.set(id, inTarget);
|
|
112
|
-
const declared =
|
|
113
|
-
(symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
|
|
114
|
-
if (
|
|
115
|
-
inTarget.length === 0 &&
|
|
116
|
-
declared !== UNANSWERED &&
|
|
117
|
-
(((declared.flags & SymbolFlags.Class) !== 0 &&
|
|
118
|
-
// The target's augmentation of an outside class merges an interface into it,
|
|
119
|
-
// so only a class declaration says where the class is.
|
|
120
|
-
declared.declarations.some(
|
|
121
|
-
(handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path),
|
|
122
|
-
)) ||
|
|
123
|
-
((declared.flags & SymbolFlags.Interface) !== 0 &&
|
|
124
|
-
declared.declarations.some(
|
|
125
|
-
(handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path),
|
|
126
|
-
)))
|
|
127
|
-
) {
|
|
128
|
-
outsideBases.set(id, declared);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
/** The nearest member of one component the class or a class it extends declares. */
|
|
134
|
-
const declaredAbove = (classId: string, component: string, seen: Set<string>): string[] => {
|
|
135
|
-
if (seen.has(classId)) {
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
138
|
-
seen.add(classId);
|
|
139
|
-
const own = members.get(classId)?.get(component);
|
|
140
|
-
if (own !== undefined) {
|
|
141
|
-
return [own.id];
|
|
142
|
-
}
|
|
143
|
-
return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const found: Reference[] = [];
|
|
147
|
-
const outsideNames = outsideMemberNames(project, bases, outsideBases);
|
|
148
|
-
for (const [classId, names] of outsideNames) {
|
|
149
|
-
for (const [component, member] of members.get(classId) ?? []) {
|
|
150
|
-
if (names.has(component)) {
|
|
151
|
-
found.push({
|
|
152
|
-
from: classId,
|
|
153
|
-
to: member.id,
|
|
154
|
-
position: member.position,
|
|
155
|
-
use: "read",
|
|
156
|
-
resolution: "override",
|
|
157
|
-
test: testFiles.has(member.position.path),
|
|
59
|
+
export const overrides: ReadRule = {
|
|
60
|
+
start() {
|
|
61
|
+
const extending: Extending[] = [];
|
|
62
|
+
return {
|
|
63
|
+
visit(node, { file }) {
|
|
64
|
+
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
65
|
+
const clause = node.heritageClauses?.find(
|
|
66
|
+
(one) => one.token === SyntaxKind.ExtendsKeyword,
|
|
67
|
+
);
|
|
68
|
+
const base = clause?.types[0];
|
|
69
|
+
if (base !== undefined) {
|
|
70
|
+
extending.push({ node, file, base: nameOf(base) });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
reads({ project, held, chains }) {
|
|
75
|
+
const found: Read[] = [];
|
|
76
|
+
const classes = extending.flatMap((one) => {
|
|
77
|
+
const id = held.declarations.get(nodeKey(one.file, one.node));
|
|
78
|
+
return id === undefined ? [] : [{ ...one, id }];
|
|
158
79
|
});
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
80
|
+
if (classes.length === 0) {
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
|
|
84
|
+
const members = new Map<string, Map<string, InventorySymbol>>();
|
|
85
|
+
for (const symbol of held.symbols) {
|
|
86
|
+
const component = memberComponent(symbol, byId);
|
|
87
|
+
if (
|
|
88
|
+
component === undefined ||
|
|
89
|
+
!OVERRIDABLE.has(symbol.kind) ||
|
|
90
|
+
symbol.visibility === "private" ||
|
|
91
|
+
symbol.visibility === "private-name"
|
|
92
|
+
) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const own = members.get(symbol.parent) ?? new Map<string, InventorySymbol>();
|
|
96
|
+
own.set(component, symbol);
|
|
97
|
+
members.set(symbol.parent, own);
|
|
98
|
+
}
|
|
99
|
+
const bases = new Map<string, readonly string[]>();
|
|
100
|
+
const outsideBases = new Map<string, TSSymbol>();
|
|
101
|
+
const fileOf = new Map(classes.map((one) => [one.id, one.file]));
|
|
102
|
+
const own = project.ownPaths();
|
|
103
|
+
const unanswered = new Set<string>();
|
|
104
|
+
const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
|
|
105
|
+
classes.forEach(({ id }, index) => {
|
|
106
|
+
const symbol = symbols[index];
|
|
107
|
+
if (symbol === UNANSWERED) {
|
|
108
|
+
unanswered.add(id);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (symbol === undefined) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const inTarget = chains
|
|
115
|
+
.chainOf(symbol)
|
|
116
|
+
.map((target) => target.id)
|
|
117
|
+
.filter((target) => byId.get(target)?.kind === "class" && target !== id);
|
|
118
|
+
bases.set(id, inTarget);
|
|
119
|
+
const declared =
|
|
120
|
+
(symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
|
|
121
|
+
if (declared === UNANSWERED) {
|
|
122
|
+
unanswered.add(id);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (
|
|
126
|
+
inTarget.length === 0 &&
|
|
127
|
+
(((declared.flags & SymbolFlags.Class) !== 0 &&
|
|
128
|
+
// The target's augmentation of an outside class merges an interface into it,
|
|
129
|
+
// so only a class declaration says where the class is.
|
|
130
|
+
declared.declarations.some(
|
|
131
|
+
(handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path),
|
|
132
|
+
)) ||
|
|
133
|
+
((declared.flags & SymbolFlags.Interface) !== 0 &&
|
|
134
|
+
declared.declarations.some(
|
|
135
|
+
(handle) =>
|
|
136
|
+
handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path),
|
|
137
|
+
)))
|
|
138
|
+
) {
|
|
139
|
+
outsideBases.set(id, declared);
|
|
140
|
+
}
|
|
173
141
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
142
|
+
|
|
143
|
+
/** The nearest member of one component the class or a class it extends declares. */
|
|
144
|
+
const declaredAbove = (classId: string, component: string, seen: Set<string>): string[] => {
|
|
145
|
+
if (seen.has(classId)) {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
seen.add(classId);
|
|
149
|
+
const member = members.get(classId)?.get(component);
|
|
150
|
+
if (member !== undefined) {
|
|
151
|
+
return [member.id];
|
|
152
|
+
}
|
|
153
|
+
return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const read = (classId: string, from: string, member: InventorySymbol): void => {
|
|
157
|
+
const file = fileOf.get(classId);
|
|
158
|
+
if (file !== undefined) {
|
|
159
|
+
found.push({
|
|
160
|
+
kind: "named",
|
|
161
|
+
from,
|
|
162
|
+
to: member.id,
|
|
163
|
+
file,
|
|
164
|
+
at: member.position,
|
|
165
|
+
use: "read",
|
|
166
|
+
via: "override",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const outside = outsideMemberNames(project, bases, outsideBases);
|
|
171
|
+
for (const classId of [...unanswered, ...outside.unanswered]) {
|
|
172
|
+
const ids = [...(members.get(classId)?.values() ?? [])].map((member) => member.id);
|
|
173
|
+
found.push({ kind: "held", from: classId, ids });
|
|
174
|
+
}
|
|
175
|
+
for (const [classId, names] of outside.names) {
|
|
176
|
+
for (const [component, member] of members.get(classId) ?? []) {
|
|
177
|
+
if (names.has(component)) {
|
|
178
|
+
read(classId, classId, member);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const [classId, baseIds] of bases) {
|
|
183
|
+
for (const [component, member] of members.get(classId) ?? []) {
|
|
184
|
+
const seen = new Set([classId]);
|
|
185
|
+
for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
|
|
186
|
+
read(classId, base, member);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return found;
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
};
|
|
179
195
|
|
|
180
196
|
/**
|
|
181
197
|
* Per class of the target with a class declared outside the target in its `extends`
|
|
@@ -186,12 +202,24 @@ function outsideMemberNames<Brand>(
|
|
|
186
202
|
project: ProjectView<Brand>,
|
|
187
203
|
bases: ReadonlyMap<string, readonly string[]>,
|
|
188
204
|
outsideBases: ReadonlyMap<string, TSSymbol>,
|
|
189
|
-
):
|
|
205
|
+
): {
|
|
206
|
+
readonly names: ReadonlyMap<string, ReadonlySet<string>>;
|
|
207
|
+
readonly unanswered: readonly string[];
|
|
208
|
+
} {
|
|
190
209
|
const direct = new Map<string, ReadonlySet<string>>();
|
|
210
|
+
const unanswered: string[] = [];
|
|
191
211
|
const error = errorType(project);
|
|
192
212
|
for (const [classId, base] of outsideBases) {
|
|
193
213
|
const instance = project.queries.declaredTypeOf(base);
|
|
194
214
|
const statics = project.queries.typeOfSymbol(base);
|
|
215
|
+
if (
|
|
216
|
+
instance === UNANSWERED ||
|
|
217
|
+
statics === UNANSWERED ||
|
|
218
|
+
project.queries.propertiesOf(instance) === UNANSWERED ||
|
|
219
|
+
(statics !== undefined && project.queries.propertiesOf(statics) === UNANSWERED)
|
|
220
|
+
) {
|
|
221
|
+
unanswered.push(classId);
|
|
222
|
+
}
|
|
195
223
|
const components = [
|
|
196
224
|
...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
|
|
197
225
|
...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
|
|
@@ -220,7 +248,7 @@ function outsideMemberNames<Brand>(
|
|
|
220
248
|
names.set(classId, found);
|
|
221
249
|
}
|
|
222
250
|
}
|
|
223
|
-
return names;
|
|
251
|
+
return { names, unanswered };
|
|
224
252
|
}
|
|
225
253
|
|
|
226
254
|
/** The global `Error` instance type's identifier, where the program's library declares one. */
|
package/src/partial.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { NotBuilt } from "./discover.ts";
|
|
1
|
+
import type { NotBuilt } from "./load/discover.ts";
|
|
2
2
|
import { relativePath } from "./paths.ts";
|
|
3
3
|
import type { UnansweredQuestion } from "./query.ts";
|
|
4
|
+
import { bytewise } from "./text/compare.ts";
|
|
4
5
|
|
|
5
6
|
/** What a run read only in part. */
|
|
6
7
|
interface Partial {
|
|
@@ -24,7 +25,7 @@ function counted(n: number, one: string, many: string): string {
|
|
|
24
25
|
*/
|
|
25
26
|
export function partialNotes(partial: Partial): readonly string[] {
|
|
26
27
|
const notes = [...partial.notBuilt]
|
|
27
|
-
.sort((a, b) => (a.id
|
|
28
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
28
29
|
.map(
|
|
29
30
|
(one) =>
|
|
30
31
|
`the derived configuration ${one.id} was not built and is not analyzed: ${one.error}`,
|
package/src/paths.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Filesystem paths
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* They are written here rather than taken from the platform's own library because
|
|
6
|
-
* every module below the command line stays free of the platform, and because the
|
|
7
|
-
* supported platform set is one, so a second spelling would be a rule nothing
|
|
8
|
-
* exercises.
|
|
2
|
+
* Filesystem paths as the supported platform spells them: a solidus between segments, a
|
|
3
|
+
* leading one for an absolute path. They are written here rather than taken from the
|
|
4
|
+
* platform's library so every module below the command line stays free of the platform.
|
|
9
5
|
*/
|
|
10
6
|
|
|
11
7
|
const SEPARATOR = "/";
|
|
@@ -15,6 +11,58 @@ export function isAbsolutePath(path: string): boolean {
|
|
|
15
11
|
return path.startsWith(SEPARATOR);
|
|
16
12
|
}
|
|
17
13
|
|
|
14
|
+
/** Whether a specifier starts a path relative to the file that writes it: `./` or `../`. */
|
|
15
|
+
export function startsRelativePath(specifier: string): boolean {
|
|
16
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Whether a specifier names a path relative to the file that writes it: `.`, `..`, `./` or `../` first. */
|
|
20
|
+
export function isRelativeSpecifier(specifier: string): boolean {
|
|
21
|
+
return specifier === "." || specifier === ".." || startsRelativePath(specifier);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether a module specifier names a path rather than a package, as the compiler reads one:
|
|
26
|
+
* a relative specifier or an absolute path.
|
|
27
|
+
*/
|
|
28
|
+
export function isPathSpecifier(specifier: string): boolean {
|
|
29
|
+
return isRelativeSpecifier(specifier) || isAbsolutePath(specifier);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether a string starts with `./`, `../` or `/`, the specifiers a browser reads as a URL
|
|
34
|
+
* rather than a bare name, so a bare `.` or `..` stays a request:
|
|
35
|
+
* https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-url-like-module-specifier
|
|
36
|
+
*/
|
|
37
|
+
export function isExplicitPath(text: string): boolean {
|
|
38
|
+
return startsRelativePath(text) || isAbsolutePath(text);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Whether a string starts with `.` or `/`, which no npm package name does, so a reader that
|
|
43
|
+
* takes any other string as a package's takes it as a path:
|
|
44
|
+
* https://docs.npmjs.com/cli/v11/configuring-npm/package-json#name
|
|
45
|
+
*/
|
|
46
|
+
export function cannotStartPackageName(text: string): boolean {
|
|
47
|
+
return text.startsWith(".") || isAbsolutePath(text);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** A URL scheme, `file:` and `node:` among them: https://url.spec.whatwg.org/#url-scheme-string */
|
|
51
|
+
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
|
|
52
|
+
|
|
53
|
+
/** Whether a string starts with a URL scheme. */
|
|
54
|
+
export function hasURLScheme(text: string): boolean {
|
|
55
|
+
return URL_SCHEME.test(text);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether a URL reference is a relative one that does not start with a solidus, the bare
|
|
60
|
+
* name included: https://url.spec.whatwg.org/#relative-url-string
|
|
61
|
+
*/
|
|
62
|
+
export function isPathRelativeURL(reference: string): boolean {
|
|
63
|
+
return reference !== "" && !reference.startsWith(SEPARATOR) && !hasURLScheme(reference);
|
|
64
|
+
}
|
|
65
|
+
|
|
18
66
|
/**
|
|
19
67
|
* One path with `.` removed, `..` applied to the segment before it, and repeated
|
|
20
68
|
* separators collapsed. A `..` that walks past the root of an absolute path is
|
|
@@ -67,6 +115,25 @@ export function dirnamePath(path: string): string {
|
|
|
67
115
|
return cut === 0 ? SEPARATOR : normalized.slice(0, cut);
|
|
68
116
|
}
|
|
69
117
|
|
|
118
|
+
/**
|
|
119
|
+
* `dir` and every directory above it, nearest first, normalized. With `top`, the list ends
|
|
120
|
+
* at `top` and is empty where `dir` is neither `top` nor below it; without, it ends at the
|
|
121
|
+
* root of the path.
|
|
122
|
+
*/
|
|
123
|
+
export function ancestors(dir: string, top?: string): readonly string[] {
|
|
124
|
+
const end = top === undefined ? undefined : normalizePath(top);
|
|
125
|
+
const found: string[] = [];
|
|
126
|
+
for (let at = normalizePath(dir); ; at = dirnamePath(at)) {
|
|
127
|
+
if (end !== undefined && at !== end && relativePath(end, at) === undefined) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
found.push(at);
|
|
131
|
+
if (at === end || dirnamePath(at) === at) {
|
|
132
|
+
return found;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
70
137
|
/**
|
|
71
138
|
* The extension of one path's last segment, from its last full stop, or the empty string
|
|
72
139
|
* where it has none. A segment that only starts with a full stop has none.
|
|
@@ -82,6 +149,18 @@ export function resolvePath(against: string, path: string): string {
|
|
|
82
149
|
return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
|
|
83
150
|
}
|
|
84
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The path a relative URL reference names against `dir`, its query and its fragment
|
|
154
|
+
* removed, which name no part of the file; undefined where the reference is not relative.
|
|
155
|
+
*/
|
|
156
|
+
export function relativeURLPath(dir: string, reference: string): string | undefined {
|
|
157
|
+
if (!isPathRelativeURL(reference)) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const cut = reference.search(/[?#]/u);
|
|
161
|
+
return resolvePath(dir, cut < 0 ? reference : reference.slice(0, cut));
|
|
162
|
+
}
|
|
163
|
+
|
|
85
164
|
/**
|
|
86
165
|
* The relative path before the query of a relative module specifier such as
|
|
87
166
|
* `./worker.ts?worker`, the module a bundler loads; undefined for a specifier with no
|
|
@@ -90,7 +169,7 @@ export function resolvePath(against: string, path: string): string {
|
|
|
90
169
|
*/
|
|
91
170
|
export function queriedModulePath(specifier: string): string | undefined {
|
|
92
171
|
const query = specifier.indexOf("?");
|
|
93
|
-
if (query < 0 || !(specifier
|
|
172
|
+
if (query < 0 || !startsRelativePath(specifier)) {
|
|
94
173
|
return undefined;
|
|
95
174
|
}
|
|
96
175
|
const raw = specifier
|
|
@@ -101,14 +180,11 @@ export function queriedModulePath(specifier: string): string | undefined {
|
|
|
101
180
|
}
|
|
102
181
|
|
|
103
182
|
/**
|
|
104
|
-
* One path as it reads below a root, or undefined when it is not below the root
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* The caller resolves both against the same directory before calling: a relative
|
|
111
|
-
* root and an absolute path are two spellings this cannot reconcile.
|
|
183
|
+
* One path as it reads below a root, or undefined when it is not strictly below the root:
|
|
184
|
+
* the root itself is not below itself. Both are normalized, then compared lexically, so a
|
|
185
|
+
* root and a path spelled through different symbolic links name nothing in common. The
|
|
186
|
+
* caller resolves both against the same directory first: a relative root and an absolute
|
|
187
|
+
* path cannot be reconciled.
|
|
112
188
|
*/
|
|
113
189
|
export function relativePath(root: string, path: string): string | undefined {
|
|
114
190
|
const at = normalizePath(root);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which calls are the platform's `require` or AMD's `define` and which constructions its `URL`
|
|
3
|
+
* (roots.23, references-ts.37, references-ts.1), decided once over the bindings of the names a callee is written with: the
|
|
4
|
+
* checker's in a program, the file's own scopes in a module read outside every program.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
isBindingElement,
|
|
9
|
+
isCallExpression,
|
|
10
|
+
isExportSpecifier,
|
|
11
|
+
isIdentifier,
|
|
12
|
+
isImportSpecifier,
|
|
13
|
+
isPropertyAccessExpression,
|
|
14
|
+
isStringLiteral,
|
|
15
|
+
isVariableDeclaration,
|
|
16
|
+
type Identifier,
|
|
17
|
+
type Node,
|
|
18
|
+
} from "@typescript/native/ast";
|
|
19
|
+
import { parenthesesRemoved, unwrapped } from "./unwrap.ts";
|
|
20
|
+
|
|
21
|
+
/** A name of the platform the analysis reads calls of. */
|
|
22
|
+
type PlatformName = "require" | "createRequire" | "define" | "URL";
|
|
23
|
+
|
|
24
|
+
/** What one written name is bound to, as far as its reader can tell. */
|
|
25
|
+
export interface Bound {
|
|
26
|
+
/** Its own name, and each one an import, a re-export or a destructuring carries it by. */
|
|
27
|
+
readonly names: ReadonlySet<string>;
|
|
28
|
+
/** The declarations of the target it resolves to, a variable's initializer read through. */
|
|
29
|
+
readonly own: readonly Node[];
|
|
30
|
+
/** Whether the type checker proves it the target's own, a function or a class it writes with a body. */
|
|
31
|
+
readonly provenOwn: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The binding of each written name, `undefined` where nothing answers which declaration it names. */
|
|
35
|
+
export type BindingOf = (written: Identifier) => Bound | undefined;
|
|
36
|
+
|
|
37
|
+
/** The name one declaration carries a binding by beside its own: the export an import or a re-export names, or the property a destructuring reads. */
|
|
38
|
+
export function carriedNames(node: Node): readonly string[] {
|
|
39
|
+
if (isImportSpecifier(node) || isExportSpecifier(node)) {
|
|
40
|
+
return [(node.propertyName ?? node.name).text];
|
|
41
|
+
}
|
|
42
|
+
const property = isBindingElement(node) ? node.propertyName : undefined;
|
|
43
|
+
return property !== undefined && (isIdentifier(property) || isStringLiteral(property))
|
|
44
|
+
? [property.text]
|
|
45
|
+
: [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A written name's binding as a file states it, with no checker to ask, so nothing is
|
|
50
|
+
* proven the target's own (conventions.3): `declarationOf` names the declaration a name
|
|
51
|
+
* resolves to, and a property is read by how it is written alone.
|
|
52
|
+
*/
|
|
53
|
+
export function lexicalBinding(
|
|
54
|
+
declarationOf: (written: Identifier) => Node | undefined,
|
|
55
|
+
): BindingOf {
|
|
56
|
+
return (written) => {
|
|
57
|
+
const parent = written.parent;
|
|
58
|
+
if (isPropertyAccessExpression(parent) && parent.name === written) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const declaration = declarationOf(written);
|
|
62
|
+
return {
|
|
63
|
+
names: new Set([
|
|
64
|
+
written.text,
|
|
65
|
+
...(declaration === undefined ? [] : carriedNames(declaration)),
|
|
66
|
+
]),
|
|
67
|
+
own: declaration === undefined ? [] : [declaration],
|
|
68
|
+
provenOwn: false,
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The name a callee or a constructor is written with, parentheses removed: an identifier or a property name. */
|
|
74
|
+
export function writtenName(expression: Node): Identifier | undefined {
|
|
75
|
+
const at = parenthesesRemoved(expression);
|
|
76
|
+
if (isIdentifier(at)) {
|
|
77
|
+
return at;
|
|
78
|
+
}
|
|
79
|
+
return isPropertyAccessExpression(at) && isIdentifier(at.name) ? at.name : undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Whether a call or construction through `callee` is one of the platform's `name`: never
|
|
84
|
+
* where its binding is proven the target's own; otherwise where it is written or carried
|
|
85
|
+
* by that name, or resolves to a variable of the target whose initializer is such a
|
|
86
|
+
* callee or, for `require`, a call of a `createRequire`. A callee nothing binds is read by
|
|
87
|
+
* how it is written.
|
|
88
|
+
*/
|
|
89
|
+
export function callsPlatformBy(
|
|
90
|
+
name: PlatformName,
|
|
91
|
+
callee: Identifier,
|
|
92
|
+
bindingOf: BindingOf,
|
|
93
|
+
seen = new Set<Node>(),
|
|
94
|
+
): boolean {
|
|
95
|
+
const bound = bindingOf(callee);
|
|
96
|
+
if (bound?.provenOwn === true) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (callee.text === name || bound?.names.has(name) === true) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return (bound?.own ?? []).some((node) => {
|
|
103
|
+
if (seen.has(node) || !isVariableDeclaration(node) || node.initializer === undefined) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
seen.add(node);
|
|
107
|
+
const value = unwrapped(node.initializer);
|
|
108
|
+
if (isCallExpression(value)) {
|
|
109
|
+
const called = writtenName(value.expression);
|
|
110
|
+
return (
|
|
111
|
+
name === "require" &&
|
|
112
|
+
called !== undefined &&
|
|
113
|
+
callsPlatformBy("createRequire", called, bindingOf, seen)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
const written = writtenName(value);
|
|
117
|
+
return written !== undefined && callsPlatformBy(name, written, bindingOf, seen);
|
|
118
|
+
});
|
|
119
|
+
}
|