ts-node icon indicating copy to clipboard operation
ts-node copied to clipboard

ERR_UNKNOWN_FILE_EXTENSION since Node.js 18.19.0 (works fine with 18.18.2)

Open mlenkeit opened this issue 2 years ago • 20 comments

Search Terms

  • ERR_UNKNOWN_FILE_EXTENSION
  • Node.js 18.19.0
  • Node.js 18.18.2

Expected Behavior

npx ts-node <script.ts> works on Node.js 18.19.0 just as it does with 18.18.2.

Actual Behavior

Since the upgrade to Node.js 18.19.0, the call fails with ERR_UNKNOWN_FILE_EXTENSION.

All other potentially related parameters remained unchanged (e.g. package-lock.json, ts-config.json, etc.).

We discovered this with GitHub workflow runs that run periodically (with schedule) at short intervals; only difference since things started failing is the Node.js version.

Steps to reproduce the problem

  • set up Node.js 18.19.2
  • set "type": "module" in package.json
  • set "esm": true in tsconfig.json
  • create main.ts with something like: console.log('Hello world')
  • run npx ts-node main.ts

Minimal reproduction

see https://github.com/mlenkeit/ts-node-repro

there's also a GitHub workflow to run this repo against Node.js 18.19.0 and 18.18.2: https://github.com/mlenkeit/ts-node-repro/actions/runs/7057725777

Specifications

  • ts-node version: v10.9.1
  • node version: v18.19.0
  • TypeScript version: v5.2.2
  • tsconfig.json, if you're using one:
    {
      "extends": "@tsconfig/node18/tsconfig.json",
      "ts-node": {
        "esm": true
      },
      "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "moduleResolution": "Node",
        "module": "ESNext",
        "noImplicitAny": false,
        "strictNullChecks": true,
        "sourceMap": true,
        "outDir": "./dist",
        "resolveJsonModule": false,
        "types": [
          "node"
        ]
      },
      "exclude": ["node_modules"]
    }
    
  • package.json:
    {
      "name": "ts-node-repro",
      "private": true,
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "type": "module",
      "dependencies": {
        "@tsconfig/node18": "18.2.2",
        "@types/node": "18.16.1",
        "ts-node": "10.9.1",
        "typescript": "5.2.2"
      }
    }
    
  • Operating system and version: macOS Sonoma 14.1.1

mlenkeit avatar Dec 01 '23 09:12 mlenkeit

Workaround: You can use node --loader ts-node/esm file.ts instead of ts-node --esm file.ts (which causes a ExperimentalWarning: --experimental-loader may be removed in the future). It would be great if ts-node provided an entrypoint that calls register from node:module, which seems to be Node's preferred way of registering loaders, now.

michael42 avatar Dec 01 '23 09:12 michael42

@michael42 thanks for the suggestion. This indeed works with both Node.js 18.18.2 and 18.19.0.

I will, however, hold back with applying that change for now, as this would require changes across a dozen or so repos. We've simply pinned Node.js to 18.18.2 for now to unblock us.

I still consider this to be a bug in ts-node.

mlenkeit avatar Dec 01 '23 09:12 mlenkeit

Same issue in node v20.10.0 (current LTS)

jetwiwo avatar Dec 04 '23 03:12 jetwiwo

Workaround: You can use node --loader ts-node/esm file.ts instead of ts-node --esm file.ts (which causes a ExperimentalWarning: --experimental-loader may be removed in the future). It would be great if ts-node provided an entrypoint that calls register from node:module, which seems to be Node's preferred way of registering loaders, now.

This does not seem to work for me: node --loader ts-node/esm -r tsconfig-paths/register ./scripts/deploy.ts

(node:129) 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)
/builds/ci-vct-dev/aws-platform-ci/aws-cdk/scripts/deploy.ts:1
import { AWS_DOMAIN } from '../shared-lib/src/environment/compile';
^^^^^^

JonWallsten avatar Dec 04 '23 10:12 JonWallsten

Workaround: You can use node --loader ts-node/esm file.ts instead of ts-node --esm file.ts (which causes a ExperimentalWarning: --experimental-loader may be removed in the future). It would be great if ts-node provided an entrypoint that calls register from node:module, which seems to be Node's preferred way of registering loaders, now.

This does not seem to work for me: node --loader ts-node/esm -r tsconfig-paths/register ./scripts/deploy.ts

(node:129) 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)
/builds/ci-vct-dev/aws-platform-ci/aws-cdk/scripts/deploy.ts:1
import { AWS_DOMAIN } from '../shared-lib/src/environment/compile';
^^^^^^

