@@ -16,6 +16,7 @@ const byline = require('byline')
16
16
const resolveFrom = require ( 'resolve-from' )
17
17
18
18
const DEFAULT_NODE_GYP_PATH = resolveFrom ( __dirname , 'node-gyp/bin/node-gyp' )
19
+ const hookStatCache = new Map ( )
19
20
20
21
let PATH = 'PATH'
21
22
@@ -33,6 +34,20 @@ function logid (pkg, stage) {
33
34
return pkg . _id + '~' + stage + ':'
34
35
}
35
36
37
+ function hookStat ( dir , stage , cb ) {
38
+ const hook = path . join ( dir , '.hooks' , stage )
39
+ const cachedStatError = hookStatCache . get ( hook )
40
+
41
+ if ( cachedStatError === undefined ) {
42
+ return fs . stat ( hook , function ( statError ) {
43
+ hookStatCache . set ( hook , statError )
44
+ cb ( statError )
45
+ } )
46
+ }
47
+
48
+ return setImmediate ( ( ) => cb ( cachedStatError ) )
49
+ }
50
+
36
51
function lifecycle ( pkg , stage , wd , opts ) {
37
52
return new Promise ( ( resolve , reject ) => {
38
53
while ( pkg && pkg . _data ) pkg = pkg . _data
@@ -46,32 +61,36 @@ function lifecycle (pkg, stage, wd, opts) {
46
61
delete pkg . scripts . prepublish
47
62
}
48
63
49
- if ( ! pkg . scripts [ stage ] ) return resolve ( )
50
-
51
- validWd ( wd || path . resolve ( opts . dir , pkg . name ) , function ( er , wd ) {
52
- if ( er ) return reject ( er )
64
+ hookStat ( opts . dir , stage , function ( statError ) {
65
+ // makeEnv is a slow operation. This guard clause prevents makeEnv being called
66
+ // and avoids a ton of unnecessary work, and results in a major perf boost.
67
+ if ( ! pkg . scripts [ stage ] && statError ) return resolve ( )
53
68
54
- if ( ( wd . indexOf ( opts . dir ) !== 0 || _incorrectWorkingDirectory ( wd , pkg ) ) &&
55
- ! opts . unsafePerm && pkg . scripts [ stage ] ) {
56
- opts . log . warn ( 'lifecycle' , logid ( pkg , stage ) , 'cannot run in wd' , pkg . _id , pkg . scripts [ stage ] , `(wd=${ wd } )` )
57
- return resolve ( )
58
- }
59
-
60
- // set the env variables, then run scripts as a child process.
61
- var env = makeEnv ( pkg , opts )
62
- env . npm_lifecycle_event = stage
63
- env . npm_node_execpath = env . NODE = env . NODE || process . execPath
64
- env . npm_execpath = require . main . filename
65
- env . INIT_CWD = process . cwd ( )
66
- env . npm_config_node_gyp = env . npm_config_node_gyp || DEFAULT_NODE_GYP_PATH
69
+ validWd ( wd || path . resolve ( opts . dir , pkg . name ) , function ( er , wd ) {
70
+ if ( er ) return reject ( er )
67
71
68
- // 'nobody' typically doesn't have permission to write to /tmp
69
- // even if it's never used, sh freaks out.
70
- if ( ! opts . unsafePerm ) env . TMPDIR = wd
72
+ if ( ( wd . indexOf ( opts . dir ) !== 0 || _incorrectWorkingDirectory ( wd , pkg ) ) &&
73
+ ! opts . unsafePerm && pkg . scripts [ stage ] ) {
74
+ opts . log . warn ( 'lifecycle' , logid ( pkg , stage ) , 'cannot run in wd' , pkg . _id , pkg . scripts [ stage ] , `(wd=${ wd } )` )
75
+ return resolve ( )
76
+ }
71
77
72
- lifecycle_ ( pkg , stage , wd , opts , env , ( er ) => {
73
- if ( er ) return reject ( er )
74
- return resolve ( )
78
+ // set the env variables, then run scripts as a child process.
79
+ var env = makeEnv ( pkg , opts )
80
+ env . npm_lifecycle_event = stage
81
+ env . npm_node_execpath = env . NODE = env . NODE || process . execPath
82
+ env . npm_execpath = require . main . filename
83
+ env . INIT_CWD = process . cwd ( )
84
+ env . npm_config_node_gyp = env . npm_config_node_gyp || DEFAULT_NODE_GYP_PATH
85
+
86
+ // 'nobody' typically doesn't have permission to write to /tmp
87
+ // even if it's never used, sh freaks out.
88
+ if ( ! opts . unsafePerm ) env . TMPDIR = wd
89
+
90
+ lifecycle_ ( pkg , stage , wd , opts , env , ( er ) => {
91
+ if ( er ) return reject ( er )
92
+ return resolve ( )
93
+ } )
75
94
} )
76
95
} )
77
96
} )
@@ -131,8 +150,8 @@ function lifecycle_ (pkg, stage, wd, opts, env, cb) {
131
150
132
151
chain (
133
152
[
134
- packageLifecycle && [ runPackageLifecycle , pkg , env , wd , opts ] ,
135
- [ runHookLifecycle , pkg , env , wd , opts ]
153
+ packageLifecycle && [ runPackageLifecycle , pkg , stage , env , wd , opts ] ,
154
+ [ runHookLifecycle , pkg , stage , env , wd , opts ]
136
155
] ,
137
156
done
138
157
)
@@ -185,9 +204,8 @@ function validWd (d, cb) {
185
204
} )
186
205
}
187
206
188
- function runPackageLifecycle ( pkg , env , wd , opts , cb ) {
207
+ function runPackageLifecycle ( pkg , stage , env , wd , opts , cb ) {
189
208
// run package lifecycle scripts in the package root, or the nearest parent.
190
- var stage = env . npm_lifecycle_event
191
209
var cmd = env . npm_lifecycle_script
192
210
193
211
var note = '\n> ' + pkg . _id + ' ' + stage + ' ' + wd +
@@ -329,17 +347,13 @@ function runCmd_ (cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
329
347
}
330
348
}
331
349
332
- function runHookLifecycle ( pkg , env , wd , opts , cb ) {
333
- // check for a hook script, run if present.
334
- var stage = env . npm_lifecycle_event
335
- var hook = path . join ( opts . dir , '.hooks' , stage )
336
- var cmd = hook
337
-
338
- fs . stat ( hook , function ( er ) {
350
+ function runHookLifecycle ( pkg , stage , env , wd , opts , cb ) {
351
+ hookStat ( opts . dir , stage , function ( er ) {
339
352
if ( er ) return cb ( )
353
+ var cmd = path . join ( opts . dir , '.hooks' , stage )
340
354
var note = '\n> ' + pkg . _id + ' ' + stage + ' ' + wd +
341
355
'\n> ' + cmd
342
- runCmd ( note , hook , pkg , env , stage , wd , opts , cb )
356
+ runCmd ( note , cmd , pkg , env , stage , wd , opts , cb )
343
357
} )
344
358
}
345
359
0 commit comments