Skip to content

Commit

Permalink
Properly support browserify 16.1.0 and above
Browse files Browse the repository at this point in the history
- Properly support `browserify` versions 16.1.0 and up by using the new
  `node` option
    * More info: browserify/browserify#1804
- Display `browserify` version in use by the plugin
- Add _external_ browserify config
- Make more dependencies version flexible
- Major README update
- Properly set up linting
- Some code clean up
  • Loading branch information
nolde committed Oct 8, 2018
1 parent b8486c1 commit e523b78
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 94 deletions.
10 changes: 9 additions & 1 deletion .editorconfig
@@ -1,9 +1,17 @@
root = true

[*.{json,js,yml}]
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.md]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
24 changes: 24 additions & 0 deletions .eslintrc.js
@@ -0,0 +1,24 @@
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 6,
sourceType: 'script'
},
env: {
node: true,
es6: true,
jest: true
},
plugins: [
'standard',
'prettier'
],
extends: [
'standard',
'prettier',
'prettier/standard'
],
rules: {
'space-before-function-paren': ['error', 'always']
}
}
26 changes: 26 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,26 @@
Changelog
=========

v2.1.0
------
- Properly support `browserify` versions 16.1.0 and up by using the new `node` option. More info [here](https://github.com/browserify/browserify/pull/1804).
- Display `browserify` version in use by the plugin.
- Add _external_ `browserify` config.
- Make more dependencies version flexible.
- Major README update.

v2.0.0
------
- Make `browserify` a peer dependency.

v1.0.5
------
- Improve `serverless` v1.18 support.

v1.0.4
------
- Improper npm release, same as v1.0.3.

v1.0.3
------
- Use `filesize` to report file size, just as serverless does.
135 changes: 102 additions & 33 deletions README.md
Expand Up @@ -7,39 +7,27 @@ Serverless Browserifier Plugin
[![NPM downloads][downloads-badge]][npm-url]
[![standardjs][standardjs-badge]][standardjs-url]

A [Serverless](https://serverless.com) v1 plugin that uses [Browserify][browserify-url] to bundle your Node.js Lambda functions.

This project has been forked from the original [serverless-plugin-browserify][original-plugin] by *Ryan Pendergast* and published under a different name.
A [Serverless](https://serverless.com) v1 plugin that uses [`browserify`][browserify-url] to bundle your Node.js Lambda functions.

## Motivation

Lambda functions with smaller code start and run faster.
Smaller Lambda functions that run faster, with minimal changes to normal serverless configuration.

With the example `package.json` and javascript code below, the default packaging for Node.js lambdas in Serverless produces a zip file that is **11.3 MB**, because it blindly includes all of `node_modules` in the zip package.
### Smaller and faster

This plugin with 2 lines of configuration produces a zip file that is **400 KB!**
A project usualy contains several dependencies that are required by different functions in miscellaneous combinations, and packaging it all up for every function generates very large and inefficient bundles. This plugin leverages [`browserify`'s][browserify-url] ability of bundling up all files requested using `require`, generating a single lean file with all that is needed for each specific function.

```json
...
"dependencies": {
"aws-sdk": "^2.6.12",
"moment": "^2.15.2",
"request": "^2.75.0",
"rxjs": "^5.0.0-rc.1"
},
...
```
Normal serverless packaging includes all files within the `node_modules` structures, including several files that are not really needed during runtime, like _package.json_ files, documentation files, and much more. And although recent versions of serverless automatically ignore _devDependencies_, you'll certainly still have more dependencies than needed for each single function.

```javascript
const Rx = require('rxjs/Rx');
const request = require('request');
...
```
Even if you choose to manually prepare your packages with `package[include|exclude]`, you will still have to take care of that for each single function individually, and manually. This is specially hard after `npm` 3, due to dependency tree flattening.
Serverless does support manual preparation of packages, but you will still have to take care of that for each single function individually, which can quickly get out of hand dependending on the number of dependencies you need. This is specially hard after `npm` 3, due to dependency tree flattening.

The reduction is package size is, on average, __superior to 90%__. This is important as AWS Lambda has an account-wide [deployment package size limit][lambda-size-limit], and reduces file transfer times.

Less code to parse also means quicker Lambda [_cold start_][container-reuse].

Also, AWS Lambda has an account-wide [deployment package size limit](http://docs.aws.amazon.com/lambda/latest/dg/limits.html).
### Minimal changes

Mind that [aws-sdk-js](https://github.com/aws/aws-sdk-js) now officially [supports browserify](https://github.com/aws/aws-sdk-js/issues/696). Read more about this in [on this article](https://rynop.wordpress.com/2016/11/01/aws-sdk-for-javascript-now-fully-componentized/).
When using this plugin, one of the goals is to reduce serverless configuration changes as much as possible. It must possible to just remove the plugin and resume normal usage of serverless, without any additional modifications.

## Installation

Expand Down Expand Up @@ -76,41 +64,116 @@ functions:
usersGet:
name: ${self:provider.stage}-${self:service}-pageGet
description: get user
handler: users/handler.hello
handler: users/handler.hello
browserify:
noParse:
- ./someBig.json #browserify can't optimize json, will take long time to parse for nothing
- ./someBig.json #browserify can't optimize json, will take long time to parse for nothing
```

If you find a package that is not supported or does not behave well with browserify, you can still use function level `package.include` to include extra modules and files to your package. That said, you are encouraged to verify if you specific case can be dealt with by leveraging all available [browserify options](https://github.com/substack/node-browserify#browserifyfiles--opts) in your `serverless.yml` custom `browserify` section.
If you find a package that is not supported or does not behave well with browserify, you can still use function level `package.include` to include extra modules and files to your package. That said, you are encouraged to verify if you specific case can be dealt with by leveraging all available [browserify options][browserify-options] in your `serverless.yml` custom `browserify` section.

You can still use serveless' `package[include|exclude]` options to include extra files within your bundles, if necessary.

## Usage

When this plugin is enabled, and `package.individually` is `true`, running `serverless deploy` and `serverless deploy -f <funcName>` will automatically browserify your Node.js lambda code.

If you want to see more information about the process, simply set `SLS_DEBUG=*`. Example:
If you want to see more information about the process, simply set `SLS_DEBUG=*`. Example:
```
$ export SLS_DEBUG=*
$ sls deploy function -v -f usersGet
```

You can also verify your bundles by simply using `sls package`, which bundles everything up but does not deploy.

## Using browserify plugins/transforms

If you want to use browserify plugins, you can easily do that by using the global browserify options. As the plugin merely passes that up to browserify, as if it is calling the main [`browserify`][browserify-options] function, you can use it to add any transformations you want.

Do you want to transpile using TypeScript? No problem! You can use [`tsify`](https://www.npmjs.com/package/tsify):

```yml
# if you have no transform package options
custom:
browserify:
transform:
- tsify # single dash!

# if you have extra transform package options
custom:
browserify:
transform:
- - tsify # array of array, two dashes!
- noImplicitAny: true

# multiple mixed transforms
custom:
browserify:
transform:
- jstify
- - tsify
- noImplicitAny: true
```

<sup>
PS: For a more in-depth example, please check [this issue](https://github.com/digitalmaas/serverless-plugin-browserifier/issues/8).
</sup>

## Best practices

__If using it with AWS, use discrete SDK clients!__

The official [aws-sdk-js][aws-sdk] officially [supports browserify][aws-sdk-support]. That allows us to further reduce the size of our bundles (and Lambda memory usage and speed) by loading only what is strictly needed.

```javascript
// instead of ...
const AWS = require('aws-sdk')
const s3 = new AWS.S3()
// ... you should use ...
const S3 = require('aws-sdk/clients/s3')
const s3 = new S3()
```

__Ignore AWS SDK!__

Although you can use discrete clients (see item above), AWS Lambda service always bundles up the latest SDK version in its Lambda container image. That means that, even if you don't add AWS SDK to your bundle, it will still be available in runtime.

Therefore, if you don't need any specific AWS SDK version, you can add the following to your plugin config:

```yml
custom:
browserify:
exclude:
- aws-sdk
- aws-sdk/clients/s3
```

## FAQ

__Should I use Webpack instead of this plugin?__

Browserify, in general, supports more modules, optimises better (generates smaller bundles), and requires less configuration. [Webpack][webpack-github] is an amazing tool, but it comes with several extras that are not really needed within a pure Node.js environment.

__What about uglification?__
__What about uglification? And babel?__

You should be able to use [`uglify-es`][uglify-url] through [`uglifyify`][uglifyify-url]. For babel usage, [`babelify`][babelify-url] should do the trick. Refer back to [_Using browserify plugins_](#using-browserify-pluginstransforms) section to set it up.

You should be able to use [`uglify-es`][uglify-url] through [`uglifyify`][uglifyify-url].
__Avoid mixing this plugin with other plugins that modify serverless' packaging behaviour!__

__And what about babel?__
This plugin _hijacks_ the normal serverless packaging process, so it will probably conflict with other plugins that use similar mechanisms.

## Useful information

- [List of browserify's transforms][useful-transforms-list]
- [A curated list of awesome Browserify resources, libraries, and tools.][useful-browserify-resources]

I believe that [`babelify`][babelify-url] should do the trick, although I don't see any reason for it. AWS Lambda already supports Node.js 6.10, with enough ES-next goodies that allows us to avoid transpilers.

## License

MIT License.
MIT License.

This project has been forked from the original [serverless-plugin-browserify][original-plugin] and published under a different name, as the original has been abandoned.

For the complete information, please refer to the [license](./LICENSE) file.

[serverless-badge]: https://img.shields.io/badge/serverless-%E2%9A%A1-yellow.svg?colorB=555555&style=flat-square
Expand All @@ -129,3 +192,9 @@ For the complete information, please refer to the [license](./LICENSE) file.
[uglify-url]: https://www.npmjs.com/package/uglify-es
[uglifyify-url]: https://www.npmjs.com/package/uglifyify
[babelify-url]: https://www.npmjs.com/package/babelify
[aws-sdk]: https://github.com/aws/aws-sdk-js
[aws-sdk-support]: https://github.com/aws/aws-sdk-js/issues/696
[container-reuse]: https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/
[lambda-size-limit]: http://docs.aws.amazon.com/lambda/latest/dg/limits.html
[useful-transforms-list]: https://github.com/browserify/browserify/wiki/list-of-transforms
[useful-browserify-resources]: https://github.com/browserify/awesome-browserify
21 changes: 11 additions & 10 deletions index.js
@@ -1,6 +1,6 @@
'use strict'

const Bb = require('bluebird')
const Promise = require('bluebird')
const path = require('path')
const os = require('os')

Expand All @@ -9,21 +9,18 @@ const configure = require('./lib/configure')
const bundle = require('./lib/bundle')

class BrowserifierPlugin {
//
constructor (serverless, options) {
this.serverless = serverless
this.options = options
this.globalBrowserifyConfig = {}
this.servicePath = path.join(this.serverless.config.servicePath || os.tmpdir(), '.serverless')
this.functionConfigCache = {}

Object.assign(
this,
validate,
configure,
bundle
)

this.hooks = {
// Handle `sls deploy`
'before:package:createDeploymentArtifacts': this.prepareAllFunctions.bind(this),
Expand All @@ -36,19 +33,23 @@ class BrowserifierPlugin {
}

prepareAllFunctions () {
return Bb
return Promise
.bind(this)
.then(this.validate)
.then(this.computeGlobalConfig)
.then(() => Bb.all(this.getAllFunctions().map(name => this.bootstrap(name).reflect())))
.then(() => {
const fns = this.getAllFunctions().map(name => this.bootstrap(name).reflect())
this.serverless.cli.log(`Browserifier: Preparing ${fns.length} functions...`)
return Promise.all(fns)
})
.then(results => results
.filter(inspection => inspection.isRejected())
.forEach(inspection => this.handleSkip(inspection.reason())))
.tapCatch(this.warnFailure)
}

prepareFunction () {
return Bb
return Promise
.bind(this)
.then(this.validate)
.then(this.computeGlobalConfig)
Expand All @@ -58,14 +59,14 @@ class BrowserifierPlugin {
}

bundleAllFunctions () {
return Bb
return Promise
.bind(this)
.then(() => Bb.all(this.getAllFunctions().map(name => this.bundle(name))))
.then(() => Promise.all(this.getAllFunctions().map(name => this.bundle(name))))
.tapCatch(this.warnFailure)
}

bundleFunction () {
return Bb
return Promise
.bind(this)
.then(() => this.bundle(this.options.function))
.tapCatch(this.warnFailure)
Expand Down

0 comments on commit e523b78

Please sign in to comment.