Skip to content

Commit af927a3

Browse files
authored
perf: cache more semver ranges (#2890)
1 parent 9c7bd33 commit af927a3

File tree

10 files changed

+70
-30
lines changed

10 files changed

+70
-30
lines changed

.yarn/versions/76e603e2.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
releases:
2+
"@yarnpkg/cli": patch
3+
"@yarnpkg/core": patch
4+
"@yarnpkg/plugin-essentials": patch
5+
"@yarnpkg/plugin-git": patch
6+
"@yarnpkg/plugin-npm-cli": patch
7+
"@yarnpkg/plugin-pnp": patch
8+
"@yarnpkg/plugin-typescript": patch
9+
10+
declined:
11+
- "@yarnpkg/plugin-compat"
12+
- "@yarnpkg/plugin-constraints"
13+
- "@yarnpkg/plugin-dlx"
14+
- "@yarnpkg/plugin-exec"
15+
- "@yarnpkg/plugin-file"
16+
- "@yarnpkg/plugin-github"
17+
- "@yarnpkg/plugin-http"
18+
- "@yarnpkg/plugin-init"
19+
- "@yarnpkg/plugin-interactive-tools"
20+
- "@yarnpkg/plugin-link"
21+
- "@yarnpkg/plugin-node-modules"
22+
- "@yarnpkg/plugin-npm"
23+
- "@yarnpkg/plugin-pack"
24+
- "@yarnpkg/plugin-patch"
25+
- "@yarnpkg/plugin-stage"
26+
- "@yarnpkg/plugin-version"
27+
- "@yarnpkg/plugin-workspace-tools"
28+
- "@yarnpkg/builder"
29+
- "@yarnpkg/doctor"
30+
- "@yarnpkg/pnpify"

packages/plugin-essentials/sources/commands/set/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default class SetVersionCommand extends BaseCommand {
5353
bundleUrl = `https://github.com/yarnpkg/berry/raw/%40yarnpkg/cli/${this.version}/packages/yarnpkg-cli/bin/yarn.js`;
5454
else if (semverUtils.satisfiesWithPrereleases(this.version, `^0.x || ^1.x`))
5555
bundleUrl = `https://github.com/yarnpkg/yarn/releases/download/v${this.version}/yarn-${this.version}.js`;
56-
else if (semver.validRange(this.version))
56+
else if (semverUtils.validRange(this.version))
5757
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`);
5858
else
5959
throw new UsageError(`Invalid version descriptor "${this.version}"`);

packages/plugin-git/sources/gitUtils.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import {Configuration, Locator, execUtils, structUtils, httpUtils} from '@yarnpkg/core';
2-
import {npath, xfs} from '@yarnpkg/fslib';
3-
import GitUrlParse from 'git-url-parse';
4-
import querystring from 'querystring';
5-
import semver from 'semver';
6-
import urlLib from 'url';
1+
import {Configuration, Locator, execUtils, structUtils, httpUtils, semverUtils} from '@yarnpkg/core';
2+
import {npath, xfs} from '@yarnpkg/fslib';
3+
import GitUrlParse from 'git-url-parse';
4+
import querystring from 'querystring';
5+
import semver from 'semver';
6+
import urlLib from 'url';
77

88
function makeGitEnvironment() {
99
return {
@@ -237,7 +237,8 @@ export async function resolveUrl(url: string, configuration: Configuration) {
237237
}
238238

239239
case TreeishProtocols.Semver: {
240-
if (!semver.validRange(request))
240+
const validRange = semverUtils.validRange(request);
241+
if (!validRange)
241242
throw new Error(`Invalid range ("${request}")`);
242243

243244
const semverTags = new Map([...refs.entries()].filter(([ref]) => {
@@ -248,7 +249,7 @@ export async function resolveUrl(url: string, configuration: Configuration) {
248249
return entry[0] !== null;
249250
}));
250251

251-
const bestVersion = semver.maxSatisfying([...semverTags.keys()], request);
252+
const bestVersion = semver.maxSatisfying([...semverTags.keys()], validRange);
252253
if (bestVersion === null)
253254
throw new Error(`No matching range ("${request}")`);
254255

packages/plugin-npm-cli/sources/commands/npm/info.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as npm from '@npm/types';
22
import {BaseCommand} from '@yarnpkg/cli';
33
import {Project, Configuration, structUtils, Descriptor} from '@yarnpkg/core';
4-
import {StreamReport, MessageName} from '@yarnpkg/core';
4+
import {StreamReport, MessageName, semverUtils} from '@yarnpkg/core';
55
import {npmHttpUtils} from '@yarnpkg/plugin-npm';
66
import {Command, Option, Usage, UsageError} from 'clipanion';
77
import path from 'path';
@@ -127,8 +127,9 @@ export default class InfoCommand extends BaseCommand {
127127

128128
// The latest version that satisfies `descriptor.range` (if it is a valid range), else `fallbackVersion`
129129
let version: string = fallbackVersion;
130-
if (semver.validRange(descriptor.range)) {
131-
const maxSatisfyingVersion = semver.maxSatisfying(versions, descriptor.range);
130+
const validRange = semverUtils.validRange(descriptor.range);
131+
if (validRange) {
132+
const maxSatisfyingVersion = semver.maxSatisfying(versions, validRange);
132133

133134
if (maxSatisfyingVersion !== null) {
134135
version = maxSatisfyingVersion;

packages/plugin-pnp/sources/commands/unplug.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {Cache, Configuration, Project, StreamReport, Package, MessageName, forma
33
import {structUtils, semverUtils} from '@yarnpkg/core';
44
import {Command, Option, Usage, UsageError} from 'clipanion';
55
import micromatch from 'micromatch';
6-
import semver from 'semver';
76

87
import * as pnpUtils from '../pnpUtils';
98

@@ -85,7 +84,7 @@ export default class UnplugCommand extends BaseCommand {
8584
? patternDescriptor
8685
: structUtils.makeDescriptor(patternDescriptor, `*`);
8786

88-
if (!semver.validRange(pseudoDescriptor.range))
87+
if (!semverUtils.validRange(pseudoDescriptor.range))
8988
throw new UsageError(`The range of the descriptor patterns must be a valid semver range (${structUtils.prettyDescriptor(configuration, pseudoDescriptor)})`);
9089

9190
return (pkg: Package) => {

packages/plugin-typescript/sources/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Descriptor, Plugin, Workspace, ResolveOptions, Manifest, AllDependencies, DescriptorHash, Package} from '@yarnpkg/core';
2-
import {structUtils, ThrowReport, miscUtils} from '@yarnpkg/core';
2+
import {structUtils, ThrowReport, miscUtils, semverUtils} from '@yarnpkg/core';
33
import {Hooks as EssentialsHooks} from '@yarnpkg/plugin-essentials';
44
import {suggestUtils} from '@yarnpkg/plugin-essentials';
55
import {Hooks as PackHooks} from '@yarnpkg/plugin-pack';
@@ -41,7 +41,7 @@ const afterWorkspaceDependencyAddition = async (
4141

4242
let range = structUtils.parseRange(descriptor.range).selector;
4343
// If the range is a tag, we have to resolve it into a semver version
44-
if (!semver.validRange(range)) {
44+
if (!semverUtils.validRange(range)) {
4545
const originalCandidates = await resolver.getCandidates(descriptor, new Map<DescriptorHash, Package>(), resolveOptions);
4646
range = structUtils.parseRange(originalCandidates[0].reference).selector;
4747
}

packages/yarnpkg-core/sources/Configuration.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import camelcase
55
import {isCI} from 'ci-info';
66
import {UsageError} from 'clipanion';
77
import pLimit, {Limit} from 'p-limit';
8-
import semver from 'semver';
98
import {PassThrough, Writable} from 'stream';
109

1110
import {CorePlugin} from './CorePlugin';
@@ -1407,7 +1406,7 @@ export class Configuration {
14071406
const packageExtensions = this.packageExtensions;
14081407

14091408
const registerPackageExtension = (descriptor: Descriptor, extensionData: PackageExtensionData, {userProvided = false}: {userProvided?: boolean} = {}) => {
1410-
if (!semver.validRange(descriptor.range))
1409+
if (!semverUtils.validRange(descriptor.range))
14111410
throw new Error(`Only semver ranges are allowed as keys for the lockfileExtensions setting`);
14121411

14131412
const extension = new Manifest();

packages/yarnpkg-core/sources/LegacyMigrationResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {xfs, ppath} from '@yarnpkg/fslib';
22
import {parseSyml} from '@yarnpkg/parsers';
3-
import semver from 'semver';
43

54
import {MessageName} from './MessageName';
65
import {Project} from './Project';
76
import {Report} from './Report';
87
import {Resolver, ResolveOptions, MinimalResolveOptions} from './Resolver';
8+
import * as semverUtils from './semverUtils';
99
import * as structUtils from './structUtils';
1010
import {DescriptorHash, Descriptor, Locator} from './types';
1111

@@ -58,7 +58,7 @@ export class LegacyMigrationResolver implements Resolver {
5858
continue;
5959
}
6060

61-
if (semver.validRange(descriptor.range))
61+
if (semverUtils.validRange(descriptor.range))
6262
descriptor = structUtils.makeDescriptor(descriptor, `npm:${descriptor.range}`);
6363

6464
const {version, resolved} = (parsed as any)[key];

packages/yarnpkg-core/sources/Workspace.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {PortablePath, npath, ppath, xfs, Filename} from '@yarnpkg/fslib';
22
import globby from 'globby';
3-
import semver from 'semver';
43

54
import {HardDependencies, Manifest} from './Manifest';
65
import {Project} from './Project';
76
import {WorkspaceResolver} from './WorkspaceResolver';
87
import * as hashUtils from './hashUtils';
8+
import * as semverUtils from './semverUtils';
99
import * as structUtils from './structUtils';
1010
import {IdentHash} from './types';
1111
import {Descriptor, Locator} from './types';
@@ -101,17 +101,18 @@ export class Workspace {
101101
if (protocol === WorkspaceResolver.protocol && pathname === `*`)
102102
return true;
103103

104-
if (!semver.validRange(pathname))
104+
const semverRange = semverUtils.validRange(pathname);
105+
if (!semverRange)
105106
return false;
106107

107108
if (protocol === WorkspaceResolver.protocol)
108-
return semver.satisfies(this.manifest.version !== null ? this.manifest.version : `0.0.0`, pathname);
109+
return semverRange.test(this.manifest.version ?? `0.0.0`);
109110

110111
if (!this.project.configuration.get(`enableTransparentWorkspaces`))
111112
return false;
112113

113114
if (this.manifest.version !== null)
114-
return semver.satisfies(this.manifest.version, pathname);
115+
return semverRange.test(this.manifest.version);
115116

116117
return false;
117118
}

packages/yarnpkg-core/sources/semverUtils.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import semver from 'semver';
22

33
export {SemVer} from 'semver';
44

5+
const satisfiesWithPrereleasesCache = new Map<string, semver.Range | null>();
6+
57
/**
68
* Returns whether the given semver version satisfies the given range. Notably
79
* this supports prerelease versions so that "2.0.0-rc.0" satisfies the range
@@ -16,15 +18,22 @@ export {SemVer} from 'semver';
1618
* See https://github.com/yarnpkg/berry/issues/575 for more context.
1719
*/
1820
export function satisfiesWithPrereleases(version: string | null, range: string, loose: boolean = false): boolean {
19-
let semverRange;
20-
try {
21-
semverRange = new semver.Range(range, {includePrerelease: true, loose});
22-
} catch (err) {
21+
if (!version)
2322
return false;
24-
}
2523

26-
if (!version)
24+
const key = `${range}${loose}`;
25+
let semverRange = satisfiesWithPrereleasesCache.get(key);
26+
if (typeof semverRange === `undefined`) {
27+
try {
28+
semverRange = new semver.Range(range, {includePrerelease: true, loose});
29+
} catch {
30+
return false;
31+
} finally {
32+
satisfiesWithPrereleasesCache.set(key, semverRange || null);
33+
}
34+
} else if (semverRange === null) {
2735
return false;
36+
}
2837

2938
let semverVersion: semver.SemVer;
3039
try {

0 commit comments

Comments
 (0)