diff --git a/.gitignore b/.gitignore index f142f4c..7cafbdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ coverage -node_modules -dist \ No newline at end of file +node_modules \ No newline at end of file diff --git a/README.md b/README.md index 3f68759..fc45c86 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,17 @@ custom: file: .build/stack.toml # toml, yaml, yml, and json format is available ``` +Optionally, you can provide a format explicitly which will allow you to choose +any output filename. This format will override the implicit one from the file +extension. + +```yaml +custom: + output: + format: toml + file: .env +``` + ### Handler Based on the configuration above the plugin will search for a file `scripts/output.js` with the following content: diff --git a/dist/file.js b/dist/file.js new file mode 100644 index 0000000..decfe8f --- /dev/null +++ b/dist/file.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var fs = require("fs"); +var StackOutputFile = /** @class */ (function () { + function StackOutputFile(path, format) { + this.path = path; + this.format = format; + } + StackOutputFile.prototype.formatData = function (data) { + var ext = this.path.split('.').pop() || ''; + var format = this.format || ext; + switch (format.toUpperCase()) { + case 'JSON': + return JSON.stringify(data, null, 2); + case 'TOML': + return require('tomlify-j0.4')(data, null, 0).replace(/ /g, ""); + case 'YAML': + case 'YML': + return require('yamljs').stringify(data); + default: + throw new Error('No formatter found for `' + format + '` extension'); + } + }; + StackOutputFile.prototype.save = function (data) { + var content = this.formatData(data); + try { + fs.writeFileSync(this.path, content); + } + catch (e) { + throw new Error('Cannot write to file: ' + this.path); + } + return Promise.resolve(); + }; + return StackOutputFile; +}()); +exports.default = StackOutputFile; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..d9c18df --- /dev/null +++ b/dist/index.js @@ -0,0 +1,4 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var plugin_1 = require("./plugin"); +module.exports = plugin_1.default; diff --git a/dist/plugin.js b/dist/plugin.js new file mode 100644 index 0000000..8a8d797 --- /dev/null +++ b/dist/plugin.js @@ -0,0 +1,99 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var assert = require("assert"); +var util = require("util"); +var file_1 = require("./file"); +var StackOutputPlugin = /** @class */ (function () { + function StackOutputPlugin(serverless, options) { + this.serverless = serverless; + this.options = options; + this.hooks = { + 'after:deploy:deploy': this.process.bind(this) + }; + this.output = this.serverless.service.custom.output; + } + Object.defineProperty(StackOutputPlugin.prototype, "file", { + get: function () { + return this.getConfig('file'); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StackOutputPlugin.prototype, "handler", { + get: function () { + return this.getConfig('handler'); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StackOutputPlugin.prototype, "stackName", { + get: function () { + return util.format('%s-%s', this.serverless.service.getServiceName(), this.serverless.getProvider('aws').getStage()); + }, + enumerable: true, + configurable: true + }); + StackOutputPlugin.prototype.hasConfig = function (key) { + return !!this.output && !!this.output[key]; + }; + StackOutputPlugin.prototype.hasHandler = function () { + return this.hasConfig('handler'); + }; + StackOutputPlugin.prototype.hasFile = function () { + return this.hasConfig('file'); + }; + StackOutputPlugin.prototype.getConfig = function (key) { + return util.format('%s/%s', this.serverless.config.servicePath, this.output[key]); + }; + StackOutputPlugin.prototype.callHandler = function (data) { + var splits = this.handler.split('.'); + var func = splits.pop() || ''; + var file = splits.join('.'); + require(file)[func](data, this.serverless, this.options); + return Promise.resolve(); + }; + StackOutputPlugin.prototype.saveFile = function (data) { + var f = new file_1.default(this.file, this.output.format); + return f.save(data); + }; + StackOutputPlugin.prototype.fetch = function () { + return this.serverless.getProvider('aws').request('CloudFormation', 'describeStacks', { StackName: this.stackName }, this.serverless.getProvider('aws').getStage(), this.serverless.getProvider('aws').getRegion()); + }; + StackOutputPlugin.prototype.beautify = function (data) { + var stack = data.Stacks.pop() || { Outputs: [] }; + var output = stack.Outputs || []; + return output.reduce(function (obj, item) { + return (Object.assign(obj, (_a = {}, _a[item.OutputKey] = item.OutputValue, _a))); + var _a; + }, {}); + }; + StackOutputPlugin.prototype.handle = function (data) { + return Promise.all([ + this.handleHandler(data), + this.handleFile(data) + ]); + }; + StackOutputPlugin.prototype.handleHandler = function (data) { + var _this = this; + return this.hasHandler() ? (this.callHandler(data).then(function () { return _this.serverless.cli.log(util.format('Stack Output processed with handler: %s', _this.output.handler)); })) : Promise.resolve(); + }; + StackOutputPlugin.prototype.handleFile = function (data) { + var _this = this; + return this.hasFile() ? (this.saveFile(data).then(function () { return _this.serverless.cli.log(util.format('Stack Output saved to file: %s', _this.output.file)); })) : Promise.resolve(); + }; + StackOutputPlugin.prototype.validate = function () { + assert(this.serverless, 'Invalid serverless configuration'); + assert(this.serverless.service, 'Invalid serverless configuration'); + assert(this.serverless.service.provider, 'Invalid serverless configuration'); + assert(this.serverless.service.provider.name, 'Invalid serverless configuration'); + assert(this.serverless.service.provider.name === 'aws', 'Only supported for AWS provider'); + assert(this.options && !this.options.noDeploy, 'Skipping deployment with --noDeploy flag'); + }; + StackOutputPlugin.prototype.process = function () { + var _this = this; + Promise.resolve() + .then(function () { return _this.validate(); }).then(function () { return _this.fetch(); }).then(function (res) { return _this.beautify(res); }).then(function (res) { return _this.handle(res); }).catch(function (err) { return _this.serverless.cli.log(util.format('Cannot process Stack Output: %s!', err.message)); }); + }; + return StackOutputPlugin; +}()); +exports.default = StackOutputPlugin; diff --git a/package.json b/package.json index abe53e7..47ffbdc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "serverless-stack-output", "description": "Serverless plugin to process AWS CloudFormation Stack Output", + "version": "0.2.6", "license": "MIT", "author": "Sebastian Müller ", "main": "dist", diff --git a/src/file.ts b/src/file.ts index 5ea6295..b945238 100644 --- a/src/file.ts +++ b/src/file.ts @@ -2,27 +2,30 @@ import * as fs from 'fs' export default class StackOutputFile { constructor ( - public path: string + public path: string, + public format: string ) { } - public format (data: object) { + public formatData (data: object) { const ext = this.path.split('.').pop() || '' - switch (ext.toUpperCase()) { + const format = this.format || ext; + + switch (format.toUpperCase()) { case 'JSON': return JSON.stringify(data, null, 2) case 'TOML': - return require('tomlify-j0.4')(data, null, 0) + return require('tomlify-j0.4')(data, null, 0).replace(/ /g, "") case 'YAML': case 'YML': return require('yamljs').stringify(data) default: - throw new Error('No formatter found for `' + ext + '` extension') + throw new Error('No formatter found for `' + format + '` extension') } } public save (data: object) { - const content = this.format(data) + const content = this.formatData(data) try { fs.writeFileSync(this.path, content) diff --git a/src/plugin.ts b/src/plugin.ts index 0189df5..8a7a917 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -67,7 +67,7 @@ export default class StackOutputPlugin { } private saveFile (data: object) { - const f = new StackOutputFile(this.file) + const f = new StackOutputFile(this.file, this.output.format) return f.save(data) } diff --git a/vendor/main.d.ts b/vendor/main.d.ts index d838acf..2406ad7 100644 --- a/vendor/main.d.ts +++ b/vendor/main.d.ts @@ -14,4 +14,5 @@ declare interface StackDescriptionList { declare interface OutputConfig { handler: string file: string + format: string } \ No newline at end of file