submodule-version 2.3.2 → 3.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/README.md +103 -20
- package/dist/runner/handleErrors/handleErrors.d.ts +1 -1
- package/dist/runner/handleErrors/handleErrors.js +3 -10
- package/dist/runner/handleErrors/handleErrors.js.map +1 -1
- package/dist/runner/index.js +0 -0
- package/dist/runner/runner.d.ts +4 -0
- package/dist/runner/runner.js +91 -37
- package/dist/runner/runner.js.map +1 -1
- package/dist/src/PubGrub/PubGrub.d.ts +28 -0
- package/dist/src/PubGrub/PubGrub.js +366 -0
- package/dist/src/PubGrub/PubGrub.js.map +1 -0
- package/dist/src/PubGrub/index.d.ts +2 -0
- package/dist/src/PubGrub/index.js +7 -0
- package/dist/src/PubGrub/index.js.map +1 -0
- package/dist/src/PubGrub/types.d.ts +56 -0
- package/dist/src/PubGrub/types.js +3 -0
- package/dist/src/PubGrub/types.js.map +1 -0
- package/dist/src/entryStore.d.ts +16 -0
- package/dist/src/entryStore.js +152 -0
- package/dist/src/entryStore.js.map +1 -0
- package/dist/src/errors.d.ts +28 -0
- package/dist/src/errors.js +30 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/git/api/GithubError.d.ts +4 -0
- package/dist/src/git/api/GithubError.js +11 -0
- package/dist/src/git/api/GithubError.js.map +1 -0
- package/dist/src/git/api/api.d.ts +8 -0
- package/dist/src/git/api/api.js +133 -0
- package/dist/src/git/api/api.js.map +1 -0
- package/dist/src/git/api/index.d.ts +3 -0
- package/dist/src/git/api/index.js +8 -0
- package/dist/src/git/api/index.js.map +1 -0
- package/dist/src/git/git.d.ts +30 -23
- package/dist/src/git/git.js +8 -258
- package/dist/src/git/git.js.map +1 -1
- package/dist/src/git/index.d.ts +1 -0
- package/dist/src/git/index.js +3 -1
- package/dist/src/git/index.js.map +1 -1
- package/dist/src/git/local/index.d.ts +1 -0
- package/dist/src/git/local/index.js +6 -0
- package/dist/src/git/local/index.js.map +1 -0
- package/dist/src/git/local/local.d.ts +26 -0
- package/dist/src/git/local/local.js +288 -0
- package/dist/src/git/local/local.js.map +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +16 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/printError.d.ts +1 -0
- package/dist/src/printError.js +158 -0
- package/dist/src/printError.js.map +1 -0
- package/dist/src/sv.d.ts +18 -8
- package/dist/src/sv.js +235 -131
- package/dist/src/sv.js.map +1 -1
- package/dist/src/versionUtil/index.d.ts +2 -0
- package/dist/src/versionUtil/index.js +6 -0
- package/dist/src/versionUtil/index.js.map +1 -0
- package/dist/src/versionUtil/versionUtil.d.ts +16 -0
- package/dist/src/versionUtil/versionUtil.js +70 -0
- package/dist/src/versionUtil/versionUtil.js.map +1 -0
- package/package.json +4 -2
- package/runner/handleErrors/handleErrors.ts +3 -10
- package/runner/runner.ts +128 -46
- package/src/PubGrub/PubGrub.ts +578 -0
- package/src/PubGrub/__tests__/PubGrub.test.ts +351 -0
- package/src/PubGrub/index.ts +12 -0
- package/src/PubGrub/types.ts +72 -0
- package/src/__tests__/entryStore.test.ts +248 -0
- package/src/__tests__/printError.test.ts +148 -0
- package/src/__tests__/sv.test.ts +441 -0
- package/src/entryStore.ts +212 -0
- package/src/errors.ts +39 -0
- package/src/git/api/GithubError.ts +5 -0
- package/src/git/api/api.ts +180 -0
- package/src/git/api/index.ts +3 -0
- package/src/git/git.ts +8 -396
- package/src/git/index.ts +1 -0
- package/src/git/local/index.ts +1 -0
- package/src/git/local/local.ts +430 -0
- package/src/index.ts +17 -0
- package/src/printError.ts +189 -0
- package/src/sv.ts +321 -134
- package/src/versionUtil/__tests__/versionUtil.test.ts +62 -0
- package/src/versionUtil/index.ts +2 -0
- package/src/versionUtil/versionUtil.ts +103 -0
- package/tsconfig.json +1 -1
- package/dist/runner/handleErrors/handleBuildGraphError.d.ts +0 -1
- package/dist/runner/handleErrors/handleBuildGraphError.js +0 -42
- package/dist/runner/handleErrors/handleBuildGraphError.js.map +0 -1
- package/dist/runner/handleErrors/handleGitError.d.ts +0 -1
- package/dist/runner/handleErrors/handleGitError.js +0 -27
- package/dist/runner/handleErrors/handleGitError.js.map +0 -1
- package/dist/runner/handleErrors/handleInstallError.d.ts +0 -1
- package/dist/runner/handleErrors/handleInstallError.js +0 -30
- package/dist/runner/handleErrors/handleInstallError.js.map +0 -1
- package/dist/src/__tests__/versionUtil.test.d.ts +0 -1
- package/dist/src/__tests__/versionUtil.test.js +0 -80
- package/dist/src/__tests__/versionUtil.test.js.map +0 -1
- package/dist/src/buildGraph/GraphError.d.ts +0 -11
- package/dist/src/buildGraph/GraphError.js +0 -11
- package/dist/src/buildGraph/GraphError.js.map +0 -1
- package/dist/src/buildGraph/buildGraph.d.ts +0 -8
- package/dist/src/buildGraph/buildGraph.js +0 -100
- package/dist/src/buildGraph/buildGraph.js.map +0 -1
- package/dist/src/buildGraph/index.d.ts +0 -2
- package/dist/src/buildGraph/index.js +0 -8
- package/dist/src/buildGraph/index.js.map +0 -1
- package/dist/src/git/__tests__/parseGitUrl.test.d.ts +0 -1
- package/dist/src/git/__tests__/parseGitUrl.test.js +0 -30
- package/dist/src/git/__tests__/parseGitUrl.test.js.map +0 -1
- package/dist/src/versionUtil.d.ts +0 -13
- package/dist/src/versionUtil.js +0 -79
- package/dist/src/versionUtil.js.map +0 -1
- package/runner/handleErrors/handleBuildGraphError.ts +0 -49
- package/runner/handleErrors/handleGitError.ts +0 -28
- package/runner/handleErrors/handleInstallError.ts +0 -26
- package/src/__tests__/versionUtil.test.ts +0 -96
- package/src/buildGraph/GraphError.ts +0 -12
- package/src/buildGraph/buildGraph.ts +0 -150
- package/src/buildGraph/index.ts +0 -2
- package/src/versionUtil.ts +0 -97
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { entryStore } from '../entryStore';
|
|
2
|
+
import { SVError } from '../errors';
|
|
3
|
+
import { versionUtil } from '../versionUtil';
|
|
4
|
+
import {
|
|
5
|
+
ICircularConflict,
|
|
6
|
+
IEntrySource,
|
|
7
|
+
IIncompatibility,
|
|
8
|
+
IPackageEntry,
|
|
9
|
+
IRequirement,
|
|
10
|
+
ITerm,
|
|
11
|
+
IVersionConflict,
|
|
12
|
+
TConflict,
|
|
13
|
+
TDependencies,
|
|
14
|
+
TPubGrubResult,
|
|
15
|
+
} from './types';
|
|
16
|
+
|
|
17
|
+
interface ISolverState {
|
|
18
|
+
selected: Map<string, string>;
|
|
19
|
+
requirements: Map<string, IRequirement[]>;
|
|
20
|
+
edges: Map<string, string[]>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type TSearchResult = {
|
|
24
|
+
ok: true;
|
|
25
|
+
state: ISolverState;
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
conflict: TConflict;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type TTermState = 'satisfied' | 'contradicted' | 'inconclusive';
|
|
32
|
+
|
|
33
|
+
const ROOT = '<root>';
|
|
34
|
+
const ROOT_VERSION = '1.0.0';
|
|
35
|
+
|
|
36
|
+
const cloneState = (state: ISolverState): ISolverState => ({
|
|
37
|
+
selected: new Map(state.selected),
|
|
38
|
+
requirements: new Map(
|
|
39
|
+
[...state.requirements].map(([name, requirements]) => [
|
|
40
|
+
name,
|
|
41
|
+
requirements.map(requirement => ({
|
|
42
|
+
...requirement,
|
|
43
|
+
chain: [...requirement.chain],
|
|
44
|
+
})),
|
|
45
|
+
]),
|
|
46
|
+
),
|
|
47
|
+
edges: new Map(
|
|
48
|
+
[...state.edges].map(([name, dependencies]) => [
|
|
49
|
+
name,
|
|
50
|
+
[...dependencies],
|
|
51
|
+
]),
|
|
52
|
+
),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const findPath = (
|
|
56
|
+
edges: Map<string, string[]>,
|
|
57
|
+
from: string,
|
|
58
|
+
target: string,
|
|
59
|
+
visited: Set<string> = new Set(),
|
|
60
|
+
): string[] | null => {
|
|
61
|
+
if (from === target) return [from];
|
|
62
|
+
if (visited.has(from)) return null;
|
|
63
|
+
|
|
64
|
+
visited.add(from);
|
|
65
|
+
|
|
66
|
+
for (const next of edges.get(from) || []) {
|
|
67
|
+
const path = findPath(edges, next, target, visited);
|
|
68
|
+
|
|
69
|
+
if (path) return [from, ...path];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export class PubGrub {
|
|
76
|
+
private entries = new Map<string, IPackageEntry>();
|
|
77
|
+
|
|
78
|
+
private incompatibilities: IIncompatibility[] = [];
|
|
79
|
+
|
|
80
|
+
private incompatibilityKeys = new Set<string>();
|
|
81
|
+
|
|
82
|
+
constructor (private source: IEntrySource = entryStore) {}
|
|
83
|
+
|
|
84
|
+
public resolve = async (
|
|
85
|
+
rootDependencies: TDependencies,
|
|
86
|
+
): Promise<TPubGrubResult> => {
|
|
87
|
+
this.entries = new Map();
|
|
88
|
+
this.incompatibilities = [];
|
|
89
|
+
this.incompatibilityKeys = new Set();
|
|
90
|
+
|
|
91
|
+
const state: ISolverState = {
|
|
92
|
+
selected: new Map([[ROOT, ROOT_VERSION]]),
|
|
93
|
+
requirements: new Map(),
|
|
94
|
+
edges: new Map(),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const roots = await Promise.all(
|
|
98
|
+
Object.entries(rootDependencies).map(async ([depPath, range]) => ({
|
|
99
|
+
entry: await this.load(depPath),
|
|
100
|
+
range: range || '*',
|
|
101
|
+
})),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
roots.forEach(({ entry, range }) => {
|
|
105
|
+
this.assertConstraint(entry.name, range, ROOT);
|
|
106
|
+
|
|
107
|
+
const requirement = this.requirement(
|
|
108
|
+
entry.name,
|
|
109
|
+
range,
|
|
110
|
+
ROOT,
|
|
111
|
+
[ROOT, entry.name],
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
this.addIncompatibility({
|
|
115
|
+
terms: [
|
|
116
|
+
{ module: ROOT, range: ROOT_VERSION, positive: true },
|
|
117
|
+
{ module: entry.name, range, positive: false },
|
|
118
|
+
],
|
|
119
|
+
cause: { type: 'root', requirement },
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const result = await this.search(state);
|
|
124
|
+
|
|
125
|
+
if (!result.ok) throw this.toError(result.conflict);
|
|
126
|
+
|
|
127
|
+
return Object.fromEntries(
|
|
128
|
+
[...result.state.selected]
|
|
129
|
+
.filter(([name]) => name !== ROOT)
|
|
130
|
+
.map(([name, version]) => {
|
|
131
|
+
const entry = this.entries.get(name)!;
|
|
132
|
+
|
|
133
|
+
return [name, {
|
|
134
|
+
...entry,
|
|
135
|
+
version,
|
|
136
|
+
dependencies: { ...entry.versions[version] },
|
|
137
|
+
}];
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
private search = async (state: ISolverState): Promise<TSearchResult> => {
|
|
143
|
+
const propagatedConflict = this.propagate(state);
|
|
144
|
+
|
|
145
|
+
if (propagatedConflict) {
|
|
146
|
+
this.learn(state, propagatedConflict);
|
|
147
|
+
|
|
148
|
+
return { ok: false, conflict: propagatedConflict };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const unresolved = [...state.requirements]
|
|
152
|
+
.filter(([name, requirements]) => (
|
|
153
|
+
name !== ROOT
|
|
154
|
+
&& !state.selected.has(name)
|
|
155
|
+
&& requirements.some(requirement => requirement.positive)
|
|
156
|
+
))
|
|
157
|
+
.map(([name]) => name);
|
|
158
|
+
|
|
159
|
+
if (!unresolved.length) return { ok: true, state };
|
|
160
|
+
|
|
161
|
+
const choices = unresolved.map(name => {
|
|
162
|
+
const entry = this.entries.get(name)!;
|
|
163
|
+
const requirements = state.requirements.get(name) || [];
|
|
164
|
+
const versions = this.candidates(name, state);
|
|
165
|
+
|
|
166
|
+
return { name, entry, requirements, versions };
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
choices.sort((a, b) => (
|
|
170
|
+
a.versions.length - b.versions.length || a.name.localeCompare(b.name)
|
|
171
|
+
));
|
|
172
|
+
|
|
173
|
+
const choice = choices[0];
|
|
174
|
+
|
|
175
|
+
if (!choice.versions.length) {
|
|
176
|
+
const conflict = this.versionConflict(
|
|
177
|
+
choice.name,
|
|
178
|
+
choice.requirements,
|
|
179
|
+
Object.keys(choice.entry.versions),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
this.learn(state, conflict);
|
|
183
|
+
|
|
184
|
+
return { ok: false, conflict };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let lastConflict: TConflict | undefined;
|
|
188
|
+
|
|
189
|
+
for (const version of choice.versions) {
|
|
190
|
+
const next = cloneState(state);
|
|
191
|
+
|
|
192
|
+
next.selected.set(choice.name, version);
|
|
193
|
+
|
|
194
|
+
const dependencyConflict = await this.addDependencies(
|
|
195
|
+
next,
|
|
196
|
+
choice.entry,
|
|
197
|
+
version,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const result = dependencyConflict
|
|
201
|
+
? { ok: false as const, conflict: dependencyConflict }
|
|
202
|
+
: await this.search(next);
|
|
203
|
+
|
|
204
|
+
if (result.ok) return result;
|
|
205
|
+
|
|
206
|
+
this.learn(next, result.conflict);
|
|
207
|
+
lastConflict = result.conflict;
|
|
208
|
+
|
|
209
|
+
/* Решение не участвовало в причине: откатываемся через этот уровень. */
|
|
210
|
+
if (!result.conflict.packages.has(choice.name)) return result;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
conflict: lastConflict || this.versionConflict(
|
|
216
|
+
choice.name,
|
|
217
|
+
choice.requirements,
|
|
218
|
+
Object.keys(choice.entry.versions),
|
|
219
|
+
),
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
* Unit propagation: если incompatibility почти удовлетворена, добавляем
|
|
225
|
+
* отрицание единственного неопределённого term в partial solution.
|
|
226
|
+
*/
|
|
227
|
+
private propagate = (state: ISolverState): TConflict | null => {
|
|
228
|
+
let changed = true;
|
|
229
|
+
|
|
230
|
+
while (changed) {
|
|
231
|
+
changed = false;
|
|
232
|
+
|
|
233
|
+
for (const incompatibility of this.incompatibilities) {
|
|
234
|
+
const relations = incompatibility.terms.map(term => (
|
|
235
|
+
this.relation(term, state)
|
|
236
|
+
));
|
|
237
|
+
|
|
238
|
+
if (relations.includes('contradicted')) continue;
|
|
239
|
+
|
|
240
|
+
const inconclusive = relations
|
|
241
|
+
.map((relation, index) => ({ relation, index }))
|
|
242
|
+
.filter(({ relation }) => relation === 'inconclusive');
|
|
243
|
+
|
|
244
|
+
if (!inconclusive.length) {
|
|
245
|
+
return this.conflictFrom(incompatibility, state);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (inconclusive.length !== 1) continue;
|
|
249
|
+
|
|
250
|
+
const term = incompatibility.terms[inconclusive[0].index];
|
|
251
|
+
|
|
252
|
+
const requirement = this.derivedRequirement(
|
|
253
|
+
term,
|
|
254
|
+
incompatibility,
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
changed = this.addRequirement(
|
|
258
|
+
state,
|
|
259
|
+
term.module,
|
|
260
|
+
requirement,
|
|
261
|
+
) || changed;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const selectedConflict = this.findSelectedConflict(state);
|
|
265
|
+
|
|
266
|
+
if (selectedConflict) return selectedConflict;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return null;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
private relation = (term: ITerm, state: ISolverState): TTermState => {
|
|
273
|
+
const selected = state.selected.get(term.module);
|
|
274
|
+
|
|
275
|
+
if (selected) {
|
|
276
|
+
const matches = versionUtil.satisfies(selected, term.range);
|
|
277
|
+
const satisfied = term.positive ? matches : !matches;
|
|
278
|
+
|
|
279
|
+
return satisfied ? 'satisfied' : 'contradicted';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const requirements = state.requirements.get(term.module) || [];
|
|
283
|
+
|
|
284
|
+
if (!requirements.length) return 'inconclusive';
|
|
285
|
+
|
|
286
|
+
const candidates = this.candidates(term.module, state);
|
|
287
|
+
|
|
288
|
+
if (!candidates.length) return 'inconclusive';
|
|
289
|
+
|
|
290
|
+
const matching = candidates.filter(version => (
|
|
291
|
+
versionUtil.satisfies(version, term.range)
|
|
292
|
+
));
|
|
293
|
+
|
|
294
|
+
if (term.positive) {
|
|
295
|
+
if (matching.length === candidates.length) return 'satisfied';
|
|
296
|
+
if (!matching.length) return 'contradicted';
|
|
297
|
+
} else {
|
|
298
|
+
if (!matching.length) return 'satisfied';
|
|
299
|
+
if (matching.length === candidates.length) return 'contradicted';
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return 'inconclusive';
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
private candidates = (name: string, state: ISolverState): string[] => {
|
|
306
|
+
const versions = Object.keys(this.entries.get(name)?.versions || {});
|
|
307
|
+
const requirements = state.requirements.get(name) || [];
|
|
308
|
+
|
|
309
|
+
return versionUtil.sort(versions).filter(version => (
|
|
310
|
+
requirements.every(requirement => {
|
|
311
|
+
const matches = versionUtil.satisfies(version, requirement.range);
|
|
312
|
+
|
|
313
|
+
return requirement.positive ? matches : !matches;
|
|
314
|
+
})
|
|
315
|
+
));
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
private addDependencies = async (
|
|
319
|
+
state: ISolverState,
|
|
320
|
+
entry: IPackageEntry,
|
|
321
|
+
version: string,
|
|
322
|
+
): Promise<ICircularConflict | null> => {
|
|
323
|
+
const dependencies = entry.versions[version] || {};
|
|
324
|
+
const dependencyNames = Object.keys(dependencies);
|
|
325
|
+
|
|
326
|
+
Object.entries(dependencies).forEach(([name, range]) => {
|
|
327
|
+
this.assertConstraint(name, range, `${entry.name}@${version}`);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
await Promise.all(dependencyNames.map(name => this.load(name)));
|
|
331
|
+
state.edges.set(entry.name, dependencyNames);
|
|
332
|
+
|
|
333
|
+
for (const [name, range] of Object.entries(dependencies)) {
|
|
334
|
+
const path = findPath(state.edges, name, entry.name);
|
|
335
|
+
|
|
336
|
+
if (path) {
|
|
337
|
+
const chain = [entry.name, ...path];
|
|
338
|
+
|
|
339
|
+
return {
|
|
340
|
+
type: 'circular',
|
|
341
|
+
name: entry.name,
|
|
342
|
+
chain,
|
|
343
|
+
packages: new Set(chain),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const parentRequirement = [...(state.requirements.get(entry.name) || [])]
|
|
348
|
+
.sort((a, b) => a.chain.length - b.chain.length)[0];
|
|
349
|
+
|
|
350
|
+
const requirement = this.requirement(
|
|
351
|
+
name,
|
|
352
|
+
range,
|
|
353
|
+
`${entry.name}@${version}`,
|
|
354
|
+
[...(parentRequirement?.chain || [ROOT, entry.name]), name],
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
this.addIncompatibility({
|
|
358
|
+
terms: [
|
|
359
|
+
{ module: entry.name, range: version, positive: true },
|
|
360
|
+
{ module: name, range, positive: false },
|
|
361
|
+
],
|
|
362
|
+
cause: { type: 'dependency', requirement },
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return null;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
private load = async (depPath: string): Promise<IPackageEntry> => {
|
|
370
|
+
const known = this.entries.get(depPath);
|
|
371
|
+
|
|
372
|
+
if (known) return known;
|
|
373
|
+
|
|
374
|
+
const entry = await this.source.fetch(depPath);
|
|
375
|
+
const duplicate = this.entries.get(entry.name);
|
|
376
|
+
|
|
377
|
+
if (duplicate && duplicate.url !== entry.url) {
|
|
378
|
+
throw new SVError('ADDON_NOT_FOUND', {
|
|
379
|
+
name: entry.name,
|
|
380
|
+
urls: [duplicate.url, entry.url],
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
this.entries.set(entry.name, entry);
|
|
385
|
+
|
|
386
|
+
return entry;
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
private addIncompatibility = (incompatibility: IIncompatibility): void => {
|
|
390
|
+
const key = incompatibility.terms
|
|
391
|
+
.map(term => `${term.module}:${term.positive}:${term.range}`)
|
|
392
|
+
.sort()
|
|
393
|
+
.join('|');
|
|
394
|
+
|
|
395
|
+
if (this.incompatibilityKeys.has(key)) return;
|
|
396
|
+
|
|
397
|
+
this.incompatibilityKeys.add(key);
|
|
398
|
+
this.incompatibilities.push(incompatibility);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
private learn = (state: ISolverState, conflict: TConflict): void => {
|
|
402
|
+
const terms = [...state.selected]
|
|
403
|
+
.filter(([name]) => name !== ROOT && conflict.packages.has(name))
|
|
404
|
+
.map(([module, range]) => ({ module, range, positive: true }));
|
|
405
|
+
|
|
406
|
+
if (!terms.length) return;
|
|
407
|
+
|
|
408
|
+
this.addIncompatibility({
|
|
409
|
+
terms,
|
|
410
|
+
cause: { type: 'learned', conflict },
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
private conflictFrom = (
|
|
415
|
+
incompatibility: IIncompatibility,
|
|
416
|
+
state: ISolverState,
|
|
417
|
+
): TConflict => {
|
|
418
|
+
if (incompatibility.cause.type === 'learned') {
|
|
419
|
+
return incompatibility.cause.conflict;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const { requirement } = incompatibility.cause;
|
|
423
|
+
const name = requirement.chain[requirement.chain.length - 1];
|
|
424
|
+
|
|
425
|
+
this.addRequirement(state, name, requirement);
|
|
426
|
+
|
|
427
|
+
return this.versionConflict(
|
|
428
|
+
name,
|
|
429
|
+
state.requirements.get(name) || [],
|
|
430
|
+
Object.keys(this.entries.get(name)?.versions || {}),
|
|
431
|
+
);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
// eslint-disable-next-line class-methods-use-this
|
|
435
|
+
private derivedRequirement = (
|
|
436
|
+
term: ITerm,
|
|
437
|
+
incompatibility: IIncompatibility,
|
|
438
|
+
): IRequirement => {
|
|
439
|
+
if (incompatibility.cause.type !== 'learned') {
|
|
440
|
+
const source = incompatibility.cause.requirement;
|
|
441
|
+
|
|
442
|
+
return { ...source, positive: !term.positive };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
range: term.range,
|
|
447
|
+
positive: !term.positive,
|
|
448
|
+
requiredBy: '<conflict>',
|
|
449
|
+
chain: [ROOT, term.module],
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// eslint-disable-next-line class-methods-use-this
|
|
454
|
+
private requirement = (
|
|
455
|
+
name: string,
|
|
456
|
+
range: string,
|
|
457
|
+
requiredBy: string,
|
|
458
|
+
chain: string[],
|
|
459
|
+
): IRequirement => ({
|
|
460
|
+
range,
|
|
461
|
+
positive: true,
|
|
462
|
+
requiredBy,
|
|
463
|
+
chain: chain[chain.length - 1] === name ? chain : [...chain, name],
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
// eslint-disable-next-line class-methods-use-this
|
|
467
|
+
private addRequirement = (
|
|
468
|
+
state: ISolverState,
|
|
469
|
+
name: string,
|
|
470
|
+
requirement: IRequirement,
|
|
471
|
+
): boolean => {
|
|
472
|
+
const requirements = state.requirements.get(name) || [];
|
|
473
|
+
|
|
474
|
+
const exists = requirements.some(current => (
|
|
475
|
+
current.range === requirement.range
|
|
476
|
+
&& current.positive === requirement.positive
|
|
477
|
+
&& current.requiredBy === requirement.requiredBy
|
|
478
|
+
));
|
|
479
|
+
|
|
480
|
+
if (exists) return false;
|
|
481
|
+
|
|
482
|
+
requirements.push(requirement);
|
|
483
|
+
state.requirements.set(name, requirements);
|
|
484
|
+
|
|
485
|
+
return true;
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
private findSelectedConflict = (
|
|
489
|
+
state: ISolverState,
|
|
490
|
+
): IVersionConflict | null => {
|
|
491
|
+
for (const [name, version] of state.selected) {
|
|
492
|
+
if (name === ROOT) continue;
|
|
493
|
+
|
|
494
|
+
const requirements = state.requirements.get(name) || [];
|
|
495
|
+
|
|
496
|
+
const valid = requirements.every(requirement => {
|
|
497
|
+
const matches = versionUtil.satisfies(version, requirement.range);
|
|
498
|
+
|
|
499
|
+
return requirement.positive ? matches : !matches;
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (valid) continue;
|
|
503
|
+
|
|
504
|
+
return this.versionConflict(
|
|
505
|
+
name,
|
|
506
|
+
requirements,
|
|
507
|
+
Object.keys(this.entries.get(name)?.versions || {}),
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return null;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// eslint-disable-next-line class-methods-use-this
|
|
515
|
+
private versionConflict = (
|
|
516
|
+
name: string,
|
|
517
|
+
requirements: IRequirement[],
|
|
518
|
+
versions: string[],
|
|
519
|
+
): IVersionConflict => ({
|
|
520
|
+
type: 'version',
|
|
521
|
+
name,
|
|
522
|
+
requirements,
|
|
523
|
+
versions: versionUtil.sort(versions),
|
|
524
|
+
packages: new Set([
|
|
525
|
+
name,
|
|
526
|
+
...requirements.flatMap(requirement => requirement.chain),
|
|
527
|
+
].filter(packageName => packageName !== ROOT)),
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// eslint-disable-next-line class-methods-use-this
|
|
531
|
+
private assertConstraint = (
|
|
532
|
+
name: string,
|
|
533
|
+
range: string,
|
|
534
|
+
parent: string,
|
|
535
|
+
): void => {
|
|
536
|
+
if (versionUtil.validate(range, true)) return;
|
|
537
|
+
|
|
538
|
+
throw new SVError('UNKNOWN_VERSION', {
|
|
539
|
+
name,
|
|
540
|
+
parent,
|
|
541
|
+
version: range,
|
|
542
|
+
});
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
// eslint-disable-next-line class-methods-use-this
|
|
546
|
+
private toError = (conflict: TConflict): SVError => {
|
|
547
|
+
if (conflict.type === 'circular') {
|
|
548
|
+
return new SVError('CIRCULAR_DEPENDENCY', {
|
|
549
|
+
name: conflict.name,
|
|
550
|
+
chain: conflict.chain,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const parents = Object.fromEntries(
|
|
555
|
+
conflict.requirements.map(requirement => [
|
|
556
|
+
requirement.requiredBy,
|
|
557
|
+
`${requirement.positive ? '' : 'not '}${requirement.range}`,
|
|
558
|
+
]),
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
const chain = [...conflict.requirements]
|
|
562
|
+
.sort((a, b) => a.chain.length - b.chain.length)[0]?.chain;
|
|
563
|
+
|
|
564
|
+
return new SVError('VERSION_CONFLICT', {
|
|
565
|
+
name: conflict.name,
|
|
566
|
+
parents,
|
|
567
|
+
constraints: conflict.requirements,
|
|
568
|
+
versions: conflict.versions,
|
|
569
|
+
chain,
|
|
570
|
+
});
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export const pubGrub = {
|
|
575
|
+
resolve: (rootDependencies: TDependencies): Promise<TPubGrubResult> => (
|
|
576
|
+
new PubGrub().resolve(rootDependencies)
|
|
577
|
+
),
|
|
578
|
+
};
|