Node: Extenstionless main files and ES modules interaction

Created on 3 May 2020  路  4Comments  路  Source: nodejs/node

  • Version: 13.9, 14.1
  • Platform: All
  • Subsystem: ES modules

What steps will reproduce the bug?

Create file package.json:

{
    "type": "module"
}
````

Create file `main` (no extension):
```js
console.log('ok');

Run:

node ./main

How often does it reproduce? Is there a required condition?

Always.

What is the expected behavior?

main file to be executed.

What do you see instead?

> node ./main

internal/modules/run_main.js:54
    internalBinding('errors').triggerUncaughtException(
                              ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for C:\Users\evg656e\Projects\sandbox\js\experimental-loader\main
    at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:65:15)
    at Loader.getFormat (internal/modules/esm/loader.js:113:42)
    at Loader.getModuleJob (internal/modules/esm/loader.js:244:31)
    at async Loader.import (internal/modules/esm/loader.js:178:17) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

Additional information

This works in version 12. But does not work in versions 13 and 14. --experimental-specifier-resolution=node flag does not help.

Real world example: we have mocha (which main entry file is extensionless), we have ts-node esm-loader, now we want to test ts files with native es modules, like that:

node --loader ts-node/esm.mjs node_modules/mocha/bin/mocha --extension ts

but we get the error described above. But if we rename node_modules/mocha/bin/mocha to node_modules/mocha/bin/mocha.js, then everything works as expected.

ES Modules

Most helpful comment

The problem is that node is supposed to be treating the ./mocha entrypoint as CommonJS, not ESM. mocha's package.json does not declare "type": "module". I believe this is a mistake in the original description of this bug report.

The addition of the --loader flag is causing node to treat the entrypoint as ESM. When --loader is omitted, node correctly treats the entrypoint as CommonJS.

To reproduce on node v14.0.0:

echo '{}' > package.json
touch hooks.mjs
touch foo
node ./foo # <-- this works; no output; exit code 0
node --loader ./hooks.mjs ./foo # <-- this fails with an error, even though ./foo should be treated as CommonJS

All 4 comments

@nodejs/modules-active-members

This is intentional. See #31415. The intent is to preserve the ability for extensionless files under "type": "module" to be non-JavaScript in the future, e.g. WASM.

The workarounds are to either make a symlink (e.g. mocha which points to mocha.js) or to create a new "type": "commonjs" package scope and have mocha be a CommonJS file (even if all it does is import() the ESM one). See https://github.com/nodejs/node/pull/31415#issuecomment-576294632.

The problem is that node is supposed to be treating the ./mocha entrypoint as CommonJS, not ESM. mocha's package.json does not declare "type": "module". I believe this is a mistake in the original description of this bug report.

The addition of the --loader flag is causing node to treat the entrypoint as ESM. When --loader is omitted, node correctly treats the entrypoint as CommonJS.

To reproduce on node v14.0.0:

echo '{}' > package.json
touch hooks.mjs
touch foo
node ./foo # <-- this works; no output; exit code 0
node --loader ./hooks.mjs ./foo # <-- this fails with an error, even though ./foo should be treated as CommonJS

I believe this is a mistake in the original description of this bug report.

Do you mind please opening a new issue for this? I think it'll get lost in this one.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mcollina picture mcollina  路  3Comments

willnwhite picture willnwhite  路  3Comments

fanjunzhi picture fanjunzhi  路  3Comments

loretoparisi picture loretoparisi  路  3Comments

stevenvachon picture stevenvachon  路  3Comments