node-dep-resolver 1.0.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/bin/cli.js +55 -0
- package/package.json +39 -0
- package/src/base/candidate/CandidateClass.js +20 -0
- package/src/base/candidate/methods/generateCandidate.js +73 -0
- package/src/base/conflicts/ConflictClass.js +12 -0
- package/src/base/constraints/ConstraintClass.js +27 -0
- package/src/base/constraints/methods/addConstraint.js +69 -0
- package/src/base/dependencies/DependencyClass.js +10 -0
- package/src/base/graph/GraphClass.js +20 -0
- package/src/base/graph/method/buildGraph.js +80 -0
- package/src/base/graph/method/printGraph.js +33 -0
- package/src/base/registry/RegistryClass.js +33 -0
- package/src/base/registry/methods/getPackageMetadata.js +18 -0
- package/src/base/registry/methods/getPackageVersion.js +13 -0
- package/src/base/registry/methods/getVersions.js +5 -0
- package/src/base/registry/methods/resolveVersion.js +17 -0
- package/src/base/resolver/ResolverClass.js +87 -0
- package/src/base/resolver/methods/addConstraint.js +12 -0
- package/src/base/resolver/methods/buildInitialState.js +11 -0
- package/src/base/resolver/methods/checkConflict.js +20 -0
- package/src/base/resolver/methods/createSnapshot.js +12 -0
- package/src/base/resolver/methods/getFirstConflict.js +10 -0
- package/src/base/resolver/methods/getValidVersions.js +9 -0
- package/src/base/resolver/methods/resolve.js +13 -0
- package/src/base/resolver/methods/restoreSnapshot.js +10 -0
- package/src/base/resolver/methods/search.js +39 -0
- package/src/base/resolver/methods/selectPackage.js +19 -0
- package/src/index.js +19 -0
- package/src/rudimentary/candidates.js +121 -0
- package/src/rudimentary/canditate.js +121 -0
- package/src/rudimentary/constraint.js +73 -0
- package/src/rudimentary/graph.js +124 -0
- package/src/rudimentary/registry.js +70 -0
- package/src/rudimentary/resolver.js +361 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import Graph from "../src/base/graph/GraphClass.js";
|
|
6
|
+
import Resolver from "../src/base/resolver/ResolverClass.js";
|
|
7
|
+
|
|
8
|
+
async function runCli() {
|
|
9
|
+
const targetPath = process.argv[2] || "./package.json";
|
|
10
|
+
const absolutePath = path.resolve(process.cwd(), targetPath);
|
|
11
|
+
|
|
12
|
+
if (!fs.existsSync(absolutePath)) {
|
|
13
|
+
console.error(`[ERROR] File not found at '${absolutePath}'`);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
console.log(`\n[INFO] Analyzing dependencies from '${targetPath}'...\n`);
|
|
18
|
+
|
|
19
|
+
let packageJson;
|
|
20
|
+
try {
|
|
21
|
+
packageJson = JSON.parse(fs.readFileSync(absolutePath, "utf-8"));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error(`[ERROR] Failed to parse JSON in '${targetPath}'`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const dependencies = {
|
|
28
|
+
...(packageJson.dependencies || {})
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (Object.keys(dependencies).length === 0) {
|
|
32
|
+
console.log("[INFO] No dependencies found in package.json.");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log("--- Building Dependency Graph ---");
|
|
37
|
+
const graphObj = new Graph();
|
|
38
|
+
const graph = await graphObj.buildGraph(dependencies);
|
|
39
|
+
graphObj.printGraph(graph);
|
|
40
|
+
|
|
41
|
+
console.log("\n--- Resolving Conflicts ---");
|
|
42
|
+
const resolver = new Resolver(dependencies);
|
|
43
|
+
const solution = await resolver.resolve();
|
|
44
|
+
|
|
45
|
+
console.log("\n========== RESOLVED DEPENDENCY SOLUTION ==========\n");
|
|
46
|
+
for (const [name, packageData] of solution) {
|
|
47
|
+
console.log(` [OK] ${name}@${packageData.version}`);
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
runCli().catch((err) => {
|
|
53
|
+
console.error("[ERROR] Fatal Error:", err.message);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-dep-resolver",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight CLI tool & library to build dependency graphs and resolve semver conflicts.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dep-resolver": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"bin"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "node --test test/*.js",
|
|
19
|
+
"start": "node bin/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/unknownsideofme/dependency-resolver.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"cli",
|
|
27
|
+
"dependency-resolver",
|
|
28
|
+
"semver",
|
|
29
|
+
"dependency-graph",
|
|
30
|
+
"conflict-resolution",
|
|
31
|
+
"backtracking"
|
|
32
|
+
],
|
|
33
|
+
"author": "debanjan",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"axios": "^1.7.0",
|
|
37
|
+
"semver": "^7.6.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Conflict from "../conflicts/ConflictClass.js";
|
|
2
|
+
import Registry from "../registry/RegistryClass.js";
|
|
3
|
+
import Dependency from "../dependencies/DependencyClass.js";
|
|
4
|
+
import generateCandidates from "./methods/generateCandidate.js";
|
|
5
|
+
|
|
6
|
+
export default class Candidates {
|
|
7
|
+
constructor(registry) {
|
|
8
|
+
this.conflicts = new Conflict();
|
|
9
|
+
this.registry = registry || new Registry(new Dependency());
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getConflicts() {
|
|
13
|
+
if (this.conflicts === null) {
|
|
14
|
+
throw new Error("Conflict not initialized");
|
|
15
|
+
}
|
|
16
|
+
return this.conflicts;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Candidates.prototype.generateCandidates = generateCandidates;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
|
|
3
|
+
export default async function generateCandidates(conflict) {
|
|
4
|
+
const candidates = [];
|
|
5
|
+
const packageName = conflict.packageName;
|
|
6
|
+
const registry = this.registry;
|
|
7
|
+
|
|
8
|
+
for (const requirement of conflict.constraints) {
|
|
9
|
+
const requester = requirement.requester;
|
|
10
|
+
const requesterParts = requester.split("@");
|
|
11
|
+
const requesterName = requesterParts[0];
|
|
12
|
+
const currentVersion = requesterParts.slice(1).join("@");
|
|
13
|
+
|
|
14
|
+
console.log(`\nLooking for alternatives for ${requester}`);
|
|
15
|
+
|
|
16
|
+
const versions = await registry.getVersions(requesterName);
|
|
17
|
+
|
|
18
|
+
for (const version of versions) {
|
|
19
|
+
if (version === currentVersion) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const packageData = await registry.getPackageVersion(
|
|
24
|
+
requesterName,
|
|
25
|
+
version
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const dependencies = packageData.dependencies || {};
|
|
29
|
+
|
|
30
|
+
if (!dependencies[packageName]) {
|
|
31
|
+
candidates.push({
|
|
32
|
+
packageName: requesterName,
|
|
33
|
+
oldVersion: currentVersion,
|
|
34
|
+
newVersion: version,
|
|
35
|
+
reason: `No longer depends on ${packageName}`
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const newRange = dependencies[packageName];
|
|
42
|
+
let compatible = true;
|
|
43
|
+
|
|
44
|
+
for (const other of conflict.constraints) {
|
|
45
|
+
if (other.requester === requester) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!semver.intersects(newRange, other.range)) {
|
|
50
|
+
compatible = false;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (compatible) {
|
|
56
|
+
candidates.push({
|
|
57
|
+
packageName: requesterName,
|
|
58
|
+
oldVersion: currentVersion,
|
|
59
|
+
newVersion: version,
|
|
60
|
+
reason:
|
|
61
|
+
`${requesterName}@${version} requires ` +
|
|
62
|
+
`${packageName} ${newRange}`
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
candidates.sort((a, b) => {
|
|
69
|
+
return semver.compare(a.newVersion, a.oldVersion);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return candidates;
|
|
73
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Conflict from "../conflicts/ConflictClass.js";
|
|
2
|
+
import { addConstraint } from "./methods/addConstraint.js";
|
|
3
|
+
export default class Constraint {
|
|
4
|
+
static #constraints = null;
|
|
5
|
+
static #conflicts = null ;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.constraints = new Map() ;
|
|
9
|
+
this.conflicts = new Conflict() ;
|
|
10
|
+
|
|
11
|
+
}
|
|
12
|
+
getConstraints(){
|
|
13
|
+
if( this.constraints === null){
|
|
14
|
+
throw new Error( "Conflicts not initialized") ;
|
|
15
|
+
}
|
|
16
|
+
return this.constraints ;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getConflicts(){
|
|
20
|
+
if(this.conflicts === null ){
|
|
21
|
+
throw new Error( "Conflicts not initialized") ;
|
|
22
|
+
}
|
|
23
|
+
return this.conflicts ;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
}
|
|
27
|
+
Constraint.prototype.addConstraint = addConstraint ;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
|
|
3
|
+
export function addConstraint(
|
|
4
|
+
dependencyName,
|
|
5
|
+
dependencyRange,
|
|
6
|
+
requestedBy
|
|
7
|
+
) {
|
|
8
|
+
if(dependencyName === null || dependencyName == ""){
|
|
9
|
+
throw new Error("Dependency name cannot be null or empty");
|
|
10
|
+
}
|
|
11
|
+
if( dependencyRange === null || dependencyRange == ""){
|
|
12
|
+
throw new Error("Dependency range cannot be null or empty");
|
|
13
|
+
}
|
|
14
|
+
if( requestedBy === null || requestedBy == ""){
|
|
15
|
+
throw new Error("Requested by cannot be null or empty");
|
|
16
|
+
}
|
|
17
|
+
let constraints = this.getConstraints() ;
|
|
18
|
+
let conflicts = this.getConflicts() ;
|
|
19
|
+
if (!constraints.has(dependencyName)) {
|
|
20
|
+
|
|
21
|
+
constraints.set(
|
|
22
|
+
dependencyName,
|
|
23
|
+
{
|
|
24
|
+
constraints: [],
|
|
25
|
+
conflict: false
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const state = constraints.get(dependencyName);
|
|
31
|
+
|
|
32
|
+
// Avoid duplicate constraints
|
|
33
|
+
const alreadyExists = state.constraints.some(
|
|
34
|
+
item =>
|
|
35
|
+
item.requester === requestedBy &&
|
|
36
|
+
item.range === dependencyRange
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (alreadyExists) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check against existing constraints
|
|
44
|
+
for (
|
|
45
|
+
const existing
|
|
46
|
+
of state.constraints
|
|
47
|
+
) {
|
|
48
|
+
|
|
49
|
+
const compatible =
|
|
50
|
+
semver.intersects(
|
|
51
|
+
existing.range,
|
|
52
|
+
dependencyRange
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!compatible) {
|
|
56
|
+
|
|
57
|
+
state.conflict = true;
|
|
58
|
+
|
|
59
|
+
conflicts.add(
|
|
60
|
+
dependencyName
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
state.constraints.push({
|
|
66
|
+
requester: requestedBy,
|
|
67
|
+
range: dependencyRange
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import buildGraph from "./method/buildGraph.js";
|
|
2
|
+
import printGraph from "./method/printGraph.js";
|
|
3
|
+
|
|
4
|
+
export default class Graph {
|
|
5
|
+
static graphMap = null;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
if (Graph.graphMap === null) {
|
|
9
|
+
Graph.graphMap = new Map();
|
|
10
|
+
}
|
|
11
|
+
this.graphMap = Graph.graphMap;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getGraphMap() {
|
|
15
|
+
return this.graphMap;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
Graph.prototype.buildGraph = buildGraph;
|
|
20
|
+
Graph.prototype.printGraph = printGraph;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveVersion,
|
|
3
|
+
getPackageVersion
|
|
4
|
+
} from "../../../rudimentary/registry.js";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
addConstraint
|
|
8
|
+
} from "../../../rudimentary/constraint.js";
|
|
9
|
+
|
|
10
|
+
export default async function buildGraph(rootDependencies) {
|
|
11
|
+
const graph = new Map();
|
|
12
|
+
|
|
13
|
+
async function visit(
|
|
14
|
+
name,
|
|
15
|
+
range,
|
|
16
|
+
requestedBy
|
|
17
|
+
) {
|
|
18
|
+
console.log(`Resolving ${name} ${range}...`);
|
|
19
|
+
|
|
20
|
+
addConstraint(
|
|
21
|
+
name,
|
|
22
|
+
range,
|
|
23
|
+
requestedBy
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const version = await resolveVersion(
|
|
27
|
+
name,
|
|
28
|
+
range
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const key = `${name}@${version}`;
|
|
32
|
+
|
|
33
|
+
if (graph.has(key)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const packageData = await getPackageVersion(
|
|
38
|
+
name,
|
|
39
|
+
version
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const node = {
|
|
43
|
+
name,
|
|
44
|
+
version,
|
|
45
|
+
dependencies: packageData.dependencies || {}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
graph.set(key, node);
|
|
49
|
+
|
|
50
|
+
for (
|
|
51
|
+
const [
|
|
52
|
+
dependencyName,
|
|
53
|
+
dependencyRange
|
|
54
|
+
]
|
|
55
|
+
of Object.entries(node.dependencies)
|
|
56
|
+
) {
|
|
57
|
+
await visit(
|
|
58
|
+
dependencyName,
|
|
59
|
+
dependencyRange,
|
|
60
|
+
`${name}@${version}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (
|
|
66
|
+
const [
|
|
67
|
+
name,
|
|
68
|
+
range
|
|
69
|
+
]
|
|
70
|
+
of Object.entries(rootDependencies)
|
|
71
|
+
) {
|
|
72
|
+
await visit(
|
|
73
|
+
name,
|
|
74
|
+
range,
|
|
75
|
+
"ROOT"
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return graph;
|
|
80
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConstraints,
|
|
3
|
+
getConflicts
|
|
4
|
+
} from "../../../rudimentary/constraint.js";
|
|
5
|
+
|
|
6
|
+
export default function printGraph(graph) {
|
|
7
|
+
for (const [key, node] of graph) {
|
|
8
|
+
console.log(key);
|
|
9
|
+
for (const [name, range] of Object.entries(node.dependencies)) {
|
|
10
|
+
console.log(` └── ${name} ${range}`);
|
|
11
|
+
}
|
|
12
|
+
console.log();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const constraints = getConstraints();
|
|
16
|
+
const conflicts = getConflicts();
|
|
17
|
+
|
|
18
|
+
if (constraints.size > 0) {
|
|
19
|
+
console.log("========== CONSTRAINTS & CONFLICTS ==========\n");
|
|
20
|
+
for (const [depName, state] of constraints) {
|
|
21
|
+
const status = state.conflict ? "CONFLICT" : "OK";
|
|
22
|
+
console.log(`${depName} [${status}]`);
|
|
23
|
+
for (const r of state.constraints) {
|
|
24
|
+
console.log(` └── requested ${r.range} by ${r.requester}`);
|
|
25
|
+
}
|
|
26
|
+
console.log();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (conflicts.size > 0) {
|
|
30
|
+
console.log(`[WARNING] Conflicts detected in ${conflicts.size} package(s): ${Array.from(conflicts).join(", ")}\n`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import getPackageMetadata from "./methods/getPackageMetadata.js";
|
|
2
|
+
import resolveVersion from "./methods/resolveVersion.js";
|
|
3
|
+
import getPackageVersion from "./methods/getPackageVersion.js";
|
|
4
|
+
import getVersions from "./methods/getVersions.js";
|
|
5
|
+
import Dependency from "../dependencies/DependencyClass.js";
|
|
6
|
+
|
|
7
|
+
export default class Registry {
|
|
8
|
+
static #REGISTRY_URL = "https://registry.npmjs.org";
|
|
9
|
+
static #metadataCache = null;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
if (Registry.#metadataCache === null) {
|
|
13
|
+
Registry.#metadataCache = new Dependency();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getMetadataCache() {
|
|
18
|
+
return Registry.#metadataCache;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setMetadataCache(metadataCache) {
|
|
22
|
+
Registry.#metadataCache = metadataCache;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getREGISTRY_URL() {
|
|
26
|
+
return Registry.#REGISTRY_URL;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Registry.prototype.getPackageMetadata = getPackageMetadata;
|
|
31
|
+
Registry.prototype.resolveVersion = resolveVersion;
|
|
32
|
+
Registry.prototype.getPackageVersion = getPackageVersion;
|
|
33
|
+
Registry.prototype.getVersions = getVersions;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
export default async function getPackageMetadata(name) {
|
|
4
|
+
const metadataCache = this.getMetadataCache();
|
|
5
|
+
if (metadataCache && metadataCache.has(name)) {
|
|
6
|
+
return metadataCache.get(name);
|
|
7
|
+
}
|
|
8
|
+
const registryUrl = this.getREGISTRY_URL();
|
|
9
|
+
const url = `${registryUrl}/${encodeURIComponent(name)}`;
|
|
10
|
+
|
|
11
|
+
const response = await axios.get(url);
|
|
12
|
+
|
|
13
|
+
if (metadataCache) {
|
|
14
|
+
metadataCache.set(name, response.data);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return response.data;
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default async function getPackageVersion(name, version) {
|
|
2
|
+
const metadata = await this.getPackageMetadata(name);
|
|
3
|
+
|
|
4
|
+
const packageData = metadata.versions[version];
|
|
5
|
+
|
|
6
|
+
if (!packageData) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`${name}@${version} does not exist`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return packageData;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
|
|
3
|
+
export default async function resolveVersion(name, range) {
|
|
4
|
+
const metadata = await this.getPackageMetadata(name);
|
|
5
|
+
const versions = Object.keys(metadata.versions);
|
|
6
|
+
const validVersions = versions.filter(version => semver.satisfies(version, range));
|
|
7
|
+
|
|
8
|
+
if (validVersions.length === 0) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`No version of ${name} satisfies ${range}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
validVersions.sort(semver.rcompare);
|
|
15
|
+
|
|
16
|
+
return validVersions[0];
|
|
17
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import Registry from "../registry/RegistryClass.js";
|
|
2
|
+
import Candidates from "../candidate/CandidateClass.js";
|
|
3
|
+
import Constraint from "../constraints/ConstraintClass.js";
|
|
4
|
+
import Conflict from "../conflicts/ConflictClass.js";
|
|
5
|
+
|
|
6
|
+
import resolve from "./methods/resolve.js";
|
|
7
|
+
import buildInitialState from "./methods/buildInitialState.js";
|
|
8
|
+
import getValidVersions from "./methods/getValidVersions.js";
|
|
9
|
+
import selectPackage from "./methods/selectPackage.js";
|
|
10
|
+
import addConstraint from "./methods/addConstraint.js";
|
|
11
|
+
import checkConflict from "./methods/checkConflict.js";
|
|
12
|
+
import getFirstConflict from "./methods/getFirstConflict.js";
|
|
13
|
+
import search from "./methods/search.js";
|
|
14
|
+
import createSnapshot from "./methods/createSnapshot.js";
|
|
15
|
+
import restoreSnapshot from "./methods/restoreSnapshot.js";
|
|
16
|
+
|
|
17
|
+
export default class Resolver {
|
|
18
|
+
static #rootDependencies = null;
|
|
19
|
+
static #selected = null;
|
|
20
|
+
static #constraints = null;
|
|
21
|
+
static #conflicts = null;
|
|
22
|
+
static #registry = null;
|
|
23
|
+
static #candidates = null;
|
|
24
|
+
|
|
25
|
+
constructor(rootDependencies, registry, candidates, constraint) {
|
|
26
|
+
this.rootDependencies = { ...rootDependencies };
|
|
27
|
+
this.selected = new Map();
|
|
28
|
+
this.constraintObj = constraint || new Constraint();
|
|
29
|
+
this.constraints = this.constraintObj.getConstraints();
|
|
30
|
+
this.conflicts = new Conflict();
|
|
31
|
+
this.registry = registry || new Registry();
|
|
32
|
+
this.candidates = candidates || new Candidates(this.registry);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getRootDependencies() {
|
|
36
|
+
if (this.rootDependencies === null) {
|
|
37
|
+
throw new Error("RootDependencies not initialized");
|
|
38
|
+
}
|
|
39
|
+
return this.rootDependencies;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getSelected() {
|
|
43
|
+
if (this.selected === null) {
|
|
44
|
+
throw new Error("Selected not initialized");
|
|
45
|
+
}
|
|
46
|
+
return this.selected;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getConstraints() {
|
|
50
|
+
if (this.constraints === null) {
|
|
51
|
+
throw new Error("Constraints not initialized");
|
|
52
|
+
}
|
|
53
|
+
return this.constraints;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getConflicts() {
|
|
57
|
+
if (this.conflicts === null) {
|
|
58
|
+
throw new Error("Conflicts not initialized");
|
|
59
|
+
}
|
|
60
|
+
return this.conflicts;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getRegistry() {
|
|
64
|
+
if (this.registry === null) {
|
|
65
|
+
throw new Error("Registry not initialized");
|
|
66
|
+
}
|
|
67
|
+
return this.registry;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getCandidates() {
|
|
71
|
+
if (this.candidates === null) {
|
|
72
|
+
throw new Error("Candidates not initialized");
|
|
73
|
+
}
|
|
74
|
+
return this.candidates;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Resolver.prototype.resolve = resolve;
|
|
79
|
+
Resolver.prototype.buildInitialState = buildInitialState;
|
|
80
|
+
Resolver.prototype.getValidVersions = getValidVersions;
|
|
81
|
+
Resolver.prototype.selectPackage = selectPackage;
|
|
82
|
+
Resolver.prototype.addConstraint = addConstraint;
|
|
83
|
+
Resolver.prototype.checkConflict = checkConflict;
|
|
84
|
+
Resolver.prototype.getFirstConflict = getFirstConflict;
|
|
85
|
+
Resolver.prototype.search = search;
|
|
86
|
+
Resolver.prototype.createSnapshot = createSnapshot;
|
|
87
|
+
Resolver.prototype.restoreSnapshot = restoreSnapshot;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default async function buildInitialState() {
|
|
2
|
+
for (const [name, range] of Object.entries(this.rootDependencies)) {
|
|
3
|
+
const versions = await this.getValidVersions(name, range);
|
|
4
|
+
|
|
5
|
+
if (versions.length === 0) {
|
|
6
|
+
throw new Error(`No version of ${name} satisfies ${range}`);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
await this.selectPackage(name, versions[0], "ROOT");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import semver from "semver";
|
|
2
|
+
|
|
3
|
+
export default function checkConflict(name) {
|
|
4
|
+
const requirements = this.constraints.get(name);
|
|
5
|
+
|
|
6
|
+
if (!requirements) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
for (let i = 0; i < requirements.length; i++) {
|
|
11
|
+
for (let j = i + 1; j < requirements.length; j++) {
|
|
12
|
+
if (!semver.intersects(requirements[i].range, requirements[j].range)) {
|
|
13
|
+
this.conflicts.add(name);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.conflicts.delete(name);
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function createSnapshot() {
|
|
2
|
+
return {
|
|
3
|
+
selected: new Map(this.selected),
|
|
4
|
+
constraints: new Map(
|
|
5
|
+
[...this.constraints].map(([name, values]) => [
|
|
6
|
+
name,
|
|
7
|
+
values.map(value => ({ ...value }))
|
|
8
|
+
])
|
|
9
|
+
),
|
|
10
|
+
conflicts: new Set(this.conflicts)
|
|
11
|
+
};
|
|
12
|
+
}
|