@cplieger/deadset-ts 8.0.0-dev.20 → 8.0.0-dev.22
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/dist/src/alias-chain.js +33 -2
- package/dist/src/analysis.js +18 -77
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contract/contract.generated.js +5 -4
- package/dist/src/dependencies.js +1 -1
- package/dist/src/exemptions/argument-places.js +131 -0
- package/dist/src/{calls.js → exemptions/calls.js} +126 -179
- package/dist/src/{exempt-classes.js → exemptions/classes.js} +1 -1
- 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} +7 -7
- package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +4 -6
- package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +31 -36
- package/dist/src/{generated-file.js → exemptions/generated-file.js} +4 -4
- package/dist/src/{injection-container.js → exemptions/injection-container.js} +19 -25
- 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/{serialization-contract.js → exemptions/serialization-contract.js} +415 -329
- package/dist/src/exemptions/sites.js +182 -0
- package/dist/src/{template-field.js → exemptions/template-field.js} +7 -7
- package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
- package/dist/src/findings/intra-function.js +50 -37
- package/dist/src/index/sites.js +1 -1
- package/dist/src/index/values.js +124 -0
- package/dist/src/intra-function-parts.js +1149 -222
- package/dist/src/liveness/sweep.js +2 -2
- package/dist/src/query.js +48 -0
- package/dist/src/roots/configured-declarations.js +1 -1
- package/dist/src/verbs/print-retained.js +23 -1
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +38 -2
- package/src/analysis.ts +19 -100
- package/src/bindings.ts +1 -1
- package/src/conformance-record.ts +2 -2
- package/src/contract/contract.generated.ts +5 -4
- package/src/dependencies.ts +1 -1
- package/src/exemptions/argument-places.ts +198 -0
- package/src/{calls.ts → exemptions/calls.ts} +190 -224
- package/src/{exempt-classes.ts → exemptions/classes.ts} +1 -1
- package/src/exemptions/decorator.ts +56 -0
- package/src/exemptions/detectors.ts +75 -0
- package/src/{enum-group.ts → exemptions/enum-group.ts} +9 -9
- package/src/{erased-stores.ts → exemptions/erased-stores.ts} +5 -8
- package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +39 -48
- package/src/{generated-file.ts → exemptions/generated-file.ts} +6 -6
- package/src/{injection-container.ts → exemptions/injection-container.ts} +28 -33
- 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/{serialization-contract.ts → exemptions/serialization-contract.ts} +563 -429
- package/src/exemptions/sites.ts +293 -0
- package/src/{template-field.ts → exemptions/template-field.ts} +11 -11
- package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
- package/src/facts.ts +1 -1
- package/src/findings/intra-function.ts +59 -37
- package/src/index/sites.ts +1 -1
- package/src/index/values.ts +170 -0
- package/src/intra-function-parts.ts +1435 -247
- package/src/liveness/sweep.ts +2 -2
- package/src/query.ts +109 -10
- package/src/roots/configured-declarations.ts +1 -2
- package/src/stores.ts +1 -1
- package/src/verbs/print-retained.ts +29 -1
- package/dist/src/exempt.js +0 -164
- package/dist/src/value-flow.js +0 -74
- package/src/decorator.ts +0 -78
- package/src/exempt.ts +0 -295
- package/src/value-flow.ts +0 -104
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exemption classes this analyzer detects, each by its detector, and what their
|
|
3
|
+
* detection reads of the run once for every project. A class without a detector here
|
|
4
|
+
* retains nothing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
8
|
+
import type { Host } from "../host.ts";
|
|
9
|
+
import type { Inventory } from "../inventory.ts";
|
|
10
|
+
import type { ProjectView } from "../session.ts";
|
|
11
|
+
import type { TSExemptionClass } from "./classes.ts";
|
|
12
|
+
import { decorator } from "./decorator.ts";
|
|
13
|
+
import { enumGroup } from "./enum-group.ts";
|
|
14
|
+
import { frameworkLifecycle } from "./framework-lifecycle.ts";
|
|
15
|
+
import { generatedFile, type GeneratedFact } from "./generated-file.ts";
|
|
16
|
+
import { injectionContainer } from "./injection-container.ts";
|
|
17
|
+
import { interfaceSatisfaction } from "./interface-satisfaction.ts";
|
|
18
|
+
import { detect, type Detector, type Detectors, type Exemption } from "./lifecycle.ts";
|
|
19
|
+
import { reflectiveLookup } from "./reflective-lookup.ts";
|
|
20
|
+
import { serializationContract } from "./serialization-contract.ts";
|
|
21
|
+
import { readTemplates, templateField } from "./template-field.ts";
|
|
22
|
+
|
|
23
|
+
export const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
|
|
24
|
+
["interface-satisfaction", interfaceSatisfaction],
|
|
25
|
+
["enum-group", enumGroup],
|
|
26
|
+
["generated-file", generatedFile],
|
|
27
|
+
["template-field", templateField],
|
|
28
|
+
["reflective-lookup", reflectiveLookup],
|
|
29
|
+
["decorator", decorator],
|
|
30
|
+
["injection-container", injectionContainer],
|
|
31
|
+
["framework-lifecycle", frameworkLifecycle],
|
|
32
|
+
["serialization-contract", serializationContract],
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** One project, while its view is open, as detection reads it. */
|
|
36
|
+
interface DetectedProject<Brand> {
|
|
37
|
+
readonly project: ProjectView<Brand>;
|
|
38
|
+
readonly held: Inventory;
|
|
39
|
+
/** The project's generated files, by path below the target root, each with what decided it. */
|
|
40
|
+
readonly generated: ReadonlyMap<string, GeneratedFact>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The detection of one run: what every project's detectors read of the run, read once,
|
|
45
|
+
* and a function that runs them over one project. A switched-off class reads nothing,
|
|
46
|
+
* its templates included, and generated files are held only where the configuration
|
|
47
|
+
* leaves them out of the analysis.
|
|
48
|
+
*/
|
|
49
|
+
export function detection(
|
|
50
|
+
host: Host,
|
|
51
|
+
targetRoot: string,
|
|
52
|
+
config: ResolvedConfig,
|
|
53
|
+
consumers: readonly string[],
|
|
54
|
+
detectors: Detectors,
|
|
55
|
+
): <Brand>(one: DetectedProject<Brand>) => readonly Exemption[] {
|
|
56
|
+
const disabled = new Set(config.exemptions.disabled);
|
|
57
|
+
const templates = disabled.has("template-field")
|
|
58
|
+
? { delimiters: config.analysis.template_delimiters, files: [] }
|
|
59
|
+
: readTemplates(host, targetRoot, config.analysis);
|
|
60
|
+
const included = config.analysis.generated_files === "include";
|
|
61
|
+
return ({ project, held, generated }) =>
|
|
62
|
+
detect(
|
|
63
|
+
{
|
|
64
|
+
project,
|
|
65
|
+
held,
|
|
66
|
+
targetRoot,
|
|
67
|
+
templates,
|
|
68
|
+
ts: config.ts,
|
|
69
|
+
consumers,
|
|
70
|
+
...(included ? {} : { generated }),
|
|
71
|
+
},
|
|
72
|
+
detectors,
|
|
73
|
+
disabled,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -27,19 +27,19 @@ import {
|
|
|
27
27
|
type Node,
|
|
28
28
|
type SourceFile,
|
|
29
29
|
} from "@typescript/native/ast";
|
|
30
|
-
import { parenthesesRemoved } from "
|
|
30
|
+
import { parenthesesRemoved } from "../unwrap.ts";
|
|
31
31
|
import { TypeFlags, type Type } from "@typescript/native/sync";
|
|
32
|
-
import { aliasChains } from "
|
|
33
|
-
import type { DetectorInput, Evidence } from "./
|
|
34
|
-
import { nodeKey } from "
|
|
35
|
-
import { renderPosition } from "
|
|
36
|
-
import { isAnswered, UNANSWERED, type Answer } from "
|
|
37
|
-
import type { ProjectView } from "
|
|
32
|
+
import { aliasChains } from "../alias-chain.ts";
|
|
33
|
+
import type { DetectorInput, Evidence } from "./lifecycle.ts";
|
|
34
|
+
import { nodeKey } from "../inventory.ts";
|
|
35
|
+
import { renderPosition } from "../position.ts";
|
|
36
|
+
import { isAnswered, UNANSWERED, type Answer } from "../query.ts";
|
|
37
|
+
import type { ProjectView } from "../session.ts";
|
|
38
|
+
import { ERASED } from "./value-reach.ts";
|
|
38
39
|
|
|
39
40
|
/** The operand types a conversion produces an enum value from. */
|
|
40
41
|
const NUMBER = TypeFlags.NumberLike;
|
|
41
42
|
const STRING = TypeFlags.StringLike;
|
|
42
|
-
const DECODED = TypeFlags.Any | TypeFlags.Unknown;
|
|
43
43
|
|
|
44
44
|
/** How one site produces a value of the type it names. */
|
|
45
45
|
type Form = "assertion" | "lookup" | "annotation";
|
|
@@ -176,7 +176,7 @@ function detailOf<Brand>(
|
|
|
176
176
|
if (parts.some((part) => (part.flags & STRING) !== 0)) {
|
|
177
177
|
return site.form === "assertion" ? "converted from string" : undefined;
|
|
178
178
|
}
|
|
179
|
-
if (parts.some((part) => (part.flags &
|
|
179
|
+
if (parts.some((part) => (part.flags & ERASED) !== 0)) {
|
|
180
180
|
return `decoded as ${enumName}`;
|
|
181
181
|
}
|
|
182
182
|
return undefined;
|
|
@@ -26,14 +26,11 @@ import {
|
|
|
26
26
|
type Expression,
|
|
27
27
|
type Node,
|
|
28
28
|
} from "@typescript/native/ast";
|
|
29
|
-
import { SymbolFlags,
|
|
30
|
-
import { must } from "
|
|
31
|
-
import type { ProjectView } from "
|
|
32
|
-
import { checkedTypes } from "./value-reach.ts";
|
|
33
|
-
import { parenthesesRemoved } from "
|
|
34
|
-
|
|
35
|
-
/** The types that keep nothing of the value they are given. */
|
|
36
|
-
const ERASED = TypeFlags.Any | TypeFlags.Unknown;
|
|
29
|
+
import { SymbolFlags, type Type } from "@typescript/native/sync";
|
|
30
|
+
import { must } from "../query.ts";
|
|
31
|
+
import type { ProjectView } from "../session.ts";
|
|
32
|
+
import { checkedTypes, ERASED } from "./value-reach.ts";
|
|
33
|
+
import { parenthesesRemoved } from "../unwrap.ts";
|
|
37
34
|
|
|
38
35
|
/** The array methods whose arguments become elements of the array they are called on. */
|
|
39
36
|
const ELEMENT_STORES: ReadonlySet<string> = new Set(["push", "unshift"]);
|
|
@@ -8,18 +8,17 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
isClassDeclaration,
|
|
12
|
-
isClassExpression,
|
|
13
11
|
isHeritageClause,
|
|
14
12
|
SyntaxKind,
|
|
15
13
|
type CallExpression,
|
|
16
14
|
type Node,
|
|
17
15
|
} from "@typescript/native/ast";
|
|
18
16
|
import type { Type } from "@typescript/native/sync";
|
|
19
|
-
import { aliasChains, type AliasChains } from "
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import
|
|
17
|
+
import { aliasChains, type AliasChains } from "../alias-chain.ts";
|
|
18
|
+
import { nameOf } from "../index/values.ts";
|
|
19
|
+
import { classesPassed, decoratorCallee, decoratorsOf } from "./calls.ts";
|
|
20
|
+
import { classMembers, memberComponent, typesAt, type ClassSide } from "../class-members.ts";
|
|
21
|
+
import type { DeclarationEntry, LifecycleContract } from "../config.ts";
|
|
23
22
|
import {
|
|
24
23
|
entriesAt,
|
|
25
24
|
names,
|
|
@@ -27,12 +26,13 @@ import {
|
|
|
27
26
|
resolveEntries,
|
|
28
27
|
spelledEntry,
|
|
29
28
|
type ResolvedEntry,
|
|
30
|
-
} from "
|
|
31
|
-
import type { DetectorInput, Evidence } from "./
|
|
32
|
-
import {
|
|
33
|
-
import { renderPosition } from "
|
|
34
|
-
import { must } from "
|
|
35
|
-
import type { ProjectView } from "
|
|
29
|
+
} from "../roots/configured-declarations.ts";
|
|
30
|
+
import type { DetectorInput, Evidence } from "./lifecycle.ts";
|
|
31
|
+
import type { Inventory } from "../inventory.ts";
|
|
32
|
+
import { renderPosition } from "../position.ts";
|
|
33
|
+
import { must } from "../query.ts";
|
|
34
|
+
import type { ProjectView } from "../session.ts";
|
|
35
|
+
import type { ProjectSites } from "./sites.ts";
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* The platform's own contract (references-ts.38): `CustomElementRegistry.define` reads
|
|
@@ -101,6 +101,7 @@ function resolveContracts<Brand>(
|
|
|
101
101
|
function byComponents<Brand>(
|
|
102
102
|
project: ProjectView<Brand>,
|
|
103
103
|
held: Inventory,
|
|
104
|
+
sites: ProjectSites,
|
|
104
105
|
chains: AliasChains,
|
|
105
106
|
contracts: readonly ResolvedContract[],
|
|
106
107
|
): readonly Component[] {
|
|
@@ -111,13 +112,18 @@ function byComponents<Brand>(
|
|
|
111
112
|
const contractOf = new Map(
|
|
112
113
|
routed.flatMap((one) => one.components.map((entry) => [entry, one.contract] as const)),
|
|
113
114
|
);
|
|
114
|
-
const calls =
|
|
115
|
-
const
|
|
115
|
+
const calls = sites.calls.map((one) => one.call);
|
|
116
|
+
const invoked = (call: CallExpression): Node | undefined =>
|
|
117
|
+
nameOf(sites.invocations.of(call).callee);
|
|
118
|
+
const decorated = sites.classes.flatMap(({ id, node }) => {
|
|
119
|
+
const decorators = decoratorsOf(node);
|
|
120
|
+
return decorators.length === 0 ? [] : [{ id, decorators }];
|
|
121
|
+
});
|
|
116
122
|
const named = entriesAt(
|
|
117
123
|
project,
|
|
118
124
|
chains,
|
|
119
125
|
[
|
|
120
|
-
...calls.flatMap((call) =>
|
|
126
|
+
...calls.flatMap((call) => invoked(call) ?? []),
|
|
121
127
|
...decorated.flatMap((one) =>
|
|
122
128
|
one.decorators.flatMap((decorator) => decoratorCallee(decorator) ?? []),
|
|
123
129
|
),
|
|
@@ -140,12 +146,12 @@ function byComponents<Brand>(
|
|
|
140
146
|
}
|
|
141
147
|
const matched = new Map<CallExpression, { contract: LifecycleContract; detail: string }[]>();
|
|
142
148
|
for (const call of calls) {
|
|
143
|
-
const routes = naming(
|
|
149
|
+
const routes = naming(invoked(call));
|
|
144
150
|
if (routes.length > 0) {
|
|
145
151
|
matched.set(call, routes);
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
|
-
for (const [call, uses] of classesPassed(project, held, chains, [...matched.keys()])) {
|
|
154
|
+
for (const [call, uses] of classesPassed(project, held, chains, sites, [...matched.keys()])) {
|
|
149
155
|
for (const route of matched.get(call) ?? []) {
|
|
150
156
|
for (const use of uses) {
|
|
151
157
|
found.push({
|
|
@@ -160,43 +166,28 @@ function byComponents<Brand>(
|
|
|
160
166
|
return found;
|
|
161
167
|
}
|
|
162
168
|
|
|
163
|
-
/** Each class
|
|
164
|
-
|
|
169
|
+
/** Each named class of the target that extends another, with its name and the class its `extends` names. */
|
|
170
|
+
function extendingClasses(sites: ProjectSites): readonly {
|
|
165
171
|
readonly id: string;
|
|
166
172
|
readonly name: Node;
|
|
167
173
|
readonly extended: Node;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if ((isClassDeclaration(node) || isClassExpression(node)) && node.name !== undefined) {
|
|
179
|
-
const id = held.declarations.get(nodeKey(file, node));
|
|
180
|
-
const clause = (node.heritageClauses ?? []).find(
|
|
181
|
-
(one) => isHeritageClause(one) && one.token === SyntaxKind.ExtendsKeyword,
|
|
182
|
-
);
|
|
183
|
-
const extended =
|
|
184
|
-
clause !== undefined && isHeritageClause(clause) ? clause.types[0] : undefined;
|
|
185
|
-
if (id !== undefined && extended !== undefined) {
|
|
186
|
-
found.push({ id, name: node.name, extended });
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
node.forEachChild(visit);
|
|
190
|
-
};
|
|
191
|
-
file.forEachChild(visit);
|
|
192
|
-
}
|
|
193
|
-
return found;
|
|
174
|
+
}[] {
|
|
175
|
+
return sites.classes.flatMap(({ id, node }) => {
|
|
176
|
+
const clause = (node.heritageClauses ?? []).find(
|
|
177
|
+
(one) => isHeritageClause(one) && one.token === SyntaxKind.ExtendsKeyword,
|
|
178
|
+
);
|
|
179
|
+
const extended = clause !== undefined && isHeritageClause(clause) ? clause.types[0] : undefined;
|
|
180
|
+
return node.name === undefined || extended === undefined
|
|
181
|
+
? []
|
|
182
|
+
: [{ id, name: node.name, extended }];
|
|
183
|
+
});
|
|
194
184
|
}
|
|
195
185
|
|
|
196
186
|
/** The classes that extend, directly or through a chain, a class the contracts' bases name. */
|
|
197
187
|
function byBases<Brand>(
|
|
198
188
|
project: ProjectView<Brand>,
|
|
199
189
|
held: Inventory,
|
|
190
|
+
sites: ProjectSites,
|
|
200
191
|
chains: AliasChains,
|
|
201
192
|
contracts: readonly ResolvedContract[],
|
|
202
193
|
): readonly Component[] {
|
|
@@ -204,7 +195,7 @@ function byBases<Brand>(
|
|
|
204
195
|
if (based.length === 0) {
|
|
205
196
|
return [];
|
|
206
197
|
}
|
|
207
|
-
const classes = extendingClasses(
|
|
198
|
+
const classes = extendingClasses(sites);
|
|
208
199
|
if (classes.length === 0) {
|
|
209
200
|
return [];
|
|
210
201
|
}
|
|
@@ -275,8 +266,8 @@ export function frameworkLifecycle<Brand>(input: DetectorInput<Brand>): readonly
|
|
|
275
266
|
}
|
|
276
267
|
const chains = aliasChains(project, held);
|
|
277
268
|
const components = [
|
|
278
|
-
...byComponents(project, held, chains, contracts),
|
|
279
|
-
...byBases(project, held, chains, contracts),
|
|
269
|
+
...byComponents(project, held, input.sites, chains, contracts),
|
|
270
|
+
...byBases(project, held, input.sites, chains, contracts),
|
|
280
271
|
];
|
|
281
272
|
if (components.length === 0) {
|
|
282
273
|
return [];
|
|
@@ -15,12 +15,12 @@ import {
|
|
|
15
15
|
type Node,
|
|
16
16
|
type SourceFile,
|
|
17
17
|
} from "@typescript/native/ast";
|
|
18
|
-
import { GENERATORS } from "
|
|
19
|
-
import type { DetectorInput, Evidence } from "./
|
|
20
|
-
import { minifiedBy } from "
|
|
21
|
-
import type { SourceFiles } from "
|
|
22
|
-
import { bytewise } from "
|
|
23
|
-
import { satisfies } from "
|
|
18
|
+
import { GENERATORS } from "../contract/contract.generated.ts";
|
|
19
|
+
import type { DetectorInput, Evidence } from "./lifecycle.ts";
|
|
20
|
+
import { minifiedBy } from "../javascript-file.ts";
|
|
21
|
+
import type { SourceFiles } from "../source-files.ts";
|
|
22
|
+
import { bytewise } from "../text/compare.ts";
|
|
23
|
+
import { satisfies } from "../version-range.ts";
|
|
24
24
|
|
|
25
25
|
/** The generated-code header, matched against each line of a leading comment. */
|
|
26
26
|
const HEADER_LINE = /^\/\/ Code generated .* DO NOT EDIT\.$/u;
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
-
isClassDeclaration,
|
|
12
|
-
isClassExpression,
|
|
13
11
|
isConstructorDeclaration,
|
|
14
12
|
isGetAccessorDeclaration,
|
|
15
13
|
isPropertyDeclaration,
|
|
@@ -18,19 +16,20 @@ import {
|
|
|
18
16
|
type Decorator,
|
|
19
17
|
type Node,
|
|
20
18
|
} from "@typescript/native/ast";
|
|
21
|
-
import { aliasChains } from "
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
19
|
+
import { aliasChains } from "../alias-chain.ts";
|
|
20
|
+
import { nameOf } from "../index/values.ts";
|
|
21
|
+
import { classesPassed, decoratorsOf, spelledDecorator } from "./calls.ts";
|
|
22
|
+
import { classMembers, type ClassMember } from "../class-members.ts";
|
|
24
23
|
import {
|
|
25
24
|
entriesAt,
|
|
26
25
|
namesADeclaration,
|
|
27
26
|
resolveEntries,
|
|
28
27
|
spelledEntry,
|
|
29
28
|
type ResolvedEntry,
|
|
30
|
-
} from "
|
|
31
|
-
import type { DetectorInput, Evidence } from "./
|
|
32
|
-
import { isParameterProperty
|
|
33
|
-
import { renderPosition } from "
|
|
29
|
+
} from "../roots/configured-declarations.ts";
|
|
30
|
+
import type { DetectorInput, Evidence } from "./lifecycle.ts";
|
|
31
|
+
import { isParameterProperty } from "../inventory.ts";
|
|
32
|
+
import { renderPosition } from "../position.ts";
|
|
34
33
|
|
|
35
34
|
/** One class a container constructs, by the registration that says so. */
|
|
36
35
|
interface Registration {
|
|
@@ -52,32 +51,36 @@ function isInjected(node: Node): boolean {
|
|
|
52
51
|
|
|
53
52
|
/** The classes of the target a call whose callee an entry names passes, with that call's entry. */
|
|
54
53
|
function registrations<Brand>(input: DetectorInput<Brand>): readonly Registration[] {
|
|
55
|
-
const { project, held } = input;
|
|
54
|
+
const { project, held, sites } = input;
|
|
56
55
|
const resolved = resolveEntries(project, held, input.ts.injection_registrations);
|
|
57
56
|
if (!resolved.some(namesADeclaration)) {
|
|
58
57
|
return [];
|
|
59
58
|
}
|
|
60
59
|
const chains = aliasChains(project, held);
|
|
61
|
-
const calls =
|
|
60
|
+
const calls = sites.calls.map((one) => one.call);
|
|
61
|
+
const invoked = (call: CallExpression): Node | undefined =>
|
|
62
|
+
nameOf(sites.invocations.of(call).callee);
|
|
62
63
|
const named = entriesAt(
|
|
63
64
|
project,
|
|
64
65
|
chains,
|
|
65
|
-
calls.flatMap((call) =>
|
|
66
|
+
calls.flatMap((call) => invoked(call) ?? []),
|
|
66
67
|
resolved,
|
|
67
68
|
);
|
|
68
69
|
const matched = new Map<CallExpression, ResolvedEntry>();
|
|
69
70
|
for (const call of calls) {
|
|
70
|
-
const name =
|
|
71
|
+
const name = invoked(call);
|
|
71
72
|
const [entry] = name === undefined ? [] : (named.get(name) ?? []);
|
|
72
73
|
if (entry !== undefined) {
|
|
73
74
|
matched.set(call, entry);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
|
-
return [...classesPassed(project, held, chains, [...matched.keys()])].flatMap(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
return [...classesPassed(project, held, chains, sites, [...matched.keys()])].flatMap(
|
|
78
|
+
([call, uses]) => {
|
|
79
|
+
const entry = matched.get(call);
|
|
80
|
+
const detail = entry === undefined ? "" : `registered by ${spelledEntry(entry.entry, held)}`;
|
|
81
|
+
return uses.map((use) => ({ id: use.id, site: use.site, detail }));
|
|
82
|
+
},
|
|
83
|
+
);
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
/**
|
|
@@ -87,22 +90,14 @@ function registrations<Brand>(input: DetectorInput<Brand>): readonly Registratio
|
|
|
87
90
|
function constructedForParameters<Brand>(
|
|
88
91
|
input: DetectorInput<Brand>,
|
|
89
92
|
): ReadonlyMap<string, Decorator> {
|
|
90
|
-
const { project, held } = input;
|
|
91
93
|
const found = new Map<string, Decorator>();
|
|
92
|
-
for (const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (id !== undefined && asks !== undefined) {
|
|
100
|
-
found.set(id, asks);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
node.forEachChild(visit);
|
|
104
|
-
};
|
|
105
|
-
file.forEachChild(visit);
|
|
94
|
+
for (const { id, node } of input.sites.classes) {
|
|
95
|
+
const [asks] = node.members.flatMap((member) =>
|
|
96
|
+
isConstructorDeclaration(member) ? member.parameters.flatMap(decoratorsOf) : [],
|
|
97
|
+
);
|
|
98
|
+
if (asks !== undefined) {
|
|
99
|
+
found.set(id, asks);
|
|
100
|
+
}
|
|
106
101
|
}
|
|
107
102
|
return found;
|
|
108
103
|
}
|