Skip to content

Commit e7ad533

Browse files
authored
fix: selectively import required semver functions (#511)
By selectively importing only the functions and classes we use, we avoid bundling in the entire semver package, and shave a few kB off from the Corepack bundle.
1 parent 004c028 commit e7ad533

File tree

6 files changed

+32
-24
lines changed

6 files changed

+32
-24
lines changed

genlist.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {readFileSync} from 'fs';
2-
import semver from 'semver';
2+
import semverCompare from 'semver/functions/compare';
33

44
const lines = readFileSync(0, `utf8`).split(/\n/).filter(line => line);
55

66
lines.sort((a, b) => {
7-
return semver.compare(a, b);
7+
return semverCompare(a, b);
88
});
99

1010
for (const version of lines)

sources/Engine.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {UsageError} from 'clipanion';
22
import fs from 'fs';
33
import path from 'path';
44
import process from 'process';
5-
import semver from 'semver';
5+
import semverRcompare from 'semver/functions/rcompare';
6+
import semverValid from 'semver/functions/valid';
7+
import semverValidRange from 'semver/ranges/valid';
68

79
import defaultConfig from '../config.json';
810

@@ -334,7 +336,7 @@ export class Engine {
334336
throw new UsageError(`This package manager (${descriptor.name}) isn't supported by this corepack build`);
335337

336338
let finalDescriptor = descriptor;
337-
if (!semver.valid(descriptor.range) && !semver.validRange(descriptor.range)) {
339+
if (!semverValid(descriptor.range) && !semverValidRange(descriptor.range)) {
338340
if (!allowTags)
339341
throw new UsageError(`Packages managers can't be referenced via tags in this context`);
340342

@@ -363,7 +365,7 @@ export class Engine {
363365

364366
// If the user asked for a specific version, no need to request the list of
365367
// available versions from the registry.
366-
if (semver.valid(finalDescriptor.range))
368+
if (semverValid(finalDescriptor.range))
367369
return {name: finalDescriptor.name, reference: finalDescriptor.range};
368370

369371
const versions = await Promise.all(Object.keys(definition.ranges).map(async range => {
@@ -374,7 +376,7 @@ export class Engine {
374376
return versions.filter(version => semverUtils.satisfiesWithPrereleases(version, finalDescriptor.range));
375377
}));
376378

377-
const highestVersion = [...new Set(versions.flat())].sort(semver.rcompare);
379+
const highestVersion = [...new Set(versions.flat())].sort(semverRcompare);
378380
if (highestVersion.length === 0)
379381
return null;
380382

sources/commands/Up.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {Command, UsageError} from 'clipanion';
2-
import semver from 'semver';
2+
import semverMajor from 'semver/functions/major';
3+
import semverValid from 'semver/functions/valid';
4+
import semverValidRange from 'semver/ranges/valid';
35

46
import type {SupportedPackageManagers} from '../types';
57

@@ -33,14 +35,14 @@ export class UpCommand extends BaseCommand {
3335
patterns: [],
3436
});
3537

36-
if (!semver.valid(descriptor.range) && !semver.validRange(descriptor.range))
38+
if (!semverValid(descriptor.range) && !semverValidRange(descriptor.range))
3739
throw new UsageError(`The 'corepack up' command can only be used when your project's packageManager field is set to a semver version or semver range`);
3840

3941
const resolved = await this.context.engine.resolveDescriptor(descriptor, {useCache: false});
4042
if (!resolved)
4143
throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`);
4244

43-
const majorVersion = semver.major(resolved.reference);
45+
const majorVersion = semverMajor(resolved.reference);
4446
const majorDescriptor = {name: descriptor.name as SupportedPackageManagers, range: `^${majorVersion}.0.0`};
4547

4648
const highestVersion = await this.context.engine.resolveDescriptor(majorDescriptor, {useCache: false});

sources/corepackUtils.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import fs from 'fs';
44
import type {Dir} from 'fs';
55
import Module from 'module';
66
import path from 'path';
7-
import semver from 'semver';
7+
import Range from 'semver/classes/range';
8+
import SemVer from 'semver/classes/semver';
9+
import semverLt from 'semver/functions/lt';
10+
import semverParse from 'semver/functions/parse';
811
import {setTimeout as setTimeoutPromise} from 'timers/promises';
912

1013
import * as engine from './Engine';
@@ -82,9 +85,9 @@ export async function findInstalledVersion(installTarget: string, descriptor: De
8285
}
8386
}
8487

85-
const range = new semver.Range(descriptor.range);
88+
const range = new Range(descriptor.range);
8689
let bestMatch: string | null = null;
87-
let maxSV: semver.SemVer | undefined = undefined;
90+
let maxSV: SemVer | undefined = undefined;
8891

8992
for await (const {name} of cacheDirectory) {
9093
// Some dot-folders tend to pop inside directories, especially on OSX
@@ -97,7 +100,7 @@ export async function findInstalledVersion(installTarget: string, descriptor: De
97100
// @ts-expect-error TODO: decipher why this produces an error
98101
if (range.test(name) && maxSV?.compare(name) !== 1) {
99102
bestMatch = name;
100-
maxSV = new semver.SemVer(bestMatch);
103+
maxSV = new SemVer(bestMatch);
101104
}
102105
}
103106

@@ -193,7 +196,7 @@ async function download(installTarget: string, url: string, algo: string, binPat
193196

194197
export async function installVersion(installTarget: string, locator: Locator, {spec}: {spec: PackageManagerSpec}): Promise<InstallSpec> {
195198
const locatorIsASupportedPackageManager = isSupportedPackageManagerLocator(locator);
196-
const locatorReference = locatorIsASupportedPackageManager ? semver.parse(locator.reference)! : parseURLReference(locator);
199+
const locatorReference = locatorIsASupportedPackageManager ? semverParse(locator.reference)! : parseURLReference(locator);
197200
const {version, build} = locatorReference;
198201

199202
const installFolder = path.join(installTarget, locator.name, version);
@@ -326,9 +329,9 @@ export async function installVersion(installTarget: string, locator: Locator, {s
326329
const lastKnownGood = await engine.getLastKnownGood();
327330
const defaultVersion = engine.getLastKnownGoodFromFileContent(lastKnownGood, locator.name);
328331
if (defaultVersion) {
329-
const currentDefault = semver.parse(defaultVersion)!;
330-
const downloadedVersion = locatorReference as semver.SemVer;
331-
if (currentDefault.major === downloadedVersion.major && semver.lt(currentDefault, downloadedVersion)) {
332+
const currentDefault = semverParse(defaultVersion)!;
333+
const downloadedVersion = locatorReference as SemVer;
334+
if (currentDefault.major === downloadedVersion.major && semverLt(currentDefault, downloadedVersion)) {
332335
await engine.activatePackageManager(lastKnownGood, locator);
333336
}
334337
}
@@ -404,7 +407,7 @@ export async function runVersion(locator: Locator, installSpec: InstallSpec & {s
404407
// Node.js segfaults when using npm@>=9.7.0 and v8-compile-cache
405408
// $ docker run -it node:20.3.0-slim corepack npm@9.7.1 --version
406409
// [SIGSEGV]
407-
if (locator.name !== `npm` || semver.lt(locator.reference, `9.7.0`))
410+
if (locator.name !== `npm` || semverLt(locator.reference, `9.7.0`))
408411
// @ts-expect-error - No types
409412
await import(`v8-compile-cache`);
410413

sources/semverUtils.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import semver from 'semver';
1+
import Range from 'semver/classes/range';
2+
import SemVer from 'semver/classes/semver';
23

34
/**
45
* Returns whether the given semver version satisfies the given range. Notably
@@ -16,17 +17,17 @@ import semver from 'semver';
1617
export function satisfiesWithPrereleases(version: string | null, range: string, loose: boolean = false): boolean {
1718
let semverRange;
1819
try {
19-
semverRange = new semver.Range(range, loose);
20+
semverRange = new Range(range, loose);
2021
} catch (err) {
2122
return false;
2223
}
2324

2425
if (!version)
2526
return false;
2627

27-
let semverVersion: semver.SemVer;
28+
let semverVersion: SemVer;
2829
try {
29-
semverVersion = new semver.SemVer(version, semverRange.loose);
30+
semverVersion = new SemVer(version, semverRange.loose);
3031
if (semverVersion.prerelease) {
3132
semverVersion.prerelease = [];
3233
}

sources/specUtils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {UsageError} from 'clipanion';
22
import fs from 'fs';
33
import path from 'path';
4-
import semver from 'semver';
4+
import semverValid from 'semver/functions/valid';
55

66
import {PreparedPackageManagerInfo} from './Engine';
77
import {NodeError} from './nodeUtils';
@@ -34,7 +34,7 @@ export function parseSpec(raw: unknown, source: string, {enforceExactVersion = t
3434

3535
const isURL = URL.canParse(range);
3636
if (!isURL) {
37-
if (enforceExactVersion && !semver.valid(range))
37+
if (enforceExactVersion && !semverValid(range))
3838
throw new UsageError(`Invalid package manager specification in ${source} (${raw}); expected a semver version${enforceExactVersion ? `` : `, range, or tag`}`);
3939

4040
if (!isSupportedPackageManager(name)) {

0 commit comments

Comments
 (0)