@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.
Files changed (72) hide show
  1. package/dist/src/alias-chain.js +33 -2
  2. package/dist/src/analysis.js +18 -77
  3. package/dist/src/conformance-record.js +2 -2
  4. package/dist/src/contract/contract.generated.js +5 -4
  5. package/dist/src/dependencies.js +1 -1
  6. package/dist/src/exemptions/argument-places.js +131 -0
  7. package/dist/src/{calls.js → exemptions/calls.js} +126 -179
  8. package/dist/src/{exempt-classes.js → exemptions/classes.js} +1 -1
  9. package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
  10. package/dist/src/exemptions/detectors.js +48 -0
  11. package/dist/src/{enum-group.js → exemptions/enum-group.js} +7 -7
  12. package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +4 -6
  13. package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +31 -36
  14. package/dist/src/{generated-file.js → exemptions/generated-file.js} +4 -4
  15. package/dist/src/{injection-container.js → exemptions/injection-container.js} +19 -25
  16. package/dist/src/exemptions/installed-globals.js +173 -0
  17. package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
  18. package/dist/src/exemptions/lifecycle.js +150 -0
  19. package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
  20. package/dist/src/{serialization-contract.js → exemptions/serialization-contract.js} +415 -329
  21. package/dist/src/exemptions/sites.js +182 -0
  22. package/dist/src/{template-field.js → exemptions/template-field.js} +7 -7
  23. package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
  24. package/dist/src/findings/intra-function.js +50 -37
  25. package/dist/src/index/sites.js +1 -1
  26. package/dist/src/index/values.js +124 -0
  27. package/dist/src/intra-function-parts.js +1149 -222
  28. package/dist/src/liveness/sweep.js +2 -2
  29. package/dist/src/query.js +48 -0
  30. package/dist/src/roots/configured-declarations.js +1 -1
  31. package/dist/src/verbs/print-retained.js +23 -1
  32. package/jsr.json +1 -1
  33. package/package.json +2 -2
  34. package/src/alias-chain.ts +38 -2
  35. package/src/analysis.ts +19 -100
  36. package/src/bindings.ts +1 -1
  37. package/src/conformance-record.ts +2 -2
  38. package/src/contract/contract.generated.ts +5 -4
  39. package/src/dependencies.ts +1 -1
  40. package/src/exemptions/argument-places.ts +198 -0
  41. package/src/{calls.ts → exemptions/calls.ts} +190 -224
  42. package/src/{exempt-classes.ts → exemptions/classes.ts} +1 -1
  43. package/src/exemptions/decorator.ts +56 -0
  44. package/src/exemptions/detectors.ts +75 -0
  45. package/src/{enum-group.ts → exemptions/enum-group.ts} +9 -9
  46. package/src/{erased-stores.ts → exemptions/erased-stores.ts} +5 -8
  47. package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +39 -48
  48. package/src/{generated-file.ts → exemptions/generated-file.ts} +6 -6
  49. package/src/{injection-container.ts → exemptions/injection-container.ts} +28 -33
  50. package/src/exemptions/installed-globals.ts +255 -0
  51. package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
  52. package/src/exemptions/lifecycle.ts +286 -0
  53. package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
  54. package/src/{serialization-contract.ts → exemptions/serialization-contract.ts} +563 -429
  55. package/src/exemptions/sites.ts +293 -0
  56. package/src/{template-field.ts → exemptions/template-field.ts} +11 -11
  57. package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
  58. package/src/facts.ts +1 -1
  59. package/src/findings/intra-function.ts +59 -37
  60. package/src/index/sites.ts +1 -1
  61. package/src/index/values.ts +170 -0
  62. package/src/intra-function-parts.ts +1435 -247
  63. package/src/liveness/sweep.ts +2 -2
  64. package/src/query.ts +109 -10
  65. package/src/roots/configured-declarations.ts +1 -2
  66. package/src/stores.ts +1 -1
  67. package/src/verbs/print-retained.ts +29 -1
  68. package/dist/src/exempt.js +0 -164
  69. package/dist/src/value-flow.js +0 -74
  70. package/src/decorator.ts +0 -78
  71. package/src/exempt.ts +0 -295
  72. 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 "./unwrap.ts";
30
+ import { parenthesesRemoved } from "../unwrap.ts";
31
31
  import { TypeFlags, type Type } from "@typescript/native/sync";
32
- import { aliasChains } from "./alias-chain.ts";
33
- import type { DetectorInput, Evidence } from "./exempt.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";
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 & DECODED) !== 0)) {
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, TypeFlags, type Type } from "@typescript/native/sync";
30
- import { must } from "./query.ts";
31
- import type { ProjectView } from "./session.ts";
32
- import { checkedTypes } from "./value-reach.ts";
33
- import { parenthesesRemoved } from "./unwrap.ts";
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 "./alias-chain.ts";
20
- import { calleeName, callsOf, classesPassed, decoratedClasses, decoratorCallee } from "./calls.ts";
21
- import { classMembers, memberComponent, typesAt, type ClassSide } from "./class-members.ts";
22
- import type { DeclarationEntry, LifecycleContract } from "./config.ts";
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 "./roots/configured-declarations.ts";
31
- import type { DetectorInput, Evidence } from "./exempt.ts";
32
- import { nodeKey, type Inventory } from "./inventory.ts";
33
- import { renderPosition } from "./position.ts";
34
- import { must } from "./query.ts";
35
- import type { ProjectView } from "./session.ts";
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 = callsOf(project);
115
- const decorated = decoratedClasses(project, held);
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) => calleeName(call.expression) ?? []),
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(calleeName(call.expression));
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 declaration of the target, with its name and the class its `extends` names. */
164
- interface Declared {
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
- /** The class declarations of the target that extend another class. */
171
- function extendingClasses<Brand>(
172
- project: ProjectView<Brand>,
173
- held: Inventory,
174
- ): readonly Declared[] {
175
- const found: Declared[] = [];
176
- for (const file of project.ownSourceFiles()) {
177
- const visit = (node: Node): void => {
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(project, held);
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 "./contract/contract.generated.ts";
19
- import type { DetectorInput, Evidence } from "./exempt.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";
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 "./alias-chain.ts";
22
- import { calleeName, callsOf, classesPassed, decoratorsOf, spelledDecorator } from "./calls.ts";
23
- import { classMembers, type ClassMember } from "./class-members.ts";
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 "./roots/configured-declarations.ts";
31
- import type { DetectorInput, Evidence } from "./exempt.ts";
32
- import { isParameterProperty, nodeKey } from "./inventory.ts";
33
- import { renderPosition } from "./position.ts";
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 = callsOf(project);
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) => calleeName(call.expression) ?? []),
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 = calleeName(call.expression);
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(([call, uses]) => {
77
- const entry = matched.get(call);
78
- const detail = entry === undefined ? "" : `registered by ${spelledEntry(entry.entry, held)}`;
79
- return uses.map((use) => ({ id: use.id, site: use.site, detail }));
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 file of project.ownSourceFiles()) {
93
- const visit = (node: Node): void => {
94
- if (isClassDeclaration(node) || isClassExpression(node)) {
95
- const id = held.declarations.get(nodeKey(file, node));
96
- const [asks] = node.members.flatMap((member) =>
97
- isConstructorDeclaration(member) ? member.parameters.flatMap(decoratorsOf) : [],
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
  }