@cplieger/deadset-ts 8.0.0-dev.6 → 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/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +37 -56
- 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 +6 -82
- package/dist/src/configuration-files.js +27 -168
- 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/contract.generated.js +13 -0
- package/dist/src/contract/report.generated.js +3 -2
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +14 -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 +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +0 -4
- package/dist/src/exempt.js +6 -22
- package/dist/src/file-facts.js +2 -25
- 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/reads-and-writes.js +2 -1
- package/dist/src/findings/self-check.js +3 -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 +7 -12
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +59 -0
- 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/injection-container.js +1 -1
- package/dist/src/intra-function-parts.js +10 -13
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +0 -7
- 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 +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +3 -225
- package/dist/src/report.js +22 -31
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -724
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +55 -61
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +34 -103
- package/dist/src/serialization-contract.js +5 -9
- package/dist/src/session.js +2 -1
- package/dist/src/suppress-file.js +21 -25
- package/dist/src/suppress.js +3 -3
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +9 -19
- 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/usage.js +10 -0
- package/dist/src/verbs/analyze.js +25 -26
- package/dist/src/verbs/describe.js +4 -4
- package/dist/src/verbs/explain.js +8 -6
- package/dist/src/verbs/print-projects.js +4 -3
- package/dist/src/verbs/print-retained.js +2 -1
- package/dist/src/verbs/print-roots.js +2 -1
- package/dist/src/verbs/version.js +2 -1
- 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 +1 -1
- package/src/analysis.ts +55 -76
- 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 +31 -296
- package/src/configuration-files.ts +27 -170
- package/src/configured-declarations.ts +18 -23
- package/src/consumers.ts +4 -4
- package/src/contract/config.generated.ts +798 -0
- package/src/contract/contract.generated.ts +25 -0
- package/src/contract/report.generated.ts +3 -2
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +15 -40
- 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 +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +0 -5
- package/src/exempt.ts +8 -30
- package/src/file-facts.ts +6 -24
- 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/reads-and-writes.ts +2 -1
- package/src/findings/self-check.ts +3 -2
- package/src/findings/unused-declarations.ts +1 -1
- package/src/findings/visibility-narrowing.ts +2 -2
- package/src/findings-pass.ts +9 -15
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +74 -0
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/host.ts +12 -5
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/injection-container.ts +1 -1
- package/src/intra-function-parts.ts +10 -14
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +0 -8
- 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 +12 -92
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +3 -301
- package/src/report.ts +24 -34
- package/src/reporters/sarif.ts +4 -4
- package/src/reporters/template.ts +4 -20
- package/src/resolve.ts +283 -1114
- package/src/roots.ts +7 -20
- package/src/run.ts +57 -75
- package/src/schema.ts +391 -388
- package/src/scope.ts +40 -126
- package/src/serialization-contract.ts +5 -10
- package/src/session.ts +2 -1
- package/src/suppress-file.ts +21 -29
- package/src/suppress.ts +3 -3
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +15 -25
- 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/usage.ts +10 -0
- package/src/verbs/analyze.ts +32 -42
- package/src/verbs/describe.ts +4 -4
- package/src/verbs/explain.ts +8 -10
- package/src/verbs/print-projects.ts +4 -3
- package/src/verbs/print-retained.ts +2 -1
- package/src/verbs/print-roots.ts +2 -1
- package/src/verbs/verb.ts +2 -2
- package/src/verbs/version.ts +2 -1
- 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/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_FAILURE, } from "../contract/contract.generated.js";
|
|
2
2
|
import { runAnalysis, runBaselineWrite, unansweredCounts, } from "../analysis.js";
|
|
3
|
-
import {
|
|
3
|
+
import { kindOf } from "../config.js";
|
|
4
4
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.js";
|
|
5
5
|
import { moduleIdentity } from "../consumers.js";
|
|
6
6
|
import { analyzerProvenance, verdictOf } from "../findings-pass.js";
|
|
7
7
|
import { dirnamePath, joinPath, normalizePath, relativePath, resolvePath } from "../paths.js";
|
|
8
8
|
import { partialNotes } from "../partial.js";
|
|
9
|
-
import { buildReport,
|
|
9
|
+
import { buildReport, capFindings, sortFindings, } from "../report.js";
|
|
10
10
|
import { reportRefusals } from "../report-schema.js";
|
|
11
11
|
import { json } from "../reporters/json.js";
|
|
12
12
|
import { RENDERINGS } from "../reporters/reporters.js";
|
|
@@ -14,6 +14,9 @@ import { parseTemplate, TemplateError } from "../reporters/template.js";
|
|
|
14
14
|
import { resolve } from "../resolve.js";
|
|
15
15
|
import { otherSchemeRows, rowReason, writeBaseline } from "../suppress-file.js";
|
|
16
16
|
import {} from "./verb.js";
|
|
17
|
+
import { UsageError } from "../usage.js";
|
|
18
|
+
import { bytewise } from "../text/compare.js";
|
|
19
|
+
import { diagnose, message } from "../product.js";
|
|
17
20
|
/** The options `analyze` takes beside the ones every verb takes. */
|
|
18
21
|
export const ANALYZE_OPTIONS = {
|
|
19
22
|
plain: ["report", "baseline-write", "exit-code", "template"],
|
|
@@ -31,7 +34,7 @@ class ReportPathError extends Error {
|
|
|
31
34
|
}
|
|
32
35
|
/** The formats this analyzer renders, for a message that names them. */
|
|
33
36
|
function formatNames() {
|
|
34
|
-
return [...RENDERINGS.keys()].sort().join(", ");
|
|
37
|
+
return [...RENDERINGS.keys()].sort(bytewise).join(", ");
|
|
35
38
|
}
|
|
36
39
|
/** The renderings one invocation writes: the ones it named, else the configuration's. */
|
|
37
40
|
function formatsOf(named, configured) {
|
|
@@ -39,10 +42,10 @@ function formatsOf(named, configured) {
|
|
|
39
42
|
const seen = new Set();
|
|
40
43
|
for (const format of formats) {
|
|
41
44
|
if (!RENDERINGS.has(format)) {
|
|
42
|
-
throw new
|
|
45
|
+
throw new UsageError(`${JSON.stringify(format)} is not a format this analyzer renders: the formats are ${formatNames()}`);
|
|
43
46
|
}
|
|
44
47
|
if (seen.has(format)) {
|
|
45
|
-
throw new
|
|
48
|
+
throw new UsageError(`the format ${JSON.stringify(format)} is named twice`);
|
|
46
49
|
}
|
|
47
50
|
seen.add(format);
|
|
48
51
|
}
|
|
@@ -128,7 +131,7 @@ function counted(n, noun) {
|
|
|
128
131
|
function templateOf(host, invoked, named, formats) {
|
|
129
132
|
if (named === undefined || named === "") {
|
|
130
133
|
if (formats.includes("template")) {
|
|
131
|
-
throw new
|
|
134
|
+
throw new UsageError("the template format renders the template --template names, and none was named");
|
|
132
135
|
}
|
|
133
136
|
return undefined;
|
|
134
137
|
}
|
|
@@ -137,7 +140,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
137
140
|
source = host.readFile(resolvePath(invoked, named));
|
|
138
141
|
}
|
|
139
142
|
catch (error) {
|
|
140
|
-
throw new
|
|
143
|
+
throw new UsageError(`--template=${named}: ${message(error)}`);
|
|
141
144
|
}
|
|
142
145
|
try {
|
|
143
146
|
return parseTemplate(source);
|
|
@@ -146,7 +149,7 @@ function templateOf(host, invoked, named, formats) {
|
|
|
146
149
|
if (!(error instanceof TemplateError)) {
|
|
147
150
|
throw error;
|
|
148
151
|
}
|
|
149
|
-
throw new
|
|
152
|
+
throw new UsageError(`--template=${named}: ${error.message}`);
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
/**
|
|
@@ -160,17 +163,13 @@ function templateOf(host, invoked, named, formats) {
|
|
|
160
163
|
export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
|
|
161
164
|
const reportPath = option("report");
|
|
162
165
|
if (reportPath === undefined || reportPath === "") {
|
|
163
|
-
throw new
|
|
166
|
+
throw new UsageError("analyze writes its report to the path --report names, and no path was named");
|
|
164
167
|
}
|
|
165
168
|
const exitCode = option("exit-code") ?? EXIT_CODE_ON;
|
|
166
169
|
if (exitCode !== EXIT_CODE_ON && exitCode !== EXIT_CODE_OFF) {
|
|
167
|
-
throw new
|
|
170
|
+
throw new UsageError(`--exit-code=${JSON.stringify(exitCode)}: the values are ${EXIT_CODE_ON} and ${EXIT_CODE_OFF}`);
|
|
168
171
|
}
|
|
169
172
|
const { config, provenance } = resolve(inputs());
|
|
170
|
-
const kind = config.targetKind;
|
|
171
|
-
if (kind === "") {
|
|
172
|
-
throw new ConfigError("missing-target-kind", "target.kind", "target.kind is not set");
|
|
173
|
-
}
|
|
174
173
|
const invoked = host.workingDirectory();
|
|
175
174
|
const baseline = option("baseline-write");
|
|
176
175
|
const documents = {
|
|
@@ -186,7 +185,7 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
186
185
|
version = host.analyzerVersion();
|
|
187
186
|
}
|
|
188
187
|
catch (error) {
|
|
189
|
-
err
|
|
188
|
+
diagnose(err, message(error));
|
|
190
189
|
return EXIT_FAILURE;
|
|
191
190
|
}
|
|
192
191
|
let report;
|
|
@@ -225,7 +224,7 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
225
224
|
conformance: CONFORMANCE,
|
|
226
225
|
declaredGaps: DECLARED_GAPS,
|
|
227
226
|
target: {
|
|
228
|
-
kind: kindOf(kind
|
|
227
|
+
kind: kindOf(config.target.kind),
|
|
229
228
|
root,
|
|
230
229
|
identity: moduleIdentity(host, scoped.target),
|
|
231
230
|
},
|
|
@@ -247,14 +246,14 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
247
246
|
...unseenLoadNotes(run.unseenLoads),
|
|
248
247
|
],
|
|
249
248
|
});
|
|
250
|
-
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.
|
|
249
|
+
report = capFindings(sortFindings(built, config.reporters.sort), config.reporters.max_findings);
|
|
251
250
|
verdict = verdictOf(result, config);
|
|
252
251
|
}
|
|
253
252
|
catch (error) {
|
|
254
253
|
if (!(error instanceof ReportPathError)) {
|
|
255
254
|
throw error;
|
|
256
255
|
}
|
|
257
|
-
err
|
|
256
|
+
diagnose(err, error.message);
|
|
258
257
|
return EXIT_FAILURE;
|
|
259
258
|
}
|
|
260
259
|
try {
|
|
@@ -281,30 +280,30 @@ export const analyzeVerb = ({ err, host, inputs, scope, openClient, option, repe
|
|
|
281
280
|
}
|
|
282
281
|
}
|
|
283
282
|
catch (error) {
|
|
284
|
-
err
|
|
283
|
+
diagnose(err, message(error));
|
|
285
284
|
return EXIT_FAILURE;
|
|
286
285
|
}
|
|
287
286
|
for (const note of partial) {
|
|
288
|
-
err
|
|
287
|
+
diagnose(err, note);
|
|
289
288
|
}
|
|
290
289
|
for (const line of [...unread, ...unfound]) {
|
|
291
|
-
err
|
|
290
|
+
diagnose(err, line);
|
|
292
291
|
}
|
|
293
292
|
for (const warning of warnings) {
|
|
294
|
-
err
|
|
293
|
+
diagnose(err, `${warning.path}:${String(warning.line)}: a <script> start tag that begins a line is not read as a block: ${warning.reason}`);
|
|
295
294
|
}
|
|
296
295
|
for (const skip of report.type_error_skips) {
|
|
297
|
-
err
|
|
296
|
+
diagnose(err, `${skip.path}:${String(skip.line)}: ${skip.message}: the function or statement holding this type error is not evaluated`);
|
|
298
297
|
}
|
|
299
298
|
if (report.totals.pending > 0) {
|
|
300
|
-
err
|
|
299
|
+
diagnose(err, `${counted(report.totals.pending, "pending finding")}: the report names a declared cross-language edge, and no merge has resolved it`);
|
|
301
300
|
}
|
|
302
301
|
if (report.totals.stale_suppressions > 0) {
|
|
303
|
-
err
|
|
302
|
+
diagnose(err, counted(report.totals.stale_suppressions, "stale suppression"));
|
|
304
303
|
}
|
|
305
304
|
if (exitCode === EXIT_CODE_OFF) {
|
|
306
305
|
if (verdict !== EXIT_CLEAN) {
|
|
307
|
-
err
|
|
306
|
+
diagnose(err, `the exit code is configured off: the verdict of this run is ${String(verdict)}`);
|
|
308
307
|
}
|
|
309
308
|
return EXIT_CLEAN;
|
|
310
309
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN, EXIT_FAILURE, EXIT_USAGE, SCHEMA_VERSIONS, } from "../contract/contract.generated.js";
|
|
2
2
|
import { CONFORMANCE } from "../conformance.js";
|
|
3
|
-
import { ANALYZER_NAME, LANGUAGE } from "../report.js";
|
|
4
3
|
import {} from "./verb.js";
|
|
4
|
+
import { diagnose, LANGUAGE, message, PRODUCT } from "../product.js";
|
|
5
5
|
/**
|
|
6
6
|
* The document `describe` writes: the analyzer, the Contract version it implements, the
|
|
7
7
|
* report schema versions it reads, the languages it claims, and its recorded result over
|
|
@@ -9,7 +9,7 @@ import {} from "./verb.js";
|
|
|
9
9
|
*/
|
|
10
10
|
export function describeDocument(version, recorded) {
|
|
11
11
|
const document = {
|
|
12
|
-
name:
|
|
12
|
+
name: PRODUCT,
|
|
13
13
|
version,
|
|
14
14
|
contract_version: CONTRACT_VERSION,
|
|
15
15
|
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
@@ -29,7 +29,7 @@ export function describeDocument(version, recorded) {
|
|
|
29
29
|
/** Writes this analyzer's description to the output stream, and nothing to the error stream. */
|
|
30
30
|
export const describeVerb = ({ out, err, host, args }) => {
|
|
31
31
|
if (args.length > 0) {
|
|
32
|
-
err
|
|
32
|
+
diagnose(err, `describe takes no argument, got ${JSON.stringify(args[0])}`);
|
|
33
33
|
return EXIT_USAGE;
|
|
34
34
|
}
|
|
35
35
|
let version;
|
|
@@ -37,7 +37,7 @@ export const describeVerb = ({ out, err, host, args }) => {
|
|
|
37
37
|
version = host.analyzerVersion();
|
|
38
38
|
}
|
|
39
39
|
catch (error) {
|
|
40
|
-
err
|
|
40
|
+
diagnose(err, `describe: ${message(error)}`);
|
|
41
41
|
return EXIT_FAILURE;
|
|
42
42
|
}
|
|
43
43
|
out.write(describeDocument(version, CONFORMANCE));
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EXIT_CLEAN, EXIT_USAGE } from "../contract/contract.generated.js";
|
|
2
2
|
import { runAnalysis } from "../analysis.js";
|
|
3
3
|
import { listing } from "../components.js";
|
|
4
|
-
import { ConfigError } from "../config.js";
|
|
5
4
|
import { OUTSIDE, swept as judged } from "../graph.js";
|
|
6
5
|
import { referenceKey } from "../matrix.js";
|
|
7
6
|
import { partialNotes } from "../partial.js";
|
|
@@ -9,6 +8,9 @@ import { resolvePath } from "../paths.js";
|
|
|
9
8
|
import { positionKey } from "../position.js";
|
|
10
9
|
import { resolve } from "../resolve.js";
|
|
11
10
|
import {} from "./verb.js";
|
|
11
|
+
import { UsageError } from "../usage.js";
|
|
12
|
+
import { bytewise } from "../text/compare.js";
|
|
13
|
+
import { diagnose } from "../product.js";
|
|
12
14
|
/**
|
|
13
15
|
* The three spellings of an explanation request, each naming the symbol to explain. They are
|
|
14
16
|
* one request, because exactly one state holds for a symbol and the answer is that state.
|
|
@@ -34,10 +36,10 @@ function namedSymbol(option) {
|
|
|
34
36
|
});
|
|
35
37
|
const [only, ...more] = named;
|
|
36
38
|
if (only === undefined) {
|
|
37
|
-
throw new
|
|
39
|
+
throw new UsageError("explain explains the symbol --why, --why-live or --why-not names, and none was named");
|
|
38
40
|
}
|
|
39
41
|
if (more.length > 0) {
|
|
40
|
-
throw new
|
|
42
|
+
throw new UsageError(`explain explains one symbol, and ${named.map((one) => `--${one.name}`).join(", ")} each name one`);
|
|
41
43
|
}
|
|
42
44
|
return only.value;
|
|
43
45
|
}
|
|
@@ -74,7 +76,7 @@ function subjectOf(symbols, named) {
|
|
|
74
76
|
const partial = symbols
|
|
75
77
|
.filter((symbol) => symbol.ref.toLowerCase().includes(folded) || symbol.name.toLowerCase().includes(folded))
|
|
76
78
|
.map(described)
|
|
77
|
-
.sort(
|
|
79
|
+
.sort(bytewise);
|
|
78
80
|
return { candidates: partial.slice(0, MAX_PARTIAL_MATCHES) };
|
|
79
81
|
}
|
|
80
82
|
/** Every reference of the run once, a reference several configurations make counted once. */
|
|
@@ -318,7 +320,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
|
|
|
318
320
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
319
321
|
});
|
|
320
322
|
if (subject === undefined) {
|
|
321
|
-
err
|
|
323
|
+
diagnose(err, `${JSON.stringify(named)} names no one symbol of the target`);
|
|
322
324
|
for (const candidate of candidates) {
|
|
323
325
|
err.write(` ${candidate}\n`);
|
|
324
326
|
}
|
|
@@ -330,7 +332,7 @@ export const explainVerb = ({ out, err, host, inputs, scope, openClient, option
|
|
|
330
332
|
out.write(explanation(analysis, subject, config.reporters.cascade));
|
|
331
333
|
}
|
|
332
334
|
for (const note of notes) {
|
|
333
|
-
err
|
|
335
|
+
diagnose(err, note);
|
|
334
336
|
}
|
|
335
337
|
return subject === undefined ? EXIT_USAGE : EXIT_CLEAN;
|
|
336
338
|
};
|
|
@@ -2,8 +2,9 @@ import { EXIT_CLEAN } from "../contract/contract.generated.js";
|
|
|
2
2
|
import { runProjects } from "../analysis.js";
|
|
3
3
|
import { partialNotes } from "../partial.js";
|
|
4
4
|
import { resolvePath } from "../paths.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolvePartial } from "../resolve.js";
|
|
6
6
|
import {} from "./verb.js";
|
|
7
|
+
import { diagnose } from "../product.js";
|
|
7
8
|
/**
|
|
8
9
|
* Writes the name of every project one run analyzes, one per line: the identifier the
|
|
9
10
|
* build matrix declares for it, or, where discovery derived the project, its
|
|
@@ -12,7 +13,7 @@ import {} from "./verb.js";
|
|
|
12
13
|
* instead, and one that fails the analysis fails this command the same way.
|
|
13
14
|
*/
|
|
14
15
|
export const printProjectsVerb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
15
|
-
const config =
|
|
16
|
+
const config = resolvePartial(inputs());
|
|
16
17
|
const scoped = scope();
|
|
17
18
|
const answer = runProjects(openClient(config), host, scoped, config);
|
|
18
19
|
for (const id of answer.configurations) {
|
|
@@ -23,7 +24,7 @@ export const printProjectsVerb = ({ out, err, host, inputs, scope, openClient })
|
|
|
23
24
|
unanswered: answer.unanswered,
|
|
24
25
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
25
26
|
})) {
|
|
26
|
-
err
|
|
27
|
+
diagnose(err, note);
|
|
27
28
|
}
|
|
28
29
|
return EXIT_CLEAN;
|
|
29
30
|
};
|
|
@@ -5,6 +5,7 @@ import { partialNotes } from "../partial.js";
|
|
|
5
5
|
import { resolvePath } from "../paths.js";
|
|
6
6
|
import { resolve } from "../resolve.js";
|
|
7
7
|
import {} from "./verb.js";
|
|
8
|
+
import { diagnose } from "../product.js";
|
|
8
9
|
/**
|
|
9
10
|
* Writes every declaration an exemption held back in the production sweep a report is
|
|
10
11
|
* built from, one per line, and nothing where no exemption held one back. One fact
|
|
@@ -27,7 +28,7 @@ export const printRetainedVerb = ({ out, err, host, inputs, scope, openClient })
|
|
|
27
28
|
unanswered: swept.unanswered,
|
|
28
29
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
29
30
|
})) {
|
|
30
|
-
err
|
|
31
|
+
diagnose(err, note);
|
|
31
32
|
}
|
|
32
33
|
return EXIT_CLEAN;
|
|
33
34
|
};
|
|
@@ -4,6 +4,7 @@ import { partialNotes } from "../partial.js";
|
|
|
4
4
|
import { resolvePath } from "../paths.js";
|
|
5
5
|
import { resolve } from "../resolve.js";
|
|
6
6
|
import {} from "./verb.js";
|
|
7
|
+
import { diagnose } from "../product.js";
|
|
7
8
|
/**
|
|
8
9
|
* One root as a line: the declaration's reference, why it is a root, and the string
|
|
9
10
|
* that named it, separated by tabs. A run of several configurations adds the ones
|
|
@@ -40,7 +41,7 @@ export const printRootsVerb = ({ out, err, host, inputs, scope, openClient }) =>
|
|
|
40
41
|
unanswered: answer.unanswered,
|
|
41
42
|
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
42
43
|
})) {
|
|
43
|
-
err
|
|
44
|
+
diagnose(err, note);
|
|
44
45
|
}
|
|
45
46
|
return answer.findings.length > 0 ? EXIT_FINDINGS : EXIT_CLEAN;
|
|
46
47
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CONTRACT_VERSION, EXIT_CLEAN } from "../contract/contract.generated.js";
|
|
2
2
|
import {} from "./verb.js";
|
|
3
|
+
import { PRODUCT } from "../product.js";
|
|
3
4
|
/** Writes the version the host reports and the Contract version this analyzer implements. */
|
|
4
5
|
export const versionVerb = ({ out, host }) => {
|
|
5
|
-
out.write(
|
|
6
|
+
out.write(`${PRODUCT} ${host.analyzerVersion()}\ncontract ${CONTRACT_VERSION}\n`);
|
|
6
7
|
return EXIT_CLEAN;
|
|
7
8
|
};
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* path from the target root. A workflow or hook file is read as text, line by line, with no
|
|
4
4
|
* YAML parsed.
|
|
5
5
|
*/
|
|
6
|
-
import { scriptTokens } from "./manifest.js";
|
|
7
6
|
import { joinPath, relativePath, resolvePath } from "./paths.js";
|
|
7
|
+
import { filesIn } from "./fs/tree.js";
|
|
8
|
+
import { commandsOf, scriptTokens, unquoted, withoutComment } from "./text/shell.js";
|
|
8
9
|
const WORKFLOWS = ".github/workflows";
|
|
9
10
|
/** A line holding a run command: indentation and one sequence marker before `run:`. */
|
|
10
11
|
const RUN_LINE = /^(\s*(?:- )?)run:(.*)$/u;
|
|
@@ -19,26 +20,13 @@ const HOOK_MANAGERS = [
|
|
|
19
20
|
];
|
|
20
21
|
/** The names GNU make reads a makefile under, in the order it tries them. */
|
|
21
22
|
const MAKEFILES = ["GNUmakefile", "makefile", "Makefile"];
|
|
22
|
-
/** The operators that end one shell command of a recipe line. */
|
|
23
|
-
const COMMAND_SEPARATORS = /&&|\|\||;|\|/u;
|
|
24
23
|
const BLOCK_SCALAR = /^[|>][+-]?$/u;
|
|
25
|
-
const QUOTED = /^(["'])(.*)\1$/su;
|
|
26
24
|
/** A token holding one of these is computed or a pattern, so it names no file. */
|
|
27
25
|
const COMPUTED = /[${*?[]/u;
|
|
28
26
|
const INDENT = /^\s*/u;
|
|
29
|
-
/** The files directly in one directory whose name `accepts`, in name order. */
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
entries = host.readDirectory(dir);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
return entries
|
|
39
|
-
.filter((entry) => !entry.directory && accepts(entry.name))
|
|
40
|
-
.map((entry) => joinPath(dir, entry.name))
|
|
41
|
-
.sort();
|
|
27
|
+
/** The files directly in one directory whose name `accepts`, by path in name order. */
|
|
28
|
+
function pathsIn(host, dir, accepts) {
|
|
29
|
+
return filesIn(host, dir, accepts).map((name) => joinPath(dir, name));
|
|
42
30
|
}
|
|
43
31
|
function readText(host, path) {
|
|
44
32
|
try {
|
|
@@ -58,7 +46,7 @@ export function runCommands(text, key = RUN_LINE) {
|
|
|
58
46
|
continue;
|
|
59
47
|
}
|
|
60
48
|
const column = (match[1] ?? "").length;
|
|
61
|
-
const value = (match[2] ?? "").trim()
|
|
49
|
+
const value = unquoted((match[2] ?? "").trim());
|
|
62
50
|
if (!BLOCK_SCALAR.test(value)) {
|
|
63
51
|
found.push(value);
|
|
64
52
|
continue;
|
|
@@ -88,7 +76,7 @@ function recipeLines(host, root) {
|
|
|
88
76
|
.replace(/\\\r?\n/gu, " ")
|
|
89
77
|
.split(/\r?\n/u)
|
|
90
78
|
.filter((line) => line.startsWith("\t"))
|
|
91
|
-
.map((line) => withoutComment(line.slice(1).replace(/^[@+-]+/u, ""))
|
|
79
|
+
.map((line) => commandsOf(withoutComment(line.slice(1).replace(/^[@+-]+/u, ""))));
|
|
92
80
|
}
|
|
93
81
|
/**
|
|
94
82
|
* Every token the recipe lines of the target root's makefile hold, each with the directory
|
|
@@ -113,31 +101,6 @@ function recipeTokens(host, root) {
|
|
|
113
101
|
}
|
|
114
102
|
return found;
|
|
115
103
|
}
|
|
116
|
-
/** One shell line up to its comment: a `#` that starts a word outside quotes. */
|
|
117
|
-
function withoutComment(line) {
|
|
118
|
-
let quote = "";
|
|
119
|
-
for (let at = 0; at < line.length; at += 1) {
|
|
120
|
-
const char = line[at];
|
|
121
|
-
if (quote !== "") {
|
|
122
|
-
if (char === quote) {
|
|
123
|
-
quote = "";
|
|
124
|
-
}
|
|
125
|
-
else if (char === "\\" && quote === '"') {
|
|
126
|
-
at += 1;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else if (char === "'" || char === '"') {
|
|
130
|
-
quote = char;
|
|
131
|
-
}
|
|
132
|
-
else if (char === "\\") {
|
|
133
|
-
at += 1;
|
|
134
|
-
}
|
|
135
|
-
else if (char === "#" && (at === 0 || /\s/u.test(line[at - 1] ?? ""))) {
|
|
136
|
-
return line.slice(0, at);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return line;
|
|
140
|
-
}
|
|
141
104
|
/**
|
|
142
105
|
* Every command the target root's workflow steps, git hooks and makefile run: the `run:`
|
|
143
106
|
* steps of the workflow files in `.github/workflows`, every line of each file in `.husky`
|
|
@@ -159,11 +122,11 @@ export function workflowTokens(host, targetRoot) {
|
|
|
159
122
|
/** The commands of the workflow steps, git hooks and hook managers at one root. */
|
|
160
123
|
function stepCommands(host, root) {
|
|
161
124
|
const commands = [];
|
|
162
|
-
for (const path of
|
|
125
|
+
for (const path of pathsIn(host, joinPath(root, WORKFLOWS), (name) => /\.ya?ml$/u.test(name))) {
|
|
163
126
|
commands.push(...runCommands(readText(host, path) ?? ""));
|
|
164
127
|
}
|
|
165
128
|
for (const dir of HOOK_DIRS) {
|
|
166
|
-
for (const path of
|
|
129
|
+
for (const path of pathsIn(host, joinPath(root, dir), () => true)) {
|
|
167
130
|
commands.push(...(readText(host, path) ?? "").split(/\r?\n/u).map(withoutComment));
|
|
168
131
|
}
|
|
169
132
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { emittedFrom } from "./emit-map.js";
|
|
9
9
|
import { readManifest } from "./manifest.js";
|
|
10
10
|
import { joinPath, relativePath } from "./paths.js";
|
|
11
|
+
import { MANIFEST } from "./fs/manifest.js";
|
|
11
12
|
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
12
13
|
const SUBPATH_WILDCARD = "*";
|
|
13
14
|
/** The paths one written target stands for in its member's sources, itself included. */
|
|
@@ -53,8 +54,7 @@ export function memberEntries(host, resolver, targetRoot) {
|
|
|
53
54
|
return [];
|
|
54
55
|
}
|
|
55
56
|
const manifest = readManifest(host, member.dir);
|
|
56
|
-
const where = relativePath(targetRoot, joinPath(member.dir,
|
|
57
|
-
joinPath(member.dir, "package.json");
|
|
57
|
+
const where = relativePath(targetRoot, joinPath(member.dir, MANIFEST)) ?? joinPath(member.dir, MANIFEST);
|
|
58
58
|
const kept = manifest.entries.flatMap((entry) => {
|
|
59
59
|
const exported = entry.member.startsWith("exports");
|
|
60
60
|
if (member.private && exported) {
|
|
@@ -9,6 +9,8 @@ import { ModuleKind, ModuleResolutionKind } from "@typescript/native/sync";
|
|
|
9
9
|
import { isStringLiteralLikeNode } from "@typescript/native/ast";
|
|
10
10
|
import { baseUrlPaths } from "./base-url.js";
|
|
11
11
|
import { dirnamePath, resolvePath } from "./paths.js";
|
|
12
|
+
import { bytewise } from "./text/compare.js";
|
|
13
|
+
import { PRODUCT } from "./product.js";
|
|
12
14
|
/** The most questions one round trip carries. */
|
|
13
15
|
const BATCH = 2048;
|
|
14
16
|
/**
|
|
@@ -55,7 +57,7 @@ function unstatable(reading) {
|
|
|
55
57
|
export function writtenPath(host, configFile) {
|
|
56
58
|
const stem = configFile.replace(/\.json$/u, "");
|
|
57
59
|
for (let counter = 0;; counter += 1) {
|
|
58
|
-
const path = `${stem}
|
|
60
|
+
const path = `${stem}.${PRODUCT}${counter === 0 ? "" : `-${String(counter)}`}.json`;
|
|
59
61
|
if (host.kindOf(path) === "absent") {
|
|
60
62
|
return path;
|
|
61
63
|
}
|
|
@@ -102,10 +104,10 @@ function configurationOf(engine, host, reading) {
|
|
|
102
104
|
...(exclude === undefined ? {} : { exclude }),
|
|
103
105
|
});
|
|
104
106
|
const file = writtenPath(host, reading.configFile);
|
|
105
|
-
const expected = [...reading.parsed.fileNames].sort().join("\u0000");
|
|
107
|
+
const expected = [...reading.parsed.fileNames].sort(bytewise).join("\u0000");
|
|
106
108
|
const listed = (value) => {
|
|
107
109
|
try {
|
|
108
|
-
return [...engine.parseConfigJson(value, file).fileNames].sort().join("\u0000");
|
|
110
|
+
return [...engine.parseConfigJson(value, file).fileNames].sort(bytewise).join("\u0000");
|
|
109
111
|
}
|
|
110
112
|
catch {
|
|
111
113
|
return undefined;
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { emittedFrom } from "./emit-map.js";
|
|
9
9
|
import { gitIgnored } from "./git-ignore.js";
|
|
10
|
-
import { dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
10
|
+
import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
11
11
|
import { satisfies } from "./version-range.js";
|
|
12
|
+
import { isRecord } from "./json-document.js";
|
|
13
|
+
import { INSTALLED_PACKAGES, MANIFEST, manifestAt } from "./fs/manifest.js";
|
|
12
14
|
/** The manifest members that name an entry point for the subpath `.` without `exports`. */
|
|
13
15
|
const LEGACY_MEMBERS = ["types", "typings", "module", "main"];
|
|
14
16
|
/** The dependency members a manifest declares other packages under. */
|
|
@@ -20,9 +22,6 @@ const DEPENDENCY_MEMBERS = [
|
|
|
20
22
|
];
|
|
21
23
|
const SOURCE = /(?<!\.d)\.(?:[mc]?ts|tsx)$/u;
|
|
22
24
|
const OUTPUT = /(?:\.d\.[mc]?ts|\.[mc]?js|\.jsx)$/u;
|
|
23
|
-
function isRecord(value) {
|
|
24
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
-
}
|
|
26
25
|
/** The package name a bare specifier names, or `undefined` for any other specifier. */
|
|
27
26
|
export function packageNameOf(specifier) {
|
|
28
27
|
if (specifier === "" ||
|
|
@@ -146,7 +145,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
146
145
|
if (links.has(key)) {
|
|
147
146
|
return links.get(key);
|
|
148
147
|
}
|
|
149
|
-
const candidate = joinPath(dir,
|
|
148
|
+
const candidate = joinPath(dir, INSTALLED_PACKAGES, name);
|
|
150
149
|
let found;
|
|
151
150
|
if (host.kindOf(candidate) === "absent") {
|
|
152
151
|
const up = dirnamePath(dir);
|
|
@@ -171,16 +170,13 @@ export function workspaceResolver(host, workspace) {
|
|
|
171
170
|
if (manifests.has(dir)) {
|
|
172
171
|
return manifests.get(dir);
|
|
173
172
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
found =
|
|
182
|
-
up === dir || root === undefined || !inside(root, up) ? undefined : nearestManifest(up);
|
|
183
|
-
}
|
|
173
|
+
const manifest = manifestAt(host, dir);
|
|
174
|
+
const up = dirnamePath(dir);
|
|
175
|
+
const found = manifest !== undefined
|
|
176
|
+
? { dir, manifest }
|
|
177
|
+
: up === dir || root === undefined || !inside(root, up)
|
|
178
|
+
? undefined
|
|
179
|
+
: nearestManifest(up);
|
|
184
180
|
manifests.set(dir, found);
|
|
185
181
|
return found;
|
|
186
182
|
};
|
|
@@ -191,7 +187,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
191
187
|
const member = candidates.find((one) => one.dir === linked);
|
|
192
188
|
return member === undefined ? { kind: "none" } : { kind: "member", member };
|
|
193
189
|
}
|
|
194
|
-
const scope = workspace.memberOf(joinPath(fromDir,
|
|
190
|
+
const scope = workspace.memberOf(joinPath(fromDir, MANIFEST));
|
|
195
191
|
if (scope?.name === name) {
|
|
196
192
|
return { kind: "member", member: scope };
|
|
197
193
|
}
|
|
@@ -221,7 +217,7 @@ export function workspaceResolver(host, workspace) {
|
|
|
221
217
|
* directory however its configuration writes it.
|
|
222
218
|
*/
|
|
223
219
|
const mirrored = (member, path) => {
|
|
224
|
-
for (
|
|
220
|
+
for (const dir of ancestors(dirnamePath(path), member.dir).filter((at) => at !== member.dir)) {
|
|
225
221
|
const found = emittedFrom(path, { outDir: dir, sourceDirs: [member.dir] }).find((candidate) => SOURCE.test(candidate) && !ignored(candidate) && host.kindOf(candidate) === "file");
|
|
226
222
|
if (found !== undefined) {
|
|
227
223
|
return found;
|