diff --git a/package-lock.json b/package-lock.json index cd55694..2b95967 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "lambda-build", - "version": "1.0.3", + "version": "1.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "lambda-build", - "version": "1.0.3", + "version": "1.0.6", "license": "MIT", "dependencies": { "@aws-sdk/client-lambda": "^3.43.0", "chalk": "^4.1.0", "esbuild": "^0.14.2", + "file-saver": "^2.0.5", "filesize": "^8.0.6", "jszip": "^3.7.1", "yargs": "^17.3.0" @@ -20,6 +21,7 @@ "lambda-build": "dist/cli.js" }, "devDependencies": { + "@types/file-saver": "^2.0.5", "@types/filesize": "^5.0.0", "@types/jszip": "^3.4.1", "@types/mocha": "^9.0.0", @@ -1188,6 +1190,12 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "node_modules/@types/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", + "dev": true + }, "node_modules/@types/filesize": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/filesize/-/filesize-5.0.0.tgz", @@ -1594,10 +1602,16 @@ } }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -2130,6 +2144,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/filesize": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.6.tgz", @@ -2229,9 +2248,9 @@ } }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -2615,32 +2634,32 @@ } }, "node_modules/mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -2657,29 +2676,18 @@ "url": "https://opencollective.com/mochajs" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "dependencies": { - "ms": "2.1.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=10" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2726,9 +2734,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -3360,9 +3368,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -4565,6 +4573,12 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==", + "dev": true + }, "@types/filesize": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/filesize/-/filesize-5.0.0.tgz", @@ -4844,9 +4858,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -5254,6 +5268,11 @@ "flat-cache": "^3.0.4" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "filesize": { "version": "8.0.6", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.6.tgz", @@ -5325,9 +5344,9 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -5615,52 +5634,44 @@ } }, "mocha": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", - "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.2", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.25", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "dependencies": { - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } + "brace-expansion": "^1.1.7" } }, "ms": { @@ -5702,9 +5713,9 @@ "dev": true }, "nanoid": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", - "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "natural-compare": { @@ -6153,9 +6164,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { diff --git a/readme.md b/readme.md index 9775c31..75b77ec 100644 --- a/readme.md +++ b/readme.md @@ -40,16 +40,25 @@ npx lambda-build `npx lambda-build archive` bundles your code and creates a local `archive.zip` file that you can then upload to aws yourself. - using `-e`, set a custom entry file: + ```bash npx lambda-build archive -e src/index.ts ``` +- using `-o`, set a custom output file: + +```bash +npx lambda-build archive -o ../archives/archive.zip +``` + - using `-x`, exclude some libraries from bundling (for example, if you already load them in a layer): + ```bash npx lambda-build archive -e src/index.ts -x lodash dayjs ``` - using `-m`, generate a `meta.json` file which you can then use to [analyze your bundle](https://bundle-buddy.com/esbuild): + ```bash npx lambda-build archive -e src/index.ts -m ``` @@ -59,11 +68,13 @@ npx lambda-build archive -e src/index.ts -m `npx lambda-build upload` bundles your code and then uploads it directly to your AWS lambda functions (requires the [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) to be locally configured). - specify the lambda functions that you would like to deploy to: + ```bash npx lambda-build upload my-lambda1 my-lambda2 -e src/index.ts ``` - you can specify the region using the `-r` flag (otherwise it defaults to `us-east-1`): + ```bash npx lambda-build upload my-lambda1 my-lambda2 -e src/index.ts -r us-east-2 ``` @@ -81,10 +92,11 @@ const res = await build({ metafile: true, }); ``` + - returns a `res` object: - - `res.archive` - *Buffer* - contents of the zip archive - - `res.archiveSize` - *string* - the size of the archive - - `res.meta` - *string* - contents of the meta file + - `res.archive` - _Buffer_ - contents of the zip archive + - `res.archiveSize` - _string_ - the size of the archive + - `res.meta` - _string_ - contents of the meta file ### `buildAndUpload()` @@ -101,10 +113,10 @@ const res = await buildAndUpload({ ``` - returns a `res` object: - - `res.archive` - *Buffer* - contents of the zip archive - - `res.archiveSize` - *string* - the size of the archive - - `res.meta` - *string* - contents of the meta file - - `updatedArns` - *string[]* - an array with the ARNs of the lambda functions that were successfully deployed + - `res.archive` - _Buffer_ - contents of the zip archive + - `res.archiveSize` - _string_ - the size of the archive + - `res.meta` - _string_ - contents of the meta file + - `updatedArns` - _string[]_ - an array with the ARNs of the lambda functions that were successfully deployed ## Screencast diff --git a/src/cli.ts b/src/cli.ts index 80ac535..1ac93bc 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,9 +1,9 @@ #!/usr/bin/env node -import yargs from 'yargs' +import yargs from 'yargs'; import { promises as fsPromises } from 'fs'; import fs from 'fs'; import { build } from 'esbuild'; -import { hideBin } from 'yargs/helpers' +import { hideBin } from 'yargs/helpers'; import { LambdaBuildCore, argsDefatuls } from './lambda-build-core'; import { LambdaBuildCli } from './lambda-build-cli'; @@ -12,43 +12,99 @@ const cli = new LambdaBuildCli(fsPromises, console, lambdaBuildCore); const main = async () => { await yargs(hideBin(process.argv)) - .command(['archive', '$0'], 'Bundle and archive your lambda function code', () => { - // - }, async (argv: yargs.Arguments) => { - await cli.commandArchive({ - entry: argv.entry as string, - external: argv.external as string[], - metafile: argv.metafile as boolean, - }); - }) - .command('upload ', 'Bundle, archive and upload to the given lambda functions', (yargs) => { - return yargs - .positional('lambdas', { describe: 'a list of lambdas to upload the bundle to' }) - .option('region', { alias: 'r', type: 'string', default: argsDefatuls.region, description: 'set the region of the lambda functions' }) - .example([ - ['$0 upload my-func1 my-func2', 'bundle, archive and upload to my-func1 and my-func2'], - ['$0 upload my-func1 -r us-east-2', 'upload the archived bundle to my-func1 in the region us-east-2'], - ]) - }, async (argv: yargs.Arguments) => { - await cli.commandUpload({ - entry: argv.entry as string, - external: argv.external as string[], - metafile: argv.metafile as boolean, - lambdas: argv.lambdas as string[], - region: argv.region as string, - }); - }) + .command( + ['archive', '$0'], + 'Bundle and archive your lambda function code', + () => { + // + }, + async (argv: yargs.Arguments) => { + await cli.commandArchive({ + entry: argv.entry as string, + output: argv.output as string, + external: argv.external as string[], + metafile: argv.metafile as boolean, + }); + } + ) + .command( + 'upload ', + 'Bundle, archive and upload to the given lambda functions', + (yargs) => { + return yargs + .positional('lambdas', { + describe: 'a list of lambdas to upload the bundle to', + }) + .option('region', { + alias: 'r', + type: 'string', + default: argsDefatuls.region, + description: 'set the region of the lambda functions', + }) + .example([ + [ + '$0 upload my-func1 my-func2', + 'bundle, archive and upload to my-func1 and my-func2', + ], + [ + '$0 upload my-func1 -r us-east-2', + 'upload the archived bundle to my-func1 in the region us-east-2', + ], + ]); + }, + async (argv: yargs.Arguments) => { + await cli.commandUpload({ + entry: argv.entry as string, + output: argv.output as string, + external: argv.external as string[], + metafile: argv.metafile as boolean, + lambdas: argv.lambdas as string[], + region: argv.region as string, + }); + } + ) .options({ - 'e': { alias: 'entry', type: 'string', default: argsDefatuls.entry, description: 'The name of the entry file containing the lambda handler(s)' }, - 'x': { alias: 'external', type: 'array', default: argsDefatuls.external, description: 'Exclude these libraries from bundling, for example libraries that you already load in a layer' }, - 'm': { alias: 'metafile', type: 'boolean', default: argsDefatuls.metafile, description: 'Generate a meta.json file that can be used to analyze the bundle' }, + e: { + alias: 'entry', + type: 'string', + default: argsDefatuls.entry, + description: + 'The name of the entry file containing the lambda handler(s)', + }, + '0': { + alias: 'output', + type: 'string', + default: argsDefatuls.output, + description: 'The output path for the archive', + }, + x: { + alias: 'external', + type: 'array', + default: argsDefatuls.external, + description: + 'Exclude these libraries from bundling, for example libraries that you already load in a layer', + }, + m: { + alias: 'metafile', + type: 'boolean', + default: argsDefatuls.metafile, + description: + 'Generate a meta.json file that can be used to analyze the bundle', + }, }) .example([ ['$0', 'bundle index.js into archive.zip'], ['$0 archive -e src/index.ts', 'bundle src/index.ts into archive.zip'], - ['$0 archive -x lodash dayjs', 'bundle and archive index.js but exclude lodash and dayjs from the bundle'], + [ + '$0 archive -e src/index.ts -o src/package.zip', + 'bundle src/index.ts into package.zip', + ], + [ + '$0 archive -x lodash dayjs', + 'bundle and archive index.js but exclude lodash and dayjs from the bundle', + ], ['$0 archive -m', 'generate a meta.json file to analyze the bundle'], - ['$0 upload --help', 'learn more about the \'upload\' command'], + ['$0 upload --help', "learn more about the 'upload' command"], ]) .wrap(yargs.terminalWidth()) .parse(); diff --git a/src/lambda-build-cli.ts b/src/lambda-build-cli.ts index 9164aaa..7cda5d9 100644 --- a/src/lambda-build-cli.ts +++ b/src/lambda-build-cli.ts @@ -2,13 +2,13 @@ import { promises as fsPromises } from 'fs'; import chalk from 'chalk'; import { LambdaBuildCore } from './lambda-build-core'; -export const ZIP_FILE_NAME = 'archive.zip'; export const META_FILE_NAME = 'meta.json'; type LogWriter = { log: typeof console.log }; interface LambdaBuildArgs { entry: string; + output: string; external: string[]; metafile: boolean; region?: string; @@ -16,13 +16,11 @@ interface LambdaBuildArgs { } export class LambdaBuildCli { - constructor( private fsys: typeof fsPromises, private output: LogWriter, - private lambdaBuildCore: LambdaBuildCore, - ) { - } + private lambdaBuildCore: LambdaBuildCore + ) {} public commandArchive = async (options: LambdaBuildArgs): Promise => { try { @@ -33,10 +31,17 @@ export class LambdaBuildCli { this.output.log(chalk.bold(' ️⚡️ Bundling %s'), options.entry); if (options.external.length) { - this.output.log(chalk.dim(' → Excluding %s'), options.external.join(',')); + this.output.log( + chalk.dim(' → Excluding %s'), + options.external.join(',') + ); } - const res = await this.lambdaBuildCore.bundleAndArchive(options.entry, options.external, options.metafile); + const res = await this.lambdaBuildCore.bundleAndArchive( + options.entry, + options.external, + options.metafile + ); if (res.meta && options.metafile) { await this.fsys.writeFile(META_FILE_NAME, res.meta); @@ -44,24 +49,39 @@ export class LambdaBuildCli { } if (res.archive) { - await this.fsys.writeFile(ZIP_FILE_NAME, res.archive); - this.output.log(chalk.green.bold(' ✔ Created %s') + chalk.dim.green(' %s'), ZIP_FILE_NAME, res.archiveSize); + await this.fsys.writeFile(options.output, res.archive); + this.output.log( + chalk.green.bold(' ✔ Created %s') + chalk.dim.green(' %s'), + options.output, + res.archiveSize + ); } this.output.log(); } catch (err) { this.outputErrors(err); } - } + }; - private publishAndLog = async (region: string, functionName: string, archive: Buffer): Promise => { + private publishAndLog = async ( + region: string, + functionName: string, + archive: Buffer + ): Promise => { this.output.log(chalk.dim(' → Uploading %s'), functionName); - const arn = await this.lambdaBuildCore.publishLambda(region, functionName, archive); + const arn = await this.lambdaBuildCore.publishLambda( + region, + functionName, + archive + ); if (arn) { - this.output.log(chalk.white(' ✔ Successfully uploaded %s'), functionName); + this.output.log( + chalk.white(' ✔ Successfully uploaded %s'), + functionName + ); } return arn; - } + }; public commandUpload = async (options: LambdaBuildArgs) => { try { @@ -69,14 +89,27 @@ export class LambdaBuildCli { // console.log('archive'); // console.log({ options }); - this.output.log(chalk.bold(' ⚡️ Bundling & Uploading %s'), options.entry); + this.output.log( + chalk.bold(' ⚡️ Bundling & Uploading %s'), + options.entry + ); if (options.external.length) { - this.output.log(chalk.dim(' → Excluding %s'), options.external.join(',')); + this.output.log( + chalk.dim(' → Excluding %s'), + options.external.join(',') + ); } - const res = await this.lambdaBuildCore.bundleAndArchive(options.entry, options.external, options.metafile); - this.output.log(chalk.green.dim(' → Bundle archived %s'), res.archiveSize); + const res = await this.lambdaBuildCore.bundleAndArchive( + options.entry, + options.external, + options.metafile + ); + this.output.log( + chalk.green.dim(' → Bundle archived %s'), + res.archiveSize + ); if (res.meta) { await this.fsys.writeFile(META_FILE_NAME, res.meta); @@ -90,15 +123,18 @@ export class LambdaBuildCli { all.push(this.publishAndLog(options.region, lambda, res.archive)); } const arns = await Promise.all(all); - const uploadedArns = arns.filter(arn => Boolean(arn)); - this.output.log(chalk.green.bold(' ✔ Successfully uploaded %s function(s)'), uploadedArns.length); + const uploadedArns = arns.filter((arn) => Boolean(arn)); + this.output.log( + chalk.green.bold(' ✔ Successfully uploaded %s function(s)'), + uploadedArns.length + ); } this.output.log(); } catch (err: unknown) { this.outputErrors(err); } - } + }; public outputErrors = (err: unknown) => { this.output.log(); @@ -109,6 +145,5 @@ export class LambdaBuildCli { this.output.log(err); } this.output.log(); - } - + }; } diff --git a/src/lambda-build-core.ts b/src/lambda-build-core.ts index 411cece..fc37814 100644 --- a/src/lambda-build-core.ts +++ b/src/lambda-build-core.ts @@ -2,7 +2,10 @@ import * as fs from 'fs'; import { build } from 'esbuild'; import JSZip from 'jszip'; import filesize from 'filesize'; -import { LambdaClient, UpdateFunctionCodeCommand } from '@aws-sdk/client-lambda'; +import { + LambdaClient, + UpdateFunctionCodeCommand, +} from '@aws-sdk/client-lambda'; const INDEX_JS = './index.js'; const INDEX_TS = './index.ts'; @@ -10,6 +13,7 @@ const BUNDLE_NAME_IN_ARCHIVE = 'index.js'; export const argsDefatuls = { entry: './index.js|ts', + output: './archive.zip', external: [], metafile: false, region: 'us-east-1', @@ -29,14 +33,13 @@ export interface ArhcivingResult extends BundlingResult { type Builder = { build: typeof build }; export class LambdaBuildCore { + constructor(private builder: Builder, private fsys: typeof fs) {} - constructor( - private builder: Builder, - private fsys: typeof fs, - ) { - } - - public bundleAndArchive = async (entry: string, external: string[], isMetafile: boolean): Promise => { + public bundleAndArchive = async ( + entry: string, + external: string[], + isMetafile: boolean + ): Promise => { const res = await this.createBundle(entry, external, isMetafile); const archive = await this.createArchive(res.bundle); const archiveSize = this.getBundleSize(archive); @@ -46,15 +49,22 @@ export class LambdaBuildCore { bundle: res.bundle, meta: res.meta, }; - } + }; - public publishLambda = async (region: string, functionName: string, archive: Buffer): Promise => { + public publishLambda = async ( + region: string, + functionName: string, + archive: Buffer + ): Promise => { const client = new LambdaClient({ region }); - const command = new UpdateFunctionCodeCommand({ FunctionName: functionName, ZipFile: archive }); + const command = new UpdateFunctionCodeCommand({ + FunctionName: functionName, + ZipFile: archive, + }); const res = await client.send(command); // console.log({ res }); return res.FunctionArn; - } + }; private determineEntry = (fileName: string): string => { const notFoundErrorMsg = `Could not find ${argsDefatuls.entry} - please specify an entry point using the -e flag`; @@ -74,7 +84,11 @@ export class LambdaBuildCore { } }; - private createBundle = async (fileName: string, external: string[], metafile: boolean): Promise => { + private createBundle = async ( + fileName: string, + external: string[], + metafile: boolean + ): Promise => { const entry = this.determineEntry(fileName); const res = await this.builder.build({ logLevel: 'silent', @@ -103,13 +117,13 @@ export class LambdaBuildCore { private getBundleSize = (buf: Buffer): string => { return filesize(buf.toString().length); - } + }; private createArchive = async (bundle: string): Promise => { const zip = new JSZip(); zip.file(BUNDLE_NAME_IN_ARCHIVE, bundle); const buf = await zip.generateAsync({ type: 'nodebuffer' }); + return buf; }; - } diff --git a/src/lambda-build-lib.ts b/src/lambda-build-lib.ts index a3a8ae5..b93b938 100644 --- a/src/lambda-build-lib.ts +++ b/src/lambda-build-lib.ts @@ -2,8 +2,8 @@ import { LambdaBuildCore, argsDefatuls } from './lambda-build-core'; export interface BuildArchiveArgs { entry?: string; - external?: string[], - metafile?: boolean, + external?: string[]; + metafile?: boolean; } export interface BuildRes { @@ -28,21 +28,23 @@ export interface BuildAndUploadRes { } export class LambdaBuildLib { - - constructor( - private lambdaBuildCore: LambdaBuildCore, - ) { - } + constructor(private lambdaBuildCore: LambdaBuildCore) {} public build = async (args?: BuildArchiveArgs): Promise => { const entry = args?.entry ?? argsDefatuls.entry; const external = args?.external ?? argsDefatuls.external; const metafile = args?.metafile ?? argsDefatuls.metafile; - const res = await this.lambdaBuildCore.bundleAndArchive(entry, external, metafile); + const res = await this.lambdaBuildCore.bundleAndArchive( + entry, + external, + metafile + ); return res; }; - public buildAndUpload = async (args: BuildAndUploadArgs): Promise => { + public buildAndUpload = async ( + args: BuildAndUploadArgs + ): Promise => { const entry = args.entry ?? argsDefatuls.entry; const external = args.external ?? argsDefatuls.external; const metafile = args.metafile ?? argsDefatuls.metafile; @@ -56,10 +58,12 @@ export class LambdaBuildLib { if (buildRes.archive && region) { const all = []; for (const lambda of lambdas) { - all.push(this.lambdaBuildCore.publishLambda(region, lambda, buildRes.archive)); + all.push( + this.lambdaBuildCore.publishLambda(region, lambda, buildRes.archive) + ); } const arns = await Promise.all(all); - updatedArns = arns.filter(arn => Boolean(arn)) as string[]; + updatedArns = arns.filter((arn) => Boolean(arn)) as string[]; } return { archive: buildRes.archive, @@ -68,5 +72,4 @@ export class LambdaBuildLib { updatedArns, }; }; - }