feat!: switch to ESM and native TypeScript type-stripping - #166
Conversation
- rewrite src/index.ts to use `export default` / `import` instead of TS-specific `export =` / `import = require()` - update tsconfig.json for `module: nodenext`, `verbatimModuleSyntax`, `erasableSyntaxOnly`, `allowImportingTsExtensions`, `rewriteRelativeImportExtensions` - add explicit `.ts` extensions to relative imports - replace `__dirname` with `import.meta.dirname` in tests - set "type": "module" in package.json, add "exports" map - bump engines to require Node >=20.19.0 <21 || >=22.12.0 (needed for require(esm) interop with CJS consumers) BREAKING CHANGE: this package is now ESM-only. CJS consumers can still `require()` it thanks to Node's require(esm) support (stable since Node 20.19.0 / 22.12.0); older Node versions are no longer supported.
3ce9c0b to
6e4ef80
Compare
|
cc @fastify/typescript |
b53aff1 to
af1000c
Compare
mcollina
left a comment
There was a problem hiding this comment.
likely there is something to fix in the tsconfig.build.json
fefd2ce to
e75bb6a
Compare
dd37d8b to
217ee9a
Compare
217ee9a to
6c2b2e3
Compare
Signed-off-by: Antonio Tripodi <Tony133@users.noreply.github.com>
| "types": "./lib/index.d.ts", | ||
| "default": "./lib/index.js" | ||
| } | ||
| }, |
There was a problem hiding this comment.
Just to confirm, you mean main/types are redundant now that exports is present, right? 😅
- "main": "./lib/index.js",
"type": "module",
- "types": "./lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"default": "./lib/index.js"
}
},
There was a problem hiding this comment.
why using exports when you only have single entrypoint?
There was a problem hiding this comment.
why using
exportswhen you only have single entrypoint?
The only advantage, now that I think about it is: "encapsulation".
The exports field explicitly defines the package's public interface. Without it, nothing prevents a user from running require('@fastify/flash/lib/flash.js') and depending on an internal file. With exports, that import throws ERR_PACKAGE_PATH_NOT_EXPORTED, so we're free to restructure the internals of lib/ later without it being a breaking change.
That said, we could also just drop it and keep this instead:
"main": "./lib/index.js",
"type": "module",
"types": "./lib/index.d.ts",
- "exports": {
- ".": {
- "types": "./lib/index.d.ts",
- "default": "./lib/index.js"
- }
- },Happy to go either way, let me know which you'd prefer 😉
| } | ||
| }, | ||
| "scripts": { | ||
| "build": "npm run clean-build && npm run lint && tsc -p ./tsconfig.build.json", |
There was a problem hiding this comment.
Is this building correctly for release? is tsconfig.build.json correct?
There was a problem hiding this comment.
Yes, verified locally on Node 24.18.0. Commands to reproduce:
npm run build
ls -la lib/
node -e "require('./lib/index.js'); console.log('require(esm) works')"
Output:
- lib/index.js, lib/index.d.ts, lib/flash.js, lib/flash.d.ts produced
- lib/index.js matches exports["."].default
- lib/index.d.ts matches exports["."].types
- require('./lib/index.js') resolves and logs "require(esm) works" confirms the exports map works for CJS consumers too
tsconfig.build.json is correct: its include: ["src"] overrides(not merges with) the base config's broader include, so test/ is correctly excluded and only the paths declared in exports get produced.
Proposal:
Follow-up to #159, as discussed with @mcollina:
Changed:
src/index.tsrewritten to ESM (export default), the oldexport =/import = require()syntax isn't "erasable" andbreaks Node's native type-stripping.
tsconfig.jsonupdated per Node's official recommended settings for type-stripping (module: nodenext,verbatimModuleSyntax,erasableSyntaxOnly,allowImportingTsExtensions,rewriteRelativeImportExtensions)..tsextensions in source;tscrewrites them to.jsin the compiledlib/output.test/index.test.ts:__dirname→import.meta.dirname, type-only imports (TestContext,Session,SessionData) marked withtypeperverbatimModuleSyntax.package.json:"type": "module", added"exports"map, bumpedenginesto>=20.19.0 <21 || >=22.12.0.README.mdBreaking change:
This is now an ESM-only package.
require()from CommonJS consumers still works, since it relies on Node'srequire(esm)support, stable as of Node 20.19.0 / 22.12.0. Consumers on older Node versions will need to upgrade.Note: