UNPKG

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