Getting tape working with TypeScript and/or ESM
EvHaus opened this issue · comments
I'm trying to add tape
to https://github.com/EvHaus/test-runner-benchmarks as a follow up to @ljharb's Twitter post here, but I'm having a really hard time figuring out how to get it to work with a Typescript codebase.
My test file looks like this:
// Alert.test.tsx
import describe from 'tape-describe';
import Alert from '.';
import React from 'react';
import {render} from '@testing-library/react';
describe('<Alert />', (test) => {
test('should render the given message', (t) => {
const {getByText} = render(<Alert>Hello World</Alert>);
t.ok(getByText('Hello World'));
});
});
And my package.json
has "type": "module"
set.
1st Attempt (tape
)
If I run:
tape tests/Alert.test.tsx
I get:
import describe from 'tape-describe';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1175:20)
at Module._compile (node:internal/modules/cjs/loader:1219:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
at Module.load (node:internal/modules/cjs/loader:1113:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Module.require (node:internal/modules/cjs/loader:1137:19)
at require (node:internal/modules/helpers:121:18)
at importOrRequire (/Users/evhaus/Git/jest-vs-jasmine/node_modules/tape/bin/import-or-require.js:14:2)
at /Users/evhaus/Git/jest-vs-jasmine/node_modules/tape/bin/tape:96:8
2nd Attempt (ts-node
)
If I switch to ts-node
and use:
ts-node ./node_modules/tape/bin/tape tests/Alert.test.tsx
I get:
import describe from 'tape-describe';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1175:20)
at Module._compile (node:internal/modules/cjs/loader:1219:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
at Object.require.extensions.<computed> [as .js] (/Users/evhaus/Git/jest-vs-jasmine/node_modules/ts-node/src/index.ts:1608:43)
at Module.load (node:internal/modules/cjs/loader:1113:32)
at Function.Module._load (node:internal/modules/cjs/loader:960:12)
at Module.require (node:internal/modules/cjs/loader:1137:19)
at require (node:internal/modules/helpers:121:18)
at importOrRequire (/Users/evhaus/Git/jest-vs-jasmine/node_modules/tape/bin/import-or-require.js:14:2)
3rd Attempt (ts-node --esm
)
If I try with ts-node --esm
, ala:
ts-node --esm ./node_modules/tape/bin/tape tests/Alert.test.tsx
I get:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /Users/evhaus/Git/jest-vs-jasmine/node_modules/tape/bin/tape
at new NodeError (node:internal/errors:399:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
at defaultGetFormat (node:internal/modules/esm/get_format:139:38)
at defaultLoad (node:internal/modules/esm/load:83:20)
at nextLoad (node:internal/modules/esm/hooks:735:28)
at load (/Users/evhaus/Git/jest-vs-jasmine/node_modules/ts-node/dist/child/child-loader.js:19:122)
at nextLoad (node:internal/modules/esm/hooks:735:28)
at Hooks.load (node:internal/modules/esm/hooks:380:26)
at handleMessage (node:internal/modules/esm/worker:165:24)
at checkForMessages (node:internal/modules/esm/worker:114:28) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
4th Attempt (tape-es
)
If I try with tape-es:
tape-es ./node_modules/tape/bin/tape tests/Alert.test.tsx
I get:
node:internal/errors:490
ErrorCaptureStackTrace(err);
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx" for /Users/evhaus/Git/jest-vs-jasmine/benchmarks/tape/tests/original/Alert/Alert.test.tsx
at new NodeError (node:internal/errors:399:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
at defaultGetFormat (node:internal/modules/esm/get_format:139:38)
at defaultLoad (node:internal/modules/esm/load:83:20)
at DefaultModuleLoader.load (node:internal/modules/esm/loader:317:26)
at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:195:22)
at new ModuleJob (node:internal/modules/esm/module_job:63:26)
at #createModuleJob (node:internal/modules/esm/loader:219:17)
at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:172:34)
at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:157:17) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
5th Attempt (ts-node
& tape-es
)
If I combine ts-node
and tape-es
, ala:
ts-node --esm ./node_modules/.bin/tape-es tests/Alert.test.tsx
I get:
node:internal/errors:490
ErrorCaptureStackTrace(err);
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".tsx" for /Users/evhaus/Git/jest-vs-jasmine/benchmarks/tape/tests/original/Alert/Alert.test.tsx
at new NodeError (node:internal/errors:399:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
at defaultGetFormat (node:internal/modules/esm/get_format:139:38)
at defaultLoad (node:internal/modules/esm/load:83:20)
at DefaultModuleLoader.load (node:internal/modules/esm/loader:317:26)
at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:195:22)
at new ModuleJob (node:internal/modules/esm/module_job:63:26)
at #createModuleJob (node:internal/modules/esm/loader:219:17)
at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:172:34)
at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:157:17) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
I know how much you hate the ESM drama, but I'm out of ideas and I'd really love to add tape
to the benchmarks (so it can also serve as possible documentation for those who want to try it). Any pointers?
tape just runs node, so the only extensions it understands by default are js, cjs, or mjs - so if you want jsx/tsx/ts to work directly, you'll have to --require
or --import
something that can handle those, like babel-node or ts-node.
@EvHaus have you had any progress on using a loader here, or transpiling beforehand?
I haven't spent any more time with it. Would you recommend the transpiling route or the ts-node
route? I'm guessing ts-node
would be a more realistic workflow from a DX perspective.
For performance, I'd recommend pre-transpiling; for DX, ts-node.
It might be good to do both, because the delta wouldn't be due to tape.
I just turned my tests into TypeScript and in my case it was as easy as:
- include
index.test.ts
in tsconfig.json - running
tsc && tape index.test.js
Not great because it requires running TypeScript first, but in my case I was already doing it: fregante/text-field-edit#29
Got it working. The trick was to:
- Use latest
ts-node
version10.9.2
- Use
ts-node --esm
- Pass
-P tsconfig.json
tots-node
- Remove
"type": "module"
frompackage.json
Glad you got it working! type module just makes things worse anyways :-)
Got it working.
Remove "type": "module" from package.json
You got it working by not doing what the issue suggested. Now it's not ESM anymore. It'd be good to keep the issue open. Likewise in my case it worked by not having TS anymore.
Removing type module means that only .mjs
is ESM (as it should be).
@fregante maybe i'm confused, help me understand why this should be open and what we need to fix?
what we need to fix?
Documenting how to deal with ESM/TS given that all the tries with ts-node above failed
@fregante if you know the answer to that, a PR to the docs would be most helpful :-)
I prefer to avoid native ESM or native TS, so I'm not the expert here.
tape just runs node, so the only extensions it understands by default are js, cjs, or mjs - so if you want jsx/tsx/ts to work directly, you'll have to
--require
or--import
something that can handle those, like babel-node or ts-node.
@ljharb Would you be able to provide a (non working) example of running tape using node and passing nodes --import
flag? I tried running node_modules/tape/bin/tape --help
and node_modules/.bin/tape --help
but there is no help output and I have no idea in what order I am meant to pass arguments or flags.
The documentation mentions the --require
flag and since you mention import, I tried --import
but I'm not sure I am even running tape correctly because running tape as above with an empty --require
or --import
flag doesn't return an error.
Tape: 5.7.5
Nodejs: 22
OS: Linux
@darcyrush that sounds like a great idea to add to https://github.com/tape-testing/tape/tree/HEAD/example !
Specifically, it's something like NODE_OPTIONS='--import=ts-node' tape '**/*.ts'
, for example.
@ljharb Thank you for the example. I tried to get ts-node
working with a work-around I stumbled across in a ts-node issue, but I still wasn't successful.
I tried the following;
NODE_OPTIONS='--import ./ts-node.register.mjs' node_modules/tape/bin/tape 'test/**/*.test.ts'`
With ts-node.register.mjs
being
import { pathToFileURL } from "node:url";
import { register } from "node:module";
register("ts-node/esm", pathToFileURL("./"));
I thought that would transpile to JS before reaching tape, but I'm still not really sure about how the 'piping' works.