@@ -27,8 +27,16 @@ const binPaths = []
27
27
// spec.raw so we don't have to fetch again when we check npxCache
28
28
const manifests = new Map ( )
29
29
30
+ const getManifest = async ( spec , flatOptions ) => {
31
+ if ( ! manifests . get ( spec . raw ) ) {
32
+ const manifest = await pacote . manifest ( spec , { ...flatOptions , preferOnline : true } )
33
+ manifests . set ( spec . raw , manifest )
34
+ }
35
+ return manifests . get ( spec . raw )
36
+ }
37
+
30
38
// Returns the required manifest if the spec is missing from the tree
31
- const missingFromTree = async ( { spec, tree, pacoteOpts } ) => {
39
+ const missingFromTree = async ( { spec, tree, flatOptions } ) => {
32
40
if ( spec . registry && ( spec . rawSpec === '' || spec . type !== 'tag' ) ) {
33
41
// registry spec that is not a specific tag.
34
42
const nodesBySpec = tree . inventory . query ( 'packageName' , spec . name )
@@ -48,17 +56,11 @@ const missingFromTree = async ({ spec, tree, pacoteOpts }) => {
48
56
}
49
57
}
50
58
}
51
- if ( ! manifests . get ( spec . raw ) ) {
52
- manifests . set ( spec . raw , await pacote . manifest ( spec , pacoteOpts ) )
53
- }
54
- return manifests . get ( spec . raw )
59
+ return await getManifest ( spec , flatOptions )
55
60
} else {
56
61
// non-registry spec, or a specific tag. Look up manifest and check
57
62
// resolved to see if it's in the tree.
58
- if ( ! manifests . get ( spec . raw ) ) {
59
- manifests . set ( spec . raw , await pacote . manifest ( spec , pacoteOpts ) )
60
- }
61
- const manifest = manifests . get ( spec . raw )
63
+ const manifest = await getManifest ( spec , flatOptions )
62
64
const nodesByManifest = tree . inventory . query ( 'packageName' , manifest . name )
63
65
for ( const node of nodesByManifest ) {
64
66
if ( node . package . resolved === manifest . _resolved ) {
@@ -78,6 +80,7 @@ const exec = async (opts) => {
78
80
localBin = resolve ( './node_modules/.bin' ) ,
79
81
locationMsg = undefined ,
80
82
globalBin = '' ,
83
+ globalPath = '' ,
81
84
output,
82
85
// dereference values because we manipulate it later
83
86
packages : [ ...packages ] = [ ] ,
@@ -106,9 +109,9 @@ const exec = async (opts) => {
106
109
return run ( )
107
110
}
108
111
109
- const pacoteOpts = { ...flatOptions , perferOnline : true }
110
-
111
112
const needPackageCommandSwap = ( args . length > 0 ) && ( packages . length === 0 )
113
+ // If they asked for a command w/o specifying a package, see if there is a
114
+ // bin that directly matches that name either globally or in the local tree.
112
115
if ( needPackageCommandSwap ) {
113
116
const dir = dirname ( dirname ( localBin ) )
114
117
const localBinPath = await localFileExists ( dir , args [ 0 ] , '/' )
@@ -131,25 +134,34 @@ const exec = async (opts) => {
131
134
const needInstall = [ ]
132
135
await Promise . all ( packages . map ( async pkg => {
133
136
const spec = npa ( pkg , path )
134
- const manifest = await missingFromTree ( { spec, tree : localTree , pacoteOpts } )
137
+ const manifest = await missingFromTree ( { spec, tree : localTree , flatOptions } )
135
138
if ( manifest ) {
139
+ // Package does not exist in the local tree
136
140
needInstall . push ( { spec, manifest } )
137
141
}
138
142
} ) )
139
143
140
144
if ( needPackageCommandSwap ) {
141
145
// Either we have a scoped package or the bin of our package we inferred
142
- // from arg[0] is not identical to the package name
146
+ // from arg[0] might not be identical to the package name
147
+ const spec = npa ( args [ 0 ] )
143
148
let commandManifest
144
149
if ( needInstall . length === 0 ) {
145
- commandManifest = await pacote . manifest ( args [ 0 ] , {
146
- ...flatOptions ,
147
- preferOnline : true ,
148
- } )
150
+ commandManifest = await getManifest ( spec , flatOptions )
149
151
} else {
150
152
commandManifest = needInstall [ 0 ] . manifest
151
153
}
154
+
152
155
args [ 0 ] = getBinFromManifest ( commandManifest )
156
+
157
+ // See if the package is installed globally, and run the translated bin
158
+ const globalArb = new Arborist ( { ...flatOptions , path : globalPath , global : true } )
159
+ const globalTree = await globalArb . loadActual ( )
160
+ const globalManifest = await missingFromTree ( { spec, tree : globalTree , flatOptions } )
161
+ if ( ! globalManifest ) {
162
+ binPaths . push ( globalBin )
163
+ return await run ( )
164
+ }
153
165
}
154
166
155
167
const add = [ ]
@@ -171,7 +183,7 @@ const exec = async (opts) => {
171
183
} )
172
184
const npxTree = await npxArb . loadActual ( )
173
185
await Promise . all ( needInstall . map ( async ( { spec } ) => {
174
- const manifest = await missingFromTree ( { spec, tree : npxTree , pacoteOpts } )
186
+ const manifest = await missingFromTree ( { spec, tree : npxTree , flatOptions } )
175
187
if ( manifest ) {
176
188
// Manifest is not in npxCache, we need to install it there
177
189
if ( ! spec . registry ) {
0 commit comments