@pnpm/building.after-install 1101.0.18 → 1101.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/extendBuildOptions.d.ts +2 -0
- package/lib/index.js +94 -9
- package/package.json +27 -24
|
@@ -41,6 +41,8 @@ export type StrictBuildOptions = {
|
|
|
41
41
|
shamefullyHoist: boolean;
|
|
42
42
|
deployAllFiles: boolean;
|
|
43
43
|
allowBuilds?: Record<string, boolean | string>;
|
|
44
|
+
enableGlobalVirtualStore?: boolean;
|
|
45
|
+
globalVirtualStoreDir?: string;
|
|
44
46
|
virtualStoreDirMaxLength: number;
|
|
45
47
|
peersSuffixMaxLength: number;
|
|
46
48
|
strictStorePkgContentCheck: boolean;
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
+
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import util from 'node:util';
|
|
4
5
|
import { linkBins } from '@pnpm/bins.linker';
|
|
@@ -6,7 +7,7 @@ import { pkgRequiresBuild } from '@pnpm/building.pkg-requires-build';
|
|
|
6
7
|
import { createAllowBuildFunction } from '@pnpm/building.policy';
|
|
7
8
|
import { LAYOUT_VERSION, WANTED_LOCKFILE, } from '@pnpm/constants';
|
|
8
9
|
import { skippedOptionalDependencyLogger } from '@pnpm/core-loggers';
|
|
9
|
-
import { calcDepState, findRuntimeNodeVersion, lockfileToDepGraph } from '@pnpm/deps.graph-hasher';
|
|
10
|
+
import { calcDepState, findRuntimeNodeVersion, iterateHashedGraphNodes, iteratePkgMeta, lockfileToDepGraph } from '@pnpm/deps.graph-hasher';
|
|
10
11
|
import { graphSequencer } from '@pnpm/deps.graph-sequencer';
|
|
11
12
|
import * as dp from '@pnpm/deps.path';
|
|
12
13
|
import { PnpmError } from '@pnpm/error';
|
|
@@ -25,6 +26,10 @@ import pLimit from 'p-limit';
|
|
|
25
26
|
import { runGroups } from 'run-groups';
|
|
26
27
|
import semver from 'semver';
|
|
27
28
|
import { extendBuildOptions, } from './extendBuildOptions.js';
|
|
29
|
+
// Serializes builds of a shared GVS projection across concurrent per-project
|
|
30
|
+
// rebuilds: the first build proceeds, concurrent ones await it and reuse the
|
|
31
|
+
// result. Keyed by the absolute projection directory.
|
|
32
|
+
const gvsBuildLocks = new Map();
|
|
28
33
|
function findPackages(packages, searched, opts) {
|
|
29
34
|
return Object.keys(packages)
|
|
30
35
|
.filter((relativeDepPath) => {
|
|
@@ -38,15 +43,18 @@ function findPackages(packages, searched, opts) {
|
|
|
38
43
|
});
|
|
39
44
|
return false;
|
|
40
45
|
}
|
|
41
|
-
return matches(searched, pkgInfo);
|
|
46
|
+
return matches(searched, pkgInfo, dp.getPkgIdWithPatchHash(relativeDepPath));
|
|
42
47
|
});
|
|
43
48
|
}
|
|
44
49
|
// TODO: move this logic to separate package as this is also used in tree-builder
|
|
45
|
-
function matches(searched, manifest) {
|
|
50
|
+
function matches(searched, manifest, pkgIdWithPatchHash) {
|
|
46
51
|
return searched.some((searchedPkg) => {
|
|
47
52
|
if (typeof searchedPkg === 'string') {
|
|
48
53
|
return manifest.name === searchedPkg;
|
|
49
54
|
}
|
|
55
|
+
if ('pkgIdWithPatchHash' in searchedPkg) {
|
|
56
|
+
return searchedPkg.pkgIdWithPatchHash === pkgIdWithPatchHash;
|
|
57
|
+
}
|
|
50
58
|
return searchedPkg.name === manifest.name && !!manifest.version &&
|
|
51
59
|
semver.satisfies(manifest.version, searchedPkg.range);
|
|
52
60
|
});
|
|
@@ -62,6 +70,9 @@ export async function buildSelectedPkgs(projects, pkgSpecs, maybeOpts) {
|
|
|
62
70
|
return {};
|
|
63
71
|
const packages = ctx.currentLockfile.packages;
|
|
64
72
|
const searched = pkgSpecs.map((arg) => {
|
|
73
|
+
if (matchesDepPath(packages, arg)) {
|
|
74
|
+
return { pkgIdWithPatchHash: dp.removePeersSuffix(arg) };
|
|
75
|
+
}
|
|
65
76
|
const { fetchSpec, name, raw, type } = npa(arg);
|
|
66
77
|
if (raw === name) {
|
|
67
78
|
return name;
|
|
@@ -107,6 +118,10 @@ export async function buildSelectedPkgs(projects, pkgSpecs, maybeOpts) {
|
|
|
107
118
|
ignoredBuilds: ignoredPkgs,
|
|
108
119
|
};
|
|
109
120
|
}
|
|
121
|
+
function matchesDepPath(packages, pkgSpec) {
|
|
122
|
+
const normalizedPkgSpec = dp.removePeersSuffix(pkgSpec);
|
|
123
|
+
return Object.keys(packages).some((depPath) => dp.removePeersSuffix(depPath) === normalizedPkgSpec);
|
|
124
|
+
}
|
|
110
125
|
export async function buildProjects(projects, maybeOpts) {
|
|
111
126
|
const reporter = maybeOpts?.reporter;
|
|
112
127
|
if ((reporter != null) && typeof reporter === 'function') {
|
|
@@ -217,8 +232,8 @@ async function _rebuild(ctx, opts) {
|
|
|
217
232
|
};
|
|
218
233
|
const ignoredPkgs = new Set();
|
|
219
234
|
const _allowBuild = createAllowBuildFunction(opts) ?? (() => undefined);
|
|
220
|
-
const allowBuild = (
|
|
221
|
-
switch (_allowBuild(
|
|
235
|
+
const allowBuild = (depPath) => {
|
|
236
|
+
switch (_allowBuild(depPath)) {
|
|
222
237
|
case true: return true;
|
|
223
238
|
case undefined: {
|
|
224
239
|
ignoredPkgs.add(depPath);
|
|
@@ -229,12 +244,28 @@ async function _rebuild(ctx, opts) {
|
|
|
229
244
|
};
|
|
230
245
|
const builtDepPaths = new Set();
|
|
231
246
|
const storeIndex = opts.skipIfHasSideEffectsCache ? new StoreIndex(opts.storeDir) : undefined;
|
|
247
|
+
// Under GVS, packages live at `<globalVirtualStoreDir>/<hash>/node_modules/<name>`,
|
|
248
|
+
// not the classic virtualStoreDir layout. The hash is computed with the same inputs
|
|
249
|
+
// as the installer so rebuild resolves the exact directory the install created.
|
|
250
|
+
const gvsDirByDepPath = new Map();
|
|
251
|
+
if (opts.enableGlobalVirtualStore) {
|
|
252
|
+
const globalVirtualStoreDir = opts.globalVirtualStoreDir ?? path.join(opts.storeDir, 'links');
|
|
253
|
+
for (const { hash, pkgMeta } of iterateHashedGraphNodes(depGraph, iteratePkgMeta(ctx.currentLockfile, depGraph), _allowBuild, opts.supportedArchitectures, nodeVersion)) {
|
|
254
|
+
const preferredGvsDir = path.join(globalVirtualStoreDir, hash);
|
|
255
|
+
gvsDirByDepPath.set(pkgMeta.depPath, fs.existsSync(preferredGvsDir)
|
|
256
|
+
? preferredGvsDir
|
|
257
|
+
: findLinkedGvsDir(pkgMeta.name, Object.values(ctx.projects), globalVirtualStoreDir) ?? preferredGvsDir);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const pkgModulesDir = (depPath) => gvsDirByDepPath.has(depPath)
|
|
261
|
+
? path.join(gvsDirByDepPath.get(depPath), 'node_modules')
|
|
262
|
+
: path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules');
|
|
232
263
|
const groups = chunks.map((chunk) => chunk.filter((depPath) => ctx.pkgsToRebuild.has(depPath) && !ctx.skipped.has(depPath)).map((depPath) => async () => {
|
|
233
264
|
const pkgSnapshot = pkgSnapshots[depPath];
|
|
234
265
|
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
235
266
|
const pkgRoots = opts.nodeLinker === 'hoisted'
|
|
236
267
|
? (ctx.modulesFile?.hoistedLocations?.[depPath] ?? []).map((hoistedLocation) => path.join(opts.lockfileDir, hoistedLocation))
|
|
237
|
-
: [path.join(
|
|
268
|
+
: [path.join(pkgModulesDir(depPath), pkgInfo.name)];
|
|
238
269
|
if (pkgRoots.length === 0) {
|
|
239
270
|
if (pkgSnapshot.optional)
|
|
240
271
|
return;
|
|
@@ -243,10 +274,32 @@ async function _rebuild(ctx, opts) {
|
|
|
243
274
|
});
|
|
244
275
|
}
|
|
245
276
|
const pkgRoot = pkgRoots[0];
|
|
277
|
+
// If another project is already building this shared projection, wait for it
|
|
278
|
+
// and reuse the result instead of racing on the same directory.
|
|
279
|
+
const gvsDir = gvsDirByDepPath.get(depPath);
|
|
280
|
+
if (gvsDir != null) {
|
|
281
|
+
const inFlight = gvsBuildLocks.get(gvsDir);
|
|
282
|
+
if (inFlight != null) {
|
|
283
|
+
await inFlight.catch(() => { });
|
|
284
|
+
pkgsThatWereRebuilt.add(depPath);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let releaseGvsLock;
|
|
289
|
+
if (gvsDir != null) {
|
|
290
|
+
let resolveLock;
|
|
291
|
+
gvsBuildLocks.set(gvsDir, new Promise((resolve) => {
|
|
292
|
+
resolveLock = resolve;
|
|
293
|
+
}));
|
|
294
|
+
releaseGvsLock = () => {
|
|
295
|
+
gvsBuildLocks.delete(gvsDir);
|
|
296
|
+
resolveLock();
|
|
297
|
+
};
|
|
298
|
+
}
|
|
246
299
|
try {
|
|
247
300
|
const extraBinPaths = ctx.extraBinPaths;
|
|
248
301
|
if (opts.nodeLinker !== 'hoisted') {
|
|
249
|
-
const modules =
|
|
302
|
+
const modules = pkgModulesDir(depPath);
|
|
250
303
|
const binPath = path.join(pkgRoot, 'node_modules', '.bin');
|
|
251
304
|
await linkBins(modules, binPath, { extraNodePaths: ctx.extraNodePaths, warn });
|
|
252
305
|
}
|
|
@@ -281,7 +334,7 @@ async function _rebuild(ctx, opts) {
|
|
|
281
334
|
// However, currently rebuild doesn't work for such packages at all, which should be fixed.
|
|
282
335
|
requiresBuild = pkgRequiresBuild(pgkManifest, new Map());
|
|
283
336
|
}
|
|
284
|
-
const hasSideEffects = requiresBuild && allowBuild(
|
|
337
|
+
const hasSideEffects = requiresBuild && allowBuild(depPath) && await runPostinstallHooks({
|
|
285
338
|
depPath,
|
|
286
339
|
extraBinPaths,
|
|
287
340
|
extraEnv: opts.extraEnv,
|
|
@@ -337,6 +390,9 @@ async function _rebuild(ctx, opts) {
|
|
|
337
390
|
}
|
|
338
391
|
throw err;
|
|
339
392
|
}
|
|
393
|
+
finally {
|
|
394
|
+
releaseGvsLock?.();
|
|
395
|
+
}
|
|
340
396
|
if (pkgRoots.length > 1) {
|
|
341
397
|
await hardLinkDir(pkgRoot, pkgRoots.slice(1));
|
|
342
398
|
}
|
|
@@ -351,7 +407,7 @@ async function _rebuild(ctx, opts) {
|
|
|
351
407
|
.map(async (depPath) => limitLinking(async () => {
|
|
352
408
|
const pkgSnapshot = pkgSnapshots[depPath];
|
|
353
409
|
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot);
|
|
354
|
-
const modules =
|
|
410
|
+
const modules = pkgModulesDir(depPath);
|
|
355
411
|
const binPath = path.join(modules, pkgInfo.name, 'node_modules', '.bin');
|
|
356
412
|
return linkBins(modules, binPath, { warn });
|
|
357
413
|
})));
|
|
@@ -366,6 +422,35 @@ async function _rebuild(ctx, opts) {
|
|
|
366
422
|
}
|
|
367
423
|
return { pkgsThatWereRebuilt, ignoredPkgs };
|
|
368
424
|
}
|
|
425
|
+
// TODO: delete once rebuild relocates GVS projections to the newly computed
|
|
426
|
+
// hash instead of building in place (https://github.com/pnpm/pnpm/issues/12302).
|
|
427
|
+
function findLinkedGvsDir(pkgName, projects, globalVirtualStoreDir) {
|
|
428
|
+
const normalizedGvsRoot = `${path.resolve(globalVirtualStoreDir)}${path.sep}`;
|
|
429
|
+
for (const { rootDir } of projects) {
|
|
430
|
+
const pkgLink = path.join(rootDir, 'node_modules', pkgName);
|
|
431
|
+
try {
|
|
432
|
+
const target = fs.readlinkSync(pkgLink);
|
|
433
|
+
const pkgRoot = path.resolve(path.dirname(pkgLink), target);
|
|
434
|
+
if (!pkgRoot.startsWith(normalizedGvsRoot))
|
|
435
|
+
continue;
|
|
436
|
+
return nthAncestorDir(pkgRoot, pkgName.split('/').length + 1);
|
|
437
|
+
}
|
|
438
|
+
catch (err) {
|
|
439
|
+
// EINVAL: pkgLink exists but is not a symlink.
|
|
440
|
+
if (util.types.isNativeError(err) && 'code' in err && (err.code === 'EINVAL' || err.code === 'ENOENT'))
|
|
441
|
+
continue;
|
|
442
|
+
throw err;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
function nthAncestorDir(dir, levels) {
|
|
448
|
+
let result = dir;
|
|
449
|
+
for (let i = 0; i < levels; i++) {
|
|
450
|
+
result = path.dirname(result);
|
|
451
|
+
}
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
369
454
|
function binDirsInAllParentDirs(pkgRoot, lockfileDir) {
|
|
370
455
|
const binDirs = [];
|
|
371
456
|
let dir = pkgRoot;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/building.after-install",
|
|
3
|
-
"version": "1101.0.
|
|
3
|
+
"version": "1101.0.20",
|
|
4
4
|
"description": "Rebuild packages that are already installed by running their lifecycle scripts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"funding": "https://opencollective.com/pnpm",
|
|
12
|
-
"repository":
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/pnpm/pnpm/tree/main/building/after-install"
|
|
15
|
+
},
|
|
13
16
|
"homepage": "https://github.com/pnpm/pnpm/tree/main/building/after-install#readme",
|
|
14
17
|
"bugs": {
|
|
15
18
|
"url": "https://github.com/pnpm/pnpm/issues"
|
|
@@ -30,37 +33,37 @@
|
|
|
30
33
|
"p-limit": "^7.3.0",
|
|
31
34
|
"run-groups": "^5.0.0",
|
|
32
35
|
"semver": "^7.8.1",
|
|
33
|
-
"@pnpm/
|
|
34
|
-
"@pnpm/building.policy": "1100.0.
|
|
35
|
-
"@pnpm/
|
|
36
|
-
"@pnpm/
|
|
37
|
-
"@pnpm/
|
|
38
|
-
"@pnpm/constants": "1100.0.0",
|
|
39
|
-
"@pnpm/core-loggers": "1100.1.2",
|
|
40
|
-
"@pnpm/deps.graph-hasher": "1100.2.2",
|
|
36
|
+
"@pnpm/bins.linker": "1100.0.12",
|
|
37
|
+
"@pnpm/building.policy": "1100.0.9",
|
|
38
|
+
"@pnpm/config.reader": "1101.7.0",
|
|
39
|
+
"@pnpm/core-loggers": "1100.1.4",
|
|
40
|
+
"@pnpm/deps.graph-hasher": "1100.2.4",
|
|
41
41
|
"@pnpm/deps.graph-sequencer": "1100.0.0",
|
|
42
|
-
"@pnpm/
|
|
42
|
+
"@pnpm/constants": "1100.0.0",
|
|
43
|
+
"@pnpm/exec.lifecycle": "1100.0.16",
|
|
43
44
|
"@pnpm/error": "1100.0.0",
|
|
44
|
-
"@pnpm/
|
|
45
|
-
"@pnpm/installing.context": "1100.0.
|
|
46
|
-
"@pnpm/
|
|
47
|
-
"@pnpm/lockfile.types": "1100.0.
|
|
48
|
-
"@pnpm/
|
|
49
|
-
"@pnpm/
|
|
50
|
-
"@pnpm/
|
|
51
|
-
"@pnpm/store.
|
|
52
|
-
"@pnpm/store.controller-types": "1100.1.
|
|
53
|
-
"@pnpm/
|
|
45
|
+
"@pnpm/deps.path": "1100.0.7",
|
|
46
|
+
"@pnpm/installing.context": "1100.0.16",
|
|
47
|
+
"@pnpm/lockfile.utils": "1100.0.12",
|
|
48
|
+
"@pnpm/lockfile.types": "1100.0.10",
|
|
49
|
+
"@pnpm/pkg-manifest.reader": "1100.0.7",
|
|
50
|
+
"@pnpm/config.normalize-registries": "1100.0.7",
|
|
51
|
+
"@pnpm/store.connection-manager": "1100.2.7",
|
|
52
|
+
"@pnpm/store.cafs": "1100.1.9",
|
|
53
|
+
"@pnpm/store.controller-types": "1100.1.4",
|
|
54
|
+
"@pnpm/types": "1101.3.1",
|
|
55
|
+
"@pnpm/installing.modules-yaml": "1100.0.8",
|
|
54
56
|
"@pnpm/store.index": "1100.1.0",
|
|
55
|
-
"@pnpm/
|
|
57
|
+
"@pnpm/building.pkg-requires-build": "1100.0.7",
|
|
58
|
+
"@pnpm/lockfile.walker": "1100.0.10"
|
|
56
59
|
},
|
|
57
60
|
"peerDependencies": {
|
|
58
61
|
"@pnpm/logger": "^1001.0.1",
|
|
59
|
-
"@pnpm/worker": "^1100.1.
|
|
62
|
+
"@pnpm/worker": "^1100.1.10"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
65
|
"@types/semver": "7.7.1",
|
|
63
|
-
"@pnpm/building.after-install": "1101.0.
|
|
66
|
+
"@pnpm/building.after-install": "1101.0.20"
|
|
64
67
|
},
|
|
65
68
|
"engines": {
|
|
66
69
|
"node": ">=22.13"
|