chore: WIP

This commit is contained in:
Austen Collins 2024-04-17 06:38:58 -07:00
parent 3a6dc67a80
commit 2ed4ff6fd5
21 changed files with 129 additions and 305 deletions

View File

@ -183,7 +183,7 @@ async function awsRequest(service, method, ...args) {
awsLog.debug(`request result: #${requestId} ${service.name}.${method}`, result);
return result;
} catch (err) {
awsLog.debug(`request error: #${requestId} - ${service.name}.${method}`, error);
awsLog.debug(`request error: #${requestId} - ${service.name}.${method}`, err);
let message = err.message != null ? err.message : String(err.code);
if (message.startsWith('Missing credentials in config')) {
// Credentials error

View File

@ -1,13 +1,15 @@
import path from 'path';
import fs from 'fs/promises';
import _ from 'lodash';
import utils from '@serverlessinc/sf-core/src/utils.js';
import ServerlessError from '../serverless-error.js';
import renderCommandHelp from '../cli/render-help/command.js';
import tokenizeException from '../utils/tokenize-exception.js';
import { fileURLToPath, pathToFileURL } from 'url';
import getRequire from '../utils/get-require.js';
import importModule from '../utils/require-with-import-fallback.js';
const { log, getPluginWriters } = utils;
const { log, getPluginWriters, readFile } = utils;
let hooksIdCounter = 0;
let nestTracker = 0;
@ -139,43 +141,66 @@ class PluginManager {
return this.asyncPluginInit();
}
async requireServicePlugin(serviceDir, pluginPath, legacyLocalPluginsPath) {
if (legacyLocalPluginsPath && !pluginPath.startsWith('./')) {
// TODO (BREAKING): Consider removing support for localPluginsPath with next major
const absolutePluginPath = path.resolve(legacyLocalPluginsPath, pluginPath);
const absolutePluginPathURL = pathToFileURL(absolutePluginPath).href;
/**
* Return a promise to require a Service plugin, whether it's local in a relative path,
* local in the service directory, or a node_module
* @param {*} serviceDir
* @param {*} pluginListedName
* @param {*} legacyLocalPluginsPath
* @returns
*/
async requireServicePlugin(serviceDir, pluginListedName, legacyLocalPluginsPath) {
const isLocatedAtLegacyPluginsPath = await (async () => {
try {
await import.meta.resolve(absolutePluginPathURL);
return true;
} catch {
return false;
}
})();
if (isLocatedAtLegacyPluginsPath) return import(absolutePluginPathURL);
}
const serviceDirRequire = (dir) => (module) => import.meta.resolve(pathToFileURL(path.resolve(dir, module)).href);
const localPluginPath = await (async () => {
/**
* Check the Service directory for the plugin.
* This will check in the node_modules of the service directory
* and in the service directory itself.
*/
const serviceDirRequire = async (dir, module) => {
// Attempt to construct the full path for local modules or node_modules
const fullPath = path.resolve(dir, module);
try {
return await serviceDirRequire(serviceDir)(pluginPath);
} catch {
return null;
// Convert the path to a URL and dynamically import the module
const moduleUrl = pathToFileURL(fullPath).href;
await import(moduleUrl);
return moduleUrl; // Return URL if module can be imported successfully
} catch (error) {
// If direct path import fails, try resolving under node_modules
const packageJsonPath = path.resolve(dir, 'node_modules', module, 'package.json');
// IMport JSON in ESM
const packageJson = await readFile(packageJsonPath);
if (!packageJson) {
throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' });
}
let { main } = JSON.parse(packageJson);
if (!main) { main = 'plugin.js'; }
const pluginPath = path.resolve(dir, 'node_modules', module, main);
const pluginUrl = pathToFileURL(pluginPath).href;
try {
await import(pluginUrl);
return pluginUrl; // Return URL if module can be imported successfully
} catch (error) {
throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' });
}
}
})();
};
const localPluginPath = await serviceDirRequire(serviceDir, pluginListedName);
if (localPluginPath) {
if (pluginPath.startsWith('./')) {
this.localPluginsPaths.push({ resolvedPath: fileURLToPath(localPluginPath), inputPath: pluginPath });
if (pluginListedName.startsWith('./')) {
this.localPluginsPaths.push({ resolvedPath: fileURLToPath(localPluginPath), inputPath: pluginListedName });
}
return import(localPluginPath);
}
// Search in "node_modules" in which framework is placed
/**
* Search in the Framework's node_modules
*/
const externalPluginPath = await (async () => {
try {
return await import.meta.resolve(pluginPath);
} catch {
return await import(pluginListedName);
} catch (error) {
throw Object.assign(new Error('Plugin not found'), { code: 'PLUGIN_NOT_FOUND' });
}
})();
@ -193,6 +218,7 @@ class PluginManager {
let Plugin;
try {
Plugin = await this.requireServicePlugin(serviceDir, name, pluginsObject.localPath);
Plugin = Plugin.default || Plugin;
} catch (error) {
if (error.code !== 'PLUGIN_NOT_FOUND') throw error;
@ -561,8 +587,7 @@ class PluginManager {
log
.get('sls:lifecycle:command:invoke')
.debug(
`Invoke ${commandsArray.join(':')}${
!hooksLength ? ' (noop due to no registered hooks)' : ''
`Invoke ${commandsArray.join(':')}${!hooksLength ? ' (noop due to no registered hooks)' : ''
}`
);
@ -628,8 +653,7 @@ class PluginManager {
} catch (errorHookException) {
const errorHookExceptionMeta = tokenizeException(errorHookException);
log.warning(
`The "error" hook crashed with:\n${
errorHookExceptionMeta.stack || errorHookExceptionMeta.message
`The "error" hook crashed with:\n${errorHookExceptionMeta.stack || errorHookExceptionMeta.message
}`
);
} finally {

View File

@ -14,53 +14,6 @@ commands.commonOptions = globalOptions;
// Export the commands Map and any other properties collectively
export default commands;
commands.set('config credentials', {
usage: 'Configures a new provider profile for the Serverless Framework',
hasAwsExtension: true,
options: {
provider: {
type: 'string',
usage: 'Name of the provider. Supported providers: aws',
required: true,
shortcut: 'p',
},
key: {
type: 'string',
usage: 'Access key for the provider',
shortcut: 'k',
required: true,
},
secret: {
type: 'string',
usage: 'Secret key for the provider',
shortcut: 's',
required: true,
},
profile: {
type: 'string',
usage: 'Name of the profile you wish to create. Defaults to "default"',
shortcut: 'n',
},
overwrite: {
usage: 'Overwrite the existing profile configuration in the credentials file',
shortcut: 'o',
type: 'boolean',
},
},
lifecycleEvents: ['config'],
});
commands.set('config', {
usage: 'Configure Serverless',
options: {
autoupdate: {
usage: 'Turn on auto update mechanism (turn off via "--no-autoupdate")',
type: 'boolean',
},
},
lifecycleEvents: ['config'],
});
commands.set('plugin install', {
usage: 'Install and add a plugin to your service',
options: {

View File

@ -1,76 +0,0 @@
import os from 'os';
import credentials from './utils/credentials.js';
import ServerlessError from '../../serverless-error.js';
import cliCommandsSchema from '../../cli/commands-schema.js';
import utils from '@serverlessinc/sf-core/src/utils.js';
const { log } = utils;
class AwsConfigCredentials {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
// Note: we're not setting the provider here as this plugin should also be
// run when the CWD is not an AWS service
// this will be merged with the core config commands
this.commands = {
config: {
commands: {
credentials: {
...cliCommandsSchema.get('config credentials'),
},
},
},
};
if (!os.homedir()) {
throw new ServerlessError(
"Can't find home directory on your local file system.",
'MISSING_HOME_DIRECTORY'
);
}
this.hooks = {
'config:credentials:config': async () => this.configureCredentials(),
};
}
async configureCredentials() {
// sanitize
this.options.provider = this.options.provider.toLowerCase();
this.options.profile = this.options.profile ? this.options.profile : 'default';
// resolve if provider option is not 'aws'
if (this.options.provider !== 'aws') return null;
// validate
if (!this.options.key || !this.options.secret) {
throw new ServerlessError(
'Please include --key and --secret options for AWS.',
'MISSING_KEY_AND_SECRET_CLI_OPTIONS'
);
}
const profiles = await credentials.resolveFileProfiles();
if (profiles.has(this.options.profile)) {
// Only update the profile if the overwrite flag was set
if (!this.options.overwrite) {
throw new ServerlessError(
`Profile "${this.options.profile}" is already configured in ~/.aws/credentials. Use the overwrite flag ("-o" or "--overwrite") to force the update.`,
'CREDENTIALS_PROFILE_ALREADY_CONFIGURED'
);
}
}
profiles.set(this.options.profile, {
accessKeyId: this.options.key,
secretAccessKey: this.options.secret,
});
const result = await credentials.saveFileProfiles(profiles);
log.notice.success(`Profile "${this.options.profile}" has been configured`);
return result;
}
}
export default AwsConfigCredentials;

View File

@ -28,24 +28,26 @@ class AwsDeployFunction {
'initialize': () => {
const commandName = this.serverless.processedInput.commands.join(' ');
if (commandName !== 'deploy function') return;
this.logger.notice();
this.logger.notice(
`Deploying function ${this.options.function} to stage ${this.serverless
`Deploying function "${this.options.function}" to stage "${this.serverless
.getProvider('aws')
.getStage()} ${this.loggerStyle.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
.getStage()}" ${this.loggerStyle.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
);
this.logger.notice();
},
'before:deploy:function:initialize': () =>
this.progress.notice('Validating', { isMainEvent: true }),
'deploy:function:initialize': async () => {
this.logger.debug('validating...');
await this.validate();
this.logger.debug('checking if function exists...')
await this.checkIfFunctionExists();
this.logger.debug('checking if function changed...')
this.checkIfFunctionChangesBetweenImageAndHandler();
},
'before:deploy:function:packageFunction': () =>
this.progress.notice('Retrieving function info', { isMainEvent: true }),
this.progress.notice('Retrieving function info...', { isMainEvent: true }),
'deploy:function:packageFunction': async () =>
this.serverless.pluginManager.spawn('package:function'),
@ -53,6 +55,7 @@ class AwsDeployFunction {
this.progress.notice('Packaging', { isMainEvent: true }),
'deploy:function:deploy': async () => {
if (!this.options['update-config']) {
this.logger.debug('deploying function code...')
await this.deployFunction();
}
await this.updateFunctionConfiguration();
@ -65,6 +68,8 @@ class AwsDeployFunction {
}
async checkIfFunctionExists() {
this.progress.notice('Checking for changes...');
// check if the function exists in the service
this.options.functionObj = this.serverless.service.getFunction(this.options.function);
@ -178,6 +183,8 @@ class AwsDeployFunction {
}
async callUpdateFunctionConfiguration(params) {
this.logger.debug('deploying function configuration changes...')
const startTime = Date.now();
const callWithRetry = async () => {
@ -440,13 +447,13 @@ class AwsDeployFunction {
}
if (!Object.keys(_.omit(params, 'FunctionName')).length) {
if (this.options['update-config']) this.logger.notice();
this.logger.notice();
const noticeMessage = [
'Function configuration did not change, and the update was skipped.',
'Function configuration did not change, so the update was skipped.',
' If you made changes to the service configuration and expected them to be deployed,',
' it most likely means that they can only be applied with a full service deployment.',
' this most likely means that they can only be applied with a full service deployment: "serverless deploy".',
].join('');
this.logger.notice.skip(
this.logger.aside(
`${noticeMessage} ${this.loggerStyle.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000
@ -461,7 +468,7 @@ class AwsDeployFunction {
await this.callUpdateFunctionConfiguration(params);
this.shouldEnsureFunctionState = true;
if (this.options['update-config']) this.logger.notice();
this.logger.notice.success(
this.logger.success(
`Function configuration updated ${this.loggerStyle.aside(
`(${Math.floor((Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000)}s)`
)}\n`
@ -482,7 +489,7 @@ class AwsDeployFunction {
this.serverless.service.provider.remoteFunctionData.Configuration.CodeSha256;
if (remoteImageSha === functionImageSha && !this.options.force) {
this.logger.notice();
this.logger.notice.skip(
this.logger.notice(
`Image did not change. Function deployment skipped. ${this.loggerStyle.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000
@ -509,7 +516,7 @@ class AwsDeployFunction {
if (remoteHash === localHash && !this.options.force) {
this.logger.notice();
this.logger.notice.skip(
this.logger.aside(
`Code did not change. Function deployment skipped. ${this.loggerStyle.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000
@ -522,16 +529,15 @@ class AwsDeployFunction {
params.ZipFile = data;
const stats = fs.statSync(artifactFilePath);
this.progress.notice(`Uploading ${this.loggerStyle.aside(`(${filesize(stats.size)})`)}`, {
this.progress.notice(`Uploading (${filesize(stats.size)})...`, {
isMainEvent: true,
});
}
this.progress.notice('Deploying', { isMainEvent: true });
await this.provider.request('Lambda', 'updateFunctionCode', params);
this.shouldEnsureFunctionState = true;
this.logger.notice();
this.logger.notice.success(
this.logger.success(
`Function code deployed ${this.loggerStyle.aside(
`(${Math.floor((Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000)}s)`
)}`

View File

@ -52,7 +52,7 @@ class AwsDeployList {
if (deployments.length === 0) {
log.notice();
log.notice.skip(
log.aside(
"No deployments found, if that's unexpected ensure that stage and region are correct"
);
return;

View File

@ -23,8 +23,8 @@ import path from 'path';
import utils from '@serverlessinc/sf-core/src/utils.js';
import memoize from 'memoizee';
const { log, style, progress, writeText } = utils;
const mainProgress = progress.get('main');
const { log, style, progress } = utils;
const mainProgress = progress.get('sls:plugin:awsdeploy');
class AwsDeploy {
constructor(serverless, options) {
@ -94,17 +94,15 @@ class AwsDeploy {
this.provider.cachedCredentials,
'dashboardProviderAlias'
);
log.notice();
log.notice(
`Deploying ${this.serverless.service.service} to stage ${this.serverless
`Deploying "${this.serverless.service.service}" to stage "${this.serverless
.getProvider('aws')
.getStage()} ${style.aside(
.getStage()}" ${style.aside(
`(${this.serverless.getProvider('aws').getRegion()}${
dashboardProviderName ? `, "${dashboardProviderName}" provider` : ''
})`
)}`
);
log.info(); // Ensure gap between verbose logging
// This is used to ensure that for `deploy` command, the `accountId` will be resolved and available
// for `generatePayload` telemetry logic
@ -134,7 +132,7 @@ class AwsDeploy {
// Deploy deploy inner lifecycle
'before:aws:deploy:deploy:createStack': () =>
mainProgress.notice('Retrieving CloudFormation stack', { isMainEvent: true }),
mainProgress.notice('Retrieving CloudFormation stack'),
'aws:deploy:deploy:createStack': async () => this.createStack(),
'aws:deploy:deploy:checkForChanges': async () => {
@ -153,7 +151,7 @@ class AwsDeploy {
'before:aws:deploy:deploy:uploadArtifacts': () => {
if (this.serverless.service.provider.shouldNotDeploy) return;
mainProgress.notice('Uploading', { isMainEvent: true });
mainProgress.notice('Uploading');
},
'aws:deploy:deploy:uploadArtifacts': async () => {
if (this.serverless.service.provider.shouldNotDeploy) return;
@ -202,8 +200,7 @@ class AwsDeploy {
'finalize': async () => {
if (this.serverless.processedInput.commands.join(' ') !== 'deploy') return;
if (this.serverless.service.provider.shouldNotDeploy) {
log.notice();
log.notice.skip(
log.aside(
`No changes to deploy. Deployment skipped. ${style.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000
@ -214,8 +211,7 @@ class AwsDeploy {
}
if (this.serverless.service.provider.deploymentWithEmptyChangeSet) {
log.notice();
log.notice.skip(
log.aside(
`Change set did not include any changes to be deployed. ${style.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000
@ -225,8 +221,7 @@ class AwsDeploy {
return;
}
log.notice();
log.notice.success(
log.success(
`Service deployed to stack ${this.serverless
.getProvider('aws')
.naming.getStackName()} ${style.aside(
@ -235,16 +230,20 @@ class AwsDeploy {
)}s)`
)}`
);
writeText();
mainProgress.remove();
writeServiceOutputs(this.serverless.serviceOutputs);
writeServiceOutputs(this.serverless.servicePluginOutputs);
if (this.options['enforce-hash-update']) {
log.notice();
log.blankLine();
log.notice(
'Your service has been deployed with new hashing algorithm. Please remove "provider.lambdaHashingVersion" from your service configuration and re-deploy without "--enforce-hash-update" flag to restore function descriptions.'
);
}
log.blankLine();
},
};
}

View File

@ -9,7 +9,7 @@ const inactiveStateNames = new Set(['REVIEW_IN_PROGRESS']);
export default {
async create() {
// Note: using three dots instead of ellipsis to support non uni-code consoles.
progress.get('main').notice('Creating CloudFormation stack', { isMainEvent: true });
progress.get('sls:plugin:awsdeploy').notice('Creating CloudFormation stack', { isMainEvent: true });
const stackName = this.provider.naming.getStackName();
let monitorCfData;

View File

@ -3,7 +3,7 @@ import utils from '@serverlessinc/sf-core/src/utils.js';
import jsyaml from 'js-yaml';
const { log, progress } = utils;
const mainProgress = progress.get('main');
const mainProgress = progress.get('sls:plugin:awsdeploy');
export default {
async ensureValidBucketExists() {

View File

@ -71,6 +71,7 @@ class AwsInfo {
if (this.serverless.processedInput.commands.join(' ') !== 'info') return;
writeServiceOutputs(this.serverless.serviceOutputs);
writeServiceOutputs(this.serverless.servicePluginOutputs);
this.logger.blankLine();
},
};
}

View File

@ -835,7 +835,7 @@ class AwsInvokeLocal {
if (error.code === 'ERR_REQUIRE_ESM') {
// Already in ESM format, use the utility if necessary or retry with a different method
return await importEsm(`${modulePath}.js`);
} else if (error.code === 'MODULE_NOT_FOUND') {
} else if (error.code === 'MODULE_NOT_FOUND' || error.code === 'ERR_MODULE_NOT_FOUND') {
// Attempt to import with `.js` extension
const pathToHandler = `${modulePath}.js`;
try {

View File

@ -260,11 +260,14 @@ class AwsCompileApigEvents {
// TODO should be removed once AWS fixes the CloudFormation problems using a separate Stage
'after:deploy:deploy': async () => {
const { getServiceState } = await import('../../../../lib/get-service-state.js');
const { updateStage } = await import('./lib/hack/update-stage.js');
// Dynamically import the module
const module = await import('../../../../lib/get-service-state.js');
// Access the default export and then the getServiceState method
const getServiceState = module.default.getServiceState.bind(this);
const state = getServiceState.call(this);
this.state = state;
const moduleTwo = await import('./lib/hack/update-stage.js');
const updateStage = moduleTwo.default.updateStage.bind(this);
return updateStage.call(this);
},

View File

@ -70,11 +70,11 @@ class AwsPackage {
if (this.serverless.processedInput.commands.join(' ') === 'package') {
log.notice();
log.notice(
`Packaging ${this.serverless.service.service} for stage ${this.serverless
`Packaging "${this.serverless.service.service}" for stage "${this.serverless
.getProvider('aws')
.getStage()} ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
.getStage()}" ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
);
log.info(); // Ensure gap between verbose logging
log.notice();
}
},
'before:package:cleanup': () => mainProgress.notice('Packaging', { isMainEvent: true }),
@ -122,8 +122,7 @@ class AwsPackage {
'finalize': () => {
if (this.serverless.processedInput.commands.join(' ') === 'package') {
log.notice();
log.notice.success(
log.success(
`Service packaged ${style.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000

View File

@ -31,11 +31,11 @@ class AwsRemove {
'initialize': async () => {
if (this.serverless.processedInput.commands.join(' ') === 'remove') {
log.notice(
`Removing ${this.serverless.service.service} from stage ${this.serverless
`Removing "${this.serverless.service.service}" from stage "${this.serverless
.getProvider('aws')
.getStage()} ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
.getStage()}" ${style.aside(`(${this.serverless.getProvider('aws').getRegion()})`)}`
);
log.info(); // Ensure gap between verbose logging
log.notice(); // Ensure gap between verbose logging
// This is used to ensure that for `remove` command, the `accountId` will be resolved and available
// for `generatePayload` telemetry logic
@ -47,6 +47,7 @@ class AwsRemove {
);
}
},
'remove:remove': async () => {
const doesEcrRepositoryExistPromise = this.checkIfEcrRepositoryExists();
await this.validate();
@ -60,10 +61,10 @@ class AwsRemove {
await this.removeEcrRepository();
}
},
'finalize': async () => {
if (this.serverless.processedInput.commands.join(' ') !== 'remove') return;
log.notice();
log.notice.success(
log.success(
`Service ${this.serverless.service.service} has been successfully removed ${style.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000

View File

@ -72,7 +72,7 @@ class AwsRollback {
)}`
);
} else {
log.notice.skip(
log.aside(
`No updates to be performed. Rollback skipped. ${style.aside(
`(${Math.floor(
(Date.now() - this.serverless.pluginManager.commandRunStartTime) / 1000

View File

@ -1,42 +0,0 @@
import ServerlessError from '../serverless-error.js';
import cliCommandsSchema from '../cli/commands-schema.js';
import utils from '@serverlessinc/sf-core/src/utils.js';
const { log } = utils;
// class wide constants
const validProviders = new Set(['aws']);
const humanReadableProvidersList = `"${Array.from(validProviders)}"`;
class Config {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
config: {
...cliCommandsSchema.get('config'),
commands: {
credentials: {
// Command defined in AWS context
validProviders,
},
},
},
};
this.hooks = {
'config:config': async () => this.updateConfig(),
'before:config:credentials:config': () => this.validate(),
};
}
// Deprecated in V4
validate() {}
// Deprecated in V4
async updateConfig() {}
}
export default Config;

View File

@ -1,7 +1,5 @@
async function loadModules() {
return Promise.all([
import('./config.js'),
import('./install.js'),
import('./package/package.js'),
import('./deploy.js'),
import('./invoke.js'),
@ -15,7 +13,6 @@ async function loadModules() {
import('./plugin/plugin.js'),
import('./plugin/list.js'),
import('./plugin/search.js'),
import('./aws/config-credentials.js'),
import('./aws/provider.js'),
import('./aws/common/index.js'),
import('./aws/package/index.js'),

View File

@ -1,44 +0,0 @@
import cliCommandsSchema from '../cli/commands-schema.js';
import download from '../utils/download-template-from-repo.js';
import utils from '@serverlessinc/sf-core/src/utils.js';
const { log, progress, style } = utils;
class Install {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
install: {
...cliCommandsSchema.get('install'),
},
};
this.hooks = {
'install:install': async () => this.install(),
};
}
async install() {
const commandRunStartTime = Date.now();
progress.get('main').notice(`Downloading service from provided url: ${this.options.url}`);
const serviceName = await download.downloadTemplateFromRepo(
this.options.url,
this.options.name
);
const message = [
`Successfully installed "${serviceName}" `,
`${
this.options.name && this.options.name !== serviceName ? `as "${this.options.name}"` : ''
}`,
].join('');
log.notice();
log.notice.success(
`${message} ${style.aside(`(${Math.floor((Date.now() - commandRunStartTime) / 1000)}s)`)}`
);
}
}
export default Install;

View File

@ -3,7 +3,8 @@ import archiver from 'archiver';
import os from 'os';
import path from 'path';
import crypto from 'crypto';
import fs from 'fs/promises';
import fs from 'fs';
import fsp from 'fs/promises';
import { promisify } from 'util';
import * as child from 'child_process';
import globby from 'globby';
@ -11,6 +12,10 @@ import _ from 'lodash';
import ServerlessError from '../../../serverless-error.js';
import utils from '@serverlessinc/sf-core/src/utils.js';
const {
createWriteStream,
unlinkSync,
} = fs;
const childProcess = promisify(child.exec);
const { log } = utils;
@ -73,8 +78,7 @@ export default {
// Create artifact in temp path and move it to the package path (if any) later
const artifactFilePath = path.join(this.serverless.serviceDir, '.serverless', zipFileName);
this.serverless.utils.writeFileDir(artifactFilePath);
const output = fs.createWriteStream(artifactFilePath);
const output = createWriteStream(artifactFilePath);
return new BbPromise((resolve, reject) => {
output.on('close', () => resolve(artifactFilePath));
@ -116,7 +120,7 @@ export default {
return BbPromise.all([
// Get file contents and stat in parallel
this.getFileContent(fullPath),
fs.statAsync(fullPath),
fsp.stat(fullPath),
]).then(
(result) => ({
data: result[0],
@ -134,7 +138,7 @@ export default {
// Useful point of entry for e.g. transpilation plugins
getFileContent(fullPath) {
return fs.readFileAsync(fullPath);
return fsp.readFile(fullPath);
},
};
@ -206,8 +210,8 @@ async function excludeNodeDevDependencies(serviceDir) {
.then(() =>
BbPromise.mapSeries(['dev', 'prod'], (env) => {
const depFile = env === 'dev' ? nodeDevDepFile : nodeProdDepFile;
return fs
.readFileAsync(depFile)
return fsp
.readFile(depFile)
.then((fileContent) =>
_.uniq(fileContent.toString('utf8').split(/[\r\n]+/)).filter(Boolean)
)
@ -229,7 +233,7 @@ async function excludeNodeDevDependencies(serviceDir) {
.filter((item) => item.length > 0 && item.match(nodeModulesRegex))
.reduce((globs, item) => {
const packagePath = path.join(serviceDir, item, 'package.json');
return fs.readFileAsync(packagePath, 'utf-8').then(
return fsp.readFile(packagePath, 'utf-8').then(
(packageJsonFile) => {
const lastIndex = item.lastIndexOf(path.sep) + 1;
const moduleName = item.substr(lastIndex);
@ -265,8 +269,8 @@ async function excludeNodeDevDependencies(serviceDir) {
})
.then(() => {
// cleanup
fs.unlinkSync(nodeDevDepFile);
fs.unlinkSync(nodeProdDepFile);
unlinkSync(nodeDevDepFile);
unlinkSync(nodeProdDepFile);
return exAndIn;
})
.catch(() => exAndIn)

View File

@ -47,7 +47,7 @@ export default {
'It will be automatically downloaded and added to package.json and serverless.yml'
);
} else {
log.notice.skip('There are no plugins available to display');
log.aside('There are no plugins available to display');
}
return BbPromise.resolve();

View File

@ -2,7 +2,6 @@ class ServerlessError extends Error {
constructor(message, code, options = {}) {
super(message);
this.code = code;
this.decoratedMessage = options.decoratedMessage;
}
}