@@ -11,6 +11,7 @@ const Config = require('@npmcli/config')
11
11
// Patch the global fs module here at the app level
12
12
require ( 'graceful-fs' ) . gracefulify ( require ( 'fs' ) )
13
13
14
+ // TODO make this only ever load once (or unload) in tests
14
15
const procLogListener = require ( './utils/proc-log-listener.js' )
15
16
16
17
const proxyCmds = new Proxy ( { } , {
@@ -48,6 +49,7 @@ const _title = Symbol('_title')
48
49
const npm = module . exports = new class extends EventEmitter {
49
50
constructor ( ) {
50
51
super ( )
52
+ // TODO make this only ever load once (or unload) in tests
51
53
require ( './utils/perf.js' )
52
54
this . started = Date . now ( )
53
55
this . command = null
@@ -77,8 +79,8 @@ const npm = module.exports = new class extends EventEmitter {
77
79
[ _runCmd ] ( cmd , impl , args , cb ) {
78
80
if ( ! this . loaded ) {
79
81
throw new Error (
80
- 'Call npm.load(cb ) before using this command.\n' +
81
- 'See the README.md or bin/npm- cli.js for example usage.'
82
+ 'Call npm.load() before using this command.\n' +
83
+ 'See lib/ cli.js for example usage.'
82
84
)
83
85
}
84
86
@@ -96,7 +98,7 @@ const npm = module.exports = new class extends EventEmitter {
96
98
args . filter ( arg => / ^ [ \u2010 - \u2015 \u2212 \uFE58 \uFE63 \uFF0D ] / . test ( arg ) )
97
99
. forEach ( arg => {
98
100
warnedNonDashArg = true
99
- log . error ( 'arg' , 'Argument starts with non-ascii dash, this is probably invalid:' , arg )
101
+ this . log . error ( 'arg' , 'Argument starts with non-ascii dash, this is probably invalid:' , arg )
100
102
} )
101
103
}
102
104
@@ -123,33 +125,32 @@ const npm = module.exports = new class extends EventEmitter {
123
125
}
124
126
}
125
127
126
- // call with parsed CLI options and a callback when done loading
127
- // XXX promisify this and stop taking a callback
128
128
load ( cb ) {
129
- if ( ! cb || typeof cb !== 'function' )
130
- throw new TypeError ( 'must call as: npm.load(callback)' )
131
-
132
- this . once ( 'load' , cb )
133
- if ( this . loaded || this . loadErr ) {
134
- this . emit ( 'load' , this . loadErr )
135
- return
129
+ if ( cb && typeof cb !== 'function' )
130
+ throw new TypeError ( 'callback must be a function if provided' )
131
+
132
+ if ( ! this . loadPromise ) {
133
+ process . emit ( 'time' , 'npm:load' )
134
+ this . log . pause ( )
135
+ this . loadPromise = new Promise ( ( resolve , reject ) => {
136
+ this [ _load ] ( ) . catch ( er => er ) . then ( ( er ) => {
137
+ this . loadErr = er
138
+ if ( ! er && this . config . get ( 'force' ) )
139
+ this . log . warn ( 'using --force' , 'Recommended protections disabled.' )
140
+
141
+ process . emit ( 'timeEnd' , 'npm:load' )
142
+ if ( er )
143
+ return reject ( er )
144
+ resolve ( )
145
+ } )
146
+ } )
136
147
}
137
- if ( this . loading )
138
- return
139
-
140
- this . loading = true
148
+ if ( ! cb )
149
+ return this . loadPromise
141
150
142
- process . emit ( 'time' , 'npm:load' )
143
- this . log . pause ( )
144
- return this [ _load ] ( ) . catch ( er => er ) . then ( ( er ) => {
145
- this . loading = false
146
- this . loadErr = er
147
- if ( ! er && this . config . get ( 'force' ) )
148
- this . log . warn ( 'using --force' , 'Recommended protections disabled.' )
149
-
150
- process . emit ( 'timeEnd' , 'npm:load' )
151
- this . emit ( 'load' , er )
152
- } )
151
+ // loadPromise is returned here for legacy purposes, old code was allowing
152
+ // the mixing of callback and promise here.
153
+ return this . loadPromise . then ( cb , cb )
153
154
}
154
155
155
156
get loaded ( ) {
@@ -167,10 +168,15 @@ const npm = module.exports = new class extends EventEmitter {
167
168
168
169
async [ _load ] ( ) {
169
170
process . emit ( 'time' , 'npm:load:whichnode' )
170
- const node = await which ( process . argv [ 0 ] ) . catch ( er => null )
171
+ let node
172
+ try {
173
+ node = which . sync ( process . argv [ 0 ] )
174
+ } catch ( _ ) {
175
+ // TODO should we throw here?
176
+ }
171
177
process . emit ( 'timeEnd' , 'npm:load:whichnode' )
172
178
if ( node && node . toUpperCase ( ) !== process . execPath . toUpperCase ( ) ) {
173
- log . verbose ( 'node symlink' , node )
179
+ this . log . verbose ( 'node symlink' , node )
174
180
process . execPath = node
175
181
this . config . execPath = node
176
182
}
@@ -198,10 +204,10 @@ const npm = module.exports = new class extends EventEmitter {
198
204
process . env . COLOR = this . color ? '1' : '0'
199
205
200
206
process . emit ( 'time' , 'npm:load:cleanupLog' )
201
- cleanUpLogFiles ( this . cache , this . config . get ( 'logs-max' ) , log . warn )
207
+ cleanUpLogFiles ( this . cache , this . config . get ( 'logs-max' ) , this . log . warn )
202
208
process . emit ( 'timeEnd' , 'npm:load:cleanupLog' )
203
209
204
- log . resume ( )
210
+ this . log . resume ( )
205
211
206
212
process . emit ( 'time' , 'npm:load:configScope' )
207
213
const configScope = this . config . get ( 'scope' )
@@ -314,9 +320,8 @@ const npm = module.exports = new class extends EventEmitter {
314
320
// now load everything required by the class methods
315
321
316
322
const log = require ( 'npmlog' )
317
- const { promisify } = require ( 'util' )
318
323
319
- const which = promisify ( require ( 'which' ) )
324
+ const which = require ( 'which' )
320
325
321
326
const deref = require ( './utils/deref-command.js' )
322
327
const setupLog = require ( './utils/setup-log.js' )
0 commit comments