Skip to content

Commit fc68547

Browse files
committed
fix: remove some npm.load timers and exit earlier for --versions
1 parent 03634be commit fc68547

File tree

10 files changed

+208
-169
lines changed

10 files changed

+208
-169
lines changed

lib/cli/entry.js

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,12 @@ module.exports = async (process, validateEngines) => {
4040
// Now actually fire up npm and run the command.
4141
// This is how to use npm programmatically:
4242
try {
43-
await npm.load()
43+
const { exec } = await npm.load()
4444

45-
// npm -v
46-
if (npm.config.get('version', 'cli')) {
47-
output.standard(npm.version)
45+
if (!exec) {
4846
return exitHandler()
4947
}
5048

51-
// npm --versions
52-
if (npm.config.get('versions', 'cli')) {
53-
npm.argv = ['version']
54-
npm.config.set('usage', false, 'cli')
55-
}
56-
5749
cmd = npm.argv.shift()
5850
if (!cmd) {
5951
output.standard(npm.usage)

lib/npm.js

Lines changed: 38 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const usage = require('./utils/npm-usage.js')
77
const LogFile = require('./utils/log-file.js')
88
const Timers = require('./utils/timers.js')
99
const Display = require('./utils/display.js')
10-
const { log, time } = require('proc-log')
10+
const { log, time, output } = require('proc-log')
1111
const { redactLog: replaceInfo } = require('@npmcli/redact')
1212
const pkg = require('../package.json')
1313
const { deref } = require('./utils/cmd-list.js')
@@ -28,20 +28,14 @@ class Npm {
2828
}
2929

3030
updateNotification = null
31-
loadErr = null
3231
argv = []
3332

3433
#command = null
3534
#runId = new Date().toISOString().replace(/[.:]/g, '_')
36-
#loadPromise = null
3735
#title = 'npm'
3836
#argvClean = []
3937
#npmRoot = null
4038

41-
#chalk = null
42-
#logChalk = null
43-
#noColorChalk = null
44-
4539
#display = null
4640
#logFile = new LogFile()
4741
#timers = new Timers({ start: 'npm' })
@@ -107,13 +101,7 @@ class Npm {
107101
}
108102

109103
async load () {
110-
if (!this.#loadPromise) {
111-
this.#loadPromise = time.start('npm:load', () => this.#load().catch((er) => {
112-
this.loadErr = er
113-
throw er
114-
}))
115-
}
116-
return this.#loadPromise
104+
return time.start('npm:load', () => this.#load().then(r => r || { exec: true }))
117105
}
118106

119107
get loaded () {
@@ -160,19 +148,24 @@ class Npm {
160148

161149
await time.start('npm:load:configload', () => this.config.load())
162150

163-
// get createSupportsColor from chalk directly if this lands
164-
// https://github.com/chalk/chalk/pull/600
165-
const [{ Chalk }, { createSupportsColor }] = await Promise.all([
166-
import('chalk'),
167-
import('supports-color'),
168-
])
169-
this.#noColorChalk = new Chalk({ level: 0 })
170-
// we get the chalk level based on a null stream meaning chalk will only use
171-
// what it knows about the environment to get color support since we already
172-
// determined in our definitions that we want to show colors.
173-
const level = Math.max(createSupportsColor(null).level, 1)
174-
this.#chalk = this.color ? new Chalk({ level }) : this.#noColorChalk
175-
this.#logChalk = this.logColor ? new Chalk({ level }) : this.#noColorChalk
151+
await this.#display.load({
152+
loglevel: this.config.get('loglevel'),
153+
stdoutColor: this.color,
154+
stderrColor: this.logColor,
155+
timing: this.config.get('timing'),
156+
unicode: this.config.get('unicode'),
157+
progress: this.flatOptions.progress,
158+
json: this.config.get('json'),
159+
heading: this.config.get('heading'),
160+
})
161+
process.env.COLOR = this.color ? '1' : '0'
162+
163+
// npm -v
164+
// return from here early so we dont create any caches/logfiles/timers etc
165+
if (this.config.get('version', 'cli')) {
166+
output.standard(this.version)
167+
return { exec: false }
168+
}
176169

177170
// mkdir this separately since the logs dir can be set to
178171
// a different location. if this fails, then we don't have
@@ -208,49 +201,29 @@ class Npm {
208201
log.verbose('argv', this.#argvClean.map(JSON.stringify).join(' '))
209202
})
210203

211-
time.start('npm:load:display', () => {
212-
this.#display.load({
213-
loglevel: this.config.get('loglevel'),
214-
// TODO: only pass in logColor and color and create chalk instances
215-
// in display load method. Then remove chalk getters from npm and
216-
// producers should emit chalk-templates (or something else).
217-
stdoutChalk: this.#chalk,
218-
stdoutColor: this.color,
219-
stderrChalk: this.#logChalk,
220-
stderrColor: this.logColor,
221-
timing: this.config.get('timing'),
222-
unicode: this.config.get('unicode'),
223-
progress: this.flatOptions.progress,
224-
json: this.config.get('json'),
225-
heading: this.config.get('heading'),
226-
})
227-
process.env.COLOR = this.color ? '1' : '0'
204+
this.#logFile.load({
205+
path: this.logPath,
206+
logsMax: this.config.get('logs-max'),
228207
})
229208

