Skip to content

Instantly share code, notes, and snippets.

@spion
Last active February 12, 2017 19:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spion/50eab6d83897e940cb99e3e634a09640 to your computer and use it in GitHub Desktop.
Save spion/50eab6d83897e940cb99e3e634a09640 to your computer and use it in GitHub Desktop.

Why .mjs

Alternative 1

As far as I can tell from the latest update, the ability to import ES6 import from CommonJS modules is off the table. The main argument for this was to enable existing modules to upgrade to ES6 without causing any breakage to their CommonJS dependants.

This is a big deal. This means that CommonJS libraries that want to upgrade to ES6 modules will HAVE to bump semver-major version to do that. And once they do it, it will no longer be possible for CommonJS libraries to import them, unless they are distributed as both compiled CJS and uncompiled ES6 sources

According to the blog post, its still possible to import a CommonJS module from an ES6 module. But given the above restriction, what is the value in that? Why not simply keep import statements for ESM only, and keep using require statements for CommonJS?

Semver major would still be bumped for those libraries which means even ES6 dependants will need to manually upgrade them. The only difference is that now they would also have to change all require statements to import statements. Thats not too big of a deal, and it may be possible to largely automate it.

What would this tradeoff buy us? Well we no longer need mjs. Consider that all module imports and requires now unambigously specify what kind of module we're expecting: import means ESM, require means CommonJS.

The only reamining item is the entry point of the app. For the entry point, we could use automatic detection, with a CLI flag that has the ability to override it. And the only cases that could be miss-guessed by automatic detection are toy apps that neither import nor export any modules.

The problem

Libraries that want to support both ESM and CommonJS dependants will need a mechanism to specify where the ESM loader can find the module version. That would be a module field in package.json

Consider that this will have to be done because CJS can't import ESM. Even in the .mjs extension case. At the very least, if the main field list a ".mjs" file, the CommonJS loader will have to look in the same directory for a JS file; the ESM loader will have to do something similar. Considering how libraries organize their sources, it seems better if we have a package.json field.

Consider now the question of what to do if there is no entry in package.json. We have two different rules based on whether we used import or require: we can try the ESM loader on whatever the old node_modules resolution algorithm leads us to, or we can try the CommonJS loader on it.

If we use the ESM loader, and the module is in fact a commonjs module, then we're doing a "use strict" interpretation of a non-strict module. It means that users must be aware of whether they're importing a commonjs or an ES module into their program; but worse, we can't give them an error if the module isn't a CJS one.

That leaves us with only a single option, which is making the "module" field in package.json mandatory. All packages without it are considered to not support ES6 importing.

This will also make it easier to check if a library has ESM support - just check if it has a "module" field in package.json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment