@@ -116,81 +116,115 @@ module.exports = {
116116 this . options . verbose && this . serverless . cli . log ( `Fetch dependency graph from ${ packageJsonPath } ` ) ;
117117 // Get first level dependency graph
118118 const command = 'npm ls -prod -json -depth=1' ; // Only prod dependencies
119- let dependencyGraph = { } ;
120- try {
121- const depJson = childProcess . execSync ( command , {
119+
120+ const ignoredNpmErrors = [
121+ { npmError : 'extraneous' , log : false } ,
122+ { npmError : 'missing' , log : false } ,
123+ { npmError : 'peer dep missing' , log : true } ,
124+ ] ;
125+
126+ return BbPromise . fromCallback ( cb => {
127+ childProcess . exec ( command , {
122128 cwd : path . dirname ( packageJsonPath ) ,
123129 maxBuffer : this . serverless . service . custom . packExternalModulesMaxBuffer || 200 * 1024 ,
124130 encoding : 'utf8'
131+ } , ( err , stdout , stderr ) => {
132+ if ( err ) {
133+ // Only exit with an error if we have critical npm errors for 2nd level inside
134+ const errors = _ . split ( stderr , '\n' ) ;
135+ const failed = _ . reduce ( errors , ( failed , error ) => {
136+ if ( failed ) {
137+ return true ;
138+ }
139+ return ! _ . isEmpty ( error ) && ! _ . some ( ignoredNpmErrors , ignoredError => _ . startsWith ( error , `npm ERR! ${ ignoredError . npmError } ` ) ) ;
140+ } , false ) ;
141+
142+ if ( failed ) {
143+ return cb ( err ) ;
144+ }
145+ }
146+ return cb ( null , stdout ) ;
125147 } ) ;
126- dependencyGraph = JSON . parse ( depJson ) ;
127- } catch ( e ) {
128- // We rethrow here. It's not recoverable.
129- throw e ;
130- }
131-
132- // (1) Generate dependency composition
133- const compositeModules = _ . uniq ( _ . flatMap ( stats . stats , compileStats => {
134- const externalModules = getExternalModules . call ( this , compileStats ) ;
135- return getProdModules . call ( this , externalModules , packagePath , dependencyGraph ) ;
136- } ) ) ;
148+ } )
149+ . then ( depJson => BbPromise . try ( ( ) => JSON . parse ( depJson ) ) )
150+ . then ( dependencyGraph => {
151+ const problems = _ . get ( dependencyGraph , 'problems' , [ ] ) ;
152+ if ( this . options . verbose && ! _ . isEmpty ( problems ) ) {
153+ this . serverless . cli . log ( `Ignoring ${ _ . size ( problems ) } NPM errors:` ) ;
154+ _ . forEach ( problems , problem => {
155+ this . serverless . cli . log ( `=> ${ problem } ` ) ;
156+ } ) ;
157+ }
137158
138- // (1.a) Install all needed modules
139- const compositeModulePath = path . join ( this . webpackOutputPath , 'dependencies' ) ;
140- const compositePackageJson = path . join ( compositeModulePath , 'package.json' ) ;
141- this . serverless . utils . writeFileSync ( compositePackageJson , '{}' ) ;
159+ // (1) Generate dependency composition
160+ const compositeModules = _ . uniq ( _ . flatMap ( stats . stats , compileStats => {
161+ const externalModules = getExternalModules . call ( this , compileStats ) ;
162+ return getProdModules . call ( this , externalModules , packagePath , dependencyGraph ) ;
163+ } ) ) ;
142164
143- this . serverless . cli . log ( 'Packing external modules: ' + compositeModules . join ( ', ' ) ) ;
165+ if ( _ . isEmpty ( compositeModules ) ) {
166+ // The compiled code does not reference any external modules at all
167+ this . serverless . cli . log ( 'No external modules needed' ) ;
168+ return BbPromise . resolve ( stats ) ;
169+ }
144170
145- return new BbPromise ( ( resolve , reject ) => {
146- const start = _ . now ( ) ;
147- npm . install ( compositeModules , {
148- cwd : compositeModulePath ,
149- maxBuffer : this . serverless . service . custom . packExternalModulesMaxBuffer || 200 * 1024 ,
150- save : true
151- } ) . then ( ( ) => {
152- this . options . verbose && this . serverless . cli . log ( `Package took [${ _ . now ( ) - start } ms]` ) ; // eslint-disable-line promise/always-return
153- resolve ( stats . stats ) ;
154- } ) . catch ( e => {
155- reject ( e ) ;
156- } ) ;
157- } )
158- . mapSeries ( compileStats => {
159- const modulePath = compileStats . compilation . compiler . outputPath ;
160-
161- // Create package.json
162- const modulePackageJson = path . join ( modulePath , 'package.json' ) ;
163- const modulePackage = {
164- dependencies : { }
165- } ;
166- const prodModules = getProdModules . call ( this , getExternalModules . call ( this , compileStats ) , packagePath , dependencyGraph ) ;
167- _ . forEach ( prodModules , prodModule => {
168- const splitModule = _ . split ( prodModule , '@' ) ;
169- // If we have a scoped module we have to re-add the @
170- if ( _ . startsWith ( prodModule , '@' ) ) {
171- splitModule . splice ( 0 , 1 ) ;
172- splitModule [ 0 ] = '@' + splitModule [ 0 ] ;
173- }
174- const moduleVersion = _ . join ( _ . tail ( splitModule ) , '@' ) ;
175- modulePackage . dependencies [ _ . first ( splitModule ) ] = moduleVersion ;
176- } ) ;
177- this . serverless . utils . writeFileSync ( modulePackageJson , JSON . stringify ( modulePackage , null , 2 ) ) ;
178-
179- // Copy modules
180- const startCopy = _ . now ( ) ;
181- return BbPromise . fromCallback ( callback => fse . copy ( path . join ( compositeModulePath , 'node_modules' ) , path . join ( modulePath , 'node_modules' ) , callback ) )
182- . tap ( ( ) => this . options . verbose && this . serverless . cli . log ( `Copy modules: ${ modulePath } [${ _ . now ( ) - startCopy } ms]` ) )
183- . then ( ( ) => {
184- // Prune extraneous packages - removes not needed ones
185- const startPrune = _ . now ( ) ;
186- return BbPromise . fromCallback ( callback => {
187- childProcess . exec ( 'npm prune' , {
188- cwd : modulePath
189- } , callback ) ;
190- } )
191- . tap ( ( ) => this . options . verbose && this . serverless . cli . log ( `Prune: ${ modulePath } [${ _ . now ( ) - startPrune } ms]` ) ) ;
192- } ) ;
193- } )
194- . return ( stats ) ;
171+ // (1.a) Install all needed modules
172+ const compositeModulePath = path . join ( this . webpackOutputPath , 'dependencies' ) ;
173+ const compositePackageJson = path . join ( compositeModulePath , 'package.json' ) ;
174+ this . serverless . utils . writeFileSync ( compositePackageJson , '{}' ) ;
175+
176+ this . serverless . cli . log ( 'Packing external modules: ' + compositeModules . join ( ', ' ) ) ;
177+
178+ return new BbPromise ( ( resolve , reject ) => {
179+ const start = _ . now ( ) ;
180+ npm . install ( compositeModules , {
181+ cwd : compositeModulePath ,
182+ maxBuffer : this . serverless . service . custom . packExternalModulesMaxBuffer || 200 * 1024 ,
183+ save : true
184+ } ) . then ( ( ) => {
185+ this . options . verbose && this . serverless . cli . log ( `Package took [${ _ . now ( ) - start } ms]` ) ; // eslint-disable-line promise/always-return
186+ resolve ( stats . stats ) ;
187+ } ) . catch ( e => {
188+ reject ( e ) ;
189+ } ) ;
190+ } )
191+ . mapSeries ( compileStats => {
192+ const modulePath = compileStats . compilation . compiler . outputPath ;
193+
194+ // Create package.json
195+ const modulePackageJson = path . join ( modulePath , 'package.json' ) ;
196+ const modulePackage = {
197+ dependencies : { }
198+ } ;
199+ const prodModules = getProdModules . call ( this , getExternalModules . call ( this , compileStats ) , packagePath , dependencyGraph ) ;
200+ _ . forEach ( prodModules , prodModule => {
201+ const splitModule = _ . split ( prodModule , '@' ) ;
202+ // If we have a scoped module we have to re-add the @
203+ if ( _ . startsWith ( prodModule , '@' ) ) {
204+ splitModule . splice ( 0 , 1 ) ;
205+ splitModule [ 0 ] = '@' + splitModule [ 0 ] ;
206+ }
207+ const moduleVersion = _ . join ( _ . tail ( splitModule ) , '@' ) ;
208+ modulePackage . dependencies [ _ . first ( splitModule ) ] = moduleVersion ;
209+ } ) ;
210+ this . serverless . utils . writeFileSync ( modulePackageJson , JSON . stringify ( modulePackage , null , 2 ) ) ;
211+
212+ // Copy modules
213+ const startCopy = _ . now ( ) ;
214+ return BbPromise . fromCallback ( callback => fse . copy ( path . join ( compositeModulePath , 'node_modules' ) , path . join ( modulePath , 'node_modules' ) , callback ) )
215+ . tap ( ( ) => this . options . verbose && this . serverless . cli . log ( `Copy modules: ${ modulePath } [${ _ . now ( ) - startCopy } ms]` ) )
216+ . then ( ( ) => {
217+ // Prune extraneous packages - removes not needed ones
218+ const startPrune = _ . now ( ) ;
219+ return BbPromise . fromCallback ( callback => {
220+ childProcess . exec ( 'npm prune' , {
221+ cwd : modulePath
222+ } , callback ) ;
223+ } )
224+ . tap ( ( ) => this . options . verbose && this . serverless . cli . log ( `Prune: ${ modulePath } [${ _ . now ( ) - startPrune } ms]` ) ) ;
225+ } ) ;
226+ } )
227+ . return ( stats ) ;
228+ } ) ;
195229 }
196230} ;
0 commit comments