ts-node: es modules and commonjs mismatch

2022-03-07

 | 

~2 min read

 | 

295 words

I added a script to a Next.JS project recently which I wanted to run as part of a pre-build step with ts-node.

The script lived in a scripts folder in the root of my project:

build.ts
import { ExpandedNote } from "../types/note"

const fs = require("fs")
const path = require("path")
const lunr = require("lunr")
const { extractNoteData } = require("../utils/extractFrontmatter")

function builder() {
  //....
}
builder()

The issue is that every time I ran this, there was a module in the dependency graph and the script would error:

% npm run pre-build

> blog-next@0.1.0 pre-build
> ts-node scripts/build.ts

(node:43492) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/stephen.weiss/code/blog-next/scripts/build.ts:136
export {};
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (internal/modules/cjs/loader.js:1001:16)
    at Module._compile (internal/modules/cjs/loader.js:1049:27)
    at Module.m._compile (/Users/stephen.weiss/code/blog-next/node_modules/ts-node/src/index.ts:1459:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/stephen.weiss/code/blog-next/node_modules/ts-node/src/index.ts:1462:12)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
    at main (/Users/stephen.weiss/code/blog-next/node_modules/ts-node/src/bin.ts:389:12)
    at Object.<anonymous> (/Users/stephen.weiss/code/blog-next/node_modules/ts-node/src/bin.ts:539:3)

I was not the first person to run into a similar situation with ts-node and I found this answer in a Github thread that solved the problem nicely.

The issue appeared to be a mismatch between module types of ES Modules and CommonJS in my script. Meanwhile Next.JS sets the compiler options for modules to ESNext by default, so I couldn’t just change my project without breaking Next.

Luckily, Typescript has an escape hatch that can be added to the tsconfig.json, from the ts-node README:

tsconfig.json
{
  "compilerOptions": {
    "module": "ESNext"
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  }
}

With this small change to my tsconfig.json my scripts now run successfully with type support and my app runs like before!



Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!