New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Default to sourceType:script when parsing files. #7501
Conversation
@@ -39,6 +39,7 @@ program.option( | |||
"List of extensions to hook into [.es6,.js,.es,.jsx,.mjs]", | |||
collect, | |||
); | |||
program.option("--source-type [script|module|unambiguous]", ""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be made to assume "module" for .mjs
files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.mjs
files are hard-coded to module
currently, so this would only effect other types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, makes sense.
Also curious to hear from @gaearon if you have thoughts. This would certainly affect CRA though realistically you may already just want to set |
If this lands as currently proposed, I suggest reporting a warning for when Or, we could just keep |
Oh that's something I can clarify. The error right now is quite helpful. If Babel is parsing as
Which I think is helps. We can certainly expand that too if we find it is needed. |
01cd317
to
7aead7e
Compare
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/7119/ |
@jdalton I know this is something you have thoughts on. Where do you stand on this these days? |
7aead7e
to
cb6ff25
Compare
How are TypeScript files going to be handled? I assume it's safe to hardcode those to modules too. |
cb6ff25
to
2d7d96b
Compare
@Kovensky Fair, I'd be fine with hard-coding |
It would need overriding if babel supports |
@Kovensky How does TS itself differentiate these cases? I assume there is a flag in their config file? |
TS specifies itself as unambiguous grammar. |
Maybe we should consider having Babylon just ignore the |
In the browser, Y'all should be tracking the node modules WG if you're not already, and use the progress you see there help inform where you invest your time right now, but at least on our end we're planning on sitting on it until |
No mater what ships in node, CJS will remain the default, and babel will absolutely need to support mjs when it ships unflagged in node. I often use babel without using ESM; converting syntax is nice but having to remember not to pollute the ecosystem with a .default is less so. |
@ljharb What about all the browser people who just wanna transpile their async/await down to normal esm modules for browser use by default? Node's default isn't the browser's default. Your default isn't theirs. |
@weswigham even with |
@weswigham the browser’s default is Script, unless you specify type=module. I agree with you - Babel’s default should match the browser’s. |
@ljharb And that file is a I'm saying that without knowing if the code is targeted for Changing the default behavior here just changes the default platform assumption. IMO, the current default is usable and it is configurable so that a given audience can achieve their desired behavior, anyway (while providing a configuration that works out of the box for most people); so why would you sacrifice that usability? Like, webpack's last release's push was for zero-config starts for webdev projects; this pushes babel in the opposite direction for web projects. |
I couldn't agree more here. This is the exact same reason webpack supports .mjs as a "module type". One thing I'd like clarified: does this mean that babel will not transpile module statements and leave as is if this PR lands? (Unless otherwise configured to do so). This in combination with the million who are using webpack will see some incredibly beneficial size reductions in their linked bundles because they are explicitly opting (in one way or another) to using ESM (and it will not be down converted to cjs) |
My hunch is that people just don't like I looked at the Unambiguous Grammar spec, and while I laud its aims, I also understand why browser vendors may be unmotivated to push it forward. If you're importing a script in the browser, you're either using That said, node and Babel could certainly follow Unambiguous Grammar spec without waiting for browsers to endorse it. |
@TheLarkInn this PR only deals with sourceType; transforming import/export is a separate issue (albeit, one that only applies when the sourceType is module). @appsforartists if node and babel are doing things "without waiting for browsers", then any argument that relates to browser interop imo vanishes; I don't think that'd be a good idea for any of the "camps" here. |
@ljharb Unambiguous Grammar feels orthogonal to browsers. Imports are never ambiguous for browsers in the first place. |
I hadn't thought about it, but that does seem like a conversation we should have pre-RC. I'd probably be open to that, since it's always easier to add a plugin than remove it in Babel. Maybe continue that discussion in another issue? |
It does seem that way, and I do understand the concern there because they are right that people associate very strongly with To me, a file extension is the least offensive way to handle that without typing the solution to one specific ecosystem. I can easily see individual communities adopting other options, or simply assuming that code is ESM, and that may well be the right choice for that specific community. Babel cannot hope to have a default that keeps every one of those happy with their individual ecosystem choices, so we're essentially left with the choice of choosing the least offensive option (in my opinion
Unless there's real indication of Node pursuing that approach and it having a real standard, I don't think I'd be comfortable to have Babel default to that. |
Whatever side channel the working group decides on, it will strictly be a way for opting in to modules instead of opting out. Opting out is guaranteed to break a ton of real world code. If the default is script, we can just add support to whatever side-channels are decided on (such as If we keep module as the default, we'll just keep on silently doing the wrong thing for the vast majority of 3rd party code, and then when the WG gets their ducks in order we're going to have to make another breaking change to implement their algorithm. As for changing the default, a lot of code happens to run fine in both strict and sloppy mode, so you won't notice anything if all that babel ends up doing is writing a "use strict". But combine that with transform-runtime, preset-env with useBuiltIns: "usage", or any plugin that needs to add an import to a file, and you easily create invalid "mixed" modules, that have require, static imports and exports/module.exports assignment all in the same file. If that output were to then be treated as a module (which it would in unambiguous grammar too, it has a static import), you then get a ReferenceError, but only at runtime. Babel will silently take your valid script (even strict mode script) and write an invalid module. If we know the input is not a module, then the aforementioned plugins know to write commonjs requires instead (well, env will once it uses the correct helper module). If we think the input is not a module but it has module syntax, it's easy to raise an error telling you what to do in your .babelrc. If we think the input is a module but it's not, we corrupt your script and there's nothing you can do to detect other than seeing if it crashes at runtime. |
I think the conceit is that everyone knows The core of the issue is that changing babel's default to be TBQH, at TS since we power editor services for a bunch of editors, we'll probably continue to be using an |
Editor services are a completely different world from compilers. Editor services will only be of use on files that a user actually wants to look at and edit, have to be able to work with incomplete code, and even if the user opens a file that the editor services guesses wrong, the worst thing that can happen is bad syntax highlighting and code completion. Babel is a compiler. If we guess wrong, we write broken code. Even if TSC guesses wrong, it can throw an error under TSC is also not used to compile third party code, but the ecosystem is moving in such a way that babel, with preset-env, will be used to compile all third party code as well. Third party code that, in a lot of cases, has already been transformed from a module into a script. Third party code that we can correctly parse as module, because its original source code was a module, and so it doesn't have sloppy mode syntax. Third party code that we will break by then emitting extra module-related garbage, because we think it's a module. |
Many modern compilers are written as services, as service behavior is a superset of the analysis a compiler needs to perform (and is quite useful to have). A compiler pretty much falls out for free from a language service, while building a service on top of a compiler is really difficult.
And I'm not talking about typescript. I'm talking about JS. I'm talking about when
It is. All the time. Especially within editors. We probably consume more of it than almost anything else. That's why a decent heuristic is so important because there's so much varied code out there. And way more old code than new code. So much old code. Nobody updates old code unless they're forced to, really.
Do you have real examples? Because TBQH most of the ones I can think of are caught at parse time inside babel/ts, such as |
// src/index.mjs
import 'react' // or any commonjs module without a modules build that uses ES6+ stdlib // webpack 4 config
module.exports = {
mode: 'development',
// note: does not exclude node_modules
modules: { rules: [
{ test: /\.m?js$/, exclude: /core-js/, loader: 'babel-loader' }
] }
} {
"presets": [[ "@babel/env", { "useBuiltIns": "usage", "modules": false } ]]
} This will write
( Note, there is actually a bug with preset-env in that it does not respect the |
@Kovensky |
@weswigham the problem here is not our modules transform (which would, at worst, just take sloppy code and accidentally make it strict); the problem is when the modules transform is disabled (which it should! you should never use transform-modules-commonjs with webpack). If the modules transform is disabled, then the output module kind should equal the input module kind. The input module kind is IIRC there were talks of changing the default of |
Y'all should default to something like |
The fact that it's not exactly the same is an important factor for me.
Defaulting to
But My concern with If we're trying to build a package repository like the npm registry, it makes zero sense for people to publish ESM as
|
Aren't we already a few years down this path, with authors publishing ESM as |
Apologies everyone for the inside baseball — @loganfsmyth I'm sure this'll be fine.
👍
If you could loop me in at that stage that'd be appreciated. I'll be advocating for an option to disable that behavior 😄 |
Some have, but to me this point seems like the sunk cost fallacy. We're talking about the an unspecificed amount of time into the future where we'll need to maintain this ecosystem. I don't think locking the ecosystem to unambiguous based on reliance of undefined behavior at the beginning makes the decision the right one for the future of the community. And again, if it turns out
Fair. Let's continue that discussion on Slack so as not to diverge this thread. |
Hey all, we've talked about this a bunch both here and in Babel's chat. I really appreciate all the discussions we've had. We've settled on postponing this change for the 7.x release because it will be such a large breaking change. The behavior will essentially be remaining like it is now, which is roughly how it was in Babel 6, assuming everything is a module and hoping for the best, and relying on users to configure Babel if things go wrong with that. It's important to not discard the good will of the community itself, especially given that we've been in beta for quite a while and people are unlikely to be expecting such large changes at this phase. I'd still like to seriously consider this as an early change for the next major release of Babel, which would allow us time to test it out and see how it feels to people, but we'll cross that bridge when we come to it. |
Alright, let's have this discussion since #6242 never really took off, and most of my TODOs in there have landed.
I am of the opinion that Babel 7 should parse
.js
files using thescript
parse goal, and.mjs
files with themodule
parse goal. In Babel 6, and 7 up to present, the default has beenmodule
. I also want to make clear that you can still easily setsourceType:module
in your Babel config to keep the current behavior, but the important question here is the default, because it influences many other parts of Babel's transformations.There are a few primary issues surrounding using
module
as the default:module
means Babel converts top-levelthis
toundefined
and injectsuse strict
, which can break when run against arbitrary filesimport
orrequire
, and it does so based on thesourceType
. Defaulting tomodule
means we may injectimport
into validCommonJS
modules, breaking them.babel-cli
we'd ideally add.mjs
to files that are modules, and.js
to scripts. If we default tomodule
then we'd potentially output.mjs
extensions on CommonJS files.These issues become more problematic when you consider that people are more frequently running Babel across arbitrary
node_modules
dependencies. Realistically this pattern is only going to increase in frequency for forseeable future.Node now follows this same pattern, with
.js
meaning CommonJS and.mjs
meaning ESM. There is potential for a field to land inpackage.json
that would allow toggling this behavior, and once that lands we could for instance consider asourceType: packageMode
option or something, but even with that the default in Node will remain.js
for CommonJS.Given all of this, it seems like it would be in the best interest of Babel to follow that same pattern, by requiring users to either use
.mjs
or opt into specific ESM behavior in their Babel configuration.