Skip to content

Instantly share code, notes, and snippets.

@itsfarseen
Last active August 8, 2021 16:11
Show Gist options
  • Save itsfarseen/7279bbfe2317e937ddc03ee2448e5dcb to your computer and use it in GitHub Desktop.
Save itsfarseen/7279bbfe2317e937ddc03ee2448e5dcb to your computer and use it in GitHub Desktop.
TS+ESM. Fuck!

JS/TS Ecosystem Sucks at not breaking things.

Here's a list of things that I discovered.

  • Import extension

    When using Node with ESM, it requires .js extension on imports. Use node --experimental-specifier-resolution=node to revert back to old node behavior of not requiring an extension.

    Why:
    This is a problem because, by default, people writing typescript write imports without extension. Typescript doesn't transform the import extensions. So if you write import "./foo" or import "./foo.ts", it will come out as-is in the output JS file. import "./foo.ts" won't obviously work because the output files are not .ts files. import "./foo" used to work when not using ESM in Node.

    So when ESM was introduced, Typescript devs, instead of going the sane path of rewriting the imports, because they are obviously changing the extension of the output files, decided to allow import "./foo.js" in Typescript. The compiler will rewrite .js to .ts just during compiling. This works, I can compile and run stuff using ts-node.

    But.. Jest is broken now. Jest resolves modules in its own way. kulshekhar/ts-jest#1057 Although the issue is filed in ts-jest, the issue is actually in jest. And I couldn't find any issue addressing the same in the jest repo.

  • millparsec, ky are esm-only. These aree some packages that I needed in my app. They only provide an ESM build.

  • Typescript can't import ESM libs when compiling in CJS mode. TS when compiling in CJS mode, transpiles all import foo from "foo" to require(). ES Modules can't be require()d.

  • Ava doesn't support BDD.

  • Uvu, docs are sparse. No proper TS+ESM support.

  • Jest errors out when importing milliparsec, because Jest uses its own resolve function which doesn't support "node exports package entrypoints"

    Refs: jestjs/jest#9771 https://nodejs.org/api/packages.html#packages_package_entry_points

  • Using jest-node-exports-resolver doesn't seem to fix this.

    Errors: Cannot find module 'milliparsec' from 'src/app.ts' ReferenceError: You are trying to import a file after the Jest environment has been torn down.

    jest-node-exports-resolver: https://github.com/k-g-a/jest-node-exports-resolver

  • Using enhanced-resolve kinda fixes it. How to: jestjs/jest#9771 (comment) Caveats: I had to also add querystring to the list in if([..].includes()). So node builtins wont be properly resolved by enhanced-resolve. Every node builtings that any of my transitive dependencies use must be listed here. That's fragile.

Constraints:

  • I must use typescript in ESM mode. TS can't properly transpile ESM imports to CJS. (See above)

Decisions:

  • Use typescript in ESM mode, not CJS mode.
  • Replace Jest with Ava. Jest has been too slow in fixing stuff related to TS+ESM setup. When one thing is fixed, another thing breaks. Tired of playing cat and mouse.
@itsfarseen
Copy link
Author

Other noteworthy things:
I tried to integrate esbuild in my testing flow. But eventually went back to using ts-node, because, it's just another fragile point in my workflow.
There was a combinatorial explosion in number of ways things broke. I couldn't even begin to make things work. So I had to kick the least priority thing among the offenders: Typescript, ESM, Jest, ESBuild.
So ESBuild was out.

I might later reintroduce it after replacing Jest with Ava. Ava looks a bit more stable. Need to check it out before I can say for sure.

@itsfarseen
Copy link
Author

If anything errors out with SyntaxError, can't import, blah blah. Do export NODE_OPTIONS=--experimental-vm-modules

@itsfarseen
Copy link
Author

And btw, nevermind Ava. It's just as broken when it comes to ESM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment