-
-
Notifications
You must be signed in to change notification settings - Fork 724
fix(transformer/top-level-statements): should not inject statements after non-import statement #12463
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
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
CodSpeed Instrumentation Performance ReportMerging #12463 will not alter performanceComparing Summary
Footnotes |
2d4a442 to
546e38d
Compare
abfd316 to
6aaab99
Compare
|
I'm not entirely sure about this. PolyfillsThe test case that's overridden here does have a rationale for why it is as it is. I don't think it's misled us exactly. It links to this Babel issue: babel/babel#12522 If an import low down in the file is a polyfill, the polyfill should execute before So this PR as it is fixes one problem, but may create another. Strictly speaking, I think the correct behavior should be to treat
Or:
The latter might make for more performant code, because browser will find all the CommonJSWe're probably not handling CommonJS correctly either. Presumably any What to do?I can see 2 options here:
@Dunqing what do you think? |
Merge activity
|
…fter non-import statement (#12463) * close #12460 Injecting top-level statements after the last import statement is unsound because once there is an import statement in the last line, it would be broken if other code relies on the injected statements. Learn more to see the following example. I suppose the reason we did this is that we had a test case that misled us. I overrode that test with the expected output. For example: Input: ```js import { observer } from 'mobx-react-lite' import { useFoo } from './useFoo' export const BazComponent = observer(function BazComponent_() { const foo = useFoo() return ( <> {foo} {bar} </> ) }) import { bar } from './bar' ``` Before output: ```js import { observer } from "mobx-react-lite"; import { useFoo } from "./useFoo"; export const BazComponent = _s(observer(_c = _s(function BazComponent_() { _s(); const foo = useFoo(); return /* @__PURE__ */ _jsxs(_Fragment, { children: [foo, bar] }); }, "useFoo{foo}", false, function() { return [useFoo]; })), "useFoo{foo}", false, function() { return [useFoo]; }); _c2 = BazComponent; import { bar } from "./bar"; import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; var _s = $RefreshSig$(); var _c, _c2; $RefreshReg$(_c, "BazComponent$observer"); $RefreshReg$(_c2, "BazComponent"); ``` After output: ```js import { observer } from "mobx-react-lite"; import { useFoo } from "./useFoo"; import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; var _s = $RefreshSig$(); export const BazComponent = _s(observer(_c = _s(function BazComponent_() { _s(); const foo = useFoo(); return /* @__PURE__ */ _jsxs(_Fragment, { children: [foo, bar] }); }, "useFoo{foo}", false, function() { return [useFoo]; })), "useFoo{foo}", false, function() { return [useFoo]; }); _c2 = BazComponent; import { bar } from "./bar"; var _c, _c2; $RefreshReg$(_c, "BazComponent$observer"); $RefreshReg$(_c2, "BazComponent"); ``` The difference is that `import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; var _s = $RefreshSig$();` has injected after `import { useFoo } from "./useFoo";`
6aaab99 to
7c2d2c6
Compare
Wow, thank you for a deeper understanding of this. First of off, I need to point out that the example code of your linked issue is not the same as the test being overridden. Example of babel/babel#12522: import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);The overridden test: // https://github.com/babel/babel/issues/12522
ReactDOM.render(
<p>Hello, World!</p>,
document.getElementById('root')
);
// Imports are hoisted, so this is still ok
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import ReactDOM from 'react-dom';There is an essential difference between them! The first case we can handle well in terms of this PR solution. But the second one, yes, we will meet a problem with that, but in my opinion, users always know the polyfill should always be put on the first line, so why I am saying that I prefer to do nothing for now. Putting this case aside, I think the first option is nice to do, but I prefer option(2) for now. The following is what I thought on this:
Anyway, don't get me wrong. I don't disagree with you! |
True. But the test case does relate to that issue. The babel authors amended the original test case to cover the hardest case where polyfills are imported later in the file. Yes, it's common practice to put imports of polyfills at the top of the file. So this import order problem only occurs in an edge case where polyfills are lower down, and as you say there's a workaround. But still... this edge case is legal, and I think in an ideal world, we'd handle it correctly like Babel does.
You are right that SWC does not handle it correctly in the presence of polyfills: SWC playground. But personally I don't think we should follow SWC by default. We know SWC takes shortcuts and has edge case bugs in many places, and I've heard people complain about that. So I think we should be aiming to be more correct than SWC. In particular, some big companies care a great deal about reliability and correctness, and it's a selling point of Oxc over SWC. So... TLDR: In practice, the polyfill problem is going to be rare. So I agree with your arguments. I think it was right to merge this PR - it solves more problems than it creates. I'll create a backlog issue about the polyfill edge case. We may want to address it further down the line when we have time. |
Thanks! |

Injecting top-level statements after the last import statement is unsound because once there is an import statement in the last line, it would be broken if other code relies on the injected statements. Learn more to see the following example.
I suppose the reason we did this is that we had a test case that misled us. I overrode that test with the expected output.
For example:
Input:
Before output:
After output:
The difference is that
import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; var _s = $RefreshSig$();has injected afterimport { useFoo } from "./useFoo";