Skip to content
This repository was archived by the owner on Nov 13, 2021. It is now read-only.

Commit 22a4c5c

Browse files
committed
feat(lib): move to webpack
1 parent f7eae98 commit 22a4c5c

File tree

5 files changed

+1313
-185
lines changed

5 files changed

+1313
-185
lines changed

README.md

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# aws-lambda-nodejs-rollup [![GitHub license](https://img.shields.io/github/license/vvo/aws-lambda-nodejs-rollup?style=flat)](https://github.com/vvo/aws-lambda-nodejs-rollup/blob/master/LICENSE) [![Tests](https://github.com/vvo/aws-lambda-nodejs-rollup/workflows/CI/badge.svg)](https://github.com/vvo/aws-lambda-nodejs-rollup/actions) [![codecov](https://codecov.io/gh/vvo/aws-lambda-nodejs-rollup/branch/master/graph/badge.svg)](https://codecov.io/gh/vvo/aws-lambda-nodejs-rollup) ![npm](https://img.shields.io/npm/v/aws-lambda-nodejs-rollup)
1+
# aws-lambda-nodejs-webpack [![GitHub license](https://img.shields.io/github/license/vvo/aws-lambda-nodejs-webpack?style=flat)](https://github.com/vvo/aws-lambda-nodejs-webpack/blob/master/LICENSE) [![Tests](https://github.com/vvo/aws-lambda-nodejs-webpack/workflows/CI/badge.svg)](https://github.com/vvo/aws-lambda-nodejs-webpack/actions) [![codecov](https://codecov.io/gh/vvo/aws-lambda-nodejs-webpack/branch/master/graph/badge.svg)](https://codecov.io/gh/vvo/aws-lambda-nodejs-webpack) ![npm](https://img.shields.io/npm/v/aws-lambda-nodejs-webpack)
22

33
---
44

