Skip to content

Commit

Permalink
Merge pull request #3 from lifeomic/node-18
Browse files Browse the repository at this point in the history
fix: correctly externalize AWS SDKs for node 18 runtime
  • Loading branch information
swain authored Nov 21, 2022
2 parents 1d9c511 + acc0450 commit 33ac128
Show file tree
Hide file tree
Showing 4 changed files with 1,082 additions and 6 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
"yargs": "^17.5.1"
},
"devDependencies": {
"@aws-sdk/client-lambda": "^3.213.0",
"@lifeomic/eslint-config-standards": "^2.1.1",
"@lifeomic/typescript-config": "^1.0.3",
"@types/adm-zip": "^0.5.0",
"@types/glob": "^7.2.0",
"@types/jest": "^27.5.1",
"@types/node": "^14.0.0",
"@types/yargs": "^17.0.10",
"aws-sdk": "^2.1258.0",
"conventional-changelog-conventionalcommits": "^4.6.3",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.5.0",
Expand Down
137 changes: 136 additions & 1 deletion src/bundle.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
jest.mock('esbuild', () => ({ build: jest.fn() }));

import { rmSync, readdirSync, writeFileSync, mkdirSync } from 'fs';
import {
rmSync,
readdirSync,
writeFileSync,
mkdirSync,
readFileSync,
} from 'fs';
import AdmZip = require('adm-zip');
import { bundle } from './bundle';
import { build } from 'esbuild';
Expand Down Expand Up @@ -245,3 +251,132 @@ test('bundle works with an array of entries', async () => {
}),
]);
});

describe('AWS SDK bundling behavior', () => {
/** Bundles the code, returning the output. */
const bundleCode = async (params: {
node: number;
code: string;
}): Promise<string> => {
writeTestInputFile('test-file.js', params.code);
await bundle({
node: params.node,
entries: [`${TEST_INPUT_DIR}/test-file.js`],
outdir: TEST_OUTPUT_DIR,
});

const outputFile = readFileSync(
`${TEST_OUTPUT_DIR}/test-file/test-file.js`,
{ encoding: 'utf8' },
);

return outputFile;
};

/**
* There's not a perfect way to confirm whether the SDK was included in the bundle.
* So, in the tests below, we use some indirect approaches to make a solid guess.
*
* In particular, we assume:
* - The SDKs are relatively large (thousands of lines). If the bundle includes the SDK,
* it's likely to increase the bundle size by thousands of lines.
*
* - The bundler output will include many string references to the files within
* a bundled package. So, e.g. "aws-sdk" will appear in the output code many, many times
* if that package was bundled.
*
* We'll use these assumptions to make assertions below.
*/

// Node 12, 14, 16 behavior
[12, 14, 16].forEach((node) => {
test(`does not bundle aws-sdk in node version ${node}`, async () => {
const output = await bundleCode({
node,
code: `
import { Lambda } from 'aws-sdk';
export const handler = () => {
new Lambda();
return {};
};
`,
});

// Expect small # of lines -- the SDK should not be bundled.
const lines = output.split('\n').length;
expect(lines).toBeLessThan(100);

// Expect the "aws-sdk" string to only appear once -- the SDK should not be bundled.
const occurrences = output.match(/aws-sdk/g)?.length;
expect(occurrences).toStrictEqual(1);
});

test(`does bundle @aws-sdk in node version ${node}`, async () => {
const output = await bundleCode({
node,
code: `
import { Lambda } from '@aws-sdk/client-lambda';
export const handler = () => {
new Lambda();
return {};
};
`,
});

// Expect large # of lines -- the SDK should be bundled.
const lines = output.split('\n').length;
expect(lines).toBeGreaterThan(5000);

// Expect the "@aws-sdk/client-lambda" string to appear many times -- the SDK should be bundled.
const occurrences = output.match(/@aws-sdk\/client-lambda/g)?.length;
expect(occurrences).toBeGreaterThan(50);
});
});

// Node 18 behavior.
test('does not bundle @aws-sdk in node version 18', async () => {
const output = await bundleCode({
node: 18,
code: `
import { Lambda } from '@aws-sdk/client-lambda';
export const handler = () => {
new Lambda();
return {};
};
`,
});

// Expect small # of lines -- the SDK should not be bundled.
const lines = output.split('\n').length;
expect(lines).toBeLessThan(100);

// Expect the "@aws-sdk/client-lambda" string to only appear once -- the SDK should not be bundled.
const occurrences = output.match(/@aws-sdk\/client-lambda/g)?.length;
expect(occurrences).toStrictEqual(1);
});

test('does bundle aws-sdk in node version 18', async () => {
const output = await bundleCode({
node: 18,
code: `
import { Lambda } from 'aws-sdk';
export const handler = () => {
new Lambda();
return {};
};
`,
});

// Expect large # of lines -- the SDK should be bundled.
const lines = output.split('\n').length;
expect(lines).toBeGreaterThan(5000);

// Expect the "aws-sdk" string to appear many times -- the SDK should be bundled.
const occurrences = output.match(/aws-sdk/g)?.length;
expect(occurrences).toBeGreaterThan(50);
});
});
11 changes: 8 additions & 3 deletions src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,15 @@ export const bundle = async ({
outdir,
entryPoints,
/**
* Don't bundle this, since it is natively available
* in the Lambda environment
* Don't bundle the AWS SDK, since it is natively available
* in the Lambda environment.
*
* Node runtimes < 18 include the v2 sdk, while runtimes >= 18 include
* the v3 SDK.
*
* https://aws.amazon.com/blogs/compute/node-js-18-x-runtime-now-available-in-aws-lambda/
*/
external: ['aws-sdk'],
external: nodeVersion >= 18 ? ['@aws-sdk/*'] : ['aws-sdk'],
/**
* As of v0.14.44, esbuild by default prefers .ts over .js files.
*
Expand Down
Loading

0 comments on commit 33ac128

Please sign in to comment.