Node: Node.JS treats *.mjs->*.js symlinks as *.js files

Created on 13 Jun 2018  路  10Comments  路  Source: nodejs/node

  • Version:v10.4.0
  • Platform:Windows

I found out that Node.js obstructs potential compatibility between browser and Node libraries, by enforcing a mjs extension. I tried to deal with this obstruction by creating a symlink with mjs for my js files. That would still not help much with including external libraries, but at least I could use my own.

However, I still get this error:

D:\web\lines>node --experimental-modules ES6test.mjs
(node:7464) ExperimentalWarning: The ESM module loader is experimental.
D:\web\lines\ES6test.js:6
import myLibrary from "./MyFile.mjs";
       ^^^^^^^^^^^^^^^

SyntaxError: Unexpected identifier
    at new Script (vm.js:74:7)
    at createScript (vm.js:246:10)
    at Proxy.runInThisContext (vm.js:298:10)
    at Module._compile (internal/modules/cjs/loader.js:670:28)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
    at Module.load (internal/modules/cjs/loader.js:612:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
    at Function.Module._load (internal/modules/cjs/loader.js:543:3)
    at createDynamicModule (internal/modules/esm/translators.js:54:15)
    at setExecutor (internal/modules/esm/create_dynamic_module.js:50:23)

This was the only workaround I could think of and it also fails, as you can see above.

I've been looking forward to finally writing same code for browser and Node, and this is a big disappointment.

ES Modules

Most helpful comment

To be certain about the behavior here... The way the resolver works, it will attempt to resolve symlinks first then will use the file extension of the resolved symlink to determine how to load the file. The reason it doesn't work is because the symlink path is discarded by the loader by default.

I can't remember exactly which Node.js 10.x version it was added in, but I know it works from 10.4.x and on... but the following will actually do what you're looking for @Darker

node --experimental-modules --preserve-symlinks-main testesm.mjs

The --preserve-symlinks-main option will instruct the loader to keep the original symlink path but it does carry along a few caveats that break backwards compatibility (which is why it is off by default)

All 10 comments

browsers don't care about file extension, so one solution is to just use .mjs everywhere for your module code. node's modules are still experimental though so nothing is final.

mjs extension is not supported by IDEs and many other thing. I can't believe Node.js developpers thought they are just force the whole world to change the standard extension for javascript files. Anyway, this issue is about node not respecting the filename given.

I can't believe Node.js developpers thought they are just force the whole world to change the standard extension for javascript files.

@Darker if I were you, I'd refrain from making such remarks. For one, things are a lot more complicated than that, and we're not enforcing .mjs onto everyone else. That said, this is how we've designed ESM to work and you may feel free to use something like Babel if you don't want to use .mjs. There's a lot of discussion involved and we didn't make the switch for no reason.

mjs extension is not supported by IDEs and many other thing.

Umm, isn't that the IDEs problem?

AFAIK .mjs is just a temporary thing that at least allows us to experiment with interop solutions. It's not a final decision that we will only support .mjs, just like it's not a final decision that ESM will only available with --experimental-modules (probably less obvious if we don't put the word experimental in everything)

Ok, I guess there's no point in arguing about an experimental feature here. But is intended that Node.js resolves symlinks like this? I was convinced the symlinks are supposed to be treated as if they were file they refer to. I think it might break other things, not just my workaround.

cc @nodejs/modules

But is intended that Node.js resolves symlinks like this?

Yes. require() works the same way.

If it's a design choice and not a bug, then I don't see the point in this staying open.

To be certain about the behavior here... The way the resolver works, it will attempt to resolve symlinks first then will use the file extension of the resolved symlink to determine how to load the file. The reason it doesn't work is because the symlink path is discarded by the loader by default.

I can't remember exactly which Node.js 10.x version it was added in, but I know it works from 10.4.x and on... but the following will actually do what you're looking for @Darker

node --experimental-modules --preserve-symlinks-main testesm.mjs

The --preserve-symlinks-main option will instruct the loader to keep the original symlink path but it does carry along a few caveats that break backwards compatibility (which is why it is off by default)

you can also use a resolve hook (https://nodejs.org/api/esm.html#esm_resolve_hook) to tell node.js to load your files as esm, without the .mjs extension.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jmichae3 picture jmichae3  路  3Comments

danialkhansari picture danialkhansari  路  3Comments

dfahlander picture dfahlander  路  3Comments

Brekmister picture Brekmister  路  3Comments

filipesilvaa picture filipesilvaa  路  3Comments