@cplieger/deadset-ts 7.2.0-dev.3 → 8.0.0-dev.1
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/bin/mapper-connection.js +7 -4
- package/dist/src/analysis.js +116 -16
- package/dist/src/augmentations.js +21 -8
- package/dist/src/automatic-types.js +189 -0
- package/dist/src/base-url.js +18 -0
- package/dist/src/component-files.js +96 -31
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +54 -15
- package/dist/src/configured-declarations.js +24 -2
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contextual-uses.js +26 -26
- package/dist/src/convention-rows.js +75 -0
- package/dist/src/conventions.js +101 -5
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +185 -30
- package/dist/src/destructuring.js +77 -2
- package/dist/src/discover.js +188 -85
- package/dist/src/edges.js +1 -1
- package/dist/src/entry-point-gaps.js +7 -10
- package/dist/src/entry-points.js +112 -11
- package/dist/src/findings/completion.js +116 -21
- package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +44 -13
- package/dist/src/findings/non-code-artifacts.js +66 -33
- package/dist/src/findings/unused-declarations.js +21 -1
- package/dist/src/findings/visibility-narrowing.js +22 -3
- package/dist/src/generated-file.js +11 -0
- package/dist/src/git-ignore.js +86 -0
- package/dist/src/graph.js +4 -1
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +313 -50
- package/dist/src/inventory.js +63 -8
- package/dist/src/json-document.js +5 -5
- package/dist/src/kinds.js +13 -13
- package/dist/src/matrix.js +18 -9
- package/dist/src/mdx-entries.js +197 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/namespace-objects.js +28 -18
- package/dist/src/overrides.js +45 -9
- package/dist/src/paths.js +9 -0
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +29 -13
- package/dist/src/reflective-lookup.js +8 -6
- package/dist/src/report.js +4 -2
- package/dist/src/reporters/template.js +22 -13
- package/dist/src/resolve.js +2 -0
- package/dist/src/roots.js +110 -38
- package/dist/src/schema.js +1 -0
- package/dist/src/serialization-contract.js +97 -20
- package/dist/src/session.js +26 -1
- package/dist/src/source-files.js +39 -14
- package/dist/src/spelled-names.js +58 -0
- package/dist/src/suppress-file.js +12 -4
- package/dist/src/suppress-inline.js +21 -3
- package/dist/src/suppress.js +7 -5
- package/dist/src/sweep.js +27 -10
- package/dist/src/symbol-keys.js +103 -2
- package/dist/src/template-field.js +9 -6
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +116 -35
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/value-reach.js +17 -5
- package/dist/src/verbs/analyze.js +19 -1
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version-range.js +10 -5
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +119 -22
- package/dist/src/workspace-programs.js +7 -3
- package/dist/src/workspace-resolution.js +72 -13
- package/dist/src/workspace.js +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +175 -16
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/component-files.ts +116 -31
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +80 -18
- package/src/configured-declarations.ts +33 -4
- package/src/conformance-record.ts +2 -2
- package/src/contextual-uses.ts +27 -27
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +249 -43
- package/src/destructuring.ts +93 -2
- package/src/discover.ts +234 -86
- package/src/edges.ts +1 -1
- package/src/entry-point-gaps.ts +8 -11
- package/src/entry-points.ts +133 -11
- package/src/finding.ts +10 -0
- package/src/findings/completion.ts +147 -24
- package/src/findings/dependencies-and-module-machinery.ts +90 -28
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +56 -14
- package/src/findings/non-code-artifacts.ts +81 -41
- package/src/findings/unused-declarations.ts +25 -1
- package/src/findings/visibility-narrowing.ts +32 -3
- package/src/generated-file.ts +16 -0
- package/src/git-ignore.ts +98 -0
- package/src/graph.ts +6 -0
- package/src/host.ts +5 -9
- package/src/import-bindings.ts +128 -0
- package/src/indexed-access.ts +152 -30
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +415 -64
- package/src/inventory.ts +83 -7
- package/src/json-document.ts +7 -7
- package/src/kinds.ts +13 -13
- package/src/matrix.ts +32 -14
- package/src/mdx-entries.ts +241 -0
- package/src/module-declarations.ts +11 -13
- package/src/namespace-objects.ts +34 -21
- package/src/overrides.ts +56 -9
- package/src/paths.ts +10 -0
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +38 -17
- package/src/reflective-lookup.ts +9 -7
- package/src/report.ts +16 -3
- package/src/reporters/template.ts +21 -13
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +108 -17
- package/src/session.ts +29 -1
- package/src/source-files.ts +66 -16
- package/src/spelled-names.ts +102 -0
- package/src/suppress-file.ts +14 -3
- package/src/suppress-inline.ts +21 -2
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/template-field.ts +9 -6
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +132 -41
- package/src/type-parameter-reads.ts +227 -0
- package/src/value-reach.ts +19 -6
- package/src/verbs/analyze.ts +28 -1
- package/src/verbs/explain.ts +5 -2
- package/src/version-range.ts +10 -5
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +127 -22
- package/src/workspace-programs.ts +7 -3
- package/src/workspace-resolution.ts +86 -14
- package/src/workspace.ts +1 -1
package/src/analysis.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* another project's checker, which holds only for views the session hands out.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { isStringLiteralLikeNode } from "@typescript/native/ast";
|
|
9
10
|
import { lineOf, readComponent } from "./component-files.ts";
|
|
10
11
|
import { kindOf, type Config, type Provenance } from "./config.ts";
|
|
11
12
|
import {
|
|
@@ -26,9 +27,11 @@ import {
|
|
|
26
27
|
discoverProjects,
|
|
27
28
|
DiscoveryError,
|
|
28
29
|
isGuess,
|
|
30
|
+
MISSING_GLOBAL_TYPE,
|
|
29
31
|
projectErrors,
|
|
30
32
|
renderDiagnostic,
|
|
31
33
|
targetRelative,
|
|
34
|
+
withBaseUrlPaths,
|
|
32
35
|
type LocatedDiagnostic,
|
|
33
36
|
type NotBuilt,
|
|
34
37
|
} from "./discover.ts";
|
|
@@ -70,7 +73,7 @@ import { injectionContainer } from "./injection-container.ts";
|
|
|
70
73
|
import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.ts";
|
|
71
74
|
import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-function-parts.ts";
|
|
72
75
|
import { inventory, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
73
|
-
import { packageEntries, readManifest } from "./manifest.ts";
|
|
76
|
+
import { packageEntries, readManifest, type ManifestEntry } from "./manifest.ts";
|
|
74
77
|
import {
|
|
75
78
|
liveFilesAt,
|
|
76
79
|
matrixOf,
|
|
@@ -80,7 +83,14 @@ import {
|
|
|
80
83
|
type Matrix,
|
|
81
84
|
type SweepResult,
|
|
82
85
|
} from "./matrix.ts";
|
|
83
|
-
import {
|
|
86
|
+
import {
|
|
87
|
+
dirnamePath,
|
|
88
|
+
extensionOf,
|
|
89
|
+
joinPath,
|
|
90
|
+
normalizePath,
|
|
91
|
+
relativePath,
|
|
92
|
+
resolvePath,
|
|
93
|
+
} from "./paths.ts";
|
|
84
94
|
import type { UnansweredQuestion } from "./query.ts";
|
|
85
95
|
import { byPosition, isComponentFile, type Position } from "./position.ts";
|
|
86
96
|
import { LANGUAGE, type TypeErrorSkip, type UnansweredCount } from "./report.ts";
|
|
@@ -105,7 +115,7 @@ import type { Scope } from "./scope.ts";
|
|
|
105
115
|
import { serializationContract } from "./serialization-contract.ts";
|
|
106
116
|
import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
|
|
107
117
|
import { joinedTestFiles, withJoinedTests } from "./test-joins.ts";
|
|
108
|
-
import { supportReferences, testSupportFiles } from "./test-support.ts";
|
|
118
|
+
import { supportReferences, testSupportFiles, type TestEvidence } from "./test-support.ts";
|
|
109
119
|
import {
|
|
110
120
|
diagnosticsOf,
|
|
111
121
|
ownedAs,
|
|
@@ -232,6 +242,8 @@ interface ReadProjects<Answer> {
|
|
|
232
242
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
233
243
|
/** Per configuration whose component files the run cannot read, the reason. */
|
|
234
244
|
readonly componentsUnread: readonly ComponentsUnread[];
|
|
245
|
+
/** Per analyzed configuration, each library it names that the compiler cannot find. */
|
|
246
|
+
readonly librariesUnfound: readonly LibraryUnfound[];
|
|
235
247
|
/** The convention rows the run applied. */
|
|
236
248
|
readonly conventionsApplied: readonly AppliedConvention[];
|
|
237
249
|
/** The dependencies the applied rows' short names use. */
|
|
@@ -381,6 +393,11 @@ function readProjects<Answer>(
|
|
|
381
393
|
publishedMembers: new Set(
|
|
382
394
|
(workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
|
|
383
395
|
),
|
|
396
|
+
runFiles: new Set(
|
|
397
|
+
opened.flatMap((one) =>
|
|
398
|
+
one.fileNames().map((name) => resolvePath(host.workingDirectory(), name)),
|
|
399
|
+
),
|
|
400
|
+
),
|
|
384
401
|
};
|
|
385
402
|
};
|
|
386
403
|
const notBuilt: NotBuilt[] = [...discovered.notBuilt];
|
|
@@ -394,6 +411,7 @@ function readProjects<Answer>(
|
|
|
394
411
|
const failures: LocatedDiagnostic[] = [];
|
|
395
412
|
const unbuildable: SetupFailure[] = [];
|
|
396
413
|
const setupDropped: SetupFailure[] = [];
|
|
414
|
+
const librariesUnfound: LibraryUnfound[] = [];
|
|
397
415
|
const consumed = new Map<string, Reference>();
|
|
398
416
|
const heldFiles = new Set<string>();
|
|
399
417
|
let outsideTests: readonly OutsideTest[] = [];
|
|
@@ -410,6 +428,12 @@ function readProjects<Answer>(
|
|
|
410
428
|
const joins = discovered.derived
|
|
411
429
|
? joinedTestFiles(engine, host, absoluteRoot, discovered.configFiles, testPatterns)
|
|
412
430
|
: new Map<string, readonly string[]>();
|
|
431
|
+
const programs = withBaseUrlPaths(
|
|
432
|
+
engine,
|
|
433
|
+
host,
|
|
434
|
+
[...discovered.configFiles, ...consumerOf.keys()],
|
|
435
|
+
workspace?.programs ?? new Map(),
|
|
436
|
+
);
|
|
413
437
|
const session = runSession(
|
|
414
438
|
engine,
|
|
415
439
|
[...discovered.configFiles, ...consumerOf.keys()],
|
|
@@ -458,10 +482,6 @@ function readProjects<Answer>(
|
|
|
458
482
|
: resolvePath(host.workingDirectory(), consumer.path),
|
|
459
483
|
paths: pathsOf(engine, project.configFile),
|
|
460
484
|
});
|
|
461
|
-
if (reading.failing.length > 0) {
|
|
462
|
-
failures.push(...reading.failing);
|
|
463
|
-
return undefined;
|
|
464
|
-
}
|
|
465
485
|
if (consumer !== undefined && reading.missing.length > 0) {
|
|
466
486
|
unbuildable.push(
|
|
467
487
|
...reading.missing.map((one) => ({
|
|
@@ -502,11 +522,35 @@ function readProjects<Answer>(
|
|
|
502
522
|
}
|
|
503
523
|
return undefined;
|
|
504
524
|
}
|
|
525
|
+
librariesUnfound.push(
|
|
526
|
+
...raw.unfound.map((one) => ({
|
|
527
|
+
configFile: project.configFile,
|
|
528
|
+
error: renderDiagnostic(one, (path) => relativePath(absoluteRoot, path) ?? path),
|
|
529
|
+
})),
|
|
530
|
+
);
|
|
531
|
+
const missingGlobal = raw.unfound.find(
|
|
532
|
+
({ diagnostic }) => diagnostic.code === MISSING_GLOBAL_TYPE,
|
|
533
|
+
);
|
|
534
|
+
const skips =
|
|
535
|
+
missingGlobal === undefined
|
|
536
|
+
? reading.skips
|
|
537
|
+
: [
|
|
538
|
+
...reading.skips,
|
|
539
|
+
...project.ownSourceFiles().map((file) => ({
|
|
540
|
+
record: {
|
|
541
|
+
path: relativePath(absoluteRoot, file.fileName) ?? file.fileName,
|
|
542
|
+
line: 1,
|
|
543
|
+
message: missingGlobal.diagnostic.text,
|
|
544
|
+
},
|
|
545
|
+
file,
|
|
546
|
+
unit: file,
|
|
547
|
+
})),
|
|
548
|
+
];
|
|
505
549
|
const configuration = ids.get(project.configFile) ?? project.configFile;
|
|
506
550
|
const read = (): ProjectRead => {
|
|
507
551
|
const held = inventory(project, host, targetRoot);
|
|
508
552
|
const found = roots(project, held, targetRoot, options);
|
|
509
|
-
const kept = skippedRoots(project, held,
|
|
553
|
+
const kept = skippedRoots(project, held, skips);
|
|
510
554
|
const rooted =
|
|
511
555
|
kept.length === 0
|
|
512
556
|
? found
|
|
@@ -523,8 +567,8 @@ function readProjects<Answer>(
|
|
|
523
567
|
configFile: project.configFile,
|
|
524
568
|
held,
|
|
525
569
|
rooted,
|
|
526
|
-
typeErrorSkips:
|
|
527
|
-
skipped: skippedUnits(
|
|
570
|
+
typeErrorSkips: skips.map((one) => one.record),
|
|
571
|
+
skipped: skippedUnits(skips, absoluteRoot),
|
|
528
572
|
referenceOnly: project
|
|
529
573
|
.ownSourceFiles()
|
|
530
574
|
.filter((file) => referenceOnly(file.fileName))
|
|
@@ -539,9 +583,7 @@ function readProjects<Answer>(
|
|
|
539
583
|
return stage(project, { configuration, read });
|
|
540
584
|
},
|
|
541
585
|
discovered.derived ? "record" : "refuse",
|
|
542
|
-
joins.size === 0
|
|
543
|
-
? workspace?.programs
|
|
544
|
-
: withJoinedTests(engine, host, workspace?.programs ?? new Map(), joins),
|
|
586
|
+
joins.size === 0 ? programs : withJoinedTests(engine, host, programs, joins),
|
|
545
587
|
() => {
|
|
546
588
|
outsideTests = testFilesAt(
|
|
547
589
|
host,
|
|
@@ -589,6 +631,7 @@ function readProjects<Answer>(
|
|
|
589
631
|
notBuilt,
|
|
590
632
|
unanswered: session.unanswered,
|
|
591
633
|
componentsUnread: workspace?.componentsUnread ?? [],
|
|
634
|
+
librariesUnfound,
|
|
592
635
|
conventionsApplied: conventions.applied,
|
|
593
636
|
conventionUses: conventions.uses,
|
|
594
637
|
memberDirs,
|
|
@@ -746,6 +789,8 @@ interface SweptProject<Extra> {
|
|
|
746
789
|
/** The component files the project holds of its own, by path below the target root. */
|
|
747
790
|
readonly componentFiles: readonly string[];
|
|
748
791
|
readonly componentWarnings: readonly ComponentWarning[];
|
|
792
|
+
/** The manifest entries the project reads back to no file of its own. */
|
|
793
|
+
readonly unmappedEntries: readonly ManifestEntry[];
|
|
749
794
|
readonly extra: Extra;
|
|
750
795
|
}
|
|
751
796
|
|
|
@@ -757,6 +802,8 @@ interface ReadProject<Extra> {
|
|
|
757
802
|
readonly evidence: readonly Exemption[];
|
|
758
803
|
readonly componentFiles: readonly string[];
|
|
759
804
|
readonly componentWarnings: readonly ComponentWarning[];
|
|
805
|
+
/** Per own file below the target root, the module specifiers its imports write. */
|
|
806
|
+
readonly imports: ReadonlyMap<string, readonly string[]>;
|
|
760
807
|
readonly extra: Extra;
|
|
761
808
|
}
|
|
762
809
|
|
|
@@ -775,6 +822,8 @@ interface ReadRun<Extra> {
|
|
|
775
822
|
readonly unanswered: readonly UnansweredQuestion[];
|
|
776
823
|
/** Per configuration whose component files the run cannot read, the reason. */
|
|
777
824
|
readonly componentsUnread: readonly ComponentsUnread[];
|
|
825
|
+
/** Per analyzed configuration, each library it names that the compiler cannot find. */
|
|
826
|
+
readonly librariesUnfound: readonly LibraryUnfound[];
|
|
778
827
|
/** The convention rows the run applied. */
|
|
779
828
|
readonly conventionsApplied: readonly AppliedConvention[];
|
|
780
829
|
/** The dependencies the applied rows' short names use. */
|
|
@@ -785,6 +834,64 @@ interface ReadRun<Extra> {
|
|
|
785
834
|
readonly nestedDirs: readonly string[];
|
|
786
835
|
}
|
|
787
836
|
|
|
837
|
+
/** The manifest section of a package test evidence counts, and the sections that keep one out of it. */
|
|
838
|
+
const DEV_SECTION = "devDependencies";
|
|
839
|
+
const OTHER_SECTIONS = ["dependencies", "peerDependencies", "optionalDependencies"] as const;
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* The imports of a run's own files and the manifests below the target root that test
|
|
843
|
+
* evidence reads: a package is a devDependency of a file where the nearest manifest at or
|
|
844
|
+
* above it that declares the package declares it under `devDependencies` alone.
|
|
845
|
+
*/
|
|
846
|
+
function testEvidence(
|
|
847
|
+
host: Host,
|
|
848
|
+
targetRoot: string,
|
|
849
|
+
projects: readonly { readonly imports: ReadonlyMap<string, readonly string[]> }[],
|
|
850
|
+
): TestEvidence {
|
|
851
|
+
const imports = new Map<string, readonly string[]>();
|
|
852
|
+
for (const one of projects) {
|
|
853
|
+
for (const [path, specifiers] of one.imports) {
|
|
854
|
+
imports.set(path, specifiers);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
const manifests = new Map<string, Readonly<Record<string, unknown>> | undefined>();
|
|
858
|
+
const manifestAt = (dir: string): Readonly<Record<string, unknown>> | undefined => {
|
|
859
|
+
if (!manifests.has(dir)) {
|
|
860
|
+
let value: unknown;
|
|
861
|
+
try {
|
|
862
|
+
value = JSON.parse(host.readFile(joinPath(dir, "package.json")));
|
|
863
|
+
} catch {
|
|
864
|
+
value = undefined;
|
|
865
|
+
}
|
|
866
|
+
manifests.set(
|
|
867
|
+
dir,
|
|
868
|
+
typeof value === "object" && value !== null && !Array.isArray(value)
|
|
869
|
+
? (value as Record<string, unknown>)
|
|
870
|
+
: undefined,
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
return manifests.get(dir);
|
|
874
|
+
};
|
|
875
|
+
const declares = (section: unknown, name: string): boolean =>
|
|
876
|
+
typeof section === "object" && section !== null && Object.hasOwn(section, name);
|
|
877
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
878
|
+
return {
|
|
879
|
+
imports,
|
|
880
|
+
devDependency: (path, name) => {
|
|
881
|
+
for (let dir = dirnamePath(joinPath(root, path)); ; dir = dirnamePath(dir)) {
|
|
882
|
+
const manifest = manifestAt(dir);
|
|
883
|
+
const other = OTHER_SECTIONS.some((one) => declares(manifest?.[one], name));
|
|
884
|
+
if (declares(manifest?.[DEV_SECTION], name) || other) {
|
|
885
|
+
return !other;
|
|
886
|
+
}
|
|
887
|
+
if (normalizePath(dir) === normalizePath(root) || dirnamePath(dir) === dir) {
|
|
888
|
+
return false;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
788
895
|
/**
|
|
789
896
|
* Reads every project of the run, its references and exemptions, and `extra`'s answer,
|
|
790
897
|
* while the project's view is open, and merges the projects into one matrix in the run's
|
|
@@ -841,8 +948,16 @@ function readRun<Extra>(
|
|
|
841
948
|
testFiles: resolved.testFilePaths,
|
|
842
949
|
evidence,
|
|
843
950
|
componentFiles: components.map(pathOf),
|
|
951
|
+
imports: new Map(
|
|
952
|
+
project.ownSourceFiles().flatMap((file) => {
|
|
953
|
+
const path = relativePath(targetRoot, file.fileName);
|
|
954
|
+
return path === undefined
|
|
955
|
+
? []
|
|
956
|
+
: [[path, file.imports.filter(isStringLiteralLikeNode).map((one) => one.text)]];
|
|
957
|
+
}),
|
|
958
|
+
),
|
|
844
959
|
componentWarnings: components.flatMap((file) =>
|
|
845
|
-
readComponent(file.originalText).warnings.map((warning) => ({
|
|
960
|
+
readComponent(file.originalText, extensionOf(file.fileName)).warnings.map((warning) => ({
|
|
846
961
|
path: pathOf(file),
|
|
847
962
|
line: lineOf(file.originalText, warning.offset),
|
|
848
963
|
reason: warning.reason,
|
|
@@ -867,6 +982,7 @@ function readRun<Extra>(
|
|
|
867
982
|
withOutside.flatMap((one) => one.references),
|
|
868
983
|
withOutside.flatMap((one) => one.projectRead.rooted.liveUnderReachability),
|
|
869
984
|
withOutside.flatMap((one) => one.testFiles),
|
|
985
|
+
testEvidence(host, targetRoot, withOutside),
|
|
870
986
|
);
|
|
871
987
|
const settled = settledSatisfaction(
|
|
872
988
|
withOutside.map((one) => ({ evidence: one.evidence, held: one.projectRead.held })),
|
|
@@ -888,8 +1004,9 @@ function readRun<Extra>(
|
|
|
888
1004
|
symbols: projectRead.held.symbols,
|
|
889
1005
|
references: [...made, ...evidenceReferences(projectRead.held.symbols, detected.inTestFiles)],
|
|
890
1006
|
roots: projectRead.rooted.liveUnderReachability,
|
|
891
|
-
testFiles: one.testFiles,
|
|
1007
|
+
testFiles: [...one.testFiles, ...supportFiles],
|
|
892
1008
|
...(supportFiles.length === 0 ? {} : { supportFiles }),
|
|
1009
|
+
...(projectRead.held.ambient.size === 0 ? {} : { ambient: projectRead.held.ambient }),
|
|
893
1010
|
...(projectRead.referenceOnly.length === 0
|
|
894
1011
|
? {}
|
|
895
1012
|
: { referenceOnly: projectRead.referenceOnly }),
|
|
@@ -910,6 +1027,7 @@ function readRun<Extra>(
|
|
|
910
1027
|
...exempt.filter(isUnansweredRecord).map((record) => record.id),
|
|
911
1028
|
],
|
|
912
1029
|
typeErrorSkips: projectRead.typeErrorSkips,
|
|
1030
|
+
unmappedEntries: projectRead.rooted.unmappedEntries,
|
|
913
1031
|
skipped: projectRead.skipped,
|
|
914
1032
|
componentFiles: one.componentFiles,
|
|
915
1033
|
componentWarnings: one.componentWarnings,
|
|
@@ -942,6 +1060,7 @@ function readRun<Extra>(
|
|
|
942
1060
|
notBuilt: read.notBuilt,
|
|
943
1061
|
unanswered: read.unanswered,
|
|
944
1062
|
componentsUnread: read.componentsUnread,
|
|
1063
|
+
librariesUnfound: read.librariesUnfound,
|
|
945
1064
|
conventionsApplied: read.conventionsApplied,
|
|
946
1065
|
conventionUses: read.conventionUses,
|
|
947
1066
|
memberDirs: read.memberDirs,
|
|
@@ -1174,7 +1293,8 @@ export function readEmitterRun(
|
|
|
1174
1293
|
* The workspace members whose dependencies the run judges: `read` holds those a built
|
|
1175
1294
|
* project holds a file below, `unbuilt` the others whose own directory, the nearest
|
|
1176
1295
|
* manifest's above the configuration, holds a configuration the run could not build. A
|
|
1177
|
-
* member no configuration holds a file of is in neither.
|
|
1296
|
+
* member no configuration holds a file of is in neither. A nested package no built project
|
|
1297
|
+
* holds a file below joins `unbuilt` the same way.
|
|
1178
1298
|
*/
|
|
1179
1299
|
function judgedMembers(
|
|
1180
1300
|
host: Host,
|
|
@@ -1198,6 +1318,16 @@ function judgedMembers(
|
|
|
1198
1318
|
judged.unbuilt.push(dir);
|
|
1199
1319
|
}
|
|
1200
1320
|
}
|
|
1321
|
+
for (const dir of read.nestedDirs) {
|
|
1322
|
+
const below = relativePath(root, dir);
|
|
1323
|
+
if (
|
|
1324
|
+
below !== undefined &&
|
|
1325
|
+
owners.has(dir) &&
|
|
1326
|
+
![...held].some((path) => path.startsWith(`${below}/`))
|
|
1327
|
+
) {
|
|
1328
|
+
judged.unbuilt.push(dir);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1201
1331
|
return judged;
|
|
1202
1332
|
}
|
|
1203
1333
|
|
|
@@ -1341,7 +1471,9 @@ function readAnalysis(
|
|
|
1341
1471
|
notBuilt: read.notBuilt,
|
|
1342
1472
|
unanswered: read.unanswered,
|
|
1343
1473
|
componentsUnread: read.componentsUnread,
|
|
1474
|
+
librariesUnfound: read.librariesUnfound,
|
|
1344
1475
|
conventionsApplied: read.conventionsApplied,
|
|
1476
|
+
unmappedEntries: unmappedEverywhere(read.projects),
|
|
1345
1477
|
},
|
|
1346
1478
|
configured: read.projects.map((one) => one.configured),
|
|
1347
1479
|
symbols,
|
|
@@ -1393,6 +1525,22 @@ function inputsOver(
|
|
|
1393
1525
|
};
|
|
1394
1526
|
}
|
|
1395
1527
|
|
|
1528
|
+
/** The manifest entries every project of the run reads back to no file, in the first project's order. */
|
|
1529
|
+
function unmappedEverywhere(
|
|
1530
|
+
projects: readonly { readonly unmappedEntries: readonly ManifestEntry[] }[],
|
|
1531
|
+
): readonly ManifestEntry[] {
|
|
1532
|
+
const key = (entry: ManifestEntry): string => `${entry.dir}\u0000${entry.member}`;
|
|
1533
|
+
const [first, ...rest] = projects;
|
|
1534
|
+
const others = rest.map((one) => new Set(one.unmappedEntries.map(key)));
|
|
1535
|
+
const found = new Map<string, ManifestEntry>();
|
|
1536
|
+
for (const entry of first?.unmappedEntries ?? []) {
|
|
1537
|
+
if (others.every((one) => one.has(key(entry)))) {
|
|
1538
|
+
found.set(key(entry), entry);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return [...found.values()];
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1396
1544
|
/** One project of a run: the name the run gives it and the file it was opened from. */
|
|
1397
1545
|
export interface RunProject {
|
|
1398
1546
|
readonly id: string;
|
|
@@ -1418,8 +1566,12 @@ interface RunFacts {
|
|
|
1418
1566
|
readonly componentWarnings: readonly ComponentWarning[];
|
|
1419
1567
|
/** Per configuration whose component files the run cannot read, the reason. */
|
|
1420
1568
|
readonly componentsUnread: readonly ComponentsUnread[];
|
|
1569
|
+
/** Per analyzed configuration, each library it names that the compiler cannot find. */
|
|
1570
|
+
readonly librariesUnfound: readonly LibraryUnfound[];
|
|
1421
1571
|
/** The convention rows the run applied. */
|
|
1422
1572
|
readonly conventionsApplied: readonly AppliedConvention[];
|
|
1573
|
+
/** The manifest entries no project of the run reads back to a file, each once. */
|
|
1574
|
+
readonly unmappedEntries: readonly ManifestEntry[];
|
|
1423
1575
|
}
|
|
1424
1576
|
|
|
1425
1577
|
/** One configuration whose component files a run cannot read. */
|
|
@@ -1428,6 +1580,13 @@ interface ComponentsUnread {
|
|
|
1428
1580
|
readonly reason: string;
|
|
1429
1581
|
}
|
|
1430
1582
|
|
|
1583
|
+
/** One library an analyzed configuration names that the compiler cannot find. */
|
|
1584
|
+
interface LibraryUnfound {
|
|
1585
|
+
readonly configFile: string;
|
|
1586
|
+
/** The compiler's error, rendered as a dropped configuration's is. */
|
|
1587
|
+
readonly error: string;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1431
1590
|
/** The findings of one run, beside what a report states about the run itself. */
|
|
1432
1591
|
export interface RunAnalysis {
|
|
1433
1592
|
readonly result: PassResult;
|
package/src/augmentations.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { Inventory, InventorySymbol } from "./inventory.ts";
|
|
11
|
+
import { referenceKey } from "./matrix.ts";
|
|
11
12
|
import type { Reference } from "./references.ts";
|
|
12
13
|
|
|
13
14
|
/** Whether one declaration is a `declare global` block: a module-level namespace named `global`. */
|
|
@@ -62,14 +63,24 @@ export function augmentationReferences(
|
|
|
62
63
|
}
|
|
63
64
|
return [];
|
|
64
65
|
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
// Two uses at one place of declarations one block holds read the block once there.
|
|
67
|
+
const made = new Set(found.map(referenceKey));
|
|
68
|
+
const reads = new Map<string, Reference>();
|
|
69
|
+
for (const reference of found) {
|
|
70
|
+
for (const to of holders(reference.to)) {
|
|
71
|
+
const read: Reference = {
|
|
72
|
+
from: reference.from,
|
|
73
|
+
to,
|
|
74
|
+
position: reference.position,
|
|
75
|
+
use: "read",
|
|
76
|
+
resolution: reference.resolution,
|
|
77
|
+
test: reference.test,
|
|
78
|
+
};
|
|
79
|
+
const key = referenceKey(read);
|
|
80
|
+
if (!made.has(key) && !reads.has(key)) {
|
|
81
|
+
reads.set(key, read);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [...reads.values()];
|
|
75
86
|
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The global names and ambient modules of the `@types` packages a compiler before version 6
|
|
3
|
+
* includes automatically: every package of each type root when the configuration lists no
|
|
4
|
+
* `types`. Version 6 and later include none, so the analysis program holds none of their
|
|
5
|
+
* declarations, and a use of one resolves to nothing; each such use is a use of the package.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
isClassDeclaration,
|
|
10
|
+
isEnumDeclaration,
|
|
11
|
+
isExportAssignment,
|
|
12
|
+
isExportDeclaration,
|
|
13
|
+
isFunctionDeclaration,
|
|
14
|
+
isIdentifier,
|
|
15
|
+
isImportDeclaration,
|
|
16
|
+
isImportEqualsDeclaration,
|
|
17
|
+
isInterfaceDeclaration,
|
|
18
|
+
isModuleBlock,
|
|
19
|
+
isModuleDeclaration,
|
|
20
|
+
isStringLiteral,
|
|
21
|
+
isTypeAliasDeclaration,
|
|
22
|
+
isVariableStatement,
|
|
23
|
+
SyntaxKind,
|
|
24
|
+
type Node,
|
|
25
|
+
type SourceFile,
|
|
26
|
+
type Statement,
|
|
27
|
+
} from "@typescript/native/ast";
|
|
28
|
+
import type { ParseFile } from "./configuration-files.ts";
|
|
29
|
+
import type { DirectoryEntry, Host } from "./host.ts";
|
|
30
|
+
import { dirnamePath, joinPath, resolvePath } from "./paths.ts";
|
|
31
|
+
|
|
32
|
+
const TYPE_ROOT = "node_modules/@types";
|
|
33
|
+
|
|
34
|
+
/** What the automatically included `@types` packages of one configuration declare. */
|
|
35
|
+
export interface AutomaticTypes {
|
|
36
|
+
/** Per global name, the packages that declare it. */
|
|
37
|
+
readonly globals: ReadonlyMap<string, readonly string[]>;
|
|
38
|
+
/** Per ambient module name, the packages that declare it. */
|
|
39
|
+
readonly modules: ReadonlyMap<string, readonly string[]>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const NONE: AutomaticTypes = { globals: new Map(), modules: new Map() };
|
|
43
|
+
|
|
44
|
+
/** What one installed type package declares, by its directory. */
|
|
45
|
+
interface PackageDeclarations {
|
|
46
|
+
readonly globals: readonly string[];
|
|
47
|
+
readonly modules: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The packages each host has read, so a run's configurations share one reading of a type
|
|
52
|
+
* root; a host reads one unchanging tree for its whole life.
|
|
53
|
+
*/
|
|
54
|
+
const READ = new WeakMap<Host, Map<string, PackageDeclarations>>();
|
|
55
|
+
|
|
56
|
+
/** Whether one file is a module: it imports, exports or assigns an export at its top level. */
|
|
57
|
+
function isModuleFile(file: SourceFile): boolean {
|
|
58
|
+
return file.statements.some(
|
|
59
|
+
(statement) =>
|
|
60
|
+
isImportDeclaration(statement) ||
|
|
61
|
+
isImportEqualsDeclaration(statement) ||
|
|
62
|
+
isExportDeclaration(statement) ||
|
|
63
|
+
isExportAssignment(statement) ||
|
|
64
|
+
((statement as { readonly modifiers?: readonly Node[] }).modifiers ?? []).some(
|
|
65
|
+
(modifier) => modifier.kind === SyntaxKind.ExportKeyword,
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The names one statement of a global scope declares, and the ambient modules it declares. */
|
|
71
|
+
function declared(statement: Statement, globals: string[], modules: string[]): void {
|
|
72
|
+
if (isVariableStatement(statement)) {
|
|
73
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
74
|
+
if (isIdentifier(declaration.name)) {
|
|
75
|
+
globals.push(declaration.name.text);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} else if (
|
|
79
|
+
isFunctionDeclaration(statement) ||
|
|
80
|
+
isClassDeclaration(statement) ||
|
|
81
|
+
isInterfaceDeclaration(statement) ||
|
|
82
|
+
isTypeAliasDeclaration(statement) ||
|
|
83
|
+
isEnumDeclaration(statement)
|
|
84
|
+
) {
|
|
85
|
+
if (statement.name !== undefined) {
|
|
86
|
+
globals.push(statement.name.text);
|
|
87
|
+
}
|
|
88
|
+
} else if (isModuleDeclaration(statement)) {
|
|
89
|
+
if (isStringLiteral(statement.name)) {
|
|
90
|
+
modules.push(statement.name.text);
|
|
91
|
+
} else {
|
|
92
|
+
globals.push(statement.name.text);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* What one declaration file declares in the global scope: its top level where it is no
|
|
99
|
+
* module, and the body of each `declare global` block where it is one.
|
|
100
|
+
*/
|
|
101
|
+
function fileDeclarations(file: SourceFile, globals: string[], modules: string[]): void {
|
|
102
|
+
if (!isModuleFile(file)) {
|
|
103
|
+
file.statements.forEach((statement) => {
|
|
104
|
+
declared(statement, globals, modules);
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
for (const statement of file.statements) {
|
|
109
|
+
if (
|
|
110
|
+
isModuleDeclaration(statement) &&
|
|
111
|
+
isIdentifier(statement.name) &&
|
|
112
|
+
statement.name.text === "global" &&
|
|
113
|
+
statement.body !== undefined &&
|
|
114
|
+
isModuleBlock(statement.body)
|
|
115
|
+
) {
|
|
116
|
+
statement.body.statements.forEach((inner) => {
|
|
117
|
+
declared(inner, globals, modules);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The entry declaration file of one installed type package's directory. */
|
|
124
|
+
function entryOf(host: Host, dir: string): string {
|
|
125
|
+
try {
|
|
126
|
+
const manifest: unknown = JSON.parse(host.readFile(joinPath(dir, "package.json")));
|
|
127
|
+
const named =
|
|
128
|
+
typeof manifest === "object" && manifest !== null
|
|
129
|
+
? ((manifest as Record<string, unknown>)["types"] ??
|
|
130
|
+
(manifest as Record<string, unknown>)["typings"])
|
|
131
|
+
: undefined;
|
|
132
|
+
if (typeof named === "string" && named !== "") {
|
|
133
|
+
const path = resolvePath(dir, named);
|
|
134
|
+
return path.endsWith(".ts") ? path : `${path}.d.ts`;
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// A package with no readable manifest is read from its index, as the compiler reads it.
|
|
138
|
+
}
|
|
139
|
+
return joinPath(dir, "index.d.ts");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Every global name and ambient module one installed type package declares: in its entry
|
|
144
|
+
* file and each file a `/// <reference path>` of a file read reaches, inside the package.
|
|
145
|
+
*/
|
|
146
|
+
function packageDeclarations(host: Host, dir: string, parse: ParseFile): PackageDeclarations {
|
|
147
|
+
const known = READ.get(host) ?? new Map<string, PackageDeclarations>();
|
|
148
|
+
READ.set(host, known);
|
|
149
|
+
const held = known.get(dir);
|
|
150
|
+
if (held !== undefined) {
|
|
151
|
+
return held;
|
|
152
|
+
}
|
|
153
|
+
const globals: string[] = [];
|
|
154
|
+
const modules: string[] = [];
|
|
155
|
+
const seen = new Set<string>();
|
|
156
|
+
const pending = [entryOf(host, dir)];
|
|
157
|
+
for (let path = pending.pop(); path !== undefined; path = pending.pop()) {
|
|
158
|
+
if (seen.has(path) || !path.startsWith(`${dir}/`)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
seen.add(path);
|
|
162
|
+
let text: string;
|
|
163
|
+
try {
|
|
164
|
+
text = host.readFile(path);
|
|
165
|
+
} catch {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const file = parse(path, text);
|
|
169
|
+
fileDeclarations(file, globals, modules);
|
|
170
|
+
for (const reference of file.referencedFiles) {
|
|
171
|
+
pending.push(resolvePath(dirnamePath(path), reference.fileName));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const found = { globals, modules };
|
|
175
|
+
known.set(dir, found);
|
|
176
|
+
return found;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* What the `@types` packages a compiler before version 6 would include for one
|
|
181
|
+
* configuration declare: none where its options list `types`, else every package of each
|
|
182
|
+
* `node_modules/@types` directory at or above the configuration's directory, the nearest
|
|
183
|
+
* holding a name winning.
|
|
184
|
+
*/
|
|
185
|
+
export function automaticTypes(
|
|
186
|
+
host: Host,
|
|
187
|
+
configFile: string,
|
|
188
|
+
options: Readonly<Record<string, unknown>>,
|
|
189
|
+
parse: ParseFile,
|
|
190
|
+
): AutomaticTypes {
|
|
191
|
+
if (options["types"] !== undefined) {
|
|
192
|
+
return NONE;
|
|
193
|
+
}
|
|
194
|
+
const globals = new Map<string, string[]>();
|
|
195
|
+
const modules = new Map<string, string[]>();
|
|
196
|
+
const add = (into: Map<string, string[]>, name: string, named: string): void => {
|
|
197
|
+
const held = into.get(name) ?? [];
|
|
198
|
+
if (!held.includes(named)) {
|
|
199
|
+
into.set(name, [...held, named]);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
const read = new Set<string>();
|
|
203
|
+
for (const root of typeRoots(configFile, options)) {
|
|
204
|
+
let entries: readonly DirectoryEntry[];
|
|
205
|
+
try {
|
|
206
|
+
entries = host.readDirectory(root);
|
|
207
|
+
} catch {
|
|
208
|
+
entries = [];
|
|
209
|
+
}
|
|
210
|
+
for (const entry of entries) {
|
|
211
|
+
if (read.has(entry.name) || host.kindOf(joinPath(root, entry.name)) !== "directory") {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
read.add(entry.name);
|
|
215
|
+
const named = `@types/${entry.name}`;
|
|
216
|
+
const found = packageDeclarations(host, joinPath(root, entry.name), parse);
|
|
217
|
+
found.globals.forEach((name) => {
|
|
218
|
+
add(globals, name, named);
|
|
219
|
+
});
|
|
220
|
+
found.modules.forEach((name) => {
|
|
221
|
+
add(modules, name, named);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { globals, modules };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The type roots of one configuration that a package manager installs into: those its
|
|
230
|
+
* `typeRoots` lists, else each `node_modules/@types` directory at or above its directory.
|
|
231
|
+
*/
|
|
232
|
+
function typeRoots(configFile: string, options: Readonly<Record<string, unknown>>): string[] {
|
|
233
|
+
const listed = options["typeRoots"];
|
|
234
|
+
if (Array.isArray(listed)) {
|
|
235
|
+
return listed.filter(
|
|
236
|
+
(root): root is string => typeof root === "string" && root.endsWith(`/${TYPE_ROOT}`),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
const roots: string[] = [];
|
|
240
|
+
for (let dir = dirnamePath(configFile); ; dir = dirnamePath(dir)) {
|
|
241
|
+
roots.push(joinPath(dir, TYPE_ROOT));
|
|
242
|
+
if (dirnamePath(dir) === dir) {
|
|
243
|
+
return roots;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|