@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.
- package/dist/src/alias-chain.js +0 -5
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +82 -91
- package/dist/src/augmentations.js +24 -25
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +279 -43
- package/dist/src/commands/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/contract.generated.js +2 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +44 -21
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/framework-lifecycle.js +41 -14
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory.js +18 -0
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +45 -20
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/query.js +46 -0
- 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 +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/symbol-keys.js +89 -83
- 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} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +75 -132
- package/dist/src/verbs/print-retained.js +3 -3
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +125 -128
- package/src/augmentations.ts +42 -28
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/contract.generated.ts +2 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/facts.ts +5 -3
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +9 -0
- package/src/findings/completion.ts +48 -23
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +23 -41
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/framework-lifecycle.ts +42 -14
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +2 -2
- package/src/inventory.ts +19 -0
- package/src/keyof-comparisons.ts +131 -116
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +93 -27
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/query.ts +81 -0
- 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 +175 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/published-reach.ts +9 -4
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +15 -3
- package/src/symbol-keys.ts +102 -102
- 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} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +83 -158
- package/src/verbs/print-retained.ts +3 -3
- package/dist/src/matrix.js +0 -273
- package/dist/src/sweep.js +0 -222
- package/src/matrix.ts +0 -463
- package/src/sweep.ts +0 -361
package/src/analysis.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
} from "./roots/configured-declarations.ts";
|
|
18
18
|
import { decorator } from "./decorator.ts";
|
|
19
19
|
import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.ts";
|
|
20
|
+
import { carriersOf, NO_CARRIERS, type Carriers } from "./exposure.ts";
|
|
20
21
|
import { deprecatedDeclarations, type Deprecation } from "./deprecation.ts";
|
|
21
22
|
import { moduleIdentity, type LoadedConsumer, type NotBuilt } from "./load/discover.ts";
|
|
22
23
|
import type { LibraryUnfound } from "./load/classify.ts";
|
|
@@ -26,17 +27,16 @@ import { readSources, type SourceFacts } from "./entries/sources.ts";
|
|
|
26
27
|
import { readWorkflows, type Workflows } from "./entries/workflow-steps.ts";
|
|
27
28
|
import { readEdgeSides } from "./edges.ts";
|
|
28
29
|
import { enumGroup } from "./enum-group.ts";
|
|
30
|
+
import { bindingsOf, distinctBindings, type BindingRecord } from "./bindings.ts";
|
|
29
31
|
import { generatedFile, generatedFilesOf, type GeneratedFact } from "./generated-file.ts";
|
|
30
|
-
import { inOperandNames } from "./in-operands.ts";
|
|
32
|
+
import { inOperandNames } from "./findings/in-operands.ts";
|
|
31
33
|
import { sourceFilesOf } from "./source-files.ts";
|
|
32
34
|
import {
|
|
33
35
|
evidenceRecords,
|
|
34
36
|
heldRecords,
|
|
35
37
|
disabledClasses,
|
|
36
38
|
exemptionsOf,
|
|
37
|
-
holdingWhile,
|
|
38
39
|
isUnansweredRecord,
|
|
39
|
-
retainedIn,
|
|
40
40
|
writtenRecords,
|
|
41
41
|
type Detector,
|
|
42
42
|
type Detectors,
|
|
@@ -49,22 +49,14 @@ import type { EmitterInput } from "./findings/emitter.ts";
|
|
|
49
49
|
import { unmatchedRootFindings, type SelfCheckFacts } from "./findings/self-check.ts";
|
|
50
50
|
import { frameworkLifecycle } from "./framework-lifecycle.ts";
|
|
51
51
|
import type { Host } from "./host.ts";
|
|
52
|
-
import { implementations, mergeImplementations } from "./implementations.ts";
|
|
52
|
+
import { implementations, mergeImplementations } from "./findings/implementations.ts";
|
|
53
53
|
import { injectionContainer } from "./injection-container.ts";
|
|
54
54
|
import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.ts";
|
|
55
55
|
import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-function-parts.ts";
|
|
56
56
|
import { type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
57
57
|
import { inventory } from "./inventory/enumerate.ts";
|
|
58
58
|
import { readManifest, type ManifestEntry } from "./roots/manifest.ts";
|
|
59
|
-
import {
|
|
60
|
-
liveFilesAt,
|
|
61
|
-
matrixOf,
|
|
62
|
-
referenceKey,
|
|
63
|
-
sweepMatrix,
|
|
64
|
-
type Configured,
|
|
65
|
-
type Matrix,
|
|
66
|
-
type SweepResult,
|
|
67
|
-
} from "./matrix.ts";
|
|
59
|
+
import { matrixOf, referenceKey, type Configured, type Matrix } from "./liveness/matrix.ts";
|
|
68
60
|
import {
|
|
69
61
|
ancestors,
|
|
70
62
|
dirnamePath,
|
|
@@ -78,7 +70,7 @@ import { byPosition, isComponentFile, type Position } from "./position.ts";
|
|
|
78
70
|
import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
|
|
79
71
|
import { skippedUnits, type SkippedUnit } from "./type-errors.ts";
|
|
80
72
|
import { reflectiveLookup } from "./reflective-lookup.ts";
|
|
81
|
-
import { references,
|
|
73
|
+
import { references, type Reference } from "./references.ts";
|
|
82
74
|
import {
|
|
83
75
|
rootSet,
|
|
84
76
|
unmatchedEverywhere,
|
|
@@ -88,18 +80,28 @@ import {
|
|
|
88
80
|
} from "./roots/root-set.ts";
|
|
89
81
|
import type { Scope } from "./scope.ts";
|
|
90
82
|
import { decodedMembers, serializationContract, type Decoded } from "./serialization-contract.ts";
|
|
91
|
-
import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside
|
|
83
|
+
import { outsideTestReferences, testFilesAt, type OutsideTest } from "./tests/outside.ts";
|
|
92
84
|
import type { UnseenLoad } from "./roots/unseen-loads.ts";
|
|
93
85
|
import type { UnresolvedSpecifier } from "./load/specifiers.ts";
|
|
86
|
+
import { formedForNode } from "./load/synthesize.ts";
|
|
94
87
|
import type { UnreadInput } from "./load/plan.ts";
|
|
95
88
|
import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.ts";
|
|
96
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
targetTestFile,
|
|
91
|
+
testFilePaths,
|
|
92
|
+
testFileRulesOf,
|
|
93
|
+
withheldBy,
|
|
94
|
+
type TestFileRule,
|
|
95
|
+
} from "./tests/file-class.ts";
|
|
96
|
+
import { testSupportFiles, type TestEvidence } from "./tests/support.ts";
|
|
97
97
|
import type { Engine, ProjectView } from "./session.ts";
|
|
98
98
|
import { accessorsOf, storesOf } from "./stores.ts";
|
|
99
99
|
import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
|
|
100
100
|
import { bindRows, readBaseline, readIgnoreFile, type Recorded } from "./suppress-file.ts";
|
|
101
101
|
import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./suppress-inline.ts";
|
|
102
|
-
import
|
|
102
|
+
import { sweep, type Sweep, type SweepPolicy } from "./liveness/sweep.ts";
|
|
103
|
+
import type { Exemption } from "./exempt.ts";
|
|
104
|
+
import type { Mode } from "./tests/file-class.ts";
|
|
103
105
|
import { readTemplates, templateField } from "./template-field.ts";
|
|
104
106
|
import { bytewise } from "./text/compare.ts";
|
|
105
107
|
import { holdsManifest, manifestAt } from "./fs/manifest.ts";
|
|
@@ -179,8 +181,12 @@ interface ProjectRead {
|
|
|
179
181
|
readonly skipped: readonly SkippedUnit[];
|
|
180
182
|
/** The project's generated files, by path below the target root, each with what decided it. */
|
|
181
183
|
readonly generated: ReadonlyMap<string, GeneratedFact>;
|
|
184
|
+
/** The binding identities its generated files publish (`bindings.6`, `bindings.7`). */
|
|
185
|
+
readonly bindings: readonly BindingRecord[];
|
|
182
186
|
/** What the project's entry sources enter and name, read once. */
|
|
183
187
|
readonly sources: SourceFacts;
|
|
188
|
+
/** Whether the run formed the program to resolve its modules as Node does (program.36). */
|
|
189
|
+
readonly formedForNode: boolean;
|
|
184
190
|
}
|
|
185
191
|
|
|
186
192
|
/** One target project the run analyzes, as its stage is handed it. */
|
|
@@ -195,6 +201,8 @@ interface ProjectsRead<Answer> {
|
|
|
195
201
|
readonly answers: readonly Answer[];
|
|
196
202
|
/** Every reference a consumer makes to a declaration of the target, each once. */
|
|
197
203
|
readonly references: readonly Reference[];
|
|
204
|
+
/** The declarations a consumer's read rules held, each once. */
|
|
205
|
+
readonly consumerHeld: readonly string[];
|
|
198
206
|
/** The test files below the target root no project the run reads holds, parsed. */
|
|
199
207
|
readonly outsideTests: readonly OutsideTest[];
|
|
200
208
|
/** The target root's workflow steps, git hooks and makefile, read once for every project. */
|
|
@@ -246,6 +254,7 @@ function readProjects<Answer>(
|
|
|
246
254
|
workflows,
|
|
247
255
|
});
|
|
248
256
|
const rooted = rootSet(project, held, targetRoot, { ...options, sources, skips });
|
|
257
|
+
const generated = generatedFilesOf(own, sources.generated);
|
|
249
258
|
return {
|
|
250
259
|
configuration: planned.id,
|
|
251
260
|
configFile: project.configFile,
|
|
@@ -257,8 +266,18 @@ function readProjects<Answer>(
|
|
|
257
266
|
.ownSourceFiles()
|
|
258
267
|
.filter((file) => referenceOnly(file.fileName))
|
|
259
268
|
.flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
|
|
260
|
-
generated
|
|
269
|
+
generated,
|
|
270
|
+
bindings: bindingsOf(
|
|
271
|
+
own.byPath,
|
|
272
|
+
generated,
|
|
273
|
+
new Map(
|
|
274
|
+
held.symbols
|
|
275
|
+
.filter((one) => one.kind === "file")
|
|
276
|
+
.map((one) => [one.position.path, one.ref]),
|
|
277
|
+
),
|
|
278
|
+
),
|
|
261
279
|
sources,
|
|
280
|
+
formedForNode: formedForNode(planned),
|
|
262
281
|
};
|
|
263
282
|
};
|
|
264
283
|
for (const file of project.ownSourceFiles()) {
|
|
@@ -267,11 +286,16 @@ function readProjects<Answer>(
|
|
|
267
286
|
return stage(project, { configuration: planned.id, read });
|
|
268
287
|
});
|
|
269
288
|
const consumed = new Map<string, Reference>();
|
|
289
|
+
const consumerHeld = new Set<string>();
|
|
270
290
|
for (const { project, consumer } of classified.consumed) {
|
|
271
291
|
const side = { id: consumer.id, root: consumer.path };
|
|
272
|
-
|
|
292
|
+
const read = consumerReferences(project, host, targetRoot, side, config, parse);
|
|
293
|
+
for (const reference of read.references) {
|
|
273
294
|
consumed.set(referenceKey(reference), reference);
|
|
274
295
|
}
|
|
296
|
+
for (const id of read.held) {
|
|
297
|
+
consumerHeld.add(id);
|
|
298
|
+
}
|
|
275
299
|
}
|
|
276
300
|
const outsideTests = testFilesAt(
|
|
277
301
|
host,
|
|
@@ -281,7 +305,13 @@ function readProjects<Answer>(
|
|
|
281
305
|
),
|
|
282
306
|
parse,
|
|
283
307
|
);
|
|
284
|
-
return {
|
|
308
|
+
return {
|
|
309
|
+
answers,
|
|
310
|
+
references: [...consumed.values()],
|
|
311
|
+
consumerHeld: [...consumerHeld],
|
|
312
|
+
outsideTests,
|
|
313
|
+
workflows,
|
|
314
|
+
};
|
|
285
315
|
});
|
|
286
316
|
}
|
|
287
317
|
|
|
@@ -386,30 +416,24 @@ export function runRoots(
|
|
|
386
416
|
};
|
|
387
417
|
}
|
|
388
418
|
|
|
419
|
+
/** The production mode a configuration sets: a consumer's test reference is classed as `analysis.consumer_tests` says. */
|
|
420
|
+
export function productionMode(config: PartialConfig): Mode {
|
|
421
|
+
return {
|
|
422
|
+
production: true,
|
|
423
|
+
consumerTestsProduction: config.analysis.consumer_tests === "production",
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
389
427
|
/** The run's sweep, beside the matrix it judged. */
|
|
390
428
|
export interface RunSweep {
|
|
391
429
|
readonly matrix: Matrix;
|
|
392
|
-
readonly sweep:
|
|
393
|
-
/** The exemption records that hold under the sweep. */
|
|
394
|
-
readonly exempt: readonly Exemption[];
|
|
395
|
-
/**
|
|
396
|
-
* The exemption records that held back a declaration some configuration of the run
|
|
397
|
-
* would otherwise judge dead, in the declarations' site order.
|
|
398
|
-
*/
|
|
399
|
-
readonly retained: readonly Exemption[];
|
|
430
|
+
readonly sweep: Sweep;
|
|
400
431
|
/** The derived configurations the run dropped. */
|
|
401
432
|
readonly notBuilt: readonly NotBuilt[];
|
|
402
433
|
/** Every question the checker could not answer, each once. */
|
|
403
434
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
404
|
-
/**
|
|
405
|
-
* Every declaration an unanswered question could have kept live, and every one only
|
|
406
|
-
* such a declaration keeps live. The run reports none of them.
|
|
407
|
-
*/
|
|
408
|
-
readonly heldByUnanswered: readonly string[];
|
|
409
435
|
/** The lines of the units type errors skipped, on which the run reports nothing. */
|
|
410
436
|
readonly skipped: readonly SkippedUnit[];
|
|
411
|
-
/** The component files the run read, by path below the target root, each once. */
|
|
412
|
-
readonly componentFiles: readonly string[];
|
|
413
437
|
}
|
|
414
438
|
|
|
415
439
|
/** One `<script` start tag of a component file the run read no block for. */
|
|
@@ -442,6 +466,8 @@ interface SweptProject<Extra> {
|
|
|
442
466
|
readonly unanswered: readonly string[];
|
|
443
467
|
/** The data members a generic declared outside the analysis fills, which it may read. */
|
|
444
468
|
readonly filledOutside: readonly string[];
|
|
469
|
+
/** What only a bundler's retry of an import Node leaves unresolved names (program.38). */
|
|
470
|
+
readonly possiblyLoaded: readonly string[];
|
|
445
471
|
readonly typeErrorSkips: readonly TypeErrorSkip[];
|
|
446
472
|
readonly skipped: readonly SkippedUnit[];
|
|
447
473
|
/** The component files the project holds of its own, by path below the target root. */
|
|
@@ -457,6 +483,9 @@ interface SweptProject<Extra> {
|
|
|
457
483
|
interface ReadProject<Extra> {
|
|
458
484
|
readonly projectRead: ProjectRead;
|
|
459
485
|
readonly references: readonly Reference[];
|
|
486
|
+
/** The declarations its read rules held where the checker did not answer. */
|
|
487
|
+
readonly held: readonly string[];
|
|
488
|
+
readonly possiblyLoaded: readonly string[];
|
|
460
489
|
readonly testFiles: readonly string[];
|
|
461
490
|
readonly evidence: readonly Exemption[];
|
|
462
491
|
readonly componentFiles: readonly string[];
|
|
@@ -477,6 +506,8 @@ interface ReadRun<Extra> {
|
|
|
477
506
|
readonly projects: readonly SweptProject<Extra>[];
|
|
478
507
|
/** Every declared consumer, each loaded, in the scope's order. */
|
|
479
508
|
readonly consumers: readonly LoadedConsumer[];
|
|
509
|
+
/** Whether the scope declares a consumer, loaded or not. */
|
|
510
|
+
readonly declaresConsumers: boolean;
|
|
480
511
|
/** The derived configurations the run dropped. */
|
|
481
512
|
readonly notBuilt: readonly NotBuilt[];
|
|
482
513
|
/** Every question the checker could not answer, each once. */
|
|
@@ -578,11 +609,13 @@ function readRun<Extra>(
|
|
|
578
609
|
);
|
|
579
610
|
const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
|
|
580
611
|
const projectRead = opened.read();
|
|
612
|
+
const testFile = targetTestFile(projectRead.sources.testFiles);
|
|
581
613
|
const resolved = references(project, projectRead.held, targetRoot, {
|
|
582
|
-
|
|
583
|
-
testFileNames: projectRead.sources.testFiles,
|
|
614
|
+
testFile,
|
|
584
615
|
directories,
|
|
616
|
+
formedForNode: projectRead.formedForNode,
|
|
585
617
|
});
|
|
618
|
+
const testFiles = testFilePaths(project, targetRoot, testFile);
|
|
586
619
|
const evidence = evidenceRecords(
|
|
587
620
|
{
|
|
588
621
|
project,
|
|
@@ -607,7 +640,9 @@ function readRun<Extra>(
|
|
|
607
640
|
return {
|
|
608
641
|
projectRead,
|
|
609
642
|
references: resolved.references,
|
|
610
|
-
|
|
643
|
+
held: resolved.held,
|
|
644
|
+
possiblyLoaded: resolved.possiblyLoaded,
|
|
645
|
+
testFiles,
|
|
611
646
|
evidence,
|
|
612
647
|
componentFiles: components.map(pathOf),
|
|
613
648
|
imports: new Map(
|
|
@@ -638,14 +673,14 @@ function readRun<Extra>(
|
|
|
638
673
|
decoded: decodedMembers(
|
|
639
674
|
project,
|
|
640
675
|
projectRead.held,
|
|
641
|
-
productionFiles(project, targetRoot,
|
|
676
|
+
productionFiles(project, targetRoot, testFiles),
|
|
642
677
|
consumers,
|
|
643
678
|
),
|
|
644
679
|
extra: extra(
|
|
645
680
|
project,
|
|
646
681
|
projectRead,
|
|
647
682
|
resolved.references,
|
|
648
|
-
|
|
683
|
+
testFiles,
|
|
649
684
|
resolved.unseenLoads.map((position) => ({
|
|
650
685
|
configuration: projectRead.configuration,
|
|
651
686
|
position,
|
|
@@ -705,7 +740,7 @@ function readRun<Extra>(
|
|
|
705
740
|
testFiles: new Set([...one.testFiles, ...support]),
|
|
706
741
|
});
|
|
707
742
|
const exempt = detected.records;
|
|
708
|
-
const made =
|
|
743
|
+
const made = [...one.references, ...(satisfied?.references ?? [])];
|
|
709
744
|
const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
|
|
710
745
|
const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
|
|
711
746
|
const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
|
|
@@ -732,13 +767,13 @@ function readRun<Extra>(
|
|
|
732
767
|
unanswered: [
|
|
733
768
|
...projectRead.held.unanswered,
|
|
734
769
|
...projectRead.rooted.unanswered,
|
|
735
|
-
...made
|
|
736
|
-
|
|
737
|
-
.map((reference) => reference.to),
|
|
770
|
+
...made.filter((reference) => reference.guessed === true).map((reference) => reference.to),
|
|
771
|
+
...one.held,
|
|
738
772
|
...exempt.filter(isUnansweredRecord).map((record) => record.id),
|
|
739
773
|
...one.decoded.unanswered,
|
|
740
774
|
],
|
|
741
775
|
filledOutside: one.decoded.filled,
|
|
776
|
+
possiblyLoaded: one.possiblyLoaded,
|
|
742
777
|
typeErrorSkips: projectRead.typeErrorSkips,
|
|
743
778
|
unmappedEntries: projectRead.rooted.unmappedEntries,
|
|
744
779
|
heldScripts: projectRead.rooted.heldScripts,
|
|
@@ -766,11 +801,13 @@ function readRun<Extra>(
|
|
|
766
801
|
unansweredHeld: [
|
|
767
802
|
...new Set([
|
|
768
803
|
...answers.flatMap((one) => one.unanswered),
|
|
769
|
-
...consumed.filter((one) => one.
|
|
804
|
+
...consumed.filter((one) => one.guessed === true).map((one) => one.to),
|
|
805
|
+
...read.result.consumerHeld,
|
|
770
806
|
]),
|
|
771
807
|
],
|
|
772
808
|
projects,
|
|
773
809
|
consumers: read.plan.discovery.consumers,
|
|
810
|
+
declaresConsumers: scope.consumers.length > 0,
|
|
774
811
|
notBuilt: read.classified.notBuilt,
|
|
775
812
|
unanswered: read.unanswered,
|
|
776
813
|
componentsUnread: read.plan.workspace?.componentsUnread ?? [],
|
|
@@ -785,33 +822,6 @@ function readRun<Extra>(
|
|
|
785
822
|
};
|
|
786
823
|
}
|
|
787
824
|
|
|
788
|
-
/**
|
|
789
|
-
* The run swept under one set of marks with the records that hold. A record a component
|
|
790
|
-
* file's markup holds counts only while the file is live, and what it holds may be what
|
|
791
|
-
* keeps the file live, so each file's markup joins only once a sweep without it finds the
|
|
792
|
-
* file live, until a sweep finds no further file live.
|
|
793
|
-
*/
|
|
794
|
-
function holdingSweep<Extra>(
|
|
795
|
-
read: ReadRun<Extra>,
|
|
796
|
-
input: Pick<SweepInput, "marked" | "mode">,
|
|
797
|
-
): { readonly swept: SweepInput; readonly result: SweepResult } {
|
|
798
|
-
const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
|
|
799
|
-
let live = new Set<string>();
|
|
800
|
-
for (;;) {
|
|
801
|
-
const swept: SweepInput = {
|
|
802
|
-
...input,
|
|
803
|
-
exempt: holdingWhile(read.exempt, live),
|
|
804
|
-
unanswered: read.unansweredHeld,
|
|
805
|
-
};
|
|
806
|
-
const result = sweepMatrix(read.matrix, swept);
|
|
807
|
-
const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
|
|
808
|
-
if (next.size === live.size) {
|
|
809
|
-
return { swept, result };
|
|
810
|
-
}
|
|
811
|
-
live = next;
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
|
|
815
825
|
/**
|
|
816
826
|
* The test references exemption evidence written in a test file makes: each is a read of
|
|
817
827
|
* the declaration it would retain, made by the test file at the evidence's site.
|
|
@@ -855,8 +865,8 @@ function countedWrites(
|
|
|
855
865
|
mode: Mode,
|
|
856
866
|
): ReadonlySet<string> {
|
|
857
867
|
return new Set(
|
|
858
|
-
|
|
859
|
-
reference.use === "write" && !(mode
|
|
868
|
+
references.flatMap((reference) =>
|
|
869
|
+
reference.use === "write" && !withheldBy(mode, reference, support) ? [reference.to] : [],
|
|
860
870
|
),
|
|
861
871
|
);
|
|
862
872
|
}
|
|
@@ -869,8 +879,9 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
|
|
|
869
879
|
const written = new Set<string>();
|
|
870
880
|
const readSome = new Set<string>();
|
|
871
881
|
for (const { configured } of read.projects) {
|
|
882
|
+
const support = new Set(configured.supportFiles ?? []);
|
|
872
883
|
for (const reference of configured.references) {
|
|
873
|
-
if (mode
|
|
884
|
+
if (withheldBy(mode, reference, support)) {
|
|
874
885
|
continue;
|
|
875
886
|
}
|
|
876
887
|
if (reference.use === "write" && reference.consumer === undefined) {
|
|
@@ -883,62 +894,27 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
|
|
|
883
894
|
return new Set([...written].filter((id) => !readSome.has(id)));
|
|
884
895
|
}
|
|
885
896
|
|
|
886
|
-
/**
|
|
887
|
-
* The sweep of a run read once, under one set of marks. Where a question went unanswered,
|
|
888
|
-
* the run is swept once more without what that question could have kept live and without
|
|
889
|
-
* the references that stood in for its answer, and every declaration that sweep judges
|
|
890
|
-
* dead and this one judges live, or dead by another rule, is held by the gap too, as is
|
|
891
|
-
* every declaration that makes such a reference.
|
|
892
|
-
*/
|
|
897
|
+
/** The sweep of a run read once, under one set of marks. */
|
|
893
898
|
function sweptOf<Extra>(
|
|
894
899
|
read: ReadRun<Extra>,
|
|
895
|
-
input: Pick<
|
|
900
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
896
901
|
): RunSweep {
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
if (held.size > 0) {
|
|
901
|
-
// What a declaration reads where the checker did not answer is unknown, so a rule
|
|
902
|
-
// over what it reads, the test of dead code among them, cannot judge it.
|
|
903
|
-
for (const { configured } of read.projects) {
|
|
904
|
-
for (const reference of configured.references) {
|
|
905
|
-
if (reference.resolution === "by-name") {
|
|
906
|
-
held.add(reference.from);
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
const dead = new Map(result.candidates.map((candidate) => [candidate.id, candidate]));
|
|
911
|
-
const answered = matrixOf(
|
|
912
|
-
read.projects.map(({ configured }) => ({
|
|
913
|
-
...configured,
|
|
914
|
-
references: configured.references.filter((one) => one.resolution !== "by-name"),
|
|
915
|
-
})),
|
|
916
|
-
);
|
|
917
|
-
const bare: SweepInput = {
|
|
918
|
-
...swept,
|
|
919
|
-
exempt: exempt.filter((record) => !isUnansweredRecord(record)),
|
|
920
|
-
unanswered: [],
|
|
921
|
-
};
|
|
922
|
-
for (const candidate of sweepMatrix(answered, bare).candidates) {
|
|
923
|
-
const kept = dead.get(candidate.id);
|
|
924
|
-
if (
|
|
925
|
-
kept?.testOfDeadCode !== candidate.testOfDeadCode ||
|
|
926
|
-
kept.relation !== candidate.relation
|
|
927
|
-
) {
|
|
928
|
-
held.add(candidate.id);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
}
|
|
902
|
+
const componentFiles = [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(
|
|
903
|
+
bytewise,
|
|
904
|
+
);
|
|
932
905
|
return {
|
|
933
906
|
matrix: read.matrix,
|
|
934
|
-
sweep:
|
|
935
|
-
|
|
936
|
-
|
|
907
|
+
sweep: sweep(read.matrix, {
|
|
908
|
+
...input,
|
|
909
|
+
exempt: read.exempt,
|
|
910
|
+
unanswered: read.unansweredHeld,
|
|
911
|
+
componentFiles,
|
|
912
|
+
unseenCallers: !read.declaresConsumers,
|
|
913
|
+
writeOnly: writtenOnly(read, input.mode),
|
|
914
|
+
}),
|
|
937
915
|
notBuilt: read.notBuilt,
|
|
938
916
|
unanswered: read.unanswered,
|
|
939
|
-
heldByUnanswered: [...held],
|
|
940
917
|
skipped: read.projects.flatMap((one) => one.skipped),
|
|
941
|
-
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
|
|
942
918
|
};
|
|
943
919
|
}
|
|
944
920
|
|
|
@@ -955,7 +931,7 @@ export function runSweep(
|
|
|
955
931
|
host: Host,
|
|
956
932
|
scope: Scope,
|
|
957
933
|
config: ResolvedConfig,
|
|
958
|
-
input: Pick<
|
|
934
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
959
935
|
detectors: Detectors = DETECTORS,
|
|
960
936
|
): RunSweep {
|
|
961
937
|
return sweptOf(
|
|
@@ -972,12 +948,14 @@ interface EmitterExtra {
|
|
|
972
948
|
readonly implementations: ReturnType<typeof implementations>;
|
|
973
949
|
readonly heldByInclusion: readonly string[];
|
|
974
950
|
readonly generated: ReadonlyMap<string, GeneratedFact>;
|
|
951
|
+
readonly bindings: readonly BindingRecord[];
|
|
975
952
|
readonly inOperands: readonly string[];
|
|
976
953
|
readonly rooted: Roots;
|
|
977
954
|
readonly directives: readonly InlineDirective[];
|
|
978
955
|
readonly declarationsNamed: readonly boolean[];
|
|
979
956
|
readonly parts: ProjectParts;
|
|
980
957
|
readonly unseenLoads: readonly UnseenLoad[];
|
|
958
|
+
readonly carriers: Carriers;
|
|
981
959
|
}
|
|
982
960
|
|
|
983
961
|
/** The project's own files below the target root that are no test file. */
|
|
@@ -1024,6 +1002,7 @@ export function readEmitterRun(
|
|
|
1024
1002
|
implementations: implementations(project, read.held),
|
|
1025
1003
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
1026
1004
|
generated: read.generated,
|
|
1005
|
+
bindings: read.bindings,
|
|
1027
1006
|
inOperands: inOperandNames(project, targetRoot, testFiles),
|
|
1028
1007
|
rooted: read.rooted,
|
|
1029
1008
|
directives: inlineDirectives(project, read.held, targetRoot).filter(
|
|
@@ -1032,6 +1011,8 @@ export function readEmitterRun(
|
|
|
1032
1011
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
1033
1012
|
parts: projectParts(project, read.held, references, targetRoot),
|
|
1034
1013
|
unseenLoads,
|
|
1014
|
+
// Only a closed world reports an unnecessary export, which is all the carriers decide.
|
|
1015
|
+
carriers: config.consumers.complete ? carriersOf(project, read.held) : NO_CARRIERS,
|
|
1035
1016
|
}),
|
|
1036
1017
|
);
|
|
1037
1018
|
// Whether a declaration carries the marker decides its code, so one whose answer went
|
|
@@ -1098,7 +1079,7 @@ export function emitterInputOver(
|
|
|
1098
1079
|
scope: Scope,
|
|
1099
1080
|
config: ResolvedConfig,
|
|
1100
1081
|
read: ReadRun<EmitterExtra>,
|
|
1101
|
-
input: Pick<
|
|
1082
|
+
input: Pick<SweepPolicy, "marked" | "mode">,
|
|
1102
1083
|
selfCheck: SelfCheckFacts,
|
|
1103
1084
|
): EmitterInput {
|
|
1104
1085
|
const targetRoot = scope.target.path;
|
|
@@ -1112,9 +1093,10 @@ export function emitterInputOver(
|
|
|
1112
1093
|
stores: storesOf(
|
|
1113
1094
|
projects.map((one) => ({
|
|
1114
1095
|
references: one.configured.references,
|
|
1096
|
+
support: new Set(one.configured.supportFiles ?? []),
|
|
1115
1097
|
accessors: one.extra.accessors,
|
|
1116
1098
|
})),
|
|
1117
|
-
swept.exempt,
|
|
1099
|
+
swept.sweep.exempt,
|
|
1118
1100
|
input.mode,
|
|
1119
1101
|
),
|
|
1120
1102
|
dependencies: dependenciesOf(
|
|
@@ -1143,12 +1125,24 @@ export function emitterInputOver(
|
|
|
1143
1125
|
encapsulated: readManifest(host, targetRoot).declaresExports,
|
|
1144
1126
|
edges: readEdgeSides(host, targetRoot),
|
|
1145
1127
|
filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
|
|
1128
|
+
possiblyLoaded: new Set(projects.flatMap((one) => one.possiblyLoaded)),
|
|
1129
|
+
carriers: {
|
|
1130
|
+
named: new Map(
|
|
1131
|
+
[
|
|
1132
|
+
...Map.groupBy(
|
|
1133
|
+
projects.flatMap((one) => [...one.extra.carriers.named]),
|
|
1134
|
+
([id]) => id,
|
|
1135
|
+
),
|
|
1136
|
+
].map(([id, entries]) => [id, [...new Set(entries.flatMap(([, named]) => named))]]),
|
|
1137
|
+
),
|
|
1138
|
+
unread: new Set(projects.flatMap((one) => [...one.extra.carriers.unread])),
|
|
1139
|
+
},
|
|
1146
1140
|
},
|
|
1147
1141
|
selfCheck,
|
|
1148
1142
|
intraFunction: intraFunctionFacts(
|
|
1149
1143
|
projects.map((one) => one.extra.parts),
|
|
1150
1144
|
projects.flatMap((one) => one.configured.references),
|
|
1151
|
-
new Set([...swept.exempt.map((one) => one.id), ...swept.
|
|
1145
|
+
new Set([...swept.sweep.exempt.map((one) => one.id), ...swept.sweep.held]),
|
|
1152
1146
|
),
|
|
1153
1147
|
};
|
|
1154
1148
|
}
|
|
@@ -1232,6 +1226,7 @@ function readAnalysis(
|
|
|
1232
1226
|
),
|
|
1233
1227
|
unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
|
|
1234
1228
|
generated: new Map(read.projects.flatMap((one) => [...one.extra.generated])),
|
|
1229
|
+
bindings: distinctBindings(read.projects.flatMap((one) => one.extra.bindings)),
|
|
1235
1230
|
},
|
|
1236
1231
|
configured: read.projects.map((one) => one.configured),
|
|
1237
1232
|
symbols,
|
|
@@ -1340,6 +1335,8 @@ interface RunFacts {
|
|
|
1340
1335
|
readonly unseenLoads: readonly UnseenLoad[];
|
|
1341
1336
|
/** Every generated file of the run, by its path below the target root, each once. */
|
|
1342
1337
|
readonly generated: ReadonlyMap<string, GeneratedFact>;
|
|
1338
|
+
/** Every binding record the run's generated files publish, each once. */
|
|
1339
|
+
readonly bindings: readonly BindingRecord[];
|
|
1343
1340
|
}
|
|
1344
1341
|
|
|
1345
1342
|
/** One configuration whose component files a run cannot read. */
|
|
@@ -1385,7 +1382,7 @@ export function runAnalysis(
|
|
|
1385
1382
|
* declarations of its own the run holds for them.
|
|
1386
1383
|
*/
|
|
1387
1384
|
export function unansweredCounts(analysis: RunAnalysis): readonly UnansweredCount[] {
|
|
1388
|
-
const held = new Set(analysis.swept.
|
|
1385
|
+
const held = new Set(analysis.swept.sweep.held);
|
|
1389
1386
|
return analysis.run.projects.flatMap((project) => {
|
|
1390
1387
|
const questions =
|
|
1391
1388
|
analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length +
|
package/src/augmentations.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type Node,
|
|
15
15
|
} from "@typescript/native/ast";
|
|
16
16
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
17
|
-
import { referenceKey } from "./matrix.ts";
|
|
17
|
+
import { referenceKey } from "./liveness/matrix.ts";
|
|
18
18
|
import type { Reference } from "./references.ts";
|
|
19
19
|
import type { ProjectView } from "./session.ts";
|
|
20
20
|
import type { SourceFiles } from "./source-files.ts";
|
|
@@ -78,7 +78,7 @@ function holdersOf(
|
|
|
78
78
|
* file or a `declare` holds that a `declare global` block, or the top level of a file in
|
|
79
79
|
* `scripts`, declares, directly or inside a namespace there.
|
|
80
80
|
*/
|
|
81
|
-
|
|
81
|
+
function ambientGlobalInterfaces(
|
|
82
82
|
held: Inventory,
|
|
83
83
|
interfaces: readonly string[],
|
|
84
84
|
scripts: ReadonlySet<string>,
|
|
@@ -89,19 +89,31 @@ export function ambientGlobalInterfaces(
|
|
|
89
89
|
: interfaces.filter((id) => held.ambient.has(id) && holders(id).global);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/** The ambient global interfaces of a project's own files that merge into a declaration outside them. */
|
|
93
|
+
interface GlobalMerges {
|
|
94
|
+
/** The files that are no module, by identifier. */
|
|
95
|
+
readonly scripts: ReadonlySet<string>;
|
|
96
|
+
/** Each such interface, by identifier. */
|
|
97
|
+
readonly interfaces: ReadonlySet<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Each such interface with every interface, namespace and block between it and the global
|
|
100
|
+
* scope: the outside declaration names it wherever a value is typed by it. Where every
|
|
101
|
+
* outside declaration is a package's, not the compiler's library, and declares no member,
|
|
102
|
+
* each member it declares as well: such an interface is a slot the package reads by type,
|
|
103
|
+
* as `keyof` does, in declarations the analysis does not walk.
|
|
104
|
+
*/
|
|
105
|
+
readonly kept: readonly string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
92
108
|
/**
|
|
93
|
-
*
|
|
94
|
-
* declared outside them,
|
|
95
|
-
* global scope: the outside declaration names it wherever a value is typed by it. Where
|
|
96
|
-
* every outside declaration is a package's, not the compiler's library, and declares no
|
|
97
|
-
* member, each member it declares as well: such an interface is a slot the package reads
|
|
98
|
-
* by type, as `keyof` does, in declarations the analysis does not walk.
|
|
109
|
+
* Which ambient global interfaces of the project's own files, `files`, merge into an interface
|
|
110
|
+
* declared outside them, and what each merge keeps.
|
|
99
111
|
*/
|
|
100
112
|
export function mergedGlobalInterfaces<Brand>(
|
|
101
113
|
project: ProjectView<Brand>,
|
|
102
114
|
held: Inventory,
|
|
103
115
|
files: SourceFiles,
|
|
104
|
-
):
|
|
116
|
+
): GlobalMerges {
|
|
105
117
|
const scripts = new Set<string>();
|
|
106
118
|
const names = new Map<string, Node>();
|
|
107
119
|
for (const file of files.byName.values()) {
|
|
@@ -124,7 +136,7 @@ export function mergedGlobalInterfaces<Brand>(
|
|
|
124
136
|
const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
|
|
125
137
|
const asked = [...names].filter(([id]) => global.has(id));
|
|
126
138
|
if (asked.length === 0) {
|
|
127
|
-
return [];
|
|
139
|
+
return { scripts, interfaces: new Set(), kept: [] };
|
|
128
140
|
}
|
|
129
141
|
const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
|
|
130
142
|
const holders = holdersOf(held, scripts);
|
|
@@ -135,24 +147,26 @@ export function mergedGlobalInterfaces<Brand>(
|
|
|
135
147
|
const node = project.declarationAt(handle)?.node;
|
|
136
148
|
return node !== undefined && isInterfaceDeclaration(node) && node.members.length === 0;
|
|
137
149
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
const interfaces = new Set<string>();
|
|
151
|
+
const kept = new Set<string>();
|
|
152
|
+
asked.forEach(([id], at) => {
|
|
153
|
+
const symbol = answers[at];
|
|
154
|
+
const outside =
|
|
155
|
+
typeof symbol === "object"
|
|
156
|
+
? symbol.declarations.filter((handle) => !files.byName.has(handle.path))
|
|
157
|
+
: [];
|
|
158
|
+
if (outside.length === 0) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
interfaces.add(id);
|
|
162
|
+
const members = outside.every(empty)
|
|
163
|
+
? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
|
|
164
|
+
: [];
|
|
165
|
+
for (const one of [id, ...(holders?.(id).ids ?? []), ...members]) {
|
|
166
|
+
kept.add(one);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return { scripts, interfaces, kept: [...kept] };
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
/**
|