diff --git a/lib/classes/PluginManager.js b/lib/classes/PluginManager.js index 91b1795468b..b933caeb889 100644 --- a/lib/classes/PluginManager.js +++ b/lib/classes/PluginManager.js @@ -372,24 +372,31 @@ class PluginManager { }; } if (!_.isObject(options)) { - throw new Error( + throw new ServerlessError( `Custom variable resolver {${variablePrefix}: ${JSON.stringify( options - )}} defined by ${pluginName} isn't an object` + )}} defined by ${pluginName} isn't an object`, + 'CUSTOM_LEGACY_VARIABLES_RESOLVER_INVALID_FORMAT' ); } else if (!variablePrefix.match(/[0-9a-zA-Z_-]+/)) { - throw new Error( - `Custom variable resolver prefix ${variablePrefix} defined by ${pluginName} may only contain alphanumeric characters, hyphens or underscores.` + throw new ServerlessError( + `Custom variable resolver prefix ${variablePrefix} defined by ${pluginName} ` + + 'may only contain alphanumeric characters, hyphens or underscores.', + 'CUSTOM_LEGACY_VARIABLES_RESOLVER_INVALID_PREFIX' ); } if (typeof options.resolver !== 'function') { - throw new Error( - `Custom variable resolver for ${variablePrefix} defined by ${pluginName} specifies a resolver that isn't a function: ${options.resolver}` + throw new ServerlessError( + `Custom variable resolver for ${variablePrefix} defined by ${pluginName} ` + + `specifies a resolver that isn't a function: ${options.resolver}`, + 'CUSTOM_LEGACY_VARIABLES_RESOLVER_INVALID_FORMAT' ); } if (options.isDisabledAtPrepopulation && typeof options.serviceName !== 'string') { - throw new Error( - `Custom variable resolver for ${variablePrefix} defined by ${pluginName} specifies isDisabledAtPrepopulation but doesn't provide a string for a name` + throw new ServerlessError( + `Custom variable resolver for ${variablePrefix} defined by ${pluginName} ` + + "specifies isDisabledAtPrepopulation but doesn't provide a string for a name", + 'CUSTOM_LEGACY_VARIABLES_RESOLVER_INVALID_CONFIGURATION' ); } this.serverless.variables.variableResolvers.push({ diff --git a/lib/classes/Service.js b/lib/classes/Service.js index bb7fddfc340..cdf9e294923 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -206,7 +206,10 @@ class Service { if (typeof value === 'object') { return _.merge(memo, value); } - throw new Error(`Non-object value specified in ${key} array: ${value}`); + throw new ServerlessError( + `Non-object value specified in ${key} array: ${value}`, + 'LEGACY_CONFIGURATION_PROPERTY_MERGE_INVALID_INPUT' + ); } return memo; diff --git a/lib/configuration/read.js b/lib/configuration/read.js index 89fe28dc6be..621f7a7c30c 100644 --- a/lib/configuration/read.js +++ b/lib/configuration/read.js @@ -16,27 +16,47 @@ const resolveTsNode = async (serviceDir) => { try { return (await resolveModulePath(__dirname, 'ts-node')).realPath; } catch (slsDepError) { - if (slsDepError.code !== 'MODULE_NOT_FOUND') throw slsDepError; + if (slsDepError.code !== 'MODULE_NOT_FOUND') { + throw new ServerlessError( + `Cannot resolve "ts-node" due to: ${slsDepError.message}`, + 'TS_NODE_RESOLUTION_ERROR' + ); + } // 2. If installed in a service, use it try { return (await resolveModulePath(serviceDir, 'ts-node')).realPath; } catch (serviceDepError) { - if (serviceDepError.code !== 'MODULE_NOT_FOUND') throw serviceDepError; + if (serviceDepError.code !== 'MODULE_NOT_FOUND') { + throw new ServerlessError( + `Cannot resolve "ts-node" due to: ${serviceDepError.message}`, + 'TS_NODE_IN_SERVICE_RESOLUTION_ERROR' + ); + } // 3. If installed globally, use it const { stdoutBuffer } = await (async () => { try { return await spawn('npm', ['root', '-g']); } catch (error) { - if (error.code !== 'ENOENT') throw error; - throw new Error('"ts-node" not found'); + if (error.code !== 'ENOENT') { + throw new ServerlessError( + `Cannot resolve "ts-node" due to unexpected "npm" error: ${error.message}`, + 'TS_NODE_NPM_RESOLUTION_ERROR' + ); + } + throw new ServerlessError('"ts-node" not found', 'TS_NODE_NOT_FOUND'); } })(); try { return require.resolve(`${String(stdoutBuffer).trim()}/ts-node`); } catch (globalDepError) { - if (globalDepError.code !== 'MODULE_NOT_FOUND') throw globalDepError; + if (globalDepError.code !== 'MODULE_NOT_FOUND') { + throw new ServerlessError( + `Cannot resolve "ts-node" due to: ${globalDepError.message}`, + 'TS_NODE_NPM_GLOBAL_RESOLUTION_ERROR' + ); + } throw new ServerlessError('"ts-node" not found', 'TS_NODE_NOT_FOUND'); } } diff --git a/lib/plugins/aws/deployFunction.js b/lib/plugins/aws/deployFunction.js index 45545b9b3a6..fb30c7c7f26 100644 --- a/lib/plugins/aws/deployFunction.js +++ b/lib/plugins/aws/deployFunction.js @@ -116,7 +116,10 @@ class AwsDeployFunction { const roleResource = this.serverless.service.resources.Resources[role]; if (roleResource.Type !== 'AWS::IAM::Role') { - throw new Error('Provided resource is not IAM Role.'); + throw new ServerlessError( + 'Provided resource is not IAM Role', + 'ROLE_REFERENCES_NON_AWS_IAM_ROLE' + ); } const roleProperties = roleResource.Properties; if (!roleProperties.RoleName) { diff --git a/lib/plugins/aws/invokeLocal/index.js b/lib/plugins/aws/invokeLocal/index.js index 0faafe05efc..4d4080c5a58 100644 --- a/lib/plugins/aws/invokeLocal/index.js +++ b/lib/plugins/aws/invokeLocal/index.js @@ -198,7 +198,10 @@ class AwsInvokeLocal { } else if (value.Ref) { configuredEnvVars[name] = await resolveCfRefValue(this.provider, value.Ref); } else { - throw new Error(`Unsupported format: ${inspect(value)}`); + throw new ServerlessError( + `Unsupported environment variable format: ${inspect(value)}`, + 'INVOKE_LOCAL_UNSUPPORTED_ENV_VARIABLE' + ); } } catch (error) { throw new ServerlessError( @@ -282,7 +285,10 @@ class AwsInvokeLocal { try { return await spawnExt('docker', ['version']); } catch { - throw new Error('Please start the Docker daemon to use the invoke local Docker integration.'); + throw new ServerlessError( + 'Please start the Docker daemon to use the invoke local Docker integration.', + 'DOCKER_DAEMON_NOT_FOUND' + ); } } @@ -410,7 +416,10 @@ class AwsInvokeLocal { if (err.stdBuffer) { process.stdout.write(err.stdBuffer); } - throw new Error(`Failed to build docker image (exit code ${err.code}})`); + throw new ServerlessError( + `Failed to build docker image (exit code ${err.code}})`, + 'DOCKER_IMAGE_BUILD_FAILED' + ); } } @@ -518,7 +527,10 @@ class AwsInvokeLocal { if (err.stdBuffer) { process.stdout.write(err.stdBuffer); } - throw new Error(`Failed to run docker for ${runtime} image (exit code ${err.code}})`); + throw new ServerlessError( + `Failed to run docker for ${runtime} image (exit code ${err.code}})`, + 'DOCKER_IMAGE_RUN_FAILED' + ); } } @@ -625,7 +637,10 @@ class AwsInvokeLocal { try { await fse.stat(artifactPath); } catch { - throw new Error(`Artifact ${artifactPath} doesn't exists, please compile it first.`); + throw new ServerlessError( + `Artifact ${artifactPath} doesn't exists, please compile it first.`, + 'JAVA_ARTIFACT_NOT_FOUND' + ); } const timeout = Number(this.options.functionObj.timeout) || @@ -685,7 +700,10 @@ class AwsInvokeLocal { } isRejected = true; reject( - new Error(`Failed to build the Java bridge. exit code=${code} signal=${signal}`) + new ServerlessError( + `Failed to build the Java bridge. exit code=${code} signal=${signal}`, + 'JAVA_BRIDGE_BUILD_FAILED' + ) ); } }); @@ -746,7 +764,10 @@ class AwsInvokeLocal { lambda = handlersContainer[handlerName]; } catch (error) { this.serverless.cli.consoleLog(chalk.red(inspect(error))); - throw new Error(`Exception encountered when loading ${pathToHandler}`); + throw new ServerlessError( + `Exception encountered when loading ${pathToHandler}`, + 'INVOKE_LOCAL_LAMBDA_INITIALIZATION_FAILED' + ); } if (typeof lambda !== 'function') { @@ -785,7 +806,10 @@ class AwsInvokeLocal { body: JSON.parse(result.body), }); } catch (e) { - throw new Error('Content-Type of response is application/json but body is not json'); + throw new ServerlessError( + 'Content-Type of response is application/json but body is not json', + 'INVOKE_LOCAL_RESPONSE_TYPE_MISMATCH' + ); } } } diff --git a/lib/plugins/aws/package/compile/events/cloudFront.js b/lib/plugins/aws/package/compile/events/cloudFront.js index c1bf444773c..4438917aad6 100644 --- a/lib/plugins/aws/package/compile/events/cloudFront.js +++ b/lib/plugins/aws/package/compile/events/cloudFront.js @@ -262,13 +262,15 @@ class AwsCompileCloudFrontEvents { const { eventType = 'default' } = cloudFront; const { maxMemorySize, maxTimeout } = this.lambdaEdgeLimits[eventType]; if (functionObj.memorySize && functionObj.memorySize > maxMemorySize) { - throw new Error( - `"${functionName}" memorySize is greater than ${maxMemorySize} which is not supported by Lambda@Edge functions of type "${eventType}"` + throw new ServerlessError( + `"${functionName}" memorySize is greater than ${maxMemorySize} which is not supported by Lambda@Edge functions of type "${eventType}"`, + 'LAMBDA_EDGE_UNSUPPORTED_MEMORY_SIZE' ); } if (functionObj.timeout && functionObj.timeout > maxTimeout) { - throw new Error( - `"${functionName}" timeout is greater than ${maxTimeout} which is not supported by Lambda@Edge functions of type "${eventType}"` + throw new ServerlessError( + `"${functionName}" timeout is greater than ${maxTimeout} which is not supported by Lambda@Edge functions of type "${eventType}"`, + 'LAMBDA_EDGE_UNSUPPORTED_TIMEOUT_VALUE' ); } }); diff --git a/lib/plugins/aws/package/lib/mergeCustomProviderResources.js b/lib/plugins/aws/package/lib/mergeCustomProviderResources.js index 4ac48103148..85a99005a35 100644 --- a/lib/plugins/aws/package/lib/mergeCustomProviderResources.js +++ b/lib/plugins/aws/package/lib/mergeCustomProviderResources.js @@ -1,6 +1,7 @@ 'use strict'; const _ = require('lodash'); +const ServerlessError = require('../../../../serverless-error'); module.exports = { mergeCustomProviderResources() { @@ -71,11 +72,12 @@ module.exports = { // default includes any future attributes we don't know about yet. default: - throw new Error( + throw new ServerlessError( `${resourceName}: Sorry, extending the ${extensionAttributeName} resource ` + 'attribute at this point is not supported. Feel free to propose support ' + 'for it in the Framework issue tracker: ' + - 'https://github.com/serverless/serverless/issues' + 'https://github.com/serverless/serverless/issues', + 'RESOURCE_EXTENSION_UNSUPPORTED_ATTRIBUTE' ); } } diff --git a/lib/plugins/aws/provider.js b/lib/plugins/aws/provider.js index ef3e203fd70..b09526a5583 100644 --- a/lib/plugins/aws/provider.js +++ b/lib/plugins/aws/provider.js @@ -1588,7 +1588,10 @@ class AwsProvider { if (!alias) return arnGetter; return { 'Fn::Join': [':', [arnGetter, alias.name]] }; } - throw new Error(`Unrecognized function address ${functionAddress}`); + throw new ServerlessError( + `Unrecognized function address ${functionAddress}`, + 'UNRECOGNIZED_FUNCTION_ADDRESS' + ); } resolveLayerArtifactName(layerName) { diff --git a/lib/plugins/aws/rollbackFunction.js b/lib/plugins/aws/rollbackFunction.js index de288d8b893..cb22afe44d3 100644 --- a/lib/plugins/aws/rollbackFunction.js +++ b/lib/plugins/aws/rollbackFunction.js @@ -1,6 +1,7 @@ 'use strict'; const BbPromise = require('bluebird'); +const ServerlessError = require('../../serverless-error'); const validate = require('./lib/validate'); const fetch = require('node-fetch'); @@ -49,9 +50,12 @@ class AwsRollbackFunction { ` and version "${funcVersion}" is available for this function.`, ' Please check the docs for more info.', ].join(''); - throw new Error(errorMessage); + throw new ServerlessError(errorMessage, 'AWS_FUNCTION_NOT_FOUND'); } - throw new Error(error.message); + throw new ServerlessError( + `Cannot resolve function "${funcName}": ${error.message}`, + 'AWS_FUNCTION_NOT_ACCESIBLE' + ); }); } diff --git a/lib/plugins/aws/utils/credentials.js b/lib/plugins/aws/utils/credentials.js index e603e009c87..24e008e3a9a 100644 --- a/lib/plugins/aws/utils/credentials.js +++ b/lib/plugins/aws/utils/credentials.js @@ -4,6 +4,7 @@ const { join } = require('path'); const { constants, readFile, writeFile, mkdir } = require('fs'); const os = require('os'); const BbPromise = require('bluebird'); +const ServerlessError = require('../../../serverless-error'); const homedir = os.homedir(); const awsConfigDirPath = join(homedir, '.aws'); @@ -101,7 +102,12 @@ module.exports = { saveFileProfiles(profiles) { return new BbPromise((resolve) => { - if (!credentialsFilePath) throw new Error('Could not resolve path to user credentials file'); + if (!credentialsFilePath) { + throw new ServerlessError( + 'Could not resolve path to user credentials file', + 'UNKNOWN_AWS_CREDENTIALS_PATH' + ); + } resolve( writeCredentialsContent( `${Array.from(profiles) diff --git a/lib/plugins/package/package.js b/lib/plugins/package/package.js index 18d525478f8..c0bf6e46885 100644 --- a/lib/plugins/package/package.js +++ b/lib/plugins/package/package.js @@ -2,6 +2,7 @@ const BbPromise = require('bluebird'); const path = require('path'); +const ServerlessError = require('../../serverless-error'); const cliCommandsSchema = require('../../cli/commands-schema'); const zipService = require('./lib/zipService'); const packageService = require('./lib/packageService'); @@ -57,7 +58,9 @@ class Package { this.serverless.cli.log(`Packaging function: ${this.options.function}...`); return BbPromise.resolve(this.packageFunction(this.options.function)); } - return BbPromise.reject(new Error('Function name must be set')); + return BbPromise.reject( + new ServerlessError('Function name must be set', 'PACKAGE_MISSING_FUNCTION_OPTION') + ); }, }; } diff --git a/lib/plugins/plugin/install.js b/lib/plugins/plugin/install.js index cdabafd8a50..d9826cefd1c 100644 --- a/lib/plugins/plugin/install.js +++ b/lib/plugins/plugin/install.js @@ -5,6 +5,7 @@ const childProcess = BbPromise.promisifyAll(require('child_process')); const fse = require('fs-extra'); const path = require('path'); const _ = require('lodash'); +const ServerlessError = require('../../serverless-error'); const cliCommandsSchema = require('../../cli/commands-schema'); const yamlAstParser = require('../../utils/yamlAstParser'); const fileExists = require('../../utils/fs/fileExists'); @@ -116,7 +117,10 @@ class PluginInstall { : newServerlessFileObj.plugins.modules; if (plugins == null) { - throw new Error('plugins modules property must be present'); + throw new ServerlessError( + 'plugins modules property must be present', + 'PLUGINS_MODULES_MISSING' + ); } plugins.push(this.options.pluginName);