@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.7
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 +2 -1
- package/dist/bin/node-host.js +3 -2
- package/dist/src/analysis.js +28 -46
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +17 -29
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +3 -2
- package/dist/src/configuration-files.js +27 -168
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1084 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +13 -39
- package/dist/src/edges.js +7 -15
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +3 -63
- package/dist/src/exempt.js +13 -40
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +11 -20
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +4 -4
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +8 -7
- package/dist/src/findings/non-code-artifacts.js +5 -6
- package/dist/src/findings/reads-and-writes.js +11 -9
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +7 -14
- package/dist/src/fs/manifest.js +43 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +34 -25
- 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/intra-function-parts.js +18 -26
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +4 -0
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +4 -360
- package/dist/src/manifest.js +5 -43
- package/dist/src/mdx-entries.js +3 -26
- 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 +3 -2
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +272 -0
- package/dist/src/report.js +39 -42
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +12 -26
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +10 -8
- package/dist/src/schema.js +4 -3
- package/dist/src/scope.js +4 -5
- package/dist/src/serialization-contract.js +4 -8
- package/dist/src/session.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +12 -13
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +5 -15
- package/dist/src/test-globals.js +6 -28
- package/dist/src/test-joins.js +6 -6
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
- package/dist/src/type-errors.js +14 -38
- package/dist/src/unseen-loads.js +2 -9
- package/dist/src/unwrap.js +23 -0
- package/dist/src/verbs/analyze.js +22 -15
- package/dist/src/verbs/describe.js +7 -7
- package/dist/src/verbs/explain.js +7 -4
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +4 -2
- 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 +4 -3
- package/dist/src/workflow-steps.js +9 -46
- package/dist/src/workspace-manifests.js +2 -2
- package/dist/src/workspace-programs.js +5 -3
- package/dist/src/workspace-resolution.js +13 -17
- package/dist/src/workspace.js +28 -116
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +29 -54
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +17 -38
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +11 -15
- package/src/configuration-files.ts +27 -170
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1148 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +14 -39
- package/src/edges.ts +7 -21
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +5 -74
- package/src/exempt.ts +17 -47
- package/src/file-facts.ts +6 -24
- package/src/finding.ts +9 -9
- package/src/findings/completion.ts +22 -23
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +6 -7
- package/src/findings/emitters.ts +6 -6
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +8 -8
- package/src/findings/non-code-artifacts.ts +5 -7
- package/src/findings/reads-and-writes.ts +15 -12
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +7 -16
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +18 -28
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +5 -0
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +6 -406
- package/src/manifest.ts +5 -46
- package/src/mdx-entries.ts +3 -26
- package/src/partial.ts +2 -1
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +3 -2
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +364 -0
- package/src/report.ts +66 -229
- package/src/reporters/annotations.ts +2 -2
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +17 -12
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +31 -38
- package/src/roots.ts +7 -20
- package/src/run.ts +10 -14
- package/src/schema.ts +4 -3
- package/src/scope.ts +4 -10
- package/src/serialization-contract.ts +4 -9
- package/src/session.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +11 -15
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +5 -18
- package/src/test-globals.ts +9 -27
- package/src/test-joins.ts +6 -8
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/shell.ts +161 -0
- package/src/{yaml-document.ts → text/yaml.ts} +46 -0
- package/src/type-errors.ts +25 -36
- package/src/unseen-loads.ts +1 -12
- package/src/unwrap.ts +34 -0
- package/src/verbs/analyze.ts +38 -25
- package/src/verbs/describe.ts +13 -7
- package/src/verbs/explain.ts +7 -4
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +4 -2
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +4 -3
- package/src/workflow-steps.ts +9 -44
- package/src/workspace-manifests.ts +2 -2
- package/src/workspace-programs.ts +5 -3
- package/src/workspace-resolution.ts +21 -17
- package/src/workspace.ts +35 -121
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/version.js +0 -7
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/version.ts +0 -7
package/src/resolve.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SEVERITIES,
|
|
3
|
+
LANGUAGES,
|
|
4
|
+
CONFIDENCES,
|
|
5
|
+
FORMATS,
|
|
6
|
+
type Confidence,
|
|
7
|
+
type Format,
|
|
8
|
+
type Language,
|
|
9
|
+
type Severity,
|
|
10
|
+
FAMILY_KEY_LENGTH,
|
|
11
|
+
} from "./contract/contract.generated.ts";
|
|
1
12
|
import {
|
|
2
13
|
ConfigError,
|
|
3
14
|
defaultConfig,
|
|
4
15
|
type BuildConfiguration,
|
|
5
16
|
type Cascade,
|
|
6
17
|
type Config,
|
|
7
|
-
type Confidence,
|
|
8
18
|
type ConsumerTests,
|
|
9
19
|
type DeclarationEntry,
|
|
10
|
-
type Format,
|
|
11
20
|
type GeneratedFiles,
|
|
12
21
|
type Inputs,
|
|
13
|
-
type Language,
|
|
14
22
|
type LifecycleContract,
|
|
15
23
|
type Origin,
|
|
16
24
|
type Provenance,
|
|
17
25
|
type Provider,
|
|
18
|
-
type Severity,
|
|
19
26
|
type Sort,
|
|
20
27
|
type Source,
|
|
21
28
|
type TargetKind,
|
|
@@ -23,6 +30,7 @@ import {
|
|
|
23
30
|
type TemplateDelimiters,
|
|
24
31
|
} from "./config.ts";
|
|
25
32
|
import { CONVENTION_ROWS } from "./convention-rows.ts";
|
|
33
|
+
import { EXEMPTION_CLASS_NAMES, type ExemptionClass } from "./exempt-classes.ts";
|
|
26
34
|
import { FIXED_SEVERITY_CODES, LIVE_CODES } from "./kinds.ts";
|
|
27
35
|
import {
|
|
28
36
|
checkDocument,
|
|
@@ -32,15 +40,14 @@ import {
|
|
|
32
40
|
SEVERITY_SECTION,
|
|
33
41
|
type KeyNode,
|
|
34
42
|
} from "./schema.ts";
|
|
43
|
+
import { bytewise } from "./text/compare.ts";
|
|
44
|
+
import { isRecord } from "./json-document.ts";
|
|
45
|
+
import { message } from "./product.ts";
|
|
35
46
|
|
|
36
47
|
/** Names the command-line flags in a refusal, where a document is named by its path. */
|
|
37
48
|
const FLAGS_LABEL = "the command-line flags";
|
|
38
49
|
|
|
39
|
-
/** The length of a severity key naming a whole family: the prefix and two digits. */
|
|
40
|
-
export const FAMILY_KEY_LENGTH = 4;
|
|
41
|
-
|
|
42
50
|
const SEVERITY_KEY = /^DS[0-9]{2}([0-9]{2})?$/u;
|
|
43
|
-
const EXEMPTION_CLASS = /^[a-z][a-z0-9-]*$/u;
|
|
44
51
|
const CONTRACT_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+$/u;
|
|
45
52
|
const ANALYZER_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/u;
|
|
46
53
|
const PROVIDER_SOURCE = /^(go|npm):[^ \t\r\n]+$/u;
|
|
@@ -69,12 +76,8 @@ const PROJECT_SEGMENT = String.raw`(?:[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.[^/
|
|
|
69
76
|
const PROJECT_PATH = new RegExp(`^${PROJECT_SEGMENT}(?:/${PROJECT_SEGMENT})*$`, "u");
|
|
70
77
|
|
|
71
78
|
const TARGET_KINDS: readonly TargetKind[] = ["application", "library"];
|
|
72
|
-
const CONFIDENCES: readonly Confidence[] = ["certain", "probable", "possible"];
|
|
73
79
|
const GENERATED_FILES: readonly GeneratedFiles[] = ["exclude", "include"];
|
|
74
80
|
const CONSUMER_TESTS: readonly ConsumerTests[] = ["test", "production"];
|
|
75
|
-
const LANGUAGES: readonly Language[] = ["go", "ts"];
|
|
76
|
-
const SEVERITIES: readonly Severity[] = ["allow", "warn", "deny"];
|
|
77
|
-
const FORMATS: readonly Format[] = ["text", "json", "github", "sarif", "template"];
|
|
78
81
|
const SORTS: readonly Sort[] = ["position", "size"];
|
|
79
82
|
const CASCADES: readonly Cascade[] = ["roots", "full"];
|
|
80
83
|
|
|
@@ -98,7 +101,7 @@ interface Doc {
|
|
|
98
101
|
readonly consumersComplete: boolean | undefined;
|
|
99
102
|
readonly rootPatterns: readonly string[] | undefined;
|
|
100
103
|
readonly severity: ReadonlyMap<string, Severity> | undefined;
|
|
101
|
-
readonly exemptionsDisabled: readonly
|
|
104
|
+
readonly exemptionsDisabled: readonly ExemptionClass[] | undefined;
|
|
102
105
|
readonly formats: readonly Format[] | undefined;
|
|
103
106
|
readonly sort: Sort | undefined;
|
|
104
107
|
readonly cascade: Cascade | undefined;
|
|
@@ -134,10 +137,6 @@ function originOf(source: DocSource, path: string): Origin {
|
|
|
134
137
|
return { source: source.kind, label: source.label };
|
|
135
138
|
}
|
|
136
139
|
|
|
137
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
138
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
140
|
function member(parent: Record<string, unknown> | undefined, name: string): unknown {
|
|
142
141
|
if (parent === undefined || !Object.hasOwn(parent, name)) {
|
|
143
142
|
return undefined;
|
|
@@ -505,7 +504,7 @@ function fixedByContract(code: string): string[] {
|
|
|
505
504
|
return FIXED_SEVERITY_CODES.filter(
|
|
506
505
|
(candidate) =>
|
|
507
506
|
code === candidate || (code.length === FAMILY_KEY_LENGTH && candidate.startsWith(code)),
|
|
508
|
-
).sort();
|
|
507
|
+
).sort(bytewise);
|
|
509
508
|
}
|
|
510
509
|
|
|
511
510
|
/** The codes a refusal names: one on its own, and several with the last joined by and. */
|
|
@@ -543,7 +542,7 @@ function readSeverity(
|
|
|
543
542
|
return undefined;
|
|
544
543
|
}
|
|
545
544
|
const severity = new Map<string, Severity>();
|
|
546
|
-
for (const code of Object.keys(object).sort()) {
|
|
545
|
+
for (const code of Object.keys(object).sort(bytewise)) {
|
|
547
546
|
const path = `${SEVERITY_SECTION}.${code}`;
|
|
548
547
|
if (!SEVERITY_KEY.test(code)) {
|
|
549
548
|
throw unimplementedSeverityKey(
|
|
@@ -577,26 +576,20 @@ function readSeverity(
|
|
|
577
576
|
return severity;
|
|
578
577
|
}
|
|
579
578
|
|
|
580
|
-
/** The exemption classes named for switching off. */
|
|
579
|
+
/** The exemption classes named for switching off, each one the vocabulary holds. */
|
|
581
580
|
function readExemptions(
|
|
582
581
|
document: Record<string, unknown> | undefined,
|
|
583
582
|
label: string,
|
|
584
|
-
): readonly
|
|
583
|
+
): readonly ExemptionClass[] | undefined {
|
|
585
584
|
const exemptions = section(document, "exemptions", "exemptions", label);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
"exemptions.disabled",
|
|
595
|
-
`${JSON.stringify(entry)} is not an exemption class name`,
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return disabled;
|
|
585
|
+
return readStrings(
|
|
586
|
+
exemptions,
|
|
587
|
+
"disabled",
|
|
588
|
+
"exemptions.disabled",
|
|
589
|
+
label,
|
|
590
|
+
0,
|
|
591
|
+
EXEMPTION_CLASS_NAMES,
|
|
592
|
+
);
|
|
600
593
|
}
|
|
601
594
|
|
|
602
595
|
/** The members that name a declaration, each belonging to exactly one shape. */
|
|
@@ -905,7 +898,7 @@ function parseDocument(text: string, node: KeyNode, label: string): unknown {
|
|
|
905
898
|
try {
|
|
906
899
|
value = JSON.parse(text);
|
|
907
900
|
} catch (error: unknown) {
|
|
908
|
-
throw malformed(label, "",
|
|
901
|
+
throw malformed(label, "", message(error));
|
|
909
902
|
}
|
|
910
903
|
checkDocument(text, node, label);
|
|
911
904
|
return value;
|
|
@@ -1002,7 +995,7 @@ function settleSeverity(
|
|
|
1002
995
|
}
|
|
1003
996
|
}
|
|
1004
997
|
const severity = new Map<string, Severity>();
|
|
1005
|
-
for (const code of [...codes].sort()) {
|
|
998
|
+
for (const code of [...codes].sort(bytewise)) {
|
|
1006
999
|
const path = `${SEVERITY_SECTION}.${code}`;
|
|
1007
1000
|
for (const source of sources) {
|
|
1008
1001
|
const value = source.doc.severity?.get(code);
|
|
@@ -1044,7 +1037,7 @@ function describeSource(
|
|
|
1044
1037
|
* print a provenance value the closed key list refuses to read back.
|
|
1045
1038
|
*/
|
|
1046
1039
|
function checkLabels(provenance: ReadonlyMap<string, Origin>): void {
|
|
1047
|
-
for (const path of [...provenance.keys()].sort()) {
|
|
1040
|
+
for (const path of [...provenance.keys()].sort(bytewise)) {
|
|
1048
1041
|
const origin = provenance.get(path);
|
|
1049
1042
|
if (origin !== undefined && origin.source !== "default" && origin.label === "") {
|
|
1050
1043
|
throw malformed(
|
package/src/roots.ts
CHANGED
|
@@ -43,17 +43,19 @@ import {
|
|
|
43
43
|
} from "./configuration-files.ts";
|
|
44
44
|
import { conventionEntries, type Conventions } from "./conventions.ts";
|
|
45
45
|
import { entryPoints, type EntryRule } from "./entry-points.ts";
|
|
46
|
-
import { globExpression } from "./glob.ts";
|
|
47
46
|
import type { Host } from "./host.ts";
|
|
48
47
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
49
48
|
import type { Manifest, ManifestEntry } from "./manifest.ts";
|
|
50
|
-
import {
|
|
49
|
+
import { ancestors, dirnamePath, relativePath, resolvePath } from "./paths.ts";
|
|
51
50
|
import { byPosition } from "./position.ts";
|
|
52
51
|
import { UNANSWERED } from "./query.ts";
|
|
53
52
|
import type { ProjectView } from "./session.ts";
|
|
54
53
|
import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
|
|
55
54
|
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
56
55
|
import { typeQueryAliases } from "./type-query-alias.ts";
|
|
56
|
+
import { bytewise } from "./text/compare.ts";
|
|
57
|
+
import { holdsManifest } from "./fs/manifest.ts";
|
|
58
|
+
import { globExpression } from "./text/glob.ts";
|
|
57
59
|
|
|
58
60
|
/** Why one declaration is a root. */
|
|
59
61
|
export type RootKind =
|
|
@@ -197,15 +199,8 @@ const SOURCE_EXTENSION = /\.(?:d\.)?[cm]?[jt]sx?$/u;
|
|
|
197
199
|
|
|
198
200
|
/** Whether no `package.json` sits between one file and the manifest's directory, which holds it. */
|
|
199
201
|
function ofPackage(host: Host, dir: string, file: string): boolean {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
relativePath(dir, at) === undefined ||
|
|
203
|
-
host.kindOf(joinPath(at, "package.json")) === "file"
|
|
204
|
-
) {
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return true;
|
|
202
|
+
const between = ancestors(dirnamePath(file), dir);
|
|
203
|
+
return between.length > 0 && !between.slice(0, -1).some((at) => holdsManifest(host, at));
|
|
209
204
|
}
|
|
210
205
|
|
|
211
206
|
/**
|
|
@@ -331,14 +326,6 @@ export function matchRef(pattern: string, ref: string): boolean {
|
|
|
331
326
|
return matchGlob(Array.from(pattern), Array.from(ref));
|
|
332
327
|
}
|
|
333
328
|
|
|
334
|
-
/** Two strings ordered bytewise, which is the order every set of a run is read in. */
|
|
335
|
-
function compare(a: string, b: string): number {
|
|
336
|
-
if (a === b) {
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
return a < b ? -1 : 1;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
329
|
/** The members of one declaration, indexed by the container that holds them. */
|
|
343
330
|
function childrenOf(held: Inventory): ReadonlyMap<string, readonly InventorySymbol[]> {
|
|
344
331
|
const byParent = new Map<string, InventorySymbol[]>();
|
|
@@ -561,7 +548,7 @@ export function roots<Brand>(
|
|
|
561
548
|
const left = sites.get(a.id);
|
|
562
549
|
const right = sites.get(b.id);
|
|
563
550
|
const site = left === undefined || right === undefined ? 0 : byPosition(left, right);
|
|
564
|
-
return site ||
|
|
551
|
+
return site || bytewise(a.kind, b.kind) || bytewise(a.source, b.source);
|
|
565
552
|
});
|
|
566
553
|
|
|
567
554
|
return {
|
package/src/run.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EXIT_FAILURE, EXIT_USAGE } from "./contract/contract.generated.ts";
|
|
1
2
|
import { ConfigError, REPOSITORY_DOCUMENT, type Inputs } from "./config.ts";
|
|
2
3
|
import { DiscoveryError, renderDiagnostic } from "./discover.ts";
|
|
3
4
|
import type { Host } from "./host.ts";
|
|
@@ -12,8 +13,9 @@ import { printRootsVerb } from "./verbs/print-roots.ts";
|
|
|
12
13
|
import { ANALYZE_OPTIONS, analyzeVerb } from "./verbs/analyze.ts";
|
|
13
14
|
import { describeVerb } from "./verbs/describe.ts";
|
|
14
15
|
import { EXPLAIN_OPTIONS, explainVerb } from "./verbs/explain.ts";
|
|
15
|
-
import {
|
|
16
|
+
import { type Verb, type VerbOptions } from "./verbs/verb.ts";
|
|
16
17
|
import { versionVerb } from "./verbs/version.ts";
|
|
18
|
+
import { diagnose, message, PRODUCT } from "./product.ts";
|
|
17
19
|
|
|
18
20
|
/** One output stream of the command line. `process.stdout` and `process.stderr` satisfy it. */
|
|
19
21
|
export interface Writer {
|
|
@@ -36,7 +38,7 @@ const VERBS: readonly {
|
|
|
36
38
|
{ name: "version", run: versionVerb },
|
|
37
39
|
];
|
|
38
40
|
|
|
39
|
-
const USAGE = `usage:
|
|
41
|
+
const USAGE = `usage: ${PRODUCT} <verb> [options]\nverbs: ${VERBS.map((verb) => verb.name).join(", ")}\n`;
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* The tokens whose presence in an option's name makes that option a request to
|
|
@@ -140,11 +142,7 @@ function readFileOr(host: Host, path: string, orUndefined: boolean): string | un
|
|
|
140
142
|
if (orUndefined) {
|
|
141
143
|
return undefined;
|
|
142
144
|
}
|
|
143
|
-
throw new ConfigError(
|
|
144
|
-
"malformed",
|
|
145
|
-
"",
|
|
146
|
-
`${path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
147
|
-
);
|
|
145
|
+
throw new ConfigError("malformed", "", `${path}: ${message(error)}`);
|
|
148
146
|
}
|
|
149
147
|
}
|
|
150
148
|
|
|
@@ -208,9 +206,7 @@ export function run(
|
|
|
208
206
|
): number {
|
|
209
207
|
const editing = sourceEditOption(args);
|
|
210
208
|
if (editing !== undefined) {
|
|
211
|
-
err
|
|
212
|
-
`deadset-ts: ${editing} is not supported: deadset-ts reports and never edits a source file\n`,
|
|
213
|
-
);
|
|
209
|
+
diagnose(err, `${editing} is not supported: ${PRODUCT} reports and never edits a source file`);
|
|
214
210
|
err.write(USAGE);
|
|
215
211
|
return EXIT_USAGE;
|
|
216
212
|
}
|
|
@@ -227,7 +223,7 @@ export function run(
|
|
|
227
223
|
);
|
|
228
224
|
}
|
|
229
225
|
if (name !== undefined) {
|
|
230
|
-
err
|
|
226
|
+
diagnose(err, `unknown verb ${JSON.stringify(name)}`);
|
|
231
227
|
}
|
|
232
228
|
err.write(USAGE);
|
|
233
229
|
return EXIT_USAGE;
|
|
@@ -273,7 +269,7 @@ function invoke(
|
|
|
273
269
|
/** Turns one refusal into the exit code the Contract's table gives it. */
|
|
274
270
|
function refuse(error: unknown, err: Writer): number {
|
|
275
271
|
if (error instanceof ConfigError) {
|
|
276
|
-
err
|
|
272
|
+
diagnose(err, error.message);
|
|
277
273
|
err.write(USAGE);
|
|
278
274
|
return EXIT_USAGE;
|
|
279
275
|
}
|
|
@@ -281,7 +277,7 @@ function refuse(error: unknown, err: Writer): number {
|
|
|
281
277
|
for (const diagnostic of error.diagnostics) {
|
|
282
278
|
err.write(`${renderDiagnostic(diagnostic)}\n`);
|
|
283
279
|
}
|
|
284
|
-
err
|
|
280
|
+
diagnose(err, error.message);
|
|
285
281
|
return EXIT_FAILURE;
|
|
286
282
|
}
|
|
287
283
|
if (error instanceof SetupError) {
|
|
@@ -291,7 +287,7 @@ function refuse(error: unknown, err: Writer): number {
|
|
|
291
287
|
return EXIT_FAILURE;
|
|
292
288
|
}
|
|
293
289
|
if (error instanceof ScopeError) {
|
|
294
|
-
err
|
|
290
|
+
diagnose(err, error.message);
|
|
295
291
|
return EXIT_FAILURE;
|
|
296
292
|
}
|
|
297
293
|
throw error;
|
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConfigError } from "./config.ts";
|
|
2
|
+
import { bytewise } from "./text/compare.ts";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* How one node of the closed key list is written: `leaf`, a scalar or an array of
|
|
@@ -157,7 +158,7 @@ function collect(node: KeyNode, at: string, keys: string[], settingsOnly: boolea
|
|
|
157
158
|
export function declaredKeys(): string[] {
|
|
158
159
|
const keys: string[] = [];
|
|
159
160
|
collect(SCHEMA_ROOT, "", keys, false);
|
|
160
|
-
return keys.sort();
|
|
161
|
+
return keys.sort(bytewise);
|
|
161
162
|
}
|
|
162
163
|
|
|
163
164
|
/**
|
|
@@ -169,7 +170,7 @@ export function declaredKeys(): string[] {
|
|
|
169
170
|
export function settingPaths(): string[] {
|
|
170
171
|
const paths: string[] = [];
|
|
171
172
|
collect(SCHEMA_ROOT, "", paths, true);
|
|
172
|
-
return paths.sort();
|
|
173
|
+
return paths.sort(bytewise);
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
/**
|
|
@@ -445,7 +446,7 @@ export function checkDocument(text: string, node: KeyNode, label: string): void
|
|
|
445
446
|
*/
|
|
446
447
|
export function nestFlags(flat: Readonly<Record<string, unknown>>, label: string): unknown {
|
|
447
448
|
const nested: Record<string, unknown> = {};
|
|
448
|
-
for (const path of Object.keys(flat).sort()) {
|
|
449
|
+
for (const path of Object.keys(flat).sort(bytewise)) {
|
|
449
450
|
if (!declaresSetting(path)) {
|
|
450
451
|
throw unimplementedKey(label, path);
|
|
451
452
|
}
|
package/src/scope.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { ConfigError } from "./config.ts";
|
|
|
2
2
|
import type { Host } from "./host.ts";
|
|
3
3
|
import { dirnamePath, isAbsolutePath, joinPath, resolvePath } from "./paths.ts";
|
|
4
4
|
import { checkDocument, type KeyNode } from "./schema.ts";
|
|
5
|
+
import { isRecord } from "./json-document.ts";
|
|
6
|
+
import { message } from "./product.ts";
|
|
5
7
|
|
|
6
8
|
/** The roles a scope document declares for a module. */
|
|
7
9
|
const ROLE_TARGET = "target";
|
|
@@ -60,10 +62,6 @@ const SCOPE_ROOT: KeyNode = {
|
|
|
60
62
|
},
|
|
61
63
|
};
|
|
62
64
|
|
|
63
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
64
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
65
|
/**
|
|
68
66
|
* One string member, or undefined where the document omits it. A member the
|
|
69
67
|
* document writes is a string holding at least one character, so `null` and `""`
|
|
@@ -145,9 +143,7 @@ export function readScope(host: Host, file: string): Scope {
|
|
|
145
143
|
try {
|
|
146
144
|
text = host.readFile(absolute);
|
|
147
145
|
} catch (error: unknown) {
|
|
148
|
-
throw new ScopeError(
|
|
149
|
-
`scope: ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
150
|
-
);
|
|
146
|
+
throw new ScopeError(`scope: ${file}: ${message(error)}`);
|
|
151
147
|
}
|
|
152
148
|
if (text.length > MAX_DOCUMENT_LENGTH) {
|
|
153
149
|
throw new ScopeError(`scope: ${file} is longer than ${String(MAX_DOCUMENT_LENGTH)} characters`);
|
|
@@ -156,9 +152,7 @@ export function readScope(host: Host, file: string): Scope {
|
|
|
156
152
|
try {
|
|
157
153
|
value = JSON.parse(text);
|
|
158
154
|
} catch (error: unknown) {
|
|
159
|
-
throw new ScopeError(
|
|
160
|
-
`scope: ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
161
|
-
);
|
|
155
|
+
throw new ScopeError(`scope: ${file}: ${message(error)}`);
|
|
162
156
|
}
|
|
163
157
|
if (!isRecord(value)) {
|
|
164
158
|
throw new ScopeError(`scope: ${file} is not one JSON object`);
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
type SourceFile,
|
|
54
54
|
type SpreadAssignment,
|
|
55
55
|
} from "@typescript/native/ast";
|
|
56
|
+
import { parenthesesRemoved } from "./unwrap.ts";
|
|
56
57
|
import {
|
|
57
58
|
SignatureKind,
|
|
58
59
|
SymbolFlags,
|
|
@@ -227,12 +228,6 @@ interface MethodForm {
|
|
|
227
228
|
readonly invocation: Invocation;
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
function unparenthesized(expression: Expression): Expression {
|
|
231
|
-
return isParenthesizedExpression(expression)
|
|
232
|
-
? unparenthesized(expression.expression)
|
|
233
|
-
: expression;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
231
|
/**
|
|
237
232
|
* The invocation a call writes through a method of `Function.prototype`, read from
|
|
238
233
|
* the syntax alone: `f.call(self, a)`, `f.apply(self, [a])`, `f.bind(self, a)`, which
|
|
@@ -241,12 +236,12 @@ function unparenthesized(expression: Expression): Expression {
|
|
|
241
236
|
* carries. Whether the method is the prototype's is resolution's to say.
|
|
242
237
|
*/
|
|
243
238
|
function throughMethod(call: CallExpression): MethodForm | undefined {
|
|
244
|
-
const expression =
|
|
239
|
+
const expression = parenthesesRemoved(call.expression);
|
|
245
240
|
if (isPropertyAccessExpression(expression) && FUNCTION_METHODS.has(expression.name.text)) {
|
|
246
241
|
const [, ...rest] = call.arguments;
|
|
247
242
|
let passed: readonly Expression[] = rest;
|
|
248
243
|
if (expression.name.text === "apply") {
|
|
249
|
-
const list = rest[0] === undefined ? undefined :
|
|
244
|
+
const list = rest[0] === undefined ? undefined : parenthesesRemoved(rest[0]);
|
|
250
245
|
passed = list === undefined ? [] : isArrayLiteralExpression(list) ? list.elements : [list];
|
|
251
246
|
}
|
|
252
247
|
return {
|
|
@@ -255,7 +250,7 @@ function throughMethod(call: CallExpression): MethodForm | undefined {
|
|
|
255
250
|
};
|
|
256
251
|
}
|
|
257
252
|
if (isCallExpression(expression)) {
|
|
258
|
-
const bound =
|
|
253
|
+
const bound = parenthesesRemoved(expression.expression);
|
|
259
254
|
if (isPropertyAccessExpression(bound) && bound.name.text === "bind") {
|
|
260
255
|
return {
|
|
261
256
|
method: bound.name,
|
package/src/session.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
type Queries,
|
|
27
27
|
type UnansweredQuestion,
|
|
28
28
|
} from "./query.ts";
|
|
29
|
+
import { MANIFEST } from "./fs/manifest.ts";
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* The compiler the analysis reads through: a client over a protocol to a separate
|
|
@@ -115,7 +116,7 @@ export function openEngine(options: EngineOptions): Engine {
|
|
|
115
116
|
if (components !== undefined && components.extensions.length > 0) {
|
|
116
117
|
const made = components.host.temporaryDirectory();
|
|
117
118
|
try {
|
|
118
|
-
components.host.writeDocument(joinPath(made.path,
|
|
119
|
+
components.host.writeDocument(joinPath(made.path, MANIFEST), [
|
|
119
120
|
mapperManifest(components.host.componentMapperCommand()),
|
|
120
121
|
]);
|
|
121
122
|
} catch (error: unknown) {
|
package/src/setup-failure.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
+
import { SETUP_FAILURE_PREFIX, type SETUP_CLASSES } from "./contract/contract.generated.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* A setup failure: a file or a component the analysis needs and the project does not
|
|
3
5
|
* provide. Each one names what is missing and the fix, on one line of the error stream
|
|
4
6
|
* that starts with its class.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
/**
|
|
8
|
-
type SetupClass =
|
|
9
|
-
| "missing-module"
|
|
10
|
-
| "missing-consumer"
|
|
11
|
-
| "workspace-member-without-source"
|
|
12
|
-
| "convention-not-literal";
|
|
9
|
+
/** A class of setup failure the Contract names. */
|
|
10
|
+
type SetupClass = (typeof SETUP_CLASSES)[number]["class"];
|
|
13
11
|
|
|
14
12
|
export interface SetupFailure {
|
|
15
13
|
readonly setupClass: SetupClass;
|
|
@@ -19,7 +17,7 @@ export interface SetupFailure {
|
|
|
19
17
|
|
|
20
18
|
/** The failure as the one line of the error stream that names it. */
|
|
21
19
|
export function setupLine(failure: SetupFailure): string {
|
|
22
|
-
return
|
|
20
|
+
return `${SETUP_FAILURE_PREFIX}${failure.setupClass}: ${failure.detail}`;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
/**
|
package/src/suppress-file.ts
CHANGED
|
@@ -5,18 +5,17 @@
|
|
|
5
5
|
* ignore file by gaining a maintainer's reason.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { CODE } from "./contract/contract.generated.ts";
|
|
8
9
|
import type { Host } from "./host.ts";
|
|
9
10
|
import type { InventorySymbol } from "./inventory.ts";
|
|
10
11
|
import { jsonChunks } from "./json-chunks.ts";
|
|
11
12
|
import { joinPath } from "./paths.ts";
|
|
12
|
-
import { positionAt, walkDocument } from "./json-document.ts";
|
|
13
|
+
import { isRecord, positionAt, walkDocument } from "./json-document.ts";
|
|
13
14
|
import type { Position } from "./position.ts";
|
|
14
15
|
import { declarationKey } from "./ref.ts";
|
|
15
16
|
import {
|
|
16
17
|
CODE_FORM,
|
|
17
|
-
SUPPRESSION_WITHOUT_REASON,
|
|
18
18
|
SuppressionError,
|
|
19
|
-
UNSCOPED_ENTRY,
|
|
20
19
|
blankReason,
|
|
21
20
|
isEntryPath,
|
|
22
21
|
isPartKind,
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
type SuppressionRecord,
|
|
28
27
|
type Suppressions,
|
|
29
28
|
} from "./suppress.ts";
|
|
29
|
+
import { message } from "./product.ts";
|
|
30
30
|
|
|
31
31
|
/** The name and the location the grammar fixes for the ignore file. */
|
|
32
32
|
export const IGNORE_FILE = "deadset-ignore.json";
|
|
@@ -80,10 +80,6 @@ function documentSite(shape: Shape): Position {
|
|
|
80
80
|
return { path: shape.file, line: 0, column: 0 };
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
84
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
83
|
/** What one entry or row resolves to. */
|
|
88
84
|
interface Read {
|
|
89
85
|
readonly records: readonly SuppressionRecord[];
|
|
@@ -101,7 +97,7 @@ function readRecord(value: unknown, site: Position, byReference: ByReference, sh
|
|
|
101
97
|
const refuse = (text: string, want: string): never => {
|
|
102
98
|
throw new SuppressionError(shape.mechanism, siteText(site), text, want);
|
|
103
99
|
};
|
|
104
|
-
if (!
|
|
100
|
+
if (!isRecord(value)) {
|
|
105
101
|
return refuse(JSON.stringify(value), shape.recordWant);
|
|
106
102
|
}
|
|
107
103
|
for (const [key, held] of Object.entries(value)) {
|
|
@@ -148,10 +144,10 @@ function readRecord(value: unknown, site: Position, byReference: ByReference, sh
|
|
|
148
144
|
};
|
|
149
145
|
const refusals: Refusal[] = [];
|
|
150
146
|
if (blankReason(reason)) {
|
|
151
|
-
refusals.push({ ...written, reported:
|
|
147
|
+
refusals.push({ ...written, reported: CODE.suppressionWithoutReason });
|
|
152
148
|
}
|
|
153
149
|
if (written.path === "") {
|
|
154
|
-
refusals.push({ ...written, reported:
|
|
150
|
+
refusals.push({ ...written, reported: CODE.unscopedIgnoreEntry });
|
|
155
151
|
}
|
|
156
152
|
if (refusals.length > 0) {
|
|
157
153
|
return { records: [], refusals };
|
|
@@ -201,9 +197,9 @@ function readDocument(
|
|
|
201
197
|
try {
|
|
202
198
|
parsed = JSON.parse(text);
|
|
203
199
|
} catch (error: unknown) {
|
|
204
|
-
return malformed(`${shape.file}: ${
|
|
200
|
+
return malformed(`${shape.file}: ${message(error)}`);
|
|
205
201
|
}
|
|
206
|
-
if (!
|
|
202
|
+
if (!isRecord(parsed)) {
|
|
207
203
|
return malformed(shape.file);
|
|
208
204
|
}
|
|
209
205
|
const walk = walkDocument(text, shape.array);
|
|
@@ -329,9 +325,9 @@ export function otherSchemeRows(text: string, own: string = OWN_SCHEME): readonl
|
|
|
329
325
|
try {
|
|
330
326
|
parsed = JSON.parse(text);
|
|
331
327
|
} catch (error: unknown) {
|
|
332
|
-
return refuse(
|
|
328
|
+
return refuse(message(error));
|
|
333
329
|
}
|
|
334
|
-
const held =
|
|
330
|
+
const held = isRecord(parsed) ? parsed[BASELINE_SHAPE.array] : undefined;
|
|
335
331
|
if (!Array.isArray(held)) {
|
|
336
332
|
return refuse(BASELINE_SHAPE.documentWant);
|
|
337
333
|
}
|
|
@@ -339,7 +335,7 @@ export function otherSchemeRows(text: string, own: string = OWN_SCHEME): readonl
|
|
|
339
335
|
try {
|
|
340
336
|
readRecord(value, documentSite(BASELINE_SHAPE), new Map(), BASELINE_SHAPE);
|
|
341
337
|
} catch (error: unknown) {
|
|
342
|
-
return refuse(
|
|
338
|
+
return refuse(message(error));
|
|
343
339
|
}
|
|
344
340
|
// readRecord has checked every member is a string and the code and symbol are present.
|
|
345
341
|
const { code, symbol, path, reason } = value as Readonly<Record<string, string>>;
|
package/src/suppress-inline.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* below it, or on its own line where it trails a token there.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { CODE } from "./contract/contract.generated.ts";
|
|
7
8
|
import {
|
|
8
9
|
isConstructorDeclaration,
|
|
9
10
|
SyntaxKind,
|
|
@@ -18,7 +19,6 @@ import type { ProjectView } from "./session.ts";
|
|
|
18
19
|
import { signatureOf } from "./signatures.ts";
|
|
19
20
|
import {
|
|
20
21
|
isPartKind,
|
|
21
|
-
SUPPRESSION_WITHOUT_REASON,
|
|
22
22
|
SuppressionError,
|
|
23
23
|
siteText,
|
|
24
24
|
type Refusal,
|
|
@@ -354,7 +354,7 @@ export function inlineSuppressions(
|
|
|
354
354
|
case "no-reason":
|
|
355
355
|
for (const code of directive.codes) {
|
|
356
356
|
refusals.push({
|
|
357
|
-
reported:
|
|
357
|
+
reported: CODE.suppressionWithoutReason,
|
|
358
358
|
code,
|
|
359
359
|
symbol: "",
|
|
360
360
|
path: site.path,
|