@pnpm/exec.lifecycle 1100.1.2 → 1100.1.4
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 +1679 -0
- package/lib/index.js +2 -1
- package/package.json +16 -16
- package/lib/index.d.ts +0 -10
- package/lib/runLifecycleHook.d.ts +0 -19
- package/lib/runLifecycleHook.js +0 -168
- package/lib/runLifecycleHooksConcurrently.d.ts +0 -18
- package/lib/runLifecycleHooksConcurrently.js +0 -67
package/lib/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { safeReadPackageJsonFromDir } from '@pnpm/pkg-manifest.reader';
|
|
2
2
|
import { runLifecycleHook } from './runLifecycleHook.js';
|
|
3
3
|
import { runLifecycleHooksConcurrently } from './runLifecycleHooksConcurrently.js';
|
|
4
|
+
import { killTrackedProcessTrees, trackChildProcess } from './trackChildProcess.js';
|
|
4
5
|
export function makeNodeRequireOption(modulePath, env) {
|
|
5
6
|
let { NODE_OPTIONS } = env ?? process.env;
|
|
6
7
|
NODE_OPTIONS = `${NODE_OPTIONS ?? process.env.NODE_OPTIONS ?? ''} --require=${quotePathIfNeeded(modulePath)}`.trim();
|
|
@@ -30,7 +31,7 @@ function removeNodePackageMapOption(nodeOptions) {
|
|
|
30
31
|
.replace(/(?:^|\s)--experimental-package-map\s+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, '')
|
|
31
32
|
.trim();
|
|
32
33
|
}
|
|
33
|
-
export { runLifecycleHook, runLifecycleHooksConcurrently, };
|
|
34
|
+
export { killTrackedProcessTrees, runLifecycleHook, runLifecycleHooksConcurrently, trackChildProcess, };
|
|
34
35
|
export async function runPostinstallHooks(opts) {
|
|
35
36
|
const pkg = await safeReadPackageJsonFromDir(opts.pkgRoot);
|
|
36
37
|
if (pkg == null)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/exec.lifecycle",
|
|
3
|
-
"version": "1100.1.
|
|
3
|
+
"version": "1100.1.4",
|
|
4
4
|
"description": "Package lifecycle hook runner",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -29,34 +29,34 @@
|
|
|
29
29
|
"!*.map"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
+
"@pnpm/bins.linker": "1100.0.18",
|
|
33
|
+
"@pnpm/core-loggers": "1100.2.3",
|
|
34
|
+
"@pnpm/error": "1100.0.1",
|
|
35
|
+
"@pnpm/fetching.directory-fetcher": "1100.0.21",
|
|
32
36
|
"@pnpm/npm-lifecycle": "^1100.0.0",
|
|
37
|
+
"@pnpm/pkg-manifest.reader": "1100.0.10",
|
|
38
|
+
"@pnpm/store.cafs-types": "1100.0.1",
|
|
39
|
+
"@pnpm/store.controller-types": "1100.1.8",
|
|
40
|
+
"@pnpm/types": "1101.4.0",
|
|
33
41
|
"chalk": "^5.6.2",
|
|
34
42
|
"is-windows": "^1.0.2",
|
|
35
43
|
"path-exists": "^5.0.0",
|
|
36
44
|
"run-groups": "^5.0.0",
|
|
37
|
-
"shlex": "^3.0.0"
|
|
38
|
-
"@pnpm/bins.linker": "1100.0.16",
|
|
39
|
-
"@pnpm/core-loggers": "1100.2.1",
|
|
40
|
-
"@pnpm/error": "1100.0.1",
|
|
41
|
-
"@pnpm/fetching.directory-fetcher": "1100.0.19",
|
|
42
|
-
"@pnpm/pkg-manifest.reader": "1100.0.9",
|
|
43
|
-
"@pnpm/store.cafs-types": "1100.0.1",
|
|
44
|
-
"@pnpm/store.controller-types": "1100.1.7",
|
|
45
|
-
"@pnpm/types": "1101.3.2"
|
|
45
|
+
"shlex": "^3.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@pnpm/logger": "^1100.0.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@jest/globals": "30.4.1",
|
|
52
|
-
"@
|
|
53
|
-
"@zkochan/rimraf": "^4.0.0",
|
|
54
|
-
"load-json-file": "^7.0.1",
|
|
55
|
-
"@pnpm/exec.lifecycle": "1100.1.2",
|
|
52
|
+
"@pnpm/exec.lifecycle": "1100.1.4",
|
|
56
53
|
"@pnpm/logger": "1100.0.0",
|
|
57
|
-
"@pnpm/prepare": "1100.0.
|
|
54
|
+
"@pnpm/prepare": "1100.0.20",
|
|
55
|
+
"@pnpm/test-fixtures": "1100.0.0",
|
|
58
56
|
"@pnpm/test-ipc-server": "1100.0.0",
|
|
59
|
-
"@
|
|
57
|
+
"@types/is-windows": "^1.0.2",
|
|
58
|
+
"@zkochan/rimraf": "^4.0.0",
|
|
59
|
+
"load-json-file": "^7.0.1"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=22.13"
|
package/lib/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { runLifecycleHook, type RunLifecycleHookOptions } from './runLifecycleHook.js';
|
|
2
|
-
import { runLifecycleHooksConcurrently, type RunLifecycleHooksConcurrentlyOptions } from './runLifecycleHooksConcurrently.js';
|
|
3
|
-
export declare function makeNodeRequireOption(modulePath: string, env?: Record<string, string | undefined>): {
|
|
4
|
-
NODE_OPTIONS: string;
|
|
5
|
-
};
|
|
6
|
-
export declare function makeNodePackageMapOption(packageMapPath: string, env?: Record<string, string | undefined>): {
|
|
7
|
-
NODE_OPTIONS: string;
|
|
8
|
-
};
|
|
9
|
-
export { runLifecycleHook, type RunLifecycleHookOptions, runLifecycleHooksConcurrently, type RunLifecycleHooksConcurrentlyOptions, };
|
|
10
|
-
export declare function runPostinstallHooks(opts: RunLifecycleHookOptions): Promise<boolean>;
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { DependencyManifest, ProjectManifest } from '@pnpm/types';
|
|
2
|
-
export interface RunLifecycleHookOptions {
|
|
3
|
-
args?: string[];
|
|
4
|
-
depPath: string;
|
|
5
|
-
extraBinPaths?: string[];
|
|
6
|
-
extraEnv?: Record<string, string>;
|
|
7
|
-
initCwd?: string;
|
|
8
|
-
optional?: boolean;
|
|
9
|
-
pkgRoot: string;
|
|
10
|
-
rootModulesDir: string;
|
|
11
|
-
scriptShell?: string;
|
|
12
|
-
silent?: boolean;
|
|
13
|
-
scriptsPrependNodePath?: boolean | 'warn-only';
|
|
14
|
-
shellEmulator?: boolean;
|
|
15
|
-
stdio?: string;
|
|
16
|
-
unsafePerm: boolean;
|
|
17
|
-
userAgent?: string;
|
|
18
|
-
}
|
|
19
|
-
export declare function runLifecycleHook(stage: string, manifest: ProjectManifest | DependencyManifest, opts: RunLifecycleHookOptions): Promise<boolean>;
|
package/lib/runLifecycleHook.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { lifecycleLogger } from '@pnpm/core-loggers';
|
|
4
|
-
import { PnpmError } from '@pnpm/error';
|
|
5
|
-
import { globalWarn } from '@pnpm/logger';
|
|
6
|
-
import { lifecycle } from '@pnpm/npm-lifecycle';
|
|
7
|
-
import chalk from 'chalk';
|
|
8
|
-
import isWindows from 'is-windows';
|
|
9
|
-
import { join as shellQuote } from 'shlex';
|
|
10
|
-
function noop() { } // eslint-disable-line:no-empty
|
|
11
|
-
export async function runLifecycleHook(stage, manifest, opts) {
|
|
12
|
-
const optional = opts.optional === true;
|
|
13
|
-
// To remediate CVE_2024_27980, Node.js does not allow .bat or .cmd files to
|
|
14
|
-
// be spawned without the "shell: true" option.
|
|
15
|
-
//
|
|
16
|
-
// https://nodejs.org/api/child_process.html#spawning-bat-and-cmd-files-on-windows
|
|
17
|
-
//
|
|
18
|
-
// Unfortunately, setting spawn's shell option also causes arguments to be
|
|
19
|
-
// evaluated before they're passed to the shell, resulting in a surprising
|
|
20
|
-
// behavior difference only with .bat/.cmd files.
|
|
21
|
-
//
|
|
22
|
-
// Instead of showing a "spawn EINVAL" error, let's throw a clearer error that
|
|
23
|
-
// this isn't supported.
|
|
24
|
-
//
|
|
25
|
-
// If this behavior needs to be supported in the future, the arguments would
|
|
26
|
-
// need to be escaped before they're passed to the .bat/.cmd file. For
|
|
27
|
-
// example, scripts such as "echo %PATH%" should be passed verbatim rather
|
|
28
|
-
// than expanded. This is difficult to do correctly. Other open source tools
|
|
29
|
-
// (e.g. Rust) attempted and introduced bugs. The Rust blog has a good
|
|
30
|
-
// high-level explanation of the same security vulnerability Node.js patched.
|
|
31
|
-
//
|
|
32
|
-
// https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html#overview
|
|
33
|
-
//
|
|
34
|
-
// Note that npm (as of version 10.5.0) doesn't support setting script-shell
|
|
35
|
-
// to a .bat or .cmd file either.
|
|
36
|
-
if (opts.scriptShell != null && typeof opts.scriptShell === 'string' && isWindowsBatchFile(opts.scriptShell)) {
|
|
37
|
-
throw new PnpmError('ERR_PNPM_INVALID_SCRIPT_SHELL_WINDOWS', 'Cannot spawn .bat or .cmd as a script shell.', {
|
|
38
|
-
hint: `\
|
|
39
|
-
The pnpm-workspace.yaml scriptShell option was configured to a .bat or .cmd file. These cannot be used as a script shell reliably.
|
|
40
|
-
|
|
41
|
-
Please unset the scriptShell option, or configure it to a .exe instead.
|
|
42
|
-
`,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
const m = { _id: getId(manifest), ...manifest };
|
|
46
|
-
m.scripts = { ...m.scripts };
|
|
47
|
-
switch (stage) {
|
|
48
|
-
case 'start':
|
|
49
|
-
if (!m.scripts.start) {
|
|
50
|
-
if (!existsSync('server.js')) {
|
|
51
|
-
throw new PnpmError('NO_SCRIPT_OR_SERVER', 'Missing script start or file server.js');
|
|
52
|
-
}
|
|
53
|
-
m.scripts.start = 'node server.js';
|
|
54
|
-
}
|
|
55
|
-
break;
|
|
56
|
-
case 'install':
|
|
57
|
-
if (!m.scripts.install && !m.scripts.preinstall) {
|
|
58
|
-
checkBindingGyp(opts.pkgRoot, m.scripts);
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
if (opts.args?.length && m.scripts?.[stage]) {
|
|
63
|
-
// It is impossible to quote a command line argument that contains newline for Windows cmd.
|
|
64
|
-
const escapedArgs = isWindows()
|
|
65
|
-
? opts.args.map((arg) => JSON.stringify(arg)).join(' ')
|
|
66
|
-
: shellQuote(opts.args);
|
|
67
|
-
m.scripts[stage] = `${m.scripts[stage]} ${escapedArgs}`;
|
|
68
|
-
}
|
|
69
|
-
// This script is used to prevent the usage of npm or Yarn.
|
|
70
|
-
// It does nothing, when pnpm is used, so we may skip its execution.
|
|
71
|
-
if (m.scripts[stage] === 'npx only-allow pnpm' || !m.scripts[stage])
|
|
72
|
-
return false;
|
|
73
|
-
if (opts.stdio !== 'inherit') {
|
|
74
|
-
lifecycleLogger.debug({
|
|
75
|
-
depPath: opts.depPath,
|
|
76
|
-
optional,
|
|
77
|
-
script: m.scripts[stage],
|
|
78
|
-
stage,
|
|
79
|
-
wd: opts.pkgRoot,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
else if (!opts.silent) {
|
|
83
|
-
process.stderr.write(chalk.dim(`$ ${m.scripts[stage]}`) + '\n');
|
|
84
|
-
}
|
|
85
|
-
const logLevel = (opts.stdio !== 'inherit' || opts.silent)
|
|
86
|
-
? 'silent'
|
|
87
|
-
: undefined;
|
|
88
|
-
await lifecycle(m, stage, opts.pkgRoot, {
|
|
89
|
-
config: {},
|
|
90
|
-
dir: opts.rootModulesDir,
|
|
91
|
-
extraBinPaths: opts.extraBinPaths,
|
|
92
|
-
extraEnv: {
|
|
93
|
-
...opts.extraEnv,
|
|
94
|
-
INIT_CWD: opts.initCwd ?? process.cwd(),
|
|
95
|
-
PNPM_SCRIPT_SRC_DIR: opts.pkgRoot,
|
|
96
|
-
...(opts.userAgent ? { npm_config_user_agent: opts.userAgent } : {}),
|
|
97
|
-
},
|
|
98
|
-
log: {
|
|
99
|
-
clearProgress: noop,
|
|
100
|
-
info: noop,
|
|
101
|
-
level: logLevel,
|
|
102
|
-
pause: noop,
|
|
103
|
-
resume: noop,
|
|
104
|
-
showProgress: noop,
|
|
105
|
-
silly: npmLog,
|
|
106
|
-
verbose: npmLog,
|
|
107
|
-
warn: (...msg) => {
|
|
108
|
-
globalWarn(msg.join(' '));
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
runConcurrently: true,
|
|
112
|
-
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
|
113
|
-
scriptShell: opts.scriptShell,
|
|
114
|
-
shellEmulator: opts.shellEmulator,
|
|
115
|
-
stdio: opts.stdio ?? 'pipe',
|
|
116
|
-
unsafePerm: opts.unsafePerm,
|
|
117
|
-
});
|
|
118
|
-
return true;
|
|
119
|
-
function npmLog(prefix, logId, stdtype, line) {
|
|
120
|
-
switch (stdtype) {
|
|
121
|
-
case 'stdout':
|
|
122
|
-
case 'stderr':
|
|
123
|
-
lifecycleLogger.debug({
|
|
124
|
-
depPath: opts.depPath,
|
|
125
|
-
line: (line ?? 0).toString(),
|
|
126
|
-
stage,
|
|
127
|
-
stdio: stdtype,
|
|
128
|
-
wd: opts.pkgRoot,
|
|
129
|
-
});
|
|
130
|
-
return;
|
|
131
|
-
case 'Returned: code:': {
|
|
132
|
-
if (opts.stdio === 'inherit') {
|
|
133
|
-
// Preventing the pnpm reporter from overriding the project's script output
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
const code = line ?? 1;
|
|
137
|
-
lifecycleLogger.debug({
|
|
138
|
-
depPath: opts.depPath,
|
|
139
|
-
exitCode: code,
|
|
140
|
-
optional,
|
|
141
|
-
stage,
|
|
142
|
-
wd: opts.pkgRoot,
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Run node-gyp when binding.gyp is available. Only do this when there are no
|
|
150
|
-
* `install` and `preinstall` scripts (see `npm help scripts`).
|
|
151
|
-
*/
|
|
152
|
-
function checkBindingGyp(root, scripts) {
|
|
153
|
-
if (existsSync(path.join(root, 'binding.gyp'))) {
|
|
154
|
-
scripts.install = 'node-gyp rebuild';
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function getId(manifest) {
|
|
158
|
-
return `${manifest.name ?? ''}@${manifest.version ?? ''}`;
|
|
159
|
-
}
|
|
160
|
-
function isWindowsBatchFile(scriptShell) {
|
|
161
|
-
// Node.js performs a similar check to determine whether it should throw
|
|
162
|
-
// EINVAL when spawning a .cmd/.bat file.
|
|
163
|
-
//
|
|
164
|
-
// https://github.com/nodejs/node/commit/6627222409#diff-1e725bfa950eda4d4b5c0c00a2bb6be3e5b83d819872a1adf2ef87c658273903
|
|
165
|
-
const scriptShellLower = scriptShell.toLowerCase();
|
|
166
|
-
return isWindows() && (scriptShellLower.endsWith('.cmd') || scriptShellLower.endsWith('.bat'));
|
|
167
|
-
}
|
|
168
|
-
//# sourceMappingURL=runLifecycleHook.js.map
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { StoreController } from '@pnpm/store.controller-types';
|
|
2
|
-
import type { ProjectManifest, ProjectRootDir } from '@pnpm/types';
|
|
3
|
-
import { type RunLifecycleHookOptions } from './runLifecycleHook.js';
|
|
4
|
-
export type RunLifecycleHooksConcurrentlyOptions = Omit<RunLifecycleHookOptions, 'depPath' | 'pkgRoot' | 'rootModulesDir'> & {
|
|
5
|
-
resolveSymlinksInInjectedDirs?: boolean;
|
|
6
|
-
storeController: StoreController;
|
|
7
|
-
extraNodePaths?: string[];
|
|
8
|
-
preferSymlinkedExecutables?: boolean;
|
|
9
|
-
};
|
|
10
|
-
export interface Importer {
|
|
11
|
-
buildIndex: number;
|
|
12
|
-
manifest: ProjectManifest;
|
|
13
|
-
rootDir: ProjectRootDir;
|
|
14
|
-
modulesDir: string;
|
|
15
|
-
stages?: string[];
|
|
16
|
-
targetDirs?: string[];
|
|
17
|
-
}
|
|
18
|
-
export declare function runLifecycleHooksConcurrently(stages: string[], importers: Importer[], childConcurrency: number, opts: RunLifecycleHooksConcurrentlyOptions): Promise<void>;
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { linkBins } from '@pnpm/bins.linker';
|
|
3
|
-
import { fetchFromDir } from '@pnpm/fetching.directory-fetcher';
|
|
4
|
-
import { logger } from '@pnpm/logger';
|
|
5
|
-
import { runGroups } from 'run-groups';
|
|
6
|
-
import { runLifecycleHook } from './runLifecycleHook.js';
|
|
7
|
-
export async function runLifecycleHooksConcurrently(stages, importers, childConcurrency, opts) {
|
|
8
|
-
const importersByBuildIndex = new Map();
|
|
9
|
-
for (const importer of importers) {
|
|
10
|
-
if (!importersByBuildIndex.has(importer.buildIndex)) {
|
|
11
|
-
importersByBuildIndex.set(importer.buildIndex, [importer]);
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
importersByBuildIndex.get(importer.buildIndex).push(importer);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
const sortedBuildIndexes = Array.from(importersByBuildIndex.keys()).sort((a, b) => a - b);
|
|
18
|
-
const groups = sortedBuildIndexes.map((buildIndex) => {
|
|
19
|
-
const importers = importersByBuildIndex.get(buildIndex);
|
|
20
|
-
return importers.map(({ manifest, modulesDir, rootDir, stages: importerStages, targetDirs }) => async () => {
|
|
21
|
-
// We are linking the bin files, in case they were created by lifecycle scripts of other workspace packages.
|
|
22
|
-
await linkBins(modulesDir, path.join(modulesDir, '.bin'), {
|
|
23
|
-
extraNodePaths: opts.extraNodePaths,
|
|
24
|
-
allowExoticManifests: true,
|
|
25
|
-
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
|
26
|
-
projectManifest: manifest,
|
|
27
|
-
warn: (message) => {
|
|
28
|
-
logger.warn({ message, prefix: rootDir });
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
const runLifecycleHookOpts = {
|
|
32
|
-
...opts,
|
|
33
|
-
depPath: rootDir,
|
|
34
|
-
pkgRoot: rootDir,
|
|
35
|
-
rootModulesDir: modulesDir,
|
|
36
|
-
};
|
|
37
|
-
let isBuilt = false;
|
|
38
|
-
for (const stage of (importerStages ?? stages)) {
|
|
39
|
-
if (await runLifecycleHook(stage, manifest, runLifecycleHookOpts)) { // eslint-disable-line no-await-in-loop
|
|
40
|
-
isBuilt = true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (targetDirs == null || targetDirs.length === 0 || !isBuilt)
|
|
44
|
-
return;
|
|
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
|
-
});
|
|
64
|
-
});
|
|
65
|
-
await runGroups(childConcurrency, groups);
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=runLifecycleHooksConcurrently.js.map
|