-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexec.ts
More file actions
98 lines (87 loc) · 2.89 KB
/
exec.ts
File metadata and controls
98 lines (87 loc) · 2.89 KB
1
2
3
4
5
6
7
8
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* @fileoverview Execute pnpm commands with optimized flags and security defaults.
*
* SECURITY: Array-based arguments prevent command injection. All elements
* in the args array are properly escaped by Node.js when passed to execBin().
*/
import { execBin } from '../../../bin/exec'
import { isDebug } from '../../../debug/namespace'
import { getCI } from '../../../env/ci'
import {
ArrayPrototypeIndexOf,
ArrayPrototypeSlice,
} from '../../../primordials/array'
import { isNpmProgressFlag } from '../npm/flags'
import {
isPnpmFrozenLockfileFlag,
isPnpmIgnoreScriptsFlag,
isPnpmInstallCommand,
isPnpmLoglevelFlag,
PNPM_INSTALL_LIKE_COMMANDS,
} from './flags'
import type { SpawnOptions } from '../../../spawn/types'
export interface PnpmOptions extends SpawnOptions {
allowLockfileUpdate?: boolean
}
/**
* Execute pnpm commands with optimized flags and settings.
*
* @example
* ```typescript
* await execPnpm(['install'])
* await execPnpm(['add', 'lodash'], { allowLockfileUpdate: true })
* ```
*/
export function execPnpm(args: string[], options?: PnpmOptions | undefined) {
const { allowLockfileUpdate, ...extBinOpts } = {
__proto__: null,
...options,
} as PnpmOptions
const useDebug = isDebug()
const terminatorPos = ArrayPrototypeIndexOf(args, '--')
const pnpmArgs = (
terminatorPos === -1 ? args : ArrayPrototypeSlice(args, 0, terminatorPos)
).filter((a: string) => !isNpmProgressFlag(a))
const otherArgs =
terminatorPos === -1 ? [] : ArrayPrototypeSlice(args, terminatorPos)
const firstArg = pnpmArgs[0]
const supportsIgnoreScripts = firstArg
? PNPM_INSTALL_LIKE_COMMANDS.has(firstArg)
: false
// pnpm uses --loglevel for all commands.
const logLevelArgs =
useDebug || pnpmArgs.some(isPnpmLoglevelFlag) ? [] : ['--loglevel', 'warn']
// Only add --ignore-scripts for commands that support it.
const hasIgnoreScriptsFlag = pnpmArgs.some(isPnpmIgnoreScriptsFlag)
const ignoreScriptsArgs =
!supportsIgnoreScripts || hasIgnoreScriptsFlag ? [] : ['--ignore-scripts']
// CI defaults: pnpm uses --frozen-lockfile by default. Suppress when
// the caller explicitly opted into lockfile updates. The full chain
// short-circuits at getCI() in non-CI runs (returns false), so the
// remaining branches are unreachable in test environments.
/* c8 ignore start */
const frozenLockfileArgs = []
if (
getCI() &&
allowLockfileUpdate &&
firstArg &&
isPnpmInstallCommand(firstArg) &&
!pnpmArgs.some(isPnpmFrozenLockfileFlag)
) {
frozenLockfileArgs.push('--no-frozen-lockfile')
}
/* c8 ignore stop */
// Note: pnpm doesn't have a --no-progress flag. It uses --reporter
// instead. We omit --no-progress to avoid "Unknown option" errors.
return execBin(
'pnpm',
[
...logLevelArgs,
...ignoreScriptsArgs,
...frozenLockfileArgs,
...pnpmArgs,
...otherArgs,
],
extBinOpts,
)
}