5-
_[CDK](https://aws.amazon.com/cdk/) Construct to build Node.js AWS lambdas using [rollup.js](https://rollupjs.org/)_
5+
_[CDK](https://aws.amazon.com/cdk/) Construct to build Node.js AWS lambdas using [webpack](https://webpack.js.org/)_
66

77
_Table of contents:_
88

@@ -16,15 +16,15 @@ _Table of contents:_
1616
## Usage example
1717

1818
```bash
19-
yarn add aws-lambda-nodejs-rollup
19+
yarn add aws-lambda-nodejs-webpack
2020
```
2121

2222
```js
2323
// infra/super-app-stack.js
2424
const sns = require("@aws-cdk/aws-sns");
2525
const subscriptions = require("@aws-cdk/aws-sns-subscriptions");
2626
const core = require("@aws-cdk/core");
27-
const { NodejsFunction } = require("aws-lambda-nodejs-rollup");
27+
const { NodejsFunction } = require("aws-lambda-nodejs-webpack");
2828

2929
module.exports = class SuperAppProductionStack extends core.Stack {
3030
constructor(scope, id, props) {
@@ -72,32 +72,39 @@ I want to be clear: I respect a LOT the work of the CDK team, and especially [@j
7272

7373
This is a list of features I thought could be interesting to users. If you need on of them, please contribute to the project.
7474

75-
- [ ] Allow passing rollup options, like externals
76-
- [ ] Allow using TypeScript, see https://github.com/rollup/plugins/tree/master/packages/typescript
77-
- [ ] Allow using babel, if you need preset-env
78-
- [ ] Allow passing babel options
79-
- [ ] Allow usage without the need of `entry`: `new NodejsFunction(this, "slack-notifications-lambda");` that would mimic https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-nodejs-readme.html#nodejs-function
80-
- [ ] Generate a bundle where entry is moved to /index.js
81-
- [ ] Use [jsii](https://github.com/aws/jsii) to build for other languages
82-
- [ ] Ask CDK team if this could live under their repositories
83-
- [ ] Allow native modules, with option `nativeModules`. They would have to be installed into a temp folder with `npm_config_arch` and `npm_config_platform` and aliased in rollup configuration
75+
- [ ] Test/Get feedback on TypeScript support
76+
- [ ] Get feedback on monorepo support
77+
- [ ] Allow passing webpack/babel options/a function that can update the full webpack configuration
78+
- [ ] Allow native modules/passing externals, with option `nativeModules` or `externals`. They would have to be installed into a temp folder with `npm_config_arch` and `npm_config_platform` and aliased in webpack configuration/or considered as externals. Externals and nativeModules seems related options but may be completely different
79+
- [ ] Use [jsii](https://github.com/aws/jsii) to build the construct for other languages
8480
- [ ] Add tests
85-
- [ ] Monorepo support
86-
- [ ] If necessary/beneficial, implement Rollup cache, see https://github.com/drg-adaptive/serverless-rollup-plugin and https://github.com/rollup/rollup/issues/2182
87-
- [ ] Other ideas?
81+
- [ ] (if current way buggy): force people to provide aliases instead of considering cwd as base node_module
82+
- [ ] Allow usage without the need of `entry`: `new NodejsFunction(this, "slack-notifications-lambda");` that would mimic https://docs.aws.amazon.com/cdk/api/latest/docs/aws-lambda-nodejs-readme.html#nodejs-function
83+
- [ ]
84+
- [x] Generate a bundle where entry is moved to /main.js
85+
- [x] Allow using TypeScript
86+
- [x] use webpack and babel cache
87+
- [x] remove webpackconfig from bundle
88+
- [x] pass runtime to babel target
89+
- [x] cdk synth generates different builds even when the lambda code does not changes, issue?
90+
- [x] Allow using babel, if you need preset-env
91+
- [x] add babel preset env by default
92+
- [ ] ~add bundling timing information to output console~ note: this would pollute cdk synth
93+
- [ ] ~Ask CDK team if this could live under their repositories~ Better be just community based
94+
- [ ] Other ideas? Open an issue
8895

8996
## How to make changes and test locally
9097

9198
```
9299
// fork and clone
93-
cd aws-lambda-nodejs-rollup
100+
cd aws-lambda-nodejs-webpack
94101
yarn
95102
yarn link
96103
yarn start
97104
98105
# in another terminal and project where you want to test changes
99-
yarn link aws-lambda-nodejs-rollup
100-
# cdk commands will now use your local aws-lambda-nodejs-rollup
106+
yarn link aws-lambda-nodejs-webpack
107+
# cdk commands will now use your local aws-lambda-nodejs-webpack
101108
```
102109

103110
## Thanks

package.json

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
2-
"name": "aws-lambda-nodejs-rollup",
2+
"name": "aws-lambda-nodejs-webpack",
33
"version": "0.0.0-development",
44
"private": false,
5-
"description": "CDK Construct to build Node.js AWS lambdas using rollup.js",
5+
"description": "CDK Construct to build Node.js AWS lambdas using webbpack",
66
"repository": {
77
"type": "git",
8-
"url": "https://github.com/vvo/aws-lambda-nodejs-rollup"
8+
"url": "https://github.com/vvo/aws-lambda-nodejs-webbpack"
99
},
1010
"license": "MIT",
1111
"author": "Vincent Voyer <vincent@codeagain.com>",
1212
"main": "dist/index.js",
13-
"module": "dist/aws-lambda-nodejs-rollup.esm.js",
13+
"module": "dist/aws-lambda-nodejs-webpack.esm.js",
1414
"typings": "dist/index.d.ts",
1515
"files": [
1616
"src/",
@@ -43,14 +43,19 @@
4343
"trailingComma": "all"
4444
},
4545
"dependencies": {
46-
"@rollup/plugin-commonjs": "14.0.0",
47-
"@rollup/plugin-json": "4.1.0",
48-
"@rollup/plugin-node-resolve": "8.4.0",
49-
"rollup": "2.22.2"
46+
"@babel/core": "7.10.5",
47+
"@babel/plugin-transform-runtime": "7.10.5",
48+
"@babel/preset-env": "7.10.4",
49+
"babel-loader": "8.1.0",
50+
"noop2": "2.0.0",
51+
"ts-loader": "^8.0.1",
52+
"webpack": "4.44.0",
53+
"webpack-cli": "3.3.12"
5054
},
5155
"devDependencies": {
5256
"@aws-cdk/aws-lambda": "1.54.0",
5357
"@aws-cdk/core": "1.54.0",
58+
"@typescript-eslint/parser": "^3.7.1",
5459
"eslint-plugin-import": "2.22.0",
5560
"eslint-plugin-jest": "23.18.0",
5661
"prettier-plugin-packagejson": "2.2.5",
@@ -73,12 +78,11 @@
7378
"jest": true,
7479
"node": true
7580
},
76-
"parser": "babel-eslint",
81+
"parser": "@typescript-eslint/parser",
7782
"parserOptions": {
7883
"ecmaVersion": 2019,
7984
"sourceType": "module"
8085
},
81-
"sideEffects": false,
8286
"extends": [
8387
"eslint:recommended",
8488
"plugin:jest/recommended",

rollup.config.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/NodejsFunction.ts

Lines changed: 106 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import * as fs from "fs";
22
import * as path from "path";
33
import * as os from "os";
4-
import * as lambda from "@aws-cdk/aws-lambda";
5-
import * as cdk from "@aws-cdk/core";
64
import * as process from "process";
75
import { spawnSync } from "child_process";
86

7+
import * as lambda from "@aws-cdk/aws-lambda";
8+
import * as cdk from "@aws-cdk/core";
9+
910
/**
1011
* Properties for a NodejsFunction
1112
*/
@@ -31,6 +32,17 @@ export interface NodejsFunctionProps extends lambda.FunctionOptions {
3132
*/
3233
readonly runtime?: lambda.Runtime;
3334

35+
/**
36+
* If you get "Module not found: Error: Can't resolve 'module_name'" errors, and you're not
37+
* actually using those modules, then it means there's a module you're using that is trying to
38+
* dynamically require other modules. This is the case with Knex.js. When this happens, pass all the modules
39+
* names found in the build error in this array.
40+
*
41+
* Example if you're only using PostgreSQL with Knex.js, use:
42+
* `modulesToIgnore: ["mssql", "pg-native", "pg-query-stream", "tedious"]`
43+
*/
44+
readonly modulesToIgnore?: string[];
45+
3446
/**
3547
* Whether to automatically reuse TCP connections when working with the AWS
3648
* SDK for JavaScript.
@@ -64,10 +76,10 @@ export class NodejsFunction extends lambda.Function {
6476
);
6577
}
6678

67-
const entry = path.resolve(props.entry);
79+
const entryFullPath = path.resolve(props.entry);
6880

69-
if (!fs.existsSync(entry)) {
70-
throw new Error(`Cannot find entry file at ${entry}`);
81+
if (!fs.existsSync(entryFullPath)) {
82+
throw new Error(`Cannot find entry file at ${entryFullPath}`);
7183
}
7284

7385
const handler = props.handler ?? "handler";
@@ -78,68 +90,110 @@ export class NodejsFunction extends lambda.Function {
7890
const runtime = props.runtime ?? defaultRunTime;
7991

8092
const outputDir = fs.mkdtempSync(
81-
path.join(os.tmpdir(), "aws-lambda-nodejs-rollup"),
82-
);
83-
const rollupConfigPath = path.join(outputDir, "rollup.config.js");
84-
const rollupBin = path.join(
85-
path.dirname(require.resolve("rollup")),
86-
"bin/rollup",
87-
);
88-
const sampleRollupPluginPath = path.dirname(
89-
require.resolve("@rollup/plugin-node-resolve"),
93+
path.join(os.tmpdir(), "aws-lambda-nodejs-webpack"),
9094
);
91-
const pluginsPath = sampleRollupPluginPath.slice(
92-
0,
93-
sampleRollupPluginPath.lastIndexOf("/node_modules"),
95+
const webpackBinPath = require.resolve("webpack-cli");
96+
const webpackConfigPath = path.join(outputDir, "webpack.config.js");
97+
const pluginsPath = path.join(
98+
webpackBinPath.slice(0, webpackBinPath.lastIndexOf("/node_modules")),
99+
"node_modules",
94100
);
95101

96-
fs.writeFileSync(
97-
rollupConfigPath,
98-
`
99-
import { nodeResolve } from "${pluginsPath}/node_modules/@rollup/plugin-node-resolve";
100-
import commonjs from "${pluginsPath}/node_modules/@rollup/plugin-commonjs";
101-
import json from "${pluginsPath}/node_modules/@rollup/plugin-json";
102-
import { builtinModules } from "module";
103-
104-
export default {
105-
input: "${entry}",
102+
const webpackConfiguration = `
103+
const { builtinModules } = require("module");
104+
const { NormalModuleReplacementPlugin } = require("${path.join(
105+
pluginsPath,
106+
"webpack",
107+
)}");
108+
109+
module.exports = {
110+
mode: "none",
111+
entry: "${entryFullPath}",
112+
target: "node",
113+
resolve: {
114+
modules: ["node_modules", "."]
115+
},
116+
devtool: "source-map",
117+
module: {
118+
rules: [
119+
{
120+
test: /\\.js$/,
121+
exclude: /node_modules/,
122+
use: {
123+
loader: "${path.join(pluginsPath, "babel-loader")}",
124+
options: {
125+
cacheDirectory: true,
126+
presets: [
127+
[
128+
"${path.join(pluginsPath, "@babel/preset-env")}",
129+
{
130+
"targets": {
131+
"node": "${
132+
runtime.name.split("nodejs")[1].split(".")[0]
133+
}"
134+
},
135+
loose: true,
136+
bugfixes: true,
137+
},
138+
]
139+
],
140+
plugins: ["${path.join(
141+
pluginsPath,
142+
"@babel/plugin-transform-runtime",
143+
)}"]
144+
}
145+
}
146+
},
147+
{
148+
test: /\\.ts$/,
149+
use: 'ts-loader',
150+
exclude: /node_modules/,
151+
},
152+
]
153+
},
154+
externals: [...builtinModules, "aws-sdk"],
106155
output: {
107-
dir: "${outputDir}",
108-
format: "cjs",
109-
preserveModules: true,
110-
exports: "auto",
111-
sourcemap: "inline",
156+
filename: "[name].js",
157+
path: "${outputDir}",
158+
libraryTarget: "commonjs2",
112159
},
160+
${(props.modulesToIgnore &&
161+
`
113162
plugins: [
114-
nodeResolve({
115-
preferBuiltins: true,
116-
}),
117-
commonjs(),
118-
json(),
119-
],
120-
external: ["aws-sdk", ...builtinModules],
121-
};
122-
`,
123-
);
124-
125-
const rollup = spawnSync(rollupBin, ["-c", rollupConfigPath]);
163+
new NormalModuleReplacementPlugin(
164+
/${props.modulesToIgnore.join("|")}/,
165+
"${path.join(pluginsPath, "noop2")}",
166+
),
167+
]
168+
`) ||
169+
""}
170+
};`;
171+
172+
fs.writeFileSync(webpackConfigPath, webpackConfiguration);
173+
174+
// to implement cache, create a script that uses webpack API, store cache in a file with JSON.stringify, based on entry path key then reuse it
175+
const webpack = spawnSync(webpackBinPath, ["--config", webpackConfigPath], {
176+
cwd: process.cwd(),
177+
});
126178

127-
if (rollup.status !== 0) {
128-
console.error("Rollup had an error bundling.");
129-
console.error(rollup.output.map(out => out?.toString()));
179+
if (webpack.status !== 0) {
180+
console.error("webpack had an error when bundling.");
181+
console.error(
182+
webpack?.output?.map(out => {
183+
return out?.toString();
184+
}),
185+
);
186+
console.error("webpack configuration was:", webpackConfiguration);
130187
process.exit(1);
131188
}
132189

133-
const entryWithoutExtension = path.join(
134-
path.dirname(props.entry),
135-
path.basename(props.entry, path.extname(props.entry)),
136-
);
190+
fs.unlinkSync(webpackConfigPath);
137191

138192
super(scope, id, {
139193
...props,
140194
runtime,
141195
code: lambda.Code.fromAsset(outputDir),
142-
handler: `${entryWithoutExtension}.${handler}`,
196+
handler: `main.${handler}`,
143197
});
144198

145199
// Enable connection reuse for aws-sdk

0 commit comments

Comments
 (0)