@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.18

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 (161) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +82 -91
  4. package/dist/src/augmentations.js +24 -25
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +279 -43
  7. package/dist/src/commands/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/contract.generated.js +2 -2
  14. package/dist/src/contract/report.generated.js +6 -2
  15. package/dist/src/dependencies.js +25 -29
  16. package/dist/src/destructuring.js +1 -19
  17. package/dist/src/element-access.js +179 -164
  18. package/dist/src/entries/build-scripts.js +2 -2
  19. package/dist/src/entries/configuration-files.js +3 -7
  20. package/dist/src/entries/conventions.js +2 -2
  21. package/dist/src/entries/html-entries.js +7 -10
  22. package/dist/src/entries/mdx-entries.js +2 -2
  23. package/dist/src/entries/static-module.js +2 -1
  24. package/dist/src/exempt.js +0 -26
  25. package/dist/src/exposure.js +400 -0
  26. package/dist/src/facts.js +4 -4
  27. package/dist/src/findings/completion.js +44 -21
  28. package/dist/src/findings/emitters.js +22 -33
  29. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  30. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  31. package/dist/src/findings/intra-function.js +3 -3
  32. package/dist/src/findings/reads-and-writes.js +71 -15
  33. package/dist/src/findings/unused-declarations.js +2 -22
  34. package/dist/src/findings/visibility-narrowing.js +14 -20
  35. package/dist/src/framework-lifecycle.js +41 -14
  36. package/dist/src/import-bindings.js +73 -28
  37. package/dist/src/index/sites.js +557 -0
  38. package/dist/src/indexed-access.js +523 -478
  39. package/dist/src/interface-satisfaction.js +1 -1
  40. package/dist/src/inventory.js +18 -0
  41. package/dist/src/keyof-comparisons.js +124 -103
  42. package/dist/src/{components.js → liveness/components.js} +1 -1
  43. package/dist/src/{graph.js → liveness/graph.js} +45 -20
  44. package/dist/src/liveness/matrix.js +99 -0
  45. package/dist/src/liveness/sweep.js +558 -0
  46. package/dist/src/load/expected-module.js +2 -2
  47. package/dist/src/load/specifiers.js +2 -4
  48. package/dist/src/load/synthesize.js +5 -0
  49. package/dist/src/load/workspace-resolution.js +2 -2
  50. package/dist/src/module-declarations.js +70 -85
  51. package/dist/src/overrides.js +134 -112
  52. package/dist/src/paths.js +46 -7
  53. package/dist/src/platform-calls.js +2 -2
  54. package/dist/src/platform-symbols.js +13 -19
  55. package/dist/src/query.js +46 -0
  56. package/dist/src/references/read-rule.js +1 -0
  57. package/dist/src/references/read-rules.js +48 -0
  58. package/dist/src/references/sink.js +237 -0
  59. package/dist/src/references/sources.js +450 -0
  60. package/dist/src/references.js +110 -1285
  61. package/dist/src/roots/configured-declarations.js +3 -0
  62. package/dist/src/roots/published-reach.js +4 -4
  63. package/dist/src/roots/root-set.js +1 -1
  64. package/dist/src/roots/unseen-loads.js +38 -10
  65. package/dist/src/serialization-contract.js +8 -3
  66. package/dist/src/stores.js +4 -2
  67. package/dist/src/symbol-keys.js +89 -83
  68. package/dist/src/tests/file-class.js +76 -0
  69. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  70. package/dist/src/{test-support.js → tests/support.js} +2 -11
  71. package/dist/src/type-parameter-reads.js +178 -139
  72. package/dist/src/unimplemented-callees.js +262 -242
  73. package/dist/src/verbs/analyze.js +3 -3
  74. package/dist/src/verbs/explain.js +75 -132
  75. package/dist/src/verbs/print-retained.js +3 -3
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/alias-chain.ts +0 -7
  79. package/src/amd.ts +130 -0
  80. package/src/analysis.ts +125 -128
  81. package/src/augmentations.ts +42 -28
  82. package/src/bindings.ts +460 -0
  83. package/src/calls.ts +338 -46
  84. package/src/commands/tools.ts +5 -11
  85. package/src/component-files.ts +4 -2
  86. package/src/conformance-record.ts +1 -1
  87. package/src/consumers.ts +8 -11
  88. package/src/contextual-uses.ts +2 -22
  89. package/src/contract/config.generated.ts +4 -1
  90. package/src/contract/contract.generated.ts +2 -2
  91. package/src/contract/report.generated.ts +6 -2
  92. package/src/dependencies.ts +27 -28
  93. package/src/destructuring.ts +1 -31
  94. package/src/element-access.ts +188 -182
  95. package/src/entries/build-scripts.ts +2 -2
  96. package/src/entries/configuration-files.ts +11 -8
  97. package/src/entries/conventions.ts +2 -1
  98. package/src/entries/html-entries.ts +15 -14
  99. package/src/entries/mdx-entries.ts +2 -2
  100. package/src/entries/static-module.ts +2 -1
  101. package/src/exempt.ts +31 -46
  102. package/src/exposure.ts +492 -0
  103. package/src/facts.ts +5 -3
  104. package/src/finding.ts +1 -1
  105. package/src/findings/boundary.ts +9 -0
  106. package/src/findings/completion.ts +48 -23
  107. package/src/findings/emitter.ts +1 -1
  108. package/src/findings/emitters.ts +23 -41
  109. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  110. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  111. package/src/findings/interfaces.ts +3 -3
  112. package/src/findings/intra-function.ts +3 -3
  113. package/src/findings/reads-and-writes.ts +99 -26
  114. package/src/findings/unused-declarations.ts +4 -28
  115. package/src/findings/visibility-narrowing.ts +25 -26
  116. package/src/framework-lifecycle.ts +42 -14
  117. package/src/import-bindings.ts +98 -29
  118. package/src/index/sites.ts +781 -0
  119. package/src/indexed-access.ts +570 -527
  120. package/src/interface-satisfaction.ts +2 -2
  121. package/src/inventory.ts +19 -0
  122. package/src/keyof-comparisons.ts +131 -116
  123. package/src/{components.ts → liveness/components.ts} +3 -3
  124. package/src/{graph.ts → liveness/graph.ts} +93 -27
  125. package/src/liveness/matrix.ts +162 -0
  126. package/src/liveness/sweep.ts +855 -0
  127. package/src/load/expected-module.ts +8 -2
  128. package/src/load/specifiers.ts +8 -5
  129. package/src/load/synthesize.ts +6 -0
  130. package/src/load/workspace-resolution.ts +9 -2
  131. package/src/module-declarations.ts +74 -98
  132. package/src/overrides.ts +162 -134
  133. package/src/paths.ts +54 -8
  134. package/src/platform-calls.ts +3 -3
  135. package/src/platform-symbols.ts +11 -27
  136. package/src/query.ts +81 -0
  137. package/src/references/read-rule.ts +60 -0
  138. package/src/references/read-rules.ts +55 -0
  139. package/src/references/sink.ts +314 -0
  140. package/src/references/sources.ts +632 -0
  141. package/src/references.ts +175 -1733
  142. package/src/report.ts +1 -1
  143. package/src/roots/configured-declarations.ts +3 -0
  144. package/src/roots/published-reach.ts +9 -4
  145. package/src/roots/root-set.ts +1 -1
  146. package/src/roots/unseen-loads.ts +49 -11
  147. package/src/serialization-contract.ts +8 -3
  148. package/src/stores.ts +15 -3
  149. package/src/symbol-keys.ts +102 -102
  150. package/src/tests/file-class.ts +135 -0
  151. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  152. package/src/{test-support.ts → tests/support.ts} +9 -20
  153. package/src/type-parameter-reads.ts +187 -158
  154. package/src/unimplemented-callees.ts +297 -280
  155. package/src/verbs/analyze.ts +3 -2
  156. package/src/verbs/explain.ts +83 -158
  157. package/src/verbs/print-retained.ts +3 -3
  158. package/dist/src/matrix.js +0 -273
  159. package/dist/src/sweep.js +0 -222
  160. package/src/matrix.ts +0 -463
  161. package/src/sweep.ts +0 -361
