@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +3 -2
- package/dist/src/analysis.js +28 -46
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +17 -29
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +3 -2
- package/dist/src/configuration-files.js +27 -168
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1084 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +13 -39
- package/dist/src/edges.js +7 -15
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +3 -63
- package/dist/src/exempt.js +13 -40
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +11 -20
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +4 -4
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +8 -7
- package/dist/src/findings/non-code-artifacts.js +5 -6
- package/dist/src/findings/reads-and-writes.js +11 -9
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +7 -14
- package/dist/src/fs/manifest.js +43 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +34 -25
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/intra-function-parts.js +18 -26
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +4 -0
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +4 -360
- package/dist/src/manifest.js +5 -43
- package/dist/src/mdx-entries.js +3 -26
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +3 -2
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +272 -0
- package/dist/src/report.js +39 -42
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +12 -26
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +10 -8
- package/dist/src/schema.js +4 -3
- package/dist/src/scope.js +4 -5
- package/dist/src/serialization-contract.js +4 -8
- package/dist/src/session.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +12 -13
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +5 -15
- package/dist/src/test-globals.js +6 -28
- package/dist/src/test-joins.js +6 -6
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
- package/dist/src/type-errors.js +14 -38
- package/dist/src/unseen-loads.js +2 -9
- package/dist/src/unwrap.js +23 -0
- package/dist/src/verbs/analyze.js +22 -15
- package/dist/src/verbs/describe.js +7 -7
- package/dist/src/verbs/explain.js +7 -4
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +4 -2
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +4 -3
- package/dist/src/workflow-steps.js +9 -46
- package/dist/src/workspace-manifests.js +2 -2
- package/dist/src/workspace-programs.js +5 -3
- package/dist/src/workspace-resolution.js +13 -17
- package/dist/src/workspace.js +28 -116
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +29 -54
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +17 -38
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +11 -15
- package/src/configuration-files.ts +27 -170
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1148 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +14 -39
- package/src/edges.ts +7 -21
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +5 -74
- package/src/exempt.ts +17 -47
- package/src/file-facts.ts +6 -24
- package/src/finding.ts +9 -9
- package/src/findings/completion.ts +22 -23
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +6 -7
- package/src/findings/emitters.ts +6 -6
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +8 -8
- package/src/findings/non-code-artifacts.ts +5 -7
- package/src/findings/reads-and-writes.ts +15 -12
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +7 -16
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +18 -28
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +5 -0
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +6 -406
- package/src/manifest.ts +5 -46
- package/src/mdx-entries.ts +3 -26
- package/src/partial.ts +2 -1
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +3 -2
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +364 -0
- package/src/report.ts +66 -229
- package/src/reporters/annotations.ts +2 -2
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +17 -12
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +31 -38
- package/src/roots.ts +7 -20
- package/src/run.ts +10 -14
- package/src/schema.ts +4 -3
- package/src/scope.ts +4 -10
- package/src/serialization-contract.ts +4 -9
- package/src/session.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +11 -15
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +5 -18
- package/src/test-globals.ts +9 -27
- package/src/test-joins.ts +6 -8
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/shell.ts +161 -0
- package/src/{yaml-document.ts → text/yaml.ts} +46 -0
- package/src/type-errors.ts +25 -36
- package/src/unseen-loads.ts +1 -12
- package/src/unwrap.ts +34 -0
- package/src/verbs/analyze.ts +38 -25
- package/src/verbs/describe.ts +13 -7
- package/src/verbs/explain.ts +7 -4
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +4 -2
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +4 -3
- package/src/workflow-steps.ts +9 -44
- package/src/workspace-manifests.ts +2 -2
- package/src/workspace-programs.ts +5 -3
- package/src/workspace-resolution.ts +21 -17
- package/src/workspace.ts +35 -121
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/version.js +0 -7
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/version.ts +0 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
1
2
|
import { testsUnseenAPI } from "./completion.js";
|
|
2
3
|
import { unreachableExports } from "./visibility-narrowing.js";
|
|
3
4
|
/** The kinds of declaration that are members of a class, an interface, a type or an enum. */
|
|
@@ -8,17 +9,17 @@ const MEMBERS = new Set([
|
|
|
8
9
|
"enum-member",
|
|
9
10
|
]);
|
|
10
11
|
/**
|
|
11
|
-
* The kinds of declaration no code of this
|
|
12
|
+
* The kinds of declaration no code of this emitter reports: an interface and a method an
|
|
12
13
|
* interface declares are the interface kinds' subjects, and a type parameter is the
|
|
13
14
|
* subject of the unused-type-parameter kind or of none.
|
|
14
15
|
*/
|
|
15
|
-
const
|
|
16
|
+
const NOT_REPORTED_HERE = new Set([
|
|
16
17
|
"file",
|
|
17
18
|
"interface",
|
|
18
19
|
"interface-method",
|
|
19
20
|
"type-parameter",
|
|
20
21
|
]);
|
|
21
|
-
/** The reader's word for each kind of declaration a finding of
|
|
22
|
+
/** The reader's word for each kind of declaration a finding of this emitter names. */
|
|
22
23
|
const WORDS = {
|
|
23
24
|
file: "file",
|
|
24
25
|
function: "function",
|
|
@@ -49,9 +50,9 @@ const TEST_OF_DEAD_CODE = "every target symbol this test references is reported
|
|
|
49
50
|
function codeOf(judged) {
|
|
50
51
|
const { candidate, symbol } = judged;
|
|
51
52
|
if (candidate.testOfDeadCode) {
|
|
52
|
-
return judged.testsUnseenAPI ? undefined :
|
|
53
|
+
return judged.testsUnseenAPI ? undefined : CODE.testOfDeadCode;
|
|
53
54
|
}
|
|
54
|
-
if ((judged.test && !candidate.unreferencedTest) ||
|
|
55
|
+
if ((judged.test && !candidate.unreferencedTest) || NOT_REPORTED_HERE.has(symbol.kind)) {
|
|
55
56
|
return undefined;
|
|
56
57
|
}
|
|
57
58
|
const referenced = candidate.productionRefs + candidate.testRefs > 0;
|
|
@@ -62,30 +63,30 @@ function codeOf(judged) {
|
|
|
62
63
|
return unreferencedCode(symbol);
|
|
63
64
|
}
|
|
64
65
|
if (candidate.heldByTests === candidate.id) {
|
|
65
|
-
return
|
|
66
|
+
return CODE.testOnlyUse;
|
|
66
67
|
}
|
|
67
68
|
if (candidate.productionRefs === 0 && judged.deprecated) {
|
|
68
|
-
return
|
|
69
|
+
return CODE.deprecatedUnused;
|
|
69
70
|
}
|
|
70
71
|
if (candidate.testRefs > 0 &&
|
|
71
72
|
(candidate.productionRefs === 0 ||
|
|
72
73
|
(candidate.relation === "reachability" && candidate.keptByTests))) {
|
|
73
|
-
return
|
|
74
|
+
return CODE.testOnlyUse;
|
|
74
75
|
}
|
|
75
76
|
if (MEMBERS.has(symbol.kind)) {
|
|
76
|
-
return
|
|
77
|
+
return CODE.unusedMember;
|
|
77
78
|
}
|
|
78
79
|
if (!symbol.exported) {
|
|
79
|
-
return
|
|
80
|
+
return CODE.unusedUnexported;
|
|
80
81
|
}
|
|
81
|
-
return judged.unreachable ? undefined :
|
|
82
|
+
return judged.unreachable ? undefined : CODE.unusedExported;
|
|
82
83
|
}
|
|
83
84
|
/** The code a declaration nothing references carries, by its shape and its visibility. */
|
|
84
85
|
function unreferencedCode(symbol) {
|
|
85
86
|
if (MEMBERS.has(symbol.kind)) {
|
|
86
|
-
return
|
|
87
|
+
return CODE.unusedMember;
|
|
87
88
|
}
|
|
88
|
-
return symbol.exported ?
|
|
89
|
+
return symbol.exported ? CODE.unusedExported : CODE.unusedUnexported;
|
|
89
90
|
}
|
|
90
91
|
/** The visibility a member's message names, where it narrows the member's reach. */
|
|
91
92
|
function visibilityWord(symbol) {
|
|
@@ -108,16 +109,16 @@ function messageOf(code, judged) {
|
|
|
108
109
|
const { candidate, symbol } = judged;
|
|
109
110
|
const word = WORDS[symbol.kind];
|
|
110
111
|
switch (code) {
|
|
111
|
-
case
|
|
112
|
+
case CODE.testOfDeadCode:
|
|
112
113
|
return TEST_OF_DEAD_CODE;
|
|
113
|
-
case
|
|
114
|
+
case CODE.deprecatedUnused:
|
|
114
115
|
return `deprecated ${word} has no production reference`;
|
|
115
|
-
case
|
|
116
|
+
case CODE.testOnlyUse:
|
|
116
117
|
return candidate.productionRefs === 0
|
|
117
118
|
? `${word} is referenced only from test files and never from production code`
|
|
118
119
|
: `${word} is referenced only from test files and from declarations that are themselves dead`;
|
|
119
120
|
}
|
|
120
|
-
const subject = code ===
|
|
121
|
+
const subject = code === CODE.unusedMember
|
|
121
122
|
? `${visibilityWord(symbol)}${word}`
|
|
122
123
|
: symbol.kind === "export-alias"
|
|
123
124
|
? word
|
|
@@ -125,7 +126,7 @@ function messageOf(code, judged) {
|
|
|
125
126
|
if (candidate.relation === "reachability") {
|
|
126
127
|
return `${subject} is referenced only from declarations that are themselves dead`;
|
|
127
128
|
}
|
|
128
|
-
if (code ===
|
|
129
|
+
if (code === CODE.unusedExported) {
|
|
129
130
|
return `${subject} has no reference in the target and none from any loaded consumer`;
|
|
130
131
|
}
|
|
131
132
|
return `${subject} has no reference in the target`;
|
|
@@ -167,14 +168,14 @@ function brandMembers(input) {
|
|
|
167
168
|
}
|
|
168
169
|
/** The kinds of member a brand is: a data member of a type or of a class. */
|
|
169
170
|
const BRANDS = new Set(["type-member", "class-member"]);
|
|
170
|
-
/** The findings of the unused-
|
|
171
|
+
/** The findings of the unused-declaration kinds, `DS1001` to `DS1004` and `DS1102` to `DS1104`. */
|
|
171
172
|
export const unusedDeclarations = (input) => [
|
|
172
|
-
...
|
|
173
|
+
...emitterFindings(input, false),
|
|
173
174
|
...unusedImports(input),
|
|
174
175
|
];
|
|
175
176
|
/**
|
|
176
|
-
* The import bindings their file never reads
|
|
177
|
-
*
|
|
177
|
+
* The import bindings their file never reads. A generated file's are the generator's to
|
|
178
|
+
* remove unless the run includes generated files.
|
|
178
179
|
*/
|
|
179
180
|
function unusedImports(input) {
|
|
180
181
|
const generated = input.config.analysis.generatedFiles === "include" ? undefined : input.files.generated;
|
|
@@ -182,7 +183,7 @@ function unusedImports(input) {
|
|
|
182
183
|
? []
|
|
183
184
|
: [
|
|
184
185
|
{
|
|
185
|
-
code:
|
|
186
|
+
code: CODE.unusedImport,
|
|
186
187
|
position: binding.position,
|
|
187
188
|
symbol: { ref: binding.ref, kind: "import", name: binding.name, sizeLines: 1 },
|
|
188
189
|
message: `import binding ${binding.name} is never read in its file`,
|
|
@@ -194,9 +195,9 @@ function unusedImports(input) {
|
|
|
194
195
|
* a record on such a member is matched against, since the report carries the container's
|
|
195
196
|
* finding rather than one of the member's own.
|
|
196
197
|
*/
|
|
197
|
-
export const fallenMembers = (input) =>
|
|
198
|
-
/**
|
|
199
|
-
function
|
|
198
|
+
export const fallenMembers = (input) => emitterFindings(input, true);
|
|
199
|
+
/** This emitter's findings, or, where `fallen` is set, its dead containers' members' alone. */
|
|
200
|
+
function emitterFindings(input, fallen) {
|
|
200
201
|
const union = input.swept.matrix.union;
|
|
201
202
|
const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
|
|
202
203
|
const unreachable = unreachableExports(input);
|
|
@@ -223,7 +224,7 @@ function familyFindings(input, fallen) {
|
|
|
223
224
|
};
|
|
224
225
|
const code = codeOf(judged);
|
|
225
226
|
if (code === undefined ||
|
|
226
|
-
(code ===
|
|
227
|
+
(code === CODE.unusedMember && (brands ??= brandMembers(input)).has(symbol.id))) {
|
|
227
228
|
continue;
|
|
228
229
|
}
|
|
229
230
|
const endLine = Math.max(symbol.position.line, symbol.endLine);
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
1
2
|
import { OUTSIDE } from "../graph.js";
|
|
2
3
|
import { declarationKey } from "../ref.js";
|
|
3
4
|
import { runnerCalls } from "../sweep.js";
|
|
4
5
|
import { writeOnlyDeclarations } from "./reads-and-writes.js";
|
|
5
|
-
const UNNECESSARY_EXPORT = "DS1101";
|
|
6
|
-
const UNREACHABLE_EXPORT = "DS1103";
|
|
7
|
-
const REDUNDANT_EXPORT_KEYWORD = "DS1104";
|
|
8
6
|
/**
|
|
9
7
|
* The root kinds by which a consumer importing the package reaches a declaration. A
|
|
10
8
|
* declaration or a file rooted by any other kind is entered by something that names it
|
|
11
9
|
* from outside the reference graph: a runtime, a tool, a framework or the configuration.
|
|
12
10
|
*/
|
|
13
11
|
const PUBLISHING = new Set(["manifest-entry", "published-api"]);
|
|
14
|
-
/** The reader's word for each kind of declaration this
|
|
12
|
+
/** The reader's word for each kind of declaration this emitter reports. */
|
|
15
13
|
const WORDS = {
|
|
16
14
|
function: "function",
|
|
17
15
|
class: "class",
|
|
@@ -109,8 +107,8 @@ function wordOf(symbol) {
|
|
|
109
107
|
}
|
|
110
108
|
/** The visibility each narrowing code names, which only the narrowing codes carry. */
|
|
111
109
|
const NARROWER = {
|
|
112
|
-
[
|
|
113
|
-
[
|
|
110
|
+
[CODE.redundantExportKeyword]: "file",
|
|
111
|
+
[CODE.unnecessaryExport]: "package",
|
|
114
112
|
};
|
|
115
113
|
function findingOf(symbol, code, message) {
|
|
116
114
|
const { path, line, column } = symbol.position;
|
|
@@ -153,12 +151,12 @@ function narrowing(facts, at, symbol, world) {
|
|
|
153
151
|
if (facts.published[file] === true && !closed) {
|
|
154
152
|
return undefined;
|
|
155
153
|
}
|
|
156
|
-
return findingOf(symbol,
|
|
154
|
+
return findingOf(symbol, CODE.redundantExportKeyword, `exported ${word} is referenced only inside the file that declares it`);
|
|
157
155
|
case "package":
|
|
158
156
|
if (!closed || !publishes(facts.rootedBy[at])) {
|
|
159
157
|
return undefined;
|
|
160
158
|
}
|
|
161
|
-
return findingOf(symbol,
|
|
159
|
+
return findingOf(symbol, CODE.unnecessaryExport, `exported ${word} is referenced only inside the package that declares it`);
|
|
162
160
|
default:
|
|
163
161
|
return undefined;
|
|
164
162
|
}
|
|
@@ -183,10 +181,10 @@ function unreachable(facts, at, symbol, candidate, input) {
|
|
|
183
181
|
const found = candidate.relation === "reachability"
|
|
184
182
|
? "is referenced only from declarations that are themselves dead"
|
|
185
183
|
: "has no reference in the target";
|
|
186
|
-
return findingOf(symbol,
|
|
184
|
+
return findingOf(symbol, CODE.unreachableExport, `exported ${wordOf(symbol)} ${found}, and nothing outside can import the file that declares it`);
|
|
187
185
|
}
|
|
188
186
|
/**
|
|
189
|
-
* The visibility-narrowing
|
|
187
|
+
* The visibility-narrowing kinds over one run: `DS1501`, `DS1101` and `DS1503`. A finding
|
|
190
188
|
* about a symbol a declared cross-language edge names is reported here like any other, and
|
|
191
189
|
* the edge's evaluation then holds it pending, because the edge stands for a reference from
|
|
192
190
|
* outside the file and the package whose use is decided in the other language. A
|
|
@@ -234,5 +232,5 @@ export function unreachableExports(input) {
|
|
|
234
232
|
}
|
|
235
233
|
return found;
|
|
236
234
|
}
|
|
237
|
-
/** The findings of the visibility-narrowing
|
|
235
|
+
/** The findings of the visibility-narrowing kinds, `DS1501`, `DS1101` and `DS1503`. */
|
|
238
236
|
export const visibilityNarrowing = narrowings;
|
|
@@ -3,27 +3,20 @@
|
|
|
3
3
|
* applied and the declared edges evaluated, and the stale suppressions, the totals and the
|
|
4
4
|
* verdict a report carries beside them.
|
|
5
5
|
*/
|
|
6
|
+
import { EXIT_CLEAN, EXIT_FINDINGS, EXIT_PENDING } from "./contract/contract.generated.js";
|
|
6
7
|
import { fails } from "./config.js";
|
|
7
8
|
import { decidedFindings } from "./findings/emitters.js";
|
|
8
9
|
import { fileRefsOf, staleSuppressions, } from "./findings/self-check.js";
|
|
9
10
|
import { isDependencySubject, isRowSubject, pairs, suppressionCounts, } from "./suppress.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
export const EXIT_PENDING = 4;
|
|
13
|
-
/** Two strings ordered bytewise. */
|
|
14
|
-
function compare(a, b) {
|
|
15
|
-
if (a === b) {
|
|
16
|
-
return 0;
|
|
17
|
-
}
|
|
18
|
-
return a < b ? -1 : 1;
|
|
19
|
-
}
|
|
11
|
+
import { bytewise } from "./text/compare.js";
|
|
12
|
+
import { PRODUCT } from "./product.js";
|
|
20
13
|
/** The canonical order: path, line, column, code, then the symbol reference. */
|
|
21
14
|
function canonical(a, b) {
|
|
22
|
-
return (
|
|
15
|
+
return (bytewise(a.position.path, b.position.path) ||
|
|
23
16
|
a.position.line - b.position.line ||
|
|
24
17
|
a.position.column - b.position.column ||
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
bytewise(a.code, b.code) ||
|
|
19
|
+
bytewise(a.symbol.ref, b.symbol.ref));
|
|
27
20
|
}
|
|
28
21
|
/**
|
|
29
22
|
* The pass over one run. The emitter table decides every record against every finding the
|
|
@@ -100,5 +93,5 @@ export function rowKey(row) {
|
|
|
100
93
|
}
|
|
101
94
|
/** The analyzer identity a baseline this analyzer writes names as its rows' provenance. */
|
|
102
95
|
export function analyzerProvenance(version) {
|
|
103
|
-
return { analyzer:
|
|
96
|
+
return { analyzer: PRODUCT, version };
|
|
104
97
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isRecord } from "../json-document.js";
|
|
2
|
+
import { joinPath } from "../paths.js";
|
|
3
|
+
/** The file a package's manifest is read from. */
|
|
4
|
+
export const MANIFEST = "package.json";
|
|
5
|
+
/** The directory a package manager installs packages into, whose files are no project's own. */
|
|
6
|
+
export const INSTALLED_PACKAGES = "node_modules";
|
|
7
|
+
/** Whether one path passes through an installed-packages directory. */
|
|
8
|
+
export function inInstalledPackages(path) {
|
|
9
|
+
return path.split("/").includes(INSTALLED_PACKAGES);
|
|
10
|
+
}
|
|
11
|
+
/** One JSON file's text and decoded value, or undefined where it cannot be read or parsed. */
|
|
12
|
+
export function readDocument(host, path) {
|
|
13
|
+
let text;
|
|
14
|
+
try {
|
|
15
|
+
text = host.readFile(path);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return { text, value: JSON.parse(text) };
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** One JSON file's decoded value, or undefined where it cannot be read or parsed. */
|
|
28
|
+
export function readJSON(host, path) {
|
|
29
|
+
return readDocument(host, path)?.value;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The manifest in one directory, decoded, or undefined where none reads as an object. A
|
|
33
|
+
* manifest this program cannot read declares nothing, the same answer as an absent one:
|
|
34
|
+
* the package manager owns the refusal of a manifest it rejects.
|
|
35
|
+
*/
|
|
36
|
+
export function manifestAt(host, dir) {
|
|
37
|
+
const value = readJSON(host, joinPath(dir, MANIFEST));
|
|
38
|
+
return isRecord(value) ? value : undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Whether one directory holds a manifest file. */
|
|
41
|
+
export function holdsManifest(host, dir) {
|
|
42
|
+
return host.kindOf(joinPath(dir, MANIFEST)) === "file";
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ancestors, joinPath, resolvePath } from "../paths.js";
|
|
2
|
+
import { LANGUAGE_ROWS } from "../contract/contract.generated.js";
|
|
3
|
+
import { LANGUAGE } from "../product.js";
|
|
4
|
+
import { bytewise } from "../text/compare.js";
|
|
5
|
+
import { globExpression } from "../text/glob.js";
|
|
6
|
+
/** The directories no walk of the tree enters, by the patterns the language's row names. */
|
|
7
|
+
const SKIPPED_DIRECTORIES = LANGUAGE_ROWS[LANGUAGE].skippedDirectories.map(globExpression);
|
|
8
|
+
function skipped(name) {
|
|
9
|
+
return SKIPPED_DIRECTORIES.some((pattern) => pattern.test(name));
|
|
10
|
+
}
|
|
11
|
+
/** One file's text without its line ending, or undefined where it cannot be read. */
|
|
12
|
+
function lineOf(host, path) {
|
|
13
|
+
try {
|
|
14
|
+
return host.readFile(path).replace(/\r?\n$/u, "");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The repository whose working tree holds `dir`: the nearest directory at or above it with a
|
|
22
|
+
* `.git`. A `.git` file, which a linked worktree or a submodule holds, names the repository's
|
|
23
|
+
* directory on its `gitdir:` line; that directory's `commondir` file, where it has one, names
|
|
24
|
+
* the directory shared with the main worktree, which holds `info/exclude`:
|
|
25
|
+
* https://git-scm.com/docs/gitrepository-layout
|
|
26
|
+
*/
|
|
27
|
+
export function repositoryOf(host, dir) {
|
|
28
|
+
for (const root of ancestors(dir)) {
|
|
29
|
+
const dotGit = joinPath(root, ".git");
|
|
30
|
+
const kind = host.kindOf(dotGit);
|
|
31
|
+
if (kind === "directory") {
|
|
32
|
+
return { root, exclude: joinPath(dotGit, "info/exclude") };
|
|
33
|
+
}
|
|
34
|
+
if (kind === "file") {
|
|
35
|
+
const named = /^gitdir: (.+)$/u.exec(lineOf(host, dotGit) ?? "")?.[1];
|
|
36
|
+
const gitDir = named === undefined ? undefined : resolvePath(root, named);
|
|
37
|
+
const common = gitDir === undefined ? undefined : lineOf(host, joinPath(gitDir, "commondir"));
|
|
38
|
+
const shared = gitDir === undefined || common === undefined ? gitDir : resolvePath(gitDir, common);
|
|
39
|
+
return { root, exclude: shared === undefined ? undefined : joinPath(shared, "info/exclude") };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
/** The entries of one directory, none where it cannot be read. */
|
|
45
|
+
export function entriesOf(host, dir) {
|
|
46
|
+
try {
|
|
47
|
+
return host.readDirectory(dir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The names of the files directly in `dir` whose name `accepts`, in bytewise order. */
|
|
54
|
+
export function filesIn(host, dir, accepts) {
|
|
55
|
+
return entriesOf(host, dir)
|
|
56
|
+
.filter((entry) => !entry.directory && accepts(entry.name))
|
|
57
|
+
.map((entry) => entry.name)
|
|
58
|
+
.sort(bytewise);
|
|
59
|
+
}
|
|
60
|
+
/** The names of the directories directly in `dir` a walk enters, in bytewise order. */
|
|
61
|
+
export function directoriesIn(host, dir) {
|
|
62
|
+
return entriesOf(host, dir)
|
|
63
|
+
.filter((entry) => entry.directory && !skipped(entry.name))
|
|
64
|
+
.map((entry) => entry.name)
|
|
65
|
+
.sort(bytewise);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Every file below `root` whose name `accepts`, by its path relative to `root` in bytewise
|
|
69
|
+
* order, read in no skipped directory.
|
|
70
|
+
*/
|
|
71
|
+
export function filesBelow(host, root, accepts) {
|
|
72
|
+
const found = [];
|
|
73
|
+
const pending = [""];
|
|
74
|
+
for (let dir = pending.pop(); dir !== undefined; dir = pending.pop()) {
|
|
75
|
+
for (const entry of entriesOf(host, joinPath(root, dir))) {
|
|
76
|
+
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
77
|
+
if (!entry.directory) {
|
|
78
|
+
if (accepts(entry.name)) {
|
|
79
|
+
found.push(path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (!skipped(entry.name)) {
|
|
83
|
+
pending.push(path);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return found.sort(bytewise);
|
|
88
|
+
}
|
package/dist/src/git-ignore.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which files
|
|
2
|
+
* Which files git ignores below a tree's root, read as git reads its ignore files:
|
|
3
3
|
* https://git-scm.com/docs/gitignore#_pattern_format
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
5
|
+
import { ancestors, dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
6
|
+
import { gitignoreExpression } from "./text/glob.js";
|
|
7
|
+
import { repositoryOf } from "./fs/tree.js";
|
|
8
|
+
/** The rules one ignore file's text writes, in order. */
|
|
8
9
|
function rulesOf(text) {
|
|
9
10
|
const rules = [];
|
|
10
11
|
for (const raw of text.split(/\r?\n/u)) {
|
|
@@ -13,7 +14,7 @@ function rulesOf(text) {
|
|
|
13
14
|
continue;
|
|
14
15
|
}
|
|
15
16
|
const negated = line.startsWith("!");
|
|
16
|
-
line = negated
|
|
17
|
+
line = negated ? line.slice(1) : line;
|
|
17
18
|
const directoryOnly = line.endsWith("/");
|
|
18
19
|
line = line.replace(/\/+$/u, "");
|
|
19
20
|
if (line === "") {
|
|
@@ -26,43 +27,51 @@ function rulesOf(text) {
|
|
|
26
27
|
rules.push({
|
|
27
28
|
negated,
|
|
28
29
|
directoryOnly,
|
|
29
|
-
expression:
|
|
30
|
+
expression: gitignoreExpression(anchored ? pattern : `**/${pattern}`),
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
return rules;
|
|
33
34
|
}
|
|
35
|
+
function rulesIn(host, path) {
|
|
36
|
+
try {
|
|
37
|
+
return rulesOf(host.readFile(path));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
34
43
|
/**
|
|
35
|
-
* Whether
|
|
36
|
-
*
|
|
37
|
-
*
|
|
44
|
+
* Whether git ignores one absolute path below `root`: by the repository's `info/exclude`,
|
|
45
|
+
* then the `.gitignore` files from the repository's root down to the path's directory, a
|
|
46
|
+
* later pattern overriding an earlier one. A path outside `root` is not ignored, and a file
|
|
47
|
+
* below an ignored directory stays ignored whatever a later pattern says, as git cannot
|
|
48
|
+
* re-include it. With no repository at or above `root`, the files at and below it are read;
|
|
49
|
+
* with one, the directories above `root` give patterns but are not themselves tested.
|
|
38
50
|
*/
|
|
39
51
|
export function gitIgnored(host, root) {
|
|
52
|
+
const found = repositoryOf(host, root);
|
|
53
|
+
const repository = found?.root;
|
|
54
|
+
const above = repository === undefined ? [] : [...ancestors(dirnamePath(root), repository)].reverse();
|
|
55
|
+
const excluded = found?.exclude === undefined ? [] : rulesIn(host, found.exclude);
|
|
40
56
|
const read = new Map();
|
|
41
57
|
const rulesAt = (dir) => {
|
|
42
58
|
let rules = read.get(dir);
|
|
43
59
|
if (rules === undefined) {
|
|
44
|
-
|
|
45
|
-
rules = rulesOf(host.readFile(joinPath(dir, ".gitignore")));
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
rules = [];
|
|
49
|
-
}
|
|
60
|
+
rules = rulesIn(host, joinPath(dir, ".gitignore"));
|
|
50
61
|
read.set(dir, rules);
|
|
51
62
|
}
|
|
52
63
|
return rules;
|
|
53
64
|
};
|
|
54
|
-
/** Whether the rules of `root` and of each directory down to one path's own decide it ignored. */
|
|
55
65
|
const decided = (path, directory) => {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
dirs.unshift(root);
|
|
66
|
+
const sources = [
|
|
67
|
+
...(repository === undefined ? [] : [[repository, excluded]]),
|
|
68
|
+
...[...above, ...[...ancestors(dirnamePath(path), root)].reverse()].map((dir) => [dir, rulesAt(dir)]),
|
|
69
|
+
];
|
|
61
70
|
let ignored = false;
|
|
62
|
-
for (const dir of
|
|
63
|
-
const below = relativePath(dir, path);
|
|
64
|
-
for (const rule of
|
|
65
|
-
if ((directory || !rule.directoryOnly) && rule.expression.test(below
|
|
71
|
+
for (const [dir, rules] of sources) {
|
|
72
|
+
const below = relativePath(dir, path) ?? "";
|
|
73
|
+
for (const rule of rules) {
|
|
74
|
+
if ((directory || !rule.directoryOnly) && rule.expression.test(below)) {
|
|
66
75
|
ignored = !rule.negated;
|
|
67
76
|
}
|
|
68
77
|
}
|
package/dist/src/html-entries.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isExportDeclaration, isImportDeclaration, isStringLiteral, } from "@typescript/native/ast";
|
|
7
7
|
import { resolveRelativeSpecifier } from "./configuration-files.js";
|
|
8
|
-
import { dirnamePath, joinPath,
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
import { ancestors, dirnamePath, joinPath, resolvePath } from "./paths.js";
|
|
9
|
+
import { filesBelow } from "./fs/tree.js";
|
|
10
|
+
import { holdsManifest } from "./fs/manifest.js";
|
|
11
11
|
/** A `<script` start tag, its attribute text captured. */
|
|
12
12
|
const SCRIPT_TAG = /<script(?=[\s>/])([^>]*)>/giu;
|
|
13
13
|
/** The end tag an inline module script's content runs to. */
|
|
@@ -20,29 +20,6 @@ const ELSEWHERE = /^(?:\/\/|[A-Za-z][A-Za-z\d+.-]*:)/u;
|
|
|
20
20
|
const CLASSIC_TYPES = new Set(["text/javascript", "application/javascript"]);
|
|
21
21
|
/** The prefixes an inline script's specifier names a file of the target with. */
|
|
22
22
|
const FILE_PREFIXES = ["/", "./", "../"];
|
|
23
|
-
/** Every HTML file below `dir`, outside every `node_modules` directory, by absolute path. */
|
|
24
|
-
function htmlFiles(host, dir) {
|
|
25
|
-
let entries;
|
|
26
|
-
try {
|
|
27
|
-
entries = host.readDirectory(dir);
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
const found = [];
|
|
33
|
-
for (const entry of entries) {
|
|
34
|
-
const path = joinPath(dir, entry.name);
|
|
35
|
-
if (entry.directory) {
|
|
36
|
-
if (entry.name !== SKIPPED) {
|
|
37
|
-
found.push(...htmlFiles(host, path));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
else if (entry.name.endsWith(".html")) {
|
|
41
|
-
found.push(path);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return found.sort();
|
|
45
|
-
}
|
|
46
23
|
/** Each attribute of one start tag's attribute text, by its lowercase name. */
|
|
47
24
|
function attributes(text) {
|
|
48
25
|
const found = new Map();
|
|
@@ -98,12 +75,7 @@ function scriptValues(parse, path, text) {
|
|
|
98
75
|
}
|
|
99
76
|
/** The directory of the nearest `package.json` at or above `dir`, inside `root`, else `root`. */
|
|
100
77
|
function manifestDirectory(host, root, dir) {
|
|
101
|
-
|
|
102
|
-
if (host.kindOf(joinPath(at, "package.json")) === "file") {
|
|
103
|
-
return at;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return root;
|
|
78
|
+
return ancestors(dir, root).find((at) => holdsManifest(host, at)) ?? root;
|
|
107
79
|
}
|
|
108
80
|
/**
|
|
109
81
|
* The own files the scripts of every HTML file below the target root load. A value
|
|
@@ -115,7 +87,8 @@ function manifestDirectory(host, root, dir) {
|
|
|
115
87
|
export function htmlEntries(host, targetRoot, files, parse) {
|
|
116
88
|
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
117
89
|
const found = [];
|
|
118
|
-
for (const
|
|
90
|
+
for (const below of filesBelow(host, root, (name) => name.endsWith(".html"))) {
|
|
91
|
+
const path = joinPath(root, below);
|
|
119
92
|
let text;
|
|
120
93
|
try {
|
|
121
94
|
text = host.readFile(path);
|
package/dist/src/in-operands.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isBinaryExpression, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
7
7
|
import { sourceFilesOf } from "./source-files.js";
|
|
8
|
+
import { bytewise } from "./text/compare.js";
|
|
8
9
|
/**
|
|
9
10
|
* The text of each string literal, or template literal with no substitution, written as
|
|
10
11
|
* the left operand of an `in` expression in a file of the project that is no test file.
|
|
@@ -26,5 +27,5 @@ export function inOperandNames(project, targetRoot, testFiles) {
|
|
|
26
27
|
file.forEachChild(visit);
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
return [...found].sort();
|
|
30
|
+
return [...found].sort(bytewise);
|
|
30
31
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -2,4 +2,4 @@ export { run, SETTING_OPTIONS } from "./run.js";
|
|
|
2
2
|
export { DECLINED_CONVENTIONS } from "./entry-point-gaps.js";
|
|
3
3
|
export { byPosition, positionKey, PositionError, renderPosition, } from "./position.js";
|
|
4
4
|
export { computedComponent, isRef, nameComponent, REF_EXPRESSIONS, renderRef, } from "./ref.js";
|
|
5
|
-
export { CONTRACT_VERSION } from "./
|
|
5
|
+
export { CONTRACT_VERSION } from "./contract/contract.generated.js";
|