Why there’s no `.bs.mjs` suffix option in `bsconfig.json`


Imagine I’m starting a project where I’m going to use ESM modules. I use the following config:

  "package-specs": {
    "module": "es6-global",
    "in-source": true
  "suffix": ".bs.js",

Now, I want to run some modules in a NodeJS environment. node would bark on me by asking to either to use .mjs extension for modules or to add:

"type": "module"

to my package.json. OK, no problem, let’s update package.json. So far, so good, everything works now.

Then I want to add a dependency on a 3-rd party ReScript library. Say, rescript-jest, rescript-test, whatever.

Once this is done, *.res files of such a library are compiled to *.bs.js along with my sources, with the settings defined by bsconfig.json. That’s quite logical.

But, I can no longer run the project with node because it wants either .mjs extension or type: module for that library. I can’t change its package.json because I have no control over it, and that library author can’t specify the type because he does not know the context where the library is going to be used.

A viable option would be switching to *.bs.mjs suffix, but there’s no such option. I have to either:

  • use plain .mjs suffix with in-source build and get file tree cluttered with no ability to distinguish between hand-crafted and generated sources;
  • use plain .mjs suffix with out-of-source build and get much pain with long/relative/paths/to/compiled/es/modules.mjs instead of just ./modules.mjs; and loose an ability to quickly switch between res and its corresponding JS in IDE;
  • use kludges like babel to make node think I’m using CJS-modules

Is there a particular reason why do we have ".js", ".mjs", ".cjs" or ".bs.js" options and not ".bs.mjs"?


Why not? It’s a string. You can set it to any valid string, e.g. "suffix": ".bs.mjs"

It’s a string enum unfortunately.
If I change it to your suggestion I get:

File "/Users/florian/Desktop/test/bsconfig.json", line 8:
Error: expect .bs.js, .js, .cjs, .mjs here 
For more details, please checkout the schema https://rescript-lang.org/docs/manual/latest/build-configuration-schema

I guess if changes happen on this front, we could also switch to

.res.js, .res.mjs, .res.cjs, etc.


I think i put it in the wrong place but if this gets any important eyes:


Thank you! I think it belongs to https://github.com/rescript-lang/rescript-compiler actually. https://github.com/rescript-association/rescript-lang.org hosts the docs.

Nevertheless, there’s a button to quickly move an issue to another repo available for maintainers, so it shouldn’t be a problem.

One more thing.

I’m asking it here to know are there some corner stones preventing us from having such options. I see no obstacles, but who knows. And if there’re indeed no reasons to not introduce .bs.mjs, .bs.cjs, .res.js, .res.mjs, .res.cjs, it should be confirmed by the core team and we as the community should make the improvement.

I would try if I see the green light.


Im with @yawaramin surprise. Seems like a degree of constraint that shouldnt really concern the compiler

1 Like

Also struggled with ESM recently. I came to a conclusion that we should avoid usage of the "type": "module" for ReScript libraries and the only way to work with ESM is to set "type": "module" for an application and compile with *.mjs suffix. And it’s a big problem not being able to distinguish between hand-crafted and generated sources.

And there are some cases why "type": "module" won’t work for a ReScript library:

  • An application where the library is used has "type": "commonjs", "module": "commonjs", and "suffix": ".bs.js" - then there’s an error “require() of ES Module is not supported”.
  • An application where the library is used has "type": "commonjs", "module": "es6", and "suffix": ".mjs" - it might work, but I have “Cannot find package ‘foo’ imported from ./bar.mjs Did you mean to import foo/entrypoints/main.cjs” in some projects, or problems with different tools that require "type": "module" for proper ESM support.
  • An application where the library is used has "type": "module", "module": "es6", and "suffix": ".bs.js" - There is “Cannot use import statement outside a module” if there are other ReScript libraries without "type": "module".
  • An application where the library is used has "type": "module", "module": "es6", and "suffix": ".mjs" - This is the only combination that works, but doesn’t look like a good idea to limit library users. It’s better to allow them decide themselves between commonjs and module using the module setting in bsconfig.

I have a library called rescript-mngutils, it is defined with commonjs, but the compiled js of this library are end up with .mjs when i change my project to es6-global and mjs suffix.

My project’s bsconfig.json

There is no need to set type with module in packages.json, so i guess may be your node version is smaller than v13.2.0.

1 Like

There are no fundamental reasons why it is not possible.
The main thing is that you can see we have too many extensions and there’s a time that people don’t like names such as bs.

1 Like

Here’s a PR: https://github.com/rescript-lang/rescript-compiler/pull/5631