-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathLegacyMigrationResolver.ts
133 lines (101 loc) Β· 5.56 KB
/
LegacyMigrationResolver.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import {xfs, ppath} from '@yarnpkg/fslib';
import {parseSyml} from '@yarnpkg/parsers';
import semver from 'semver';
import {MessageName} from './MessageName';
import {Project} from './Project';
import {Report} from './Report';
import {Resolver, ResolveOptions, MinimalResolveOptions} from './Resolver';
import * as structUtils from './structUtils';
import {DescriptorHash, Descriptor, Locator} from './types';
const IMPORTED_PATTERNS: Array<[RegExp, (version: string, ...args: Array<string>) => string]> = [
// These ones come from Git urls
[/^(git(?:\+(?:https|ssh))?:\/\/.*(?:\.git)?)#(.*)$/, (version, $0, $1, $2) => `${$1}#commit=${$2}`],
// These ones come from the GitHub HTTP endpoints
[/^https:\/\/((?:[^/]+?)@)?codeload\.github\.com\/([^/]+\/[^/]+)\/tar\.gz\/([0-9a-f]+)$/, (version, $0, $1 = ``, $2, $3) => `https://${$1}github.com/${$2}.git#commit=${$3}`],
[/^https:\/\/((?:[^/]+?)@)?github\.com\/([^/]+\/[^/]+?)(?:\.git)?#([0-9a-f]+)$/, (version, $0, $1 = ``, $2, $3) => `https://${$1}github.com/${$2}.git#commit=${$3}`],
// These ones come from the npm registry
// Note: /download/ is used by custom registries like Taobao
[/^https?:\/\/[^/]+\/(?:[^/]+\/)*(?:@[^/]+\/)?([^/]+)\/(?:-|download)\/\1-[^/]+\.tgz(?:#|$)/, version => `npm:${version}`],
// The GitHub package registry uses a different style of URLs
[/^https:\/\/npm\.pkg\.github\.com\/download\/(?:@[^/]+)\/(?:[^/]+)\/(?:[^/]+)\/(?:[0-9a-f]+)$/, version => `npm:${version}`],
// FontAwesome too; what is it with these registries that made them think using a different url pattern was a good idea?
[/^https:\/\/npm\.fontawesome\.com\/(?:@[^/]+)\/([^/]+)\/-\/([^/]+)\/\1-\2.tgz(?:#|$)/, version => `npm:${version}`],
// These ones come from the old Yarn offline mirror - we assume they came from npm
[/^[^/]+\.tgz#[0-9a-f]+$/, version => `npm:${version}`],
];
export class LegacyMigrationResolver implements Resolver {
private resolutions: Map<DescriptorHash, Locator> | null = null;
async setup(project: Project, {report}: {report: Report}) {
const lockfilePath = ppath.join(project.cwd, project.configuration.get(`lockfileFilename`));
// No need to enable it if the lockfile doesn't exist
if (!xfs.existsSync(lockfilePath))
return;
const content = await xfs.readFilePromise(lockfilePath, `utf8`);
const parsed = parseSyml(content);
// No need to enable it either if the lockfile is modern
if (Object.prototype.hasOwnProperty.call(parsed, `__metadata`))
return;
const resolutions = this.resolutions = new Map();
for (const key of Object.keys(parsed)) {
let descriptor = structUtils.tryParseDescriptor(key);
if (!descriptor) {
report.reportWarning(MessageName.YARN_IMPORT_FAILED, `Failed to parse the string "${key}" into a proper descriptor`);
continue;
}
if (semver.validRange(descriptor.range))
descriptor = structUtils.makeDescriptor(descriptor, `npm:${descriptor.range}`);
const {version, resolved} = (parsed as any)[key];
// Workspaces don't have the "resolved" key; we can skip them, as their
// resolution will be recomputed when needed anyway
if (!resolved)
continue;
let reference;
for (const [pattern, matcher] of IMPORTED_PATTERNS) {
const match = resolved.match(pattern);
if (match) {
reference = matcher(version, ...match);
break;
}
}
if (!reference) {
report.reportWarning(MessageName.YARN_IMPORT_FAILED, `${structUtils.prettyDescriptor(project.configuration, descriptor)}: Only some patterns can be imported from legacy lockfiles (not "${resolved}")`);
continue;
}
const resolution = structUtils.makeLocator(descriptor, reference);
resolutions.set(descriptor.descriptorHash, resolution);
}
}
supportsDescriptor(descriptor: Descriptor, opts: MinimalResolveOptions) {
if (!this.resolutions)
return false;
return this.resolutions.has(descriptor.descriptorHash);
}
supportsLocator(locator: Locator, opts: MinimalResolveOptions) {
// This resolver only supports the descriptor -> locator part of the
// resolution, not the locator -> package one.
return false;
}
shouldPersistResolution(locator: Locator, opts: MinimalResolveOptions): never {
throw new Error(`Assertion failed: This resolver doesn't support resolving locators to packages`);
}
bindDescriptor(descriptor: Descriptor, fromLocator: Locator, opts: MinimalResolveOptions) {
return descriptor;
}
getResolutionDependencies(descriptor: Descriptor, opts: MinimalResolveOptions) {
return [];
}
async getCandidates(descriptor: Descriptor, dependencies: unknown, opts: ResolveOptions) {
if (!this.resolutions)
throw new Error(`Assertion failed: The resolution store should have been setup`);
const resolution = this.resolutions.get(descriptor.descriptorHash);
if (!resolution)
throw new Error(`Assertion failed: The resolution should have been registered`);
return [resolution];
}
async getSatisfying(descriptor: Descriptor, references: Array<string>, opts: ResolveOptions) {
return null;
}
async resolve(locator: Locator, opts: ResolveOptions): Promise<never> {
throw new Error(`Assertion failed: This resolver doesn't support resolving locators to packages`);
}
}