egoist / esbuild-register

Transpile JSX, TypeScript and esnext features on the fly with esbuild
MIT License
982 stars 52 forks source link

`"type": "module"` causes `SyntaxError: Cannot use import statement outside a module` in node >= 20.6.0 #96

Open adriencaccia opened 12 months ago

adriencaccia commented 12 months ago

Hey,

With node>=20.6.0, using "type": "module", running node --loader esbuild-register/loader -r esbuild-register foo.ts where foo.ts is

import { bar } from "./bar";

console.log("works");
console.log(bar);

Results in the following error:

/home/runner/work/esbuild-register-node20/esbuild-register-node20/foo.ts:1
import { bar } from "./bar";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at internalCompileFunction (node:internal/vm:73:18)
    at loadCJSModule (node:internal/modules/esm/translators:154:23)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:233:7)
    at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:308:24)
    at async loadESM (node:internal/process/esm_loader:42:7)
    at async handleMainPromise (node:internal/modules/run_main:66:[12](https://github.com/adriencaccia/esbuild-register-node20/actions/runs/6177908642/job/16770040562#step:6:13))

Node.js v[20](https://github.com/adriencaccia/esbuild-register-node20/actions/runs/6177908642/job/16770040562#step:6:21).6.0

I made a minimal reproduction at https://github.com/adriencaccia/esbuild-register-node20.

You can see in this job that it works up to node@20.5.1, but fails for 20.6.0 and 20.6.1.

If you have any idea how to navigate from here, with the changelog of node 20.6.0, I would be happy to help fix the issue!

uhyo commented 10 months ago

Hi, after facing with this problem I ended up creating an alternative implementation that seems to work with Node.js >= 20.6.0.

https://github.com/uhyo/nitrogql/tree/v1.3.1/packages/esbuild-register

I don't intend to advise you to migrate to this one, but this may help understanding and fixing the issue in esbuild-register. I should have rather contributed to esbuild-register but it was too tough for me to not break everything while fixing the issue. Sorry :pray:


Anyways, my analysis is that returning source along with format: 'commonjs' wasn't supported until Node.js 20.6.0, meaning that it was just ignored. When the load hook returned format: 'commonjs', Node.js invoked the CommonJS loader for that file.

Node.js 20.6.0 added support for returning CommonJS source, in which case a new, different CJS loader is used. This means that the hooks registered to the old (or "monkey-patchable") loader don't take effect. esbuild-register unintendedly opted in to the new feature, which is the cause of the problem.

Although I haven't tested one, a possible fix is to just remove source: source from the load hook. However, that won't be a long-time fix because the docs says that ”This behavior for nullish source is temporary — in the future, nullish source will not be supported.”

uiolee commented 7 months ago

same issue