Node: v14 regresses #16605: node --experimental-modules does not allow running files without an extension

Created on 5 May 2020  路  8Comments  路  Source: nodejs/node

What steps will reproduce the bug?

~ $ mkdir my-project && cd my-project

my-project $ npm init -y >/dev/null

my-project $ jq '. + { type: "module" }' package.json > a && mv a package.json

my-project $ echo "console.log('hi');" > lib.js

my-project $ echo "import './lib.js';" > main

my-project $ nvm use 12 && node --experimental-modules --no-warnings main
Now using node v12.14.0 (npm v6.14.5)
hi

my-project $ nvm use 14 && node --experimental-modules --no-warnings main
Now using node v14.2.0 (npm v6.14.4)
internal/modules/run_main.js:54
    internalBinding('errors').triggerUncaughtException(
                              ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /home/jeff/my-project/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'
}

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

Seems pretty reliable.

What is the expected behavior?

Node should interpret a specified file, even if it has no extension.

What do you see instead?

Node throws if the specified file has no extension.

All 8 comments

@jeffs this is correct - files without an extension are disallowed under "type": "module". I would still like to revert that change personally for the main entry point, but it's a tricky consensus issue.

This is not strictly a regression though since "type": "module" is new behaviour in terms of its semantics.

Thanks Guy, but I'm not sure what you mean. "type: "module was already supported in v12, and the behavior has now changed in a backward-incompatible way. That seems like the definition of a regression to me. Am I missing some subtlety of the vocabulary? Are you saying the breakage is deliberate, and therefore not considered a regression by the implementers?

Are you aware of any work-around? I'd really like to not have to add a separate platform-specific shell script to wrap every Node program. I'm happy to help with implementation or consensus-building if I can.

Don't use "type": "module" if you need an extensionless file to run.

It's not strictly a regression because modules semantics are experimental and unstable.

I would like to see if we can change this still... just summoning the energy for it.

I see, thanks for explaining. Fair enough: Any feature with "experimental" right in the name is bound to change.

~Node seems to respect the flag --input-type=module even when the input comes from a file, though the docs only mention it for strings. That's good enough for me.~ I stand corrected; user error.

Re. energy: Ping me if I can buy you a coffee or a Red Bull. I agree that this change is bad: It's likely to cause a lot more pain than it avoids.

D'oh, --input-type throws if and only if (!) the file contains an import statement. Please do let the world know if you discover any work-around.

Same issue when --experimental-loader is used. It replicates in both v14.2.0 & v14.3.0.

I've tried also enabling --experimental-specifier-resolution=node but, instead of fixing the missing extension problem, it throws another error.

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

TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
    at Loader.getFormat (internal/modules/esm/loader.js:122:13)
    at async Loader.getModuleJob (internal/modules/esm/loader.js:243:20)
    at async Loader.import (internal/modules/esm/loader.js:177:17) {
  code: 'ERR_INVALID_RETURN_PROPERTY_VALUE'
}

Should I overwrite also the https://nodejs.org/api/esm.html#esm_code_getformat_code_hook in my loader to return { format: 'commonjs' } for extension-less urls (that doesn't start with nodejs: prefix, e.g. nodejs:events)?

@adrian-branescu when adding --experimental-specifier-resolution=node without and custom loader (ts-node/esm) it breaks with Expected string to be returned for the "format" from the "loader getFormat" function but got type object.
Our output file extension is .js so it happens even for files with extensions :(

Was this page helpful?
0 / 5 - 0 ratings

Related issues

willnwhite picture willnwhite  路  3Comments

stevenvachon picture stevenvachon  路  3Comments

Icemic picture Icemic  路  3Comments

jmichae3 picture jmichae3  路  3Comments

vsemozhetbyt picture vsemozhetbyt  路  3Comments