> This is the situation we don't want to be in, where the consumer is choosing. The consumer doesn't know. Even if you, a human, read the file, you don't know what the author's intention was.
If I am importing a file URI, then I am the author, and I know what it is. File URI's are primarily used for linking together stuff in the same git tree. If I am using third-party code, it will be a bare module import specifier from node_modules, not a file URI.
I agree that there are weird situations where this isn't the case, but you don't need to worry about those. Why? Because module support in Node is a new feature, and therefore opt-in. The people who choose to opt-in know what they are doing, and are probably running a pure ESM codebase already. The goal should be to make something useful to the 99%, and let the other 1% keep doing it the old way.
If you are already using Rollup, then this will be plug & play. Rollup already assumes that everything you `import` is a module, and puts them in strict mode.
> We really can't though, because its already full of files that are intended to be used with rollup/babel/etc, which have incomplete implementations of esm.
That's a legitimate concern. Even if using "module" itself is too risky, but the approach itself seems solid. If I want to publish something to NPM that supports native ESM in Node, it's easy for me to add something to package.json that indicates my support, without renaming all my files.
My packages on NPM have both CJS and ESM entry points. The ESM entry point is for tools like Webpack & Rollup, which do not understand the .mjs extension. I tried using the .mjs file extension for this, and it just broke things. There is no way we can re-write every Webpack and Rollup config file in existence to be compatible with this.
Even in the same project, you might have multiple people working. The specifics end up not mattering, because the goal here is to always be unambiguous by default. If you want to have your own setup in your project, you can use a loader resolve hook and come up with your own rules.
Assuming that everything you import is esm is doable, but we want cjs to continue being a first-ish-class citizen, since so much code uses it.
Right now you can opt into a dual mode package by having a file.js and a file.mjs and setting main to `./file` without an extension, and it will "just work". You can also have index.js and index.mjs without a main field in package.json.
This disagrees with my experience working in this space every single day.
Most projects in the Node ecosystem will continue to use CJS. If you are writing "Node software", like web servers, there is no reason not to just use CJS. It will remain a well-supported and well-used option for as long as Node stays relevant. CJS stays a "first-class citizen" by not changing anything, so you get your wish by default.
On the other hand, activating Node ESM support is a conscious choice. This will typically happen on a project-by-project basis. When I say "project", I mean a single GIT repository, a single NPM module, a single Web site, or something like that. If your organization is big enough to have multiple teams (mine is), each project typically has its own team.
The interfaces between these projects are usually package.json files. This is true even for giant code-bases like Babel that live in a single GIT repo but have dozens of tiny sub-projects. Within a project, people use local file references like `import('./util/foo.js')`. Between projects, people use bare import specifiers like `import('lodash')` or `import('@babel/parser')`.
So, I think it's safe to say that a team decides to switch on ESM support, they know what is going on within their own little world. Their `import` statements can be ESM-only for local files, and they will have zero trouble. Meanwhile, if bare module specifiers like `import('lodash')` rely on package.json metadata, nobody needs to care what the other teams are doing. Any project can enable / disable ESM support at any time, and nobody will know the difference.
This all goes back to an old organizational principle that the boundaries within a company often manifest themselves as boundaries within the software architecture. This has to do with the communication overhead of coordinating changes between teams. It's easier to codify the organization structure into a versioned interface than to face the chaos of just "winging it". In the Node ecosystem, this translates practically into versioned package.json files and bare module specifiers. If your requirements don't take this into account, then I am genuinely curious in hearing what your workplace culture is like.
If I am importing a file URI, then I am the author, and I know what it is. File URI's are primarily used for linking together stuff in the same git tree. If I am using third-party code, it will be a bare module import specifier from node_modules, not a file URI.
I agree that there are weird situations where this isn't the case, but you don't need to worry about those. Why? Because module support in Node is a new feature, and therefore opt-in. The people who choose to opt-in know what they are doing, and are probably running a pure ESM codebase already. The goal should be to make something useful to the 99%, and let the other 1% keep doing it the old way.
If you are already using Rollup, then this will be plug & play. Rollup already assumes that everything you `import` is a module, and puts them in strict mode.
> We really can't though, because its already full of files that are intended to be used with rollup/babel/etc, which have incomplete implementations of esm.
That's a legitimate concern. Even if using "module" itself is too risky, but the approach itself seems solid. If I want to publish something to NPM that supports native ESM in Node, it's easy for me to add something to package.json that indicates my support, without renaming all my files.
My packages on NPM have both CJS and ESM entry points. The ESM entry point is for tools like Webpack & Rollup, which do not understand the .mjs extension. I tried using the .mjs file extension for this, and it just broke things. There is no way we can re-write every Webpack and Rollup config file in existence to be compatible with this.