@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
|
@@ -1,66 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runProjects } from "../analysis.js";
|
|
2
2
|
import { partialNotes } from "../partial.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { EXIT_CLEAN, EXIT_FAILURE } from "./verb.js";
|
|
3
|
+
import { resolvePath } from "../paths.js";
|
|
4
|
+
import { resolveUnkinded } from "../resolve.js";
|
|
5
|
+
import { EXIT_CLEAN } from "./verb.js";
|
|
7
6
|
/**
|
|
8
|
-
* Writes the name of every project one run analyzes, one per line
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
7
|
+
* Writes the name of every project one run analyzes, one per line: the identifier the
|
|
8
|
+
* build matrix declares for it, or, where discovery derived the project, its
|
|
9
|
+
* configuration file's path below the target root. The projects are read as the
|
|
10
|
+
* analysis reads them, so a project the analysis drops is named on the error stream
|
|
11
|
+
* instead, and one that fails the analysis fails this command the same way.
|
|
13
12
|
*/
|
|
14
13
|
export const printProjectsVerb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
15
|
-
const
|
|
14
|
+
const config = resolveUnkinded(inputs());
|
|
16
15
|
const scoped = scope();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
|
|
20
|
-
const root = resolvePath(host.workingDirectory(), scoped.target.path);
|
|
21
|
-
const notBuilt = [...discovered.notBuilt];
|
|
22
|
-
const failures = [];
|
|
23
|
-
const session = runSession(engine, discovered.configFiles, (project) => {
|
|
24
|
-
const id = ids.get(project.configFile) ?? project.configFile;
|
|
25
|
-
const errors = projectErrors(diagnosticsOf(project));
|
|
26
|
-
const [unloaded] = errors.load;
|
|
27
|
-
if (unloaded !== undefined && isGuess(discovered, project.configFile)) {
|
|
28
|
-
const error = renderDiagnostic(unloaded, (path) => relativePath(root, path) ?? path);
|
|
29
|
-
notBuilt.push({ id, configFile: project.configFile, error: targetRelative(error, root) });
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
failures.push(...[...errors.load, ...errors.check].map((one) => renderDiagnostic(one)));
|
|
33
|
-
return id;
|
|
34
|
-
}, discovered.derived ? "record" : "refuse");
|
|
35
|
-
for (const configFile of session.unopened) {
|
|
36
|
-
if (!isGuess(discovered, configFile)) {
|
|
37
|
-
throw new DiscoveryError(`${configFile} opened no project`, []);
|
|
38
|
-
}
|
|
39
|
-
notBuilt.push({
|
|
40
|
-
id: ids.get(configFile) ?? configFile,
|
|
41
|
-
configFile,
|
|
42
|
-
error: "the compiler opened no project for the configuration",
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
if (failures.length > 0) {
|
|
46
|
-
for (const line of failures) {
|
|
47
|
-
err.write(`${line}\n`);
|
|
48
|
-
}
|
|
49
|
-
err.write(`deadset-ts: ${String(failures.length)} error(s), so no answer was produced\n`);
|
|
50
|
-
return EXIT_FAILURE;
|
|
51
|
-
}
|
|
52
|
-
const projects = session.projects.filter((id) => id !== undefined);
|
|
53
|
-
if (projects.length === 0 && notBuilt.length > 0) {
|
|
54
|
-
for (const note of partialNotes({ notBuilt, unanswered: [] })) {
|
|
55
|
-
err.write(`deadset-ts: ${note}\n`);
|
|
56
|
-
}
|
|
57
|
-
err.write("deadset-ts: no configuration discovery derived could be built\n");
|
|
58
|
-
return EXIT_FAILURE;
|
|
59
|
-
}
|
|
60
|
-
for (const id of projects) {
|
|
16
|
+
const answer = runProjects(openClient(config), host, scoped, config);
|
|
17
|
+
for (const id of answer.configurations) {
|
|
61
18
|
out.write(`${id}\n`);
|
|
62
19
|
}
|
|
63
|
-
for (const note of partialNotes({
|
|
20
|
+
for (const note of partialNotes({
|
|
21
|
+
notBuilt: answer.notBuilt,
|
|
22
|
+
unanswered: answer.unanswered,
|
|
23
|
+
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
24
|
+
})) {
|
|
64
25
|
err.write(`deadset-ts: ${note}\n`);
|
|
65
26
|
}
|
|
66
27
|
return EXIT_CLEAN;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { runSweep } from "../analysis.js";
|
|
2
2
|
import { retainedLines } from "../exempt.js";
|
|
3
3
|
import { partialNotes } from "../partial.js";
|
|
4
|
+
import { resolvePath } from "../paths.js";
|
|
4
5
|
import { resolve } from "../resolve.js";
|
|
5
6
|
import { EXIT_CLEAN } from "./verb.js";
|
|
6
7
|
/**
|
|
@@ -13,14 +14,18 @@ import { EXIT_CLEAN } from "./verb.js";
|
|
|
13
14
|
export const printRetainedVerb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
14
15
|
const { config } = resolve(inputs());
|
|
15
16
|
const scoped = scope();
|
|
16
|
-
const swept = runSweep(openClient(
|
|
17
|
+
const swept = runSweep(openClient(config), host, scoped, config, {
|
|
17
18
|
marked: [],
|
|
18
19
|
mode: { production: true },
|
|
19
20
|
});
|
|
20
21
|
for (const line of retainedLines(swept.matrix.union.symbols, swept.retained)) {
|
|
21
22
|
out.write(`${line}\n`);
|
|
22
23
|
}
|
|
23
|
-
for (const note of partialNotes({
|
|
24
|
+
for (const note of partialNotes({
|
|
25
|
+
notBuilt: swept.notBuilt,
|
|
26
|
+
unanswered: swept.unanswered,
|
|
27
|
+
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
28
|
+
})) {
|
|
24
29
|
err.write(`deadset-ts: ${note}\n`);
|
|
25
30
|
}
|
|
26
31
|
return EXIT_CLEAN;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runRoots } from "../analysis.js";
|
|
2
2
|
import { partialNotes } from "../partial.js";
|
|
3
|
+
import { resolvePath } from "../paths.js";
|
|
3
4
|
import { resolve } from "../resolve.js";
|
|
4
5
|
import { EXIT_CLEAN, EXIT_FINDINGS } from "./verb.js";
|
|
5
6
|
/**
|
|
@@ -25,7 +26,7 @@ function rootLine(root, several) {
|
|
|
25
26
|
export const printRootsVerb = ({ out, err, host, inputs, scope, openClient }) => {
|
|
26
27
|
const { config, provenance } = resolve(inputs());
|
|
27
28
|
const scoped = scope();
|
|
28
|
-
const answer = runRoots(openClient(
|
|
29
|
+
const answer = runRoots(openClient(config), host, scoped, config, provenance);
|
|
29
30
|
const several = answer.configurations.length > 1;
|
|
30
31
|
for (const root of answer.roots) {
|
|
31
32
|
out.write(`${rootLine(root, several)}\n`);
|
|
@@ -33,7 +34,11 @@ export const printRootsVerb = ({ out, err, host, inputs, scope, openClient }) =>
|
|
|
33
34
|
for (const finding of answer.findings) {
|
|
34
35
|
err.write(`${finding.code}: roots.patterns names nothing: ${finding.symbol.ref}\n`);
|
|
35
36
|
}
|
|
36
|
-
for (const note of partialNotes({
|
|
37
|
+
for (const note of partialNotes({
|
|
38
|
+
notBuilt: answer.notBuilt,
|
|
39
|
+
unanswered: answer.unanswered,
|
|
40
|
+
targetRoot: resolvePath(host.workingDirectory(), scoped.target.path),
|
|
41
|
+
})) {
|
|
37
42
|
err.write(`deadset-ts: ${note}\n`);
|
|
38
43
|
}
|
|
39
44
|
return answer.findings.length > 0 ? EXIT_FINDINGS : EXIT_CLEAN;
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a version satisfies a dependency range, under the grammar npm, pnpm and yarn
|
|
3
|
+
* share: `||` alternatives of hyphen ranges or comparator runs, `x` and missing parts
|
|
4
|
+
* standing for any value, and a prerelease admitted only where a comparator names one
|
|
5
|
+
* of the same version. A range this grammar does not read is satisfied by nothing.
|
|
6
|
+
*/
|
|
7
|
+
const NUMBER = /^(?:0|[1-9]\d*)$/u;
|
|
8
|
+
const IDENTIFIER = /^[0-9A-Za-z-]+$/u;
|
|
9
|
+
const WILDCARD = new Set(["x", "X", "*"]);
|
|
10
|
+
/** The prerelease identifiers after a hyphen, or `undefined` where one is malformed. */
|
|
11
|
+
function prereleaseOf(text) {
|
|
12
|
+
if (text === undefined) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const parts = text.split(".");
|
|
16
|
+
return parts.every((part) => IDENTIFIER.test(part)) ? parts : undefined;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* One version as a range writes it, a leading `v` or `=` allowed, or `undefined`
|
|
20
|
+
* where it is not one.
|
|
21
|
+
*/
|
|
22
|
+
function partialOf(text) {
|
|
23
|
+
const match = /^[v=]?([^.+-]+)(?:\.([^.+-]+)(?:\.([^.+-]+)(?:-([^+]+))?)?)?(?:\+[0-9A-Za-z.-]+)?$/u.exec(text);
|
|
24
|
+
if (match === null) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const parts = [];
|
|
28
|
+
let wild = false;
|
|
29
|
+
for (const part of [match[1], match[2], match[3]]) {
|
|
30
|
+
if (part === undefined || WILDCARD.has(part)) {
|
|
31
|
+
wild = true;
|
|
32
|
+
parts.push(undefined);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (wild || !NUMBER.test(part)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
parts.push(Number(part));
|
|
39
|
+
}
|
|
40
|
+
const prerelease = prereleaseOf(match[4]);
|
|
41
|
+
if (prerelease === undefined || (prerelease.length > 0 && wild)) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
return { major: parts[0], minor: parts[1], patch: parts[2], prerelease };
|
|
45
|
+
}
|
|
46
|
+
/** One exact version, or `undefined` where the text is not one. */
|
|
47
|
+
function versionOf(text) {
|
|
48
|
+
const partial = partialOf(text.trim());
|
|
49
|
+
if (partial?.major === undefined || partial.minor === undefined || partial.patch === undefined) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
major: partial.major,
|
|
54
|
+
minor: partial.minor,
|
|
55
|
+
patch: partial.patch,
|
|
56
|
+
prerelease: partial.prerelease,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function exact(major, minor, patch, prerelease = []) {
|
|
60
|
+
return { major, minor, patch, prerelease };
|
|
61
|
+
}
|
|
62
|
+
/** The lowest prerelease of one version, which bounds a range from above. */
|
|
63
|
+
function floorOf(major, minor, patch) {
|
|
64
|
+
return exact(major, minor, patch, ["0"]);
|
|
65
|
+
}
|
|
66
|
+
function compareIdentifiers(a, b) {
|
|
67
|
+
const numericA = NUMBER.test(a);
|
|
68
|
+
const numericB = NUMBER.test(b);
|
|
69
|
+
if (numericA && numericB) {
|
|
70
|
+
return Math.sign(Number(a) - Number(b));
|
|
71
|
+
}
|
|
72
|
+
if (numericA !== numericB) {
|
|
73
|
+
return numericA ? -1 : 1;
|
|
74
|
+
}
|
|
75
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
76
|
+
}
|
|
77
|
+
/** Two versions ordered by precedence. */
|
|
78
|
+
function compare(a, b) {
|
|
79
|
+
const core = Math.sign(a.major - b.major) || Math.sign(a.minor - b.minor) || Math.sign(a.patch - b.patch);
|
|
80
|
+
if (core !== 0) {
|
|
81
|
+
return core;
|
|
82
|
+
}
|
|
83
|
+
if (a.prerelease.length === 0 || b.prerelease.length === 0) {
|
|
84
|
+
return Math.sign(b.prerelease.length - a.prerelease.length);
|
|
85
|
+
}
|
|
86
|
+
for (let at = 0; at < Math.max(a.prerelease.length, b.prerelease.length); at += 1) {
|
|
87
|
+
const left = a.prerelease[at];
|
|
88
|
+
const right = b.prerelease[at];
|
|
89
|
+
if (left === undefined || right === undefined) {
|
|
90
|
+
return left === undefined ? -1 : 1;
|
|
91
|
+
}
|
|
92
|
+
const order = compareIdentifiers(left, right);
|
|
93
|
+
if (order !== 0) {
|
|
94
|
+
return order;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
function passes(version, comparator) {
|
|
100
|
+
const order = compare(version, comparator.version);
|
|
101
|
+
switch (comparator.operator) {
|
|
102
|
+
case "<":
|
|
103
|
+
return order < 0;
|
|
104
|
+
case "<=":
|
|
105
|
+
return order <= 0;
|
|
106
|
+
case ">":
|
|
107
|
+
return order > 0;
|
|
108
|
+
case ">=":
|
|
109
|
+
return order >= 0;
|
|
110
|
+
case "=":
|
|
111
|
+
return order === 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** The comparators a version with missing parts stands for: any value of each. */
|
|
115
|
+
function anyOf(partial) {
|
|
116
|
+
const { major, minor } = partial;
|
|
117
|
+
if (major === undefined) {
|
|
118
|
+
return [{ operator: ">=", version: exact(0, 0, 0) }];
|
|
119
|
+
}
|
|
120
|
+
if (minor === undefined) {
|
|
121
|
+
return [
|
|
122
|
+
{ operator: ">=", version: exact(major, 0, 0) },
|
|
123
|
+
{ operator: "<", version: floorOf(major + 1, 0, 0) },
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
if (partial.patch === undefined) {
|
|
127
|
+
return [
|
|
128
|
+
{ operator: ">=", version: exact(major, minor, 0) },
|
|
129
|
+
{ operator: "<", version: floorOf(major, minor + 1, 0) },
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
return [{ operator: "=", version: exact(major, minor, partial.patch, partial.prerelease) }];
|
|
133
|
+
}
|
|
134
|
+
/** The lowest version a partial version stands for. */
|
|
135
|
+
function lowest(partial) {
|
|
136
|
+
return exact(partial.major ?? 0, partial.minor ?? 0, partial.patch ?? 0, partial.prerelease);
|
|
137
|
+
}
|
|
138
|
+
function tilde(partial) {
|
|
139
|
+
const { major, minor } = partial;
|
|
140
|
+
if (major === undefined || minor === undefined) {
|
|
141
|
+
return anyOf(partial);
|
|
142
|
+
}
|
|
143
|
+
return [
|
|
144
|
+
{ operator: ">=", version: lowest(partial) },
|
|
145
|
+
{ operator: "<", version: floorOf(major, minor + 1, 0) },
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
function caret(partial) {
|
|
149
|
+
const { major, minor, patch } = partial;
|
|
150
|
+
if (major === undefined) {
|
|
151
|
+
return anyOf(partial);
|
|
152
|
+
}
|
|
153
|
+
const from = { operator: ">=", version: lowest(partial) };
|
|
154
|
+
if (major > 0 || minor === undefined) {
|
|
155
|
+
return [from, { operator: "<", version: floorOf(major + 1, 0, 0) }];
|
|
156
|
+
}
|
|
157
|
+
if (minor > 0 || patch === undefined) {
|
|
158
|
+
return [from, { operator: "<", version: floorOf(0, minor + 1, 0) }];
|
|
159
|
+
}
|
|
160
|
+
return [from, { operator: "<", version: floorOf(0, 0, patch + 1) }];
|
|
161
|
+
}
|
|
162
|
+
/** The comparators one primitive comparison stands for, a partial version filled in. */
|
|
163
|
+
function primitive(operator, partial) {
|
|
164
|
+
const { major, minor, patch } = partial;
|
|
165
|
+
if (major === undefined) {
|
|
166
|
+
return operator === "<" || operator === ">"
|
|
167
|
+
? [{ operator: "<", version: floorOf(0, 0, 0) }]
|
|
168
|
+
: [{ operator: ">=", version: exact(0, 0, 0) }];
|
|
169
|
+
}
|
|
170
|
+
if (minor !== undefined && patch !== undefined) {
|
|
171
|
+
return [{ operator, version: lowest(partial) }];
|
|
172
|
+
}
|
|
173
|
+
const next = minor === undefined ? exact(major + 1, 0, 0) : exact(major, minor + 1, 0);
|
|
174
|
+
switch (operator) {
|
|
175
|
+
case ">":
|
|
176
|
+
return [{ operator: ">=", version: next }];
|
|
177
|
+
case ">=":
|
|
178
|
+
return [{ operator: ">=", version: lowest(partial) }];
|
|
179
|
+
case "<":
|
|
180
|
+
return [{ operator: "<", version: floorOf(major, minor ?? 0, 0) }];
|
|
181
|
+
case "<=":
|
|
182
|
+
return [{ operator: "<", version: floorOf(next.major, next.minor, 0) }];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** The comparators one written comparator stands for, or `undefined` where it is none. */
|
|
186
|
+
function comparatorsOf(token) {
|
|
187
|
+
const match = /^(<=|>=|<|>|=|~>|~|\^)?(.*)$/u.exec(token);
|
|
188
|
+
const operator = match?.[1] ?? "";
|
|
189
|
+
const partial = partialOf(match?.[2] ?? "");
|
|
190
|
+
if (partial === undefined) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
switch (operator) {
|
|
194
|
+
case "~":
|
|
195
|
+
case "~>":
|
|
196
|
+
return tilde(partial);
|
|
197
|
+
case "^":
|
|
198
|
+
return caret(partial);
|
|
199
|
+
case "<":
|
|
200
|
+
case "<=":
|
|
201
|
+
case ">":
|
|
202
|
+
case ">=":
|
|
203
|
+
return primitive(operator, partial);
|
|
204
|
+
default:
|
|
205
|
+
return anyOf(partial);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** The comparators of one alternative, or `undefined` where it does not read. */
|
|
209
|
+
function alternativeOf(text) {
|
|
210
|
+
const spaced = text.trim().replaceAll(/(<=|>=|<|>|=|~>|~|\^)\s+/gu, "$1");
|
|
211
|
+
if (spaced === "") {
|
|
212
|
+
return anyOf({ prerelease: [] });
|
|
213
|
+
}
|
|
214
|
+
const hyphen = /^(\S+)\s+-\s+(\S+)$/u.exec(spaced);
|
|
215
|
+
if (hyphen !== null) {
|
|
216
|
+
const from = partialOf(hyphen[1] ?? "");
|
|
217
|
+
const to = partialOf(hyphen[2] ?? "");
|
|
218
|
+
if (from === undefined || to === undefined) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
return [...primitive(">=", from), ...(to.major === undefined ? [] : primitive("<=", to))];
|
|
222
|
+
}
|
|
223
|
+
const found = [];
|
|
224
|
+
for (const token of spaced.split(/\s+/u)) {
|
|
225
|
+
const comparators = comparatorsOf(token);
|
|
226
|
+
if (comparators === undefined) {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
found.push(...comparators);
|
|
230
|
+
}
|
|
231
|
+
return found;
|
|
232
|
+
}
|
|
233
|
+
/** Whether one alternative admits one version, its prerelease rule included. */
|
|
234
|
+
function admits(alternative, version) {
|
|
235
|
+
if (!alternative.every((comparator) => passes(version, comparator))) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (version.prerelease.length === 0) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
return alternative.some(({ version: bound }) => bound.prerelease.length > 0 &&
|
|
242
|
+
bound.major === version.major &&
|
|
243
|
+
bound.minor === version.minor &&
|
|
244
|
+
bound.patch === version.patch);
|
|
245
|
+
}
|
|
246
|
+
/** Whether `version` satisfies `range`; `false` where either does not read. */
|
|
247
|
+
export function satisfies(version, range) {
|
|
248
|
+
const parsed = versionOf(version);
|
|
249
|
+
if (parsed === undefined) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const alternatives = range.split("||").map(alternativeOf);
|
|
253
|
+
return (alternatives.every((alternative) => alternative !== undefined) &&
|
|
254
|
+
alternatives.some((alternative) => admits(alternative, parsed)));
|
|
255
|
+
}
|
package/dist/src/version.js
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The entry points the manifests of a workspace name, read back to the members'
|
|
3
|
+
* sources. Each member's manifest is read the way the target's own is, and each target
|
|
4
|
+
* is read back to the source file its member's configurations compile it from. A
|
|
5
|
+
* member whose manifest forbids publishing it exports nothing anyone outside the
|
|
6
|
+
* workspace imports, so only its commands are entry points.
|
|
7
|
+
*/
|
|
8
|
+
import { emittedFrom } from "./emit-map.js";
|
|
9
|
+
import { readManifest } from "./manifest.js";
|
|
10
|
+
import { joinPath, relativePath } from "./paths.js";
|
|
11
|
+
/** The one character a subpath pattern of a manifest's `exports` holds. */
|
|
12
|
+
const SUBPATH_WILDCARD = "*";
|
|
13
|
+
/** The paths one written target stands for in its member's sources, itself included. */
|
|
14
|
+
function sourcesOf(resolver, member, path) {
|
|
15
|
+
if (!path.includes(SUBPATH_WILDCARD)) {
|
|
16
|
+
const source = resolver.sourceOf(member, path);
|
|
17
|
+
return source === undefined ? [path] : [source];
|
|
18
|
+
}
|
|
19
|
+
const patterns = member.configurations.flatMap(({ layout }) => layout === undefined ? [] : emittedFrom(path, layout));
|
|
20
|
+
return [...new Set([path, ...patterns])];
|
|
21
|
+
}
|
|
22
|
+
/** One manifest's entries with every target read back to its member's sources. */
|
|
23
|
+
function sourceEntries(resolver, member, entries, spelled) {
|
|
24
|
+
return entries.flatMap((entry) => sourcesOf(resolver, member, entry.path).map((path) => ({
|
|
25
|
+
...entry,
|
|
26
|
+
member: spelled(entry),
|
|
27
|
+
path,
|
|
28
|
+
})));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The target's own manifest with its targets read back to the sources of the member
|
|
32
|
+
* the target is, where it is one.
|
|
33
|
+
*/
|
|
34
|
+
export function targetManifestIn(resolver, targetRoot, manifest) {
|
|
35
|
+
const member = resolver.workspace.members.find((one) => one.dir === targetRoot);
|
|
36
|
+
return member === undefined
|
|
37
|
+
? manifest
|
|
38
|
+
: {
|
|
39
|
+
...manifest,
|
|
40
|
+
entries: sourceEntries(resolver, member, manifest.entries, (entry) => entry.member),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The entry points every other member's manifest names, each spelled as the member
|
|
45
|
+
* that named it under the manifest's path below the target root, a private member's
|
|
46
|
+
* `exports` left out: only the workspace imports a private package, and the workspace
|
|
47
|
+
* is analyzed. An entry is published where its member is not private and the member
|
|
48
|
+
* named in its own manifest is one a consumer imports.
|
|
49
|
+
*/
|
|
50
|
+
export function memberEntries(host, resolver, targetRoot) {
|
|
51
|
+
return resolver.workspace.members.flatMap((member) => {
|
|
52
|
+
if (member.dir === targetRoot) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const manifest = readManifest(host, member.dir);
|
|
56
|
+
const where = relativePath(targetRoot, joinPath(member.dir, "package.json")) ??
|
|
57
|
+
joinPath(member.dir, "package.json");
|
|
58
|
+
const kept = manifest.entries.flatMap((entry) => {
|
|
59
|
+
const exported = entry.member.startsWith("exports");
|
|
60
|
+
if (member.private && exported) {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
const published = !member.private && entry.published && (manifest.declaresExports ? exported : true);
|
|
64
|
+
return [{ ...entry, published }];
|
|
65
|
+
});
|
|
66
|
+
return sourceEntries(resolver, member, kept, (entry) => `${where} ${entry.member}`);
|
|
67
|
+
});
|
|
68
|
+
}
|