230-
time.start('npm:load:logFile', () => {
231-
this.#logFile.load({
232-
path: this.logPath,
233-
logsMax: this.config.get('logs-max'),
234-
})
235-
log.verbose('logfile', this.#logFile.files[0] || 'no logfile created')
209+
this.#timers.load({
210+
path: this.config.get('timing') ? this.logPath : null,
236211
})
237212

238-
time.start('npm:load:timers', () =>
239-
this.#timers.load({
240-
path: this.config.get('timing') ? this.logPath : null,
241-
})
242-
)
243-
244-
time.start('npm:load:configScope', () => {
245-
const configScope = this.config.get('scope')
246-
if (configScope && !/^@/.test(configScope)) {
247-
this.config.set('scope', `@${configScope}`, this.config.find('scope'))
248-
}
249-
})
213+
const configScope = this.config.get('scope')
214+
if (configScope && !/^@/.test(configScope)) {
215+
this.config.set('scope', `@${configScope}`, this.config.find('scope'))
216+
}
250217

251218
if (this.config.get('force')) {
252219
log.warn('using --force', 'Recommended protections disabled.')
253220
}
221+
222+
// npm --versions
223+
if (this.config.get('versions', 'cli')) {
224+
this.argv = ['version']
225+
this.config.set('usage', false, 'cli')
226+
}
254227
}
255228

