@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.11
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/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +181 -341
- package/dist/src/augmentations.js +106 -13
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +6 -82
- package/dist/src/configuration-files.js +28 -176
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +73 -3
- package/dist/src/conventions.js +36 -66
- package/dist/src/dependencies.js +103 -40
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +11 -27
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +139 -72
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +5 -5
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +13 -10
- package/dist/src/findings/non-code-artifacts.js +7 -8
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +41 -32
- package/dist/src/findings/visibility-narrowing.js +19 -16
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +7 -4
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/graph.js +5 -3
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory.js +2 -21
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +23 -65
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +76 -4
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +1 -0
- package/dist/src/references.js +48 -30
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -729
- package/dist/src/roots.js +273 -87
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +595 -21
- package/dist/src/session.js +34 -29
- package/dist/src/source-files.js +1 -1
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +33 -51
- package/dist/src/sweep.js +7 -9
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +8 -11
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +166 -126
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +10 -7
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +62 -64
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +245 -502
- package/src/augmentations.ts +135 -17
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +31 -302
- package/src/configuration-files.ts +34 -179
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +85 -3
- package/src/conventions.ts +46 -84
- package/src/dependencies.ts +118 -46
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +12 -38
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +14 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +181 -84
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +7 -7
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +13 -11
- package/src/findings/non-code-artifacts.ts +9 -11
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +45 -32
- package/src/findings/visibility-narrowing.ts +20 -17
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +7 -4
- package/src/git-ignore.ts +50 -26
- package/src/graph.ts +5 -3
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory.ts +2 -20
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +33 -76
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +122 -4
- package/src/mdx-entries.ts +3 -26
- package/src/outside-tests.ts +1 -1
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +1 -0
- package/src/references.ts +52 -31
- package/src/report-schema.ts +68 -0
- package/src/report.ts +101 -224
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +339 -104
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +763 -22
- package/src/session.ts +70 -54
- package/src/source-files.ts +1 -1
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +43 -58
- package/src/sweep.ts +23 -9
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +17 -13
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +18 -288
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +224 -162
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +10 -11
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +74 -64
- package/dist/src/base-url.js +0 -18
- package/dist/src/discover.js +0 -468
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/src/config.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ResolvedConfig } from "./contract/config.generated.ts";
|
|
2
|
+
import { SEVERITIES, type Severity } from "./contract/contract.generated.ts";
|
|
3
|
+
import { LANGUAGE } from "./product.ts";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* The name of the repository configuration at the target root, which is also the
|
|
@@ -7,266 +9,32 @@ import { CONTRACT_VERSION } from "./version.ts";
|
|
|
7
9
|
*/
|
|
8
10
|
export const REPOSITORY_DOCUMENT = "deadset.json";
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
* Whether the target is an application, whose every caller is in the analyzed
|
|
12
|
-
* graph, or a library, whose published API has callers outside it.
|
|
13
|
-
*/
|
|
14
|
-
export type TargetKind = "application" | "library";
|
|
12
|
+
export type { PartialConfig, ResolvedConfig } from "./contract/config.generated.ts";
|
|
15
13
|
|
|
16
|
-
/**
|
|
17
|
-
export type
|
|
14
|
+
/** Whether the target is an application or a library. */
|
|
15
|
+
export type TargetKind = Extract<ResolvedConfig["target"]["kind"], string>;
|
|
16
|
+
export type DeclarationEntry = ResolvedConfig["ts"]["serializers"][number];
|
|
17
|
+
export type LifecycleContract = ResolvedConfig["ts"]["lifecycle_contracts"][number];
|
|
18
|
+
export type BuildConfiguration = ResolvedConfig["analysis"]["configurations"][number];
|
|
19
|
+
export type ProjectConfiguration = Extract<BuildConfiguration, { readonly project: string }>;
|
|
20
|
+
export type TemplateDelimiters = ResolvedConfig["analysis"]["template_delimiters"];
|
|
21
|
+
export type Sort = ResolvedConfig["reporters"]["sort"];
|
|
22
|
+
export type Cascade = ResolvedConfig["reporters"]["cascade"];
|
|
18
23
|
|
|
19
|
-
/** The kind a target kind setting gives
|
|
20
|
-
export function kindOf(
|
|
21
|
-
return typeof
|
|
24
|
+
/** The kind a target kind setting gives this analyzer's declarations. */
|
|
25
|
+
export function kindOf(kind: ResolvedConfig["target"]["kind"]): TargetKind {
|
|
26
|
+
return typeof kind === "string" ? kind : kind[LANGUAGE];
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
/** The lowest reachability class a finding must reach to be reported. */
|
|
25
|
-
export type Confidence = "certain" | "probable" | "possible";
|
|
26
|
-
|
|
27
|
-
/** Whether declarations in generated files are analyzed. */
|
|
28
|
-
export type GeneratedFiles = "exclude" | "include";
|
|
29
|
-
|
|
30
|
-
/** How a reference from a consumer's test file counts. */
|
|
31
|
-
export type ConsumerTests = "test" | "production";
|
|
32
|
-
|
|
33
|
-
/** One language an analysis covers. */
|
|
34
|
-
export type Language = "go" | "ts";
|
|
35
|
-
|
|
36
|
-
/** What a finding of one issue kind does to a run. */
|
|
37
|
-
export type Severity = "allow" | "warn" | "deny";
|
|
38
|
-
|
|
39
|
-
/** The severities ranked, the strongest highest. */
|
|
40
|
-
const SEVERITY_RANK: Readonly<Record<Severity, number>> = { allow: 0, warn: 1, deny: 2 };
|
|
41
|
-
|
|
42
29
|
/** Whether a finding at one severity fails a run under the failing severity. */
|
|
43
30
|
export function fails(severity: Severity, failOn: Severity): boolean {
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** One rendering of the finding list. */
|
|
48
|
-
export type Format = "text" | "json" | "github" | "sarif" | "template";
|
|
49
|
-
|
|
50
|
-
/** The order findings are rendered in. */
|
|
51
|
-
export type Sort = "position" | "size";
|
|
52
|
-
|
|
53
|
-
/** How much of a dead component a rendering names. */
|
|
54
|
-
export type Cascade = "roots" | "full";
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* A platform configuration: an operating system, an architecture and the build
|
|
58
|
-
* tags in effect. It is the shape a language built under build constraints reads,
|
|
59
|
-
* and this analyzer leaves it to that analysis.
|
|
60
|
-
*/
|
|
61
|
-
interface PlatformConfiguration {
|
|
62
|
-
readonly shape: "platform";
|
|
63
|
-
readonly id: string;
|
|
64
|
-
readonly os: string;
|
|
65
|
-
readonly arch: string;
|
|
66
|
-
readonly tags: readonly string[];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* A project configuration: one compiler configuration file, as a path below the
|
|
71
|
-
* target root with `/` between segments.
|
|
72
|
-
*/
|
|
73
|
-
export interface ProjectConfiguration {
|
|
74
|
-
readonly shape: "project";
|
|
75
|
-
readonly id: string;
|
|
76
|
-
readonly project: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* One entry of the build matrix: a configuration the target builds under, in
|
|
81
|
-
* exactly one of the two shapes the matrix admits.
|
|
82
|
-
*/
|
|
83
|
-
export type BuildConfiguration = PlatformConfiguration | ProjectConfiguration;
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The pair of action delimiters a template is parsed with. Both members are
|
|
87
|
-
* required once a document names the object, so the pair is one setting rather
|
|
88
|
-
* than two.
|
|
89
|
-
*/
|
|
90
|
-
export interface TemplateDelimiters {
|
|
91
|
-
readonly left: string;
|
|
92
|
-
readonly right: string;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** How the analysis is scoped and filtered. */
|
|
96
|
-
export interface Analysis {
|
|
97
|
-
readonly languages: readonly Language[];
|
|
98
|
-
readonly minConfidence: Confidence;
|
|
99
|
-
readonly generatedFiles: GeneratedFiles;
|
|
100
|
-
readonly consumerTests: ConsumerTests;
|
|
101
|
-
readonly configurations: readonly BuildConfiguration[];
|
|
102
|
-
readonly matrixComplete: boolean;
|
|
103
|
-
readonly templateDirs: readonly string[];
|
|
104
|
-
readonly templateDelimiters: TemplateDelimiters;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** How findings are rendered. */
|
|
108
|
-
interface Reporters {
|
|
109
|
-
readonly formats: readonly Format[];
|
|
110
|
-
readonly sort: Sort;
|
|
111
|
-
readonly cascade: Cascade;
|
|
112
|
-
readonly maxFindings: number;
|
|
113
|
-
readonly failOn: Severity;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* One declaration a configuration entry names, in exactly one of three shapes: a
|
|
118
|
-
* declaration of the analyzed program by its stable symbol reference, a declaration a
|
|
119
|
-
* module exports by the bare specifier an import writes for the module and the
|
|
120
|
-
* declaration's path from its exports, or a declaration of the global scope by its
|
|
121
|
-
* path from that scope. A path joins the name and each member with dots and spells a
|
|
122
|
-
* static member with `:static` after it.
|
|
123
|
-
*/
|
|
124
|
-
export type DeclarationEntry =
|
|
125
|
-
| { readonly shape: "symbol"; readonly symbol: string }
|
|
126
|
-
| { readonly shape: "module"; readonly module: string; readonly name: string }
|
|
127
|
-
| { readonly shape: "global"; readonly global: string };
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* One framework's lifecycle contract: the declarations whose use makes a class one of
|
|
131
|
-
* the framework's components, the classes whose subclasses are its components, and the
|
|
132
|
-
* names of the members the framework calls on a component, each spelled as the last
|
|
133
|
-
* component of a member's stable symbol reference.
|
|
134
|
-
*/
|
|
135
|
-
export interface LifecycleContract {
|
|
136
|
-
readonly components: readonly DeclarationEntry[];
|
|
137
|
-
readonly bases: readonly DeclarationEntry[];
|
|
138
|
-
readonly members: readonly string[];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* One analyzer the orchestrator may run, in exactly one of two shapes: an installed
|
|
143
|
-
* analyzer, named by its command alone, or an acquirable one, which also names the
|
|
144
|
-
* artifact acquisition fetches and verifies. This analyzer validates and prints the
|
|
145
|
-
* list and runs nothing from it.
|
|
146
|
-
*/
|
|
147
|
-
export type Provider =
|
|
148
|
-
| {
|
|
149
|
-
readonly shape: "installed";
|
|
150
|
-
readonly name: string;
|
|
151
|
-
readonly languages: readonly Language[];
|
|
152
|
-
readonly command: string;
|
|
153
|
-
}
|
|
154
|
-
| {
|
|
155
|
-
readonly shape: "acquirable";
|
|
156
|
-
readonly name: string;
|
|
157
|
-
readonly languages: readonly Language[];
|
|
158
|
-
readonly command: string;
|
|
159
|
-
readonly source: string;
|
|
160
|
-
readonly version: string;
|
|
161
|
-
readonly digest: string;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/** The analyzers the orchestrator runs. */
|
|
165
|
-
interface Providers {
|
|
166
|
-
readonly analyzers: readonly Provider[];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/** The section the TypeScript analyzer owns. */
|
|
170
|
-
export interface TSSection {
|
|
171
|
-
readonly testFiles: readonly string[];
|
|
172
|
-
readonly entryFiles: readonly string[];
|
|
173
|
-
readonly componentExtensions: readonly string[];
|
|
174
|
-
readonly disabledConventions: readonly string[];
|
|
175
|
-
readonly injectionRegistrations: readonly DeclarationEntry[];
|
|
176
|
-
readonly lifecycleContracts: readonly LifecycleContract[];
|
|
177
|
-
readonly serializers: readonly DeclarationEntry[];
|
|
178
|
-
/** Whether the maintainer declares the roots the analysis finds every root the target has. */
|
|
179
|
-
readonly rootsComplete: boolean;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* The resolved configuration: the closed key list of the Contract's
|
|
184
|
-
* configuration schema, decoded, with every default applied.
|
|
185
|
-
*
|
|
186
|
-
* `targetKind` is empty exactly in the configuration every default states, which
|
|
187
|
-
* is not a configuration a run may use: the field has no default and resolution
|
|
188
|
-
* refuses a result that carries none.
|
|
189
|
-
*
|
|
190
|
-
* `severity` is a map rather than an object because its keys come from a
|
|
191
|
-
* document, and an object keyed by document text answers for names it never
|
|
192
|
-
* held. The order it is written in is the emitter's, not the map's.
|
|
193
|
-
*/
|
|
194
|
-
export interface Config {
|
|
195
|
-
readonly contractVersion: string;
|
|
196
|
-
readonly targetKind: TargetKindSetting | "";
|
|
197
|
-
readonly analysis: Analysis;
|
|
198
|
-
readonly consumersComplete: boolean;
|
|
199
|
-
readonly rootPatterns: readonly string[];
|
|
200
|
-
readonly severity: ReadonlyMap<string, Severity>;
|
|
201
|
-
readonly exemptionsDisabled: readonly string[];
|
|
202
|
-
readonly reporters: Reporters;
|
|
203
|
-
readonly providers: Providers;
|
|
204
|
-
readonly ts: TSSection;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* The configuration every documented default states. `targetKind` is empty: the
|
|
209
|
-
* field has no default and is never inferred.
|
|
210
|
-
*/
|
|
211
|
-
export function defaultConfig(): Config {
|
|
212
|
-
return {
|
|
213
|
-
contractVersion: CONTRACT_VERSION,
|
|
214
|
-
targetKind: "",
|
|
215
|
-
analysis: {
|
|
216
|
-
languages: [],
|
|
217
|
-
minConfidence: "probable",
|
|
218
|
-
generatedFiles: "exclude",
|
|
219
|
-
consumerTests: "test",
|
|
220
|
-
configurations: [],
|
|
221
|
-
matrixComplete: false,
|
|
222
|
-
templateDirs: [],
|
|
223
|
-
templateDelimiters: { left: "{{", right: "}}" },
|
|
224
|
-
},
|
|
225
|
-
consumersComplete: false,
|
|
226
|
-
rootPatterns: [],
|
|
227
|
-
severity: new Map(),
|
|
228
|
-
exemptionsDisabled: [],
|
|
229
|
-
reporters: {
|
|
230
|
-
formats: ["text"],
|
|
231
|
-
sort: "position",
|
|
232
|
-
cascade: "roots",
|
|
233
|
-
maxFindings: 0,
|
|
234
|
-
failOn: "deny",
|
|
235
|
-
},
|
|
236
|
-
providers: {
|
|
237
|
-
analyzers: [
|
|
238
|
-
{ shape: "installed", name: "deadset-go", languages: ["go"], command: "deadset-go" },
|
|
239
|
-
{ shape: "installed", name: "deadset-ts", languages: ["ts"], command: "deadset-ts" },
|
|
240
|
-
],
|
|
241
|
-
},
|
|
242
|
-
ts: {
|
|
243
|
-
testFiles: [
|
|
244
|
-
"**/*.test.{ts,tsx,mts,cts}",
|
|
245
|
-
"**/*.test-d.{ts,tsx,mts,cts}",
|
|
246
|
-
"**/*.spec-d.{ts,tsx,mts,cts}",
|
|
247
|
-
"**/*.spec.*",
|
|
248
|
-
"**/__tests__/**",
|
|
249
|
-
"**/__mocks__/**",
|
|
250
|
-
"**/test-d/**",
|
|
251
|
-
],
|
|
252
|
-
entryFiles: [],
|
|
253
|
-
componentExtensions: [".vue", ".svelte", ".astro"],
|
|
254
|
-
disabledConventions: [],
|
|
255
|
-
injectionRegistrations: [],
|
|
256
|
-
lifecycleContracts: [],
|
|
257
|
-
serializers: [],
|
|
258
|
-
rootsComplete: false,
|
|
259
|
-
},
|
|
260
|
-
};
|
|
31
|
+
return SEVERITIES.indexOf(severity) >= SEVERITIES.indexOf(failOn);
|
|
261
32
|
}
|
|
262
33
|
|
|
263
34
|
/** Where a resolved setting came from, in the order the sources outrank each other. */
|
|
264
35
|
export type Source = "flag" | "repository" | "central" | "default";
|
|
265
36
|
|
|
266
|
-
/**
|
|
267
|
-
* One setting's source together with the file or flag that supplied it. `label`
|
|
268
|
-
* is empty exactly when `source` is `"default"`.
|
|
269
|
-
*/
|
|
37
|
+
/** One setting's source with the file or flag that supplied it, empty for a default. */
|
|
270
38
|
export interface Origin {
|
|
271
39
|
readonly source: Source;
|
|
272
40
|
readonly label: string;
|
|
@@ -281,63 +49,24 @@ export function renderOrigin(origin: Origin): string {
|
|
|
281
49
|
}
|
|
282
50
|
|
|
283
51
|
/**
|
|
284
|
-
* The origin of each resolved setting, keyed by the setting's dotted path. A
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* empty.
|
|
52
|
+
* The origin of each resolved setting, keyed by the setting's dotted path. A section
|
|
53
|
+
* has no entry; a keyed section carries one entry per member it names, or one for
|
|
54
|
+
* itself when it is empty.
|
|
288
55
|
*/
|
|
289
56
|
export type Provenance = ReadonlyMap<string, Origin>;
|
|
290
57
|
|
|
291
|
-
/**
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
* supplied each setting `flags` names, keyed by dotted setting path.
|
|
296
|
-
*/
|
|
297
|
-
export interface Inputs {
|
|
298
|
-
readonly flags?: string;
|
|
299
|
-
readonly repository?: string;
|
|
300
|
-
readonly central?: string;
|
|
301
|
-
readonly repositoryLabel?: string;
|
|
302
|
-
readonly centralLabel?: string;
|
|
303
|
-
readonly flagLabels?: ReadonlyMap<string, string>;
|
|
58
|
+
/** One configuration document: its text and the path it was read from. */
|
|
59
|
+
export interface Document {
|
|
60
|
+
readonly text: string;
|
|
61
|
+
readonly label: string;
|
|
304
62
|
}
|
|
305
63
|
|
|
306
|
-
/** Which refusal a {@link ConfigError} carries. */
|
|
307
|
-
type ErrorKind =
|
|
308
|
-
/**
|
|
309
|
-
* A document that is not one JSON instance of the closed key list: a syntax
|
|
310
|
-
* error, a value of the wrong type, a value outside the closed set a key
|
|
311
|
-
* declares, or a member written twice at one level.
|
|
312
|
-
*/
|
|
313
|
-
| "malformed"
|
|
314
|
-
/**
|
|
315
|
-
* A key the closed key list does not declare, or a severity key naming no live
|
|
316
|
-
* kind or a kind whose severity the Contract fixes.
|
|
317
|
-
*/
|
|
318
|
-
| "unimplemented-key"
|
|
319
|
-
/** A resolved configuration no source supplied a target kind for. */
|
|
320
|
-
| "missing-target-kind";
|
|
321
|
-
|
|
322
64
|
/**
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* one caller that turns it into an exit code is the command line.
|
|
65
|
+
* The configuration sources one invocation carries. `flags` holds the text each
|
|
66
|
+
* setting option was given, by the setting's dotted path.
|
|
326
67
|
*/
|
|
327
|
-
export
|
|
328
|
-
|
|
329
|
-
readonly
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* The key or field the message names, spelled as the document spells it. It is
|
|
333
|
-
* empty when the refusal names none.
|
|
334
|
-
*/
|
|
335
|
-
readonly key: string;
|
|
336
|
-
|
|
337
|
-
constructor(kind: ErrorKind, key: string, message: string) {
|
|
338
|
-
super(message);
|
|
339
|
-
this.name = "ConfigError";
|
|
340
|
-
this.kind = kind;
|
|
341
|
-
this.key = key;
|
|
342
|
-
}
|
|
68
|
+
export interface Inputs {
|
|
69
|
+
readonly flags?: ReadonlyMap<string, string>;
|
|
70
|
+
readonly repository?: Document;
|
|
71
|
+
readonly central?: Document;
|
|
343
72
|
}
|
|
@@ -19,10 +19,19 @@ import {
|
|
|
19
19
|
type SourceFile,
|
|
20
20
|
} from "@typescript/native/ast";
|
|
21
21
|
import type { Host } from "./host.ts";
|
|
22
|
-
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
22
|
+
import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "./paths.ts";
|
|
23
23
|
import type { SourceFiles } from "./source-files.ts";
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
import { bytewise } from "./text/compare.ts";
|
|
25
|
+
import { isRecord } from "./json-document.ts";
|
|
26
|
+
import { filesIn } from "./fs/tree.ts";
|
|
27
|
+
import {
|
|
28
|
+
DEPENDENCY_SECTIONS,
|
|
29
|
+
holdsManifest,
|
|
30
|
+
inInstalledPackages,
|
|
31
|
+
manifestAt,
|
|
32
|
+
readJSON,
|
|
33
|
+
} from "./fs/manifest.ts";
|
|
34
|
+
import { shellWords } from "./text/shell.ts";
|
|
26
35
|
|
|
27
36
|
/** A configuration module's name, each form with the spelling a root reports it by. */
|
|
28
37
|
const MODULE_FORMS: readonly { readonly name: RegExp; readonly form: string }[] = [
|
|
@@ -37,14 +46,6 @@ const MODULE_FORMS: readonly { readonly name: RegExp; readonly form: string }[]
|
|
|
37
46
|
/** A JSON configuration file's name. */
|
|
38
47
|
const DOCUMENT_NAME = /^(?:[\w-]+(?:\.[\w-]+)?\.config\.json|\.[\w-]+rc(?:\.json)?)$/u;
|
|
39
48
|
|
|
40
|
-
/** The manifest members a dependency is declared in. */
|
|
41
|
-
const DEPENDENCY_MEMBERS = [
|
|
42
|
-
"dependencies",
|
|
43
|
-
"devDependencies",
|
|
44
|
-
"peerDependencies",
|
|
45
|
-
"optionalDependencies",
|
|
46
|
-
] as const;
|
|
47
|
-
|
|
48
49
|
/** The prefixes a string names a file with, relative to the file that writes it. */
|
|
49
50
|
const RELATIVE_PREFIXES = ["./", "../"] as const;
|
|
50
51
|
|
|
@@ -72,9 +73,6 @@ export const RESOLVED_EXTENSIONS = [
|
|
|
72
73
|
".cjs",
|
|
73
74
|
] as const;
|
|
74
75
|
|
|
75
|
-
/** The directory a package manager installs packages into, whose files are no project's own. */
|
|
76
|
-
const MODULES_DIR = "node_modules";
|
|
77
|
-
|
|
78
76
|
/** The file's name, the last segment of its path. */
|
|
79
77
|
function basename(path: string): string {
|
|
80
78
|
return path.slice(path.lastIndexOf("/") + 1);
|
|
@@ -82,11 +80,8 @@ function basename(path: string): string {
|
|
|
82
80
|
|
|
83
81
|
/** Whether one path is the project's own: below the target root and in no `node_modules`. */
|
|
84
82
|
function isProjectPath(root: string, path: string): boolean {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
89
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
const below = relativePath(root, path);
|
|
84
|
+
return below !== undefined && !inInstalledPackages(below);
|
|
90
85
|
}
|
|
91
86
|
|
|
92
87
|
/** The form one file name is as a configuration module, or undefined where it is none. */
|
|
@@ -126,120 +121,13 @@ const CONFIG_FLAGS: ReadonlySet<string> = new Set(["--config", "-c"]);
|
|
|
126
121
|
/** The extensions of a module a configuration flag may name. */
|
|
127
122
|
const MODULE_EXTENSIONS = [".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"] as const;
|
|
128
123
|
|
|
129
|
-
/** The characters that end a word outside quotes and separate nothing: blanks. */
|
|
130
|
-
// Stryker disable next-line Regex: its \v to \V mutant is no valid escape under the u flag, so the whole module fails to parse
|
|
131
|
-
const BLANK = /[ \t\r\f\v]/u;
|
|
132
|
-
|
|
133
|
-
/** The characters that end a word outside quotes and begin an operator: newline included. */
|
|
134
|
-
const OPERATOR = /[\n;&|()<>]/u;
|
|
135
|
-
|
|
136
|
-
/** The characters a backslash escapes inside double quotes; before any other it stands for itself. */
|
|
137
|
-
const DOUBLE_QUOTE_ESCAPES = new Set(["$", "`", '"', "\\"]);
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* The words of one command line, as POSIX token recognition reads quotes, backslashes,
|
|
141
|
-
* line continuations, comments and operators. A word an expansion may change (an unquoted
|
|
142
|
-
* `$`, backquote, glob character or leading `~`, or a `$` or backquote in double quotes)
|
|
143
|
-
* is computed, and so is the rest of a line whose quote is never closed. A computed word
|
|
144
|
-
* and each operator are undefined, so a flag's file is never read across them.
|
|
145
|
-
*/
|
|
146
|
-
function shellWords(command: string): readonly (string | undefined)[] {
|
|
147
|
-
const words: (string | undefined)[] = [];
|
|
148
|
-
let word: string | undefined;
|
|
149
|
-
let computed = false;
|
|
150
|
-
const end = (): void => {
|
|
151
|
-
if (word !== undefined) {
|
|
152
|
-
words.push(computed ? undefined : word);
|
|
153
|
-
}
|
|
154
|
-
word = undefined;
|
|
155
|
-
computed = false;
|
|
156
|
-
};
|
|
157
|
-
for (let at = 0; at < command.length; at += 1) {
|
|
158
|
-
const char = command.charAt(at);
|
|
159
|
-
if (char === "\\" && command.charAt(at + 1) === "\n") {
|
|
160
|
-
at += 1;
|
|
161
|
-
} else if (BLANK.test(char)) {
|
|
162
|
-
end();
|
|
163
|
-
} else if (OPERATOR.test(char)) {
|
|
164
|
-
end();
|
|
165
|
-
words.push(undefined);
|
|
166
|
-
} else if (char === "#" && word === undefined) {
|
|
167
|
-
const newline = command.indexOf("\n", at);
|
|
168
|
-
at = newline < 0 ? command.length : newline - 1;
|
|
169
|
-
} else if (char === "\\") {
|
|
170
|
-
at += 1;
|
|
171
|
-
word = (word ?? "") + command.charAt(at);
|
|
172
|
-
} else if (char === "'" || char === '"') {
|
|
173
|
-
const close = closingQuote(command, at);
|
|
174
|
-
if (close < 0) {
|
|
175
|
-
word ??= "";
|
|
176
|
-
computed = true;
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
const quoted =
|
|
180
|
-
char === "'"
|
|
181
|
-
? { text: command.slice(at + 1, close), computed: false }
|
|
182
|
-
: doubleQuoted(command.slice(at + 1, close));
|
|
183
|
-
word = (word ?? "") + quoted.text;
|
|
184
|
-
computed ||= quoted.computed;
|
|
185
|
-
at = close;
|
|
186
|
-
} else {
|
|
187
|
-
computed ||= /[$`*?[]/u.test(char) || (char === "~" && word === undefined);
|
|
188
|
-
word = (word ?? "") + char;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
end();
|
|
192
|
-
return words;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** The text between one pair of double quotes, unescaped, and whether an expansion may change it. */
|
|
196
|
-
function doubleQuoted(inner: string): { readonly text: string; readonly computed: boolean } {
|
|
197
|
-
let text = "";
|
|
198
|
-
let computed = false;
|
|
199
|
-
for (let at = 0; at < inner.length; at += 1) {
|
|
200
|
-
const char = inner.charAt(at);
|
|
201
|
-
const next = inner.charAt(at + 1);
|
|
202
|
-
if (char === "\\" && next === "\n") {
|
|
203
|
-
at += 1;
|
|
204
|
-
} else if (char === "\\" && DOUBLE_QUOTE_ESCAPES.has(next)) {
|
|
205
|
-
text += next;
|
|
206
|
-
at += 1;
|
|
207
|
-
} else {
|
|
208
|
-
computed ||= char === "$" || char === "`";
|
|
209
|
-
text += char;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
return { text, computed };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** The index of the quote that closes the one at `open`, or -1 where none does. */
|
|
216
|
-
function closingQuote(command: string, open: number): number {
|
|
217
|
-
const quote = command.charAt(open);
|
|
218
|
-
for (let at = open + 1; at < command.length; at += 1) {
|
|
219
|
-
const char = command.charAt(at);
|
|
220
|
-
if (char === quote) {
|
|
221
|
-
return at;
|
|
222
|
-
}
|
|
223
|
-
if (quote === '"' && char === "\\") {
|
|
224
|
-
at += 1;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return -1;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
124
|
/**
|
|
231
125
|
* The files the scripts of the manifest in one directory name after a configuration flag,
|
|
232
126
|
* `--config <file>`, `-c <file>` or `--config=<file>`, each a JSON file or a module below
|
|
233
127
|
* the target root and in no `node_modules`.
|
|
234
128
|
*/
|
|
235
129
|
function scriptConfigurations(host: Host, root: string, dir: string): readonly string[] {
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
const manifest: unknown = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
|
|
239
|
-
scripts = isRecord(manifest) ? manifest["scripts"] : undefined;
|
|
240
|
-
} catch {
|
|
241
|
-
return [];
|
|
242
|
-
}
|
|
130
|
+
const scripts = manifestAt(host, dir)?.["scripts"];
|
|
243
131
|
if (!isRecord(scripts)) {
|
|
244
132
|
return [];
|
|
245
133
|
}
|
|
@@ -270,15 +158,6 @@ function scriptConfigurations(host: Host, root: string, dir: string): readonly s
|
|
|
270
158
|
return found;
|
|
271
159
|
}
|
|
272
160
|
|
|
273
|
-
/** The decoded value of one JSON file, or undefined where it cannot be read or parsed. */
|
|
274
|
-
function readDocument(host: Host, path: string): unknown {
|
|
275
|
-
try {
|
|
276
|
-
return JSON.parse(host.readFile(path)) as unknown;
|
|
277
|
-
} catch {
|
|
278
|
-
return undefined;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
161
|
/** The configuration files one project holds and sits beside. */
|
|
283
162
|
interface ConfigurationFiles {
|
|
284
163
|
/**
|
|
@@ -346,10 +225,10 @@ export function configurationFiles(
|
|
|
346
225
|
): ConfigurationFiles {
|
|
347
226
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
348
227
|
const manifests = new Map<string, boolean>();
|
|
349
|
-
const
|
|
228
|
+
const manifestHeld = (dir: string): boolean => {
|
|
350
229
|
let held = manifests.get(dir);
|
|
351
230
|
if (held === undefined) {
|
|
352
|
-
held = host
|
|
231
|
+
held = holdsManifest(host, dir);
|
|
353
232
|
manifests.set(dir, held);
|
|
354
233
|
}
|
|
355
234
|
return held;
|
|
@@ -360,34 +239,24 @@ export function configurationFiles(
|
|
|
360
239
|
for (const file of files.byPath.values()) {
|
|
361
240
|
const form = configurationModuleForm(basename(file.fileName));
|
|
362
241
|
const fileDir = dirnamePath(file.fileName);
|
|
363
|
-
if (form !== undefined &&
|
|
242
|
+
if (form !== undefined && manifestHeld(fileDir)) {
|
|
364
243
|
modules.push({ file, form, manifestDir: fileDir });
|
|
365
244
|
}
|
|
366
|
-
for (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
) {
|
|
245
|
+
for (const dir of ancestors(dirnamePath(file.fileName), root)) {
|
|
246
|
+
if (dirs.has(dir)) {
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
371
249
|
dirs.add(dir);
|
|
372
250
|
}
|
|
373
251
|
}
|
|
374
252
|
|
|
375
253
|
const pending: { path: string; manifestDir: string }[] = [];
|
|
376
254
|
const named: { path: string; manifestDir: string }[] = [];
|
|
377
|
-
for (const dir of [...dirs].sort()) {
|
|
378
|
-
if (!
|
|
255
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
256
|
+
if (!manifestHeld(dir)) {
|
|
379
257
|
continue;
|
|
380
258
|
}
|
|
381
|
-
|
|
382
|
-
try {
|
|
383
|
-
names = host
|
|
384
|
-
.readDirectory(dir)
|
|
385
|
-
.filter((entry) => !entry.directory && isConfigurationDocumentName(entry.name))
|
|
386
|
-
.map((entry) => entry.name)
|
|
387
|
-
.sort();
|
|
388
|
-
} catch {
|
|
389
|
-
names = [];
|
|
390
|
-
}
|
|
259
|
+
const names = filesIn(host, dir, isConfigurationDocumentName);
|
|
391
260
|
pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
|
|
392
261
|
for (const path of scriptConfigurations(host, root, dir)) {
|
|
393
262
|
const held = files.byName.get(path);
|
|
@@ -410,7 +279,7 @@ export function configurationFiles(
|
|
|
410
279
|
continue;
|
|
411
280
|
}
|
|
412
281
|
if (document === undefined) {
|
|
413
|
-
const value =
|
|
282
|
+
const value = readJSON(host, next.path);
|
|
414
283
|
if (value === undefined) {
|
|
415
284
|
unreadable.add(next.path);
|
|
416
285
|
continue;
|
|
@@ -438,7 +307,7 @@ export function configurationFiles(
|
|
|
438
307
|
outside:
|
|
439
308
|
parse === undefined
|
|
440
309
|
? []
|
|
441
|
-
: outsideModules(host, root, [...dirs], files,
|
|
310
|
+
: outsideModules(host, root, [...dirs], files, manifestHeld, parse, named),
|
|
442
311
|
};
|
|
443
312
|
}
|
|
444
313
|
|
|
@@ -551,25 +420,16 @@ function outsideModules(
|
|
|
551
420
|
root: string,
|
|
552
421
|
dirs: readonly string[],
|
|
553
422
|
files: SourceFiles,
|
|
554
|
-
|
|
423
|
+
manifestHeld: (dir: string) => boolean,
|
|
555
424
|
parse: ParseFile,
|
|
556
425
|
named: readonly { readonly path: string; readonly manifestDir: string }[],
|
|
557
426
|
): readonly OutsideModule[] {
|
|
558
427
|
const pending: { path: string; manifestDir: string }[] = [...named];
|
|
559
|
-
for (const dir of [...dirs].sort()) {
|
|
560
|
-
if (!
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
let names: readonly string[];
|
|
564
|
-
try {
|
|
565
|
-
names = host
|
|
566
|
-
.readDirectory(dir)
|
|
567
|
-
.filter((entry) => !entry.directory && configurationModuleForm(entry.name) !== undefined)
|
|
568
|
-
.map((entry) => entry.name)
|
|
569
|
-
.sort();
|
|
570
|
-
} catch {
|
|
428
|
+
for (const dir of [...dirs].sort(bytewise)) {
|
|
429
|
+
if (!manifestHeld(dir)) {
|
|
571
430
|
continue;
|
|
572
431
|
}
|
|
432
|
+
const names = filesIn(host, dir, (name) => configurationModuleForm(name) !== undefined);
|
|
573
433
|
pending.push(...names.map((name) => ({ path: joinPath(dir, name), manifestDir: dir })));
|
|
574
434
|
}
|
|
575
435
|
const found = new Map<string, { file: SourceFile; manifestDirs: string[] }>();
|
|
@@ -607,17 +467,12 @@ function outsideModules(
|
|
|
607
467
|
|
|
608
468
|
/** Every dependency the manifest in one directory declares, or none where it holds none to read. */
|
|
609
469
|
export function declaredIn(host: Host, dir: string): readonly string[] {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
value = JSON.parse(host.readFile(joinPath(dir, MANIFEST)));
|
|
613
|
-
} catch {
|
|
614
|
-
return [];
|
|
615
|
-
}
|
|
616
|
-
if (!isRecord(value)) {
|
|
470
|
+
const value = manifestAt(host, dir);
|
|
471
|
+
if (value === undefined) {
|
|
617
472
|
return [];
|
|
618
473
|
}
|
|
619
474
|
const names = new Set<string>();
|
|
620
|
-
for (const member of
|
|
475
|
+
for (const member of DEPENDENCY_SECTIONS) {
|
|
621
476
|
const section = value[member];
|
|
622
477
|
if (isRecord(section)) {
|
|
623
478
|
Object.keys(section).forEach((name) => names.add(name));
|