mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
Fix #5242 Do this by first supplying the `servicePath` in `~/lib/plugins/lib/validate.js` and `~/lib/plugins/print.js` in a consistent manner with the code in `~/lib/classes/PluginManager.js`. Then, provide a default of `process.cwd()` for `servicePath` in `getCacheFilePath` and `getServerlessConfigFile`.
170 lines
6.1 KiB
JavaScript
170 lines
6.1 KiB
JavaScript
'use strict';
|
|
|
|
const os = require('os');
|
|
const _ = require('lodash');
|
|
const BbPromise = require('bluebird');
|
|
const getServerlessConfigFile = require('../../utils/getServerlessConfigFile');
|
|
const jc = require('json-cycle');
|
|
const YAML = require('js-yaml');
|
|
|
|
class Print {
|
|
constructor(serverless, options) {
|
|
this.serverless = serverless;
|
|
this.options = options || {};
|
|
this.cache = {};
|
|
|
|
this.commands = {
|
|
print: {
|
|
usage: 'Print your compiled and resolved config file',
|
|
lifecycleEvents: [
|
|
'print',
|
|
],
|
|
options: {
|
|
format: {
|
|
usage: 'Print configuration in given format ("yaml", "json", "text"). Default: yaml',
|
|
},
|
|
path: {
|
|
usage: 'Optional period-separated path to print a sub-value (eg: "provider.name")',
|
|
},
|
|
transform: {
|
|
usage: 'Optional transform-function to apply to the value ("keys")',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
this.hooks = {
|
|
'print:print': () => BbPromise.bind(this)
|
|
.then(this.print),
|
|
};
|
|
}
|
|
|
|
adorn(svc) {
|
|
const service = svc;
|
|
// service object treatment
|
|
this.cache.serviceService = service.service;
|
|
if (_.isObject(this.cache.serviceService)) {
|
|
service.service = service.service.name;
|
|
service.serviceObject = this.cache.serviceService;
|
|
}
|
|
// provider treatment and defaults
|
|
this.cache.serviceProvider = service.provider;
|
|
if (_.isString(this.cache.serviceProvider)) {
|
|
service.provider = { name: this.cache.serviceProvider };
|
|
}
|
|
service.provider = _.merge({
|
|
stage: 'dev',
|
|
region: 'us-east-1',
|
|
variableSyntax: '\\${([ ~:a-zA-Z0-9._\'",\\-\\/\\(\\)]+?)}',
|
|
}, service.provider);
|
|
}
|
|
strip(svc) {
|
|
const service = svc;
|
|
if (_.isObject(this.cache.serviceService)) {
|
|
service.service = this.cache.serviceService;
|
|
delete service.serviceObject;
|
|
}
|
|
if (_.isString(this.cache.serviceProvider)) {
|
|
service.provider = this.cache.serviceProvider;
|
|
} else { // is object
|
|
if (!this.cache.serviceProvider.stage) {
|
|
delete service.provider.stage;
|
|
}
|
|
if (!this.cache.serviceProvider.region) {
|
|
delete service.provider.region;
|
|
}
|
|
if (this.cache.serviceProvider.variableSyntax) {
|
|
service.provider.variableSyntax = this.cache.serviceProvider.variableSyntax;
|
|
}
|
|
}
|
|
}
|
|
print() {
|
|
// #####################################################################
|
|
// ## KEEP SYNCHRONIZED WITH EQUIVALENT IN ~/lib/classes/Variables.js ##
|
|
// ## there, see `populateService` ##
|
|
// ## here, see below ##
|
|
// #####################################################################
|
|
// ###################################################################
|
|
// ## KEEP SYNCHRONIZED WITH EQUIVALENT IN ~/lib/classes/Service.js ##
|
|
// ## there, see `constructor` and `loadServiceFileParam` ##
|
|
// ## here, see `strip` and `adorn` ##
|
|
// ###################################################################
|
|
// The *already loaded* Service (i.e. serverless.yml) is adorned with extras for use by the
|
|
// framework and throughout its codebase. We could try using the populated service but that
|
|
// would require playing whack-a-mole on issues as changes are made to the service anywhere in
|
|
// the codebase. Avoiding that, this method must read the serverless.yml file itself, adorn it
|
|
// as the Service class would and then populate it, reversing the adornments thereafter in
|
|
// preparation for printing the service for the user.
|
|
return getServerlessConfigFile(this.serverless.config.servicePath)
|
|
.then((svc) => {
|
|
const service = svc;
|
|
this.adorn(service);
|
|
// Need to delete variableSyntax to avoid self-matching errors
|
|
this.serverless.variables.loadVariableSyntax();
|
|
delete service.provider.variableSyntax; // cached by adorn, restored by strip
|
|
this.serverless.variables.service = service;
|
|
return this.serverless.variables.populateObject(service)
|
|
.then((populated) => {
|
|
let conf = populated;
|
|
this.strip(conf);
|
|
|
|
// dig into the object
|
|
if (this.options.path) {
|
|
const steps = this.options.path.split('.');
|
|
for (const step of steps) {
|
|
conf = conf[step];
|
|
|
|
if (conf === undefined) {
|
|
return BbPromise.reject(
|
|
new this.serverless.classes.Error(`Path "${this.options.path}" not found`)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// apply an optional filter
|
|
if (this.options.transform) {
|
|
if (this.options.transform === 'keys') {
|
|
conf = Object.keys(conf);
|
|
} else {
|
|
return BbPromise.reject(
|
|
new this.serverless.classes.Error('Transform can only be "keys"')
|
|
);
|
|
}
|
|
}
|
|
|
|
// print configuration in the specified format
|
|
const format = this.options.format || 'yaml';
|
|
let out;
|
|
|
|
if (format === 'text') {
|
|
if (_.isArray(conf)) {
|
|
out = conf.join(os.EOL);
|
|
} else {
|
|
if (_.isObject(conf)) {
|
|
return BbPromise.reject(
|
|
new this.serverless.classes.Error('Cannot print an object as "text"')
|
|
);
|
|
}
|
|
|
|
out = String(conf);
|
|
}
|
|
} else if (format === 'json') {
|
|
out = jc.stringify(conf, null, ' ');
|
|
} else if (format === 'yaml') {
|
|
out = YAML.dump(JSON.parse(jc.stringify(conf)), { noRefs: true });
|
|
} else {
|
|
return BbPromise.reject(
|
|
new this.serverless.classes.Error('Format must be "yaml", "json" or "text"')
|
|
);
|
|
}
|
|
|
|
this.serverless.cli.consoleLog(out);
|
|
return BbPromise.resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = Print;
|