256229
get isShellout () {
@@ -283,15 +256,15 @@ class Npm {
283256
}
284257

285258
get noColorChalk () {
286-
return this.#noColorChalk
259+
return this.#display.chalk.noColor
287260
}
288261

289262
get chalk () {
290-
return this.#chalk
263+
return this.#display.chalk.stdout
291264
}
292265

293266
get logChalk () {
294-
return this.#logChalk
267+
return this.#display.chalk.stderr
295268
}
296269

297270
get global () {

lib/utils/display.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class Display {
121121
}
122122

123123
// colors
124+
#noColorChalk
124125
#stdoutChalk
125126
#stdoutColor
126127
#stderrChalk
@@ -161,25 +162,44 @@ class Display {
161162
}
162163
}
163164

164-
load ({
165+
get chalk () {
166+
return {
167+
noColor: this.#noColorChalk,
168+
stdout: this.#stdoutChalk,
169+
stderr: this.#stderrChalk,
170+
}
171+
}
172+
173+
async load ({
165174
heading,
166175
json,
167176
loglevel,
168177
progress,
169-
stderrChalk,
170178
stderrColor,
171-
stdoutChalk,
172179
stdoutColor,
173180
timing,
174181
unicode,
175182
}) {
183+
// get createSupportsColor from chalk directly if this lands
184+
// https://github.com/chalk/chalk/pull/600
185+
const [{ Chalk }, { createSupportsColor }] = await Promise.all([
186+
import('chalk'),
187+
import('supports-color'),
188+
])
189+
// we get the chalk level based on a null stream meaning chalk will only use
190+
// what it knows about the environment to get color support since we already
191+
// determined in our definitions that we want to show colors.
192+
const level = Math.max(createSupportsColor(null).level, 1)
193+
194+
this.#noColorChalk = new Chalk({ level: 0 })
195+
176196
this.#stdoutColor = stdoutColor
177-
this.#stdoutChalk = stdoutChalk
197+
this.#stdoutChalk = stdoutColor ? new Chalk({ level }) : this.#noColorChalk
178198

179199
this.#stderrColor = stderrColor
180-
this.#stderrChalk = stderrChalk
200+
this.#stderrChalk = stderrColor ? new Chalk({ level }) : this.#noColorChalk
181201

182-
this.#logColors = COLOR_PALETTE({ chalk: stderrChalk })
202+
this.#logColors = COLOR_PALETTE({ chalk: this.#stderrChalk })
183203

184204
this.#levelIndex = LEVEL_OPTIONS[loglevel].index
185205
this.#timing = timing

lib/utils/log-file.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class LogFiles {
7272
}
7373
}
7474

75+
log.verbose('logfile', this.files[0] || 'no logfile created')
76+
7577
// Kickoff cleaning process, even if we aren't writing a logfile.
7678
// This is async but it will always ignore the current logfile
7779
// Return the result so it can be awaited in tests
@@ -234,7 +236,7 @@ class LogFiles {
234236
} catch (e) {
235237
// Disable cleanup failure warnings when log writing is disabled
236238
if (this.#logsMax > 0) {
237-
log.warn('logfile', 'error cleaning log files', e)
239+
log.verbose('logfile', 'error cleaning log files', e)
238240
}
239241
} finally {
240242
log.silly('logfile', 'done cleaning log files')
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* IMPORTANT
2+
* This snapshot file is auto-generated, but designed for humans.
3+
* It should be checked into source control and tracked carefully.
4+
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
5+
* Make sure to inspect the output below. Do not ignore changes!
6+
*/
7+
'use strict'
8+
exports[`test/lib/cli/exit-handler.js TAP handles unknown error with logs and debug file > debug file contents 1`] = `
9+
XX timing npm:load:whichnode Completed in {TIME}ms
10+
XX silly config:load:file:{CWD}/npmrc
11+
XX silly config:load:file:{CWD}/prefix/.npmrc
12+
XX silly config:load:file:{CWD}/home/.npmrc
13+
XX silly config:load:file:{CWD}/global/etc/npmrc
14+
XX timing npm:load:configload Completed in {TIME}ms
15+
XX timing npm:load:mkdirpcache Completed in {TIME}ms
16+
XX timing npm:load:mkdirplogs Completed in {TIME}ms
17+
XX verbose title npm
18+
XX verbose argv "--fetch-retries" "0" "--cache" "{CWD}/cache" "--loglevel" "silly" "--color" "false" "--timing" "true"
19+
XX timing npm:load:setTitle Completed in {TIME}ms
20+
XX verbose logfile logs-max:10 dir:{CWD}/cache/_logs/{DATE}-
21+
XX verbose logfile {CWD}/cache/_logs/{DATE}-debug-0.log
22+
XX timing npm:load Completed in {TIME}ms
23+
XX verbose stack Error: Unknown error
24+
XX verbose cwd {CWD}/prefix
25+
XX verbose {OS}
26+
XX verbose {NODE-VERSION}
27+
XX verbose npm {NPM-VERSION}
28+
XX error code ECODE
29+
XX error ERR SUMMARY Unknown error
30+
XX error ERR DETAIL Unknown error
31+
XX verbose exit 1
32+
XX timing npm Completed in {TIME}ms
33+
XX verbose code 1
34+
XX error Timing info written to: {CWD}/cache/_logs/{DATE}-timing.json
35+
XX error A complete log of this run can be found in: {CWD}/cache/_logs/{DATE}-debug-0.log
36+
`
37+
38+
exports[`test/lib/cli/exit-handler.js TAP handles unknown error with logs and debug file > logs 1`] = `
39+
timing npm:load:whichnode Completed in {TIME}ms
40+
silly config:load:file:{CWD}/npmrc
41+
silly config:load:file:{CWD}/prefix/.npmrc
42+
silly config:load:file:{CWD}/home/.npmrc
43+
silly config:load:file:{CWD}/global/etc/npmrc
44+
timing npm:load:configload Completed in {TIME}ms
45+
timing npm:load:mkdirpcache Completed in {TIME}ms
46+
timing npm:load:mkdirplogs Completed in {TIME}ms
47+
verbose title npm
48+
verbose argv "--fetch-retries" "0" "--cache" "{CWD}/cache" "--loglevel" "silly" "--color" "false" "--timing" "true"
49+
timing npm:load:setTitle Completed in {TIME}ms
50+
verbose logfile logs-max:10 dir:{CWD}/cache/_logs/{DATE}-
51+
verbose logfile {CWD}/cache/_logs/{DATE}-debug-0.log
52+
timing npm:load Completed in {TIME}ms
53+
verbose stack Error: Unknown error
54+
verbose cwd {CWD}/prefix
55+
verbose {OS}
56+
verbose {NODE-VERSION}
57+
verbose npm {NPM-VERSION}
58+
error code ECODE
59+
error ERR SUMMARY Unknown error
60+
error ERR DETAIL Unknown error
61+
verbose exit 1
62+
timing npm Completed in {TIME}ms
63+
verbose code 1
64+
error Timing info written to: {CWD}/cache/_logs/{DATE}-timing.json
65+
error A complete log of this run can be found in: {CWD}/cache/_logs/{DATE}-debug-0.log
66+
`

0 commit comments

Comments
 (0)