Parcel v2.12.0

Parcel v2.12.0 introduces support for macros, which enable you to generate code at build time using regular JavaScript functions. It also includes a new online REPL, improves our CSS bundling support, adds configuration options to fine tune your app's code splitting, and improves performance and memory usage.

Macros

#

Parcel v2.12.0 introduces support for macros. Originally implemented in Bun, Macros are JavaScript functions that run at build time instead of being bundled. The value returned by a macro is inlined into the bundle in place of the original function call. This allows you to generate constants, code, and even additional assets without any custom plugins.

Macros are imported using an import attribute to indicate that they should run at build time rather than being bundled into the output. You can import any JavaScript or TypeScript module as a macro, including built-in Node modules and packages from npm.

This example uses the regexgen library to generate an optimized regular expression from a set of strings at build time.

import regexgen from 'regexgen' with {type: 'macro'};

const regex = regexgen(['foobar', 'foobaz', 'foozap', 'fooza']);
console.log(regex);

This compiles to the following bundle:

console.log(/foo(?:zap?|ba[rz])/);

As you can see, the regexgen library has been completely compiled away, and we are left with a static regular expression!

Parcel's implementation of macros also enables you to generate functions at build time, and even emit new assets. For example, macros can generate CSS which will be statically extracted into a CSS bundle as if it was imported from the JS file.

This example accepts a string of CSS and returns a generated class name. The CSS is added as an asset and bundled into a CSS file, and the JavaScript bundle only includes the generated class name as a static string.

index.ts:
import {css} from './css.ts' with {type: 'macro'};

<div className={css('color: red; &:hover { color: green }')}>
Hello!
</div>
css.ts:
import type {MacroContext} from '@parcel/macros';

export async function css(this: MacroContext | void, code: string) {
let className = hash(code);
code = `.${className} { ${code} }`;

this?.addAsset({
type: 'css',
content: code
});

return className;
}

The bundled output of the above example would look like this:

index.js:
<div className="ax63jk4">
Hello!
</div>
index.css:
.ax63jk4 {
color: red;
&:hover {
color: green;
}
}

This is basically a CSS-in-JS library with build-time static extraction in just a few lines of code! And since macros are just regular JavaScript or TypeScript functions, you can create any API on top of this that you want, for example generating CSS using an object API or theme values. We are excited to see what innovations this unlocks – for CSS-in-JS and much more. Check out the documentation to learn more.

REPL

#

Parcel now has a REPL where you can try it out right in your browser! Niklas Mischkulnig has been working on the REPL over the course of several years, and we're excited that it's finally integrated into our website. The REPL features a full code editor, file browser, and support for most Parcel features including watch mode, dev server, hot module replacement, and more.

Under the hood, the REPL is powered by cutting edge web technologies including Web Assembly, service workers to host the dev server, a web worker backend for multi-core processing, and IndexedDB for package manager caching. It can even install packages from npm by running a compiled version of Yarn for the browser.

The REPL is great for playing around with Parcel and seeing how things are compiled. It's also great for creating shareable bug reproductions. Check it out!

Lightning CSS powered bundling

#

Parcel has used Lightning CSS to transform CSS files by default for a while. Now Parcel uses it for bundling CSS files as well. This brings support for modern CSS features like @import with cascade layers, improved support for importing with media and supports queries, and more correct handling of complex CSS ordering issues.

Manual shared bundles

#

By default, Parcel automatically code splits your code for maximum cache efficiency. Common modules that are shared between multiple parts of your application are extracted into a shared bundle so that they can be loaded and cached separately by the browser. But sometimes, you might know more than Parcel about how a certain part of your app should be loaded, choosing to over-fetch up front in order to optimize later. In this release, we're introducing support for manual shared bundles to address this.

Manual shared bundles let you configure Parcel to ensure that certain modules are always bundled together no matter where they are used. For example, you could create a vendor bundle that loads a set of commonly used libraries, or group together the assets for a specific route into a single bundle. This is specified using globs to match against file paths, as well as additional options to group by root asset or type, split into multiple parallel http requests, and more. Check out the documentation for all the details.

Manual shared bundles are best applied after trying Parcel's default bundling algorithm and doing real-world performance testing. When you have a specific performance issue that can't be solved by changing your code structure, use manual shared bundles to override Parcel's default behavior. Manual shared bundles can also be useful when porting code from other bundlers like webpack.

Performance improvements

#

Like most Parcel releases, v2.12.0 also includes some performance improvements. In this release, we've improved our core graph data structure to reduce memory usage by ~52%, and improve performance of writes by ~5%. For a real world, very large app, this amounts to an ~800MB reduction in memory usage with no regression in startup, build, or shutdown times! If you're interested in learning how we made these optimizations, check out the PR. And for more background on our custom graph data structure backed by SharedArrayBuffer, we now have some documentation.

Also in this release, we've improved the way Parcel stores the graph to disk to work in small chunks during idle time in development rather than blocking the process from shutting down while the graph is being serialized. Instead of serializing the entire graph at once, we now track which parts have been modified and only re-serialize those. This should improve the perceived shut down performance of Parcel for very large projects.

Thanks!

#

Thanks to everyone who contributed to this release! Check out the full changelog for all of the other bug fixes and improvements.