Make sure you have "type": "module" in package.json or rename the file to file.mts

jetwiwo avatar Dec 05 '23 23:12 jetwiwo

Same issue in node v20.10.0 (current LTS)

@jetwiwo right, thanks for mentioning that. FWIW, it looks like it doesn't work with Node.js 20 at all. I've added Node.js 20 versions to the repo for reproducing the problem.

image

https://github.com/mlenkeit/ts-node-repro/actions/runs/7125070507

mlenkeit avatar Dec 07 '23 07:12 mlenkeit

See #1997 - this sounds like a duplicate of that one.

ajvincent avatar Dec 08 '23 15:12 ajvincent

See #1997 - this sounds like a duplicate of that one.

I agree that #1997 and this one probably share the same root cause.

Yet, I think they differ in their implication: If ts-node changed the way it works on a major version bump of Node.js, I'd say that's acceptable. But if ts-node suddenly stopped working on a minor version bump of Node.js, I'd say it's a bug.

mlenkeit avatar Dec 13 '23 09:12 mlenkeit

Got bit by this just a few minutes ago as well, would agree with not expecting breaking changes with minor version bumps.

MythicManiac avatar Dec 13 '23 15:12 MythicManiac

Does anyone know which version of node it works on ? Currently have this issue with node 20, running ts-node --esm in a module

rreeves8 avatar Dec 18 '23 18:12 rreeves8

I'm using tsx with AWS CDK until this is resolved.

In cdk.json, change the app property to:

  "app": "npx tsx bin/app.ts",

rcollette avatar Dec 19 '23 17:12 rcollette

Does anyone know which version of node it works on ? Currently have this issue with node 20, running ts-node --esm in a module

@rreeves8 it works on Node.js 18.18.2. To my knowledge, any newer version runs into the problem described here (incl. Node.js 20). For following this kind of problem for Node.js 20, you may want to take a look at #1997 as well.

mlenkeit avatar Dec 20 '23 11:12 mlenkeit

node --loader ts-node/esm index.ts works for me

kuncevic avatar Dec 26 '23 22:12 kuncevic

Workaround: You can use node --loader ts-node/esm file.ts instead of ts-node --esm file.ts (which causes a ExperimentalWarning: --experimental-loader may be removed in the future). It would be great if ts-node provided an entrypoint that calls register from node:module, which seems to be Node's preferred way of registering loaders, now.

This does not seem to work for me: node --loader ts-node/esm -r tsconfig-paths/register ./scripts/deploy.ts

(node:129) 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)
/builds/ci-vct-dev/aws-platform-ci/aws-cdk/scripts/deploy.ts:1
import { AWS_DOMAIN } from '../shared-lib/src/environment/compile';
^^^^^^

Make sure you have "type": "module" in package.json or rename the file to file.mts

thanks, it works for me

seemrcola avatar Dec 27 '23 06:12 seemrcola

FWIW, we have decided to switch to tsx, given that there hasn't been any reaction yet from the maintainers of ts-node.

In addition to replacing the dependency itself, we have removed the ts-node config from our tsconfig.json file and also uninstalled any explicit dependencies to swc (if present), as it seems to be the default in tsx.

We struggled a bit to make it work across Node.js versions, given that the --loader and --import options seem to be mutually exclusive, and ended up having dedicated scripts in our package.json.

mlenkeit avatar Jan 15 '24 11:01 mlenkeit

Any update on this? This has rendered ts-node completely unusable and not fit for purpose. The node --loader ts-node/esm file.ts and similar workarounds do not always work, and break all existing projects that now have to temporarily implement the workaround while this gets fixed.

sovietspaceship avatar Apr 13 '24 17:04 sovietspaceship

The other workaround is to use tsx instead. tsx also comes with watch mode, which is a bonus.

qiulang avatar Apr 28 '24 03:04 qiulang

For anyone looking for a one-liner workaround in the future, you can implement @qiulang's suggestion by making the following change in your cdk.json file:

-   "app": "npx ts-node --prefer-ts-exts bin/project-cdk.ts",
+   "app": "npx tsx bin/project-cdk.ts",

Since both tsx is called via npx, you technically don't need to add it as a dependency... But it's probably a good idea to run an npm i --save-dev tsx to avoid any unexpected issues that may occur from tsx version changes in the future, especially if your code runs in CI.

andrewodri avatar Aug 05 '24 16:08 andrewodri