Created
January 23, 2020 14:15
-
-
Save aksel/d7a6adf1862b21a6945c0253fb8a2820 to your computer and use it in GitHub Desktop.
A very barebones Webpack plugin, that builds Node functions, and recursively traverses nested stacks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const { yamlParse } = require('yaml-cfn'); | |
const merge = require('lodash/merge'); | |
const resourceReducer = (acc, [key, value]) => { | |
switch (value.Type) { | |
case 'AWS::Serverless::Application': | |
return merge(acc, { applications: { [key]: value } }); | |
case 'AWS::Serverless::Function': | |
return merge(acc, { functions: { [key]: value } }); | |
default: | |
return acc; | |
} | |
}; | |
/** | |
* Heavily inspired by https://github.com/SnappyTutorials/aws-sam-webpack-plugin | |
* Parses SAM templates, and spits out entries for each AWS::Serverless::Function | |
* Will recursively traverse nested stacks as well, for each AWS::Serverless::Application. | |
* | |
* Also copies yaml templates into .aws-sam/build folders, unmodified. | |
* | |
* Note, that nested stacks within nested stacks don't work; | |
* shit breaks after first recursion because of file paths. | |
* But we don't need that yet, so it will have to wait. | |
* It is doable though! *just* have to continually prepend a parent's stack file path to a child. | |
* | |
* TODO: Handle nested stacks within nested stacks (i.e. second level of recursion) | |
*/ | |
class SamPlugin { | |
constructor() { | |
this.entryPoints = {}; | |
this.stacks = []; | |
} | |
/** | |
* Get template at path | |
* @param {string} [path]="." | |
* @return {{}} Entry points | |
*/ | |
entries(path = '.') { | |
const templatePath = `${path}/template.yaml`; | |
const yaml = fs.readFileSync(templatePath); | |
if (!yaml) { | |
throw new Error(`No template found at ${templatePath}`); | |
} | |
this.stacks.push(templatePath); | |
const template = yamlParse(yaml.toString()); | |
this.entryForTemplate(template, path); | |
return this.entryPoints; | |
} | |
/** | |
* Finds applications and functions within a template's Resources. | |
* Adds functions to this.entries. | |
* Recursively traverses nested stacks (applications), via this.entry(template path) | |
* | |
* Path prefix is used to ensure that input and output file structures are the same. | |
* Nested stacks point to functions by path relative to their root. | |
* | |
* This lets us use an unmodified yaml file afterwards; the relative file paths are the same. | |
* @param template - Parsed YAML template. | |
* @param pathPrefix - Prefix to be added to entry point. | |
*/ | |
entryForTemplate(template, pathPrefix) { | |
const { Resources } = template; | |
const initialValue = { applications: {}, functions: {} }; | |
const { | |
applications, | |
functions, | |
} = Object.entries(Resources).reduce(resourceReducer, initialValue); | |
// Parse functions into entries | |
Object | |
.values(functions) | |
.forEach((f) => { | |
const path = [pathPrefix, f.Properties.CodeUri].join('/'); | |
this.entryPoints[path] = path; | |
}); | |
// Recursively traverse nested stacks | |
Object | |
.values(applications) | |
.forEach((value) => this.entries(`./${value.Properties.Location.replace('/template.yaml', '')}`)); | |
} | |
// Add outputTemplates as afterEmit hook. | |
apply = (compiler) => compiler.hooks.afterEmit.tap('SamPlugin', this.outputTemplates); | |
// Loop through stacks, and copy them from source to .aws-sam/build | |
outputTemplates = () => this.stacks.forEach((stack) => { | |
fs.copyFileSync(stack, `./.aws-sam/build/${stack.replace('./', '')}`); | |
}); | |
} | |
module.exports = SamPlugin; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment