I write stuff in TypeScript and used Tape as a test runner. After converting my project to use ECMAScript modules (ESM), tape stopped working, causing quite a bit of headache.
- Write tests in TypeScript. Tests live next to source files.
- Do not compile tests to the dist directory.
- Use ts-node to transpile tests on the fly and use tape as the test runner.
- Use
ts-node tape
to run stuff. Results in various errors, becausetape
isn't a true ESM package (I think).
I converted my project to ESM based on Sindre Sorhus' guide.
This is an excerpt of my package.json
:
{
//...
"type": "module",
"scripts": {
"test": "node --loader ts-node/esm node_modules/tape/bin/tape 'src/**/*.spec.ts' | tap-diff",
}
// ...
}
ts-config.json
:
{
"compilerOptions": {
"moduleResolution": "node16",
"module": "node16",
"target": "es2017",
"declaration": true,
"outDir": "./dist",
"lib": ["dom", "es2017"],
"baseUrl": "./src",
},
"include": ["./src/**/*.ts"],
"exclude": ["node_modules", "dist", "src/**/test"],
"ts-node": {
"transpileOnly": true,
"files": true,
"experimentalResolver": true
}
}
Unfortunately, it doesn't work right out of the box:
SyntaxError: Cannot use import statement outside a module
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1153:20)
at Module._compile (node:internal/modules/cjs/loader:1205:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1295:10)
at Module.load (node:internal/modules/cjs/loader:1091:32)
at Module._load (node:internal/modules/cjs/loader:938:12)
at Module.require (node:internal/modules/cjs/loader:1115:19)
at require (node:internal/modules/helpers:130:18)
at importOrRequire (./node_modules/tape/bin/import-or-require.js:14:2)
The culprit is tape
. It doesn't like the .ts
-extension. I had to patch tape
this way:
Edit node_modules/tape/bin/import-or-require.js
line 11 (in tape 5.7.0
) and tell tape
to also load .ts
-files. Because it's just the filename. It'll be transpiled on the fly via the ts-node
loader.
if (ext === '.mjs' || (ext === '.js' && getPackageType.sync(file) === 'module') || (ext === '.ts' && getPackageType.sync(file) === 'module')) {
After that, it runs flawlessly. Don't like editing files in node_modules
? I didn't either, but since I learned about the extremely simple patch-package
, I use it quite often to reliably change small errors. Read more about it here and here.