Skip to content

Commit 185da44

Browse files
authored
fix: streamline the cache exploration (#135)
If a large number of versions are cached on the host system, trying to read the whole cache at once can be quite memory intensive. Instead, we can use an iterative approach that doesn't hold the whole directory content in memory.
1 parent 29da06c commit 185da44

File tree

1 file changed

+19
-14
lines changed

1 file changed

+19
-14
lines changed

sources/corepackUtils.ts

+19-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import {once} from 'events';
12
import fs from 'fs';
3+
import type {Dir} from 'fs';
24
import path from 'path';
35
import semver from 'semver';
46

@@ -45,30 +47,35 @@ export async function fetchAvailableVersions(spec: RegistrySpec): Promise<Array<
4547
export async function findInstalledVersion(installTarget: string, descriptor: Descriptor) {
4648
const installFolder = path.join(installTarget, descriptor.name);
4749

48-
let folderContent: Array<string>;
50+
let cacheDirectory: Dir;
4951
try {
50-
folderContent = await fs.promises.readdir(installFolder);
52+
cacheDirectory = await fs.promises.opendir(installFolder);
5153
} catch (error) {
5254
if ((error as nodeUtils.NodeError).code === `ENOENT`) {
53-
folderContent = [];
55+
return null;
5456
} else {
5557
throw error;
5658
}
5759
}
5860

59-
const candidateVersions: Array<string> = [];
60-
for (const entry of folderContent) {
61+
const range = new semver.Range(descriptor.range);
62+
let bestMatch: string | null = null;
63+
let maxSV: semver.SemVer | undefined = undefined;
64+
65+
for await (const {name} of cacheDirectory) {
6166
// Some dot-folders tend to pop inside directories, especially on OSX
62-
if (entry.startsWith(`.`))
67+
if (name.startsWith(`.`))
6368
continue;
6469

65-
candidateVersions.push(entry);
70+
// If the dirname correspond to an in-range version and is not lower than
71+
// the previous best match (or if there is not yet a previous best match),
72+
// it's our new best match.
73+
if (range.test(name) && maxSV?.compare(name) !== 1) {
74+
bestMatch = name;
75+
maxSV = new semver.SemVer(bestMatch);
76+
}
6677
}
6778

68-
const bestMatch = semver.maxSatisfying(candidateVersions, descriptor.range);
69-
if (bestMatch === null)
70-
return null;
71-
7279
return bestMatch;
7380
}
7481

@@ -106,9 +113,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s
106113

107114
stream.pipe(sendTo);
108115

109-
await new Promise(resolve => {
110-
sendTo.on(`finish`, resolve);
111-
});
116+
await once(sendTo, `finish`);
112117

113118
await fs.promises.mkdir(path.dirname(installFolder), {recursive: true});
114119
try {

0 commit comments

Comments
 (0)