Browse Source

Merge branch 'master' of https://github.com/cyyuen/serverless-webpack into cyyuen-master

master
Nicola Peduzzi 8 years ago
parent
commit
25f443caa5
  1. 44
      README.md
  2. 7
      examples/include-external-npm-packages/package.json
  3. 3
      examples/include-external-npm-packages/serverless.yml
  4. 4
      examples/include-external-npm-packages/webpack.config.js
  5. 7
      index.js
  6. 1
      lib/compile.js
  7. 123
      lib/packExternalModules.js

44
README.md

@ -3,9 +3,9 @@
[![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) [![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com)
[![CircleCI](https://circleci.com/gh/elastic-coders/serverless-webpack.svg?style=shield)](https://circleci.com/gh/elastic-coders/serverless-webpack) [![CircleCI](https://circleci.com/gh/elastic-coders/serverless-webpack.svg?style=shield)](https://circleci.com/gh/elastic-coders/serverless-webpack)
A Serverless v1.0 plugin to build your lambda functions with [Webpack](https://webpack.github.io). A Serverless v1.0 plugin to build your lambda functions with [Webpack](https://webpack.github.io).
This plugin is for you if you want to use the latest Javascript version with [Babel](https://babeljs.io/); This plugin is for you if you want to use the latest Javascript version with [Babel](https://babeljs.io/);
use custom [resource loaders](https://webpack.github.io/docs/loaders.html); use custom [resource loaders](https://webpack.github.io/docs/loaders.html);
try your lambda functions locally and much more! try your lambda functions locally and much more!
@ -67,26 +67,46 @@ module.exports = {
By default, the plugin will try to bundle all dependencies. However, you don't By default, the plugin will try to bundle all dependencies. However, you don't
want to include all modules in some cases such as selectively import, excluding want to include all modules in some cases such as selectively import, excluding
builtin package (aws-sdk) and handling webpack-incompatible modules. In this case, builtin package (ie: `aws-sdk`) and handling webpack-incompatible modules.
you add all the modules, you want to exclude from bundled files, into `externals` field
of your `webpack.config.js` and add those, you want to include in final distribution,
into `serverless.yml`:
```javascript In this case you might add external modules in
[Webpack `externals` configuration](https://webpack.github.io/docs/configuration.html#externals).
Those modules can be included in the Serverless bundle with the `webpackIncludeModules`
option in `serverless.yml`:
```js
// webpack.config.js // webpack.config.js
{ var nodeExternals = require('webpack-node-externals')
externals: ["module1", "module2"] // modules to be excluded from bundled file
modules.export = {
// we use webpack-node-externals to excludes all node deps.
// You can manually set the externals too.
externals: [nodeExternals()],
} }
``` ```
```yaml
# serverless.yml
custom:
webpackIncludeModules: true # enable auto-packing of external modules
```
All modules stated in `externals` will be excluded from bundled files. If an excluded module
is stated as `dependencies` in `package.json`, it will be packed into the Serverless
artifact under the `node_modules` directory.
By default, the plugin will use the `package.json` file in working directory, If you want to
use a different package conf, set `packagePath` to your custom package.json. eg:
```yaml ```yaml
# serverless.yml # serverless.yml
custom: custom:
webpackIncludeModules: webpackIncludeModules:
- module1 # modules to be included in distribution packagePath: '../package.json' # relative path to custom package.json file.
``` ```
> Note that only relative path is supported at the moment.
You can find an example setup in the [`examples`](./examples) folder. You can find an example setups in the [`examples`](./examples) folder.
## Usage ## Usage
@ -112,7 +132,7 @@ Options are:
- `--port` or `-p` (optional) The local server port. Defaults to `8000` - `--port` or `-p` (optional) The local server port. Defaults to `8000`
The `serve` command will automatically look for the local `serverless.yml` and serve The `serve` command will automatically look for the local `serverless.yml` and serve
all the `http` events. For example this configuration will generate a GET enpoint: all the `http` events. For example this configuration will generate a GET enpoint:
```yaml ```yaml

7
examples/include-external-npm-packages/package.json

@ -10,10 +10,11 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"serverless-webpack": "^1.0.0-beta.2", "serverless-webpack": "^1.0.0-beta.2",
"webpack": "^1.13.1" "webpack": "^1.13.1",
"webpack-node-externals": "^1.3.3",
"aws-sdk": "^2.5.3" // packages in devDependencies won't be included in distribution
}, },
"dependencies": { "dependencies": { // packages in dependencies will be included in distribution if required
"aws-sdk": "^2.5.3",
"fbgraph": "^1.3.0" "fbgraph": "^1.3.0"
} }
} }

3
examples/include-external-npm-packages/serverless.yml

@ -9,8 +9,7 @@ provider:
runtime: nodejs4.3 runtime: nodejs4.3
custom: custom:
webpackIncludeModules: # modules to be included in distribution webpackIncludeModules: true # enable auto including modules
- fbgraph
functions: functions:
first: first:

4
examples/include-external-npm-packages/webpack.config.js

@ -1,7 +1,7 @@
var path = require('path'); var nodeExternals = require('webpack-node-externals');
module.exports = { module.exports = {
entry: './handler.js', entry: './handler.js',
target: 'node', target: 'node',
externals: ["fbgraph", "aws-sdk"] // modules to be excluded from bundled file externals: [nodeExternals()] // exclude external modules
}; };

7
index.js

@ -7,7 +7,7 @@ const compile = require('./lib/compile');
const cleanup = require('./lib/cleanup'); const cleanup = require('./lib/cleanup');
const run = require('./lib/run'); const run = require('./lib/run');
const serve = require('./lib/serve'); const serve = require('./lib/serve');
const packExternalModules = require('./lib/packExternalModules') const packExternalModules = require('./lib/packExternalModules');
class ServerlessWebpack { class ServerlessWebpack {
constructor(serverless, options) { constructor(serverless, options) {
@ -30,7 +30,6 @@ class ServerlessWebpack {
lifecycleEvents: [ lifecycleEvents: [
'validate', 'validate',
'compile', 'compile',
'packExternalModules',
], ],
options: { options: {
out: { out: {
@ -102,9 +101,7 @@ class ServerlessWebpack {
.then(this.validate), .then(this.validate),
'webpack:compile': () => BbPromise.bind(this) 'webpack:compile': () => BbPromise.bind(this)
.then(this.compile), .then(this.compile)
'webpack:packExternalModules': () => BbPromise.bind(this)
.then(this.packExternalModules), .then(this.packExternalModules),
'webpack:invoke:invoke': () => BbPromise.bind(this) 'webpack:invoke:invoke': () => BbPromise.bind(this)

1
lib/compile.js

@ -12,6 +12,7 @@ module.exports = {
return BbPromise return BbPromise
.fromCallback(cb => compiler.run(cb)) .fromCallback(cb => compiler.run(cb))
.then(stats => { .then(stats => {
this.serverless.cli.consoleLog(stats.toString({ this.serverless.cli.consoleLog(stats.toString({
colors: true, colors: true,
hash: false, hash: false,

123
lib/packExternalModules.js

@ -5,38 +5,111 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const npm = require('npm-programmatic'); const npm = require('npm-programmatic');
function getProdModules(externalModules, packagePath) {
const packageJson = require(path.join(process.cwd(), packagePath));
const prodModules = [];
// only process the module stated in dependencies section
if (!packageJson.dependencies) {
return []
}
externalModules.forEach(module => {
const moduleVersion = packageJson.dependencies[module];
if (moduleVersion) {
prodModules.push(`${module}@${moduleVersion}`);
}
});
return prodModules;
}
function getExternalModuleName(module) {
const path = /^external "(.*)"$/.exec(module.identifier())[1];
const pathComponents = path.split('/');
const main = pathComponents[0];
// this is a package within a namespace
if (main.charAt(0) == '@') {
return `${main}/${pathComponents[1]}`
}
return main
}
function isExternalModule(module) {
return module.identifier().indexOf('external ') === 0;
}
function getExternalModules(stats) {
const externals = new Set();
stats.compilation.chunks.forEach(function(chunk) {
// Explore each module within the chunk (built inputs):
chunk.modules.forEach(function(module) {
// Explore each source file path that was included into the module:
if (isExternalModule(module)) {
externals.add(getExternalModuleName(module));
}
});
});
return Array.from(externals);
}
module.exports = { module.exports = {
packExternalModules() { packExternalModules(stats) {
const includes = ( const includes = (
this.serverless.service.custom && this.serverless.service.custom &&
this.serverless.service.custom.webpackIncludeModules this.serverless.service.custom.webpackIncludeModules
); );
return BbPromise.resolve().then(() => { return BbPromise.resolve().then(() => {
if (!includes || includes.length === 0) {
return;
}
this.serverless.cli.log('Packing external modules: ' + includes.join(","));
const tmpPackageJson = path.join(this.serverless.config.servicePath, 'package.json'); if (!includes) {
return;
}
const packagePath = includes.packagePath || './package.json';
const externalModules = getExternalModules(stats);
// this plugin will only install modules stated in dependencies section of package.json
const prodModules = getProdModules(externalModules, packagePath);
if (prodModules.length === 0) {
return;
}
this.serverless.cli.log('Packing external modules: ' + prodModules.join(", "));
const tmpPackageJson = path.join(this.serverless.config.servicePath, 'package.json');
// create a temp package.json in dist directory so that we can install the dependencies later. // create a temp package.json in dist directory so that we can install the dependencies later.
fs.writeFileSync(tmpPackageJson, "{}"); fs.writeFileSync(tmpPackageJson, "{}");
return new BbPromise((resolve, reject) => { return new BbPromise((resolve, reject) => {
npm.install(includes, { npm.install(prodModules, {
cwd: this.serverless.config.servicePath, cwd: this.serverless.config.servicePath,
save: false save: true
}).then(() => { }).then(() => {
fs.unlink(tmpPackageJson); // fs.unlink(tmpPackageJson);
resolve() resolve()
}).catch(e => { }).catch(e => {
fs.unlink(tmpPackageJson); // fs.unlink(tmpPackageJson);
reject(e); reject(e);
})
}) })
}) })
}, });
}
}; };

Loading…
Cancel
Save