@cplieger/deadset-ts 8.0.0-dev.1 → 8.0.0-dev.10
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 +4 -3
- 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/dist/src/session.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { API, SymbolFlags } from "@typescript/native/sync";
|
|
2
2
|
import { componentLayer, mapperManifest } from "./component-layer.js";
|
|
3
|
-
import { DiscoveryError } from "./discover.js";
|
|
4
3
|
import { joinPath } from "./paths.js";
|
|
5
4
|
import { queriesOf, UnansweredLog, } from "./query.js";
|
|
5
|
+
import { MANIFEST } from "./fs/manifest.js";
|
|
6
|
+
/** The most questions one round trip carries. */
|
|
7
|
+
const BATCH = 2048;
|
|
8
|
+
/** Asks one question of every item, in round trips of at most {@link BATCH}, answers in order. */
|
|
9
|
+
export function batched(engine, items, question) {
|
|
10
|
+
const answers = [];
|
|
11
|
+
for (let at = 0; at < items.length; at += BATCH) {
|
|
12
|
+
answers.push(...engine.batch(items.slice(at, at + BATCH).map(question)));
|
|
13
|
+
}
|
|
14
|
+
return answers;
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Opens the compiler client the analysis reads through.
|
|
8
18
|
*
|
|
@@ -16,7 +26,7 @@ export function openEngine(options) {
|
|
|
16
26
|
if (components !== undefined && components.extensions.length > 0) {
|
|
17
27
|
const made = components.host.temporaryDirectory();
|
|
18
28
|
try {
|
|
19
|
-
components.host.writeDocument(joinPath(made.path,
|
|
29
|
+
components.host.writeDocument(joinPath(made.path, MANIFEST), [
|
|
20
30
|
mapperManifest(components.host.componentMapperCommand()),
|
|
21
31
|
]);
|
|
22
32
|
}
|
|
@@ -252,14 +262,13 @@ function projectsOf(snapshot, built, written) {
|
|
|
252
262
|
return opened;
|
|
253
263
|
}
|
|
254
264
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
* `first` runs over every opened project before the first visit and `last` after the last,
|
|
260
|
-
* both while the client answers; the snapshot and the client are then released.
|
|
265
|
+
* Opens every project the compiler configurations name in one never updated snapshot of one
|
|
266
|
+
* client, and hands them to `use` while the client answers; the snapshot and the client are
|
|
267
|
+
* then released. A configuration `options.programs` describes is built as that program, or
|
|
268
|
+
* opened from the configuration it writes. One the snapshot holds nothing for has no view.
|
|
261
269
|
*/
|
|
262
|
-
export function
|
|
270
|
+
export function openSession(engine, configFiles, options, use) {
|
|
271
|
+
const programs = options.programs ?? new Map();
|
|
263
272
|
try {
|
|
264
273
|
const built = [];
|
|
265
274
|
const params = [];
|
|
@@ -281,30 +290,13 @@ export function runSession(engine, configFiles, visit, unopened = "refuse", prog
|
|
|
281
290
|
const opened = projectsOf(snapshot, built, written);
|
|
282
291
|
const log = new UnansweredLog();
|
|
283
292
|
const views = [];
|
|
284
|
-
const missing = [];
|
|
285
293
|
for (const configFile of configFiles) {
|
|
286
294
|
const project = opened.get(configFile);
|
|
287
|
-
if (project
|
|
288
|
-
|
|
289
|
-
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
290
|
-
}
|
|
291
|
-
missing.push(configFile);
|
|
292
|
-
continue;
|
|
295
|
+
if (project !== undefined) {
|
|
296
|
+
views.push(viewOf(project, configFile, engine, log));
|
|
293
297
|
}
|
|
294
|
-
views.push(viewOf(project, configFile, engine, log));
|
|
295
298
|
}
|
|
296
|
-
|
|
297
|
-
configFile: view.configFile,
|
|
298
|
-
fileNames: () => view.ownSourceFiles().map((file) => file.fileName),
|
|
299
|
-
})));
|
|
300
|
-
const projects = views.map((view) => visit(view));
|
|
301
|
-
last();
|
|
302
|
-
return {
|
|
303
|
-
projects,
|
|
304
|
-
unopened: missing,
|
|
305
|
-
timing: engine.getTimingInfo(),
|
|
306
|
-
unanswered: log.questions,
|
|
307
|
-
};
|
|
299
|
+
return use({ views }, () => log.questions);
|
|
308
300
|
}
|
|
309
301
|
finally {
|
|
310
302
|
snapshot.dispose();
|
|
@@ -314,3 +306,16 @@ export function runSession(engine, configFiles, visit, unopened = "refuse", prog
|
|
|
314
306
|
engine.close();
|
|
315
307
|
}
|
|
316
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Runs `visit` over the project {@link openSession} opens for each configuration, and answers
|
|
311
|
+
* in their order; a configuration the snapshot opens nothing for throws before any visit.
|
|
312
|
+
*/
|
|
313
|
+
export function runSession(engine, configFiles, visit) {
|
|
314
|
+
return openSession(engine, configFiles, {}, (opened) => {
|
|
315
|
+
const unopened = configFiles.find((configFile) => !opened.views.some((view) => view.configFile === configFile));
|
|
316
|
+
if (unopened !== undefined) {
|
|
317
|
+
throw new Error(`${unopened} opened no project`);
|
|
318
|
+
}
|
|
319
|
+
return { projects: opened.views.map((view) => visit(view)), timing: engine.getTimingInfo() };
|
|
320
|
+
});
|
|
321
|
+
}
|
package/dist/src/source-files.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* in a document or a string names one: as the file itself, or as a file the
|
|
4
4
|
* compiler emits from it.
|
|
5
5
|
*/
|
|
6
|
-
import { commonDirectory, emittedFrom } from "./emit-map.js";
|
|
6
|
+
import { commonDirectory, emittedFrom } from "./load/emit-map.js";
|
|
7
7
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
8
8
|
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
9
9
|
const SUBPATH_WILDCARD = "*";
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
* one record shape, so one decoder and one matcher serve both, and a row moves into the
|
|
5
5
|
* ignore file by gaining a maintainer's reason.
|
|
6
6
|
*/
|
|
7
|
+
import { CODE } from "./contract/contract.generated.js";
|
|
8
|
+
import { readTargetDocument } from "./fs/document.js";
|
|
7
9
|
import { jsonChunks } from "./json-chunks.js";
|
|
8
|
-
import {
|
|
9
|
-
import { positionAt, walkDocument } from "./json-document.js";
|
|
10
|
+
import { isRecord, positionAt, readStrict, StrictJSONError } from "./json-document.js";
|
|
10
11
|
import { declarationKey } from "./ref.js";
|
|
11
|
-
import { CODE_FORM,
|
|
12
|
+
import { CODE_FORM, SuppressionError, blankReason, isEntryPath, isPartKind, isSymbolRef, siteText, } from "./suppress.js";
|
|
13
|
+
import { message } from "./product.js";
|
|
12
14
|
/** The name and the location the grammar fixes for the ignore file. */
|
|
13
15
|
export const IGNORE_FILE = "deadset-ignore.json";
|
|
14
16
|
/** The name and the location the grammar fixes for the baseline. */
|
|
@@ -42,9 +44,6 @@ const PATH_WANT = "a path relative to the target root, with the solidus as separ
|
|
|
42
44
|
function documentSite(shape) {
|
|
43
45
|
return { path: shape.file, line: 0, column: 0 };
|
|
44
46
|
}
|
|
45
|
-
function isObject(value) {
|
|
46
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47
|
-
}
|
|
48
47
|
/**
|
|
49
48
|
* Reads one entry or row: the records it binds, the refusals it carries, or the
|
|
50
49
|
* malformed value that ends the run.
|
|
@@ -53,7 +52,7 @@ function readRecord(value, site, byReference, shape) {
|
|
|
53
52
|
const refuse = (text, want) => {
|
|
54
53
|
throw new SuppressionError(shape.mechanism, siteText(site), text, want);
|
|
55
54
|
};
|
|
56
|
-
if (!
|
|
55
|
+
if (!isRecord(value)) {
|
|
57
56
|
return refuse(JSON.stringify(value), shape.recordWant);
|
|
58
57
|
}
|
|
59
58
|
for (const [key, held] of Object.entries(value)) {
|
|
@@ -99,10 +98,10 @@ function readRecord(value, site, byReference, shape) {
|
|
|
99
98
|
};
|
|
100
99
|
const refusals = [];
|
|
101
100
|
if (blankReason(reason)) {
|
|
102
|
-
refusals.push({ ...written, reported:
|
|
101
|
+
refusals.push({ ...written, reported: CODE.suppressionWithoutReason });
|
|
103
102
|
}
|
|
104
103
|
if (written.path === "") {
|
|
105
|
-
refusals.push({ ...written, reported:
|
|
104
|
+
refusals.push({ ...written, reported: CODE.unscopedIgnoreEntry });
|
|
106
105
|
}
|
|
107
106
|
if (refusals.length > 0) {
|
|
108
107
|
return { records: [], refusals };
|
|
@@ -131,28 +130,27 @@ function byReferenceOf(symbols) {
|
|
|
131
130
|
}
|
|
132
131
|
/** Reads one document at the target root, binding each record against the run's declarations. */
|
|
133
132
|
function readDocument(host, targetRoot, symbols, shape) {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
133
|
+
const text = readTargetDocument(host, targetRoot, shape.file);
|
|
134
|
+
if (text === undefined) {
|
|
136
135
|
return { records: [], refusals: [] };
|
|
137
136
|
}
|
|
138
|
-
const text = host.readFile(file);
|
|
139
137
|
const malformed = (detail, site = documentSite(shape)) => {
|
|
140
138
|
throw new SuppressionError(shape.mechanism, siteText(site), detail, shape.documentWant);
|
|
141
139
|
};
|
|
142
|
-
let
|
|
140
|
+
let document;
|
|
143
141
|
try {
|
|
144
|
-
|
|
142
|
+
document = readStrict(text, shape.array);
|
|
145
143
|
}
|
|
146
144
|
catch (error) {
|
|
147
|
-
|
|
145
|
+
if (!(error instanceof StrictJSONError)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
return malformed(error.message, positionAt(text, shape.file, error.offset));
|
|
148
149
|
}
|
|
149
|
-
|
|
150
|
+
const parsed = document.value;
|
|
151
|
+
if (!isRecord(parsed)) {
|
|
150
152
|
return malformed(shape.file);
|
|
151
153
|
}
|
|
152
|
-
const walk = walkDocument(text, shape.array);
|
|
153
|
-
if (walk.twice !== undefined) {
|
|
154
|
-
malformed(`the member ${JSON.stringify(walk.twice.name)} is written twice, so neither value is chosen`, positionAt(text, shape.file, walk.twice.offset));
|
|
155
|
-
}
|
|
156
154
|
for (const key of Object.keys(parsed)) {
|
|
157
155
|
if (key !== "description" && key !== shape.array) {
|
|
158
156
|
malformed(`the member ${JSON.stringify(key)} is not one the grammar declares`);
|
|
@@ -167,7 +165,7 @@ function readDocument(host, targetRoot, symbols, shape) {
|
|
|
167
165
|
const records = [];
|
|
168
166
|
const refusals = [];
|
|
169
167
|
held.forEach((value, index) => {
|
|
170
|
-
const offset =
|
|
168
|
+
const offset = document.starts.get(index);
|
|
171
169
|
const site = offset === undefined ? documentSite(shape) : positionAt(text, shape.file, offset);
|
|
172
170
|
const read = readRecord(value, site, byReference, shape);
|
|
173
171
|
records.push(...read.records);
|
|
@@ -227,9 +225,9 @@ export function otherSchemeRows(text, own = OWN_SCHEME) {
|
|
|
227
225
|
parsed = JSON.parse(text);
|
|
228
226
|
}
|
|
229
227
|
catch (error) {
|
|
230
|
-
return refuse(
|
|
228
|
+
return refuse(message(error));
|
|
231
229
|
}
|
|
232
|
-
const held =
|
|
230
|
+
const held = isRecord(parsed) ? parsed[BASELINE_SHAPE.array] : undefined;
|
|
233
231
|
if (!Array.isArray(held)) {
|
|
234
232
|
return refuse(BASELINE_SHAPE.documentWant);
|
|
235
233
|
}
|
|
@@ -238,7 +236,7 @@ export function otherSchemeRows(text, own = OWN_SCHEME) {
|
|
|
238
236
|
readRecord(value, documentSite(BASELINE_SHAPE), new Map(), BASELINE_SHAPE);
|
|
239
237
|
}
|
|
240
238
|
catch (error) {
|
|
241
|
-
return refuse(
|
|
239
|
+
return refuse(message(error));
|
|
242
240
|
}
|
|
243
241
|
// readRecord has checked every member is a string and the code and symbol are present.
|
|
244
242
|
const { code, symbol, path, reason } = value;
|
|
@@ -279,10 +277,9 @@ function* separatorsEscaped(pieces) {
|
|
|
279
277
|
* A baseline holding one row per finding, in the order given, which is the report's, and
|
|
280
278
|
* the other language's rows `kept`, Go's rows ahead of TypeScript's and each scheme's in
|
|
281
279
|
* the order given, so two analyzers writing one document in either order write the same
|
|
282
|
-
* bytes, in the form grammar/suppression.md fixes. Every
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* rather than written, because the grammar requires a reason on every row.
|
|
280
|
+
* bytes, in the form grammar/suppression.md fixes. Every row carries the provenance as
|
|
281
|
+
* its reason, so a finding missing a value a row needs, or an identity missing its name
|
|
282
|
+
* or version, is refused rather than written.
|
|
286
283
|
*/
|
|
287
284
|
export function writeBaseline(findings, provenance, kept = []) {
|
|
288
285
|
if (provenance.analyzer === "" || provenance.version === "") {
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
* one or more codes and a reason, and binding to every declaration that begins on the line
|
|
4
4
|
* below it, or on its own line where it trails a token there.
|
|
5
5
|
*/
|
|
6
|
+
import { CODE } from "./contract/contract.generated.js";
|
|
6
7
|
import { isConstructorDeclaration, SyntaxKind, } from "@typescript/native/ast";
|
|
7
8
|
import { importBindings } from "./import-bindings.js";
|
|
8
9
|
import { isParameterProperty, nodeKey } from "./inventory.js";
|
|
9
10
|
import { byPosition, positionKey, renderPosition } from "./position.js";
|
|
10
11
|
import { importRef } from "./ref.js";
|
|
11
12
|
import { signatureOf } from "./signatures.js";
|
|
12
|
-
import { isPartKind,
|
|
13
|
+
import { isPartKind, SuppressionError, siteText, } from "./suppress.js";
|
|
13
14
|
/**
|
|
14
15
|
* The three expressions the grammar applies to the text of each line comment, in this
|
|
15
16
|
* order: a comment in the namespace, a well-formed directive, and a directive that lacks
|
|
@@ -274,7 +275,7 @@ export function inlineSuppressions(perProject) {
|
|
|
274
275
|
case "no-reason":
|
|
275
276
|
for (const code of directive.codes) {
|
|
276
277
|
refusals.push({
|
|
277
|
-
reported:
|
|
278
|
+
reported: CODE.suppressionWithoutReason,
|
|
278
279
|
code,
|
|
279
280
|
symbol: "",
|
|
280
281
|
path: site.path,
|
package/dist/src/suppress.js
CHANGED
|
@@ -6,23 +6,18 @@
|
|
|
6
6
|
* withholds a finding its code would have produced there, dormant when the configuration
|
|
7
7
|
* withholds every such finding anyway, and stale otherwise.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { KIND_ROWS, SYMBOL_SUBJECTS, } from "./contract/contract.generated.js";
|
|
10
|
+
import { UsageError } from "./usage.js";
|
|
10
11
|
import { positionKey } from "./position.js";
|
|
11
12
|
import { isRef } from "./ref.js";
|
|
12
|
-
/** The code a suppression that carries no reason is reported under. */
|
|
13
|
-
export const SUPPRESSION_WITHOUT_REASON = "DS1701";
|
|
14
|
-
/** The code an ignore entry or a baseline row that names no path is reported under. */
|
|
15
|
-
export const UNSCOPED_ENTRY = "DS1702";
|
|
16
|
-
/** The code a suppression that withholds nothing is reported under. */
|
|
17
|
-
export const STALE_SUPPRESSION = "DS1703";
|
|
18
13
|
/**
|
|
19
14
|
* A directive, an entry or a document the grammar refuses before any finding exists: an
|
|
20
15
|
* instruction the analysis cannot carry out. It is a malformed input, so the command line
|
|
21
16
|
* answers it with the usage code.
|
|
22
17
|
*/
|
|
23
|
-
export class SuppressionError extends
|
|
18
|
+
export class SuppressionError extends UsageError {
|
|
24
19
|
constructor(mechanism, at, text, want) {
|
|
25
|
-
super(
|
|
20
|
+
super(`${mechanism} ${at}: ${text}: want ${want}`);
|
|
26
21
|
this.name = "SuppressionError";
|
|
27
22
|
}
|
|
28
23
|
}
|
|
@@ -57,6 +52,7 @@ export const GO_REF_EXPRESSIONS = [
|
|
|
57
52
|
`^go://${GO_PATH}#${GO_IDENT}$`,
|
|
58
53
|
`^go://${GO_PATH}#${GO_IDENT}(?:\\.${GO_IDENT})+$`,
|
|
59
54
|
`^go://${GO_PATH}#${GO_IDENT}(?:\\.${GO_IDENT})?\\[${GO_IDENT}\\]$`,
|
|
55
|
+
`^go://${GO_PATH}#(?:${GO_IDENT}(?:\\.${GO_IDENT})?)?\\(${GO_IDENT}\\)$`,
|
|
60
56
|
`^go://${GO_PATH}#${GO_FILE}:file$`,
|
|
61
57
|
`^go://${GO_PATH}#${GO_PATH}(?:@${GO_VERSION})?:require$`,
|
|
62
58
|
`^go://${GO_PATH}#${GO_PATH}(?:@${GO_VERSION})?:replace$`,
|
|
@@ -70,73 +66,59 @@ export function isSymbolRef(value) {
|
|
|
70
66
|
export function blankReason(reason) {
|
|
71
67
|
return reason === undefined || /^[ \t\r\n]*$/u.test(reason);
|
|
72
68
|
}
|
|
69
|
+
/** The subject of the kind each live code names. */
|
|
70
|
+
const SUBJECT_OF_CODE = new Map(KIND_ROWS.map((row) => [row.code, row.subject]));
|
|
71
|
+
/** The subject a finding about one symbol kind is about. */
|
|
72
|
+
function subjectOf(kind) {
|
|
73
|
+
return Object.hasOwn(SYMBOL_SUBJECTS, kind)
|
|
74
|
+
? SYMBOL_SUBJECTS[kind]
|
|
75
|
+
: undefined;
|
|
76
|
+
}
|
|
77
|
+
/** Whether a subject is a row of a document rather than a declaration of the program. */
|
|
78
|
+
function rowOf(subject) {
|
|
79
|
+
return subject === "file" || subject === "dependency" || subject === "record";
|
|
80
|
+
}
|
|
73
81
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* and marks nothing, and an inline directive never binds one.
|
|
77
|
-
*/
|
|
78
|
-
const DEPENDENCY_SUBJECTS = new Map([
|
|
79
|
-
["dependency", ["DS1601"]],
|
|
80
|
-
["module-directive", ["DS1605"]],
|
|
81
|
-
]);
|
|
82
|
-
/**
|
|
83
|
-
* The subject kinds that are a row of a document rather than a declaration of the program,
|
|
84
|
-
* each with the codes that report one. Apart from a dependency row, nothing withholds a
|
|
85
|
-
* finding about one.
|
|
82
|
+
* Whether a finding's subject is a row of a document rather than a declaration. Apart
|
|
83
|
+
* from a dependency row, nothing withholds a finding about one.
|
|
86
84
|
*/
|
|
87
|
-
const ROW_SUBJECTS = new Map([
|
|
88
|
-
["file", ["DS1501", "DS1502"]],
|
|
89
|
-
...DEPENDENCY_SUBJECTS,
|
|
90
|
-
["root", ["DS1704"]],
|
|
91
|
-
["configured-declaration", ["DS1706"]],
|
|
92
|
-
["suppression", ["DS1701", "DS1702", "DS1703"]],
|
|
93
|
-
["edge", ["DS1705"]],
|
|
94
|
-
]);
|
|
95
|
-
/** Whether a finding's subject is a row of a document rather than a declaration. */
|
|
96
85
|
export function isRowSubject(kind) {
|
|
97
|
-
return
|
|
86
|
+
return rowOf(subjectOf(kind));
|
|
98
87
|
}
|
|
99
88
|
/** Whether a code reports a row of a document. */
|
|
100
89
|
export function isRowCode(code) {
|
|
101
|
-
return
|
|
90
|
+
return rowOf(SUBJECT_OF_CODE.get(code));
|
|
102
91
|
}
|
|
103
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Whether a finding's subject is a dependency row. An ignore entry or a baseline row
|
|
94
|
+
* naming the row's reference and file withholds its finding and marks nothing, and an
|
|
95
|
+
* inline directive never binds one.
|
|
96
|
+
*/
|
|
104
97
|
export function isDependencySubject(kind) {
|
|
105
|
-
return
|
|
98
|
+
return subjectOf(kind) === "dependency";
|
|
106
99
|
}
|
|
107
100
|
/** Whether a code reports a dependency row, which only an ignore entry or a baseline row names. */
|
|
108
101
|
export function isDependencyCode(code) {
|
|
109
|
-
return
|
|
102
|
+
return SUBJECT_OF_CODE.get(code) === "dependency";
|
|
110
103
|
}
|
|
111
104
|
/** Whether a code reports a row of a document that no record of any mechanism can name. */
|
|
112
105
|
export function isUnsuppressibleCode(code) {
|
|
113
106
|
return isRowCode(code) && !isDependencyCode(code);
|
|
114
107
|
}
|
|
115
108
|
/**
|
|
116
|
-
*
|
|
109
|
+
* Whether a finding's subject is a part of the declaration its reference names: a part
|
|
117
110
|
* has no reference of its own, so a finding about one names the declaration that holds
|
|
118
111
|
* it, and the part is decided inside that declaration whatever uses it.
|
|
119
112
|
*/
|
|
120
|
-
const PART_SUBJECTS = new Set([
|
|
121
|
-
"parameter",
|
|
122
|
-
"receiver",
|
|
123
|
-
"result",
|
|
124
|
-
"statement",
|
|
125
|
-
"case",
|
|
126
|
-
"store",
|
|
127
|
-
]);
|
|
128
|
-
/** Whether a finding's subject is a part of the declaration its reference names. */
|
|
129
113
|
export function isPartSubject(kind) {
|
|
130
|
-
return
|
|
114
|
+
return subjectOf(kind) === "part";
|
|
131
115
|
}
|
|
132
|
-
/** The prefix of the codes of the part kinds, the intra-function range. */
|
|
133
|
-
const PART_KIND_PREFIX = "DS18";
|
|
134
116
|
/**
|
|
135
117
|
* Whether a code is a part kind, whose record withholds its finding and marks nothing:
|
|
136
118
|
* the record says the part is wanted, not that the declaration holding it is.
|
|
137
119
|
*/
|
|
138
120
|
export function isPartKind(code) {
|
|
139
|
-
return
|
|
121
|
+
return SUBJECT_OF_CODE.get(code) === "part";
|
|
140
122
|
}
|
|
141
123
|
/** One finding's identity: its code and the position of the thing it names. */
|
|
142
124
|
function claimKey(finding) {
|
|
@@ -154,12 +136,12 @@ export function pairs(record, finding) {
|
|
|
154
136
|
if (record.code !== finding.code) {
|
|
155
137
|
return false;
|
|
156
138
|
}
|
|
157
|
-
if (
|
|
139
|
+
if (isDependencySubject(finding.symbol.kind)) {
|
|
158
140
|
return (record.mechanism !== "inline" &&
|
|
159
141
|
record.symbol === finding.symbol.ref &&
|
|
160
142
|
record.path === finding.position.path);
|
|
161
143
|
}
|
|
162
|
-
if (
|
|
144
|
+
if (isRowSubject(finding.symbol.kind)) {
|
|
163
145
|
return false;
|
|
164
146
|
}
|
|
165
147
|
if (record.bound !== "" && record.bound === positionKey(finding.position)) {
|
package/dist/src/sweep.js
CHANGED
|
@@ -84,10 +84,10 @@ export function runnerCalls(symbol) {
|
|
|
84
84
|
* That run's roots are the graph's own and, of each test file, its top level and every
|
|
85
85
|
* function it exports, which a test runner may call, and every export of a manual mock,
|
|
86
86
|
* which the runner reads; any other export of a test file, and test-support code, which
|
|
87
|
-
* runs where a test imports it, are judged as any file's are.
|
|
88
|
-
* `
|
|
87
|
+
* runs where a test imports it, are judged as any file's are. `held` seeds it as a mark
|
|
88
|
+
* seeds a sweep; with `namespaces` false a file read reaches none of the file's exports.
|
|
89
89
|
*/
|
|
90
|
-
export function reachedWithTheTests(graph, held) {
|
|
90
|
+
export function reachedWithTheTests(graph, held, namespaces = true) {
|
|
91
91
|
const tests = graph.symbols.flatMap((symbol, at) => {
|
|
92
92
|
const exported = symbol.exported && graph.symbols[graph.parent[at] ?? OUTSIDE]?.kind === "file";
|
|
93
93
|
const called = exported && runnerCalls(symbol);
|
|
@@ -97,9 +97,7 @@ export function reachedWithTheTests(graph, held) {
|
|
|
97
97
|
? [{ at, kind: "test-runner" }]
|
|
98
98
|
: [];
|
|
99
99
|
});
|
|
100
|
-
return reachable({ ...graph, rooted: [...graph.rooted, ...tests] }, flagged(graph, held), {
|
|
101
|
-
production: false,
|
|
102
|
-
});
|
|
100
|
+
return reachable({ ...graph, rooted: [...graph.rooted, ...tests] }, flagged(graph, held), { production: false }, namespaces);
|
|
103
101
|
}
|
|
104
102
|
/**
|
|
105
103
|
* The references a loaded consumer makes that the mode counts. A production mode counts
|
|
@@ -126,7 +124,7 @@ function flagged(graph, ids) {
|
|
|
126
124
|
* included, while a root reaches through the references the mode counts. The held seed
|
|
127
125
|
* runs first, so what it reaches is expanded under the wider rule.
|
|
128
126
|
*/
|
|
129
|
-
function reachable(graph, held, mode) {
|
|
127
|
+
function reachable(graph, held, mode, namespaces = true) {
|
|
130
128
|
const reached = graph.symbols.map(() => false);
|
|
131
129
|
const expanded = graph.symbols.map(() => false);
|
|
132
130
|
const queue = [];
|
|
@@ -154,7 +152,7 @@ function reachable(graph, held, mode) {
|
|
|
154
152
|
if (!holding && mode.production && edge.test) {
|
|
155
153
|
continue;
|
|
156
154
|
}
|
|
157
|
-
enter(edge.to, !edge.evaluation);
|
|
155
|
+
enter(edge.to, namespaces && !edge.evaluation);
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
};
|
|
@@ -169,7 +167,7 @@ function reachable(graph, held, mode) {
|
|
|
169
167
|
}
|
|
170
168
|
// A consumer's reference reaches what it names as one of the target's own does.
|
|
171
169
|
for (const edge of callsOf(graph, mode)) {
|
|
172
|
-
enter(edge.to, !edge.evaluation);
|
|
170
|
+
enter(edge.to, namespaces && !edge.evaluation);
|
|
173
171
|
}
|
|
174
172
|
walk(false);
|
|
175
173
|
return reached;
|
package/dist/src/symbol-keys.js
CHANGED
|
@@ -5,16 +5,14 @@
|
|
|
5
5
|
* No name node resolves to such a member: the key's name resolves to the constant. An
|
|
6
6
|
* iteration reads the member its well-known iteration symbol keys.
|
|
7
7
|
*/
|
|
8
|
-
import { isComputedPropertyName, isForOfStatement, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral,
|
|
8
|
+
import { isComputedPropertyName, isForOfStatement, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isPropertyAccessExpression, isSpreadElement, isStringLiteral, } from "@typescript/native/ast";
|
|
9
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
9
10
|
import { nodeKey } from "./inventory.js";
|
|
10
11
|
import { renderPosition } from "./position.js";
|
|
11
12
|
import { isAnswered } from "./query.js";
|
|
12
13
|
/** The identifier an element access's argument names its key by, where it is a name. */
|
|
13
14
|
export function keyNameOf(argument) {
|
|
14
|
-
|
|
15
|
-
while (isParenthesizedExpression(at)) {
|
|
16
|
-
at = at.expression;
|
|
17
|
-
}
|
|
15
|
+
const at = parenthesesRemoved(argument);
|
|
18
16
|
if (isIdentifier(at)) {
|
|
19
17
|
return at;
|
|
20
18
|
}
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
* type, because nothing ties the name to a type.
|
|
13
13
|
*/
|
|
14
14
|
import { readComponent } from "./component-files.js";
|
|
15
|
-
import { ConfigError } from "./config.js";
|
|
16
15
|
import { memberNames } from "./member-names.js";
|
|
17
16
|
import { extensionOf, isAbsolutePath, joinPath, normalizePath, relativePath } from "./paths.js";
|
|
18
17
|
import { isComponentFile } from "./position.js";
|
|
18
|
+
import { bytewise } from "./text/compare.js";
|
|
19
|
+
import { filesBelow } from "./fs/tree.js";
|
|
20
|
+
import { UsageError } from "./usage.js";
|
|
19
21
|
/** The kinds of declaration an action can name. */
|
|
20
22
|
const READ_BY_A_TEMPLATE = new Set([
|
|
21
23
|
"method",
|
|
@@ -30,13 +32,6 @@ const READ_BY_A_TEMPLATE = new Set([
|
|
|
30
32
|
const READ_BY_MARKUP = new Set([...READ_BY_A_TEMPLATE, "enum-member"]);
|
|
31
33
|
/** One identifier, as the grammar of the analyzed language spells one. */
|
|
32
34
|
const IDENTIFIER = /[\p{ID_Start}$_][\p{ID_Continue}$\u200C\u200D]*/uy;
|
|
33
|
-
/** Two strings ordered bytewise. */
|
|
34
|
-
function compare(a, b) {
|
|
35
|
-
if (a === b) {
|
|
36
|
-
return 0;
|
|
37
|
-
}
|
|
38
|
-
return a < b ? -1 : 1;
|
|
39
|
-
}
|
|
40
35
|
/**
|
|
41
36
|
* Every template under the configured directories, read once for the run, in path
|
|
42
37
|
* order and each file once. A directory is named relative to the target root, and
|
|
@@ -48,34 +43,29 @@ function compare(a, b) {
|
|
|
48
43
|
export function readTemplates(host, targetRoot, analysis) {
|
|
49
44
|
const files = new Map();
|
|
50
45
|
const read = (dir) => {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
const path = joinPath(dir, entry.name);
|
|
54
|
-
if (entry.directory) {
|
|
55
|
-
read(path);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
46
|
+
for (const below of filesBelow(host, joinPath(targetRoot, dir), () => true)) {
|
|
47
|
+
const path = joinPath(dir, below);
|
|
58
48
|
const absolute = joinPath(targetRoot, path);
|
|
59
49
|
if (!files.has(path) && host.kindOf(absolute) === "file") {
|
|
60
50
|
files.set(path, host.readFile(absolute));
|
|
61
51
|
}
|
|
62
52
|
}
|
|
63
53
|
};
|
|
64
|
-
for (const configured of analysis.
|
|
54
|
+
for (const configured of analysis.template_dirs) {
|
|
65
55
|
const dir = normalizePath(configured);
|
|
66
56
|
if (isAbsolutePath(dir) ||
|
|
67
57
|
dir === ".." ||
|
|
68
58
|
dir.startsWith("../") ||
|
|
69
59
|
host.kindOf(joinPath(targetRoot, dir)) !== "directory") {
|
|
70
|
-
throw new
|
|
60
|
+
throw new UsageError(`analysis.template_dirs: ${JSON.stringify(configured)} is not a directory below the target root`);
|
|
71
61
|
}
|
|
72
62
|
read(dir);
|
|
73
63
|
}
|
|
74
64
|
return {
|
|
75
|
-
delimiters: analysis.
|
|
65
|
+
delimiters: analysis.template_delimiters,
|
|
76
66
|
files: [...files]
|
|
77
67
|
.map(([path, text]) => ({ path, text }))
|
|
78
|
-
.sort((a, b) =>
|
|
68
|
+
.sort((a, b) => bytewise(a.path, b.path)),
|
|
79
69
|
};
|
|
80
70
|
}
|
|
81
71
|
/** The end of the quoted string that opens at `from`, past its closing quote. */
|
package/dist/src/test-support.js
CHANGED
|
@@ -5,21 +5,18 @@
|
|
|
5
5
|
* another such file, so it is judged with the tests: what it references is referenced by
|
|
6
6
|
* test code.
|
|
7
7
|
*/
|
|
8
|
-
import { CONVENTION_ROWS } from "./convention-rows.js";
|
|
8
|
+
import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES } from "./convention-rows.js";
|
|
9
9
|
import { packageOfSpecifier } from "./dependencies.js";
|
|
10
10
|
/** The root kind that never takes a file out of test-support code: the skip only withholds. */
|
|
11
11
|
const SKIP_ROOT = "type-error";
|
|
12
12
|
/**
|
|
13
13
|
* The test runners and test libraries an import of which is test evidence: each runner a
|
|
14
|
-
* convention row reads test files for, the
|
|
14
|
+
* convention row reads test files for, and the convention table's test libraries.
|
|
15
15
|
*/
|
|
16
16
|
const TEST_PACKAGES = new Set([
|
|
17
17
|
...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined).map((row) => row.package),
|
|
18
|
-
|
|
19
|
-
"@playwright/test",
|
|
18
|
+
...TEST_LIBRARIES,
|
|
20
19
|
]);
|
|
21
|
-
/** The scopes every package of which is a test library. */
|
|
22
|
-
const TEST_SCOPES = ["@testing-library/"];
|
|
23
20
|
/**
|
|
24
21
|
* The Node.js built-in modules a bare specifier names, which are no package. The set is
|
|
25
22
|
* closed: Node.js adds a new built-in under the `node:` scheme alone
|
|
@@ -73,7 +70,7 @@ const NODE_BUILTINS = new Set([
|
|
|
73
70
|
function namesTestPackage(specifier) {
|
|
74
71
|
const name = specifier.startsWith("node:") ? specifier : packageOfSpecifier(specifier);
|
|
75
72
|
return (name !== undefined &&
|
|
76
|
-
(TEST_PACKAGES.has(name) ||
|
|
73
|
+
(TEST_PACKAGES.has(name) || TEST_LIBRARY_SCOPES.some((scope) => name.startsWith(scope))));
|
|
77
74
|
}
|
|
78
75
|
/**
|
|
79
76
|
* Whether a file carries test evidence: an import of a test runner or a test library, or
|
|
@@ -106,9 +103,9 @@ function evidenceReader(tests, evidence) {
|
|
|
106
103
|
* The files of one project that are test-support code, below the target root: the
|
|
107
104
|
* largest set of files a test file reaches, none of them a test file or holding a root,
|
|
108
105
|
* each carrying test evidence of its own, whose every user outside the set is a test
|
|
109
|
-
* file
|
|
110
|
-
*
|
|
111
|
-
* the test-file rules classified.
|
|
106
|
+
* file; and the files only tests reach that are not among them. A root has a caller
|
|
107
|
+
* outside the reference graph: a runtime, a tool, a configuration, or a consumer of a
|
|
108
|
+
* library's published API. `testFiles` are the files the test-file rules classified.
|
|
112
109
|
*/
|
|
113
110
|
export function testSupportFiles(symbols, references, roots, testFiles, evidence) {
|
|
114
111
|
const fileOf = new Map(symbols.map((symbol) => [symbol.id, symbol.position.path]));
|
|
@@ -162,7 +159,7 @@ export function testSupportFiles(symbols, references, roots, testFiles, evidence
|
|
|
162
159
|
const onlyTests = withoutOutsideUsers(new Set([...reached].filter((path) => !rooted.has(path))));
|
|
163
160
|
const carries = evidenceReader(tests, evidence);
|
|
164
161
|
const support = withoutOutsideUsers(new Set([...onlyTests].filter((path) => carries(onlyTests, path))));
|
|
165
|
-
return support;
|
|
162
|
+
return { support, testUsed: new Set([...onlyTests].filter((path) => !support.has(path))) };
|
|
166
163
|
}
|
|
167
164
|
/** The references with every one a test-support file makes counted as a test reference. */
|
|
168
165
|
export function supportReferences(references, support) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two strings in the order of their UTF-8 bytes, the order every set of a run is read in.
|
|
3
|
+
* That is code point order, which UTF-16 code unit order (`<` on strings) breaks above
|
|
4
|
+
* U+FFFF: a surrogate unit is below U+E000 while the code point it encodes is above U+FFFF.
|
|
5
|
+
*/
|
|
6
|
+
export function bytewise(a, b) {
|
|
7
|
+
if (a === b) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
const length = Math.min(a.length, b.length);
|
|
11
|
+
for (let at = 0; at < length; at += 1) {
|
|
12
|
+
const x = a.charCodeAt(at);
|
|
13
|
+
const y = b.charCodeAt(at);
|
|
14
|
+
if (x !== y) {
|
|
15
|
+
return ranked(x) < ranked(y) ? -1 : 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return a.length < b.length ? -1 : 1;
|
|
19
|
+
}
|
|
20
|
+
/** One UTF-16 unit moved so that a surrogate ranks above every unit of U+E000 to U+FFFF. */
|
|
21
|
+
function ranked(unit) {
|
|
22
|
+
if (unit >= 0xe000) {
|
|
23
|
+
return unit - 0x800;
|
|
24
|
+
}
|
|
25
|
+
return unit >= 0xd800 ? unit + 0x2000 : unit;
|
|
26
|
+
}
|