Skip to content

Commit db838ab

Browse files
committed
fix: route ESM Lambdas through handler.mjs via CJS shim
AWS Lambda's CJS resolver picks `dist/handler.js` before `dist/handler.mjs` when the handler string is `node_modules/datadog-lambda-js/dist/handler.handler`, causing ESM user functions to fail with ERR_REQUIRE_ESM (or the surface "Cannot find module" variant when _tryRequireSync swallows it). Today `dist/handler.js` ships as a verbatim copy of `dist/handler.cjs` (via `cp ./dist/handler.cjs ./dist/handler.js` in the publish scripts), so it can never load ESM user code. This replaces the copy with a real `src/handler.js` shim that: - Detects ESM user code via the task root `package.json` `type` field or a `.mjs` `DD_LAMBDA_HANDLER` / `_HANDLER`. - For CJS user code: synchronously delegates to `handler.cjs`, preserving the prior fast path (no cold-start regression). - For ESM user code: exposes an async handler that lazily `import()`s `handler.mjs` on the first invocation. `handler.mjs`'s async `load()` transparently handles both CJS and ESM user modules. `scripts/update_dist_version.sh` already copies `src/handler.*` into `dist/`, so the shim ships automatically; the explicit `cp ./dist/handler.cjs ./dist/handler.js` lines in `publish_npm.sh` and `publish_prod.sh` are no longer needed and are removed. Refs: #782 Refs: SLES-2888, SLES-2895, SVLS-9238
1 parent 99a1f4f commit db838ab

3 files changed

Lines changed: 63 additions & 2 deletions

File tree

.gitlab/scripts/publish_npm.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,4 @@ if [ -d "./dist" ]; then
2929
rm -rf ./dist
3030
fi
3131
yarn build
32-
cp ./dist/handler.cjs ./dist/handler.js
3332
npm publish

scripts/publish_prod.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ else
9595
rm -rf ./dist
9696
fi
9797
yarn build
98-
cp ./dist/handler.cjs ./dist/handler.js
9998
yarn publish --new-version "$NEW_VERSION"
10099
fi
101100

src/handler.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// CJS entry shim used by AWS Lambda's bootstrap when the handler string
2+
// resolves to `node_modules/datadog-lambda-js/dist/handler.handler`.
3+
//
4+
// Lambda's resolver searches for `.js` before `.mjs`, so this file is what
5+
// gets loaded for both CJS and ESM user functions. We branch at runtime:
6+
//
7+
// - CJS user code: synchronously delegate to handler.cjs (preserves the
8+
// prior fast-path behavior — no extra dynamic import on cold start).
9+
//
10+
// - ESM user code: expose an async handler that lazily `import()`s
11+
// handler.mjs on the first invocation. handler.mjs's async `load()`
12+
// transparently handles both CJS and ESM user modules, so this also
13+
// fixes the ERR_REQUIRE_ESM failure mode reported in
14+
// https://github.com/DataDog/datadog-lambda-js/issues/782.
15+
16+
"use strict";
17+
18+
const fs = require("fs");
19+
const path = require("path");
20+
21+
function userIsEsm() {
22+
const handlerEnv = process.env.DD_LAMBDA_HANDLER || process.env._HANDLER || "";
23+
if (/\.mjs(\..*)?$/.test(handlerEnv)) {
24+
return true;
25+
}
26+
27+
const taskRoot = process.env.LAMBDA_TASK_ROOT || process.cwd();
28+
try {
29+
const pkgPath = path.join(taskRoot, "package.json");
30+
const raw = fs.readFileSync(pkgPath, "utf8");
31+
const pkg = JSON.parse(raw);
32+
if (pkg && pkg.type === "module") {
33+
return true;
34+
}
35+
} catch (_) {
36+
// package.json missing or unreadable — assume CJS, matches prior behavior.
37+
}
38+
39+
return false;
40+
}
41+
42+
if (userIsEsm()) {
43+
let cachedHandler;
44+
let cachedError;
45+
46+
exports.handler = async function ddEsmHandler(event, context) {
47+
if (cachedError) {
48+
throw cachedError;
49+
}
50+
if (!cachedHandler) {
51+
try {
52+
const mod = await import("./handler.mjs");
53+
cachedHandler = mod.handler;
54+
} catch (error) {
55+
cachedError = error;
56+
throw error;
57+
}
58+
}
59+
return cachedHandler(event, context);
60+
};
61+
} else {
62+
module.exports = require("./handler.cjs");
63+
}

0 commit comments

Comments
 (0)