diff --git a/lib/packExternalModules.js b/lib/packExternalModules.js index 6b1fc9ef45..0c7ab2f98d 100644 --- a/lib/packExternalModules.js +++ b/lib/packExternalModules.js @@ -153,20 +153,25 @@ function findExternalOrigin(issuer) { } function getExternalModules(stats) { + if (!stats.compilation.chunks) { + return []; + } const externals = new Set(); + for (const chunk of stats.compilation.chunks) { + if (!chunk.modulesIterable) { + continue; + } - _.forEach(stats.compilation.chunks, chunk => { // Explore each module within the chunk (built inputs): - chunk.forEachModule(module => { + for (const module of chunk.modulesIterable) { if (isExternalModule(module)) { externals.add({ origin: _.get(findExternalOrigin(module.issuer), 'rawRequest'), external: getExternalModuleName(module) }); } - }); - }); - + } + } return Array.from(externals); } @@ -206,146 +211,146 @@ module.exports = { // Determine and create packager return BbPromise.try(() => Packagers.get.call(this, this.configuration.packager)) - .then(packager => { - // Fetch needed original package.json sections - const sectionNames = packager.copyPackageSectionNames; - const packageJson = this.serverless.utils.readFileSync(packageJsonPath); - const packageSections = _.pick(packageJson, sectionNames); - if (!_.isEmpty(packageSections)) { - this.options.verbose && this.serverless.cli.log(`Using package.json sections ${_.join(_.keys(packageSections), ', ')}`); - } + .then(packager => { + // Fetch needed original package.json sections + const sectionNames = packager.copyPackageSectionNames; + const packageJson = this.serverless.utils.readFileSync(packageJsonPath); + const packageSections = _.pick(packageJson, sectionNames); + if (!_.isEmpty(packageSections)) { + this.options.verbose && this.serverless.cli.log(`Using package.json sections ${_.join(_.keys(packageSections), ', ')}`); + } - // Get first level dependency graph - this.options.verbose && this.serverless.cli.log(`Fetch dependency graph from ${packageJsonPath}`); + // Get first level dependency graph + this.options.verbose && this.serverless.cli.log(`Fetch dependency graph from ${packageJsonPath}`); - return packager.getProdDependencies(path.dirname(packageJsonPath), 1) - .then(dependencyGraph => { - const problems = _.get(dependencyGraph, 'problems', []); - if (this.options.verbose && !_.isEmpty(problems)) { - this.serverless.cli.log(`Ignoring ${_.size(problems)} NPM errors:`); - _.forEach(problems, problem => { - this.serverless.cli.log(`=> ${problem}`); - }); - } - - // (1) Generate dependency composition - const compositeModules = _.uniq(_.flatMap(stats.stats, compileStats => { - const externalModules = _.concat( - getExternalModules.call(this, compileStats), - _.map(packageForceIncludes, whitelistedPackage => ({ external: whitelistedPackage })) - ); - return getProdModules.call(this, externalModules, packagePath, dependencyGraph, packageForceExcludes); - })); - removeExcludedModules.call(this, compositeModules, packageForceExcludes, true); - - if (_.isEmpty(compositeModules)) { - // The compiled code does not reference any external modules at all - this.serverless.cli.log('No external modules needed'); - return BbPromise.resolve(); - } - - // (1.a) Install all needed modules - const compositeModulePath = path.join(this.webpackOutputPath, 'dependencies'); - const compositePackageJson = path.join(compositeModulePath, 'package.json'); - - // (1.a.1) Create a package.json - const compositePackage = _.defaults({ - name: this.serverless.service.service, - version: '1.0.0', - description: `Packaged externals for ${this.serverless.service.service}`, - private: true, - scripts: packageScripts - }, packageSections); - const relPath = path.relative(compositeModulePath, path.dirname(packageJsonPath)); - addModulesToPackageJson(compositeModules, compositePackage, relPath); - this.serverless.utils.writeFileSync(compositePackageJson, JSON.stringify(compositePackage, null, 2)); - - // (1.a.2) Copy package-lock.json if it exists, to prevent unwanted upgrades - const packageLockPath = path.join(path.dirname(packageJsonPath), packager.lockfileName); - let hasPackageLock = false; - return BbPromise.fromCallback(cb => fse.pathExists(packageLockPath, cb)) - .then(exists => { - if (exists) { - this.serverless.cli.log('Package lock found - Using locked versions'); - try { - let packageLockFile = this.serverless.utils.readFileSync(packageLockPath); - packageLockFile = packager.rebaseLockfile(relPath, packageLockFile); - if (_.isObject(packageLockFile)) { - packageLockFile = JSON.stringify(packageLockFile, null, 2); - } - - this.serverless.utils.writeFileSync(path.join(compositeModulePath, packager.lockfileName), packageLockFile); - hasPackageLock = true; - } catch(err) { - this.serverless.cli.log(`Warning: Could not read lock file: ${err.message}`); + return packager.getProdDependencies(path.dirname(packageJsonPath), 1) + .then(dependencyGraph => { + const problems = _.get(dependencyGraph, 'problems', []); + if (this.options.verbose && !_.isEmpty(problems)) { + this.serverless.cli.log(`Ignoring ${_.size(problems)} NPM errors:`); + _.forEach(problems, problem => { + this.serverless.cli.log(`=> ${problem}`); + }); } - } - return BbPromise.resolve(); - }) - .then(() => { - const start = _.now(); - this.serverless.cli.log('Packing external modules: ' + compositeModules.join(', ')); - return packager.install(compositeModulePath, this.configuration.packagerOptions) - .then(() => this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`)) - .return(stats.stats); - }) - .mapSeries(compileStats => { - const modulePath = compileStats.compilation.compiler.outputPath; - - // Create package.json - const modulePackageJson = path.join(modulePath, 'package.json'); - const modulePackage = _.defaults({ - name: this.serverless.service.service, - version: '1.0.0', - description: `Packaged externals for ${this.serverless.service.service}`, - private: true, - scripts: packageScripts, - dependencies: {} - }, packageSections); - const prodModules = getProdModules.call(this, - _.concat( - getExternalModules.call(this, compileStats), - _.map(packageForceIncludes, whitelistedPackage => ({ external: whitelistedPackage })) - ), packagePath, dependencyGraph, packageForceExcludes); - removeExcludedModules.call(this, prodModules, packageForceExcludes); - const relPath = path.relative(modulePath, path.dirname(packageJsonPath)); - addModulesToPackageJson(prodModules, modulePackage, relPath); - this.serverless.utils.writeFileSync(modulePackageJson, JSON.stringify(modulePackage, null, 2)); - - // GOOGLE: Copy modules only if not google-cloud-functions - // GCF Auto installs the package json - if (_.get(this.serverless, 'service.provider.name') === 'google') { - return BbPromise.resolve(); - } - - const startCopy = _.now(); - return BbPromise.try(() => { - // Only copy dependency modules if demanded by packager - if (packager.mustCopyModules) { - return BbPromise.fromCallback(callback => fse.copy(path.join(compositeModulePath, 'node_modules'), path.join(modulePath, 'node_modules'), callback)); + + // (1) Generate dependency composition + const compositeModules = _.uniq(_.flatMap(stats.stats, compileStats => { + const externalModules = _.concat( + getExternalModules.call(this, compileStats), + _.map(packageForceIncludes, whitelistedPackage => ({ external: whitelistedPackage })) + ); + return getProdModules.call(this, externalModules, packagePath, dependencyGraph, packageForceExcludes); + })); + removeExcludedModules.call(this, compositeModules, packageForceExcludes, true); + + if (_.isEmpty(compositeModules)) { + // The compiled code does not reference any external modules at all + this.serverless.cli.log('No external modules needed'); + return BbPromise.resolve(); } - return BbPromise.resolve(); - }) - .then(() => hasPackageLock ? - BbPromise.fromCallback(callback => fse.copy(path.join(compositeModulePath, packager.lockfileName), path.join(modulePath, packager.lockfileName), callback)) : - BbPromise.resolve() - ) - .tap(() => this.options.verbose && this.serverless.cli.log(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`)) - .then(() => { - // Prune extraneous packages - removes not needed ones - const startPrune = _.now(); - return packager.prune(modulePath, this.configuration.packagerOptions) - .tap(() => this.options.verbose && this.serverless.cli.log(`Prune: ${modulePath} [${_.now() - startPrune} ms]`)); - }) - .then(() => { - // Prune extraneous packages - removes not needed ones - const startRunScripts = _.now(); - return packager.runScripts(modulePath, _.keys(packageScripts)) - .tap(() => this.options.verbose && this.serverless.cli.log(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`)); + + // (1.a) Install all needed modules + const compositeModulePath = path.join(this.webpackOutputPath, 'dependencies'); + const compositePackageJson = path.join(compositeModulePath, 'package.json'); + + // (1.a.1) Create a package.json + const compositePackage = _.defaults({ + name: this.serverless.service.service, + version: '1.0.0', + description: `Packaged externals for ${this.serverless.service.service}`, + private: true, + scripts: packageScripts + }, packageSections); + const relPath = path.relative(compositeModulePath, path.dirname(packageJsonPath)); + addModulesToPackageJson(compositeModules, compositePackage, relPath); + this.serverless.utils.writeFileSync(compositePackageJson, JSON.stringify(compositePackage, null, 2)); + + // (1.a.2) Copy package-lock.json if it exists, to prevent unwanted upgrades + const packageLockPath = path.join(path.dirname(packageJsonPath), packager.lockfileName); + let hasPackageLock = false; + return BbPromise.fromCallback(cb => fse.pathExists(packageLockPath, cb)) + .then(exists => { + if (exists) { + this.serverless.cli.log('Package lock found - Using locked versions'); + try { + let packageLockFile = this.serverless.utils.readFileSync(packageLockPath); + packageLockFile = packager.rebaseLockfile(relPath, packageLockFile); + if (_.isObject(packageLockFile)) { + packageLockFile = JSON.stringify(packageLockFile, null, 2); + } + + this.serverless.utils.writeFileSync(path.join(compositeModulePath, packager.lockfileName), packageLockFile); + hasPackageLock = true; + } catch (err) { + this.serverless.cli.log(`Warning: Could not read lock file: ${err.message}`); + } + } + return BbPromise.resolve(); + }) + .then(() => { + const start = _.now(); + this.serverless.cli.log('Packing external modules: ' + compositeModules.join(', ')); + return packager.install(compositeModulePath, this.configuration.packagerOptions) + .then(() => this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`)) + .return(stats.stats); + }) + .mapSeries(compileStats => { + const modulePath = compileStats.compilation.compiler.outputPath; + + // Create package.json + const modulePackageJson = path.join(modulePath, 'package.json'); + const modulePackage = _.defaults({ + name: this.serverless.service.service, + version: '1.0.0', + description: `Packaged externals for ${this.serverless.service.service}`, + private: true, + scripts: packageScripts, + dependencies: {} + }, packageSections); + const prodModules = getProdModules.call(this, + _.concat( + getExternalModules.call(this, compileStats), + _.map(packageForceIncludes, whitelistedPackage => ({ external: whitelistedPackage })) + ), packagePath, dependencyGraph, packageForceExcludes); + removeExcludedModules.call(this, prodModules, packageForceExcludes); + const relPath = path.relative(modulePath, path.dirname(packageJsonPath)); + addModulesToPackageJson(prodModules, modulePackage, relPath); + this.serverless.utils.writeFileSync(modulePackageJson, JSON.stringify(modulePackage, null, 2)); + + // GOOGLE: Copy modules only if not google-cloud-functions + // GCF Auto installs the package json + if (_.get(this.serverless, 'service.provider.name') === 'google') { + return BbPromise.resolve(); + } + + const startCopy = _.now(); + return BbPromise.try(() => { + // Only copy dependency modules if demanded by packager + if (packager.mustCopyModules) { + return BbPromise.fromCallback(callback => fse.copy(path.join(compositeModulePath, 'node_modules'), path.join(modulePath, 'node_modules'), callback)); + } + return BbPromise.resolve(); + }) + .then(() => hasPackageLock ? + BbPromise.fromCallback(callback => fse.copy(path.join(compositeModulePath, packager.lockfileName), path.join(modulePath, packager.lockfileName), callback)) : + BbPromise.resolve() + ) + .tap(() => this.options.verbose && this.serverless.cli.log(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`)) + .then(() => { + // Prune extraneous packages - removes not needed ones + const startPrune = _.now(); + return packager.prune(modulePath, this.configuration.packagerOptions) + .tap(() => this.options.verbose && this.serverless.cli.log(`Prune: ${modulePath} [${_.now() - startPrune} ms]`)); + }) + .then(() => { + // Prune extraneous packages - removes not needed ones + const startRunScripts = _.now(); + return packager.runScripts(modulePath, _.keys(packageScripts)) + .tap(() => this.options.verbose && this.serverless.cli.log(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`)); + }); + }) + .return(); }); - }) - .return(); }); - }); } }; diff --git a/tests/packExternalModules.test.js b/tests/packExternalModules.test.js index c608396ae2..0ed594f80f 100644 --- a/tests/packExternalModules.test.js +++ b/tests/packExternalModules.test.js @@ -25,8 +25,14 @@ class ChunkMock { this._modules = modules; } - forEachModule(fn) { - _.forEach(this._modules, fn); + get modulesIterable() { + return this._modules; + } +} + +class ChunkMockNoModulesIterable { + constructor(modules) { + this._modules = modules; } } @@ -34,7 +40,7 @@ const packagerMockFactory = { create(sandbox) { const packagerMock = { lockfileName: 'mocked-lock.json', - copyPackageSectionNames: [ 'section1', 'section2' ], + copyPackageSectionNames: ['section1', 'section2'], mustCopyModules: true, rebaseLockfile: sandbox.stub(), getProdDependencies: sandbox.stub(), @@ -106,10 +112,10 @@ describe('packExternalModules', () => { options: { verbose: true }, - configuration: new Configuration({ + configuration: new Configuration({ webpack: { includeModules: true - } + } }) }, baseModule); }); @@ -152,7 +158,10 @@ describe('packExternalModules', () => { { identifier: _.constant('external "bluebird"') }, - ]) + ]), + new ChunkMockNoModulesIterable([ + + ]), ], compiler: { outputPath: '/my/Service/Path/.webpack/service' @@ -316,11 +325,11 @@ describe('packExternalModules', () => { module.configuration = new Configuration(); module.compileStats = { stats: [] }; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - expect(fsExtraMock.copy).to.not.have.been.called, - expect(packagerFactoryMock.get).to.not.have.been.called, - expect(writeFileSyncStub).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + expect(fsExtraMock.copy).to.not.have.been.called, + expect(packagerFactoryMock.get).to.not.have.been.called, + expect(writeFileSyncStub).to.not.have.been.called, + ])); }); it('should copy needed package sections if available', () => { @@ -331,7 +340,7 @@ describe('packExternalModules', () => { private: true, scripts: {}, section1: { - value: 'myValue' + value: 'myValue' }, dependencies: { '@scoped/vendor': '1.0.0', @@ -377,19 +386,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should install external modules', () => { @@ -427,19 +436,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should rebase file references', () => { @@ -513,25 +522,25 @@ describe('packExternalModules', () => { mockery.registerMock(path.join(process.cwd(), 'locals', 'package.json'), packageLocalRefMock); return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledThrice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.thirdCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules and the lock file should have been copied - expect(fsExtraMock.copy).to.have.been.calledTwice, - // Lock file rebase should have been called - expect(packagerMock.rebaseLockfile).to.have.been.calledOnce, - expect(packagerMock.rebaseLockfile).to.have.been.calledWith(sinon.match.any, sinon.match(fakePackageLockJSON)), - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])) - .finally(() => { - process.cwd.restore(); - }); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledThrice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.thirdCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules and the lock file should have been copied + expect(fsExtraMock.copy).to.have.been.calledTwice, + // Lock file rebase should have been called + expect(packagerMock.rebaseLockfile).to.have.been.calledOnce, + expect(packagerMock.rebaseLockfile).to.have.been.calledWith(sinon.match.any, sinon.match(fakePackageLockJSON)), + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])) + .finally(() => { + process.cwd.restore(); + }); }); it('should skip module copy for Google provider', () => { @@ -570,19 +579,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.not.been.called, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.not.have.been.called, - expect(packagerMock.runScripts).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.not.been.called, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.not.have.been.called, + expect(packagerMock.runScripts).to.not.have.been.called, + ])); }); it('should reject if packager install fails', () => { @@ -595,13 +604,13 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.rejectedWith('npm install failed') - .then(() => BbPromise.all([ - // npm ls and npm install should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.not.have.been.called, - expect(packagerMock.runScripts).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + // npm ls and npm install should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.not.have.been.called, + expect(packagerMock.runScripts).to.not.have.been.called, + ])); }); it('should reject if packager returns a critical error', () => { @@ -611,17 +620,17 @@ describe('packExternalModules', () => { packagerMock.getProdDependencies.callsFake(() => BbPromise.reject(new Error('something went wrong'))); module.compileStats = stats; return expect(module.packExternalModules()).to.be.rejectedWith('something went wrong') - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.not.have.been.called, - // The modules should have been copied - expect(fsExtraMock.copy).to.not.have.been.called, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.not.have.been.called, - expect(packagerMock.prune).to.not.have.been.called, - expect(packagerMock.runScripts).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.not.have.been.called, + // The modules should have been copied + expect(fsExtraMock.copy).to.not.have.been.called, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.not.have.been.called, + expect(packagerMock.prune).to.not.have.been.called, + expect(packagerMock.runScripts).to.not.have.been.called, + ])); }); it('should not install modules if no external modules are reported', () => { @@ -630,17 +639,17 @@ describe('packExternalModules', () => { packagerMock.getProdDependencies.returns(BbPromise.resolve()); module.compileStats = noExtStats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.not.have.been.called, - // The modules should have been copied - expect(fsExtraMock.copy).to.not.have.been.called, - // npm install and npm prune should not have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.not.have.been.called, - expect(packagerMock.prune).to.not.have.been.called, - expect(packagerMock.runScripts).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.not.have.been.called, + // The modules should have been copied + expect(fsExtraMock.copy).to.not.have.been.called, + // npm install and npm prune should not have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.not.have.been.called, + expect(packagerMock.prune).to.not.have.been.called, + expect(packagerMock.runScripts).to.not.have.been.called, + ])); }); it('should report ignored packager problems in verbose mode', () => { @@ -658,12 +667,12 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => { - expect(packagerMock.getProdDependencies).to.have.been.calledOnce; - expect(serverless.cli.log).to.have.been.calledWith('=> Problem 1'); - expect(serverless.cli.log).to.have.been.calledWith('=> Problem 2'); - return null; - }); + .then(() => { + expect(packagerMock.getProdDependencies).to.have.been.calledOnce; + expect(serverless.cli.log).to.have.been.calledWith('=> Problem 1'); + expect(serverless.cli.log).to.have.been.calledWith('=> Problem 2'); + return null; + }); }); it('should install external modules when forced', () => { @@ -709,19 +718,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should add forced external modules without version when not in production dependencies', () => { @@ -767,19 +776,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should exclude external modules when forced', () => { @@ -824,19 +833,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should reject if devDependency is required at runtime', () => { @@ -849,14 +858,14 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = statsWithDevDependency; return expect(module.packExternalModules()).to.be.rejectedWith('Serverless-webpack dependency error: eslint.') - .then(() => BbPromise.all([ - expect(module.serverless.cli.log).to.have.been.calledWith(sinon.match(/ERROR: Runtime dependency 'eslint' found in devDependencies/)), - // npm ls and npm install should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.not.have.been.called, - expect(packagerMock.prune).to.not.have.been.called, - expect(packagerMock.runScripts).to.not.have.been.called, - ])); + .then(() => BbPromise.all([ + expect(module.serverless.cli.log).to.have.been.calledWith(sinon.match(/ERROR: Runtime dependency 'eslint' found in devDependencies/)), + // npm ls and npm install should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.not.have.been.called, + expect(packagerMock.prune).to.not.have.been.called, + expect(packagerMock.runScripts).to.not.have.been.called, + ])); }); it('should ignore aws-sdk if set only in devDependencies', () => { @@ -877,14 +886,14 @@ describe('packExternalModules', () => { module.compileStats = statsWithIgnoredDevDependency; mockery.registerMock(path.join(process.cwd(), 'ignoreDevDeps', 'package.json'), packageIgnoredDevDepsMock); return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - expect(module.serverless.cli.log).to.have.been.calledWith(sinon.match(/INFO: Runtime dependency 'aws-sdk' found in devDependencies/)), - // npm ls and npm install should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + expect(module.serverless.cli.log).to.have.been.calledWith(sinon.match(/INFO: Runtime dependency 'aws-sdk' found in devDependencies/)), + // npm ls and npm install should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should succeed if devDependency is required at runtime but forcefully excluded', () => { @@ -904,13 +913,13 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = statsWithDevDependency; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // npm ls and npm install should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // npm ls and npm install should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should read package-lock if found', () => { @@ -951,20 +960,20 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledThrice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify({ info: 'lockfile' }, null, 2)), - expect(writeFileSyncStub.thirdCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules and the lock file should have been copied - expect(fsExtraMock.copy).to.have.been.calledTwice, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledThrice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify({ info: 'lockfile' }, null, 2)), + expect(writeFileSyncStub.thirdCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules and the lock file should have been copied + expect(fsExtraMock.copy).to.have.been.calledTwice, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should continue if package-lock cannot be read', () => { @@ -1004,19 +1013,19 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); it('should skip module copy if demanded by packager', () => { @@ -1055,22 +1064,22 @@ describe('packExternalModules', () => { packagerMock.mustCopyModules = false; module.compileStats = stats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should not have been copied - expect(fsExtraMock.copy).to.not.have.been.called, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])) - .finally(() => { - packagerMock.mustCopyModules = true; - }); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should not have been copied + expect(fsExtraMock.copy).to.not.have.been.called, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])) + .finally(() => { + packagerMock.mustCopyModules = true; + }); }); describe('peer dependencies', () => { @@ -1171,20 +1180,21 @@ describe('packExternalModules', () => { packagerMock.runScripts.returns(BbPromise.resolve()); module.compileStats = peerDepStats; return expect(module.packExternalModules()).to.be.fulfilled - .then(() => BbPromise.all([ - // The module package JSON and the composite one should have been stored - expect(writeFileSyncStub).to.have.been.calledTwice, - expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), - expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), - // The modules should have been copied - expect(fsExtraMock.copy).to.have.been.calledOnce, - // npm ls and npm prune should have been called - expect(packagerMock.getProdDependencies).to.have.been.calledOnce, - expect(packagerMock.install).to.have.been.calledOnce, - expect(packagerMock.prune).to.have.been.calledOnce, - expect(packagerMock.runScripts).to.have.been.calledOnce, - ])); + .then(() => BbPromise.all([ + // The module package JSON and the composite one should have been stored + expect(writeFileSyncStub).to.have.been.calledTwice, + expect(writeFileSyncStub.firstCall.args[1]).to.equal(JSON.stringify(expectedCompositePackageJSON, null, 2)), + expect(writeFileSyncStub.secondCall.args[1]).to.equal(JSON.stringify(expectedPackageJSON, null, 2)), + // The modules should have been copied + expect(fsExtraMock.copy).to.have.been.calledOnce, + // npm ls and npm prune should have been called + expect(packagerMock.getProdDependencies).to.have.been.calledOnce, + expect(packagerMock.install).to.have.been.calledOnce, + expect(packagerMock.prune).to.have.been.calledOnce, + expect(packagerMock.runScripts).to.have.been.calledOnce, + ])); }); }); }); }); +