@@ -1,11 +1,12 @@
1
1
  import { CODE } from "../contract/contract.generated.ts";
2
2
  import type { Finding } from "../finding.ts";
3
- import { OUTSIDE, type Graph } from "../graph.ts";
4
- import type { InventorySymbol, SymbolKind } from "../inventory.ts";
5
- import type { MatrixCandidate } from "../matrix.ts";
3
+ import { OUTSIDE, type Graph } from "../liveness/graph.ts";
4
+ import { WORDS, type InventorySymbol } from "../inventory.ts";
5
+ import type { Candidate } from "../liveness/sweep.ts";
6
6
  import { declarationKey } from "../ref.ts";
7
7
  import type { RootKind } from "../roots/root-set.ts";
8
- import { runnerCalls } from "../sweep.ts";
8
+ import { runnerCalls } from "../liveness/sweep.ts";
9
+ import { exposedTypes, exposes } from "../exposure.ts";
9
10
  import type { Emitter, EmitterInput } from "./emitter.ts";
10
11
  import { writeOnlyDeclarations } from "./reads-and-writes.ts";
11
12
 
@@ -19,18 +20,6 @@ type Reach = "file" | "package" | "outside";
19
20
  */
20
21
  const PUBLISHING: ReadonlySet<RootKind> = new Set<RootKind>(["manifest-entry", "published-api"]);
