diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6b397cdd7c..d432fb99ea 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -97,3 +97,34 @@ jobs: - name: "Run tests" run: "npm run test" + + serverless-v3-pre: + runs-on: ${{ matrix.os }} + name: Node.js ${{ matrix.node }} on ${{ matrix.os }} with Serverless v3 pre-release + + strategy: + matrix: + os: + - ubuntu-20.04 + node: + - "14" + + steps: + - name: "Checkout" + uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: "Install Node.js" + uses: actions/setup-node@v2 + with: + node-version: "${{ matrix.node }}" + + - name: "Install Serverless v3 pre-release" + run: npm install serverless@pre-3 + + - name: "Install dependencies" + run: npm ci + + - name: "Run tests" + run: "npm run test" diff --git a/index.js b/index.js index 8cc999ea39..8232a3a144 100644 --- a/index.js +++ b/index.js @@ -22,10 +22,15 @@ class ServerlessWebpack { return lib; } - constructor(serverless, options) { + constructor(serverless, options, v3Utils) { this.serverless = serverless; this.options = options; + if (v3Utils) { + this.log = v3Utils.log; + this.progress = v3Utils.progress; + } + if ( ((_.has(this.serverless, 'service.custom.webpack') && _.isString(this.serverless.service.custom.webpack) && @@ -101,7 +106,8 @@ class ServerlessWebpack { BbPromise.bind(this) .then(() => this.serverless.pluginManager.spawn('webpack:validate')) .then(() => (this.skipCompile ? BbPromise.resolve() : this.serverless.pluginManager.spawn('webpack:compile'))) - .then(() => this.serverless.pluginManager.spawn('webpack:package')), + .then(() => this.serverless.pluginManager.spawn('webpack:package')) + .then(() => this.log && this.progress.get('webpack').remove()), 'after:package:createDeploymentArtifacts': () => BbPromise.bind(this).then(this.cleanup), diff --git a/index.test.js b/index.test.js index b595a2c2a6..5b91e94cc9 100644 --- a/index.test.js +++ b/index.test.js @@ -37,7 +37,7 @@ describe('ServerlessWebpack', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/lib/cleanup.js b/lib/cleanup.js index 4f354530f8..d2d97e564a 100644 --- a/lib/cleanup.js +++ b/lib/cleanup.js @@ -11,16 +11,35 @@ module.exports = { const cli = this.options.verbose ? this.serverless.cli : { log: _.noop }; if (!keepOutputDirectory) { - cli.log(`Remove ${webpackOutputPath}`); + if (this.log) { + this.log.verbose(`Remove ${webpackOutputPath}`); + } else { + cli.log(`Remove ${webpackOutputPath}`); + } if (this.serverless.utils.dirExistsSync(webpackOutputPath)) { // Remove async to speed up process fse .remove(webpackOutputPath) - .then(() => cli.log(`Removing ${webpackOutputPath} done`)) - .catch(error => cli.log(`Error occurred while removing ${webpackOutputPath}: ${error}`)); + .then(() => { + if (this.log) { + this.log.verbose(`Removing ${webpackOutputPath} done`); + } else { + cli.log(`Removing ${webpackOutputPath} done`); + } + return null; + }) + .catch(error => { + if (this.log) { + this.log.error(`Error occurred while removing ${webpackOutputPath}: ${error}`); + } else { + cli.log(`Error occurred while removing ${webpackOutputPath}: ${error}`); + } + }); } } else { - cli.log(`Keeping ${webpackOutputPath}`); + if (!this.log) { + cli.log(`Keeping ${webpackOutputPath}`); + } } return BbPromise.resolve(); diff --git a/lib/compile.js b/lib/compile.js index b06dab7d1f..3693cd6b85 100644 --- a/lib/compile.js +++ b/lib/compile.js @@ -10,9 +10,9 @@ function ensureArray(obj) { return _.isArray(obj) ? obj : [obj]; } -function getStatsLogger(statsConfig, consoleLog) { +function getStatsLogger(statsConfig, consoleLog, { log, ServerlessError }) { return stats => { - logStats(stats, statsConfig, consoleLog); + logStats(stats, statsConfig, consoleLog, { log, ServerlessError }); }; } @@ -79,17 +79,12 @@ function getExternalModules({ compilation }) { return Array.from(externals); } -function webpackCompile(config, logStats, ServerlessError) { +function webpackCompile(config, logStats) { return BbPromise.fromCallback(cb => webpack(config).run(cb)).then(stats => { // ensure stats in any array in the case of concurrent build. stats = stats.stats ? stats.stats : [stats]; - _.forEach(stats, compileStats => { - logStats(compileStats); - if (compileStats.hasErrors()) { - throw _.assign(new ServerlessError('Webpack compilation error, see stats above'), { stats: compileStats }); - } - }); + _.forEach(stats, logStats); return _.map(stats, compileStats => ({ outputPath: compileStats.compilation.compiler.outputPath, @@ -103,15 +98,25 @@ function webpackConcurrentCompile(configs, logStats, concurrency, ServerlessErro return BbPromise.map( configs, config => - webpackCompile(config, logStats, ServerlessError).catch(error => { + webpackCompile(config, logStats).catch(error => { errors.push(error); return error.stats; }), { concurrency } ).then(stats => { if (errors.length) { - if (errors.length === 1) throw errors[0]; - throw new ServerlessError('Webpack compilation errors, see stats above'); + if (!this.log) { + if (errors.length === 1) { + throw errors[0]; + } + throw new ServerlessError('Webpack compilation errors, see stats above'); + } + throw new ServerlessError( + `Webpack compilation failed:\n\n${_.join( + _.map(errors, error => error.message), + '\n\n' + )}` + ); } return _.flatten(stats); }); @@ -119,23 +124,33 @@ function webpackConcurrentCompile(configs, logStats, concurrency, ServerlessErro module.exports = { compile() { - this.serverless.cli.log('Bundling with Webpack...'); + if (this.log) { + this.log.verbose('[Webpack] Building with Webpack'); + this.progress.get('webpack').update('[Webpack] Building with Webpack'); + } else { + this.serverless.cli.log('Bundling with Webpack...'); + } const configs = ensureArray(this.webpackConfig); if (configs[0] === undefined) { return BbPromise.reject('Unable to find Webpack configuration'); } - const logStats = getStatsLogger(configs[0].stats, this.serverless.cli.consoleLog); + const logStats = getStatsLogger(configs[0].stats, this.serverless.cli.consoleLog, { + log: this.log, + ServerlessError: this.serverless.classes.Error + }); if (!this.configuration) { return BbPromise.reject(new this.serverless.classes.Error('Missing plugin configuration')); } const concurrency = this.configuration.concurrency; - return webpackConcurrentCompile(configs, logStats, concurrency, this.serverless.classes.Error).then(stats => { - this.compileStats = { stats }; - return BbPromise.resolve(); - }); + return webpackConcurrentCompile + .call(this, configs, logStats, concurrency, this.serverless.classes.Error) + .then(stats => { + this.compileStats = { stats }; + return BbPromise.resolve(); + }); } }; diff --git a/lib/logStats.js b/lib/logStats.js index 5d28720221..e6245a83cc 100644 --- a/lib/logStats.js +++ b/lib/logStats.js @@ -1,5 +1,6 @@ 'use strict'; +const _ = require('lodash'); const tty = require('tty'); const defaultStatsConfig = { @@ -10,9 +11,53 @@ const defaultStatsConfig = { children: false }; -module.exports = function (stats, statsConfig, consoleLog) { +module.exports = function (stats, statsConfig, consoleLog, { log, ServerlessError }) { const statsOutput = stats.toString(statsConfig || defaultStatsConfig); - if (statsOutput) { - consoleLog(statsOutput); + if (!log) { + if (statsOutput) { + consoleLog(statsOutput); + } + } else { + if (statsConfig) { + if (!statsOutput) { + return; + } + log(); + log(`${_.split(_.trim(statsOutput), '\n').join('\n ')}`); + } else { + const warningsOutput = stats.toString({ + all: false, + errors: false, + errorsCount: false, + warnings: true, + warningsCount: false, + logging: 'warn' + }); + if (warningsOutput) { + log.warning(); + log.warning(`${_.split(_.trim(_.replace(warningsOutput, /WARNING /g, '')), '\n').join('\n ')}`); + } + } + } + if (!stats.hasErrors()) { + return; + } + if (!log) { + throw _.assign(new ServerlessError('Webpack compilation error, see stats above'), { stats }); + } + const errorsOutput = stats.toString({ + all: false, + errors: true, + errorsCount: false, + errorDetails: true, + warnings: false, + warningsCount: false, + logging: 'error' + }); + if (errorsOutput) { + throw _.assign( + new ServerlessError(`${_.split(_.trim(_.replace(errorsOutput, /ERROR /g, '')), '\n').join('\n ')}`), + { stats } + ); } }; diff --git a/lib/packExternalModules.js b/lib/packExternalModules.js index 85477bfbfb..68de1d922a 100644 --- a/lib/packExternalModules.js +++ b/lib/packExternalModules.js @@ -57,7 +57,11 @@ function removeExcludedModules(modules, packageForceExcludes, log) { }); if (log && !_.isEmpty(excludedModules)) { - this.serverless.cli.log(`Excluding external modules: ${_.join(excludedModules, ', ')}`); + if (this.log) { + this.log(`Excluding external modules: ${_.join(excludedModules, ', ')}`); + } else { + this.serverless.cli.log(`Excluding external modules: ${_.join(excludedModules, ', ')}`); + } } } @@ -90,9 +94,13 @@ function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, de if (fse.pathExistsSync(customNodeModulesDir)) { nodeModulesBase = customNodeModulesDir; } else { - this.serverless.cli.log( - `WARNING: ${customNodeModulesDir} dose not exist. Please check nodeModulesRelativeDir setting` - ); + if (this.log) { + this.log.warning(`${customNodeModulesDir} dose not exist. Please check nodeModulesRelativeDir setting`); + } else { + this.serverless.cli.log( + `WARNING: ${customNodeModulesDir} dose not exist. Please check nodeModulesRelativeDir setting` + ); + } } } @@ -102,18 +110,27 @@ function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, de const peerDependencies = require(modulePackagePath).peerDependencies; if (!_.isEmpty(peerDependencies)) { - this.options.verbose && this.serverless.cli.log(`Adding explicit peers for dependency ${module.external}`); + if (this.log) { + this.log.verbose(`Adding explicit peers for dependency ${module.external}`); + } else { + this.options.verbose && this.serverless.cli.log(`Adding explicit peers for dependency ${module.external}`); + } const peerDependenciesMeta = require(modulePackagePath).peerDependenciesMeta; if (!_.isEmpty(peerDependenciesMeta)) { _.forEach(peerDependencies, (value, key) => { if (peerDependenciesMeta[key] && peerDependenciesMeta[key].optional === true) { - this.options.verbose && - this.serverless.cli.log( + if (this.log) { + this.log.verbose( `Skipping peers dependency ${key} for dependency ${module.external} because it's optional` ); - + } else { + this.options.verbose && + this.serverless.cli.log( + `Skipping peers dependency ${key} for dependency ${module.external} because it's optional` + ); + } _.unset(peerDependencies, key); } }); @@ -132,9 +149,15 @@ function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, de } } } catch (e) { - this.serverless.cli.log( - `WARNING: Could not check for peer dependencies of ${module.external}. Set nodeModulesRelativeDir if node_modules is in different directory.` - ); + if (this.log) { + this.log.warning( + `Could not check for peer dependencies of ${module.external}. Set nodeModulesRelativeDir if node_modules is in different directory.` + ); + } else { + this.serverless.cli.log( + `WARNING: Could not check for peer dependencies of ${module.external}. Set nodeModulesRelativeDir if node_modules is in different directory.` + ); + } } } else { if (!packageJson.devDependencies || !packageJson.devDependencies[module.external]) { @@ -146,7 +169,11 @@ function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, de moduleVersion = _.get(dependencyGraph, [ 'dependencies', module.external, 'version' ]); } if (!moduleVersion) { - this.serverless.cli.log(`WARNING: Could not determine version of module ${module.external}`); + if (this.log) { + this.log.warning(`Could not determine version of module ${module.external}`); + } else { + this.serverless.cli.log(`WARNING: Could not determine version of module ${module.external}`); + } } prodModules.push(moduleVersion ? `${module.external}@${moduleVersion}` : module.external); } else if ( @@ -160,16 +187,27 @@ function getProdModules(externalModules, packagePath, nodeModulesRelativeDir, de if (!_.includes(ignoredDevDependencies, module.external)) { // Runtime dependency found in devDependencies but not forcefully excluded - this.serverless.cli.log( - `ERROR: Runtime dependency '${module.external}' found in devDependencies. Move it to dependencies or use forceExclude to explicitly exclude it.` - ); + if (this.log) { + this.log.error( + `Runtime dependency '${module.external}' found in devDependencies. Move it to dependencies or use forceExclude to explicitly exclude it.` + ); + } else { + this.serverless.cli.log( + `ERROR: Runtime dependency '${module.external}' found in devDependencies. Move it to dependencies or use forceExclude to explicitly exclude it.` + ); + } throw new this.serverless.classes.Error(`Serverless-webpack dependency error: ${module.external}.`); } - - this.options.verbose && - this.serverless.cli.log( - `INFO: Runtime dependency '${module.external}' found in devDependencies. It has been excluded automatically.` + if (this.log) { + this.log.verbose( + `Runtime dependency '${module.external}' found in devDependencies. It has been excluded automatically.` ); + } else { + this.options.verbose && + this.serverless.cli.log( + `INFO: Runtime dependency '${module.external}' found in devDependencies. It has been excluded automatically.` + ); + } } } }); @@ -203,6 +241,10 @@ module.exports = { if (!includes) { return BbPromise.resolve(); } + if (this.log) { + this.log.verbose('Packing external modules'); + this.progress.get('webpack').notice('[Webpack] Packing external modules'); + } // Read plugin configuration const packageForceIncludes = _.get(includes, 'forceInclude', []); @@ -226,19 +268,35 @@ module.exports = { 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), ', ')}`); + if (this.log) { + this.log.verbose(`Using package.json sections ${_.join(_.keys(packageSections), ', ')}`); + } else { + 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}`); + if (this.log) { + this.log.verbose(`Fetch dependency graph from ${packageJsonPath}`); + } else { + 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:`); + if (this.log) { + this.log.verbose(`Ignoring ${_.size(problems)} NPM errors:`); + } else { + this.serverless.cli.log(`Ignoring ${_.size(problems)} NPM errors:`); + } _.forEach(problems, problem => { - this.serverless.cli.log(`=> ${problem}`); + if (this.log) { + this.log.verbose(`=> ${problem}`); + } else { + this.serverless.cli.log(`=> ${problem}`); + } }); } @@ -265,7 +323,11 @@ module.exports = { if (_.isEmpty(compositeModules)) { // The compiled code does not reference any external modules at all - this.serverless.cli.log('No external modules needed'); + if (this.log) { + this.log('No external modules needed'); + } else { + this.serverless.cli.log('No external modules needed'); + } return BbPromise.resolve(); } @@ -294,7 +356,11 @@ module.exports = { return BbPromise.fromCallback(cb => fse.pathExists(packageLockPath, cb)) .then(exists => { if (exists) { - this.serverless.cli.log('Package lock found - Using locked versions'); + if (this.log) { + this.log('Package lock found - Using locked versions'); + } else { + this.serverless.cli.log('Package lock found - Using locked versions'); + } try { let packageLockFile = this.serverless.utils.readFileSync(packageLockPath); packageLockFile = packager.rebaseLockfile(relPath, packageLockFile); @@ -308,17 +374,32 @@ module.exports = { ); hasPackageLock = true; } catch (err) { - this.serverless.cli.log(`Warning: Could not read lock file: ${err.message}`); + if (this.log) { + this.log.warning(`Could not read lock file: ${err.message}`); + } else { + 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(', ')); + if (this.log) { + this.log('Packing external modules: ' + compositeModules.join(', ')); + } else { + 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]`)) + .then(() => { + if (this.log) { + this.log.verbose(`Package took [${_.now() - start} ms]`); + } else { + this.options.verbose && this.serverless.cli.log(`Package took [${_.now() - start} ms]`); + } + return null; + }) .return(stats.stats); }) .mapSeries(compileStats => { @@ -386,32 +467,37 @@ module.exports = { ) : BbPromise.resolve() ) - .tap( - () => + .tap(() => { + if (this.log) { + this.log.verbose(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`); + } else { this.options.verbose && - this.serverless.cli.log(`Copy modules: ${modulePath} [${_.now() - startCopy} ms]`) - ) + 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]`) - ); + return packager.prune(modulePath, this.configuration.packagerOptions).tap(() => { + if (this.log) { + this.log.verbose(`Prune: ${modulePath} [${_.now() - startPrune} ms]`); + } else { + 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 packager.runScripts(modulePath, _.keys(packageScripts)).tap(() => { + if (this.log) { + this.log.verbose(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`); + } else { + this.options.verbose && + this.serverless.cli.log(`Run scripts: ${modulePath} [${_.now() - startRunScripts} ms]`); + } + }); }); }) .return(); diff --git a/lib/packageModules.js b/lib/packageModules.js index b7b877546a..b29c0fe812 100644 --- a/lib/packageModules.js +++ b/lib/packageModules.js @@ -12,13 +12,19 @@ const { getAllNodeFunctions } = require('./utils'); function setArtifactPath(funcName, func, artifactPath) { const version = this.serverless.getVersion(); - this.options.verbose && this.serverless.cli.log(`Setting artifact for function '${funcName}' to '${artifactPath}'`); + if (this.log) { + this.log.verbose(`Setting artifact for function '${funcName}' to '${artifactPath}'`); + } else { + this.options.verbose && this.serverless.cli.log(`Setting artifact for function '${funcName}' to '${artifactPath}'`); + } // Serverless changed the artifact path location in version 1.18 if (semver.lt(version, '1.18.0')) { func.artifact = artifactPath; func.package = _.assign({}, func.package, { disable: true }); - this.serverless.cli.log(`${funcName} is packaged by the webpack plugin. Ignore messages from SLS.`); + if (!this.log) { + this.serverless.cli.log(`${funcName} is packaged by the webpack plugin. Ignore messages from SLS.`); + } } else { func.package = { artifact: artifactPath @@ -50,8 +56,12 @@ function zip(directory, name) { const existingFilesLength = files.length; files = _.filter(files, f => f.match(this.configuration.excludeRegex) === null); - if (this.options.verbose) { - this.serverless.cli.log(`Excluded ${existingFilesLength - files.length} file(s) based on excludeRegex`); + if (this.log) { + this.log.verbose(`Excluded ${existingFilesLength - files.length} file(s) based on excludeRegex`); + } else { + if (this.options.verbose) { + this.serverless.cli.log(`Excluded ${existingFilesLength - files.length} file(s) based on excludeRegex`); + } } zipMethod = nodeZip; @@ -79,10 +89,12 @@ function zip(directory, name) { zipMethod(zipArgs) .then(() => { resolve(artifactFilePath); - - this.options.verbose && - this.serverless.cli.log(`Zip method used: ${zipMethod.name === 'nodeZip' ? 'node' : 'native'}`); - + if (this.log) { + this.log.verbose(`Zip method used: ${zipMethod.name === 'nodeZip' ? 'node' : 'native'}`); + } else { + this.options.verbose && + this.serverless.cli.log(`Zip method used: ${zipMethod.name === 'nodeZip' ? 'node' : 'native'}`); + } return null; }) .catch(err => { @@ -126,6 +138,10 @@ module.exports = { if (this.skipCompile) { return BbPromise.resolve(); } + if (this.log) { + this.log.verbose('[Webpack] Packaging modules'); + this.progress.get('webpack').notice('[Webpack] Packaging modules'); + } const stats = this.compileStats; @@ -135,20 +151,28 @@ module.exports = { const modulePath = compileStats.outputPath; const startZip = _.now(); - return zip - .call(this, modulePath, filename) - .tap( - () => - this.options.verbose && + return zip.call(this, modulePath, filename).tap(() => { + if (this.log) { + this.log.verbose( + `Zip ${_.isEmpty(entryFunction) ? 'service' : 'function'}: ${modulePath} [${_.now() - startZip} ms]` + ); + } else { + this.options.verbose && this.serverless.cli.log( `Zip ${_.isEmpty(entryFunction) ? 'service' : 'function'}: ${modulePath} [${_.now() - startZip} ms]` - ) - ); + ); + } + }); }); }, copyExistingArtifacts() { - this.serverless.cli.log('Copying existing artifacts...'); + if (this.log) { + this.log.verbose('[Webpack] Copying existing artifacts'); + this.progress.get('webpack').notice('[Webpack] Copying existing artifacts'); + } else { + this.serverless.cli.log('Copying existing artifacts...'); + } // When invoked as a part of `deploy function`, // only function passed with `-f` flag should be processed. const functionNames = this.options.function ? [this.options.function] : getAllNodeFunctions.call(this); diff --git a/lib/packagers/index.js b/lib/packagers/index.js index 1cf330cf98..4bfc44892d 100644 --- a/lib/packagers/index.js +++ b/lib/packagers/index.js @@ -36,7 +36,11 @@ const registeredPackagers = { module.exports.get = function (packagerId) { if (!_.has(registeredPackagers, packagerId)) { const message = `Could not find packager '${packagerId}'`; - this.serverless.cli.log(`ERROR: ${message}`); + if (this.log) { + this.log.error(message); + } else { + this.serverless.cli.log(`ERROR: ${message}`); + } throw new this.serverless.classes.Error(message); } return registeredPackagers[packagerId]; diff --git a/lib/packagers/index.test.js b/lib/packagers/index.test.js index e3ec7a38ea..5634f423c3 100644 --- a/lib/packagers/index.test.js +++ b/lib/packagers/index.test.js @@ -43,7 +43,7 @@ describe('packagers factory', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/lib/prepareLocalInvoke.test.js b/lib/prepareLocalInvoke.test.js index 180098d9e8..b6d27e3bd3 100644 --- a/lib/prepareLocalInvoke.test.js +++ b/lib/prepareLocalInvoke.test.js @@ -28,7 +28,7 @@ describe('prepareLocalInvoke', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub() }; diff --git a/lib/prepareOfflineInvoke.test.js b/lib/prepareOfflineInvoke.test.js index 4bfcdcf6f2..24abbb736c 100644 --- a/lib/prepareOfflineInvoke.test.js +++ b/lib/prepareOfflineInvoke.test.js @@ -26,7 +26,7 @@ describe('prepareOfflineInvoke', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub() }; diff --git a/lib/run.js b/lib/run.js index fbb8bf46e7..d071ada620 100644 --- a/lib/run.js +++ b/lib/run.js @@ -7,10 +7,21 @@ const webpack = require('webpack'); module.exports = { watch(command) { const functionName = this.options.function; + const watchProgress = this.log && this.progress.get('webpack'); if (functionName) { - this.serverless.cli.log(`Watch function ${functionName}...`); + if (this.log) { + this.log.verbose(`[Webpack] Watch function "${functionName}"`); + watchProgress.notice(`[Webpack] Watch function "${functionName}"`); + } else { + this.serverless.cli.log(`Watch function ${functionName}...`); + } } else { - this.serverless.cli.log('Watch service...'); + if (this.log) { + this.log.verbose('[Webpack] Watch service'); + watchProgress.notice('[Webpack] Watch service'); + } else { + this.serverless.cli.log('Watch service...'); + } } const compiler = webpack(this.webpackConfig); @@ -18,7 +29,11 @@ module.exports = { const usePolling = this.options['webpack-use-polling']; if (usePolling) { watchOptions.poll = _.isInteger(usePolling) ? usePolling : 3000; - this.serverless.cli.log(`Enabled polling (${watchOptions.poll} ms)`); + if (this.log) { + this.log(`Enabled polling (${watchOptions.poll} ms)`); + } else { + this.serverless.cli.log(`Enabled polling (${watchOptions.poll} ms)`); + } } return new BbPromise((resolve, reject) => { @@ -27,6 +42,7 @@ module.exports = { reject(err); return; } + // eslint-disable-next-line promise/catch-or-return, promise/no-promise-in-callback BbPromise.try(() => { if (this.originalServicePath) { @@ -39,13 +55,35 @@ module.exports = { return BbPromise.resolve(); } - this.serverless.cli.log('Sources changed.'); + if (this.log) { + this.log('Sources changed.'); + } else { + this.serverless.cli.log('Sources changed.'); + } if (_.isFunction(command)) { return command(); } - this.options.verbose && this.serverless.cli.log(`Invoke ${command}`); + + if (this.log) { + this.log.verbose(`Invoke ${command}`); + } else { + this.options.verbose && this.serverless.cli.log(`Invoke ${command}`); + } return this.serverless.pluginManager.spawn(command); - }).then(() => this.serverless.cli.log('Waiting for changes ...'), reject); + }).then(() => { + if (this.log) { + if (functionName) { + this.log.verbose(`[Webpack] Watch function "${functionName}"`); + watchProgress.notice(`[Webpack] Watch function "${functionName}"`); + } else { + this.log.verbose('[Webpack] Watch service'); + watchProgress.notice('[Webpack] Watch service'); + } + } else { + this.serverless.cli.log('Waiting for changes ...'); + } + return null; + }, reject); }); }); } diff --git a/lib/validate.js b/lib/validate.js index 780243045b..0383b90b3f 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -71,9 +71,13 @@ module.exports = { ); if (_.size(sortedFiles) > 1) { - this.serverless.cli.log( - `WARNING: More than one matching handlers found for '${fileName}'. Using '${_.first(sortedFiles)}'.` - ); + if (this.log) { + this.log.warning(`More than one matching handlers found for "${fileName}". Using "${_.first(sortedFiles)}"`); + } else { + this.serverless.cli.log( + `WARNING: More than one matching handlers found for '${fileName}'. Using '${_.first(sortedFiles)}'.` + ); + } } return path.extname(_.first(sortedFiles)); }; @@ -83,10 +87,18 @@ module.exports = { const handlerFile = getHandlerFile(handler); if (!handlerFile) { - _.get(this.serverless, 'service.provider.name') !== 'google' && - this.serverless.cli.log( - `\nWARNING: Entry for ${name}@${handler} could not be retrieved.\nPlease check your service config if you want to use lib.entries.` - ); + if (_.get(this.serverless, 'service.provider.name') !== 'google') { + if (this.log) { + this.log.warning(); + this.log.warning( + `Entry for ${name}@${handler} could not be retrieved.\nPlease check your service config if you want to use lib.entries.` + ); + } else { + this.serverless.cli.log( + `\nWARNING: Entry for ${name}@${handler} could not be retrieved.\nPlease check your service config if you want to use lib.entries.` + ); + } + } return {}; } const ext = getEntryExtension(handlerFile); @@ -99,12 +111,20 @@ module.exports = { // Initialize plugin configuration this.configuration = new Configuration(this.serverless.service.custom); - this.options.verbose && - this.serverless.cli.log(`Using configuration:\n${JSON.stringify(this.configuration, null, 2)}`); + if (this.log) { + this.log.verbose(`Using configuration:\n${JSON.stringify(this.configuration, null, 2)}`); + } else { + this.options.verbose && + this.serverless.cli.log(`Using configuration:\n${JSON.stringify(this.configuration, null, 2)}`); + } if (this.configuration.hasLegacyConfig) { - this.serverless.cli.log( - 'Legacy configuration detected. Consider to use "custom.webpack" as object (see README).' - ); + if (this.log) { + this.log.warning('Legacy configuration detected. Consider to use "custom.webpack" as object (see README).'); + } else { + this.serverless.cli.log( + 'Legacy configuration detected. Consider to use "custom.webpack" as object (see README).' + ); + } } this.webpackConfig = this.configuration.config || this.configuration.webpackConfig; @@ -154,7 +174,11 @@ module.exports = { const webpackConfig = require(webpackConfigFilePath); this.webpackConfig = webpackConfig.default || webpackConfig; } catch (err) { - this.serverless.cli.log(`Could not load webpack config '${webpackConfigFilePath}'`); + if (this.log) { + this.log.error(`Could not load webpack config "${webpackConfigFilePath}"`); + } else { + this.serverless.cli.log(`Could not load webpack config '${webpackConfigFilePath}'`); + } return BbPromise.reject(err); } } @@ -197,7 +221,11 @@ module.exports = { // Skip compilation with --no-build or noBuild if (this.skipCompile) { - this.serverless.cli.log('Skipping build and using existing compiled output'); + if (this.log) { + this.log('Skipping build and using existing compiled output'); + } else { + this.serverless.cli.log('Skipping build and using existing compiled output'); + } if (!fse.pathExistsSync(this.webpackConfig.output.path)) { return BbPromise.reject(new this.serverless.classes.Error('No compiled output found')); } @@ -205,18 +233,27 @@ module.exports = { } if (!this.keepOutputDirectory) { - this.options.verbose && this.serverless.cli.log(`Removing ${this.webpackConfig.output.path}`); + if (this.log) { + this.log.verbose(`Removing ${this.webpackConfig.output.path}`); + } else { + this.options.verbose && this.serverless.cli.log(`Removing ${this.webpackConfig.output.path}`); + } fse.removeSync(this.webpackConfig.output.path); } this.webpackOutputPath = this.webpackConfig.output.path; // In case of individual packaging we have to create a separate config for each function if (_.has(this.serverless, 'service.package') && this.serverless.service.package.individually) { - this.options.verbose && - this.serverless.cli.log( + if (this.log) { + this.log.verbose( `Individually packaging with concurrency at ${this.configuration.concurrency} entries a time.` ); - + } else { + this.options.verbose && + this.serverless.cli.log( + `Individually packaging with concurrency at ${this.configuration.concurrency} entries a time.` + ); + } if (this.webpackConfig.entry && !_.isEqual(this.webpackConfig.entry, entries)) { return BbPromise.reject( new this.serverless.classes.Error( diff --git a/lib/wpwatch.js b/lib/wpwatch.js index bb7488272b..7e5604be42 100644 --- a/lib/wpwatch.js +++ b/lib/wpwatch.js @@ -9,17 +9,29 @@ module.exports = { wpwatch() { if (this.options['webpack-no-watch']) { // If we do not watch we will just run an ordinary compile - this.serverless.cli.log('Watch disabled by option.'); + if (!this.log) { + this.serverless.cli.log('Watch disabled by option.'); + } return this.serverless.pluginManager.spawn('webpack:compile'); } - this.serverless.cli.log('Bundling with Webpack...'); + const watchProgress = this.log && this.progress.get('webpack'); + if (this.log) { + this.log.verbose('[Webpack] Building with Webpack'); + watchProgress.update('[Webpack] Building with Webpack'); + } else { + this.serverless.cli.log('Bundling with Webpack...'); + } const watchOptions = {}; const usePolling = this.options['webpack-use-polling']; if (usePolling) { watchOptions.poll = _.isInteger(usePolling) ? usePolling : 3000; - this.serverless.cli.log(`Enabled polling (${watchOptions.poll} ms)`); + if (this.log) { + this.log(`Enabled polling (${watchOptions.poll} ms)`); + } else { + this.serverless.cli.log(`Enabled polling (${watchOptions.poll} ms)`); + } } let currentCompileWatch = null; @@ -75,8 +87,12 @@ module.exports = { throw err; } - process.env.SLS_DEBUG && - this.serverless.cli.log(`Webpack watch invoke: HASH NEW=${stats && stats.hash} CUR=${lastHash}`); + if (this.log) { + this.log.verbose(`Webpack watch invoke: HASH NEW=${stats && stats.hash} CUR=${lastHash}`); + } else { + process.env.SLS_DEBUG && + this.serverless.cli.log(`Webpack watch invoke: HASH NEW=${stats && stats.hash} CUR=${lastHash}`); + } // If the file hash did not change there were no effective code changes detected // (comment changes do not change the compile hash and do not account for a rebuild!) @@ -91,17 +107,31 @@ module.exports = { if (stats) { lastHash = stats.hash; - logStats(stats, consoleStats, this.serverless.cli.consoleLog); + try { + logStats(stats, consoleStats, this.serverless.cli.consoleLog, { + log: this.log, + ServerlessError: this.serverless.classes.Error + }); + } catch (error) { + if (this.log) { + this.log.error(error.message); + } + } } if (firstRun) { firstRun = false; - this.serverless.cli.log('Watching for changes...'); + if (this.log) { + this.log.verbose('[Webpack] Watch service...'); + watchProgress.notice('[Webpack] Watch service...'); + } else { + this.serverless.cli.log('Watching for changes...'); + } callback(); } else if (canEmit && currentCompileWatch === null) { // eslint-disable-next-line promise/no-promise-in-callback currentCompileWatch = BbPromise.resolve(this.serverless.pluginManager.spawn('webpack:compile:watch')).then( - () => this.serverless.cli.log('Watching for changes...') + () => !this.log && this.serverless.cli.log('Watching for changes...') ); } }); diff --git a/tests/cleanup.test.js b/tests/cleanup.test.js index a246988674..5cf4e0e6de 100644 --- a/tests/cleanup.test.js +++ b/tests/cleanup.test.js @@ -45,7 +45,7 @@ describe('cleanup', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), error: sandbox.stub(), diff --git a/tests/compile.test.js b/tests/compile.test.js index 186c56cbe2..9a5a841f0b 100644 --- a/tests/compile.test.js +++ b/tests/compile.test.js @@ -38,7 +38,7 @@ describe('compile', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/tests/packExternalModules.test.js b/tests/packExternalModules.test.js index 57af3c5877..491583e4a7 100644 --- a/tests/packExternalModules.test.js +++ b/tests/packExternalModules.test.js @@ -91,7 +91,7 @@ describe('packExternalModules', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/tests/packageModules.test.js b/tests/packageModules.test.js index c5f10dbac6..464c9dfb8e 100644 --- a/tests/packageModules.test.js +++ b/tests/packageModules.test.js @@ -58,7 +58,7 @@ describe('packageModules', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/tests/prepareStepOfflineInvoke.test.js b/tests/prepareStepOfflineInvoke.test.js index 7bdf87b2e1..4db8aaea80 100644 --- a/tests/prepareStepOfflineInvoke.test.js +++ b/tests/prepareStepOfflineInvoke.test.js @@ -26,7 +26,7 @@ describe('prepareStepOfflineInvoke', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub() }; diff --git a/tests/run.test.js b/tests/run.test.js index 7cc191b1c6..b4f363a584 100644 --- a/tests/run.test.js +++ b/tests/run.test.js @@ -43,7 +43,7 @@ describe('run', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/tests/runPluginSupport.test.js b/tests/runPluginSupport.test.js index a30a2bf1fb..388924f9d2 100644 --- a/tests/runPluginSupport.test.js +++ b/tests/runPluginSupport.test.js @@ -47,7 +47,7 @@ describe('runPluginSupport', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub() diff --git a/tests/validate.test.js b/tests/validate.test.js index 5e7c0e97ae..8be829dd7a 100644 --- a/tests/validate.test.js +++ b/tests/validate.test.js @@ -42,7 +42,7 @@ describe('validate', () => { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub() }; diff --git a/tests/wpwatch.test.js b/tests/wpwatch.test.js index f63a3c990e..d58d7598f8 100644 --- a/tests/wpwatch.test.js +++ b/tests/wpwatch.test.js @@ -45,7 +45,7 @@ describe('wpwatch', function () { }); beforeEach(() => { - serverless = new Serverless(); + serverless = new Serverless({ commands: ['print'], options: {} }); serverless.cli = { log: sandbox.stub(), consoleLog: sandbox.stub()