The purpose of this repo is to demonstrate an issue that exists when using Middy alongside the latest version of TypeScript (5.2.2) which was published on August 24, 2023.
I have a package of Middy middleware that is used across a number of lambdas. The middleware is written in TypeScript, transpiled to JS (via the TypeScript compiler), and published to a private NPM registry. The resulting JS code uses CommonJS; we haven't made the leap to ESM yet.
Middy is wonderful and publishes out a CJS and ESM copy of its library which (normally) allows us to avoid the issues and complexities that arise when intermingling CJS and ESM modules 🙂
Unfortunately, the latest version of TypeScript is preventing me from being able to build/publish my middleware package. The reason for this is due to how TypeScript incorrectly interprets Middy as being ESM without recognizing that there is a CommonJS option available for it to pull in.
There are two TypeScript config settings that are interdependent: module and moduleResolution. As noted in the linked docs, node16
(and nodenext
) should be used for "modern versions" of Node.js whereas node
is targeted for versions of Node.js older than v10. Looking at the recommended tsconfig for Node v18 confirms these values should be set to node16
.
In previous versions of TypeScript, it turns out this was working somewhat accidentally for us. That is because our tsconfig had a moduleResolution
value set to node
instead of node16
. This mismatched configuration with module
was allowed in older versions of TypeScript. Some folks actually flagged this as a viable fix when people ran into issues with moduleResolution
set to node16
(see GitHub issue). Ultimately, having mismatched module
and moduleResolution
values in this way was flagged as a TypeScript bug and fixed in version 5.2.2 (see release notes).
As such, we've updated our moduleResoution
value to be node16
, as recommended and now enforced by TypeScript.
When attempting to build our middleware package with TypeScript 5.2.2, we receive the following error:
src/index.ts:1:19 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("@middy/core")' call instead.
To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/workspaces/middy-ts-demo/package.json'.
- Install dependencies
$ npm install
- Attempt to run a build -- this executes the TypeScript compiler.
$ npm run build
- Note that the following error is thrown by the TypeScript compiler.
> [email protected] build > tsc src/index.ts:1:19 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("@middy/core")' call instead. To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/workspaces/middy-ts-demo/package.json'. 1 import middy from '@middy/core'; ~~~~~~~~~~~~~ Found 1 error in src/index.ts:1
It seems as though TypeScript is incorrectly interpreting our dependency on Middy as ESM, even though the CommonJS version is available in the package. I believe TypeScript is viewing the combination of the index.d.ts
file and the type
of module
in the package.json as indicators to the compiler that the module is ESM.
However, I have found that by utilizing the .cts file extension for the type definitions in the CommonJS version of Middy, TypeScript properly understands that the package is CommonJS.
These steps build upon the steps outlined in the Demo Problem Using This Repo section above. Complete those steps first if you haven't already.
- Manually create a copy of
node_modules/@middy/core/index.d.ts
namednode_modules/@middy/core/index.d.cts
. - Manualy update the
exports
value of thenode_modules/@middy/core/package.json
file to the following. Note that the only change is to the value ofrequire
>types
."exports": { ".": { "import": { "types": "./index.d.ts", "default": "./index.js" }, "require": { "types": "./index.d.cts", "default": "./index.cjs" } } },
- Attempt to run a build
$ npm run build
- Note that the build completes successfully.