21
22
 
22
- /** The reader's word for each kind of declaration this emitter reports. */
23
- const WORDS: Partial<Readonly<Record<SymbolKind, string>>> = {
24
- function: "function",
25
- class: "class",
26
- interface: "interface",
27
- type: "type alias",
28
- enum: "enum",
29
- namespace: "namespace",
30
- variable: "variable",
31
- "export-alias": "re-export",
32
- };
33
-
34
23
  /** Per declaration of the run, the facts both rules read. */
35
24
  interface Facts {
36
25
  readonly graph: Graph;
@@ -142,10 +131,6 @@ function topLevelExport(facts: Facts, at: number, symbol: InventorySymbol): bool
142
131
  return symbol.exported && parent !== OUTSIDE && facts.graph.symbols[parent]?.kind === "file";
143
132
  }
144
133
 
145
- function wordOf(symbol: InventorySymbol): string {
146
- return WORDS[symbol.kind] ?? symbol.kind;
147
- }
148
-
149
134
  /** The visibility each narrowing code names, which only the narrowing codes carry. */
150
135
  const NARROWER: Readonly<Record<string, "file" | "package">> = {
151
136
  [CODE.redundantExportKeyword]: "file",
@@ -181,7 +166,11 @@ function narrowing(
181
166
  facts: Facts,
182
167
  at: number,
183
168
  symbol: InventorySymbol,
184
- world: { readonly closed: boolean; readonly writeOnly: ReadonlySet<string> },
169
+ world: {
170
+ readonly closed: boolean;
171
+ readonly writeOnly: ReadonlySet<string>;
172
+ readonly exposed: ReadonlySet<string> | "every";
173
+ },
185
174
  ): Finding | undefined {
186
175
  const { closed } = world;
187
176
  const file = facts.fileOf[at] ?? OUTSIDE;
@@ -195,7 +184,7 @@ function narrowing(
195
184
  ) {
196
185
  return undefined;
197
186
  }
198
- const word = wordOf(symbol);
187
+ const word = WORDS[symbol.kind];
199
188
  switch (facts.reach[at]) {
200
189
  case "file":
201
190
  if (facts.published[file] === true && !closed) {
@@ -207,7 +196,7 @@ function narrowing(
207
196
  `exported ${word} is referenced only inside the file that declares it`,
208
197
  );
209
198
  case "package":
210
- if (!closed || !publishes(facts.rootedBy[at])) {
199
+ if (!closed || !publishes(facts.rootedBy[at]) || exposes(world.exposed, symbol)) {
211
200
  return undefined;
212
201
  }
213
202
  return findingOf(
@@ -231,7 +220,7 @@ function unreachable(
231
220
  facts: Facts,
232
221
  at: number,
233
222
  symbol: InventorySymbol,
234
- candidate: MatrixCandidate,
223
+ candidate: Candidate,
235
224
  input: EmitterInput,
236
225
  ): Finding | undefined {
237
226
  if (
@@ -252,7 +241,7 @@ function unreachable(
252
241
  return findingOf(
253
242
  symbol,
254
243
  CODE.unreachableExport,
255
- `exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`,
244
+ `exported ${WORDS[symbol.kind]} ${found}, and nothing outside can import the file that declares it`,
256
245
  );
257
246
  }
258
247
 
@@ -267,7 +256,17 @@ export function narrowings(input: EmitterInput): readonly Finding[] {
267
256
  const facts = factsOf(input.swept.matrix.union);
268
257
  const { declared, loaded } = input.boundary.consumers;
269
258
  const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
270
- const world = { closed, writeOnly: writeOnlyDeclarations(input) };
259
+ const world = {
260
+ closed,
261
+ writeOnly: writeOnlyDeclarations(input),
262
+ exposed: closed
263
+ ? exposedTypes(
264
+ facts.graph,
265
+ facts.reach.map((reach) => reach === "outside"),
266
+ input.boundary.carriers,
267
+ )
268
+ : new Set<string>(),
269
+ };
271
270
  const candidates = new Map(
272
271
  input.swept.sweep.candidates.map((candidate) => [candidate.id, candidate]),
273
272
  );
@@ -1,9 +1,10 @@
1
1
  /**
2
- * The framework-lifecycle class: a member a `ts.lifecycle_contracts` entry lists is held
3
- * back on each class that is the entry's component, being a class a decorator or a call
4
- * the entry's `components` name is attached to or passes, or one extending, directly or
5
- * through a chain, a class its `bases` name. A component's members are its type's, so a
6
- * member a base class declares answers for every subclass.
2
+ * The framework-lifecycle class: a member a lifecycle contract lists is held back on each
3
+ * class that is the contract's component, being a class a decorator or a call the
4
+ * contract's `components` name is attached to or passes, or one extending, directly or
5
+ * through a chain, a class its `bases` name. The contracts are the platform's custom
6
+ * elements and each `ts.lifecycle_contracts` entry. A component's members are its type's,
7
+ * so a member a base class declares answers for every subclass.
7
8
  */
8
9
 
9
10
  import {
@@ -33,6 +34,30 @@ import { renderPosition } from "./position.ts";
33
34
  import { must } from "./query.ts";
34
35
  import type { ProjectView } from "./session.ts";
35
36
 
37
+ /**
38
+ * The platform's own contract (references-ts.38): `CustomElementRegistry.define` reads
39
+ * these members off the class it registers, its constructor's and its prototype's
40
+ * (https://html.spec.whatwg.org/multipage/custom-elements.html#dom-customelementregistry-define),
41
+ * and calls each callback as a custom element reaction.
42
+ */
43
+ const CUSTOM_ELEMENTS: LifecycleContract = {
44
+ components: [{ global: "CustomElementRegistry.define" }],
45
+ members: [
46
+ "observedAttributes:static",
47
+ "disabledFeatures:static",
48
+ "formAssociated:static",
49
+ "connectedCallback",
50
+ "disconnectedCallback",
51
+ "connectedMoveCallback",
52
+ "adoptedCallback",
53
+ "attributeChangedCallback",
54
+ "formAssociatedCallback",
55
+ "formResetCallback",
56
+ "formDisabledCallback",
57
+ "formStateRestoreCallback",
58
+ ],
59
+ };
60
+
36
61
  /** One class that is a component of one contract, and what says it is. */
37
62
  interface Component {
38
63
  readonly contract: LifecycleContract;
@@ -238,16 +263,17 @@ function byBases<Brand>(
238
263
 
239
264
  /**
240
265
  * The framework-lifecycle detector: each member a contract lists, on each class that
241
- * is a component of that contract's framework, recorded where the class was found to
242
- * be one.
266
+ * is a component of that contract's framework, recorded once per place where the class
267
+ * was found to be one.
243
268
  */
244
269
  export function frameworkLifecycle<Brand>(input: DetectorInput<Brand>): readonly Evidence[] {
245
270
  const { project, held, targetRoot } = input;
246
- if (input.ts.lifecycle_contracts.length === 0) {
271
+ const declared = [CUSTOM_ELEMENTS, ...input.ts.lifecycle_contracts];
272
+ const contracts = resolveContracts(project, held, declared);
273
+ if (contracts.every((one) => one.components.length === 0 && one.bases.length === 0)) {
247
274
  return [];
248
275
  }
249
276
  const chains = aliasChains(project, held);
250
- const contracts = resolveContracts(project, held, input.ts.lifecycle_contracts);
251
277
  const components = [
252
278
  ...byComponents(project, held, chains, contracts),
253
279
  ...byBases(project, held, chains, contracts),
@@ -256,7 +282,7 @@ export function frameworkLifecycle<Brand>(input: DetectorInput<Brand>): readonly
256
282
  return [];
257
283
  }
258
284
  const ids = new Set(components.map((one) => one.id));
259
- const sides: readonly ClassSide[] = input.ts.lifecycle_contracts.some((contract) =>
285
+ const sides: readonly ClassSide[] = declared.some((contract) =>
260
286
  contract.members.some((name) => name.endsWith(":static")),
261
287
  )
262
288
  ? ["instance", "static"]
@@ -264,7 +290,7 @@ export function frameworkLifecycle<Brand>(input: DetectorInput<Brand>): readonly
264
290
  const members = sides.map((side) => classMembers(project, held, ids, side));
265
291
  const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
266
292
 
267
- const found: Evidence[] = [];
293
+ const found = new Map<string, Evidence>();
268
294
  for (const component of components) {
269
295
  const listed = new Set(component.contract.members);
270
296
  const site = renderPosition(
@@ -275,10 +301,12 @@ export function frameworkLifecycle<Brand>(input: DetectorInput<Brand>): readonly
275
301
  for (const member of members.flatMap((side) => side.get(component.id) ?? [])) {
276
302
  const symbol = byId.get(member.id);
277
303
  const spelled = symbol === undefined ? undefined : memberComponent(symbol, byId);
278
- if (spelled !== undefined && listed.has(spelled)) {
279
- found.push({ id: member.id, detail: component.detail, site });
304
+ // A configured contract may name the platform's registry too; one record answers both.
305
+ const key = `${member.id}\0${component.detail}\0${site.path}:${String(site.line)}:${String(site.column)}`;
306
+ if (spelled !== undefined && listed.has(spelled) && !found.has(key)) {
307
+ found.set(key, { id: member.id, detail: component.detail, site });
280
308
  }
281
309
  }
282
310
  }
283
- return found;
311
+ return [...found.values()];
284
312
  }
@@ -4,17 +4,20 @@
4
4
  */
5
5
 
6
6
  import {
7
+ isExportDeclaration,
7
8
  isExportSpecifier,
8
9
  isExternalModuleReference,
9
- isIdentifier,
10
10
  isImportDeclaration,
11
11
  isImportEqualsDeclaration,
12
12
  isJsxElement,
13
13
  isJsxFragment,
14
14
  isJsxSelfClosingElement,
15
+ isNamedExports,
15
16
  isNamespaceImport,
16
17
  SyntaxKind,
17
18
  type Identifier,
19
+ type ImportDeclaration,
20
+ type ImportEqualsDeclaration,
18
21
  type Node,
19
22
  type SourceFile,
20
23
  } from "@typescript/native/ast";
@@ -41,42 +44,108 @@ export interface JsxOptions {
41
44
  readonly jsxFragmentFactory?: unknown;
42
45
  }
43
46
 
47
+ /** One name a file's top-level import declaration binds. */
48
+ interface ImportBinding {
49
+ readonly name: Identifier;
50
+ /** The export name it binds, `default` for a default import; absent where it binds a whole module. */
51
+ readonly imported?: string;
52
+ /** An `import x = …` declaration: whether it is exported, and whether it names an external module. */
53
+ readonly equals?: { readonly exported: boolean; readonly external: boolean };
54
+ }
55
+
56
+ const bindingsCache = new WeakMap<SourceFile, readonly ImportBinding[]>();
57
+
44
58
  /**
45
- * The names one file's import declarations bind: the default, named and namespace names of
46
- * an `import` declaration, and the name of an `import x = require()` declaration that is
47
- * not exported. None is judged in a declaration file or a component file.
59
+ * Every name one import declaration binds, in source order: the default, named and
60
+ * namespace names of an `import` declaration and the name of an `import x = …` declaration.
61
+ * A namespace import and an import assignment bind a whole module.
48
62
  */
49
- export function importBindings(file: SourceFile): readonly Identifier[] {
50
- if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
51
- return [];
63
+ export function declarationBindings(
64
+ declaration: ImportDeclaration | ImportEqualsDeclaration,
65
+ ): readonly ImportBinding[] {
66
+ if (isImportEqualsDeclaration(declaration)) {
67
+ return [
68
+ {
69
+ name: declaration.name,
70
+ equals: {
71
+ exported: (declaration.modifiers ?? []).some(
72
+ (modifier) => modifier.kind === SyntaxKind.ExportKeyword,
73
+ ),
74
+ external: isExternalModuleReference(declaration.moduleReference),
75
+ },
76
+ },
77
+ ];
52
78
  }
53
- const found: Identifier[] = [];
54
- for (const statement of file.statements) {
55
- if (isImportDeclaration(statement)) {
56
- const clause = statement.importClause;
57
- if (clause?.name !== undefined) {
58
- found.push(clause.name);
59
- }
60
- const named = clause?.namedBindings;
61
- if (named !== undefined && isNamespaceImport(named)) {
62
- found.push(named.name);
63
- } else if (named !== undefined) {
64
- for (const element of named.elements) {
65
- found.push(element.name);
66
- }
67
- }
68
- } else if (
69
- isImportEqualsDeclaration(statement) &&
70
- isExternalModuleReference(statement.moduleReference) &&
71
- isIdentifier(statement.name) &&
72
- !(statement.modifiers ?? []).some((modifier) => modifier.kind === SyntaxKind.ExportKeyword)
73
- ) {
74
- found.push(statement.name);
79
+ const clause = declaration.importClause;
80
+ const found: ImportBinding[] = [];
81
+ if (clause?.name !== undefined) {
82
+ found.push({ name: clause.name, imported: "default" });
83
+ }
84
+ const named = clause?.namedBindings;
85
+ if (named !== undefined && isNamespaceImport(named)) {
86
+ found.push({ name: named.name });
87
+ } else if (named !== undefined) {
88
+ for (const element of named.elements) {
89
+ found.push({ name: element.name, imported: element.propertyName?.text ?? element.name.text });
75
90
  }
76
91
  }
77
92
  return found;
78
93
  }
79
94
 
95
+ /** Every name one file's top-level import declarations bind, in source order. */
96
+ export function bindingsOf(file: SourceFile): readonly ImportBinding[] {
97
+ const known = bindingsCache.get(file);
98
+ if (known !== undefined) {
99
+ return known;
100
+ }
101
+ const found = file.statements.flatMap((statement) =>
102
+ isImportDeclaration(statement) || isImportEqualsDeclaration(statement)
103
+ ? declarationBindings(statement)
104
+ : [],
105
+ );
106
+ bindingsCache.set(file, found);
107
+ return found;
108
+ }
109
+
110
+ /**
111
+ * The export names an import or a re-export declaration reads from the module it names,
112
+ * `undefined` standing for the whole module where a namespace or a star reads it; none for
113
+ * any other node.
114
+ */
115
+ export function importedNames(node: Node): readonly (string | undefined)[] {
116
+ if (isImportDeclaration(node)) {
117
+ return declarationBindings(node).map((binding) => binding.imported);
118
+ }
119
+ if (!isExportDeclaration(node)) {
120
+ return [];
121
+ }
122
+ const clause = node.exportClause;
123
+ return clause === undefined || !isNamedExports(clause)
124
+ ? [undefined]
125
+ : clause.elements.map((one) => (one.propertyName ?? one.name).text);
126
+ }
127
+
128
+ /** The export names {@link importedNames} gives one node, without the whole module. */
129
+ export function namesImported(node: Node): readonly string[] {
130
+ return importedNames(node).flatMap((name) => name ?? []);
131
+ }
132
+
133
+ /**
134
+ * The bindings of one file that are judged as imports: those of {@link bindingsOf} but an
135
+ * `import x = …` that is exported or names no external module. None is judged in a
136
+ * declaration file or a component file.
137
+ */
138
+ export function importBindings(file: SourceFile): readonly Identifier[] {
139
+ if (file.isDeclarationFile || !JUDGED.test(file.fileName)) {
140
+ return [];
141
+ }
142
+ return bindingsOf(file).flatMap((binding) =>
143
+ binding.equals !== undefined && (binding.equals.exported || !binding.equals.external)
144
+ ? []
145
+ : [binding.name],
146
+ );
147
+ }
148
+
80
149
  /** Whether one identifier names a binding in a local export, which reads it whatever it resolves to. */
81
150
  export function exportsLocally(use: Identifier): boolean {
82
151
  return isExportSpecifier(use.parent);