DEV Community

Cover image for Tree-Shaking Without a Bundler? Here's How Snowpack Does It!
Fred K. Schott for Pika

Posted on

Tree-Shaking Without a Bundler? Here's How Snowpack Does It!

Until Snowpack came on the scene, application bundling was the only way to do tree-shaking (dead-code elimination) on the web.

But Snowpack is the unbundler. It runs only on your dependencies without ever touching your application code. So how could it tree-shake?

Here's a quick post about how we made this production optimization happen for faster production apps, thanks to some essential help from the RollupJS team 🙌

The Setup

Bundlers "tree-shake" your web application by finding and removing unused code, shaving off valuable bytes when you deploy to production.

Since Snowpack never touches your application code, how could we do the same? We had to get creative.

Snowpack v1.0 added an initial "scan" phase behind the --optimize flag. It runs a quick scan of your app to learn how you use each dependency.

... and that's it! We pass that info to our internal installer (powered by Rollup) and eliminate any dead code at install time.

The Challenge

"Passing that info to our Rollup-powered installer" was an interesting challenge: How do you tell Rollup that code in your top-level entrypoints is unused?

This was an unusual request, and something the Rollup team (rightfully!) didn't want to build into the bundler itself.

We created an issue on the Rollup repo to outline the problem, and with @lukastaegert's help came up with a really clever solution via a new plugin...

issue screenshot

Here's how we solved it: We create an in-memory "envelope" wrapper for Rollup of every dep & fill it with the exact imports used by your app.

Rollup sees the envelope, reads the scanned imports to the real dep, & then automatically tree-shakes out everything not found in the ✉️
You can see exactly how it works here, coming in at only ~50 LOC.

plugin screenshot

This all runs behind the production-only --optimize flag, so you can add & remove imports as you develop your app without worrying about tree-shaking or needing to re-install as your code changes.

But even when optimizing for production, just reading & scanning your application code should be a hell of a lot faster than reading, scanning AND BUNDLING your app code with a traditional bundler (so you can still expect fast production deployments).

What's Next?

We're experimenting with replacing our current import scanner with the even faster es-module-lexer by @guybedford, a WASM-powered scanner written in C.

GitHub logo guybedford / es-module-lexer

Low-overhead lexer dedicated to ES module parsing for fast analysis

ES Module Lexer

Build Status

A JS module syntax lexer used in es-module-shims.

Outputs the list of exports and locations of import specifiers, including dynamic import and import meta handling.

A very small single JS file (4KiB gzipped) that includes inlined Web Assembly for very fast source analysis of ECMAScript module syntax only.

For an example of the performance, Angular 1 (720KiB) is fully parsed in 5ms, in comparison to the fastest JS parser, Acorn which takes over 100ms.

Comprehensively handles the JS language grammar while remaining small and fast. - ~10ms per MB of JS cold and ~5ms per MB of JS warm, see benchmarks for more info.


npm install es-module-lexer

For use in CommonJS:

const { init, parse } = require('es-module-lexer');
(async () => {
  // either await init, or call parse asynchronously
  // this is necessary for the Web Assembly

Thanks for Reading!

Enjoyed this post? Throw Snowpack a 🌟on GH to help spread the word:

GitHub logo pikapkg / snowpack

The near-instant build tool for modern web apps.


What is Snowpack?

Snowpack is a modern, lightweight toolchain for web application development. Traditional dev bundlers like webpack or Parcel need to rebuild & rebundle entire chunks of your application every time you save a single file. This introduces lag between changing a file and seeing those changes reflected in the browser, sometimes as slow as several seconds.

Snowpack solves this problem by serving your application unbundled in development. Any time you change a file, Snowpack never rebuilds more than a single file. There's no bundling to speak of, just a few milliseconds of single-file rebuilding and then an instant update in the browser via HMR. We call this new approach O(1) Build Tooling. You can read more about it in our Snowpack 2.0 Release Post.

When you're ready to deploy your web application to users, you can add back a traditional bundler like Webpack or Parcel. With Snowpack you…

Top comments (2)

zhusjfaker profile image

export * from './a';
export * from './b';

import { TestBComponent } from './c';

snowpack will install a.ts & b.ts

treeshake is not work

nickytonline profile image
Nick Taylor

Fred, I really enjoyed your discussion about Snowpack on the Syntax podcast. Snowpack looks very promising! ❄️

play pause Syntax - Tasty Web Development Treats