@@ -16,6 +16,7 @@ const byline = require('byline')
1616const resolveFrom = require ( 'resolve-from' )
1717
1818const DEFAULT_NODE_GYP_PATH = resolveFrom ( __dirname , 'node-gyp/bin/node-gyp' )
19+ const hookStatCache = new Map ( )
1920
2021let PATH = 'PATH'
2122
@@ -33,6 +34,20 @@ function logid (pkg, stage) {
3334 return pkg . _id + '~' + stage + ':'
3435}
3536
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+
3651function lifecycle ( pkg , stage , wd , opts ) {
3752 return new Promise ( ( resolve , reject ) => {
3853 while ( pkg && pkg . _data ) pkg = pkg . _data
@@ -46,32 +61,36 @@ function lifecycle (pkg, stage, wd, opts) {
4661 delete pkg . scripts . prepublish
4762 }
4863
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 ( )
5368
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 )
6771
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+ }
7177
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+ } )
7594 } )
7695 } )
7796 } )
@@ -131,8 +150,8 @@ function lifecycle_ (pkg, stage, wd, opts, env, cb) {
131150
132151 chain (
133152 [
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 ]
136155 ] ,
137156 done
138157 )
@@ -185,9 +204,8 @@ function validWd (d, cb) {
185204 } )
186205}
187206
188- function runPackageLifecycle ( pkg , env , wd , opts , cb ) {
207+ function runPackageLifecycle ( pkg , stage , env , wd , opts , cb ) {
189208 // run package lifecycle scripts in the package root, or the nearest parent.
190- var stage = env . npm_lifecycle_event
191209 var cmd = env . npm_lifecycle_script
192210
193211 var note = '\n> ' + pkg . _id + ' ' + stage + ' ' + wd +
@@ -329,17 +347,13 @@ function runCmd_ (cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
329347 }
330348}
331349
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 ) {
339352 if ( er ) return cb ( )
353+ var cmd = path . join ( opts . dir , '.hooks' , stage )
340354 var note = '\n> ' + pkg . _id + ' ' + stage + ' ' + wd +
341355 '\n> ' + cmd
342- runCmd ( note , hook , pkg , env , stage , wd , opts , cb )
356+ runCmd ( note , cmd , pkg , env , stage , wd , opts , cb )
343357 } )
344358}
345359
0 commit comments