Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["es2015", "stage-0"],
"plugins": ["transform-runtime"]
}
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
1.0/index.js
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
logs
*.log
npm-debug.log
.eslintcache

# Runtime data
pids
Expand Down Expand Up @@ -37,4 +38,4 @@ node_modules

#SERVERLESS STUFF
admin.env
.env
.env
178 changes: 178 additions & 0 deletions 1.0/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
'use strict';

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

var _createClass2 = require('babel-runtime/helpers/createClass');

var _createClass3 = _interopRequireDefault(_createClass2);

var _regenerator = require('babel-runtime/regenerator');

var _regenerator2 = _interopRequireDefault(_regenerator);

var _promise = require('babel-runtime/core-js/promise');

var _promise2 = _interopRequireDefault(_promise);

var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');

var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);

var runWebpack = function () {
var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(config) {
return _regenerator2.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
return _context.abrupt('return', new _promise2.default(function (resolve, reject) {
(0, _webpack2.default)(config).run(function (err, stats) {
if (err) {
return reject(err);
}
return resolve(stats);
});
}));

case 1:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));

return function runWebpack(_x) {
return _ref.apply(this, arguments);
};
}();

var _fs = require('fs');

var _fs2 = _interopRequireDefault(_fs);

var _path = require('path');

var _path2 = _interopRequireDefault(_path);

var _webpack = require('webpack');

var _webpack2 = _interopRequireDefault(_webpack);

var _nodeZip = require('node-zip');

var _nodeZip2 = _interopRequireDefault(_nodeZip);

var _fp = require('lodash/fp');

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function format(stats) {
return stats.toString({
colors: true,
hash: false,
version: false,
chunks: false,
children: false
});
}

var artifact = 'handler.js';

var getConfig = function getConfig(servicePath) {
return require(_path2.default.resolve(servicePath, './webpack.config.js'));
}; // eslint-disable-line global-require

var zip = function zip(zipper, readFile, dir) {
(0, _fp.forEach)(function (file) {
return zipper.file(file, readFile(_path2.default.resolve(dir, file)));
}, _fs2.default.readdirSync(dir));
return zipper.generate({
type: 'nodebuffer',
compression: 'DEFLATE',
platform: process.platform
});
};

module.exports = function () {
function ServerlessWebpack(serverless) {
(0, _classCallCheck3.default)(this, ServerlessWebpack);

this.serverless = serverless;
this.hooks = {
'before:deploy:createDeploymentArtifacts': this.optimize.bind(this)
};
}

(0, _createClass3.default)(ServerlessWebpack, [{
key: 'optimize',
value: function () {
var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2() {
var servicePath, serverlessTmpDirPath, handlerNames, entrypoints, webpackConfig, outputDir, stats, data, zipFileName, artifactFilePath;
return _regenerator2.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (this.serverless.getVersion().startsWith('1.0')) {
_context2.next = 2;
break;
}

throw new this.serverless.classes.Error('This version of serverless-webpack-plugin requires Serverless 1.0');

case 2:
servicePath = this.serverless.config.servicePath;
serverlessTmpDirPath = _path2.default.join(servicePath, '.serverless');
handlerNames = (0, _fp.uniq)((0, _fp.map)(function (f) {
return f.handler.split('.')[0];
}, this.serverless.service.functions));
entrypoints = (0, _fp.map)(function (h) {
return './' + h + '.js';
}, handlerNames);
webpackConfig = getConfig(servicePath);

webpackConfig.context = servicePath;
webpackConfig.entry = (0, _fp.compact)((0, _fp.concat)(webpackConfig.entry, entrypoints));

outputDir = _path2.default.join(serverlessTmpDirPath, 'output');

webpackConfig.output = {
libraryTarget: 'commonjs',
path: outputDir,
filename: artifact
};

_context2.next = 13;
return runWebpack(webpackConfig);

case 13:
stats = _context2.sent;

this.serverless.cli.log(format(stats));

data = zip(new _nodeZip2.default(), _fs2.default.readFileSync, outputDir);
zipFileName = this.serverless.service.service + '-' + new Date().getTime().toString() + '.zip';
artifactFilePath = _path2.default.resolve(serverlessTmpDirPath, zipFileName);


this.serverless.utils.writeFileSync(artifactFilePath, data);
this.serverless.service.package.artifact = artifactFilePath;

case 20:
case 'end':
return _context2.stop();
}
}
}, _callee2, this);
}));

function optimize() {
return _ref2.apply(this, arguments);
}

return optimize;
}()
}]);
return ServerlessWebpack;
}();
9 changes: 9 additions & 0 deletions 1.0/src/.eslintrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
parser: babel-eslint
extends: airbnb
env:
es6: true
rules:
strict: 0
object-shorthand: 0
react/require-extension: 0
95 changes: 95 additions & 0 deletions 1.0/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import fs from 'fs';
import path from 'path';
import webpack from 'webpack';
import Zip from 'node-zip';

import {
compact,
concat,
forEach,
map,
uniq,
} from 'lodash/fp';

async function runWebpack(config) {
return new Promise((resolve, reject) => {
webpack(config).run((err, stats) => {
if (err) {
return reject(err);
}
return resolve(stats);
});
});
}

function format(stats) {
return stats.toString({
colors: true,
hash: false,
version: false,
chunks: false,
children: false,
});
}

