Tags: jeremydaly/lambda-api
Tags
fix: keep the CommonJS interop shim out of the ESM build (#346) (#347) * fix: keep the CommonJS interop shim out of the ESM build (#346) The dual CJS/ESM build (#326, shipped in 1.5.0) compiles a single `src/` tree twice. Seven source files ended with an interop shim: if (typeof module !== 'undefined') { module.exports = createAPI; module.exports.default = createAPI; } which restores the historical CommonJS shape — but it was emitted into `dist/esm` as well. Bundlers that inline the ESM artifact into a generated CommonJS wrapper (esbuild `--format=cjs`, AWS CDK `NodejsFunction`, SST, Serverless Framework) leave exactly one `module` in scope: the consumer's. The shim then replaced the consumer's `module.exports`, deleting their `handler`. On Lambda this deployed cleanly and failed on every invocation with `Runtime.HandlerNotFound: index.handler is undefined or not exported`, with nothing in the error naming lambda-api. `src/` is now pure ESM. The interop footers are appended to `dist/cjs` only, by `scripts/cjs-interop.js`, which also loads each patched file back and fails the build if the CommonJS shape is wrong. Footers are per-file on purpose: the `.default` self-reference is correct on the package root but would make `mimemap['default']` resolve to the whole MIME map. `src/lib/s3-service.js` now exports its `service` object so the footer can reach it; that object must stay the module value so `response.js` and the unit suites (`sinon.stub`) share one set of mutable properties. Nothing about `dist/cjs` changes: `require('lambda-api')` is still callable, `.default` still self-references, and `require('lambda-api/lib/*')` still returns the value itself. Regression coverage: - `module-compat` fails if any `dist/esm` file references `module.exports`, `typeof module`, or `exports.` — it named all 7 files before the fix. - `module-compat` pins the `dist/cjs` interop shape the footers must preserve. - New e2e Layer 1 fixture bundles an ESM handler with esbuild `--format=cjs` against the packed tarball and asserts the consumer's `handler` export survives and returns 200. It reported `["default"] / handler undefined` before the fix. Closes #346 * refactor: derive the CJS footer per module instead of enumerating files The footer table listed all seven modules by name, so adding a new `src/lib/*.js` with a default export would silently get no footer and its CommonJS shape would drift from its siblings — a failure nothing would catch. The footer is now derived from the module's own exports: - only `default` -> collapse to that value - named exports -> leave SWC's output alone - both -> fail the build, ambiguous, needs an explicit entry That leaves two real exceptions rather than a seven-entry table: the package root (callable + `.default` self-reference) and lib/s3-service.js (no default export; must resolve to the mutable service object). Also drops the build-time contract check. It duplicated __tests__/module-compat.unit.js, which already runs on the publish path — `prepublishOnly` -> `npm test` -> `jest unit`, and `unit` matches module-compat.unit.js. dist/cjs is byte-for-byte identical to the previous implementation. * fix: classify export shapes totally in the CJS interop step The named-export check listed keywords (`const|let|var|function|class|{`), which only covered the forms that happen to exist in src/ today. Three forms slipped through, and two of them were silent: export async function a(){}; export default b -> footer applied, `a` DROPPED export * from './x.js'; export default b -> footer applied, re-exports DROPPED export { x as default } -> no footer, require() gave {default} Stated as a negative lookahead instead — any `export` line that is not `export default` is a named export — so every form classifies correctly and mixed modules fail the build rather than losing exports. `as default` now counts as a default export, so `export { x as default }` is caught too. Verified across all five shapes: the three above now fail the build loudly, default-only still collapses, named-only is still left alone. dist/cjs is byte-for-byte unchanged. Also marks the s3-service `service` export @internal — it exists so the build step can reach the object, not as public API. * test: pin the CJS footer classification for every export form The classification is the part of the build step that fails silently — a module mixing a default with named exports would collapse to the default and drop the rest — but it had no automated coverage; it was only ever checked by hand. scripts/cjs-interop.js now exposes `footerFor(relative, source)` and runs its side effects under `require.main === module`, so the decision is testable without touching the filesystem. __tests__/cjs-interop.unit.js pins 17 cases across all three outcomes plus the exceptions, and runs under `jest unit`, so it gates PRs on the Node matrix. Mutation-checked: restoring the enumerated named-export regex fails `default + async function` and `default + star`; dropping the `as default` alternation fails `export { x as default }`. dist/cjs is byte-for-byte unchanged. * refactor: key the CJS footer exceptions by plain path, drop generated comments The s3-service key was a computed `path.join('lib','s3-service.js')` so it would match `path.relative` on Windows. The relative path is now normalized to forward slashes at the call site instead, so the table reads as plain strings. The explanatory comments moved out of the emitted footers and into the table itself — dist/ is generated output, and the MARKER already points readers at this script. The root footer drops from six lines to three.
chore(deps): bump brace-expansion (#339) Bumps and [brace-expansion](https://github.com/juliangruber/brace-expansion). These dependencies needed to be updated together. Updates `brace-expansion` from 2.1.1 to 2.1.4 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v2.1.1...v2.1.4) Updates `brace-expansion` from 1.1.11 to 1.1.18 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v2.1.1...v2.1.4) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 2.1.4 dependency-type: indirect - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
PreviousNext