Skip to content

Commit

Permalink
Merge branch 'master' into lambda-nodejs-command-hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
jogold authored Nov 29, 2020
2 parents 2fc0d32 + f85c08c commit 324304e
Show file tree
Hide file tree
Showing 168 changed files with 7,004 additions and 217 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. See [standa
* **appmesh:** renames gateway listener static methods to use shorter names
* **appmesh:** renames gateway route static methods to use shorter names
* **appmesh:** changes Route's spec to a union-like class. RouteSpec is now defined using protocol variant static methods
* **efs:** `keyId` property uses the ARN instead of the `keyId` to support cross-account encryption key usage. The filesystem will be replaced.
* **lambda-nodejs:** local bundling now requires `esbuild` to be installed.
* **lambda-nodejs**: `projectRoot` has been replaced by `depsLockFilePath`. It should point to your dependency lock file (`package-lock.json` or `yarn.lock`)
* **lambda-nodejs**: `parcelEnvironment` has been renamed to `bundlingEnvironment`
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/assert/lib/assertions/have-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function haveResource(
}

/**
* Sugar for calling ``haveResources`` with ``allowValueExtension`` set to ``true``.
* Sugar for calling ``haveResource`` with ``allowValueExtension`` set to ``true``.
*/
export function haveResourceLike(
resourceType: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-apigateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ new apigw.DomainName(this, 'domain-name', {
domainName: 'example.com',
certificate: acm.Certificate.fromCertificateArn(this, 'cert', 'arn:aws:acm:us-east-1:1111111:certificate/11-3336f1-44483d-adc7-9cd375c5169d'),
mtls: {
bucket: new Bucket(this, 'bucket')),
bucket: new Bucket(this, 'bucket'),
key: 'truststore.pem',
version: 'version',
},
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-eks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -989,8 +989,8 @@ For example, you can fetch the address of a [`LoadBalancer`](https://kubernetes.
// query the load balancer address
const myServiceAddress = new KubernetesObjectValue(this, 'LoadBalancerAttribute', {
cluster: cluster,
resourceType: 'service',
resourceName: 'my-service',
objectType: 'service',
objectName: 'my-service',
jsonPath: '.status.loadBalancer.ingress[0].hostname', // https://kubernetes.io/docs/reference/kubectl/jsonpath/
});

Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export interface ICluster extends IResource, ec2.IConnectable {
* @param manifest a list of Kubernetes resource specifications
* @returns a `KubernetesManifest` object.
*/
addManifest(id: string, ...manifest: any[]): KubernetesManifest;
addManifest(id: string, ...manifest: Record<string, any>[]): KubernetesManifest;

/**
* Defines a Helm chart in this cluster.
Expand Down Expand Up @@ -641,7 +641,7 @@ abstract class ClusterBase extends Resource implements ICluster {
* @param manifest a list of Kubernetes resource specifications
* @returns a `KubernetesResource` object.
*/
public addManifest(id: string, ...manifest: any[]): KubernetesManifest {
public addManifest(id: string, ...manifest: Record<string, any>[]): KubernetesManifest {
return new KubernetesManifest(this, `manifest-${id}`, { cluster: this, manifest });
}

Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-eks/lib/k8s-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface KubernetesManifestProps {
* }]
*
*/
readonly manifest: any[];
readonly manifest: Record<string, any>[];
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-eks/lib/legacy-cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ export class LegacyCluster extends Resource implements ICluster {
});
}

public addManifest(_id: string, ..._manifest: any[]): KubernetesManifest {
public addManifest(_id: string, ..._manifest: Record<string, any>[]): KubernetesManifest {
throw new Error('legacy cluster does not support adding kubernetes manifests');
}

Expand Down Expand Up @@ -438,7 +438,7 @@ class ImportedCluster extends Resource implements ICluster {
}
}

public addManifest(_id: string, ..._manifest: any[]): KubernetesManifest {
public addManifest(_id: string, ..._manifest: Record<string, any>[]): KubernetesManifest {
throw new Error('legacy cluster does not support adding kubernetes manifests');
}

Expand Down
59 changes: 41 additions & 18 deletions packages/@aws-cdk/aws-lambda-nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,37 @@ All other properties of `lambda.Function` are supported, see also the [AWS Lambd
The `NodejsFunction` construct automatically [reuses existing connections](https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html)
when working with the AWS SDK for JavaScript. Set the `awsSdkConnectionReuse` prop to `false` to disable it.

Use the `bundlingEnvironment` prop to define environments variables when esbuild runs:
Use the `environment` prop under `bundling` to define environments variables when esbuild runs:

```ts
new lambda.NodejsFunction(this, 'my-handler', {
bundlingEnvironment: {
NODE_ENV: 'production',
bundling: {
environment: {
NODE_ENV: 'production',
},
},
});
```

Use the `buildArgs` prop to pass build arguments when building the bundling image:
Use the `buildArgs` under `bundling` prop to pass build arguments when building the bundling image:

```ts
new lambda.NodejsFunction(this, 'my-handler', {
buildArgs: {
HTTPS_PROXY: 'https://127.0.0.1:3001',
},
bundling: {
buildArgs: {
HTTPS_PROXY: 'https://127.0.0.1:3001',
},
}
});
```

Use the `bundlingDockerImage` prop to use a custom bundling image:
Use the `dockerImage` prop under `bundling` to use a custom bundling image:

```ts
new lambda.NodejsFunction(this, 'my-handler', {
bundlingDockerImage: dk.BundlingDockerImage.fromAsset('/path/to/Dockerfile'),
bundling: {
dockerImage: cdk.BundlingDockerImage.fromAsset('/path/to/Dockerfile'),
},
});
```

Expand All @@ -90,32 +96,49 @@ case you need to ensure that this path includes `entry` and any module/dependenc
used by your function. Otherwise bundling will fail.

### Configuring esbuild
The `NodejsFunction` construct exposes some [esbuild](https://esbuild.github.io/) options via properties: `minify`, `sourceMaps` and `target`.
The `NodejsFunction` construct exposes some [esbuild](https://esbuild.github.io/) options via properties under `bundling`: `minify`, `sourceMap`, `target` and `loader`.

```ts
new lambda.NodejsFunction(this, 'my-handler', {
bundling: {
minify: true, // minify code, defaults to false
sourceMap: true, // include source map, defaults to false
target: 'es2020', // target environment for the generated JavaScript code
loader: { // Use the 'dataurl' loader for '.png' files
'.png': 'dataurl',
},
},
});
```

### Working with modules

#### Externals
By default, all node modules are bundled except for `aws-sdk`. This can be configured by specifying
the `externalModules` prop.
the `externalModules` prop under `bundling`.

```ts
new lambda.NodejsFunction(this, 'my-handler', {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'cool-module', // 'cool-module' is already available in a Layer
],
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'cool-module', // 'cool-module' is already available in a Layer
],
},
});
```

#### Install modules
By default, all node modules referenced in your Lambda code will be bundled by esbuild.
Use the `nodeModules` prop to specify a list of modules that should not be bundled
but instead included in the `node_modules` folder of the Lambda package. This is useful
Use the `nodeModules` prop under `bundling` to specify a list of modules that should not be
bundled but instead included in the `node_modules` folder of the Lambda package. This is useful
when working with native dependencies or when esbuild fails to bundle a module.

```ts
new lambda.NodejsFunction(this, 'my-handler', {
nodeModules: ['native-module', 'other-module']
bundling: {
nodeModules: ['native-module', 'other-module'],
},
});
```

Expand Down
10 changes: 5 additions & 5 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/bundling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,14 @@ export class Bundling implements cdk.BundlingOptions {
this.relativeEntryPath = path.relative(projectRoot, path.resolve(props.entry));

this.externals = [
...this.props.externalModules ?? ['aws-sdk'], // Mark aws-sdk as external by default (available in the runtime)
...this.props.nodeModules ?? [], // Mark the modules that we are going to install as externals also
...props.externalModules ?? ['aws-sdk'], // Mark aws-sdk as external by default (available in the runtime)
...props.nodeModules ?? [], // Mark the modules that we are going to install as externals also
];

// Docker bundling
const shouldBuildImage = props.forceDockerBundling || !Bundling.runsLocally;
this.image = shouldBuildImage
? props.bundlingDockerImage ?? cdk.BundlingDockerImage.fromAsset(path.join(__dirname, '../lib'), {
? props.dockerImage ?? cdk.BundlingDockerImage.fromAsset(path.join(__dirname, '../lib'), {
buildArgs: {
...props.buildArgs ?? {},
IMAGE: props.runtime.bundlingDockerImage.image,
Expand All @@ -83,7 +83,7 @@ export class Bundling implements cdk.BundlingOptions {

const bundlingCommand = this.createBundlingCommand(cdk.AssetStaging.BUNDLING_INPUT_DIR, cdk.AssetStaging.BUNDLING_OUTPUT_DIR);
this.command = ['bash', '-c', bundlingCommand];
this.environment = props.bundlingEnvironment;
this.environment = props.environment;

// Local bundling
if (!props.forceDockerBundling) { // only if Docker is not forced
Expand All @@ -106,7 +106,7 @@ export class Bundling implements cdk.BundlingOptions {
localCommand,
],
{
env: { ...process.env, ...props.bundlingEnvironment ?? {} },
env: { ...process.env, ...props.environment ?? {} },
stdio: [ // show output
'ignore', // ignore stdio
process.stderr, // redirect stdout to stderr
Expand Down
29 changes: 22 additions & 7 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import * as lambda from '@aws-cdk/aws-lambda';
import * as cdk from '@aws-cdk/core';
import { Bundling } from './bundling';
import { BundlingOptions } from './types';
import { findUp, LockFile, nodeMajorVersion, parseStackTrace } from './util';
import { callsites, findUp, LockFile, nodeMajorVersion } from './util';

/**
* Properties for a NodejsFunction
*/
export interface NodejsFunctionProps extends lambda.FunctionOptions, BundlingOptions {
export interface NodejsFunctionProps extends lambda.FunctionOptions {
/**
* Path to the entry file (JavaScript or TypeScript).
*
Expand Down Expand Up @@ -62,6 +62,14 @@ export interface NodejsFunctionProps extends lambda.FunctionOptions, BundlingOpt
* a `yarn.lock` or `package-lock.json` file
*/
readonly depsLockFilePath?: string;

/**
* Bundling options
*
* @default - use default bundling options: no minify, no sourcemap, all
* modules are bundled.
*/
readonly bundling?: BundlingOptions;
}

/**
Expand Down Expand Up @@ -100,7 +108,7 @@ export class NodejsFunction extends lambda.Function {
...props,
runtime,
code: Bundling.bundle({
...props,
...props.bundling ?? {},
entry,
runtime,
depsLockFilePath,
Expand Down Expand Up @@ -152,12 +160,19 @@ function findEntry(id: string, entry?: string): string {
* Finds the name of the file where the `NodejsFunction` is defined
*/
function findDefiningFile(): string {
const stackTrace = parseStackTrace();
const functionIndex = stackTrace.findIndex(s => /NodejsFunction/.test(s.methodName || ''));
let definingIndex;
const sites = callsites();
for (const [index, site] of sites.entries()) {
if (site.getFunctionName() === 'NodejsFunction') {
// The next site is the site where the NodejsFunction was created
definingIndex = index + 1;
break;
}
}

if (functionIndex === -1 || !stackTrace[functionIndex + 1]) {
if (!definingIndex || !sites[definingIndex]) {
throw new Error('Cannot find defining file.');
}

return stackTrace[functionIndex + 1].file;
return sites[definingIndex].getFileName();
}
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export interface BundlingOptions {
*
* @default - no environment variables are defined.
*/
readonly bundlingEnvironment?: { [key: string]: string; };
readonly environment?: { [key: string]: string; };

/**
* A list of modules that should be considered as externals (already available
Expand Down Expand Up @@ -100,7 +100,7 @@ export interface BundlingOptions {
*
* @default - use the Docker image provided by @aws-cdk/aws-lambda-nodejs
*/
readonly bundlingDockerImage?: BundlingDockerImage;
readonly dockerImage?: BundlingDockerImage;

/**
* Command hooks
Expand Down
59 changes: 23 additions & 36 deletions packages/@aws-cdk/aws-lambda-nodejs/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,33 @@ import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

// From https://github.com/errwischt/stacktrace-parser/blob/master/src/stack-trace-parser.js
const STACK_RE = /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?):(\d+)(?::(\d+))?\)?\s*$/i;

/**
* A parsed stack trace line
*/
export interface StackTrace {
readonly file: string;
readonly methodName?: string;
readonly lineNumber: number;
readonly column: number;
export interface CallSite {
getThis(): any;
getTypeName(): string;
getFunctionName(): string;
getMethodName(): string;
getFileName(): string;
getLineNumber(): number;
getColumnNumber(): number;
getFunction(): Function;
getEvalOrigin(): string;
isNative(): boolean;
isToplevel(): boolean;
isEval(): boolean;
isConstructor(): boolean;
}

/**
* Parses the stack trace of an error
* Get callsites from the V8 stack trace API
*
* https://github.com/sindresorhus/callsites
*/
export function parseStackTrace(error?: Error): StackTrace[] {
const err = error || new Error();

if (!err.stack) {
return [];
}

const lines = err.stack.split('\n');

const stackTrace: StackTrace[] = [];

for (const line of lines) {
const results = STACK_RE.exec(line);
if (results) {
stackTrace.push({
file: results[2],
methodName: results[1],
lineNumber: parseInt(results[3], 10),
column: parseInt(results[4], 10),
});
}
}

return stackTrace;
export function callsites(): CallSite[] {
const _prepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
const stack = new Error().stack?.slice(1);
Error.prepareStackTrace = _prepareStackTrace;
return stack as unknown as CallSite[];
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-lambda-nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assert": "0.0.0",
"@aws-cdk/aws-ec2": "0.0.0",
"cdk-build-tools": "0.0.0",
"cdk-integ-tools": "0.0.0",
"delay": "4.4.0",
Expand Down
Loading

0 comments on commit 324304e

Please sign in to comment.