Skip to content

Commit db2034d

Browse files
committed
Make path resolution lazy
1 parent 83bd137 commit db2034d

File tree

13 files changed

+226
-104
lines changed

13 files changed

+226
-104
lines changed

src/commands/raw-npm.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import meow from 'meow'
55

66
import constants from '../constants'
77
import { commonFlags, validationFlags } from '../flags'
8+
import { getNpmBinPath } from '../shadow/npm-paths'
89
import { getFlagListOutput } from '../utils/output-formatting'
9-
import { findBinPathDetails } from '../utils/path-resolve'
1010

1111
import type { CliSubcommand } from '../utils/meow-with-subcommands'
1212

@@ -62,16 +62,7 @@ async function setupCommand(
6262
cli.showHelp()
6363
return
6464
}
65-
const { path: binPath } = await findBinPathDetails(binName)
66-
if (!binPath) {
67-
// The exit code 127 indicates that the command or binary being executed
68-
// could not be found.
69-
console.error(
70-
`Socket unable to locate ${binName}; ensure it is available in the PATH environment variable.`
71-
)
72-
process.exit(127)
73-
}
74-
const spawnPromise = spawn(binPath, <string[]>argv, {
65+
const spawnPromise = spawn(getNpmBinPath(), <string[]>argv, {
7566
signal: abortSignal,
7667
stdio: 'inherit'
7768
})

src/commands/raw-npx.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import meow from 'meow'
55

66
import constants from '../constants'
77
import { commonFlags, validationFlags } from '../flags'
8+
import { getNpxBinPath } from '../shadow/npm-paths'
89
import { getFlagListOutput } from '../utils/output-formatting'
9-
import { findBinPathDetails } from '../utils/path-resolve'
1010

1111
import type { CliSubcommand } from '../utils/meow-with-subcommands'
1212

@@ -62,16 +62,7 @@ async function setupCommand(
6262
cli.showHelp()
6363
return
6464
}
65-
const { path: binPath } = await findBinPathDetails(binName)
66-
if (!binPath) {
67-
// The exit code 127 indicates that the command or binary being executed
68-
// could not be found.
69-
console.error(
70-
`Socket unable to locate ${binName}; ensure it is available in the PATH environment variable.`
71-
)
72-
process.exit(127)
73-
}
74-
const spawnPromise = spawn(binPath, <string[]>argv, {
65+
const spawnPromise = spawn(getNpxBinPath(), <string[]>argv, {
7566
signal: abortSignal,
7667
stdio: 'inherit'
7768
})

src/shadow/arborist/index.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ import { SafeEdge } from './lib/edge'
33
import { SafeNode } from './lib/node'
44
import { SafeOverrideSet } from './lib/override-set'
55
import {
6-
arboristClassPath,
7-
arboristEdgeClassPath,
8-
arboristNodeClassPath,
9-
arboristOverrideSetClassPath
6+
getArboristClassPath,
7+
getArboristEdgeClassPath,
8+
getArboristNodeClassPath,
9+
getArboristOverrideSetClassPath
1010
} from '../npm-paths'
1111

1212
export function installSafeArborist() {
1313
// Override '@npmcli/arborist' module exports with patched variants based on
1414
// https://github.com/npm/cli/pull/7025.
1515
const cache: { [key: string]: any } = require.cache
16-
cache[arboristClassPath] = { exports: SafeArborist }
17-
cache[arboristEdgeClassPath] = { exports: SafeEdge }
18-
cache[arboristNodeClassPath] = { exports: SafeNode }
19-
cache[arboristOverrideSetClassPath] = { exports: SafeOverrideSet }
16+
cache[getArboristClassPath()] = { exports: SafeArborist }
17+
cache[getArboristEdgeClassPath()] = { exports: SafeEdge }
18+
cache[getArboristNodeClassPath()] = { exports: SafeNode }
19+
cache[getArboristOverrideSetClassPath()] = { exports: SafeOverrideSet }
2020
}

src/shadow/arborist/lib/arborist/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { kRiskyReify, reify } from './reify'
2-
import { arboristClassPath } from '../../../npm-paths'
2+
import { getArboristClassPath } from '../../../npm-paths'
33

44
import type { ArboristClass, ArboristReifyOptions } from './types'
55
import type { SafeNode } from '../node'
66

7-
export const Arborist: ArboristClass = require(arboristClassPath)
7+
export const Arborist: ArboristClass = require(getArboristClassPath())
88

99
export const kCtorArgs = Symbol('ctorArgs')
1010

src/shadow/arborist/lib/dep-valid.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { arboristDepValidPath } from '../../npm-paths'
1+
import { getArboristDepValidPath } from '../../npm-paths'
22

33
import type { SafeNode } from './node'
44

@@ -7,4 +7,4 @@ export const depValid: (
77
requested: string,
88
accept: string | undefined,
99
requester: SafeNode
10-
) => boolean = require(arboristDepValidPath)
10+
) => boolean = require(getArboristDepValidPath())

src/shadow/arborist/lib/edge.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { depValid } from './dep-valid'
22
import { SafeNode } from './node'
33
import { SafeOverrideSet } from './override-set'
4-
import { arboristEdgeClassPath } from '../../npm-paths'
4+
import { getArboristEdgeClassPath } from '../../npm-paths'
55

66
import type { Edge as BaseEdge, DependencyProblem } from '@npmcli/arborist'
77

@@ -56,7 +56,7 @@ export type Explanation = {
5656
from: object | undefined
5757
} | null
5858

59-
export const Edge: EdgeClass = require(arboristEdgeClassPath)
59+
export const Edge: EdgeClass = require(getArboristEdgeClassPath())
6060

6161
// The Edge class makes heavy use of private properties which subclasses do NOT
6262
// have access to. So we have to recreate any functionality that relies on those

src/shadow/arborist/lib/node.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import semver from 'semver'
22

33
import { SafeOverrideSet } from './override-set'
4-
import { arboristNodeClassPath } from '../../npm-paths'
4+
import { getArboristNodeClassPath } from '../../npm-paths'
55
import { getLogger } from '../../proc-log'
66

77
import type { SafeEdge } from './edge'
@@ -74,7 +74,7 @@ type NodeClass = Omit<
7474
updateOverridesEdgeInRemoved(otherOverrideSet: SafeOverrideSet): boolean
7575
}
7676

77-
const Node: NodeClass = require(arboristNodeClassPath)
77+
const Node: NodeClass = require(getArboristNodeClassPath())
7878

7979
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
8080
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/node.js:

src/shadow/arborist/lib/override-set.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import npa from 'npm-package-arg'
22
import semver from 'semver'
33

44
import constants from '../../../constants'
5-
import { arboristOverrideSetClassPath } from '../../npm-paths'
5+
import { getArboristOverrideSetClassPath } from '../../npm-paths'
66
import { getLogger } from '../../proc-log'
77

88
import type { SafeEdge } from './edge'
@@ -31,7 +31,7 @@ interface OverrideSetClass {
3131
isEqual(otherOverrideSet: SafeOverrideSet | undefined): boolean
3232
}
3333

34-
const OverrideSet: OverrideSetClass = require(arboristOverrideSetClassPath)
34+
const OverrideSet: OverrideSetClass = require(getArboristOverrideSetClassPath())
3535

3636
// Implementation code not related to patch https://github.com/npm/cli/pull/7025
3737
// is based on https://github.com/npm/cli/blob/v11.0.0/workspaces/arborist/lib/override-set.js:

src/shadow/link.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,30 @@ import process from 'node:process'
33

44
import cmdShim from 'cmd-shim'
55

6+
import {
7+
getNpmBinPath,
8+
getNpxBinPath,
9+
isNpmBinPathShadowed,
10+
isNpxBinPathShadowed
11+
} from './npm-paths'
612
import constants from '../constants'
7-
import { findBinPathDetails } from '../utils/path-resolve'
13+
14+
const { NPX } = constants
815

916
export async function installLinks(
1017
realBinPath: string,
1118
binName: 'npm' | 'npx'
1219
): Promise<string> {
20+
const isNpx = binName === NPX
1321
// Find package manager being shadowed by this process.
14-
const { path: binPath, shadowed } = await findBinPathDetails(binName)
15-
if (!binPath) {
16-
// The exit code 127 indicates that the command or binary being executed
17-
// could not be found.
18-
console.error(
19-
`Socket unable to locate ${binName}; ensure it is available in the PATH environment variable.`
20-
)
21-
process.exit(127)
22-
}
22+
const binPath = isNpx ? getNpxBinPath() : getNpmBinPath()
2323
// Lazily access constants.WIN32.
2424
const { WIN32 } = constants
2525
// TODO: Is this early exit needed?
2626
if (WIN32 && binPath) {
2727
return binPath
2828
}
29+
const shadowed = isNpx ? isNpxBinPathShadowed() : isNpmBinPathShadowed()
2930
// Move our bin directory to front of PATH so its found first.
3031
if (!shadowed) {
3132
if (WIN32) {

src/shadow/npm-paths.ts

Lines changed: 135 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,147 @@ import path from 'node:path'
33
import process from 'node:process'
44

55
import constants from '../constants'
6-
import { findNpmPath } from '../utils/path-resolve'
6+
import { findBinPathDetailsSync, findNpmPathSync } from '../utils/path-resolve'
77

8-
const { NODE_MODULES, SOCKET_CLI_ISSUES_URL } = constants
8+
const { NODE_MODULES, NPM, NPX, SOCKET_CLI_ISSUES_URL } = constants
99

10-
const npmEntrypoint = realpathSync.native(process.argv[1]!)
11-
const npmPath = findNpmPath(npmEntrypoint)
12-
if (npmPath === undefined) {
10+
function exitWithBinPathError(binName: string): never {
1311
console.error(
14-
`Unable to find npm CLI install directory.
15-
Searched parent directories of ${npmEntrypoint}.
16-
17-
This is may be a bug with socket-npm related to changes to the npm CLI.
18-
Please report to ${SOCKET_CLI_ISSUES_URL}.`
12+
`Socket unable to locate ${binName}; ensure it is available in the PATH environment variable.`
1913
)
2014
// The exit code 127 indicates that the command or binary being executed
2115
// could not be found.
2216
process.exit(127)
2317
}
2418

25-
export const npmNmPath = path.join(npmPath, NODE_MODULES)
26-
export const arboristPkgPath = path.join(npmNmPath, '@npmcli/arborist')
27-
export const arboristClassPath = path.join(
28-
arboristPkgPath,
29-
'lib/arborist/index.js'
30-
)
31-
export const arboristDepValidPath = path.join(
32-
arboristPkgPath,
33-
'lib/dep-valid.js'
34-
)
35-
export const arboristEdgeClassPath = path.join(arboristPkgPath, 'lib/edge.js')
36-
export const arboristNodeClassPath = path.join(arboristPkgPath, 'lib/node.js')
37-
export const arboristOverrideSetClassPath = path.join(
38-
arboristPkgPath,
39-
'lib/override-set.js'
40-
)
19+
let _npmBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined
20+
function getNpmBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {
21+
if (_npmBinPathDetails === undefined) {
22+
_npmBinPathDetails = findBinPathDetailsSync(NPM)
23+
}
24+
return _npmBinPathDetails
25+
}
26+
27+
let _npxBinPathDetails: ReturnType<typeof findBinPathDetailsSync> | undefined
28+
function getNpxBinPathDetails(): ReturnType<typeof findBinPathDetailsSync> {
29+
if (_npxBinPathDetails === undefined) {
30+
_npxBinPathDetails = findBinPathDetailsSync(NPX)
31+
}
32+
return _npxBinPathDetails
33+
}
34+
35+
let _npmBinPath: string | undefined
36+
export function getNpmBinPath(): string {
37+
if (_npmBinPath === undefined) {
38+
_npmBinPath = getNpmBinPathDetails().path
39+
if (!_npmBinPath) {
40+
exitWithBinPathError(NPM)
41+
}
42+
}
43+
return _npmBinPath
44+
}
45+
46+
export function isNpmBinPathShadowed() {
47+
return getNpmBinPathDetails().shadowed
48+
}
49+
50+
let _npxBinPath: string | undefined
51+
export function getNpxBinPath(): string {
52+
if (_npxBinPath === undefined) {
53+
_npxBinPath = getNpxBinPathDetails().path
54+
if (!_npxBinPath) {
55+
exitWithBinPathError(NPX)
56+
}
57+
}
58+
return _npxBinPath
59+
}
60+
61+
export function isNpxBinPathShadowed() {
62+
return getNpxBinPathDetails().shadowed
63+
}
64+
65+
let _npmPath: string | undefined
66+
export function getNpmPath() {
67+
if (_npmPath === undefined) {
68+
const npmEntrypoint = path.dirname(realpathSync.native(getNpmBinPath()))
69+
_npmPath = findNpmPathSync(npmEntrypoint)
70+
if (!_npmPath) {
71+
console.error(
72+
`Unable to find npm CLI install directory.
73+
Searched parent directories of ${npmEntrypoint}.
74+
75+
This is may be a bug with socket-npm related to changes to the npm CLI.
76+
Please report to ${SOCKET_CLI_ISSUES_URL}.`
77+
)
78+
// The exit code 127 indicates that the command or binary being executed
79+
// could not be found.
80+
process.exit(127)
81+
}
82+
}
83+
return _npmPath
84+
}
85+
86+
let _npmNmPath: string | undefined
87+
export function getNpmNodeModulesPath() {
88+
if (_npmNmPath === undefined) {
89+
_npmNmPath = path.join(getNpmPath(), NODE_MODULES)
90+
}
91+
return _npmNmPath
92+
}
93+
94+
let _arboristPkgPath: string | undefined
95+
export function getArboristPackagePath() {
96+
if (_arboristPkgPath === undefined) {
97+
_arboristPkgPath = path.join(getNpmNodeModulesPath(), '@npmcli/arborist')
98+
}
99+
return _arboristPkgPath
100+
}
101+
102+
let _arboristClassPath: string | undefined
103+
export function getArboristClassPath() {
104+
if (_arboristClassPath === undefined) {
105+
_arboristClassPath = path.join(
106+
getArboristPackagePath(),
107+
'lib/arborist/index.js'
108+
)
109+
}
110+
return _arboristClassPath
111+
}
112+
113+
let _arboristDepValidPath: string | undefined
114+
export function getArboristDepValidPath() {
115+
if (_arboristDepValidPath === undefined) {
116+
_arboristDepValidPath = path.join(
117+
getArboristPackagePath(),
118+
'lib/dep-valid.js'
119+
)
120+
}
121+
return _arboristDepValidPath
122+
}
123+
124+
let _arboristEdgeClassPath: string | undefined
125+
export function getArboristEdgeClassPath() {
126+
if (_arboristEdgeClassPath === undefined) {
127+
_arboristEdgeClassPath = path.join(getArboristPackagePath(), 'lib/edge.js')
128+
}
129+
return _arboristEdgeClassPath
130+
}
131+
132+
let _arboristNodeClassPath: string | undefined
133+
export function getArboristNodeClassPath() {
134+
if (_arboristNodeClassPath === undefined) {
135+
_arboristNodeClassPath = path.join(getArboristPackagePath(), 'lib/node.js')
136+
}
137+
return _arboristNodeClassPath
138+
}
139+
140+
let _arboristOverrideSetClassPath: string | undefined
141+
export function getArboristOverrideSetClassPath() {
142+
if (_arboristOverrideSetClassPath === undefined) {
143+
_arboristOverrideSetClassPath = path.join(
144+
getArboristPackagePath(),
145+
'lib/override-set.js'
146+
)
147+
}
148+
return _arboristOverrideSetClassPath
149+
}

0 commit comments

Comments
 (0)