-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Luciano Fantone
committed
Jan 8, 2019
0 parents
commit 00ac34c
Showing
11 changed files
with
7,450 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# EditorConfig helps developers define and maintain consistent | ||
# coding styles between different editors and IDEs | ||
# editorconfig.org | ||
|
||
root = true | ||
|
||
[*] | ||
indent_style = spaces | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"extends": [ | ||
"standard", | ||
"plugin:prettier/recommended" | ||
], | ||
"parserOptions": { | ||
"sourceType": "script" | ||
}, | ||
"env": { | ||
"node": true, | ||
"jest": true | ||
}, | ||
"rules": { | ||
"semi": [2, "always"], | ||
"no-extra-semi": 2, | ||
"strict": ["error", "global"] | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
*.DS_Store | ||
|
||
# Serverless directories | ||
.serverless | ||
|
||
# Logs | ||
logs | ||
*.log | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# Runtime data | ||
pids | ||
*.pid | ||
*.seed | ||
*.pid.lock | ||
|
||
# Directory for instrumented libs generated by jscoverage/JSCover | ||
lib-cov | ||
|
||
# Coverage directory used by tools like istanbul | ||
coverage | ||
|
||
# nyc test coverage | ||
.nyc_output | ||
|
||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) | ||
.grunt | ||
|
||
# Bower dependency directory (https://bower.io/) | ||
bower_components | ||
|
||
# node-waf configuration | ||
.lock-wscript | ||
|
||
# Compiled binary addons (http://nodejs.org/api/addons.html) | ||
build/Release | ||
|
||
# Dependency directories | ||
node_modules/ | ||
jspm_packages/ | ||
|
||
# Typescript v1 declaration files | ||
typings/ | ||
|
||
# Optional npm cache directory | ||
.npm | ||
|
||
# Optional eslint cache | ||
.eslintcache | ||
|
||
# Optional REPL history | ||
.node_repl_history | ||
|
||
# Output of 'npm pack' | ||
*.tgz | ||
|
||
# Yarn Integrity file | ||
.yarn-integrity | ||
|
||
# dotenv environment variables file | ||
.env |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"singleQuote": true, | ||
"printWidth": 100 | ||
} |
Empty file.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
'use strict'; | ||
const { rollup } = require('rollup'); | ||
const { F, has, ifElse, is, path, pathOr, propEq } = require('ramda'); | ||
const { arrayOutputSchema, inputSchema, outputSchema } = require('./src/schemas'); | ||
const fse = require('fs-extra'); | ||
const Promise = require('bluebird'); | ||
|
||
/** | ||
* Check if given value is an instance of array | ||
* @func | ||
* @param {*} value to check | ||
* @returns boolean | ||
*/ | ||
const isArray = is(Array); | ||
/** | ||
* Check if the `verbose` options equals true. | ||
* @func | ||
* @param {object} value to check the property verbose | ||
* @returns boolean | ||
*/ | ||
const isVerbose = propEq('verbose', true); | ||
/** | ||
* Check if given object has the property `watch` | ||
* @func | ||
* @param {object} value to check property `watch` | ||
* @returns boolean | ||
*/ | ||
const hasWatch = has('watch'); | ||
/** | ||
* This method will a function that will we use to validate the config schema. | ||
* @func | ||
* @param {array|object} value to check if is an array | ||
* @returns function to validate schema | ||
*/ | ||
const validateOutput = ifElse(isArray, arrayOutputSchema.validateSync, outputSchema.validateSync); | ||
/** | ||
* Method to return a logger function that will only be used in case that the verbose options is true | ||
* @param {Serverless} serverless instance of serverless | ||
* @param {Serverless.options} options serverless option object | ||
* @returns function to log on console | ||
*/ | ||
const createLogger = (serverless, options) => | ||
ifElse(isVerbose, () => path(['cli', 'log'], serverless), () => F)(options); | ||
|
||
/** | ||
* | ||
* | ||
* @class ServerlessRollupPlugin | ||
*/ | ||
class ServerlessRollupPlugin { | ||
constructor(serverless, options) { | ||
this.serverless = serverless; | ||
this.options = options; | ||
this.commands = { | ||
rollup: { | ||
usage: 'Bundle your lambda function with rollup', | ||
lifecycleEvents: ['compile'], | ||
options: { | ||
config: { | ||
usage: | ||
'Specify the rollup config you want to use' + | ||
'(e.g. "--config \'rollup.config.js\'" or "-c \'rollup.config.js\'")', | ||
required: true, | ||
shortcut: 'c' | ||
} | ||
}, | ||
commands: { | ||
compile: { | ||
type: 'entrypoint', | ||
lifecycleEvents: ['bundle'] | ||
}, | ||
prepare: { | ||
type: 'entrypoint', | ||
lifecycleEvents: ['validate', 'serverless'] | ||
}, | ||
clean: { | ||
type: 'entrypoint', | ||
lifecycleEvents: ['delete'] | ||
} | ||
} | ||
} | ||
}; | ||
|
||
this.hooks = { | ||
// Serverless deploy | ||
'before:package:createDeploymentArtifacts': () => | ||
Promise.bind(this) | ||
.then(() => this.serverless.pluginManager.spawn('rollup:prepare')) | ||
.then(() => this.serverless.pluginManager.spawn('rollup:compile')), | ||
'after:package:createDeploymentArtifacts': () => | ||
Promise.bind(this).then(() => this.serverless.pluginManager.spawn('rollup:clean')), | ||
|
||
// Serverless deploy function | ||
'before:package:function:package': () => | ||
Promise.bind(this) | ||
.then(() => this.serverless.pluginManager.spawn('rollup:prepare')) | ||
.then(() => this.serverless.pluginManager.spawn('rollup:compile')), | ||
'after:package:function:package': () => | ||
Promise.bind(this).then(() => this.serverless.pluginManager.spawn('rollup:clean')), | ||
|
||
// Serverless rollup | ||
'before:rollup:compile': this.validate.bind(this), | ||
'rollup:compile': this.bundle.bind(this), | ||
'rollup:clean': this.clean.bind(this), | ||
|
||
// Internal events | ||
'rollup:prepare:validate': this.validate.bind(this), | ||
'rollup:prepare:serverless': this.prepare.bind(this), | ||
'rollup:compile:bundle': this.bundle.bind(this), | ||
'rollup:clean:delete': this.clean.bind(this) | ||
}; | ||
|
||
this.log = createLogger(this.serverless, this.options).bind(this.serverless.cli); | ||
} | ||
|
||
getConfigPath() { | ||
return `${this.serverless.config.servicePath}/${pathOr( | ||
this.options.config, | ||
['service', 'custom', 'rollup', 'config'], | ||
this.serverless | ||
)}`; | ||
} | ||
|
||
getOutputFolder() { | ||
const { output } = this.config; | ||
// FIXME(lf): this will only work on unix envs. | ||
return output.dir || output.file.split('/')[0]; | ||
} | ||
|
||
async bundle() { | ||
this.log('Rollup: Config file is valid, about to bundle lambda function'); | ||
try { | ||
const bundle = await rollup(this.config); | ||
await bundle.write(this.config.output); | ||
|
||
this.log('Rollup: Bundle created successfully!'); | ||
} catch (error) { | ||
this.log('Rollup: There was an error while bundling the service with rollup.'); | ||
throw error; | ||
} | ||
} | ||
|
||
clean() { | ||
const folderPath = this.getOutputFolder(); | ||
this.log(`Rollup: Removing temporary folder ${folderPath}`); | ||
|
||
if (this.serverless.utils.dirExistsSync(folderPath)) { | ||
fse.removeSync(folderPath); | ||
} | ||
} | ||
|
||
prepare() { | ||
// Grab all functions data and mapped their package setting use the rollup output if in the serverless config is set to package individually, | ||
// otherwise, set the global package setting. | ||
const functionNames = this.serverless.service.getAllFunctions(); | ||
const functions = {}; | ||
const output = this.getOutputFolder(); | ||
|
||
// Rollup will bundle all in a single file, so individual package should be override to false and include only the output folder; | ||
const originalPackage = this.serverless.service.package; | ||
const include = originalPackage.include || []; | ||
// Add Rollup output folder so serverles include it in the package. | ||
this.log('Rollup: Setting package options'); | ||
include.push(`${this.getOutputFolder()}/**`); | ||
|
||
// Modify functions handler to use the rollup output folder | ||
this.log('Rollup: Prepare functions handler location'); | ||
|
||
functionNames.forEach(name => { | ||
const func = this.serverless.service.getFunction(name); | ||
const handler = `${output}/${func.handler}`; | ||
this.log(`Rollup: Preparing ${name} function, setting handler to ${handler}`); | ||
functions[name] = { ...func, handler }; | ||
}); | ||
|
||
this.log(`Rollup: Overriding service options`); | ||
this.serverless.service.update({ | ||
package: { | ||
...originalPackage, | ||
individually: false, | ||
exclude: ['**/*'], | ||
include | ||
}, | ||
functions | ||
}); | ||
} | ||
|
||
validate() { | ||
const path = this.getConfigPath(); | ||
this.log(`Rollup: Starting rollup plugin with config located at ${path}`); | ||
|
||
const config = require(path); | ||
if (isArray(config)) { | ||
this.log( | ||
'Rollup: This plugin does not support multiple inputs, please check the rollup config file' | ||
); | ||
throw new Error('Invalid rollup config'); | ||
} | ||
|
||
if (hasWatch(config)) { | ||
this.log( | ||
'Rollup: This plugin does not support watch mode, please check the rollup config file' | ||
); | ||
throw new Error('Watch property is not supported'); | ||
} | ||
|
||
if (inputSchema.validateSync(config) && validateOutput(config.output)) { | ||
this.config = config; | ||
} else { | ||
this.log('Given config file is not valid, please check the rollup config file'); | ||
throw new Error('Rollup config is not valid'); | ||
} | ||
} | ||
} | ||
|
||
module.exports = ServerlessRollupPlugin; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
'use strict'; | ||
const Plugin = require('./index'); | ||
|
||
const mockServerless = { | ||
cli: { | ||
log: jest.fn() | ||
} | ||
}; | ||
const mockOptions = { | ||
config: 'rollup.config.test.js' | ||
}; | ||
|
||
test('should create a new plugin instance', () => { | ||
const plugin = new Plugin(mockServerless, mockOptions); | ||
|
||
expect(plugin).toHaveProperty('serverless', mockServerless); | ||
expect(plugin).toHaveProperty('options', mockOptions); | ||
expect(plugin).toHaveProperty('commands'); | ||
expect(plugin).toHaveProperty('hooks'); | ||
}); | ||
|
||
test('should set input and output options', () => { | ||
const plugin = new Plugin(mockServerless, mockOptions); | ||
|
||
plugin.beforeRollupStart(); | ||
expect(plugin).toHaveProperty('inputOptions'); | ||
expect(plugin).toHaveProperty('outputOptions'); | ||
}); |
Oops, something went wrong.