diff --git a/README.md b/README.md index e538bf4..14be48b 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Executes a lambda given the `options` object, which is a dictionary where the ke | `lambdaHandler`|optional handler name, default to `handler`| | `region`|optional, AWS region, default to `us-east-1`|| `callbackWaitsForEmptyEventLoop`|optional, default to `false`. Setting it to True will wait for an empty loop before returning.| | `timeoutMs`|optional, timeout, default to 3000 ms| +| `esm`|boolean, marks that the script is an ECMAScript module (use import), default false| | `environment`|optional, extra environment variables for the lambda| | `envfile`|optional, load an environment file before booting| | `envdestroy`|optional, destroy added environment on closing, default to false| @@ -137,6 +138,7 @@ lambdaLocal.execute({ * `-e, --event-path ` (required --watch is not in use) Specify event data file name. * `-h, --handler ` (optional) Lambda function handler name. Default is "handler". * `-t, --timeout ` (optional) Seconds until lambda function timeout. Default is 3 seconds. +* `--esm` (optional) Load lambda function as ECMAScript module. * `-r, --region ` (optional) Sets the AWS region, defaults to us-east-1. * `-P, --profile-path ` (optional) Read the specified AWS credentials file. * `-p, --profile ` (optional) Use with **-P**: Read the AWS profile of the file. diff --git a/src/cli.ts b/src/cli.ts index 238f616..e3c6b87 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -21,6 +21,8 @@ import utils = require('./lib/utils'); .option('-e, --event-path ', '(required if --watch is not in use) Event data file name.') .option('-h, --handler ', '(optional) Lambda function handler name. Default is \'handler\'.') + .option('--esm', + '(optional) Load lambda function as ECMAScript module.') .option('-t, --timeout ', '(optional) Seconds until lambda function timeout. Default is 3 seconds.') .option('-r, --region ', @@ -50,6 +52,7 @@ import utils = require('./lib/utils'); var eventPath = program.eventPath, lambdaPath = program.lambdaPath, lambdaHandler = program.handler, + esm = program.esm, profilePath = program.profilePath, profileName = program.profile, region = program.region, @@ -129,6 +132,17 @@ import utils = require('./lib/utils'); _close_inspector = function(){inspector.close();} } } + + if (esm) { + if (utils.get_node_major_version() < 12) { + console.log("Loading ESCMAScript modules not available on NodeJS < 12.0.0."); + process.exit(1); + } + if (program.watch) { + console.log("Watch mode not supported for ECMAScript lambda modules."); + process.exit(1); + } + } var event = function(){ if(program.watch) return null; return require(utils.getAbsolutePath(eventPath)); @@ -140,6 +154,7 @@ import utils = require('./lib/utils'); event: event, lambdaPath: lambdaPath, lambdaHandler: lambdaHandler, + esm: esm, profilePath: profilePath, profileName: profileName, region: region, diff --git a/src/lambdalocal.ts b/src/lambdalocal.ts index 175c59e..fd53722 100644 --- a/src/lambdalocal.ts +++ b/src/lambdalocal.ts @@ -122,6 +122,7 @@ function _executeSync(opts) { lambdaFunc = opts.lambdaFunc, lambdaPath = opts.lambdaPath, lambdaHandler = opts.lambdaHandler || 'handler', + esm = opts.esm, profilePath = opts.profilePath, profileName = opts.profileName || process.env['AWS_PROFILE'] || process.env['AWS_DEFAULT_PROFILE'], region = opts.region, @@ -246,30 +247,40 @@ function _executeSync(opts) { var ctx = context.generate_context(); - try { - // load lambda function - if (!(lambdaFunc)){ - // delete this function from the require.cache to ensure every dependency is refreshed - delete require.cache[require.resolve(lambdaPath)]; - lambdaFunc = require(lambdaPath); - } - - //load event - if (event instanceof Function){ - event = event(); - } + const executeLambdaFunc = lambdaFunc => { + try { + //load event + if (event instanceof Function){ + event = event(); + } - //start timeout - context._init_timeout(); + //start timeout + context._init_timeout(); - // execute lambda function - var result = lambdaFunc[lambdaHandler](event, ctx, ctx.done); - if (result) { - if (result.then) { - result.then(ctx.succeed, ctx.fail); - } else { - ctx.succeed(null); + // execute lambda function + var result = lambdaFunc[lambdaHandler](event, ctx, ctx.done); + if (result) { + if (result.then) { + result.then(ctx.succeed, ctx.fail); + } else { + ctx.succeed(null); + } } + } catch(err){ + ctx.fail(err); + } + } + + try { + if (lambdaFunc) { + executeLambdaFunc(lambdaFunc); + } else if (esm) { + // use eval to avoid typescript transpilation of dynamic import () + eval("import(lambdaPath)").then(executeLambdaFunc, err => ctx.fail(err)); + } else { + // delete this function from the require.cache to ensure every dependency is refreshed + delete require.cache[require.resolve(lambdaPath)]; + executeLambdaFunc(require(lambdaPath)); } } catch(err){ ctx.fail(err); diff --git a/test/functs/test-func-esm.mjs b/test/functs/test-func-esm.mjs new file mode 100644 index 0000000..74532a3 --- /dev/null +++ b/test/functs/test-func-esm.mjs @@ -0,0 +1,3 @@ +export async function handler(event, context) { + return {"result": event.key, "context": context} +} diff --git a/test/test.js b/test/test.js index 1b59f50..4a92204 100644 --- a/test/test.js +++ b/test/test.js @@ -482,6 +482,25 @@ describe("- Testing lambdalocal.js", function () { }); }); } + if (get_node_major_version() >= 12) { + describe('* Loading ECMAScript module', function () { + it('should load ECMAScript lambda module successfully', function () { + var lambdalocal = require(lambdalocal_path); + lambdalocal.setLogger(winston); + return lambdalocal.execute({ + event: require(path.join(__dirname, "./events/test-event.js")), + lambdaPath: path.join(__dirname, "./functs/test-func-esm.mjs"), + lambdaHandler: functionName, + esm: true, + callbackWaitsForEmptyEventLoop: false, + timeoutMs: timeoutMs, + verboseLevel: 1 + }).then(function (data) { + assert.equal(data.result, "testvar") + }) + }) + }) + } }); describe("- Testing cli.js", function () { var spawnSync = require('child_process').spawnSync; @@ -570,6 +589,14 @@ describe("- Testing cli.js", function () { assert.equal(r.status, 1); console.log(r.output); }); + + it("should fail: esm with unsupported watch mode", function () { + var command = get_shell("node ../build/cli.js -l ./functs/test-func-esm.mjs --esm --watch"); + var r = spawnSync(command[0], command[1]); + process_outputs(r); + assert.equal(r.status, 1); + console.log(r.output); + }) }); describe("* Environment test run", function () {