UNPKG

19.9 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.getIdentVendorPath = exports.prettyDependent = exports.prettyResolution = exports.prettyWorkspace = exports.sortDescriptors = exports.prettyLocatorNoColors = exports.prettyLocator = exports.prettyReference = exports.prettyDescriptor = exports.prettyRange = exports.prettyIdent = exports.slugifyLocator = exports.slugifyIdent = exports.stringifyLocator = exports.stringifyDescriptor = exports.stringifyIdent = exports.requirableIdent = exports.convertToManifestRange = exports.makeRange = exports.parseFileStyleRange = exports.parseRange = exports.tryParseLocator = exports.parseLocator = exports.tryParseDescriptor = exports.parseDescriptor = exports.tryParseIdent = exports.parseIdent = exports.areVirtualPackagesEquivalent = exports.areLocatorsEqual = exports.areDescriptorsEqual = exports.areIdentsEqual = exports.bindLocator = exports.bindDescriptor = exports.devirtualizeLocator = exports.devirtualizeDescriptor = exports.isVirtualLocator = exports.isVirtualDescriptor = exports.virtualizePackage = exports.virtualizeDescriptor = exports.copyPackage = exports.renamePackage = exports.convertPackageToLocator = exports.convertLocatorToDescriptor = exports.convertDescriptorToLocator = exports.convertToIdent = exports.makeLocator = exports.makeDescriptor = exports.makeIdent = void 0;
4const tslib_1 = require("tslib");
5const fslib_1 = require("@yarnpkg/fslib");
6const querystring_1 = tslib_1.__importDefault(require("querystring"));
7const semver_1 = tslib_1.__importDefault(require("semver"));
8const formatUtils = tslib_1.__importStar(require("./formatUtils"));
9const hashUtils = tslib_1.__importStar(require("./hashUtils"));
10const miscUtils = tslib_1.__importStar(require("./miscUtils"));
11const structUtils = tslib_1.__importStar(require("./structUtils"));
12const VIRTUAL_PROTOCOL = `virtual:`;
13const VIRTUAL_ABBREVIATE = 5;
14function makeIdent(scope, name) {
15 if (scope === null || scope === void 0 ? void 0 : scope.startsWith(`@`))
16 throw new Error(`Invalid scope: don't prefix it with '@'`);
17 return { identHash: hashUtils.makeHash(scope, name), scope, name };
18}
19exports.makeIdent = makeIdent;
20function makeDescriptor(ident, range) {
21 return { identHash: ident.identHash, scope: ident.scope, name: ident.name, descriptorHash: hashUtils.makeHash(ident.identHash, range), range };
22}
23exports.makeDescriptor = makeDescriptor;
24function makeLocator(ident, reference) {
25 return { identHash: ident.identHash, scope: ident.scope, name: ident.name, locatorHash: hashUtils.makeHash(ident.identHash, reference), reference };
26}
27exports.makeLocator = makeLocator;
28function convertToIdent(source) {
29 return { identHash: source.identHash, scope: source.scope, name: source.name };
30}
31exports.convertToIdent = convertToIdent;
32function convertDescriptorToLocator(descriptor) {
33 return { identHash: descriptor.identHash, scope: descriptor.scope, name: descriptor.name, locatorHash: descriptor.descriptorHash, reference: descriptor.range };
34}
35exports.convertDescriptorToLocator = convertDescriptorToLocator;
36function convertLocatorToDescriptor(locator) {
37 return { identHash: locator.identHash, scope: locator.scope, name: locator.name, descriptorHash: locator.locatorHash, range: locator.reference };
38}
39exports.convertLocatorToDescriptor = convertLocatorToDescriptor;
40function convertPackageToLocator(pkg) {
41 return { identHash: pkg.identHash, scope: pkg.scope, name: pkg.name, locatorHash: pkg.locatorHash, reference: pkg.reference };
42}
43exports.convertPackageToLocator = convertPackageToLocator;
44function renamePackage(pkg, locator) {
45 return {
46 identHash: locator.identHash,
47 scope: locator.scope,
48 name: locator.name,
49 locatorHash: locator.locatorHash,
50 reference: locator.reference,
51 version: pkg.version,
52 languageName: pkg.languageName,
53 linkType: pkg.linkType,
54 dependencies: new Map(pkg.dependencies),
55 peerDependencies: new Map(pkg.peerDependencies),
56 dependenciesMeta: new Map(pkg.dependenciesMeta),
57 peerDependenciesMeta: new Map(pkg.peerDependenciesMeta),
58 bin: new Map(pkg.bin),
59 };
60}
61exports.renamePackage = renamePackage;
62function copyPackage(pkg) {
63 return renamePackage(pkg, pkg);
64}
65exports.copyPackage = copyPackage;
66function virtualizeDescriptor(descriptor, entropy) {
67 if (entropy.includes(`#`))
68 throw new Error(`Invalid entropy`);
69 return makeDescriptor(descriptor, `virtual:${entropy}#${descriptor.range}`);
70}
71exports.virtualizeDescriptor = virtualizeDescriptor;
72function virtualizePackage(pkg, entropy) {
73 if (entropy.includes(`#`))
74 throw new Error(`Invalid entropy`);
75 return renamePackage(pkg, makeLocator(pkg, `virtual:${entropy}#${pkg.reference}`));
76}
77exports.virtualizePackage = virtualizePackage;
78function isVirtualDescriptor(descriptor) {
79 return descriptor.range.startsWith(VIRTUAL_PROTOCOL);
80}
81exports.isVirtualDescriptor = isVirtualDescriptor;
82function isVirtualLocator(locator) {
83 return locator.reference.startsWith(VIRTUAL_PROTOCOL);
84}
85exports.isVirtualLocator = isVirtualLocator;
86function devirtualizeDescriptor(descriptor) {
87 if (!isVirtualDescriptor(descriptor))
88 throw new Error(`Not a virtual descriptor`);
89 return makeDescriptor(descriptor, descriptor.range.replace(/^[^#]*#/, ``));
90}
91exports.devirtualizeDescriptor = devirtualizeDescriptor;
92function devirtualizeLocator(locator) {
93 if (!isVirtualLocator(locator))
94 throw new Error(`Not a virtual descriptor`);
95 return makeLocator(locator, locator.reference.replace(/^[^#]*#/, ``));
96}
97exports.devirtualizeLocator = devirtualizeLocator;
98function bindDescriptor(descriptor, params) {
99 if (descriptor.range.includes(`::`))
100 return descriptor;
101 return makeDescriptor(descriptor, `${descriptor.range}::${querystring_1.default.stringify(params)}`);
102}
103exports.bindDescriptor = bindDescriptor;
104function bindLocator(locator, params) {
105 if (locator.reference.includes(`::`))
106 return locator;
107 return makeLocator(locator, `${locator.reference}::${querystring_1.default.stringify(params)}`);
108}
109exports.bindLocator = bindLocator;
110function areIdentsEqual(a, b) {
111 return a.identHash === b.identHash;
112}
113exports.areIdentsEqual = areIdentsEqual;
114function areDescriptorsEqual(a, b) {
115 return a.descriptorHash === b.descriptorHash;
116}
117exports.areDescriptorsEqual = areDescriptorsEqual;
118function areLocatorsEqual(a, b) {
119 return a.locatorHash === b.locatorHash;
120}
121exports.areLocatorsEqual = areLocatorsEqual;
122/**
123 * Virtual packages are considered equivalent when they belong to the same
124 * package identity and have the same dependencies. Note that equivalence
125 * is not the same as equality, as the references may be different.
126 */
127function areVirtualPackagesEquivalent(a, b) {
128 if (!isVirtualLocator(a))
129 throw new Error(`Invalid package type`);
130 if (!isVirtualLocator(b))
131 throw new Error(`Invalid package type`);
132 if (!areIdentsEqual(a, b))
133 return false;
134 if (a.dependencies.size !== b.dependencies.size)
135 return false;
136 for (const dependencyDescriptorA of a.dependencies.values()) {
137 const dependencyDescriptorB = b.dependencies.get(dependencyDescriptorA.identHash);
138 if (!dependencyDescriptorB)
139 return false;
140 if (!areDescriptorsEqual(dependencyDescriptorA, dependencyDescriptorB)) {
141 return false;
142 }
143 }
144 return true;
145}
146exports.areVirtualPackagesEquivalent = areVirtualPackagesEquivalent;
147function parseIdent(string) {
148 const ident = tryParseIdent(string);
149 if (!ident)
150 throw new Error(`Invalid ident (${string})`);
151 return ident;
152}
153exports.parseIdent = parseIdent;
154function tryParseIdent(string) {
155 const match = string.match(/^(?:@([^/]+?)\/)?([^/]+)$/);
156 if (!match)
157 return null;
158 const [, scope, name] = match;
159 const realScope = typeof scope !== `undefined`
160 ? scope
161 : null;
162 return makeIdent(realScope, name);
163}
164exports.tryParseIdent = tryParseIdent;
165function parseDescriptor(string, strict = false) {
166 const descriptor = tryParseDescriptor(string, strict);
167 if (!descriptor)
168 throw new Error(`Invalid descriptor (${string})`);
169 return descriptor;
170}
171exports.parseDescriptor = parseDescriptor;
172function tryParseDescriptor(string, strict = false) {
173 const match = strict
174 ? string.match(/^(?:@([^/]+?)\/)?([^/]+?)(?:@(.+))$/)
175 : string.match(/^(?:@([^/]+?)\/)?([^/]+?)(?:@(.+))?$/);
176 if (!match)
177 return null;
178 const [, scope, name, range] = match;
179 if (range === `unknown`)
180 throw new Error(`Invalid range (${string})`);
181 const realScope = typeof scope !== `undefined`
182 ? scope
183 : null;
184 const realRange = typeof range !== `undefined`
185 ? range
186 : `unknown`;
187 return makeDescriptor(makeIdent(realScope, name), realRange);
188}
189exports.tryParseDescriptor = tryParseDescriptor;
190function parseLocator(string, strict = false) {
191 const locator = tryParseLocator(string, strict);
192 if (!locator)
193 throw new Error(`Invalid locator (${string})`);
194 return locator;
195}
196exports.parseLocator = parseLocator;
197function tryParseLocator(string, strict = false) {
198 const match = strict
199 ? string.match(/^(?:@([^/]+?)\/)?([^/]+?)(?:@(.+))$/)
200 : string.match(/^(?:@([^/]+?)\/)?([^/]+?)(?:@(.+))?$/);
201 if (!match)
202 return null;
203 const [, scope, name, reference] = match;
204 if (reference === `unknown`)
205 throw new Error(`Invalid reference (${string})`);
206 const realScope = typeof scope !== `undefined`
207 ? scope
208 : null;
209 const realReference = typeof reference !== `undefined`
210 ? reference
211 : `unknown`;
212 return makeLocator(makeIdent(realScope, name), realReference);
213}
214exports.tryParseLocator = tryParseLocator;
215function parseRange(range, opts) {
216 const match = range.match(/^([^#:]*:)?((?:(?!::)[^#])*)(?:#((?:(?!::).)*))?(?:::(.*))?$/);
217 if (match === null)
218 throw new Error(`Invalid range (${range})`);
219 const protocol = typeof match[1] !== `undefined`
220 ? match[1]
221 : null;
222 if (typeof (opts === null || opts === void 0 ? void 0 : opts.requireProtocol) === `string` && protocol !== opts.requireProtocol)
223 throw new Error(`Invalid protocol (${protocol})`);
224 else if ((opts === null || opts === void 0 ? void 0 : opts.requireProtocol) && protocol === null)
225 throw new Error(`Missing protocol (${protocol})`);
226 const source = typeof match[3] !== `undefined`
227 ? decodeURIComponent(match[2])
228 : null;
229 if ((opts === null || opts === void 0 ? void 0 : opts.requireSource) && source === null)
230 throw new Error(`Missing source (${range})`);
231 const rawSelector = typeof match[3] !== `undefined`
232 ? decodeURIComponent(match[3])
233 : decodeURIComponent(match[2]);
234 const selector = (opts === null || opts === void 0 ? void 0 : opts.parseSelector)
235 ? querystring_1.default.parse(rawSelector)
236 : rawSelector;
237 const params = typeof match[4] !== `undefined`
238 ? querystring_1.default.parse(match[4])
239 : null;
240 return {
241 // @ts-expect-error
242 protocol,
243 // @ts-expect-error
244 source,
245 // @ts-expect-error
246 selector,
247 // @ts-expect-error
248 params,
249 };
250}
251exports.parseRange = parseRange;
252function parseFileStyleRange(range, { protocol }) {
253 const { selector, params } = parseRange(range, {
254 requireProtocol: protocol,
255 requireBindings: true,
256 });
257 if (typeof params.locator !== `string`)
258 throw new Error(`Assertion failed: Invalid bindings for ${range}`);
259 const parentLocator = parseLocator(params.locator, true);
260 const path = selector;
261 return { parentLocator, path };
262}
263exports.parseFileStyleRange = parseFileStyleRange;
264function encodeUnsafeCharacters(str) {
265 str = str.replace(/%/g, `%25`);
266 str = str.replace(/:/g, `%3A`);
267 str = str.replace(/#/g, `%23`);
268 return str;
269}
270function hasParams(params) {
271 if (params === null)
272 return false;
273 return Object.entries(params).length > 0;
274}
275function makeRange({ protocol, source, selector, params }) {
276 let range = ``;
277 if (protocol !== null)
278 range += `${protocol}`;
279 if (source !== null)
280 range += `${encodeUnsafeCharacters(source)}#`;
281 range += encodeUnsafeCharacters(selector);
282 if (hasParams(params))
283 range += `::${querystring_1.default.stringify(params)}`;
284 return range;
285}
286exports.makeRange = makeRange;
287/**
288 * The range used internally may differ from the range stored in the
289 * Manifest (package.json). This removes any params indicated for internal use.
290 * An internal param starts with "__".
291 * @param range range to convert
292 */
293function convertToManifestRange(range) {
294 const { params, protocol, source, selector } = parseRange(range);
295 for (const name in params)
296 if (name.startsWith(`__`))
297 delete params[name];
298 return makeRange({ protocol, source, params, selector });
299}
300exports.convertToManifestRange = convertToManifestRange;
301function requirableIdent(ident) {
302 if (ident.scope) {
303 return `@${ident.scope}/${ident.name}`;
304 }
305 else {
306 return `${ident.name}`;
307 }
308}
309exports.requirableIdent = requirableIdent;
310function stringifyIdent(ident) {
311 if (ident.scope) {
312 return `@${ident.scope}/${ident.name}`;
313 }
314 else {
315 return `${ident.name}`;
316 }
317}
318exports.stringifyIdent = stringifyIdent;
319function stringifyDescriptor(descriptor) {
320 if (descriptor.scope) {
321 return `@${descriptor.scope}/${descriptor.name}@${descriptor.range}`;
322 }
323 else {
324 return `${descriptor.name}@${descriptor.range}`;
325 }
326}
327exports.stringifyDescriptor = stringifyDescriptor;
328function stringifyLocator(locator) {
329 if (locator.scope) {
330 return `@${locator.scope}/${locator.name}@${locator.reference}`;
331 }
332 else {
333 return `${locator.name}@${locator.reference}`;
334 }
335}
336exports.stringifyLocator = stringifyLocator;
337function slugifyIdent(ident) {
338 if (ident.scope !== null) {
339 return `@${ident.scope}-${ident.name}`;
340 }
341 else {
342 return ident.name;
343 }
344}
345exports.slugifyIdent = slugifyIdent;
346function slugifyLocator(locator) {
347 const { protocol, selector } = parseRange(locator.reference);
348 const humanProtocol = protocol !== null
349 ? protocol.replace(/:$/, ``)
350 : `exotic`;
351 const humanVersion = semver_1.default.valid(selector);
352 const humanReference = humanVersion !== null
353 ? `${humanProtocol}-${humanVersion}`
354 : `${humanProtocol}`;
355 // 10 hex characters means that 47 different entries have 10^-9 chances of
356 // causing a hash collision. Since this hash is joined with the package name
357 // (making it highly unlikely you'll have more than a handful of instances
358 // of any single package), this should provide a good enough guard in most
359 // cases.
360 //
361 // Also note that eCryptfs eats some bytes, so the theoretical maximum for a
362 // file size is around 140 bytes (but we don't need as much, as explained).
363 const hashTruncate = 10;
364 const slug = locator.scope
365 ? `${slugifyIdent(locator)}-${humanReference}-${locator.locatorHash.slice(0, hashTruncate)}`
366 : `${slugifyIdent(locator)}-${humanReference}-${locator.locatorHash.slice(0, hashTruncate)}`;
367 return fslib_1.toFilename(slug);
368}
369exports.slugifyLocator = slugifyLocator;
370function prettyIdent(configuration, ident) {
371 if (ident.scope) {
372 return `${formatUtils.pretty(configuration, `@${ident.scope}/`, formatUtils.Type.SCOPE)}${formatUtils.pretty(configuration, ident.name, formatUtils.Type.NAME)}`;
373 }
374 else {
375 return `${formatUtils.pretty(configuration, ident.name, formatUtils.Type.NAME)}`;
376 }
377}
378exports.prettyIdent = prettyIdent;
379function prettyRangeNoColors(range) {
380 if (range.startsWith(VIRTUAL_PROTOCOL)) {
381 const nested = prettyRangeNoColors(range.substr(range.indexOf(`#`) + 1));
382 const abbrev = range.substr(VIRTUAL_PROTOCOL.length, VIRTUAL_ABBREVIATE);
383 // I'm not satisfied of how the virtual packages appear in the output
384 // eslint-disable-next-line no-constant-condition
385 return false ? `${nested} (virtual:${abbrev})` : `${nested} [${abbrev}]`;
386 }
387 else {
388 return range.replace(/\?.*/, `?[...]`);
389 }
390}
391function prettyRange(configuration, range) {
392 return `${formatUtils.pretty(configuration, prettyRangeNoColors(range), formatUtils.Type.RANGE)}`;
393}
394exports.prettyRange = prettyRange;
395function prettyDescriptor(configuration, descriptor) {
396 return `${prettyIdent(configuration, descriptor)}${formatUtils.pretty(configuration, `@`, formatUtils.Type.RANGE)}${prettyRange(configuration, descriptor.range)}`;
397}
398exports.prettyDescriptor = prettyDescriptor;
399function prettyReference(configuration, reference) {
400 return `${formatUtils.pretty(configuration, prettyRangeNoColors(reference), formatUtils.Type.REFERENCE)}`;
401}
402exports.prettyReference = prettyReference;
403function prettyLocator(configuration, locator) {
404 return `${prettyIdent(configuration, locator)}${formatUtils.pretty(configuration, `@`, formatUtils.Type.REFERENCE)}${prettyReference(configuration, locator.reference)}`;
405}
406exports.prettyLocator = prettyLocator;
407function prettyLocatorNoColors(locator) {
408 return `${stringifyIdent(locator)}@${prettyRangeNoColors(locator.reference)}`;
409}
410exports.prettyLocatorNoColors = prettyLocatorNoColors;
411function sortDescriptors(descriptors) {
412 return miscUtils.sortMap(descriptors, [
413 descriptor => stringifyIdent(descriptor),
414 descriptor => descriptor.range,
415 ]);
416}
417exports.sortDescriptors = sortDescriptors;
418function prettyWorkspace(configuration, workspace) {
419 return prettyIdent(configuration, workspace.locator);
420}
421exports.prettyWorkspace = prettyWorkspace;
422function prettyResolution(configuration, descriptor, locator) {
423 const devirtualizedDescriptor = isVirtualDescriptor(descriptor)
424 ? devirtualizeDescriptor(descriptor)
425 : descriptor;
426 if (locator === null) {
427 return `${structUtils.prettyDescriptor(configuration, devirtualizedDescriptor)}${formatUtils.pretty(configuration, `✘`, `red`)}`;
428 }
429 else if (devirtualizedDescriptor.identHash === locator.identHash) {
430 return `${structUtils.prettyDescriptor(configuration, devirtualizedDescriptor)}${prettyReference(configuration, locator.reference)}`;
431 }
432 else {
433 return `${structUtils.prettyDescriptor(configuration, devirtualizedDescriptor)}${prettyLocator(configuration, locator)}`;
434 }
435}
436exports.prettyResolution = prettyResolution;
437function prettyDependent(configuration, locator, descriptor) {
438 if (descriptor === null) {
439 return `${prettyLocator(configuration, locator)}`;
440 }
441 else {
442 return `${prettyLocator(configuration, locator)} (via ${structUtils.prettyRange(configuration, descriptor.range)})`;
443 }
444}
445exports.prettyDependent = prettyDependent;
446/**
447 * The presence of a `node_modules` directory in the path is extremely common
448 * in the JavaScript ecosystem to denote whether a path belongs to a vendor
449 * or not. I considered using a more generic path for packages that aren't
450 * always JS-only (such as when using the Git fetcher), but that unfortunately
451 * caused various JS apps to start showing errors when working with git repos.
452 *
453 * As a result, all packages from all languages will follow this convention. At
454 * least it'll be consistent, and linkers will always have the ability to remap
455 * them to a different location if that's a critical requirement.
456 */
457function getIdentVendorPath(ident) {
458 return `node_modules/${requirableIdent(ident)}`;
459}
460exports.getIdentVendorPath = getIdentVendorPath;