Skip to content

Commit

Permalink
fix(lambda-nodejs): parcel is too big to bundle
Browse files Browse the repository at this point in the history
Stop bundling parcel-bundler and make it an undeclared peer dependency.

Transform build function into a class.

Fixes aws#6340

BREAKING CHANGE: parcel-bundler v1.x is now a peer dependency of @aws-cdk/aws-lambda-nodejs. Please add it to your package.json.
  • Loading branch information
Elad Ben-Israel authored Feb 19, 2020
2 parents 9209ef6 + a2048ff commit a93e4d5
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 119 deletions.
9 changes: 9 additions & 0 deletions packages/@aws-cdk/aws-lambda-nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@

This library provides constructs for Node.js Lambda functions.

To use this module, you will need to add a dependency on `parcel-bundler` in your
`package.json`:

```
yarn add parcel-bundler@^1
# or
npm install parcel-bundler@^1
```

### Node.js Function
Define a `NodejsFunction`:

Expand Down
97 changes: 0 additions & 97 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/build.ts

This file was deleted.

113 changes: 113 additions & 0 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { spawnSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { findPkgPath, updatePkg } from './util';

/**
* Builder options
*/
export interface BuilderOptions {
/**
* Entry file
*/
readonly entry: string;

/**
* The output directory
*/
readonly outDir: string;

/**
* Expose modules as UMD under this name
*/
readonly global: string;

/**
* Minify
*/
readonly minify?: boolean;

/**
* Include source maps
*/
readonly sourceMaps?: boolean;

/**
* The cache directory
*/
readonly cacheDir?: string;

/**
* The node version to use as target for Babel
*/
readonly nodeVersion?: string;
}

/**
* Builder
*/
export class Builder {
private readonly parcelBinPath: string;

constructor(private readonly options: BuilderOptions) {
let parcelPkgPath: string;
try {
parcelPkgPath = require.resolve('parcel-bundler/package.json'); // This will throw if `parcel-bundler` cannot be found
} catch (err) {
throw new Error('It looks like parcel-bundler is not installed. Please install v1.x of parcel-bundler with yarn or npm.');
}
const parcelDir = path.dirname(parcelPkgPath);
const parcelPkg = JSON.parse(fs.readFileSync(parcelPkgPath, 'utf8'));

if (!parcelPkg.version || !/^1\./.test(parcelPkg.version)) { // Peer dependency on parcel v1.x
throw new Error(`This module has a peer dependency on parcel-bundler v1.x. Got v${parcelPkg.version}.`);
}

this.parcelBinPath = path.join(parcelDir, parcelPkg.bin.parcel);
}

public build(): void {
const pkgPath = findPkgPath();
let originalPkg;

try {
if (this.options.nodeVersion && pkgPath) {
// Update engines.node (Babel target)
originalPkg = updatePkg(pkgPath, {
engines: { node: `>= ${this.options.nodeVersion}` }
});
}

const args = [
'build', this.options.entry,
'--out-dir', this.options.outDir,
'--out-file', 'index.js',
'--global', this.options.global,
'--target', 'node',
'--bundle-node-modules',
'--log-level', '2',
!this.options.minify && '--no-minify',
!this.options.sourceMaps && '--no-source-maps',
...this.options.cacheDir
? ['--cache-dir', this.options.cacheDir]
: [],
].filter(Boolean) as string[];

const parcel = spawnSync(this.parcelBinPath, args);

if (parcel.error) {
throw parcel.error;
}

if (parcel.status !== 0) {
throw new Error(parcel.stdout.toString().trim());
}
} catch (err) {
throw new Error(`Failed to build file at ${this.options.entry}: ${err}`);
} finally { // Always restore package.json to original
if (pkgPath && originalPkg) {
fs.writeFileSync(pkgPath, originalPkg);
}
}
}
}
5 changes: 3 additions & 2 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as cdk from '@aws-cdk/core';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { build } from './build';
import { Builder } from './builder';
import { nodeMajorVersion, parseStackTrace } from './util';

/**
Expand Down Expand Up @@ -86,7 +86,7 @@ export class NodejsFunction extends lambda.Function {
const runtime = props.runtime || defaultRunTime;

// Build with Parcel
build({
const builder = new Builder({
entry,
outDir: handlerDir,
global: handler,
Expand All @@ -95,6 +95,7 @@ export class NodejsFunction extends lambda.Function {
cacheDir: props.cacheDir,
nodeVersion: extractVersion(runtime),
});
builder.build();

super(scope, id, {
...props,
Expand Down
9 changes: 3 additions & 6 deletions packages/@aws-cdk/aws-lambda-nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,13 @@
"cdk-build-tools": "1.24.0",
"cdk-integ-tools": "1.24.0",
"fs-extra": "^8.1.0",
"parcel-bundler": "^1.12.4",
"pkglint": "1.24.0"
},
"dependencies": {
"@aws-cdk/aws-lambda": "1.24.0",
"@aws-cdk/core": "1.24.0",
"parcel-bundler": "^1.12.4"
"@aws-cdk/core": "1.24.0"
},
"bundledDependencies": [
"parcel-bundler"
],
"homepage": "https://github.com/aws/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-lambda": "1.24.0",
Expand All @@ -107,4 +104,4 @@
"node": ">= 10.3.0"
},
"stability": "experimental"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import { spawnSync } from 'child_process';
import { build } from '../lib/build';
import * as fs from 'fs';
import { Builder } from '../lib/builder';

let parcelPkgPath: string;
let parcelPkg: Buffer;
beforeAll(() => {
parcelPkgPath = require.resolve('parcel-bundler/package.json');
parcelPkg = fs.readFileSync(parcelPkgPath);
});

afterEach(() => {
fs.writeFileSync(parcelPkgPath, parcelPkg);
});

jest.mock('child_process', () => ({
spawnSync: jest.fn((_cmd: string, args: string[]) => {
Expand All @@ -16,12 +28,13 @@ jest.mock('child_process', () => ({
}));

test('calls parcel with the correct args', () => {
build({
const builder = new Builder({
entry: 'entry',
global: 'handler',
outDir: 'out-dir',
cacheDir: 'cache-dir',
});
builder.build();

expect(spawnSync).toHaveBeenCalledWith(expect.stringContaining('parcel-bundler'), expect.arrayContaining([
'build', 'entry',
Expand All @@ -38,17 +51,31 @@ test('calls parcel with the correct args', () => {
});

test('throws in case of error', () => {
expect(() => build({
const builder = new Builder({
entry: 'error',
global: 'handler',
outDir: 'out-dir'
})).toThrow('parcel-error');
});
expect(() => builder.build()).toThrow('parcel-error');
});

test('throws if status is not 0', () => {
expect(() => build({
const builder = new Builder({
entry: 'status',
global: 'handler',
outDir: 'out-dir'
})).toThrow('status-error');
});
expect(() => builder.build()).toThrow('status-error');
});

test('throws when parcel-bundler is not 1.x', () => {
fs.writeFileSync(parcelPkgPath, JSON.stringify({
...JSON.parse(parcelPkg.toString()),
version: '2.3.4'
}));
expect(() => new Builder({
entry: 'entry',
global: 'handler',
outDir: 'out-dur'
})).toThrow(/This module has a peer dependency on parcel-bundler v1.x. Got v2.3.4./);
});
22 changes: 14 additions & 8 deletions packages/@aws-cdk/aws-lambda-nodejs/test/function.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ import { Stack } from '@aws-cdk/core';
import * as fs from 'fs-extra';
import * as path from 'path';
import { NodejsFunction } from '../lib';
import { build, BuildOptions } from '../lib/build';
import { Builder, BuilderOptions } from '../lib/builder';

jest.mock('../lib/build', () => ({
build: jest.fn((options: BuildOptions) => {
require('fs-extra').ensureDirSync(options.outDir); // eslint-disable-line @typescript-eslint/no-require-imports
})
}));
jest.mock('../lib/builder', () => {
return {
Builder: jest.fn().mockImplementation((options: BuilderOptions) => {
return {
build: jest.fn(() => {
require('fs-extra').ensureDirSync(options.outDir); // eslint-disable-line @typescript-eslint/no-require-imports
})
};
})
};
});

let stack: Stack;
const buildDir = path.join(__dirname, '.build');
Expand All @@ -27,7 +33,7 @@ test('NodejsFunction with .ts handler', () => {
// WHEN
new NodejsFunction(stack, 'handler1');

expect(build).toHaveBeenCalledWith(expect.objectContaining({
expect(Builder).toHaveBeenCalledWith(expect.objectContaining({
entry: expect.stringContaining('function.test.handler1.ts'), // Automatically finds .ts handler file
global: 'handler',
outDir: expect.stringContaining(buildDir)
Expand All @@ -43,7 +49,7 @@ test('NodejsFunction with .js handler', () => {
new NodejsFunction(stack, 'handler2');

// THEN
expect(build).toHaveBeenCalledWith(expect.objectContaining({
expect(Builder).toHaveBeenCalledWith(expect.objectContaining({
entry: expect.stringContaining('function.test.handler2.js'), // Automatically finds .ts handler file
}));
});
Expand Down

0 comments on commit a93e4d5

Please sign in to comment.