@cplieger/deadset-ts 4.0.1 → 5.1.0
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 +4 -4
- package/dist/bin/component-mapper.js +5 -0
- package/dist/bin/mapper-connection.js +101 -0
- package/dist/bin/node-host.js +24 -2
- package/dist/src/analysis.js +301 -41
- package/dist/src/component-files.js +612 -0
- package/dist/src/component-layer.js +132 -0
- package/dist/src/config.js +9 -2
- package/dist/src/configuration-files.js +290 -0
- package/dist/src/conformance-record.js +3 -9
- package/dist/src/contextual-uses.js +240 -0
- package/dist/src/convention-rows.js +389 -0
- package/dist/src/conventions.js +565 -0
- package/dist/src/dependencies.js +36 -3
- package/dist/src/destructuring.js +6 -6
- package/dist/src/discover.js +10 -3
- package/dist/src/emit-map.js +72 -0
- package/dist/src/entry-point-gaps.js +81 -35
- package/dist/src/entry-points.js +50 -32
- package/dist/src/exempt-classes.js +6 -1
- package/dist/src/exempt.js +32 -20
- package/dist/src/findings/completion.js +46 -32
- package/dist/src/findings/emitters.js +22 -1
- package/dist/src/findings/interfaces.js +6 -2
- package/dist/src/findings/reads-and-writes.js +53 -9
- package/dist/src/findings/unused-declarations.js +47 -3
- package/dist/src/generated-file.js +24 -0
- package/dist/src/graph.js +13 -5
- package/dist/src/interface-satisfaction.js +2 -68
- package/dist/src/inventory.js +8 -3
- package/dist/src/kinds.js +6 -5
- package/dist/src/manifest.js +37 -11
- package/dist/src/matrix.js +21 -13
- package/dist/src/partial.js +3 -1
- package/dist/src/pnpm-workspace.js +120 -0
- package/dist/src/position.js +17 -2
- package/dist/src/print.js +2 -0
- package/dist/src/query.js +15 -0
- package/dist/src/references.js +404 -36
- package/dist/src/report.js +26 -6
- package/dist/src/reporters/annotations.js +10 -3
- package/dist/src/reporters/template.js +3 -0
- package/dist/src/resolve.js +37 -11
- package/dist/src/roots.js +37 -8
- package/dist/src/run.js +13 -3
- package/dist/src/schema.js +2 -0
- package/dist/src/session.js +141 -34
- package/dist/src/setup-failure.js +23 -0
- package/dist/src/source-files.js +4 -61
- package/dist/src/stores.js +3 -0
- package/dist/src/sweep.js +4 -2
- package/dist/src/template-field.js +38 -6
- package/dist/src/test-support.js +59 -0
- package/dist/src/type-errors.js +252 -0
- package/dist/src/type-query-alias.js +92 -0
- package/dist/src/value-flow.js +74 -0
- package/dist/src/verbs/analyze.js +24 -4
- package/dist/src/verbs/explain.js +4 -1
- package/dist/src/verbs/print-projects.js +17 -56
- package/dist/src/verbs/print-retained.js +7 -2
- package/dist/src/verbs/print-roots.js +7 -2
- package/dist/src/version-range.js +255 -0
- package/dist/src/version.js +1 -1
- package/dist/src/workspace-manifests.js +68 -0
- package/dist/src/workspace-programs.js +342 -0
- package/dist/src/workspace-resolution.js +307 -0
- package/dist/src/workspace-run.js +75 -0
- package/dist/src/workspace.js +358 -0
- package/jsr.json +1 -1
- package/package.json +3 -2
- package/src/analysis.ts +475 -72
- package/src/component-files.ts +718 -0
- package/src/component-layer.ts +160 -0
- package/src/config.ts +11 -2
- package/src/configuration-files.ts +370 -0
- package/src/conformance-record.ts +3 -10
- package/src/contextual-uses.ts +354 -0
- package/src/convention-rows.ts +448 -0
- package/src/conventions.ts +788 -0
- package/src/dependencies.ts +49 -2
- package/src/destructuring.ts +16 -6
- package/src/discover.ts +10 -6
- package/src/emit-map.ts +93 -0
- package/src/entry-point-gaps.ts +92 -38
- package/src/entry-points.ts +79 -36
- package/src/exempt-classes.ts +6 -1
- package/src/exempt.ts +69 -20
- package/src/file-facts.ts +2 -0
- package/src/findings/completion.ts +55 -36
- package/src/findings/emitters.ts +32 -1
- package/src/findings/interfaces.ts +9 -2
- package/src/findings/reads-and-writes.ts +70 -9
- package/src/findings/unused-declarations.ts +60 -3
- package/src/generated-file.ts +27 -0
- package/src/graph.ts +15 -4
- package/src/host.ts +23 -0
- package/src/index.ts +1 -1
- package/src/interface-satisfaction.ts +1 -92
- package/src/inventory.ts +17 -3
- package/src/kinds.ts +7 -5
- package/src/manifest.ts +47 -14
- package/src/matrix.ts +43 -15
- package/src/partial.ts +6 -1
- package/src/pnpm-workspace.ts +135 -0
- package/src/position.ts +23 -2
- package/src/print.ts +2 -0
- package/src/query.ts +26 -0
- package/src/references.ts +472 -40
- package/src/report.ts +56 -6
- package/src/reporters/annotations.ts +12 -3
- package/src/reporters/template.ts +3 -0
- package/src/resolve.ts +65 -11
- package/src/roots.ts +70 -9
- package/src/run.ts +15 -5
- package/src/schema.ts +2 -0
- package/src/session.ts +218 -37
- package/src/setup-failure.ts +39 -0
- package/src/source-files.ts +4 -66
- package/src/stores.ts +9 -0
- package/src/sweep.ts +9 -2
- package/src/template-field.ts +42 -7
- package/src/test-support.ts +77 -0
- package/src/type-errors.ts +340 -0
- package/src/type-query-alias.ts +133 -0
- package/src/value-flow.ts +104 -0
- package/src/verbs/analyze.ts +38 -4
- package/src/verbs/explain.ts +4 -1
- package/src/verbs/print-projects.ts +17 -69
- package/src/verbs/print-retained.ts +7 -2
- package/src/verbs/print-roots.ts +7 -2
- package/src/verbs/verb.ts +6 -3
- package/src/version-range.ts +309 -0
- package/src/version.ts +1 -1
- package/src/workspace-manifests.ts +100 -0
- package/src/workspace-programs.ts +486 -0
- package/src/workspace-resolution.ts +393 -0
- package/src/workspace-run.ts +148 -0
- package/src/workspace.ts +463 -0
package/dist/src/kinds.js
CHANGED
|
@@ -30,6 +30,7 @@ const ROWS = [
|
|
|
30
30
|
name: "unused-member",
|
|
31
31
|
languages: ["go", "ts"],
|
|
32
32
|
rule: "A struct field, class member or type member with no reference, a private member included. A member referenced only from its own declaration site counts as unreferenced.",
|
|
33
|
+
precondition: "A TypeScript member that no value writes and nothing reads, whose type names a type parameter of its declaring interface, class or type alias, is not reported when every part of that declaration that names the type parameter is a member no value writes and nothing reads. TypeScript compares object types by their members, so such members are what make two instantiations of their type distinct types, and deleting them leaves the type parameter phantom and lets one instantiation stand for another. A member a value writes is decided by the rule of DS1301 instead, whatever its type names.",
|
|
33
34
|
defaultSeverity: "deny",
|
|
34
35
|
maxClass: "certain",
|
|
35
36
|
fixability: "deletable",
|
|
@@ -39,7 +40,7 @@ const ROWS = [
|
|
|
39
40
|
code: "DS1004",
|
|
40
41
|
name: "test-only-use",
|
|
41
42
|
languages: ["go", "ts"],
|
|
42
|
-
rule: "A symbol with zero production references and at least one test reference: an unused-exported, unused-unexported or unused-member candidate whose test reference count is not zero, reported once under this code. A reference from a consumer's test files is a test reference unless the configuration counts consumer tests as production.",
|
|
43
|
+
rule: "A symbol with zero production references and at least one test reference: an unused-exported, unused-unexported or unused-member candidate whose test reference count is not zero, reported once under this code. A reference from a consumer's test files is a test reference unless the configuration counts consumer tests as production. A reference from test-support code, a package or file that only test code imports, is a test reference, and a declaration of test-support code is never reported under this code.",
|
|
43
44
|
defaultSeverity: "deny",
|
|
44
45
|
maxClass: "certain",
|
|
45
46
|
fixability: "deletable",
|
|
@@ -70,7 +71,7 @@ const ROWS = [
|
|
|
70
71
|
name: "unnecessary-export",
|
|
71
72
|
languages: ["go", "ts"],
|
|
72
73
|
rule: "An exported symbol whose every reference is inside the symbol's own package or module, reported as a candidate for unexporting. The subject is a package-level declaration or a method, and three subjects are excluded: an interface method, whose exportedness is the contract of the interface that declares it; a struct field, which an encoder reads by name; and a method that satisfies an interface some symbol uses as a type, which cannot be unexported without its type ceasing to satisfy that interface. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference; where the edge's other side is unknown to the analyzer the finding is emitted pending.",
|
|
73
|
-
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first.",
|
|
74
|
+
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type that a parameter or a result of an exported function or method of its own package names is not reported when a reference from outside the package references that function or method, because the caller holds values of the type, and unexporting it would leave an exported signature naming a type its callers cannot name.",
|
|
74
75
|
defaultSeverity: "warn",
|
|
75
76
|
maxClass: "certain",
|
|
76
77
|
fixability: "narrowable",
|
|
@@ -81,7 +82,7 @@ const ROWS = [
|
|
|
81
82
|
name: "unnecessary-exposure",
|
|
82
83
|
languages: ["go"],
|
|
83
84
|
rule: "An exported symbol of a non-internal package whose every reference is inside the target module, reported as a candidate for relocation behind an internal boundary. The subject is a package-level declaration or a method, and three subjects are excluded: an interface method, whose exportedness is the contract of the interface that declares it; a struct field, which an encoder reads by name; and a method that satisfies an interface some symbol uses as a type, which cannot be unexported without its type ceasing to satisfy that interface. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference; where the edge's other side is unknown to the analyzer the finding is emitted pending.",
|
|
84
|
-
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first.",
|
|
85
|
+
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type that a parameter or a result of an exported function or method of its own package names is not reported when a reference from outside the target module references that function or method, because the caller holds values of the type, and moving it behind an internal boundary would leave an exported signature naming a type its callers cannot name.",
|
|
85
86
|
defaultSeverity: "warn",
|
|
86
87
|
maxClass: "certain",
|
|
87
88
|
fixability: "narrowable",
|
|
@@ -143,7 +144,7 @@ const ROWS = [
|
|
|
143
144
|
code: "DS1301",
|
|
144
145
|
name: "write-only-symbol",
|
|
145
146
|
languages: ["go", "ts"],
|
|
146
|
-
rule: "A package-level or module-level variable, struct field, class member or collection that production code writes and never reads. In production mode a read from a test file counts as no read. A comparison of struct values reads every field of the type: in Go, a value of a struct type used as an operand of `==` or `!=`, as a map key, or as a `switch` tag or `case` expression is a read of every field of that type, transitively through the fields of every struct type it holds, recorded at the comparison, because equality reads every field and a field that decides equality carries information. In TypeScript a comparison of two object references reads no member, because `===` compares identity and never a member. The finding names each write position, so the deletion set is visible.",
|
|
147
|
+
rule: "A package-level or module-level variable, struct field, class member, type member or collection that production code writes and never reads. In production mode a read from a test file counts as no read. A comparison of struct values reads every field of the type: in Go, a value of a struct type used as an operand of `==` or `!=`, as a map key, or as a `switch` tag or `case` expression is a read of every field of that type, transitively through the fields of every struct type it holds, recorded at the comparison, because equality reads every field and a field that decides equality carries information. In TypeScript a comparison of two object references reads no member, because `===` compares identity and never a member. In TypeScript a property of an object literal writes the member of the same name that the literal's contextual type declares, whether or not the compiler checks the literal for excess properties. Where that contextual type is a union or an intersection, the property writes the member of that name on each constituent that declares one. So a literal that a declaration annotates, a function returns, a call passes or a `satisfies` expression checks writes the members of the type it is given. A store through the value a field or a member holds reads that field or member rather than writing it, because the store lands in storage every other holder of the value reads. In Go that store is an assignment, an increment, a compound assignment or a `delete` whose target is reached through a struct field holding a map, a slice or a pointer. Examples are `s.f[k] = v`, `s.f.x = v` and `delete(s.f, k)`. The same store through a field holding an array or a struct is a store into the field itself. TypeScript holds every object by reference, so an element or property store through a member's value reads the member. The finding names each write position, so the deletion set is visible. Deleting the subject and its writes also deletes every declaration whose only uses they hold. Such a declaration is a type parameter of the subject's declaring type that the subject's type alone names, or an import binding the writes alone use. The finding's message names each such declaration.",
|
|
147
148
|
defaultSeverity: "deny",
|
|
148
149
|
maxClass: "certain",
|
|
149
150
|
fixability: "deletable",
|
|
@@ -278,7 +279,7 @@ const ROWS = [
|
|
|
278
279
|
name: "unused-parameter",
|
|
279
280
|
languages: ["go", "ts"],
|
|
280
281
|
rule: "A parameter with no reference inside its function body, on a function whose signature is free to change.",
|
|
281
|
-
precondition: "The signature must be free, defined as follows: the function is not a method retained by interface satisfaction, is not used as a value, is not a go:linkname or cgo target, and is not a stub whose body is empty or only panics. A parameter its body never names is dead whatever the callers, so a published declaration of a library is reported too, with the fixability the vocabulary gives the kind: the signature change is a breaking change.",
|
|
282
|
+
precondition: "The signature must be free, defined as follows: the function is not a method retained by interface satisfaction, is not used as a value, is not a go:linkname or cgo target, is not a function the Go test driver runs (a test, benchmark or fuzz test of a test file, or TestMain), and is not a stub whose body is empty or only panics. A parameter its body never names is dead whatever the callers, so a published declaration of a library is reported too, with the fixability the vocabulary gives the kind: the signature change is a breaking change.",
|
|
282
283
|
defaultSeverity: "warn",
|
|
283
284
|
maxClass: "certain",
|
|
284
285
|
fixability: "manual",
|
package/dist/src/manifest.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The entry points a package manifest names, which are roots because something
|
|
3
3
|
* outside the analyzed program reaches them: a consumer importing the package, or a
|
|
4
|
-
* shell running its command.
|
|
4
|
+
* shell running its command or one of its scripts.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* to another package, whose entry points are that package's roots and not this
|
|
8
|
-
* target's.
|
|
6
|
+
* One call reads one manifest, the one in the directory it is given.
|
|
9
7
|
*/
|
|
10
8
|
import { isAbsolutePath, joinPath, resolvePath } from "./paths.js";
|
|
11
9
|
/** The manifest's own name. */
|
|
@@ -26,6 +24,21 @@ function isTarget(value, requireDot) {
|
|
|
26
24
|
}
|
|
27
25
|
return !requireDot || value.startsWith("./");
|
|
28
26
|
}
|
|
27
|
+
/** Where a script's command line splits into tokens: white space and the shell's operators. */
|
|
28
|
+
const SCRIPT_SEPARATORS = /\s+|&&|\|\||;|\|/u;
|
|
29
|
+
/** A token written inside one pair of quotes. */
|
|
30
|
+
const QUOTED = /^(["'])(.*)\1$/su;
|
|
31
|
+
/**
|
|
32
|
+
* Every token one script's command line holds, each with one pair of enclosing quotes
|
|
33
|
+
* removed. A token holding a wildcard is a pattern rather than a path, and names no
|
|
34
|
+
* entry point.
|
|
35
|
+
*/
|
|
36
|
+
export function scriptTokens(command) {
|
|
37
|
+
return command
|
|
38
|
+
.split(SCRIPT_SEPARATORS)
|
|
39
|
+
.map((token) => token.replace(QUOTED, "$2"))
|
|
40
|
+
.filter((token) => token !== "" && !token.includes("*"));
|
|
41
|
+
}
|
|
29
42
|
/** One member's spelling below another: `exports` then `exports["."]`. */
|
|
30
43
|
function below(member, key) {
|
|
31
44
|
return `${member}[${JSON.stringify(key)}]`;
|
|
@@ -33,12 +46,9 @@ function below(member, key) {
|
|
|
33
46
|
/**
|
|
34
47
|
* Every target one `exports` value carries, at the member each is written under.
|
|
35
48
|
*
|
|
36
|
-
* The value
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* file of the same package and each is an entry point of its own; a key's meaning
|
|
40
|
-
* is not read, because a subpath and a condition both lead to a target and the
|
|
41
|
-
* walk needs no more than that.
|
|
49
|
+
* The value nests strings, subpath or condition objects and alternative arrays to
|
|
50
|
+
* any depth; a `null` blocks a subpath. Every level is walked, because each
|
|
51
|
+
* condition names its own entry file, and a key's meaning is never read.
|
|
42
52
|
*/
|
|
43
53
|
function exportTargets(value, member, found) {
|
|
44
54
|
if (typeof value === "string") {
|
|
@@ -83,7 +93,7 @@ function manifestOf(host, targetRoot) {
|
|
|
83
93
|
return isRecord(value) ? value : undefined;
|
|
84
94
|
}
|
|
85
95
|
/**
|
|
86
|
-
* What the
|
|
96
|
+
* What the manifest in `targetRoot` declares about where its package is entered.
|
|
87
97
|
*
|
|
88
98
|
* A target is taken as written. It is not resolved to a source file here, because
|
|
89
99
|
* which file a target names is a fact about the compiler configuration that emits
|
|
@@ -113,6 +123,22 @@ export function readManifest(host, targetRoot) {
|
|
|
113
123
|
}
|
|
114
124
|
}
|
|
115
125
|
exportTargets(manifest["exports"], "exports", found);
|
|
126
|
+
const scripts = manifest["scripts"];
|
|
127
|
+
if (isRecord(scripts)) {
|
|
128
|
+
for (const [name, command] of Object.entries(scripts)) {
|
|
129
|
+
if (typeof command !== "string") {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
for (const token of new Set(scriptTokens(command))) {
|
|
133
|
+
found.push({
|
|
134
|
+
member: below("scripts", name),
|
|
135
|
+
path: token,
|
|
136
|
+
role: "script",
|
|
137
|
+
published: false,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
116
142
|
return {
|
|
117
143
|
entries: found.map((entry) => ({ ...entry, path: resolvePath(targetRoot, entry.path) })),
|
|
118
144
|
declaresExports: manifest["exports"] !== undefined,
|
package/dist/src/matrix.js
CHANGED
|
@@ -32,32 +32,31 @@ export function referenceKey(reference) {
|
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Indexes what the passes answered for each configuration of one run, in the run's order.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* configuration alone holds is in the union with that one configuration, so it is
|
|
40
|
-
* judged where it exists rather than skipped.
|
|
41
|
-
*
|
|
42
|
-
* One reference written at one position of a file several configurations compile is seen
|
|
43
|
-
* once per configuration and is one reference, so the union holds it once; two references
|
|
44
|
-
* one configuration makes at one position are two.
|
|
35
|
+
* A declaration is keyed by its rendered position, with the fields of the first
|
|
36
|
+
* configuration that judges it; one in a file every holder holds only for its references
|
|
37
|
+
* is judged by none and is not in the union. One reference at one position of a file
|
|
38
|
+
* several configurations compile is one reference; two one configuration makes are two.
|
|
45
39
|
*/
|
|
46
40
|
export function matrixOf(per) {
|
|
47
41
|
const symbols = [];
|
|
48
42
|
const holders = new Map();
|
|
49
43
|
const references = [];
|
|
50
44
|
const firstSeen = new Map();
|
|
45
|
+
const judged = new Set();
|
|
51
46
|
per.forEach((one, index) => {
|
|
47
|
+
const referenceOnly = new Set(one.referenceOnly);
|
|
52
48
|
for (const symbol of one.symbols) {
|
|
53
49
|
const held = holders.get(symbol.id);
|
|
54
50
|
if (held === undefined) {
|
|
55
51
|
holders.set(symbol.id, [one.configuration]);
|
|
56
|
-
symbols.push(symbol);
|
|
57
52
|
}
|
|
58
53
|
else if (!held.includes(one.configuration)) {
|
|
59
54
|
held.push(one.configuration);
|
|
60
55
|
}
|
|
56
|
+
if (!referenceOnly.has(symbol.position.path) && !judged.has(symbol.id)) {
|
|
57
|
+
judged.add(symbol.id);
|
|
58
|
+
symbols.push(symbol);
|
|
59
|
+
}
|
|
61
60
|
}
|
|
62
61
|
for (const reference of one.references) {
|
|
63
62
|
const key = referenceKey(reference);
|
|
@@ -74,8 +73,8 @@ export function matrixOf(per) {
|
|
|
74
73
|
symbols.sort((a, b) => byPosition(a.position, b.position) || compare(a.ref, b.ref));
|
|
75
74
|
return {
|
|
76
75
|
configurations: per.map((one) => one.configuration),
|
|
77
|
-
graphs: per.map((one) => graphOf(one.symbols, one.references, one.roots, one.testFiles)),
|
|
78
|
-
union: graphOf(symbols, references, per.flatMap((one) => one.roots), [...new Set(per.flatMap((one) => one.testFiles))]),
|
|
76
|
+
graphs: per.map((one) => graphOf(one.symbols, one.references, one.roots, one.testFiles, one.supportFiles)),
|
|
77
|
+
union: graphOf(symbols, references, per.flatMap((one) => one.roots), [...new Set(per.flatMap((one) => one.testFiles))], [...new Set(per.flatMap((one) => one.supportFiles ?? []))]),
|
|
79
78
|
heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
|
|
80
79
|
};
|
|
81
80
|
}
|
|
@@ -131,6 +130,15 @@ export function sweepMatrix(matrix, input) {
|
|
|
131
130
|
components: componentsOf(union, dead, testOfDeadCode),
|
|
132
131
|
};
|
|
133
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* The file declarations at `paths`, below the target root, that one sweep holds live
|
|
135
|
+
* under both relations, the rule a judged declaration is live by.
|
|
136
|
+
*/
|
|
137
|
+
export function liveFilesAt(matrix, result, paths) {
|
|
138
|
+
return matrix.union.symbols.filter((symbol) => symbol.kind === "file" &&
|
|
139
|
+
paths.has(symbol.position.path) &&
|
|
140
|
+
(result.liveUnder.get(symbol.id)?.length ?? 0) === RELATIONS.length);
|
|
141
|
+
}
|
|
134
142
|
/** The relations in the order a declaration's entry lists them. */
|
|
135
143
|
const RELATIONS = ["reference-counting", "reachability"];
|
|
136
144
|
/**
|
package/dist/src/partial.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { relativePath } from "./paths.js";
|
|
1
2
|
/** A count with its noun, so a line reads for one as well as for several. */
|
|
2
3
|
function counted(n, one, many) {
|
|
3
4
|
return `${String(n)} ${n === 1 ? one : many}`;
|
|
@@ -14,7 +15,8 @@ export function partialNotes(partial) {
|
|
|
14
15
|
if (partial.unanswered.length > 0) {
|
|
15
16
|
const byProject = new Map();
|
|
16
17
|
for (const question of partial.unanswered) {
|
|
17
|
-
|
|
18
|
+
const configFile = relativePath(partial.targetRoot, question.configFile) ?? question.configFile;
|
|
19
|
+
byProject.set(configFile, (byProject.get(configFile) ?? 0) + 1);
|
|
18
20
|
}
|
|
19
21
|
const where = [...byProject].map(([configFile, n]) => `${configFile}: ${String(n)}`).join(", ");
|
|
20
22
|
notes.push(`the checker answered ${counted(partial.unanswered.length, "question", "questions")} with a failure (${where}); every declaration an answer could have kept live is kept live and reported by nothing`);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `packages` list of a `pnpm-workspace.yaml`. The document is read as far as that
|
|
3
|
+
* one key needs and no further: a top-level `packages` key holding a block sequence or
|
|
4
|
+
* a one-line flow sequence of plain or quoted strings. Any other value of the key is
|
|
5
|
+
* refused rather than guessed at, because a list read wrong names the wrong packages.
|
|
6
|
+
*/
|
|
7
|
+
/** The characters a plain scalar may not start with. */
|
|
8
|
+
const INDICATORS = new Set(["[", "]", "{", "}", ",", "&", "*", "!", "|", ">", "%", "@", "`", "#"]);
|
|
9
|
+
/** One line with its comment removed, a `#` inside quotes kept. */
|
|
10
|
+
function uncommented(line) {
|
|
11
|
+
let quote;
|
|
12
|
+
for (let at = 0; at < line.length; at += 1) {
|
|
13
|
+
const character = line[at];
|
|
14
|
+
if (quote !== undefined) {
|
|
15
|
+
if (character === quote) {
|
|
16
|
+
quote = undefined;
|
|
17
|
+
}
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (character === "'" || character === '"') {
|
|
21
|
+
quote = character;
|
|
22
|
+
}
|
|
23
|
+
else if (character === "#" && (at === 0 || /\s/u.test(line[at - 1] ?? ""))) {
|
|
24
|
+
return line.slice(0, at).trimEnd();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return line.trimEnd();
|
|
28
|
+
}
|
|
29
|
+
/** One scalar as written, or `undefined` where it is not a string this reader reads. */
|
|
30
|
+
function scalarOf(text) {
|
|
31
|
+
const value = text.trim();
|
|
32
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
33
|
+
const inner = value.slice(1, -1);
|
|
34
|
+
return /'(?!')/u.test(inner.replaceAll("''", "")) ? undefined : inner.replaceAll("''", "'");
|
|
35
|
+
}
|
|
36
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
37
|
+
const inner = value.slice(1, -1);
|
|
38
|
+
return inner.includes("\\") || inner.includes('"') ? undefined : inner;
|
|
39
|
+
}
|
|
40
|
+
if (value === "" ||
|
|
41
|
+
INDICATORS.has(value[0] ?? "") ||
|
|
42
|
+
value.startsWith("'") ||
|
|
43
|
+
value.startsWith('"')) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
if (value.startsWith("- ") || value.startsWith("? ") || /:(?:\s|$)/u.test(value)) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
/** The entries of a one-line flow sequence, or `undefined` where one does not read. */
|
|
52
|
+
function flowOf(text) {
|
|
53
|
+
const inner = text.slice(1, -1).trim();
|
|
54
|
+
if (inner === "") {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const entries = inner.split(",");
|
|
58
|
+
if (entries.length > 1 && entries.at(-1)?.trim() === "") {
|
|
59
|
+
entries.pop();
|
|
60
|
+
}
|
|
61
|
+
const found = [];
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const value = scalarOf(entry);
|
|
64
|
+
if (value === undefined) {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
found.push(value);
|
|
68
|
+
}
|
|
69
|
+
return found;
|
|
70
|
+
}
|
|
71
|
+
const KEY = /^(?:packages|'packages'|"packages")\s*:(?:\s(.*))?$/u;
|
|
72
|
+
/** The `packages` list one `pnpm-workspace.yaml` text declares. */
|
|
73
|
+
export function pnpmPackages(text) {
|
|
74
|
+
const lines = text.split(/\r?\n/u).map(uncommented);
|
|
75
|
+
const keys = lines.flatMap((line, at) => (KEY.test(line) ? [at] : []));
|
|
76
|
+
const [at] = keys;
|
|
77
|
+
if (at === undefined) {
|
|
78
|
+
return { kind: "absent" };
|
|
79
|
+
}
|
|
80
|
+
if (keys.length > 1) {
|
|
81
|
+
return { kind: "refused", reason: "the packages key is declared more than once" };
|
|
82
|
+
}
|
|
83
|
+
const rest = (KEY.exec(lines[at] ?? "")?.[1] ?? "").trim();
|
|
84
|
+
if (rest !== "") {
|
|
85
|
+
const listed = rest.startsWith("[") && rest.endsWith("]") ? flowOf(rest) : undefined;
|
|
86
|
+
return listed === undefined
|
|
87
|
+
? {
|
|
88
|
+
kind: "refused",
|
|
89
|
+
reason: `packages holds ${JSON.stringify(rest)}, not a sequence of strings`,
|
|
90
|
+
}
|
|
91
|
+
: { kind: "listed", patterns: listed };
|
|
92
|
+
}
|
|
93
|
+
const patterns = [];
|
|
94
|
+
for (const line of lines.slice(at + 1)) {
|
|
95
|
+
if (line.trim() === "") {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const item = /^(\s*)-(?:\s+(.*))?$/u.exec(line);
|
|
99
|
+
if (item === null) {
|
|
100
|
+
if (/^\s/u.test(line)) {
|
|
101
|
+
return {
|
|
102
|
+
kind: "refused",
|
|
103
|
+
reason: `packages holds ${JSON.stringify(line.trim())}, not a sequence item`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
const value = scalarOf(item[2] ?? "");
|
|
109
|
+
if (value === undefined) {
|
|
110
|
+
return {
|
|
111
|
+
kind: "refused",
|
|
112
|
+
reason: `the packages item ${JSON.stringify(line.trim())} is not a plain or quoted string`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
patterns.push(value);
|
|
116
|
+
}
|
|
117
|
+
return patterns.length === 0
|
|
118
|
+
? { kind: "refused", reason: "packages holds no sequence" }
|
|
119
|
+
: { kind: "listed", patterns };
|
|
120
|
+
}
|
package/dist/src/position.js
CHANGED
|
@@ -13,18 +13,33 @@ export class PositionError extends Error {
|
|
|
13
13
|
this.file = file;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
/** Whether the compiler read one file through a content mapper, which makes it a component file. */
|
|
17
|
+
export function isComponentFile(file) {
|
|
18
|
+
return file.contentMapper !== undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The length of the file as it is written. A component file's module holds the file's
|
|
22
|
+
* text at its own offsets and appends what it needs past its end.
|
|
23
|
+
*/
|
|
24
|
+
export function writtenLength(file) {
|
|
25
|
+
return isComponentFile(file) ? file.originalText.length : file.text.length;
|
|
26
|
+
}
|
|
16
27
|
/**
|
|
17
28
|
* The position of one offset of one file, rendered against the target root.
|
|
18
29
|
*
|
|
19
30
|
* `offset` is a UTF-16 code unit offset into the file's text, which is what every
|
|
20
|
-
* node of the tree carries.
|
|
31
|
+
* node of the tree carries. An offset a component file's module appends renders where
|
|
32
|
+
* the module's span map places it in the file.
|
|
21
33
|
*/
|
|
22
34
|
export function renderPosition(file, root, offset) {
|
|
23
35
|
const path = relativePath(root, file.fileName);
|
|
24
36
|
if (path === undefined) {
|
|
25
37
|
throw new PositionError(file.fileName, root);
|
|
26
38
|
}
|
|
27
|
-
const
|
|
39
|
+
const written = isComponentFile(file) && offset >= file.originalText.length
|
|
40
|
+
? Math.min(file.spanMap?.virtualToOriginalPosition(offset).position ?? offset, file.originalText.length)
|
|
41
|
+
: offset;
|
|
42
|
+
const { line, character } = file.getLineAndCharacterOfPosition(written);
|
|
28
43
|
return { path, line: line + 1, column: character + 1 };
|
|
29
44
|
}
|
|
30
45
|
/**
|
package/dist/src/print.js
CHANGED
|
@@ -64,6 +64,8 @@ export function printConfig(config, provenance) {
|
|
|
64
64
|
ts: {
|
|
65
65
|
test_files: config.ts.testFiles,
|
|
66
66
|
entry_files: config.ts.entryFiles,
|
|
67
|
+
component_extensions: config.ts.componentExtensions,
|
|
68
|
+
disabled_conventions: config.ts.disabledConventions,
|
|
67
69
|
injection_registrations: config.ts.injectionRegistrations.map(declarationOf),
|
|
68
70
|
lifecycle_contracts: config.ts.lifecycleContracts.map((entry) => ({
|
|
69
71
|
components: entry.components.map(declarationOf),
|
package/dist/src/query.js
CHANGED
|
@@ -73,6 +73,21 @@ function symbolLocation(symbol) {
|
|
|
73
73
|
function typeLocation(type) {
|
|
74
74
|
return `type ${String(type.id)}`;
|
|
75
75
|
}
|
|
76
|
+
/** The property tables over one project's questions. */
|
|
77
|
+
export function propertyTables(queries) {
|
|
78
|
+
const held = new Map();
|
|
79
|
+
return {
|
|
80
|
+
of: (type) => {
|
|
81
|
+
const known = held.get(type.id);
|
|
82
|
+
if (known !== undefined) {
|
|
83
|
+
return { properties: known, asked: false };
|
|
84
|
+
}
|
|
85
|
+
const properties = queries.propertiesOf(type);
|
|
86
|
+
held.set(type.id, properties);
|
|
87
|
+
return { properties, asked: true };
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
76
91
|
/**
|
|
77
92
|
* The guarded questions over one project's checker. `configFile` names the project
|
|
78
93
|
* in the log, `batch` sends many questions in one round trip, `log` is the run's,
|