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

ts-node cannot run mixed ESM/CJS project #2110

Open
m-ronchi opened this issue Mar 8, 2024 · 2 comments
Open

ts-node cannot run mixed ESM/CJS project #2110

m-ronchi opened this issue Mar 8, 2024 · 2 comments

Comments

@m-ronchi
Copy link

m-ronchi commented Mar 8, 2024

Search Terms

ESM CJS mixed project
SyntaxError: Named export not found. The requested module is a CommonJS module, which may not support all module.exports as named exports.

Expected Behavior

ts-node works and prints BAR

Actual Behavior

$ npx tsc && node dist/test.mjs
BAR
$ npx ts-node --esm src/test.mts
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".mts" for /***/ts-node-bug/src/test.mts
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
    at defaultLoad (node:internal/modules/esm/load:143:22)
    at async nextLoad (node:internal/modules/esm/hooks:865:22)
    at async nextLoad (node:internal/modules/esm/hooks:865:22)
    at async Hooks.load (node:internal/modules/esm/hooks:448:20)
    at async MessagePort.handleMessage (node:internal/modules/esm/worker:196:18) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
$ node --loader ts-node/esm src/test.mjs
(node:65265) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
file:///****/ts-node-bug/src/test.mts:1
import { foo } from "./lib.js";
         ^^^
SyntaxError: Named export 'foo' not found. The requested module './lib.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from './lib.js';
const { foo } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

Node.js v20.11.1
$ node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));' src/test.mts                   cluster: prod
file:///***/ts-node-bug/src/test.mts:1
import { foo } from "./lib.js";
         ^^^
SyntaxError: Named export 'foo' not found. The requested module './lib.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from './lib.js';
const { foo } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

Node.js v20.11.1

Steps to reproduce the problem

run this:
ts-node-bug.zip

Minimal reproduction

lib.ts (inferred as CommonJS module)

export const foo = "BAR"

test.mts (ESM)

import { foo } from "./lib.js";

console.log(foo);

Specifications

ts-node v10.9.2
node v20.11.1
compiler v5.4.2

  • tsconfig.json, if you're using one:
{
  "compilerOptions": {
    "lib": [ "es2023" ],
    "module": "node16",
    "target": "es2022",

    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node16",
    "noEmit": false,
    "outDir": "dist/",
    "sourceMap": true,
    "strictNullChecks": true,
  },
  "include": [
    "src"
  ]
}

  • package.json:
{
  "name": "ts-node-bug",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.11.25",
    "ts-node": "^10.9.2"
  }
}

  • Operating system and version: macos
  • If Windows, are you using WSL or WSL2?:
@fardolieri
Copy link

fardolieri commented Mar 15, 2024

I wanted to create a new issue targeting the same problem but then I found this one so I am commenting here. See this stackblitz.

// main.cts
(async () => {
  const dep = await import('./dep.mjs');
  console.log(dep);
})()
// dep.mts
export const dep = 'dependency';

I expected ts-node main.cts and tsc && node dist/main.cjs to behave the same but I get this error

Cannot find module '[..]/dep.mjs' imported from [..]/main.cts

What do I need to do to make this run with ts-node?
Edit: ts-node-esm main.cts works for me.

@gmarinov
Copy link

It appears this broke in Node 18.19 and versions released since have the issue.

There have been multiple issues tracking facets of it, in multiple repositories (node, typescript, ts-node, esbuild, tsx among others), over the last few months, but no resolution. In #2094 the common strategy is to work around the issue - either downgrade Node to 18.18, or use whatever alternative works in your scenario, for example tsx (in which the same issue is half-fixed). None of those workarounds represent an actual fix.

The scenario is very simple: it happens in mixed CJS/ESM repositories, using TS in development with tooling that isn't always new, and inevitably some dependencies that don't support a global switch to modules in package.json. Node and some tooling (tsx) defaults to CJS in the absence of type=module in package.json, and so import-s are now broken, especially in an .mjs file importing .ts, e.g. import { named } from './tsfile' (treated as CJS). The CJS-compatible import('./file').then(...) pattern works in the forced CJS context. Or you could rename your .ts file to .mts and hope that the ESM system wakes up.

I'd love if ts-node had updates about this, but I'm already having to test with alternatives. 🤷

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

3 participants