@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
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the compiler writes what it compiles: the output extension each source
|
|
3
|
+
* extension becomes, and the output path one source path is written to. A path a
|
|
4
|
+
* manifest or a document names is read back to its source through this one rule,
|
|
5
|
+
* whether the path is the target's own or a workspace package's.
|
|
6
|
+
*/
|
|
7
|
+
import { dirnamePath, joinPath, relativePath } from "./paths.js";
|
|
8
|
+
/** The source extensions the compiler emits each output extension from. */
|
|
9
|
+
const EMITTED = [
|
|
10
|
+
{ emitted: ".d.ts", sources: [".ts", ".tsx"] },
|
|
11
|
+
{ emitted: ".d.mts", sources: [".mts"] },
|
|
12
|
+
{ emitted: ".d.cts", sources: [".cts"] },
|
|
13
|
+
{ emitted: ".js", sources: [".ts", ".tsx"] },
|
|
14
|
+
{ emitted: ".jsx", sources: [".tsx"] },
|
|
15
|
+
{ emitted: ".mjs", sources: [".mts"] },
|
|
16
|
+
{ emitted: ".cjs", sources: [".cts"] },
|
|
17
|
+
];
|
|
18
|
+
/** One path with its extension replaced, where it carries the one named. */
|
|
19
|
+
function reExtended(path, emitted, source) {
|
|
20
|
+
return path.endsWith(emitted) ? path.slice(0, path.length - emitted.length) + source : undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The longest directory every path shares, which is where the compiler roots its
|
|
24
|
+
* output when the configuration declares no root directory and its directory is not
|
|
25
|
+
* the root.
|
|
26
|
+
*/
|
|
27
|
+
export function commonDirectory(paths) {
|
|
28
|
+
const first = paths[0];
|
|
29
|
+
if (first === undefined) {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
let common = dirnamePath(first).split("/");
|
|
33
|
+
for (const path of paths.slice(1)) {
|
|
34
|
+
const segments = dirnamePath(path).split("/");
|
|
35
|
+
let shared = 0;
|
|
36
|
+
while (shared < common.length &&
|
|
37
|
+
shared < segments.length &&
|
|
38
|
+
common[shared] === segments[shared]) {
|
|
39
|
+
shared += 1;
|
|
40
|
+
}
|
|
41
|
+
common = common.slice(0, shared);
|
|
42
|
+
}
|
|
43
|
+
return common.join("/");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The source paths one written path may be compiled from, in the order they are
|
|
47
|
+
* tried: for each output extension the path carries, the path re-extended where it
|
|
48
|
+
* stands, then read below each source directory where it is below the directory that
|
|
49
|
+
* output is written to.
|
|
50
|
+
*/
|
|
51
|
+
export function emittedFrom(path, layout) {
|
|
52
|
+
const found = [];
|
|
53
|
+
for (const { emitted, sources } of EMITTED) {
|
|
54
|
+
if (!path.endsWith(emitted)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const emitDir = emitted.startsWith(".d.")
|
|
58
|
+
? (layout.declarationDir ?? layout.outDir)
|
|
59
|
+
: layout.outDir;
|
|
60
|
+
const below = emitDir === undefined ? undefined : relativePath(emitDir, path);
|
|
61
|
+
const mapped = below === undefined ? [] : layout.sourceDirs.map((dir) => joinPath(dir, below));
|
|
62
|
+
for (const source of sources) {
|
|
63
|
+
for (const from of [path, ...mapped]) {
|
|
64
|
+
const candidate = reExtended(from, emitted, source);
|
|
65
|
+
if (candidate !== undefined) {
|
|
66
|
+
found.push(candidate);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return found;
|
|
72
|
+
}
|
|
@@ -7,15 +7,18 @@
|
|
|
7
7
|
* Every row carries a reason, because the list is read where the rows are shown
|
|
8
8
|
* one at a time: beside a finding, or in a report's declared gaps.
|
|
9
9
|
*/
|
|
10
|
-
/** What is declined for a tool
|
|
11
|
-
const EVERY_CONVENTION = "
|
|
12
|
-
/** Why a tool
|
|
13
|
-
const UNREAD = "No rule reads it
|
|
14
|
-
"ts.entry_files or roots.patterns
|
|
15
|
-
|
|
10
|
+
/** What is declined for a tool whose own conventions are entered only through its configuration files. */
|
|
11
|
+
const EVERY_CONVENTION = "every file it loads by its own convention beyond its configuration files";
|
|
12
|
+
/** Why a tool whose own conventions are not read declines them. */
|
|
13
|
+
const UNREAD = "No rule reads it beyond a configuration file the file-name form or a string enters, so a " +
|
|
14
|
+
"file only it loads is reported as never imported unless ts.entry_files or roots.patterns " +
|
|
15
|
+
"names it.";
|
|
16
|
+
/** Why a configuration's file names that are not relative literals are declined. */
|
|
17
|
+
const NOT_RELATIVE = "A string of a configuration file names a file only where it is a literal starting with ./ " +
|
|
18
|
+
"or ../, so a file named otherwise is reported as never imported.";
|
|
19
|
+
/** The tools whose own conventions are not read. */
|
|
16
20
|
const UNREAD_TOOLS = [
|
|
17
21
|
"angular",
|
|
18
|
-
"astro",
|
|
19
22
|
"astro-db",
|
|
20
23
|
"astro-markdoc",
|
|
21
24
|
"astro-og-canvas",
|
|
@@ -48,7 +51,6 @@ const UNREAD_TOOLS = [
|
|
|
48
51
|
"esbuild",
|
|
49
52
|
"eve",
|
|
50
53
|
"execa",
|
|
51
|
-
"expo",
|
|
52
54
|
"expressive-code",
|
|
53
55
|
"fast",
|
|
54
56
|
"fumadocs",
|
|
@@ -85,7 +87,6 @@ const UNREAD_TOOLS = [
|
|
|
85
87
|
"nano-staged",
|
|
86
88
|
"nest",
|
|
87
89
|
"netlify",
|
|
88
|
-
"next",
|
|
89
90
|
"next-intl",
|
|
90
91
|
"next-mdx",
|
|
91
92
|
"nitro",
|
|
@@ -93,7 +94,6 @@ const UNREAD_TOOLS = [
|
|
|
93
94
|
"node-modules-inspector",
|
|
94
95
|
"nodemon",
|
|
95
96
|
"npm-package-json-lint",
|
|
96
|
-
"nuxt",
|
|
97
97
|
"nuxtjs-i18n",
|
|
98
98
|
"nx",
|
|
99
99
|
"nyc",
|
|
@@ -118,17 +118,14 @@ const UNREAD_TOOLS = [
|
|
|
118
118
|
"prettier",
|
|
119
119
|
"prisma",
|
|
120
120
|
"quasar",
|
|
121
|
-
"qwik",
|
|
122
121
|
"railway",
|
|
123
122
|
"raycast",
|
|
124
123
|
"react-cosmos",
|
|
125
124
|
"react-email",
|
|
126
125
|
"react-native",
|
|
127
|
-
"react-router",
|
|
128
126
|
"relay",
|
|
129
127
|
"release-it",
|
|
130
128
|
"remark",
|
|
131
|
-
"remix",
|
|
132
129
|
"rolldown",
|
|
133
130
|
"rollup",
|
|
134
131
|
"rsbuild",
|
|
@@ -144,11 +141,9 @@ const UNREAD_TOOLS = [
|
|
|
144
141
|
"sst",
|
|
145
142
|
"starlight",
|
|
146
143
|
"stencil",
|
|
147
|
-
"storybook",
|
|
148
144
|
"stylelint",
|
|
149
145
|
"svelte",
|
|
150
146
|
"sveltejs-package",
|
|
151
|
-
"sveltekit",
|
|
152
147
|
"svgo",
|
|
153
148
|
"svgr",
|
|
154
149
|
"swc",
|
|
@@ -171,7 +166,6 @@ const UNREAD_TOOLS = [
|
|
|
171
166
|
"typescript-content-mapper",
|
|
172
167
|
"unbuild",
|
|
173
168
|
"unocss",
|
|
174
|
-
"unplugin-auto-import",
|
|
175
169
|
"unplugin-icons",
|
|
176
170
|
"unplugin-vue-components",
|
|
177
171
|
"unplugin-vue-i18n",
|
|
@@ -201,9 +195,15 @@ const UNREAD_TOOLS = [
|
|
|
201
195
|
const PARTLY_ENTERED = [
|
|
202
196
|
{
|
|
203
197
|
tool: "eslint",
|
|
204
|
-
convention: "
|
|
205
|
-
reason: "Only
|
|
206
|
-
"
|
|
198
|
+
convention: "a legacy configuration written in YAML or in JSON with comments",
|
|
199
|
+
reason: "Only a configuration module and a strict JSON configuration file are read, so a " +
|
|
200
|
+
"dependency only such a file names is reported as unused.",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
tool: "eslint",
|
|
204
|
+
convention: "a plugin or shareable configuration named without its package's prefix",
|
|
205
|
+
reason: "A string uses a dependency only where it spells the dependency's name, so a " +
|
|
206
|
+
"dependency only a shortened name loads is reported as unused.",
|
|
207
207
|
},
|
|
208
208
|
{
|
|
209
209
|
tool: "playwright",
|
|
@@ -213,8 +213,8 @@ const PARTLY_ENTERED = [
|
|
|
213
213
|
},
|
|
214
214
|
{
|
|
215
215
|
tool: "playwright",
|
|
216
|
-
convention: "
|
|
217
|
-
reason:
|
|
216
|
+
convention: "a globalSetup or globalTeardown file named without a leading ./ or ../",
|
|
217
|
+
reason: NOT_RELATIVE,
|
|
218
218
|
},
|
|
219
219
|
{
|
|
220
220
|
tool: "playwright",
|
|
@@ -222,16 +222,64 @@ const PARTLY_ENTERED = [
|
|
|
222
222
|
reason: "Only the configuration object's own testDir and testMatch are read, so a test " +
|
|
223
223
|
"directory a project entry moves is not entered.",
|
|
224
224
|
},
|
|
225
|
+
{
|
|
226
|
+
tool: "expo",
|
|
227
|
+
convention: "an app directory the expo-router entry of an app configuration moves",
|
|
228
|
+
reason: "The convention row reads the app directory at its default places only, so a route " +
|
|
229
|
+
"below a moved directory is reported as never imported unless ts.entry_files names it.",
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
tool: "next",
|
|
233
|
+
convention: "a page extension next.config adds through pageExtensions",
|
|
234
|
+
reason: "The convention row roots the default extensions only, so a route file of another " +
|
|
235
|
+
"extension is reported as never imported unless ts.entry_files names it.",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
tool: "nuxt",
|
|
239
|
+
convention: "the files of a layer below layers/ beyond its nuxt.config",
|
|
240
|
+
reason: "The convention row reads the project's own directories, so a layer's pages, plugins " +
|
|
241
|
+
"and server routes are reported as never imported unless ts.entry_files names them.",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
tool: "qwik",
|
|
245
|
+
convention: "the routes of @qwik.dev/router",
|
|
246
|
+
reason: "No convention row holds for that package, so its route files are reported as " +
|
|
247
|
+
"never imported unless ts.entry_files names them.",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
tool: "react-router",
|
|
251
|
+
convention: "a route module app/routes.ts names outside the routes directory",
|
|
252
|
+
reason: "The convention row roots the routes directory, and a string of routes.ts names no " +
|
|
253
|
+
"file, so a route module elsewhere is reported as never imported unless " +
|
|
254
|
+
"ts.entry_files names it.",
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
tool: "storybook",
|
|
258
|
+
convention: "a story the stories member of .storybook/main names outside *.stories.* files",
|
|
259
|
+
reason: "The convention row roots *.stories.* files, so a story file named otherwise is " +
|
|
260
|
+
"reported as never imported unless ts.entry_files names it.",
|
|
261
|
+
},
|
|
225
262
|
{
|
|
226
263
|
tool: "stryker",
|
|
227
|
-
convention: "a configuration file named on
|
|
228
|
-
reason: "Only the file names the tool looks for by default are entered.",
|
|
264
|
+
convention: "a configuration file named on a command line no package.json script writes",
|
|
265
|
+
reason: "Only the file names the tool looks for by default and the files a script names are entered.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
tool: "unplugin-auto-import",
|
|
269
|
+
convention: "an import its generated declarations add for a component's template alone",
|
|
270
|
+
reason: "Only a global declared as an alias of an export is read, so an export the generated " +
|
|
271
|
+
"declarations name only for templates is reported as unused.",
|
|
229
272
|
},
|
|
230
273
|
{
|
|
231
274
|
tool: "vite",
|
|
232
|
-
convention: "its configuration
|
|
233
|
-
reason:
|
|
234
|
-
|
|
275
|
+
convention: "an entry point its configuration names without a leading ./ or ../, or computes",
|
|
276
|
+
reason: NOT_RELATIVE,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
tool: "vite",
|
|
280
|
+
convention: "the module scripts an index.html loads",
|
|
281
|
+
reason: "No rule reads an HTML file, so a script only index.html loads is reported as never " +
|
|
282
|
+
"imported unless ts.entry_files names it.",
|
|
235
283
|
},
|
|
236
284
|
{
|
|
237
285
|
tool: "vitest",
|
|
@@ -241,15 +289,14 @@ const PARTLY_ENTERED = [
|
|
|
241
289
|
},
|
|
242
290
|
{
|
|
243
291
|
tool: "vitest",
|
|
244
|
-
convention: "the
|
|
245
|
-
reason: "
|
|
246
|
-
"
|
|
292
|
+
convention: "the setup files of a configuration that moves its root",
|
|
293
|
+
reason: "A string is read against the configuration file's directory, so a setup file named " +
|
|
294
|
+
"against a root the configuration moves is not entered.",
|
|
247
295
|
},
|
|
248
296
|
{
|
|
249
297
|
tool: "vitest",
|
|
250
|
-
convention: "
|
|
251
|
-
reason:
|
|
252
|
-
"may not state, so none of them is entered.",
|
|
298
|
+
convention: "a setup file named without a leading ./ or ../",
|
|
299
|
+
reason: NOT_RELATIVE,
|
|
253
300
|
},
|
|
254
301
|
{
|
|
255
302
|
tool: "vitest",
|
|
@@ -259,9 +306,8 @@ const PARTLY_ENTERED = [
|
|
|
259
306
|
},
|
|
260
307
|
{
|
|
261
308
|
tool: "webpack",
|
|
262
|
-
convention: "its configuration
|
|
263
|
-
reason:
|
|
264
|
-
"configuration, so a file only it names is reported as never imported.",
|
|
309
|
+
convention: "an entry point its configuration names without a leading ./ or ../, or computes",
|
|
310
|
+
reason: NOT_RELATIVE,
|
|
265
311
|
},
|
|
266
312
|
];
|
|
267
313
|
/** Two strings ordered bytewise. */
|
package/dist/src/entry-points.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The files a tool or the platform enters without any import naming them: the
|
|
3
|
-
* configuration files
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* configuration files a tool loads by their file name and the files their strings
|
|
4
|
+
* name, the test files the runner executes, and a worker a call addresses by a string
|
|
5
|
+
* literal.
|
|
6
6
|
*
|
|
7
7
|
* Each rule is read from the program and nothing else: a file name the tool
|
|
8
8
|
* itself looks for, a literal written in a configuration file, or a literal
|
|
@@ -12,45 +12,31 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { isArrayLiteralExpression, isAsExpression, isCallExpression, isExportAssignment, isIdentifier, isMetaProperty, isNewExpression, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, } from "@typescript/native/unstable/ast";
|
|
14
14
|
import { SymbolFlags } from "@typescript/native/unstable/sync";
|
|
15
|
+
import { configurationFiles, filesNamedBy, moduleStrings, } from "./configuration-files.js";
|
|
15
16
|
import { globExpression } from "./glob.js";
|
|
16
17
|
import { dirnamePath, relativePath, resolvePath } from "./paths.js";
|
|
17
18
|
import { UNANSWERED } from "./query.js";
|
|
18
19
|
/** The extensions the test runner and the browser-test runner load a configuration from. */
|
|
19
20
|
const MODULE_EXTENSIONS = ["js", "mjs", "cjs", "ts", "mts", "cts"];
|
|
20
|
-
/** The extensions the
|
|
21
|
+
/** The extensions the mutation tester's configuration is loaded from. */
|
|
21
22
|
const SCRIPT_EXTENSIONS = ["js", "mjs", "cjs"];
|
|
23
|
+
/** The tool file names the configuration form does not cover. */
|
|
22
24
|
const CONVENTIONS = [
|
|
23
25
|
{
|
|
24
26
|
rule: "test-runner",
|
|
25
|
-
stems: ["vitest.
|
|
27
|
+
stems: ["vitest.workspace", "vitest.projects"],
|
|
26
28
|
extensions: MODULE_EXTENSIONS,
|
|
27
29
|
testRunner: true,
|
|
28
30
|
},
|
|
29
|
-
{
|
|
30
|
-
rule: "lint-configuration",
|
|
31
|
-
stems: ["eslint.config"],
|
|
32
|
-
extensions: MODULE_EXTENSIONS,
|
|
33
|
-
testRunner: false,
|
|
34
|
-
},
|
|
35
31
|
{
|
|
36
32
|
rule: "mutation-testing",
|
|
37
|
-
stems: ["stryker.
|
|
33
|
+
stems: ["stryker.conf", ".stryker.config", ".stryker.conf"],
|
|
38
34
|
extensions: SCRIPT_EXTENSIONS,
|
|
39
35
|
testRunner: false,
|
|
40
36
|
},
|
|
41
|
-
{
|
|
42
|
-
rule: "mutation-testing",
|
|
43
|
-
stems: ["vitest.stryker.config"],
|
|
44
|
-
extensions: MODULE_EXTENSIONS,
|
|
45
|
-
testRunner: true,
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
rule: "browser-tests",
|
|
49
|
-
stems: ["playwright.config"],
|
|
50
|
-
extensions: MODULE_EXTENSIONS,
|
|
51
|
-
testRunner: false,
|
|
52
|
-
},
|
|
53
37
|
];
|
|
38
|
+
/** The stem of the browser-test runner's configuration, whose test directory is read. */
|
|
39
|
+
const BROWSER_TEST_CONFIG = "playwright.config";
|
|
54
40
|
/** The browser-test runner's own test-file pattern, which holds where a configuration names none. */
|
|
55
41
|
const BROWSER_TEST_MATCH = "**/*.{spec,test}.{js,ts,jsx,tsx,cjs,cts,cjsx,ctsx,mjs,mts,mjsx,mtsx}";
|
|
56
42
|
/** The members of a test-runner configuration's `test` object that name setup files. */
|
|
@@ -174,6 +160,37 @@ function filesNamed(files, dir, text) {
|
|
|
174
160
|
}
|
|
175
161
|
return files.named(resolvePath(dir, pathPart(text)));
|
|
176
162
|
}
|
|
163
|
+
/** Whether one file name is the browser-test runner's configuration. */
|
|
164
|
+
function isBrowserTestConfig(name) {
|
|
165
|
+
return MODULE_EXTENSIONS.some((ext) => name === `${BROWSER_TEST_CONFIG}.${ext}`);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The files the configuration files of one project enter: each configuration module,
|
|
169
|
+
* with what it exports, and each own file a relative string of a configuration module
|
|
170
|
+
* or a JSON configuration file names, read against that file's directory.
|
|
171
|
+
*/
|
|
172
|
+
function configurationEntries(host, targetRoot, files, parse) {
|
|
173
|
+
const { modules, documents, outside } = configurationFiles(host, targetRoot, files, parse);
|
|
174
|
+
const found = [];
|
|
175
|
+
const strings = (dir, texts) => {
|
|
176
|
+
for (const text of texts) {
|
|
177
|
+
for (const file of filesNamedBy(files, dir, text)) {
|
|
178
|
+
found.push({ file, rule: "configuration-string", source: text, exports: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
for (const { file, form } of modules) {
|
|
183
|
+
found.push({ file, rule: "configuration-file", source: form, exports: true });
|
|
184
|
+
strings(dirnamePath(file.fileName), moduleStrings(file).map((one) => one.text));
|
|
185
|
+
}
|
|
186
|
+
for (const document of documents) {
|
|
187
|
+
strings(dirnamePath(document.path), document.strings);
|
|
188
|
+
}
|
|
189
|
+
for (const { file } of outside) {
|
|
190
|
+
strings(dirnamePath(file.fileName), moduleStrings(file).map((one) => one.text));
|
|
191
|
+
}
|
|
192
|
+
return found;
|
|
193
|
+
}
|
|
177
194
|
/**
|
|
178
195
|
* The setup files one test-runner configuration names: every literal under
|
|
179
196
|
* `test.setupFiles` and `test.globalSetup`, read against the configuration's own
|
|
@@ -360,26 +377,27 @@ function workersOf(project, file, files) {
|
|
|
360
377
|
return found;
|
|
361
378
|
}
|
|
362
379
|
/**
|
|
363
|
-
* Every file of one project the
|
|
364
|
-
*
|
|
380
|
+
* Every file of one project the rules enter, in no particular order and possibly more
|
|
381
|
+
* than once: a file two rules enter appears once per rule.
|
|
365
382
|
*
|
|
366
383
|
* `testFiles` is the configuration's test-file patterns, matched against each
|
|
367
384
|
* file's path below the target root; a file one of them matches is a test file the
|
|
368
385
|
* runner executes.
|
|
369
386
|
*/
|
|
370
|
-
export function entryPoints(project, files, testFiles) {
|
|
371
|
-
const found = [];
|
|
387
|
+
export function entryPoints(project, files, testFiles, host, targetRoot, parse) {
|
|
388
|
+
const found = [...configurationEntries(host, targetRoot, files, parse)];
|
|
372
389
|
const tests = testFiles.map((pattern) => ({ pattern, expression: globExpression(pattern) }));
|
|
373
390
|
for (const [path, file] of files.byPath) {
|
|
374
|
-
const
|
|
391
|
+
const name = basename(path);
|
|
392
|
+
const named = conventionOf(name);
|
|
375
393
|
if (named !== undefined) {
|
|
376
394
|
found.push({ file, rule: named.convention.rule, source: named.spelled, exports: true });
|
|
377
395
|
if (named.convention.testRunner) {
|
|
378
396
|
found.push(...setupFilesOf(file, files));
|
|
379
397
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
398
|
+
}
|
|
399
|
+
if (isBrowserTestConfig(name)) {
|
|
400
|
+
found.push(...browserTestsOf(file, files));
|
|
383
401
|
}
|
|
384
402
|
for (const { pattern, expression } of tests) {
|
|
385
403
|
if (expression.test(path)) {
|
|
@@ -26,7 +26,12 @@ export const EXEMPTION_CLASSES = [
|
|
|
26
26
|
confidence: "certain",
|
|
27
27
|
typescriptVisibility: { private: false, privateName: false },
|
|
28
28
|
},
|
|
29
|
-
{
|
|
29
|
+
{
|
|
30
|
+
class: "generated-file",
|
|
31
|
+
languages: ["go", "ts"],
|
|
32
|
+
confidence: "certain",
|
|
33
|
+
typescriptVisibility: { private: true, privateName: false },
|
|
34
|
+
},
|
|
30
35
|
{ class: "linkname-cgo-asm-plugin", languages: ["go"], confidence: "certain" },
|
|
31
36
|
{
|
|
32
37
|
class: "template-field",
|
package/dist/src/exempt.js
CHANGED
|
@@ -88,8 +88,13 @@ function detected(exemptionClass, detect, input) {
|
|
|
88
88
|
* test file makes is none there.
|
|
89
89
|
*/
|
|
90
90
|
export function computeExemptions(input, detectors, holding) {
|
|
91
|
+
return detectExemptions(input, detectors, holding).records;
|
|
92
|
+
}
|
|
93
|
+
/** The records of {@link computeExemptions}, beside the test-file evidence it set aside. */
|
|
94
|
+
export function detectExemptions(input, detectors, holding) {
|
|
91
95
|
const visibility = new Map(input.held.symbols.map((symbol) => [symbol.id, symbol.visibility]));
|
|
92
96
|
const found = [];
|
|
97
|
+
const inTestFiles = [];
|
|
93
98
|
for (const exemptionClass of TS_EXEMPTION_CLASSES) {
|
|
94
99
|
const detect = detectors.get(exemptionClass);
|
|
95
100
|
if (detect === undefined || holding.disabled.has(exemptionClass)) {
|
|
@@ -100,18 +105,21 @@ export function computeExemptions(input, detectors, holding) {
|
|
|
100
105
|
if (!retainable(allowed, visibility.get(evidence.id))) {
|
|
101
106
|
continue;
|
|
102
107
|
}
|
|
103
|
-
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
found.push({
|
|
108
|
+
const record = {
|
|
107
109
|
id: evidence.id,
|
|
108
110
|
class: exemptionClass,
|
|
109
111
|
detail: evidence.detail,
|
|
110
112
|
site: evidence.site,
|
|
111
|
-
|
|
113
|
+
...(evidence.whileLive === undefined ? {} : { whileLive: evidence.whileLive }),
|
|
114
|
+
};
|
|
115
|
+
if (holding.mode.production && holding.testFiles.has(evidence.site.path)) {
|
|
116
|
+
inTestFiles.push(record);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
found.push(record);
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
|
-
return exemptionsOf(found);
|
|
122
|
+
return { records: exemptionsOf(found), inTestFiles };
|
|
115
123
|
}
|
|
116
124
|
/** Two strings ordered bytewise. */
|
|
117
125
|
function compare(a, b) {
|
|
@@ -122,14 +130,14 @@ function compare(a, b) {
|
|
|
122
130
|
}
|
|
123
131
|
/**
|
|
124
132
|
* Records as a run holds them: ordered by site, then class, then declaration, and one
|
|
125
|
-
* per declaration, class and
|
|
126
|
-
* sites, or by several projects, is one record.
|
|
133
|
+
* per declaration, class, detail and file it holds while live, the one at the first
|
|
134
|
+
* site. A fact found at many sites, or by several projects, is one record.
|
|
127
135
|
*/
|
|
128
136
|
export function exemptionsOf(found) {
|
|
129
137
|
const ordered = [...found].sort((a, b) => byPosition(a.site, b.site) || compare(a.class, b.class) || compare(a.id, b.id));
|
|
130
138
|
const seen = new Set();
|
|
131
139
|
return ordered.filter((record) => {
|
|
132
|
-
const fact = [record.id, record.class, record.detail].join("\u0000");
|
|
140
|
+
const fact = [record.id, record.class, record.detail, record.whileLive ?? ""].join("\u0000");
|
|
133
141
|
if (seen.has(fact)) {
|
|
134
142
|
return false;
|
|
135
143
|
}
|
|
@@ -137,6 +145,13 @@ export function exemptionsOf(found) {
|
|
|
137
145
|
return true;
|
|
138
146
|
});
|
|
139
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The records that hold while the component files at `live` are live: every record that
|
|
150
|
+
* holds whatever is live, and each one a live file's markup holds, as a record of the run.
|
|
151
|
+
*/
|
|
152
|
+
export function holdingWhile(records, live) {
|
|
153
|
+
return exemptionsOf(records.flatMap(({ whileLive, ...record }) => whileLive === undefined || live.has(whileLive) ? [record] : []));
|
|
154
|
+
}
|
|
140
155
|
/** The records per declaration, each declaration's in the order given. */
|
|
141
156
|
function byDeclaration(records) {
|
|
142
157
|
const grouped = new Map();
|
|
@@ -152,24 +167,21 @@ function byDeclaration(records) {
|
|
|
152
167
|
return grouped;
|
|
153
168
|
}
|
|
154
169
|
/**
|
|
155
|
-
* The records that held back a declaration some configuration
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* on a declaration that a relation or a mark holds live in every configuration held
|
|
162
|
-
* nothing back. The order is the declarations' site order, and for one declaration
|
|
163
|
-
* the input's.
|
|
170
|
+
* The records that held back a declaration: one some configuration's graph, swept
|
|
171
|
+
* again with no exemption and its marks, judges a candidate, whatever another
|
|
172
|
+
* configuration answers, or one in `writeOnly`, which the run would report as written
|
|
173
|
+
* and never read without the record's read. A declaration a relation or a mark holds
|
|
174
|
+
* live in every configuration is held back by no record. The order is the
|
|
175
|
+
* declarations' site order, and for one declaration the input's.
|
|
164
176
|
*/
|
|
165
|
-
export function retainedIn(matrix, input) {
|
|
177
|
+
export function retainedIn(matrix, input, writeOnly = new Set()) {
|
|
166
178
|
const exempt = input.exempt ?? [];
|
|
167
179
|
if (exempt.length === 0) {
|
|
168
180
|
return [];
|
|
169
181
|
}
|
|
170
182
|
const bare = { marked: input.marked, mode: input.mode };
|
|
171
183
|
const dead = new Set(matrix.graphs.flatMap((graph) => sweep(graph, bare).candidates.map((candidate) => candidate.id)));
|
|
172
|
-
const held = byDeclaration(exempt.filter((record) => dead.has(record.id)));
|
|
184
|
+
const held = byDeclaration(exempt.filter((record) => dead.has(record.id) || writeOnly.has(record.id)));
|
|
173
185
|
return matrix.union.symbols.flatMap((symbol) => held.get(symbol.id) ?? []);
|
|
174
186
|
}
|
|
175
187
|
/**
|