diff --git a/packages/@aws-cdk/aws-lambda-nodejs/README.md b/packages/@aws-cdk/aws-lambda-nodejs/README.md index 981bf6755148e..359f914da040a 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/README.md +++ b/packages/@aws-cdk/aws-lambda-nodejs/README.md @@ -46,10 +46,9 @@ when working with the AWS SDK for JavaScript. Set the `awsSdkConnectionReuse` pr ## Lock file -The `NodejsFunction` requires a dependencies lock file (`yarn.lock` or -`package-lock.json`). When bundling in a Docker container, the path containing this -lock file is used as the source (`/asset-input`) for the volume mounted in the -container. +The `NodejsFunction` requires a dependencies lock file (`yarn.lock`, `pnpm-lock.yaml` or +`package-lock.json`). When bundling in a Docker container, the path containing this lock file is +used as the source (`/asset-input`) for the volume mounted in the container. By default, the construct will try to automatically determine your project lock file. Alternatively, you can specify the `depsLockFilePath` prop manually. In this @@ -114,8 +113,9 @@ new lambda.NodejsFunction(this, 'my-handler', { ``` The modules listed in `nodeModules` must be present in the `package.json`'s dependencies or -installed. The same version will be used for installation. The lock file (`yarn.lock` or -`package-lock.json`) will be used along with the right installer (`yarn` or `npm`). +installed. The same version will be used for installation. The lock file (`yarn.lock`, +`pnpm-lock.yaml` or `package-lock.json`) will be used along with the right installer (`yarn`, +`pnpm` or `npm`). When working with `nodeModules` using native dependencies, you might want to force bundling in a Docker container even if `esbuild` is available in your environment. This can be done by setting @@ -219,7 +219,7 @@ new lambda.NodejsFunction(this, 'my-handler', { ``` This image should have `esbuild` installed **globally**. If you plan to use `nodeModules` it -should also have `npm` or `yarn` depending on the lock file you're using. +should also have `npm`, `yarn` or `pnpm` depending on the lock file you're using. Use the [default image provided by `@aws-cdk/aws-lambda-nodejs`](https://github.com/aws/aws-cdk/blob/master/packages/%40aws-cdk/aws-lambda-nodejs/lib/Dockerfile) as a source of inspiration. diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile b/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile index 27fe83af43c35..578b1b8135d5c 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/Dockerfile @@ -1,11 +1,14 @@ # The correct AWS SAM build image based on the runtime of the function will be # passed as build arg. The default allows to do `docker build .` when testing. -ARG IMAGE=public.ecr.aws/sam/build-nodejs12.x +ARG IMAGE=public.ecr.aws/sam/build-nodejs14.x FROM $IMAGE # Install yarn RUN npm install --global yarn@1.22.5 +# Install pnpm +RUN npm install --global pnpm + # Install esbuild # (unsafe-perm because esbuild has a postinstall script) ARG ESBUILD_VERSION=0 diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts index 476c1e45b595c..d5519dbc5da34 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/function.ts @@ -84,28 +84,11 @@ export class NodejsFunction extends lambda.Function { throw new Error('Only `NODEJS` runtimes are supported.'); } - // Find lock file - let depsLockFilePath: string; - if (props.depsLockFilePath) { - if (!fs.existsSync(props.depsLockFilePath)) { - throw new Error(`Lock file at ${props.depsLockFilePath} doesn't exist`); - } - if (!fs.statSync(props.depsLockFilePath).isFile()) { - throw new Error('`depsLockFilePath` should point to a file'); - } - depsLockFilePath = path.resolve(props.depsLockFilePath); - } else { - const lockFile = findUp(PackageManager.YARN.lockFile) ?? findUp(PackageManager.NPM.lockFile); - if (!lockFile) { - throw new Error('Cannot find a package lock file (`yarn.lock` or `package-lock.json`). Please specify it with `depsFileLockPath`.'); - } - depsLockFilePath = lockFile; - } - // Entry and defaults const entry = path.resolve(findEntry(id, props.entry)); const handler = props.handler ?? 'handler'; const runtime = props.runtime ?? lambda.Runtime.NODEJS_14_X; + const depsLockFilePath = findLockFile(props.depsLockFilePath); super(scope, id, { ...props, @@ -126,6 +109,33 @@ export class NodejsFunction extends lambda.Function { } } +/** + * Checks given lock file or searches for a lock file + */ +function findLockFile(depsLockFilePath?: string): string { + if (depsLockFilePath) { + if (!fs.existsSync(depsLockFilePath)) { + throw new Error(`Lock file at ${depsLockFilePath} doesn't exist`); + } + + if (!fs.statSync(depsLockFilePath).isFile()) { + throw new Error('`depsLockFilePath` should point to a file'); + } + + return path.resolve(depsLockFilePath); + } + + const lockFile = findUp(PackageManager.PNPM.lockFile) + ?? findUp(PackageManager.YARN.lockFile) + ?? findUp(PackageManager.NPM.lockFile); + + if (!lockFile) { + throw new Error('Cannot find a package lock file (`pnpm-lock.yaml`, `yarn.lock` or `package-lock.json`). Please specify it with `depsFileLockPath`.'); + } + + return lockFile; +} + /** * Searches for an entry file. Preference order is the following: * 1. Given entry file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts index dd5260f87838c..a95373bd6d45f 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/lib/package-manager.ts @@ -23,6 +23,12 @@ export class PackageManager { runCommand: ['yarn', 'run'], }); + public static PNPM = new PackageManager({ + lockFile: 'pnpm-lock.yaml', + installCommand: ['pnpm', 'install'], + runCommand: ['pnpm', 'run'], + }); + public static fromLockFile(lockFilePath: string): PackageManager { const lockFile = path.basename(lockFilePath); @@ -31,6 +37,8 @@ export class PackageManager { return PackageManager.NPM; case PackageManager.YARN.lockFile: return PackageManager.YARN; + case PackageManager.PNPM.lockFile: + return PackageManager.PNPM; default: return PackageManager.NPM; } diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts index 7d79365da5077..5a2549e8fbfc6 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/bundling.test.ts @@ -231,6 +231,27 @@ test('Detects yarn.lock', () => { }); }); +test('Detects pnpm-lock.yaml', () => { + const pnpmLock = '/project/pnpm-lock.yaml'; + Bundling.bundle({ + entry: __filename, + depsLockFilePath: pnpmLock, + runtime: Runtime.NODEJS_12_X, + nodeModules: ['delay'], + forceDockerBundling: true, + }); + + // Correctly bundles with esbuild + expect(Code.fromAsset).toHaveBeenCalledWith(path.dirname(pnpmLock), { + assetHashType: AssetHashType.OUTPUT, + bundling: expect.objectContaining({ + command: expect.arrayContaining([ + expect.stringMatching(/pnpm-lock\.yaml.+pnpm install/), + ]), + }), + }); +}); + test('with Docker build args', () => { Bundling.bundle({ entry, diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts index efdbd8b8aa4cc..d1b85dfcca2b8 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/docker.test.ts @@ -39,6 +39,19 @@ test('can yarn install with non root user', () => { expect(proc.status).toEqual(0); }); +test('can pnpm install with non root user', () => { + const proc = spawnSync('docker', [ + 'run', '-u', '500:500', + 'esbuild', + 'bash', '-c', [ + 'mkdir /tmp/test', + 'cd /tmp/test', + 'pnpm add constructs', + ].join(' && '), + ]); + expect(proc.status).toEqual(0); +}); + test('cache folders have the right permissions', () => { const proc = spawnSync('docker', [ 'run', 'esbuild', diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts index f561bce592f12..e0721bbec3e38 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/package-manager.test.ts @@ -15,8 +15,15 @@ test('from a yarn.lock', () => { expect(packageManager.runBinCommand('my-bin')).toBe('yarn run my-bin'); }); -test('defaults to NPM', () => { +test('from a pnpm-lock.yaml', () => { const packageManager = PackageManager.fromLockFile('/path/to/pnpm-lock.yaml'); + expect(packageManager).toEqual(PackageManager.PNPM); + + expect(packageManager.runBinCommand('my-bin')).toBe('pnpm run my-bin'); +}); + +test('defaults to NPM', () => { + const packageManager = PackageManager.fromLockFile('/path/to/other.lock'); expect(packageManager).toEqual(PackageManager.NPM); });