Parcel 2 beta 1

📦 Parcel 2 beta 1 - improved stability, tree shaking, source map performance, and more! 🚀

The Parcel team is super excited to release the first beta of Parcel 2 today! This marks the first Parcel 2 release that’s more stable than our nightly and alpha releases, and our commitment to avoid changing most user facing APIs. Please try it out and give us your feedback on GitHub!

It’s been a while since our last alpha release, and there has been a ton of active development on Parcel 2 in the meantime. We’ve been focusing on stability, performance, and reliability as we prepare for our stable v2 release, but we’ve managed to sneak in a few new features too!

Tree shaking

#

Building a production JavaScript compiler is extraordinarily challenging. We’ve been working on Parcel’s tree shaking implementation since 2018, and it has improved tremendously since then. Parcel 2 enables tree shaking by default, and we’ve recently been using it to deploy some very large applications in production at Atlassian and Adobe.

Parcel’s tree shaking implementation is unique among bundlers. Aside from supporting ES modules as many other tools do, Parcel also supports tree shaking CommonJS natively. While some libraries are now offering ES modules, most of the code on npm is still written in or transpiled to CommonJS before it is published. CommonJS can be difficult to statically analyze, like much of JavaScript, and we’ve put a huge amount of effort into making this transparent. We can statically analyze in a majority of cases, and automatically bail out and wrap the module in a function when it performs unsafe operations. We’ve discovered and fixed many bugs and edge cases in our tree shaking implementation over the last few months, and tested it extensively. We’d love to hear how it works on your application! Please report any bugs you find.

Parcel now generates source maps for tree shaken bundles as well. This was a limitation since the initial tree shaking release, and a major challenge for us. Since tree shaking does not simply concatenate files together linearly, it was difficult to combine source maps in the correct way. Instead, we now store ASTs in our cache, and combine them together instead. This preserves location information as part of the AST nodes, which can be used to generate a final source map at the end as part of code generation.

In addition to source maps, location information also allows us to give more accurate error messages. We are now able to show detailed code frames for errors such as importing a non-existent export from a module and more.

HUGE thanks to Niklas Mischkulnig for all of his work on improving the tree shaking compiler in Parcel. 🙏

Source maps

#

Source maps can be quite CPU and memory intensive to generate, especially when combining source maps from many files. We previously used the Mozilla source-map library to do this, but ran into performance issues on large bundles.

To address this, we’ve implemented our own library for combining and manipulating source maps. It’s a native node module, written in C++, that’s purpose-built for Parcel’s use cases. It utilizes FlatBuffers for serialization between workers and to the cache, which dramatically reduces the cost of generating and parsing that we had previously seen with JSON. Overall, it’s ~20x faster than the JavaScript implementation at combining source maps together. As an example, a bundle that previously took 3 seconds to generate the source map now takes just 175ms!

In addition to the native node module, there’s also a Web Assembly build of the same library, which allows it to be used in environments like web browsers. While not as fast as the native bindings, it’s exciting that the same code can be reused rather than needing to maintain a pure JS implementation as well.

Thanks to Jasper De Moor for all of his amazing work on source maps in Parcel! 🥳

Content hashing

#

We have supported long term caching in Parcel via content hashed file names since v1.7.0. These content hashes have historically been generated by hashing each individual file contained within the bundle. This ensured that changes to any file in the bundle caused the file name to update, and invalidate browser and CDN caches. However, it did not take into account cases where Parcel runtime code itself changed, rather than source code. This could happen when upgrading the version of Parcel, or a plugin that ran later (e.g. a minifier).

Parcel now generates hashes based on the final content of the bundle, after all packaging and minification has been done. This means that even if runtime code injected by Parcel changes, or your minifier changes the way it compiles your code, the content hashes will now update properly.

This was a challenge to implement, because bundles may reference other bundles as part of the code. Since the final names won’t be known until after the code has been generated, Parcel now inserts placeholder references into the content of the bundle, rather than the final bundle names. At the end, these are replaced with the final names as they are being written to disk.

In addition to more reliable content hashing, Parcel now avoids the cascading invalidation problem in many cases. This issue is well covered in the blog post linked above, but essentially since bundles may reference other bundles by content hashed file names, when a leaf bundle updates, all of the bundles leading to that bundle must also update in order to bust the cache. This leads to sub-optimal cache performance.

Instead of directly referencing bundles by full content hashed name, Parcel now includes a manifest in each entry bundle. This manifest includes a mapping of stable bundle ids to final content hashed filenames. Rather than referencing other bundles directly, only the bundle id is included. When a bundle further down the tree updates, invalidation no longer needs to cascade to intermediary bundles because the bundle id is stable. Only the entry bundle (containing the manifest) and the bundle that changed need to update. This can improve the cache hit rate significantly.

Thanks to Maia Teegarden and Will Binns-Smith for their work on content hashing in Parcel 2!

Resolver diagnostics

#

Parcel now includes improved error reporting when it cannot find a module you’ve referenced. This includes a code frame stack showing you exactly where the error occurred, and any intermediary files that led to the issue.

For example, the below screenshot shows an error that would typically only include the first line (Failed to resolve ‘invalid-entries’ from ‘./src/index.js’) and possibly the first code frame if you’re lucky. However, this doesn’t tell you where the error actually occurred. In this case, the invalid-entries module does exist, but it points to a non-existent file in its package.json. Parcel now displays a second code frame for the package.json, pointing to the exact line that caused the underlying issue.

Upgrading

#

Upgrading from the previous alpha release of Parcel 2 should be fairly straightforward, but there are a few things to be aware of.

Documentation

#

We’ve been working on a new documentation website for Parcel 2! It’s still very much a work in progress, but we plan to cover everything from getting started with building a basic application or library with Parcel, to more advanced features, recipes, and building your own plugins. Please check it out and give us your feedback!

Stability

#

As mentioned earlier, this is the first beta release of Parcel 2. This means it is more stable than a nightly or alpha release, but some changes are still expected before the fully stable release. In particular, beta means we are not planning on changing most user facing APIs such as configuration formats (in package.json and .parcelrc) and CLI arguments. Some changes are still expected to plugin APIs before the first release candidate, however we do not expect major changes at this point.

Try it out!

#

If you’ve been waiting to try out Parcel 2, now would be a great time! You can install it by running yarn add parcel@next. If you need assistance, you can ask questions on GitHub discussions, and if you run into bugs, please report them on GitHub issues. You can also find me @devongovett on Twitter. We’re really excited to hear your feedback!