diff --git a/.yarn/versions/76e603e2.yml b/.yarn/versions/76e603e2.yml new file mode 100644 index 000000000000..b0f13833ec81 --- /dev/null +++ b/.yarn/versions/76e603e2.yml @@ -0,0 +1,30 @@ +releases: + "@yarnpkg/cli": patch + "@yarnpkg/core": patch + "@yarnpkg/plugin-essentials": patch + "@yarnpkg/plugin-git": patch + "@yarnpkg/plugin-npm-cli": patch + "@yarnpkg/plugin-pnp": patch + "@yarnpkg/plugin-typescript": patch + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-exec" + - "@yarnpkg/plugin-file" + - "@yarnpkg/plugin-github" + - "@yarnpkg/plugin-http" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-link" + - "@yarnpkg/plugin-node-modules" + - "@yarnpkg/plugin-npm" + - "@yarnpkg/plugin-pack" + - "@yarnpkg/plugin-patch" + - "@yarnpkg/plugin-stage" + - "@yarnpkg/plugin-version" + - "@yarnpkg/plugin-workspace-tools" + - "@yarnpkg/builder" + - "@yarnpkg/doctor" + - "@yarnpkg/pnpify" diff --git a/packages/plugin-essentials/sources/commands/set/version.ts b/packages/plugin-essentials/sources/commands/set/version.ts index c4e699f3c127..705878d9615d 100644 --- a/packages/plugin-essentials/sources/commands/set/version.ts +++ b/packages/plugin-essentials/sources/commands/set/version.ts @@ -53,7 +53,7 @@ export default class SetVersionCommand extends BaseCommand { bundleUrl = `https://github.com/yarnpkg/berry/raw/%40yarnpkg/cli/${this.version}/packages/yarnpkg-cli/bin/yarn.js`; else if (semverUtils.satisfiesWithPrereleases(this.version, `^0.x || ^1.x`)) bundleUrl = `https://github.com/yarnpkg/yarn/releases/download/v${this.version}/yarn-${this.version}.js`; - else if (semver.validRange(this.version)) + else if (semverUtils.validRange(this.version)) throw new UsageError(`Support for ranges got removed - please use the exact version you want to install, or 'latest' to get the latest build available`); else throw new UsageError(`Invalid version descriptor "${this.version}"`); diff --git a/packages/plugin-git/sources/gitUtils.ts b/packages/plugin-git/sources/gitUtils.ts index bc5635ee97c0..cfa48e96d242 100644 --- a/packages/plugin-git/sources/gitUtils.ts +++ b/packages/plugin-git/sources/gitUtils.ts @@ -1,9 +1,9 @@ -import {Configuration, Locator, execUtils, structUtils, httpUtils} from '@yarnpkg/core'; -import {npath, xfs} from '@yarnpkg/fslib'; -import GitUrlParse from 'git-url-parse'; -import querystring from 'querystring'; -import semver from 'semver'; -import urlLib from 'url'; +import {Configuration, Locator, execUtils, structUtils, httpUtils, semverUtils} from '@yarnpkg/core'; +import {npath, xfs} from '@yarnpkg/fslib'; +import GitUrlParse from 'git-url-parse'; +import querystring from 'querystring'; +import semver from 'semver'; +import urlLib from 'url'; function makeGitEnvironment() { return { @@ -237,7 +237,8 @@ export async function resolveUrl(url: string, configuration: Configuration) { } case TreeishProtocols.Semver: { - if (!semver.validRange(request)) + const validRange = semverUtils.validRange(request); + if (!validRange) throw new Error(`Invalid range ("${request}")`); const semverTags = new Map([...refs.entries()].filter(([ref]) => { @@ -248,7 +249,7 @@ export async function resolveUrl(url: string, configuration: Configuration) { return entry[0] !== null; })); - const bestVersion = semver.maxSatisfying([...semverTags.keys()], request); + const bestVersion = semver.maxSatisfying([...semverTags.keys()], validRange); if (bestVersion === null) throw new Error(`No matching range ("${request}")`); diff --git a/packages/plugin-npm-cli/sources/commands/npm/info.ts b/packages/plugin-npm-cli/sources/commands/npm/info.ts index 2526ff4f6857..f186926dbb62 100644 --- a/packages/plugin-npm-cli/sources/commands/npm/info.ts +++ b/packages/plugin-npm-cli/sources/commands/npm/info.ts @@ -1,7 +1,7 @@ import * as npm from '@npm/types'; import {BaseCommand} from '@yarnpkg/cli'; import {Project, Configuration, structUtils, Descriptor} from '@yarnpkg/core'; -import {StreamReport, MessageName} from '@yarnpkg/core'; +import {StreamReport, MessageName, semverUtils} from '@yarnpkg/core'; import {npmHttpUtils} from '@yarnpkg/plugin-npm'; import {Command, Option, Usage, UsageError} from 'clipanion'; import path from 'path'; @@ -127,8 +127,9 @@ export default class InfoCommand extends BaseCommand { // The latest version that satisfies `descriptor.range` (if it is a valid range), else `fallbackVersion` let version: string = fallbackVersion; - if (semver.validRange(descriptor.range)) { - const maxSatisfyingVersion = semver.maxSatisfying(versions, descriptor.range); + const validRange = semverUtils.validRange(descriptor.range); + if (validRange) { + const maxSatisfyingVersion = semver.maxSatisfying(versions, validRange); if (maxSatisfyingVersion !== null) { version = maxSatisfyingVersion; diff --git a/packages/plugin-pnp/sources/commands/unplug.ts b/packages/plugin-pnp/sources/commands/unplug.ts index 2155eee788ed..3969362d004a 100644 --- a/packages/plugin-pnp/sources/commands/unplug.ts +++ b/packages/plugin-pnp/sources/commands/unplug.ts @@ -3,7 +3,6 @@ import {Cache, Configuration, Project, StreamReport, Package, MessageName, forma import {structUtils, semverUtils} from '@yarnpkg/core'; import {Command, Option, Usage, UsageError} from 'clipanion'; import micromatch from 'micromatch'; -import semver from 'semver'; import * as pnpUtils from '../pnpUtils'; @@ -85,7 +84,7 @@ export default class UnplugCommand extends BaseCommand { ? patternDescriptor : structUtils.makeDescriptor(patternDescriptor, `*`); - if (!semver.validRange(pseudoDescriptor.range)) + if (!semverUtils.validRange(pseudoDescriptor.range)) throw new UsageError(`The range of the descriptor patterns must be a valid semver range (${structUtils.prettyDescriptor(configuration, pseudoDescriptor)})`); return (pkg: Package) => { diff --git a/packages/plugin-typescript/sources/index.ts b/packages/plugin-typescript/sources/index.ts index d4258fb2ce48..27ed602ce692 100644 --- a/packages/plugin-typescript/sources/index.ts +++ b/packages/plugin-typescript/sources/index.ts @@ -1,5 +1,5 @@ import {Descriptor, Plugin, Workspace, ResolveOptions, Manifest, AllDependencies, DescriptorHash, Package} from '@yarnpkg/core'; -import {structUtils, ThrowReport, miscUtils} from '@yarnpkg/core'; +import {structUtils, ThrowReport, miscUtils, semverUtils} from '@yarnpkg/core'; import {Hooks as EssentialsHooks} from '@yarnpkg/plugin-essentials'; import {suggestUtils} from '@yarnpkg/plugin-essentials'; import {Hooks as PackHooks} from '@yarnpkg/plugin-pack'; @@ -41,7 +41,7 @@ const afterWorkspaceDependencyAddition = async ( let range = structUtils.parseRange(descriptor.range).selector; // If the range is a tag, we have to resolve it into a semver version - if (!semver.validRange(range)) { + if (!semverUtils.validRange(range)) { const originalCandidates = await resolver.getCandidates(descriptor, new Map(), resolveOptions); range = structUtils.parseRange(originalCandidates[0].reference).selector; } diff --git a/packages/yarnpkg-core/sources/Configuration.ts b/packages/yarnpkg-core/sources/Configuration.ts index bcb472dd837c..475eca79f417 100644 --- a/packages/yarnpkg-core/sources/Configuration.ts +++ b/packages/yarnpkg-core/sources/Configuration.ts @@ -5,7 +5,6 @@ import camelcase import {isCI} from 'ci-info'; import {UsageError} from 'clipanion'; import pLimit, {Limit} from 'p-limit'; -import semver from 'semver'; import {PassThrough, Writable} from 'stream'; import {CorePlugin} from './CorePlugin'; @@ -1407,7 +1406,7 @@ export class Configuration { const packageExtensions = this.packageExtensions; const registerPackageExtension = (descriptor: Descriptor, extensionData: PackageExtensionData, {userProvided = false}: {userProvided?: boolean} = {}) => { - if (!semver.validRange(descriptor.range)) + if (!semverUtils.validRange(descriptor.range)) throw new Error(`Only semver ranges are allowed as keys for the lockfileExtensions setting`); const extension = new Manifest(); diff --git a/packages/yarnpkg-core/sources/LegacyMigrationResolver.ts b/packages/yarnpkg-core/sources/LegacyMigrationResolver.ts index b28aac1cf8fe..0889747584bd 100644 --- a/packages/yarnpkg-core/sources/LegacyMigrationResolver.ts +++ b/packages/yarnpkg-core/sources/LegacyMigrationResolver.ts @@ -1,11 +1,11 @@ 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 semverUtils from './semverUtils'; import * as structUtils from './structUtils'; import {DescriptorHash, Descriptor, Locator} from './types'; @@ -58,7 +58,7 @@ export class LegacyMigrationResolver implements Resolver { continue; } - if (semver.validRange(descriptor.range)) + if (semverUtils.validRange(descriptor.range)) descriptor = structUtils.makeDescriptor(descriptor, `npm:${descriptor.range}`); const {version, resolved} = (parsed as any)[key]; diff --git a/packages/yarnpkg-core/sources/Workspace.ts b/packages/yarnpkg-core/sources/Workspace.ts index 7926e15b2e1b..017a7488d349 100644 --- a/packages/yarnpkg-core/sources/Workspace.ts +++ b/packages/yarnpkg-core/sources/Workspace.ts @@ -1,11 +1,11 @@ import {PortablePath, npath, ppath, xfs, Filename} from '@yarnpkg/fslib'; import globby from 'globby'; -import semver from 'semver'; import {HardDependencies, Manifest} from './Manifest'; import {Project} from './Project'; import {WorkspaceResolver} from './WorkspaceResolver'; import * as hashUtils from './hashUtils'; +import * as semverUtils from './semverUtils'; import * as structUtils from './structUtils'; import {IdentHash} from './types'; import {Descriptor, Locator} from './types'; @@ -101,17 +101,18 @@ export class Workspace { if (protocol === WorkspaceResolver.protocol && pathname === `*`) return true; - if (!semver.validRange(pathname)) + const semverRange = semverUtils.validRange(pathname); + if (!semverRange) return false; if (protocol === WorkspaceResolver.protocol) - return semver.satisfies(this.manifest.version !== null ? this.manifest.version : `0.0.0`, pathname); + return semverRange.test(this.manifest.version ?? `0.0.0`); if (!this.project.configuration.get(`enableTransparentWorkspaces`)) return false; if (this.manifest.version !== null) - return semver.satisfies(this.manifest.version, pathname); + return semverRange.test(this.manifest.version); return false; } diff --git a/packages/yarnpkg-core/sources/semverUtils.ts b/packages/yarnpkg-core/sources/semverUtils.ts index b942f0b962cc..85f345482b55 100644 --- a/packages/yarnpkg-core/sources/semverUtils.ts +++ b/packages/yarnpkg-core/sources/semverUtils.ts @@ -2,6 +2,8 @@ import semver from 'semver'; export {SemVer} from 'semver'; +const satisfiesWithPrereleasesCache = new Map(); + /** * Returns whether the given semver version satisfies the given range. Notably * this supports prerelease versions so that "2.0.0-rc.0" satisfies the range @@ -16,15 +18,22 @@ export {SemVer} from 'semver'; * See https://github.com/yarnpkg/berry/issues/575 for more context. */ export function satisfiesWithPrereleases(version: string | null, range: string, loose: boolean = false): boolean { - let semverRange; - try { - semverRange = new semver.Range(range, {includePrerelease: true, loose}); - } catch (err) { + if (!version) return false; - } - if (!version) + const key = `${range}${loose}`; + let semverRange = satisfiesWithPrereleasesCache.get(key); + if (typeof semverRange === `undefined`) { + try { + semverRange = new semver.Range(range, {includePrerelease: true, loose}); + } catch { + return false; + } finally { + satisfiesWithPrereleasesCache.set(key, semverRange || null); + } + } else if (semverRange === null) { return false; + } let semverVersion: semver.SemVer; try {