-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathnpm-paths.mts
More file actions
executable file
·143 lines (131 loc) · 4.7 KB
/
npm-paths.mts
File metadata and controls
executable file
·143 lines (131 loc) · 4.7 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { existsSync } from 'node:fs'
import Module from 'node:module'
import path from 'node:path'
import { resolveBinPathSync } from '@socketsecurity/registry/lib/bin'
import { logger } from '@socketsecurity/registry/lib/logger'
import constants, { NODE_MODULES, NPM } from '../constants.mts'
import { findBinPathDetailsSync, findNpmDirPathSync } from './path-resolve.mts'
function exitWithBinPathError(binName: string): never {
logger.fail(
`Socket unable to locate ${binName}; ensure it is available in the PATH environment variable`,
)
// The exit code 127 indicates that the command or binary being executed
// could not be found.
// eslint-disable-next-line n/no-process-exit
process.exit(127)
// This line is never reached in production, but helps tests.
throw new Error('process.exit called')
}
// Find a binary next to the running node binary (process.execPath).
// This avoids picking up a project-local binary from node_modules/.bin
// on PATH, e.g. the standalone "npx" package which bundles npm@5.1.0
// that is incompatible with Node 22+.
function findBinNextToNode(binName: string): string | undefined {
const nodeDir = path.dirname(process.execPath)
const binPath = path.join(nodeDir, binName)
if (existsSync(binPath)) {
try {
return resolveBinPathSync(binPath)
} catch {
return undefined
}
}
return undefined
}
let _npmBinPath: string | undefined
export function getNpmBinPath(): string {
if (_npmBinPath === undefined) {
_npmBinPath = getNpmBinPathDetails().path
if (!_npmBinPath) {
exitWithBinPathError(NPM)
}
}
return _npmBinPath
}
let _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined
function getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {
if (_npmBinPathDetails === undefined) {
// First try to find npm next to the node binary to avoid picking up
// a project-local npm from node_modules/.bin on PATH.
const npmNextToNode = findBinNextToNode(NPM)
if (npmNextToNode) {
_npmBinPathDetails = { name: NPM, path: npmNextToNode, shadowed: false }
} else {
_npmBinPathDetails = findBinPathDetailsSync(NPM)
}
}
return _npmBinPathDetails
}
let _npmDirPath: string | undefined
export function getNpmDirPath() {
if (_npmDirPath === undefined) {
const npmBinPath = getNpmBinPath()
_npmDirPath = npmBinPath ? findNpmDirPathSync(npmBinPath) : undefined
if (!_npmDirPath) {
_npmDirPath = constants.ENV.SOCKET_CLI_NPM_PATH || undefined
}
if (!_npmDirPath) {
let message = 'Unable to find npm CLI install directory.'
if (npmBinPath) {
message += `\nSearched parent directories of ${path.dirname(npmBinPath)}.`
}
message +=
'\n\nThis is may be a bug with socket-npm related to changes to the npm CLI.'
message += `\nPlease report to ${constants.SOCKET_CLI_ISSUES_URL}.`
logger.fail(message)
// The exit code 127 indicates that the command or binary being executed
// could not be found.
// eslint-disable-next-line n/no-process-exit
process.exit(127)
// This line is never reached in production, but helps tests.
throw new Error('process.exit called')
}
}
return _npmDirPath
}
let _npmRequire: NodeJS.Require | undefined
export function getNpmRequire(): NodeJS.Require {
if (_npmRequire === undefined) {
const npmDirPath = getNpmDirPath()
const npmNmPath = path.join(npmDirPath, `${NODE_MODULES}/npm`)
_npmRequire = Module.createRequire(
path.join(
existsSync(npmNmPath) ? npmNmPath : npmDirPath,
'<dummy-basename>',
),
)
}
return _npmRequire
}
let _npxBinPath: string | undefined
export function getNpxBinPath(): string {
if (_npxBinPath === undefined) {
_npxBinPath = getNpxBinPathDetails().path
if (!_npxBinPath) {
exitWithBinPathError('npx')
}
}
return _npxBinPath
}
let _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined
function getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {
if (_npxBinPathDetails === undefined) {
// First try to find npx next to the node binary to avoid picking up
// a project-local npx from node_modules/.bin on PATH (e.g., the
// standalone npx package which bundles npm@5.1.0, incompatible
// with Node 22+).
const npxNextToNode = findBinNextToNode('npx')
if (npxNextToNode) {
_npxBinPathDetails = { name: 'npx', path: npxNextToNode, shadowed: false }
} else {
_npxBinPathDetails = findBinPathDetailsSync('npx')
}
}
return _npxBinPathDetails
}
export function isNpmBinPathShadowed() {
return getNpmBinPathDetails().shadowed
}
export function isNpxBinPathShadowed() {
return getNpxBinPathDetails().shadowed
}