Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting tape working with TypeScript and/or ESM #595

Closed
EvHaus opened this issue May 28, 2023 · 14 comments
Closed

Getting tape working with TypeScript and/or ESM #595

EvHaus opened this issue May 28, 2023 · 14 comments

Comments

@EvHaus
Copy link

EvHaus commented May 28, 2023

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?

@ljharb
Copy link
Collaborator

ljharb commented May 30, 2023

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
Copy link
Collaborator

ljharb commented Jan 10, 2024

@EvHaus have you had any progress on using a loader here, or transpiling beforehand?

@EvHaus
Copy link
Author

EvHaus commented Jan 10, 2024

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.

@ljharb
Copy link
Collaborator

ljharb commented Jan 10, 2024

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.

@fregante
Copy link
Contributor

I just turned my tests into TypeScript and in my case it was as easy as:

  1. include index.test.ts in tsconfig.json
  2. 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

@EvHaus
Copy link
Author

EvHaus commented Mar 3, 2024

Got it working. The trick was to:

  • Use latest ts-node version 10.9.2
  • Use ts-node --esm
  • Pass -P tsconfig.json to ts-node
  • Remove "type": "module" from package.json

@EvHaus EvHaus closed this as completed Mar 3, 2024
@ljharb
Copy link
Collaborator

ljharb commented Mar 3, 2024

Glad you got it working! type module just makes things worse anyways :-)

@fregante
Copy link
Contributor

fregante commented Mar 3, 2024

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.

@ljharb
Copy link
Collaborator

ljharb commented Mar 3, 2024

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?

@fregante
Copy link
Contributor

fregante commented Mar 3, 2024

what we need to fix?

Documenting how to deal with ESM/TS given that all the tries with ts-node above failed

@ljharb
Copy link
Collaborator

ljharb commented Mar 3, 2024

@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.

@darcyrush
Copy link

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

@ljharb
Copy link
Collaborator

ljharb commented May 2, 2024

@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.

@darcyrush
Copy link

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants