@cplieger/deadset-ts 8.0.0-dev.7 → 8.0.0-dev.8
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/node-host.js +21 -3
- package/dist/src/analysis.js +13 -14
- package/dist/src/config.js +6 -83
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/consumers.js +2 -2
- package/dist/src/contract/config.generated.js +544 -0
- package/dist/src/contract/report.generated.js +3 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/edges.js +36 -112
- package/dist/src/exempt-classes.js +0 -2
- package/dist/src/exempt.js +1 -1
- package/dist/src/findings/completion.js +5 -4
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +1 -1
- package/dist/src/findings/non-code-artifacts.js +2 -2
- package/dist/src/findings/unused-declarations.js +1 -1
- package/dist/src/findings/visibility-narrowing.js +2 -2
- package/dist/src/findings-pass.js +1 -1
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +59 -0
- package/dist/src/injection-container.js +1 -1
- package/dist/src/json-document.js +194 -67
- package/dist/src/kinds.js +0 -7
- package/dist/src/print.js +10 -79
- package/dist/src/report-schema.js +3 -224
- package/dist/src/resolve.js +245 -715
- package/dist/src/run.js +48 -55
- package/dist/src/schema.js +320 -344
- package/dist/src/scope.js +33 -101
- package/dist/src/serialization-contract.js +1 -1
- package/dist/src/suppress-file.js +15 -17
- package/dist/src/suppress.js +3 -3
- package/dist/src/template-field.js +4 -4
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +12 -15
- package/dist/src/verbs/explain.js +3 -3
- package/dist/src/verbs/print-projects.js +2 -2
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/analysis.ts +30 -26
- package/src/config.ts +31 -298
- package/src/configured-declarations.ts +18 -23
- package/src/consumers.ts +4 -4
- package/src/contract/config.generated.ts +798 -0
- package/src/contract/report.generated.ts +3 -2
- package/src/discover.ts +1 -1
- package/src/edges.ts +39 -124
- package/src/exempt-classes.ts +0 -5
- package/src/exempt.ts +4 -4
- package/src/findings/completion.ts +5 -6
- package/src/findings/emitter.ts +2 -2
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +1 -1
- package/src/findings/non-code-artifacts.ts +4 -4
- package/src/findings/unused-declarations.ts +1 -1
- package/src/findings/visibility-narrowing.ts +2 -2
- package/src/findings-pass.ts +3 -3
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +74 -0
- package/src/host.ts +12 -5
- package/src/injection-container.ts +1 -1
- package/src/json-document.ts +236 -76
- package/src/kinds.ts +0 -8
- package/src/print.ts +11 -92
- package/src/report-schema.ts +3 -299
- package/src/resolve.ts +281 -1105
- package/src/run.ts +50 -63
- package/src/schema.ts +391 -389
- package/src/scope.ts +39 -119
- package/src/serialization-contract.ts +1 -1
- package/src/suppress-file.ts +15 -20
- package/src/suppress.ts +3 -3
- package/src/template-field.ts +10 -7
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +12 -23
- package/src/verbs/explain.ts +3 -7
- package/src/verbs/print-projects.ts +2 -2
- package/src/verbs/verb.ts +2 -2
package/src/run.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { CONFIG_MAX_BYTES, SETTINGS } from "./contract/config.generated.ts";
|
|
1
2
|
import { EXIT_FAILURE, EXIT_USAGE } from "./contract/contract.generated.ts";
|
|
2
|
-
import {
|
|
3
|
+
import { REPOSITORY_DOCUMENT, type Document, type Inputs } from "./config.ts";
|
|
3
4
|
import { DiscoveryError, renderDiagnostic } from "./discover.ts";
|
|
5
|
+
import { readTargetDocument, TargetDocumentError } from "./fs/document.ts";
|
|
4
6
|
import type { Host } from "./host.ts";
|
|
5
7
|
import { joinPath, resolvePath } from "./paths.ts";
|
|
6
8
|
import { readScope, ScopeError, scopeForDir, type Scope } from "./scope.ts";
|
|
7
9
|
import { SetupError, setupLine } from "./setup-failure.ts";
|
|
10
|
+
import { UsageError } from "./usage.ts";
|
|
8
11
|
import { openEngine, type Engine, type EngineOptions } from "./session.ts";
|
|
9
12
|
import { printConfigVerb } from "./verbs/print-config.ts";
|
|
10
13
|
import { printProjectsVerb } from "./verbs/print-projects.ts";
|
|
@@ -48,33 +51,16 @@ const USAGE = `usage: ${PRODUCT} <verb> [options]\nverbs: ${VERBS.map((verb) =>
|
|
|
48
51
|
const SOURCE_EDIT_TOKENS = ["fix", "edit", "delete", "rewrite"] as const;
|
|
49
52
|
|
|
50
53
|
/**
|
|
51
|
-
* Maps an option name to the setting of the Contract's configuration schema it supplies
|
|
52
|
-
*
|
|
53
|
-
* the spelling the setting's provenance names. The name is this command's vocabulary and
|
|
54
|
-
* is never derived from the path: a setting reaches the command line only by an entry,
|
|
55
|
-
* and a path names a key the schema declares.
|
|
54
|
+
* Maps an option name to the setting of the Contract's configuration schema it supplies:
|
|
55
|
+
* the options contract/settings.json names for a setting this analyzer reads.
|
|
56
56
|
*/
|
|
57
|
-
export const SETTING_OPTIONS: ReadonlyMap<string, string> = new Map(
|
|
58
|
-
[
|
|
59
|
-
|
|
60
|
-
["cascade", "reporters.cascade"],
|
|
61
|
-
["max-findings", "reporters.max_findings"],
|
|
62
|
-
["fail-on", "reporters.fail_on"],
|
|
63
|
-
]);
|
|
57
|
+
export const SETTING_OPTIONS: ReadonlyMap<string, string> = new Map(
|
|
58
|
+
SETTINGS.flatMap((row) => (row.option === undefined ? [] : [[row.option, row.key] as const])),
|
|
59
|
+
);
|
|
64
60
|
|
|
65
61
|
/** The options that take a value and are not settings. */
|
|
66
62
|
const PLAIN_OPTIONS: ReadonlySet<string> = new Set(["target", "config", "central", "scope"]);
|
|
67
63
|
|
|
68
|
-
/**
|
|
69
|
-
* The setting options whose value is an integer rather than a string. A value
|
|
70
|
-
* written other than as digits is passed on as the string it is, so resolution
|
|
71
|
-
* refuses it by its type rather than reading `1e2` as 100.
|
|
72
|
-
*/
|
|
73
|
-
const INTEGER_OPTIONS: ReadonlySet<string> = new Set(["max-findings"]);
|
|
74
|
-
|
|
75
|
-
/** An integer as an option writes it: an optional minus sign and digits alone. */
|
|
76
|
-
const INTEGER_TEXT = /^-?[0-9]+$/u;
|
|
77
|
-
|
|
78
64
|
/** One invocation's options, by name: the last value of each, and every value of a repeatable one. */
|
|
79
65
|
interface Options {
|
|
80
66
|
readonly named: ReadonlyMap<string, string>;
|
|
@@ -109,7 +95,7 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
|
|
|
109
95
|
const repeated = new Map<string, string[]>();
|
|
110
96
|
for (const arg of args) {
|
|
111
97
|
if (!arg.startsWith("-")) {
|
|
112
|
-
throw new
|
|
98
|
+
throw new UsageError(`unexpected argument ${JSON.stringify(arg)}`);
|
|
113
99
|
}
|
|
114
100
|
const [spelled, ...rest] = arg.split("=");
|
|
115
101
|
const name = (spelled ?? "").replace(/^-+/u, "");
|
|
@@ -119,14 +105,10 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
|
|
|
119
105
|
!own.plain.includes(name) &&
|
|
120
106
|
!own.repeatable.includes(name)
|
|
121
107
|
) {
|
|
122
|
-
throw new
|
|
108
|
+
throw new UsageError(`unknown option ${JSON.stringify(spelled ?? arg)}`);
|
|
123
109
|
}
|
|
124
110
|
if (rest.length === 0) {
|
|
125
|
-
throw new
|
|
126
|
-
"malformed",
|
|
127
|
-
"",
|
|
128
|
-
`option ${JSON.stringify(spelled ?? arg)} takes a value`,
|
|
129
|
-
);
|
|
111
|
+
throw new UsageError(`option ${JSON.stringify(spelled ?? arg)} takes a value`);
|
|
130
112
|
}
|
|
131
113
|
const value = rest.join("=");
|
|
132
114
|
named.set(name, value);
|
|
@@ -135,47 +117,54 @@ function readOptions(args: readonly string[], own: VerbOptions): Options {
|
|
|
135
117
|
return { named, repeated };
|
|
136
118
|
}
|
|
137
119
|
|
|
138
|
-
|
|
120
|
+
/** The document an option names. */
|
|
121
|
+
function documentAt(host: Host, label: string): Document {
|
|
139
122
|
try {
|
|
140
|
-
|
|
123
|
+
const path = resolvePath(host.workingDirectory(), label);
|
|
124
|
+
// A file past the bound reads as one byte more than it, which resolution refuses.
|
|
125
|
+
return { text: host.readFile(path, CONFIG_MAX_BYTES + 1), label };
|
|
141
126
|
} catch (error: unknown) {
|
|
142
|
-
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
throw new ConfigError("malformed", "", `${path}: ${message(error)}`);
|
|
127
|
+
throw new UsageError(`${label}: ${message(error)}`);
|
|
146
128
|
}
|
|
147
129
|
}
|
|
148
130
|
|
|
149
131
|
/**
|
|
150
|
-
* The configuration
|
|
151
|
-
*
|
|
152
|
-
|
|
132
|
+
* The repository configuration at the target root, none where it is absent. One that
|
|
133
|
+
* cannot be read is a configuration document that cannot be, refused with the usage code.
|
|
134
|
+
*/
|
|
135
|
+
function repositoryAt(host: Host, target: string): Document | undefined {
|
|
136
|
+
const label = joinPath(target, REPOSITORY_DOCUMENT);
|
|
137
|
+
const root = resolvePath(host.workingDirectory(), target);
|
|
138
|
+
try {
|
|
139
|
+
const text = readTargetDocument(host, root, REPOSITORY_DOCUMENT, label);
|
|
140
|
+
return text === undefined ? undefined : { text, label };
|
|
141
|
+
} catch (error: unknown) {
|
|
142
|
+
throw error instanceof TargetDocumentError ? new UsageError(error.message) : error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The configuration sources one invocation carries: the repository configuration at
|
|
148
|
+
* the target root or the path an option named, the central configuration, and the
|
|
149
|
+
* text each setting option was given.
|
|
153
150
|
*/
|
|
154
151
|
function inputsOf(host: Host, options: Options): Inputs {
|
|
155
|
-
const target = options.named.get("target") ?? ".";
|
|
156
152
|
const named = options.named.get("config");
|
|
157
|
-
const
|
|
158
|
-
const repository =
|
|
153
|
+
const target = options.named.get("target") ?? ".";
|
|
154
|
+
const repository = named === undefined ? repositoryAt(host, target) : documentAt(host, named);
|
|
159
155
|
const centralLabel = options.named.get("central");
|
|
160
|
-
const central = centralLabel === undefined ? undefined :
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
const flagLabels = new Map<string, string>();
|
|
164
|
-
for (const [name, path] of SETTING_OPTIONS) {
|
|
156
|
+
const central = centralLabel === undefined ? undefined : documentAt(host, centralLabel);
|
|
157
|
+
const flags = new Map<string, string>();
|
|
158
|
+
for (const [name, key] of SETTING_OPTIONS) {
|
|
165
159
|
const value = options.named.get(name);
|
|
166
|
-
if (value
|
|
167
|
-
|
|
160
|
+
if (value !== undefined) {
|
|
161
|
+
flags.set(key, value);
|
|
168
162
|
}
|
|
169
|
-
flat[path] = INTEGER_OPTIONS.has(name) && INTEGER_TEXT.test(value) ? Number(value) : value;
|
|
170
|
-
flagLabels.set(path, `--${name}`);
|
|
171
163
|
}
|
|
172
|
-
const flags = Object.keys(flat).length === 0 ? undefined : JSON.stringify(flat);
|
|
173
|
-
|
|
174
164
|
return {
|
|
175
|
-
|
|
176
|
-
...(repository === undefined ? {} : { repository
|
|
177
|
-
...(central === undefined
|
|
178
|
-
flagLabels,
|
|
165
|
+
flags,
|
|
166
|
+
...(repository === undefined ? {} : { repository }),
|
|
167
|
+
...(central === undefined ? {} : { central }),
|
|
179
168
|
};
|
|
180
169
|
}
|
|
181
170
|
|
|
@@ -193,9 +182,7 @@ function scopeOf(host: Host, options: Options): Scope {
|
|
|
193
182
|
* the exit code: 0 for a completed verb, 1 for a finding that fails the run, 2 for a
|
|
194
183
|
* usage error, a requested source edit or a refused configuration, 3 for a failure that
|
|
195
184
|
* produced no answer. It writes nothing outside `out` and `err` and never exits the
|
|
196
|
-
* process.
|
|
197
|
-
* `openClient` opens the compiler client; a caller that watches what a run reads
|
|
198
|
-
* supplies its own of either.
|
|
185
|
+
* process. A caller that watches what a run reads supplies its own `host` or `openClient`.
|
|
199
186
|
*/
|
|
200
187
|
export function run(
|
|
201
188
|
args: readonly string[],
|
|
@@ -255,7 +242,7 @@ function invoke(
|
|
|
255
242
|
openClient: (config) => {
|
|
256
243
|
opened = openClient({
|
|
257
244
|
collectTiming: false,
|
|
258
|
-
components: { extensions: config.ts.
|
|
245
|
+
components: { extensions: config.ts.component_extensions, host: streams.host },
|
|
259
246
|
});
|
|
260
247
|
return opened;
|
|
261
248
|
},
|
|
@@ -268,7 +255,7 @@ function invoke(
|
|
|
268
255
|
|
|
269
256
|
/** Turns one refusal into the exit code the Contract's table gives it. */
|
|
270
257
|
function refuse(error: unknown, err: Writer): number {
|
|
271
|
-
if (error instanceof
|
|
258
|
+
if (error instanceof UsageError) {
|
|
272
259
|
diagnose(err, error.message);
|
|
273
260
|
err.write(USAGE);
|
|
274
261
|
return EXIT_USAGE;
|
|
@@ -286,7 +273,7 @@ function refuse(error: unknown, err: Writer): number {
|
|
|
286
273
|
}
|
|
287
274
|
return EXIT_FAILURE;
|
|
288
275
|
}
|
|
289
|
-
if (error instanceof ScopeError) {
|
|
276
|
+
if (error instanceof ScopeError || error instanceof TargetDocumentError) {
|
|
290
277
|
diagnose(err, error.message);
|
|
291
278
|
return EXIT_FAILURE;
|
|
292
279
|
}
|