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
WIP: Rust-based JS transformer using SWC #5537
Conversation
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Co-authored-by: Amin Yahyaabadi <aminyahyaabadi74@gmail.com>
Only used by original RPi 2 Model B, backward compatible with armv6, process.arch doesn't distinguish between them
… into realpath # Conflicts: # packages/utils/node-resolver-core/src/NodeResolver.js
|
Benchmark ResultsKitchen Sink ✅
Timings
Cold Bundles
Cached Bundles
React HackerNews ✅
Timings
Cold Bundles
Cached Bundles
AtlasKit Editor ✅
Timings
Cold Bundles
Cached Bundles
Three.js ✅
Timings
Cold BundlesNo bundle changes detected. Cached Bundles
|
Although I'm a fan of improving performance at any cost, the Babel ecosystem is absolutely massive. I believe that the majority of Parcel users on bigger projects will need to use a Specifically, Parcel could use just SWC's parser, create a plugin/transformation interface compatible with Babel, then print the AST again with Rust. I would think that the majority of the performance hit is from parsing and generation anyway (if not, this is probably not a worthwhile endeavor). |
It should be noted that this doesn't prevent you from using Babel in addition to SWC in Parcel. However, I think these days many projects might not need any custom babel stuff that SWC doesn't also provide. Preset env + JSX + TypeScript + a couple proposals (decorators, optional chaining, etc.) is all most projects need, and other popular Babel plugins can be ported to Rust for performance if needed as well. In the meantime, the fewer Babel plugins you run the faster your build will go, so offloading as much as possible to SWC will help even if you still have a couple custom plugins. As for doing only parsing and codegen in Rust but transformation in Babel, I don't think this will help as much as you think. Specifically, because SWC's AST is represented by Rust structures and not JS objects, you essentially have to pay the cost of converting between these, which is very slow. In fact, SWC has a JS-based plugin API that does just this, and it was only slightly faster than Babel the last time I tried it. Doing everything in Rust is MUCH faster. Additionally, parsing and codegen are actually not the slowest parts of Babel as evidenced by my recent performance related PRs (#4366, #4815, #4826), all of which replace various transformations with faster alternatives (still in JS) that don't rely on Babel. We've found the slowest part to be due to Babel's mutable AST, which makes traversal and scope tracking very slow. This PR is the next step - if we're going to be rewriting pieces of our infrastructure for better performance, why not also do so in a language that is more performance oriented? As I said above, I'm not totally sure this is the right path forward though so I appreciate the feedback. |
I agree that performance is a big part of any bundler, but personally, I don't care if my app takes 2 seconds or 30 seconds or even 2 minutes to build so much as I care that the build works reliably and supports any language features or extensions I may need. I suggest the default remain as-is as long as there are other parts of bundling that can be optimized more easily, simply because the JS transformer is arguably the most important part of a bundler and there will inevitably be bugs with SWC compared to Babel, at least initially. However, I will definitely test this once added, and it's absolutely a good path forward. Slowly moving away from pure JavaScript solutions is a good idea when optimizing for performance, and performance is always useful for a good developer experience. |
Babel is currently the focus because it is the slowest part of Parcel at the moment, by far. Whatever we do, Babel will of course still be supported automatically when a .babelrc or other Babel config is detected. This is only replacing Parcel's built in transforms. Also, if merged this would likely remain an experimental opt-in alternative plugin for some time while we test it out in the real world. |
…ormer # Conflicts: # packages/core/package-manager/src/NodeResolverBase.js # packages/core/package-manager/src/NodeResolverSync.js
…ormer # Conflicts: # packages/core/integration-tests/test/javascript.js
This is a WIP/POC of using a Rust-based transformer using SWC to replace the current JS transformer, for performance reasons. It works by passing the asset content to the native module, which returns the transformed code plus a list of dependencies. Overall it seems about 2x faster than the current JS transformer, but it would be much more if it also replaced Babel.
Most of the features are ported over, and it's passing most but not all of the JS tests - there are still a couple differences in e.g. ESM -> commonjs transformation to account for. Currently it's only for development mode as porting scope hoisting would be a much larger job. So in fact both the SWC and JS transformers are configured to run, but SWC will run without scope hoisting, and the JS transformer will run with scope hoisting enabled.
Although currently it only handles the job of the previous JS transformer, it could also take over the job of the Babel transformer in cases where there is no babel config. This includes transpiling syntax down to lower targets based on browserslist (SWC has an equivalent of Babel's preset-env).
It remains a question whether we want to go this route or stick with the JS version. Pros of sticking with JS include easier contributions for people who don't know Rust, ability to reuse ASTs in the case where custom Babel plugins are also being used, etc. Additionally, porting over scope hoisting will be a very large job, but at least we've already worked out most of the edge cases and have a good test suite so perhaps not completely unreasonable That said, the performance gains may be worth it. Open to opinions here!