Ok, so I’ve finally gotten far enough converting our internal libraries to figure out that attempting to make a small utility library ESM is fine, but it’s not feasible (as of 6/8/2022) for anything complex because something is inevitably going to be CJS and it will either not be loaded by node or via "moduleResolution": "nodenext"
.
I have to go back to dual builds - One CJS and one ESM-like (but not totally), basically what we have been doing for years to allow for execution of node scripts that use CJS libraries as well as allow webpack to tree-shake ESM-like front end libraries.
It builds! (This is the same as saying "it works on my machine").
BUT when used in any larger application, it appears that you aren't going to feasibly be able to use "moduleResolution": "nodenext"
for some time. If you use any dependency that does not meet the ESM standard, you will have to switch to "moduleResolution": "node"
and fake/hack the main
in the package.json
(at least for building purposes. I have a test case on this branch and filed an issue with some helpful context here: microsoft/TypeScript#49388
- A publishable set of packages (not just an internal build)
- Typescript project references
- Yarn 2+ workspaces
- ESM only modules that meet the specification (no fallbacks to CJS)
- Browser and Node targeted modules reusing a
shared
library (all published) - Follow the latest guidance from https://www.typescriptlang.org/docs/handbook/esm-node.html
- Build simply with
tsc -b
- Exports map not recognized without
main
fallback - this simply means you need to use"moduleResolution": "nodenext"
.
client
- browser targeted tsconfigcli
- node targeted tsconfigshared
- code used by both environments
- Be sure to check the
.vscode
settings to setup the build task, and setup the preference to use the workspace's typescript sdk. - Add the following to aid with the ESM style imports:
{ "javascript.preferences.importModuleSpecifierEnding": "js", "typescript.preferences.importModuleSpecifierEnding": "js" }
There are other resources out there, so I'll keep this brief to my key notes:
- sindresorhus/esm-package gist
- Jaid/migratingRules gist on eslint
- Jest repo a proper yarn workspaces example.
- You cannot use
.ts
extension in your imports, but strangely enough, if you add.js
extension to a project that builds properly, vscode will allow you tocmd+click
to go into the desired.ts
file. Very strange, but ultimately the output indist
is then correct.