1- const log = require ( 'npmlog' )
21const os = require ( 'os' )
32const path = require ( 'path' )
43const writeFileAtomic = require ( 'write-file-atomic' )
@@ -13,8 +12,6 @@ let logFileName
1312let npm // set by the cli
1413let wroteLogFile = false
1514
16- const timings = { }
17-
1815const getLogFile = ( ) => {
1916 // we call this multiple times, so we need to treat it as a singleton because
2017 // the date is part of the name
@@ -24,20 +21,13 @@ const getLogFile = () => {
2421 return logFileName
2522}
2623
27- process . on ( 'timing' , ( name , value ) => {
28- if ( timings [ name ] )
29- timings [ name ] += value
30- else
31- timings [ name ] = value
32- } )
33-
3424process . on ( 'exit' , code => {
3525 // process.emit is synchronous, so the timeEnd handler will run before the
3626 // unfinished timer check below
3727 process . emit ( 'timeEnd' , 'npm' )
38- log . disableProgress ( )
28+ npm . log . disableProgress ( )
3929 for ( const [ name , timers ] of npm . timers )
40- log . verbose ( 'unfinished npm timer' , name , timers )
30+ npm . log . verbose ( 'unfinished npm timer' , name , timers )
4131
4232 if ( npm . config . loaded && npm . config . get ( 'timing' ) ) {
4333 try {
@@ -49,7 +39,7 @@ process.on('exit', code => {
4939 command : process . argv . slice ( 2 ) ,
5040 logfile : getLogFile ( ) ,
5141 version : npm . version ,
52- ...timings ,
42+ ...npm . timings ,
5343 } ) + '\n' )
5444
5545 const st = fs . lstatSync ( path . dirname ( npm . config . get ( 'cache' ) ) )
@@ -61,14 +51,14 @@ process.on('exit', code => {
6151 }
6252
6353 if ( ! code )
64- log . info ( 'ok' )
54+ npm . log . info ( 'ok' )
6555 else {
66- log . verbose ( 'code' , code )
56+ npm . log . verbose ( 'code' , code )
6757 if ( ! exitHandlerCalled ) {
68- log . error ( '' , 'Exit handler never called!' )
58+ npm . log . error ( '' , 'Exit handler never called!' )
6959 console . error ( '' )
70- log . error ( '' , 'This is an error with npm itself. Please report this error at:' )
71- log . error ( '' , ' <https://github.com/npm/cli/issues>' )
60+ npm . log . error ( '' , 'This is an error with npm itself. Please report this error at:' )
61+ npm . log . error ( '' , ' <https://github.com/npm/cli/issues>' )
7262 // TODO this doesn't have an npm.config.loaded guard
7363 writeLogFile ( )
7464 }
@@ -78,10 +68,10 @@ process.on('exit', code => {
7868 writeLogFile ( )
7969 if ( wroteLogFile ) {
8070 // just a line break
81- if ( log . levels [ log . level ] <= log . levels . error )
71+ if ( npm . log . levels [ npm . log . level ] <= npm . log . levels . error )
8272 console . error ( '' )
8373
84- log . error (
74+ npm . log . error (
8575 '' ,
8676 [
8777 'A complete log of this run can be found in:' ,
@@ -93,121 +83,114 @@ process.on('exit', code => {
9383 // these are needed for the tests to have a clean slate in each test case
9484 exitHandlerCalled = false
9585 wroteLogFile = false
96-
97- // actually exit.
98- process . exit ( code )
9986} )
10087
101- const exit = ( code , noLog ) => {
102- log . verbose ( 'exit' , code || 0 )
103- if ( log . level === 'silent' )
104- noLog = true
105-
106- // noLog is true if there was an error, including if config wasn't loaded, so
107- // this doesn't need a config.loaded guard
108- if ( code && ! noLog )
109- writeLogFile ( )
110-
111- // Exit directly -- nothing in the CLI should still be running in the
112- // background at this point, and this makes sure anything left dangling
113- // for whatever reason gets thrown away, instead of leaving the CLI open
114- process . stdout . write ( '' , ( ) => {
115- process . exit ( code )
116- } )
117- }
118-
11988const exitHandler = ( err ) => {
120- log . disableProgress ( )
89+ npm . log . disableProgress ( )
12190 if ( ! npm . config . loaded ) {
122- // logging won't work unless we pretend that it's ready
12391 err = err || new Error ( 'Exit prior to config file resolving.' )
12492 console . error ( err . stack || err . message )
12593 }
12694
127- if ( exitHandlerCalled )
128- err = err || new Error ( 'Exit handler called more than once.' )
129-
130- // only show the notification if it finished before the other stuff we
131- // were doing. no need to hang on `npm -v` or something.
95+ // only show the notification if it finished.
13296 if ( typeof npm . updateNotification === 'string' ) {
133- const { level } = log
134- log . level = log . levels . notice
135- log . notice ( '' , npm . updateNotification )
136- log . level = level
97+ const { level } = npm . log
98+ npm . log . level = ' notice'
99+ npm . log . notice ( '' , npm . updateNotification )
100+ npm . log . level = level
137101 }
138102
139103 exitHandlerCalled = true
140- if ( ! err )
141- return exit ( )
142-
143- // if we got a command that just shells out to something else, then it
144- // will presumably print its own errors and exit with a proper status
145- // code if there's a problem. If we got an error with a code=0, then...
146- // something else went wrong along the way, so maybe an npm problem?
147- const isShellout = npm . shelloutCommands . includes ( npm . command )
148- const quietShellout = isShellout && typeof err . code === 'number' && err . code
149- if ( quietShellout )
150- return exit ( err . code , true )
151- else if ( typeof err === 'string' ) {
152- log . error ( '' , err )
153- return exit ( 1 , true )
154- } else if ( ! ( err instanceof Error ) ) {
155- log . error ( 'weird error' , err )
156- return exit ( 1 , true )
157- }
158104
159- if ( ! err . code ) {
160- const matchErrorCode = err . message . match ( / ^ (?: E r r o r : ) ? ( E [ A - Z ] + ) / )
161- err . code = matchErrorCode && matchErrorCode [ 1 ]
162- }
163-
164- for ( const k of [ 'type' , 'stack' , 'statusCode' , 'pkgid' ] ) {
165- const v = err [ k ]
166- if ( v )
167- log . verbose ( k , replaceInfo ( v ) )
105+ let exitCode
106+ let noLog
107+
108+ if ( err ) {
109+ exitCode = 1
110+ // if we got a command that just shells out to something else, then it
111+ // will presumably print its own errors and exit with a proper status
112+ // code if there's a problem. If we got an error with a code=0, then...
113+ // something else went wrong along the way, so maybe an npm problem?
114+ const isShellout = npm . shelloutCommands . includes ( npm . command )
115+ const quietShellout = isShellout && typeof err . code === 'number' && err . code
116+ if ( quietShellout ) {
117+ exitCode = err . code
118+ noLog = true
119+ } else if ( typeof err === 'string' ) {
120+ noLog = true
121+ npm . log . error ( '' , err )
122+ } else if ( ! ( err instanceof Error ) ) {
123+ noLog = true
124+ npm . log . error ( 'weird error' , err )
125+ } else {
126+ if ( ! err . code ) {
127+ const matchErrorCode = err . message . match ( / ^ (?: E r r o r : ) ? ( E [ A - Z ] + ) / )
128+ err . code = matchErrorCode && matchErrorCode [ 1 ]
129+ }
130+
131+ for ( const k of [ 'type' , 'stack' , 'statusCode' , 'pkgid' ] ) {
132+ const v = err [ k ]
133+ if ( v )
134+ npm . log . verbose ( k , replaceInfo ( v ) )
135+ }
136+
137+ npm . log . verbose ( 'cwd' , process . cwd ( ) )
138+
139+ const args = replaceInfo ( process . argv )
140+ npm . log . verbose ( '' , os . type ( ) + ' ' + os . release ( ) )
141+ npm . log . verbose ( 'argv' , args . map ( JSON . stringify ) . join ( ' ' ) )
142+ npm . log . verbose ( 'node' , process . version )
143+ npm . log . verbose ( 'npm ' , 'v' + npm . version )
144+
145+ for ( const k of [ 'code' , 'syscall' , 'file' , 'path' , 'dest' , 'errno' ] ) {
146+ const v = err [ k ]
147+ if ( v )
148+ npm . log . error ( k , v )
149+ }
150+
151+ const msg = errorMessage ( err , npm )
152+ for ( const errline of [ ...msg . summary , ...msg . detail ] )
153+ npm . log . error ( ...errline )
154+
155+ if ( npm . config . loaded && npm . config . get ( 'json' ) ) {
156+ const error = {
157+ error : {
158+ code : err . code ,
159+ summary : messageText ( msg . summary ) ,
160+ detail : messageText ( msg . detail ) ,
161+ } ,
162+ }
163+ console . error ( JSON . stringify ( error , null , 2 ) )
164+ }
165+
166+ if ( typeof err . errno === 'number' )
167+ exitCode = err . errno
168+ else if ( typeof err . code === 'number' )
169+ exitCode = err . code
170+ }
168171 }
172+ npm . log . verbose ( 'exit' , exitCode || 0 )
169173
170- log . verbose ( 'cwd' , process . cwd ( ) )
171-
172- const args = replaceInfo ( process . argv )
173- log . verbose ( '' , os . type ( ) + ' ' + os . release ( ) )
174- log . verbose ( 'argv' , args . map ( JSON . stringify ) . join ( ' ' ) )
175- log . verbose ( 'node' , process . version )
176- log . verbose ( 'npm ' , 'v' + npm . version )
177-
178- for ( const k of [ 'code' , 'syscall' , 'file' , 'path' , 'dest' , 'errno' ] ) {
179- const v = err [ k ]
180- if ( v )
181- log . error ( k , v )
182- }
174+ if ( npm . log . level === 'silent' )
175+ noLog = true
183176
184- const msg = errorMessage ( err , npm )
185- for ( const errline of [ ...msg . summary , ...msg . detail ] )
186- log . error ( ...errline )
187-
188- if ( npm . config . loaded && npm . config . get ( 'json' ) ) {
189- const error = {
190- error : {
191- code : err . code ,
192- summary : messageText ( msg . summary ) ,
193- detail : messageText ( msg . detail ) ,
194- } ,
195- }
196- console . error ( JSON . stringify ( error , null , 2 ) )
197- }
177+ // noLog is true if there was an error, including if config wasn't loaded, so
178+ // this doesn't need a config.loaded guard
179+ if ( exitCode && ! noLog )
180+ writeLogFile ( )
198181
199- exit ( typeof err . errno === 'number' ? err . errno : typeof err . code === 'number' ? err . code : 1 )
182+ // explicitly call process.exit now so we don't hang on things like the
183+ // update notifier, also flush stdout beforehand because process.exit doesn't
184+ // wait for that to happen.
185+ process . stdout . write ( '' , ( ) => process . exit ( exitCode ) )
200186}
201187
202188const messageText = msg => msg . map ( line => line . slice ( 1 ) . join ( ' ' ) ) . join ( '\n' )
203189
204190const writeLogFile = ( ) => {
205- if ( wroteLogFile )
206- return
207-
208191 try {
209192 let logOutput = ''
210- log . record . forEach ( m => {
193+ npm . log . record . forEach ( m => {
211194 const p = [ m . id , m . level ]
212195 if ( m . prefix )
213196 p . push ( m . prefix )
@@ -230,7 +213,7 @@ const writeLogFile = () => {
230213 fs . chownSync ( file , st . uid , st . gid )
231214
232215 // truncate once it's been written.
233- log . record . length = 0
216+ npm . log . record . length = 0
234217 wroteLogFile = true
235218 } catch ( ex ) {
236219
0 commit comments