const artifact = 'handler.js';

const getConfig = servicePath =>
require(path.resolve(servicePath, './webpack.config.js')); // eslint-disable-line global-require

const zip = (zipper, readFile, dir) => {
forEach(file =>
zipper.file(file, readFile(path.resolve(dir, file))
), fs.readdirSync(dir));
return zipper.generate({
type: 'nodebuffer',
compression: 'DEFLATE',
platform: process.platform,
});
};

module.exports = class ServerlessWebpack {
constructor(serverless) {
this.serverless = serverless;
this.hooks = {
'before:deploy:createDeploymentArtifacts': this.optimize.bind(this),
};
}

async optimize() {
if (!this.serverless.getVersion().startsWith('1.0')) {
throw new this.serverless.classes.Error(
'This version of serverless-webpack-plugin requires Serverless 1.0'
);
}
const servicePath = this.serverless.config.servicePath;
const serverlessTmpDirPath = path.join(servicePath, '.serverless');

const handlerNames = uniq(map(f =>
f.handler.split('.')[0], this.serverless.service.functions));
const entrypoints = map(h => `./${h}.js`, handlerNames);

const webpackConfig = getConfig(servicePath);
webpackConfig.context = servicePath;
webpackConfig.entry = compact(concat(webpackConfig.entry, entrypoints));

const outputDir = path.join(serverlessTmpDirPath, 'output');
webpackConfig.output = {
libraryTarget: 'commonjs',
path: outputDir,
filename: artifact,
};

const stats = await runWebpack(webpackConfig);
this.serverless.cli.log(format(stats));

const data = zip(new Zip(), fs.readFileSync, outputDir);

const zipFileName =
`${this.serverless.service.service}-${(new Date).getTime().toString()}.zip`;
const artifactFilePath = path.resolve(serverlessTmpDirPath, zipFileName);

this.serverless.utils.writeFileSync(artifactFilePath, data);
this.serverless.service.package.artifact = artifactFilePath;
}
};
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
Serverless Webpack Plugin
=============================

Forked from [serverless-optimizer-plugin](https://github.com/serverless/serverless-optimizer-plugin) this plugin uses
Forked from [serverless-optimizer-plugin](https://github.com/serverless/serverless-optimizer-plugin) this plugin uses
webpack to optimize your Serverless Node.js Functions on deployment.

Reducing the file size of your AWS Lambda Functions allows AWS to provision them more quickly, speeding up the response
Reducing the file size of your AWS Lambda Functions allows AWS to provision them more quickly, speeding up the response
time of your Lambdas. Smaller Lambda sizes also helps you develop faster because you can upload them faster.
This Severless Plugin is absolutely recommended for every project including Lambdas with Node.js.

**Note:** Requires Serverless *v0.5.0*.
**Note:** Requires Serverless *v0.5.0* or Serverless *v1.0*

### Setup

* Install the plugin and webpack in the root of your Serverless Project:
```

```sh
npm install serverless-webpack-plugin webpack --save-dev
```

* Add the plugin to the `plugins` array in your Serverless Project's `s-project.json`, like this:
#### Using with Serverless 1.0...

* Add the plugin to the `plugins` array in `serverless.yml`:

```yaml
plugins:
- serverless-webpack-plugin/1.0
```

#### Using with Serverless 0.5...

* Add the plugin to the `plugins` array in your Serverless Project's `s-project.json`, like this:

```json
plugins: [
"serverless-webpack-plugin"
]
Expand Down Expand Up @@ -85,13 +97,13 @@ module.exports = {
}
};
```
**Note:** Some node modules don't play nicely with `webpack.optimize.UglifyJsPlugin` in this case, you can omit it from
**Note:** Some node modules don't play nicely with `webpack.optimize.UglifyJsPlugin` in this case, you can omit it from
your config, or add the offending modules to `externals`. For more on externals see below.

### Externals
Externals specified in your webpack config will be properly packaged into the deployment.
This is useful when working with modules that have binary dependencies, are incompatible with `webpack.optimize.UglifyJsPlugin`
or if you simply want to improve build performance. Check out [webpack-node-externals](https://github.com/liady/webpack-node-externals)
Externals specified in your webpack config will be properly packaged into the deployment.
This is useful when working with modules that have binary dependencies, are incompatible with `webpack.optimize.UglifyJsPlugin`
or if you simply want to improve build performance. Check out [webpack-node-externals](https://github.com/liady/webpack-node-externals)
for an easy way to externalize all node modules.

### Source Maps
Expand All @@ -103,8 +115,8 @@ you can specify those modules with entry option in your webpack config.
For example if you need to load the babel-polyfill, you can do that
by adding `entry: ['babel-polyfill']` to your webpack config.
This will first load the babel-polyfill module and then your lambda function module.

### Improving deploy performance
The plugin builds directly from the source files, using "magic handlers" to include the parent directory (as mentioned in
the [0.5.0 release notes](https://github.com/serverless/serverless/releases/tag/v0.5.0)) is unnecessary.

The plugin builds directly from the source files, using "magic handlers" to include the parent directory (as mentioned in
the [0.5.0 release notes](https://github.com/serverless/serverless/releases/tag/v0.5.0)) is unnecessary.
Loading