tslib should follow standards for ESM/CJS detection
tslib's package.json has a few issues RE detecting whether it's CJS or ESM. Due to lack of standards support, it has to be special-cased in a number of places, eg jest-preset-angular. Workaround like this are needed in any projects using jest, typescript, and tslib.
The following changes to the package.json would fix things - happy to provide a PR if it will be considered:
- There is no
"type": "module"field, which tells node code that .js files support ESM - The ESM tslib (currently
"tslib.es6.js") should have an .mjs file extension - The CJS tslib (currently `"tslib.js") should have a .cjs file extension.
To be clear, just the type field would help, but I think it would be preferable to complete all 3 at once.
Node has documented their logic for determining whether a module is CJS or ESM. This appears to be what other other projects, eg jest, are using to determine what type of syntax to expect when loading js files.
The node logic doesn't seem to address cases where both CJS and ESM files are published in a package, which seems like a major oversight. However, it should be possible to satisfy both while making no effective changes to tslib's package.json for clients depending on the current fields.
@ahnpnl - I thought you might want to chime in on this issue, since you've done some work to special-case handling of tslib.
What I experienced was: If using esbuild or swc to compile tslib.es6.js, Jest cannot process the compiled output. However, if I use TypeScript API to compile tslib.es6.js, Jest is happy with the output.
Possibly related to https://github.com/microsoft/tslib/issues/161#issuecomment-1032886001
So are there any workarounds right now? I'm an indirect consumer of tslib, does anybody have a working npm package I can enforce using instead?
I tried changing tslib.js so it exports a plain object with the functions in there including __decorate, and Jest still doesn't work, same error 😓
as a workaround I made https://github.com/StratoKit/tslib
Hi @wmertens - there are 2 fixes I've used:
- Explicitly link tslib.es6.js in your jest.config.js
moduleNameMappersection, eg:
...
moduleNameMapper: {
tslib: 'tslib/tslib.es6.js',
}
...
This is hard-coded in jest-preset-angular b/c the ability to resolve tslib ES6 in jest is broken (thus this issue).
- Create a copy of
tslib.es6.jsand rename it totslib.mjs- jest (and node) always treat .mjs files as ESM. Then put it in a place, or update webpack config, so that it's linked instead ofnode_modules/tslib.
@johncrim when I try option 1. it complains that node_modules isn't transpiled, and I'd prefer to keep it that way, unless there's a way to tell jest to only transpile tslib?
Option 2 is a variation of the package changes I made, so basically the same maintenance burden.
What I experienced was: If using
esbuildorswcto compiletslib.es6.js, Jest cannot process the compiled output. However, if I use TypeScript API to compiletslib.es6.js, Jest is happy with the output.Possibly related to #161 (comment)
So are there any workarounds right now? I'm an indirect consumer of tslib, does anybody have a working npm package I can enforce using instead?
In case it matters to anyone, I've also posted most of what's written below to https://github.com/evanw/esbuild/issues/2296, as that issue is also quite related in my view.
My main project uses just tsc and esbuild, with no further tooling. (So, no Jest.) We recently found a way forward in ES5:
- https://github.com/keymanapp/keyman/pull/8904
It's probably a bit hacky, but this relies on two details:
-
esbuildversion 0.15.16 and above now supportaliasoptions in their build configuration parameters. With this, we can aliastslibimports to a custom package that wraps the ES5-based tslib.js file found in thetslibdistribution.esbuildcan't handle the object-destructuringmodules/index.jssyntax in ES5 mode, as it requires ES6 - and that's whatesbuildpicks up when doing module-resolution. - To ensure that we don't reproduce the same problem, the wrapping package's tsconfig.json uses "Node" module-resolution mode, not "Node16" or higher - we wish to ignore import maps. This instead connects us to
tslib's package.jsonmainentry, which is theCommonJSimport leading to the pure-ES5tslib.js, whichesbuildcan handle.
On second thought, it may be possible to add a resolution-phase plugin to do the redirect - the issue is about which tslib script the import resolves to, after all. But, what we have is working now, "if it's not broke, don't fix it", and it provides a decent site to add the plugins added by the next entry.
For further enhancements, we've also developed this one:
- https://github.com/keymanapp/keyman/pull/8964
As is, the wrapping package approach unfortunately does not work well with esbuild treeshaking... but we can utilize esbuild's plugin architecture and the patterns in the backing tslib script to do it ourselves without too much issue. This requires two plugins and an extra build pass for the first:
- First phase: Sets
write: false, scans for alltslibhelper function names used by the codebase, then determines which ones can be safely tree-shaken. An 'ignore' pattern may also be set - we use this to avoid picking up names from a file defining embedded worker code. - Second phase: now that we know which
tslibhelpers can be eliminated, the second plugin looks fortslib.jsand erases the relevant method definitions and theexporterstatements.