UVU — Run Tests with TypeScript and ESM

We’re using UVU as the test runner for packages and applications. UVU works nicely in CommonJS projects using plain JavaScript and also with TypeScript using a require hook.

We used the ts-node/register hook with UVU, but that one doesn't work anymore when migrating your code from CommonJS to ESM. With ESM and TypeScript, you need an import hook, related to the import syntax used in ESM. And UVU doesn't support import hooks at this point.

Yet, you can still run your TypeScript tests with UVU’s test runner. This tutorial shows you how!

UVU Series Overview

  1. Run Tests with TypeScript and ESM

The Problem: ESM Wants import Instead of require

Testing your CommonJS TypeScript code with UVU worked fine when using a require hook. A common require hook is ts-node/register. Don’t worry if you’re using another require hook in your project: it’s fine if it’s working:

{
  "name": "app-name",
  "type": "commonjs",
  "devDependencies": {
    "ts-node": "~10.9.1",
    "typescript": "~5.2.2",
    "uvu": "~0.5.6"
  },
  "scripts": {
    "start": "ts-node server.ts",
    "test": "uvu -r ts-node/register"
  }
}

You probably receive an error message like the following when migrating your code from CommonJS to ESM. The used ts-node/register hook isn’t working anymore, you’re receiving an error related to require() of ES modules is not supported:

$ npm test

> uvu -r ts-node/register

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/marcus/Projects/supercharge/superchargejs.com/test/show-docs.ts  
require() of ES modules is not supported.  
require() of /Users/marcus/Projects/supercharge/superchargejs.com/test/show-docs.ts from /Users/marcus/Projects/supercharge/superchargejs.com/node_modules/uvu/run/index.js is an ES module file as it is a .ts file whose nearest parent package.json contains "type": "module" which defines all .ts files in that package scope as ES modules.  
Instead change the requiring code to use import(), or remove "type": "module" from /Users/marcus/Projects/supercharge/superchargejs.com/package.json.

    at createErrRequireEsm (/Users/marcus/Projects/supercharge/superchargejs.com/node_modules/ts-node/dist-raw/node-internal-errors.js:46:15)
    at assertScriptCanLoadAsCJS (/Users/marcus/Projects/supercharge/superchargejs.com/node_modules/ts-node/dist-raw/node-internal-modules-cjs-loader.js:584:11)
    …

That error is correct. The used ts-node/register hook for on-the-fly TypeScript compilation isn’t working with ESM. Let’s make it work!

Run UVU Tests with TypeScript and ESM

We used ts-node to run our TypeScript code while being CommonJS code. We expected there’s ESM support when using ts-node, too. Yet, the ts-node/esm import hook isn’t working as expected in ts-node 10.9.1. There’s an open GitHub issue for that.

If ts-node/esm isn’t working, let’s find another import hook so that our TypeScript code with ESM works fine. There’s tsx, a tool to enhance Node.js supporting TypeScript and ESM with the help of esbuild.

tsx comes with a command line interface allowing you to run TypeScript scripts. It also comes with an import hook. Here are two NPM scripts running the test suite: 1. using tsx directly to run the UVU executable
2. using node with the tsx import hook running the UVU executable

Both ways are fine and run the test suite of TypeScript files in an ESM project:

{
  "name": "app-name",
  "type": "module",
  "devDependencies": {
    "tsx": "~4.3.0",
    "typescript": "~5.2.2",
    "uvu": "~0.5.6"
  },
  "scripts": {
    "start": "tsx server.ts",
    "test": "tsx ./node_modules/uvu/bin.js",
    "test:alt": "node --import tsx ./node_modules/uvu/bin.js",
  }
}

That’s it. UVU now processes the test files correctly:

$ npm test

> supercharge-homepage@2.0.0 test
> c8 tsx ./node_modules/uvu/bin.js

test/show-docs.ts  
• • • •   (4 / 4)

Enjoy testing your TypeScript test files in an ESM project with UVU!


Mentioned Resources

Explore the Library

Find interesting tutorials and solutions for your problems.