@pnpm/exec.lifecycle 1100.1.14 → 1100.1.16
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @pnpm/lifecycle
|
|
2
2
|
|
|
3
|
+
## 1100.1.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed argument forwarding on Windows when `shellEmulator` is enabled. Paths ending in a backslash, line breaks, and literal shell expressions are preserved [pnpm/pnpm#14548](https://github.com/pnpm/pnpm/issues/14548).
|
|
8
|
+
|
|
9
|
+
- Updated dependencies:
|
|
10
|
+
- @pnpm/bins.linker@1100.0.30
|
|
11
|
+
- @pnpm/error@1100.1.4
|
|
12
|
+
- @pnpm/fetching.directory-fetcher@1100.0.32
|
|
13
|
+
- @pnpm/pkg-manifest.reader@1100.0.19
|
|
14
|
+
- @pnpm/workspace.task-scheduler@1100.0.1
|
|
15
|
+
|
|
16
|
+
## 1100.1.15
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Workspace install, rebuild, pack, publish, stage, and lifecycle work now starts as soon as its dependencies finish instead of waiting for an unrelated topological group.
|
|
21
|
+
|
|
22
|
+
- Fixed recursive `run` cleanup on Windows when a lifecycle script fails while another script's process tree is still running.
|
|
23
|
+
|
|
24
|
+
- Updated dependencies:
|
|
25
|
+
- @pnpm/bins.linker@1100.0.29
|
|
26
|
+
- @pnpm/core-loggers@1100.3.4
|
|
27
|
+
- @pnpm/fetching.directory-fetcher@1100.0.31
|
|
28
|
+
- @pnpm/pkg-manifest.reader@1100.0.18
|
|
29
|
+
- @pnpm/store.cafs-types@1100.1.0
|
|
30
|
+
- @pnpm/store.controller-types@1101.2.0
|
|
31
|
+
- @pnpm/types@1102.1.0
|
|
32
|
+
|
|
3
33
|
## 1100.1.14
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/lib/runLifecycleHook.js
CHANGED
|
@@ -7,6 +7,7 @@ import { lifecycle } from '@pnpm/npm-lifecycle';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import isWindows from 'is-windows';
|
|
9
9
|
import { join as shellQuote } from 'shlex';
|
|
10
|
+
import { trackChildProcess } from './trackChildProcess.js';
|
|
10
11
|
function noop() { } // eslint-disable-line:no-empty
|
|
11
12
|
export async function runLifecycleHook(stage, manifest, opts) {
|
|
12
13
|
const optional = opts.optional === true;
|
|
@@ -61,7 +62,7 @@ Please unset the scriptShell option, or configure it to a .exe instead.
|
|
|
61
62
|
}
|
|
62
63
|
if (opts.args?.length && m.scripts?.[stage]) {
|
|
63
64
|
// It is impossible to quote a command line argument that contains newline for Windows cmd.
|
|
64
|
-
const escapedArgs = isWindows()
|
|
65
|
+
const escapedArgs = isWindows() && !opts.shellEmulator
|
|
65
66
|
? opts.args.map((arg) => JSON.stringify(arg)).join(' ')
|
|
66
67
|
: shellQuote(opts.args);
|
|
67
68
|
m.scripts[stage] = `${m.scripts[stage]} ${escapedArgs}`;
|
|
@@ -108,6 +109,7 @@ Please unset the scriptShell option, or configure it to a .exe instead.
|
|
|
108
109
|
globalWarn(msg.join(' '));
|
|
109
110
|
},
|
|
110
111
|
},
|
|
112
|
+
onSpawn: trackChildProcess,
|
|
111
113
|
runConcurrently: true,
|
|
112
114
|
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
|
113
115
|
scriptShell: opts.scriptShell,
|
|
@@ -15,4 +15,10 @@ export interface Importer {
|
|
|
15
15
|
stages?: string[];
|
|
16
16
|
targetDirs?: string[];
|
|
17
17
|
}
|
|
18
|
-
export declare function runLifecycleHooksConcurrently(
|
|
18
|
+
export declare function runLifecycleHooksConcurrently(params: {
|
|
19
|
+
childConcurrency: number;
|
|
20
|
+
importers: Importer[];
|
|
21
|
+
opts: RunLifecycleHooksConcurrentlyOptions;
|
|
22
|
+
projectDependencies?: Map<ProjectRootDir, ProjectRootDir[]>;
|
|
23
|
+
stages: string[];
|
|
24
|
+
}): Promise<void>;
|
|
@@ -2,66 +2,93 @@ import path from 'node:path';
|
|
|
2
2
|
import { linkBins } from '@pnpm/bins.linker';
|
|
3
3
|
import { fetchFromDir } from '@pnpm/fetching.directory-fetcher';
|
|
4
4
|
import { logger } from '@pnpm/logger';
|
|
5
|
-
import {
|
|
5
|
+
import { scheduleGraph } from '@pnpm/workspace.task-scheduler';
|
|
6
6
|
import { runLifecycleHook } from './runLifecycleHook.js';
|
|
7
|
-
export async function runLifecycleHooksConcurrently(
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
7
|
+
export async function runLifecycleHooksConcurrently(params) {
|
|
8
|
+
const { childConcurrency, importers, opts, projectDependencies, stages } = params;
|
|
9
|
+
const importersByRootDir = new Map(importers.map((importer) => [importer.rootDir, importer]));
|
|
10
|
+
const dependencies = projectDependencies == null
|
|
11
|
+
? dependenciesFromBuildIndexes(importers)
|
|
12
|
+
: new Map(importers.map(({ rootDir }) => [
|
|
13
|
+
rootDir,
|
|
14
|
+
(projectDependencies.get(rootDir) ?? []).filter((dependency) => importersByRootDir.has(dependency)),
|
|
15
|
+
]));
|
|
16
|
+
let firstError;
|
|
17
|
+
await scheduleGraph(dependencies, {
|
|
18
|
+
bail: true,
|
|
19
|
+
concurrency: childConcurrency,
|
|
20
|
+
runNode: async (rootDir) => {
|
|
21
|
+
const { manifest, modulesDir, stages: importerStages, targetDirs } = importersByRootDir.get(rootDir);
|
|
22
|
+
try {
|
|
23
|
+
// We are linking the bin files, in case they were created by lifecycle scripts of other workspace packages.
|
|
24
|
+
await linkBins(modulesDir, path.join(modulesDir, '.bin'), {
|
|
25
|
+
extraNodePaths: opts.extraNodePaths,
|
|
26
|
+
allowExoticManifests: true,
|
|
27
|
+
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
28
|
+
projectManifest: manifest,
|
|
29
|
+
warn: (message) => {
|
|
30
|
+
logger.warn({ message, prefix: rootDir });
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const runLifecycleHookOpts = {
|
|
34
|
+
...opts,
|
|
35
|
+
depPath: rootDir,
|
|
36
|
+
pkgRoot: rootDir,
|
|
37
|
+
rootModulesDir: modulesDir,
|
|
38
|
+
};
|
|
39
|
+
let isBuilt = false;
|
|
40
|
+
for (const stage of (importerStages ?? stages)) {
|
|
41
|
+
if (await runLifecycleHook(stage, manifest, runLifecycleHookOpts)) { // eslint-disable-line no-await-in-loop
|
|
42
|
+
isBuilt = true;
|
|
43
|
+
}
|
|
41
44
|
}
|
|
45
|
+
if (targetDirs == null || targetDirs.length === 0 || !isBuilt)
|
|
46
|
+
return 'passed';
|
|
47
|
+
// Re-import only the freshly-built source — fetchFromDir already
|
|
48
|
+
// excludes the source's node_modules/. `keepModulesDir: true` makes
|
|
49
|
+
// importIndexedDir skip the destructive makeEmptyDir fast path
|
|
50
|
+
// (#11088) and preserve the target's existing node_modules (bin
|
|
51
|
+
// symlinks + transitive deps from the initial install) via its
|
|
52
|
+
// staging/move path. Replaces the old scanDir-into-filesMap
|
|
53
|
+
// workaround (#4299) that the fast path then wiped, causing ENOENT
|
|
54
|
+
// on .bin/<tool>. Stays on storeController.importPackage so source
|
|
55
|
+
// files keep their hardlinks (no copy-loop).
|
|
56
|
+
const filesResponse = await fetchFromDir(rootDir, { resolveSymlinks: opts.resolveSymlinksInInjectedDirs });
|
|
57
|
+
await Promise.all(targetDirs.map(async (targetDir) => opts.storeController.importPackage(targetDir, {
|
|
58
|
+
filesResponse: {
|
|
59
|
+
resolvedFrom: 'local-dir',
|
|
60
|
+
...filesResponse,
|
|
61
|
+
},
|
|
62
|
+
force: false,
|
|
63
|
+
keepModulesDir: true,
|
|
64
|
+
})));
|
|
65
|
+
return 'passed';
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
firstError ??= error;
|
|
69
|
+
return 'aborted';
|
|
42
70
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Re-import only the freshly-built source — fetchFromDir already
|
|
46
|
-
// excludes the source's node_modules/. `keepModulesDir: true` makes
|
|
47
|
-
// importIndexedDir skip the destructive makeEmptyDir fast path
|
|
48
|
-
// (#11088) and preserve the target's existing node_modules (bin
|
|
49
|
-
// symlinks + transitive deps from the initial install) via its
|
|
50
|
-
// staging/move path. Replaces the old scanDir-into-filesMap
|
|
51
|
-
// workaround (#4299) that the fast path then wiped, causing ENOENT
|
|
52
|
-
// on .bin/<tool>. Stays on storeController.importPackage so source
|
|
53
|
-
// files keep their hardlinks (no copy-loop).
|
|
54
|
-
const filesResponse = await fetchFromDir(rootDir, { resolveSymlinks: opts.resolveSymlinksInInjectedDirs });
|
|
55
|
-
await Promise.all(targetDirs.map(async (targetDir) => opts.storeController.importPackage(targetDir, {
|
|
56
|
-
filesResponse: {
|
|
57
|
-
resolvedFrom: 'local-dir',
|
|
58
|
-
...filesResponse,
|
|
59
|
-
},
|
|
60
|
-
force: false,
|
|
61
|
-
keepModulesDir: true,
|
|
62
|
-
})));
|
|
63
|
-
});
|
|
71
|
+
},
|
|
72
|
+
onNodeSkipped: () => { },
|
|
64
73
|
});
|
|
65
|
-
|
|
74
|
+
if (firstError != null)
|
|
75
|
+
throw firstError;
|
|
76
|
+
}
|
|
77
|
+
function dependenciesFromBuildIndexes(importers) {
|
|
78
|
+
const groups = new Map();
|
|
79
|
+
for (const { buildIndex, rootDir } of importers) {
|
|
80
|
+
const group = groups.get(buildIndex) ?? [];
|
|
81
|
+
group.push(rootDir);
|
|
82
|
+
groups.set(buildIndex, group);
|
|
83
|
+
}
|
|
84
|
+
const dependencies = new Map();
|
|
85
|
+
let previous = [];
|
|
86
|
+
for (const buildIndex of [...groups.keys()].sort((left, right) => left - right)) {
|
|
87
|
+
const group = groups.get(buildIndex);
|
|
88
|
+
for (const rootDir of group)
|
|
89
|
+
dependencies.set(rootDir, previous);
|
|
90
|
+
previous = group;
|
|
91
|
+
}
|
|
92
|
+
return dependencies;
|
|
66
93
|
}
|
|
67
94
|
//# sourceMappingURL=runLifecycleHooksConcurrently.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/exec.lifecycle",
|
|
3
|
-
"version": "1100.1.
|
|
3
|
+
"version": "1100.1.16",
|
|
4
4
|
"description": "Package lifecycle hook runner",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -29,19 +29,19 @@
|
|
|
29
29
|
"!*.map"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@pnpm/bins.linker": "1100.0.
|
|
33
|
-
"@pnpm/core-loggers": "1100.3.
|
|
34
|
-
"@pnpm/error": "1100.1.
|
|
35
|
-
"@pnpm/fetching.directory-fetcher": "1100.0.
|
|
36
|
-
"@pnpm/npm-lifecycle": "^1100.
|
|
37
|
-
"@pnpm/pkg-manifest.reader": "1100.0.
|
|
38
|
-
"@pnpm/store.cafs-types": "1100.0
|
|
39
|
-
"@pnpm/store.controller-types": "1101.
|
|
40
|
-
"@pnpm/types": "1102.
|
|
32
|
+
"@pnpm/bins.linker": "1100.0.30",
|
|
33
|
+
"@pnpm/core-loggers": "1100.3.4",
|
|
34
|
+
"@pnpm/error": "1100.1.4",
|
|
35
|
+
"@pnpm/fetching.directory-fetcher": "1100.0.32",
|
|
36
|
+
"@pnpm/npm-lifecycle": "^1100.1.0",
|
|
37
|
+
"@pnpm/pkg-manifest.reader": "1100.0.19",
|
|
38
|
+
"@pnpm/store.cafs-types": "1100.1.0",
|
|
39
|
+
"@pnpm/store.controller-types": "1101.2.0",
|
|
40
|
+
"@pnpm/types": "1102.1.0",
|
|
41
|
+
"@pnpm/workspace.task-scheduler": "1100.0.1",
|
|
41
42
|
"chalk": "^6.0.0",
|
|
42
43
|
"is-windows": "^1.0.2",
|
|
43
44
|
"path-exists": "^5.0.0",
|
|
44
|
-
"run-groups": "^5.0.0",
|
|
45
45
|
"shlex": "^3.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@jest/globals": "30.4.1",
|
|
52
|
-
"@pnpm/exec.lifecycle": "1100.1.
|
|
52
|
+
"@pnpm/exec.lifecycle": "1100.1.16",
|
|
53
53
|
"@pnpm/logger": "1100.0.0",
|
|
54
|
-
"@pnpm/prepare": "1100.0.
|
|
54
|
+
"@pnpm/prepare": "1100.0.29",
|
|
55
55
|
"@pnpm/test-fixtures": "1100.0.1",
|
|
56
56
|
"@pnpm/test-ipc-server": "1100.0.0",
|
|
57
57
|
"@types/is-windows": "^1.0.2",
|