How Babel helped to save 710kb from a production bundle and how it’s so much more than esnext compiler

conor hastings
codeburst
Published in
4 min readJun 25, 2017

--

medium recommends using an image to grab your attention, i think this one does. i don’t know if it’s high resolution like they said it should be but i like it.

I’ll start this off by saying the client side bundles at my company are way too big, like really big, like upsettingly big.

I make myself feel better knowing they used to be way bigger, our apps are mostly used in modern browsers on ~modernish computers, and whatever other excuse I can come up with to not feel like I’m failing every moment of the day.

A lot of the performance talk on the internet (at least the stuff I run into) focuses on big sweeping changes, where we take a 7mb monolithic app and code split it into and walk outside and a stranger hands us a hundred dollars on the street, but my world doesn’t work this day. I like to focus on making the smallest changes that can make the biggest immediate improvements, sometimes you make the sweeping changes but usually time just doesn’t permit it.

We use a lot of svg icons in our React applications, and a lot of them are shared between applications, so we’ve wrapped some up in a shared library that is used by ~5 applications. You might imagine that `application a` is using 40 of these, `application b` is using 15 of these, and so on and so forth, you get the point, or maybe you don’t, usually it takes me a long time to get to no point at all.

Anyway, back to the svgs, since we’re not distributing esmodules right now, i noticed that although our apps were importing the icons like this:

import { Chair } from "@socialtables/icon-svg";

We would end up bundling all 226 (and growing) icons in the library. My first idea was just to push people towards doing direct imports as follows:

import Chair from "@socialtables/icon-svg/dist/chair";

but in that case all it takes is one person messing it up to end up bloating the bundle.

Webpack 2 is getting better here with warnings for bundle size but we still need to address the root issue, where I want to use syntax one but end up with the output of syntax two going into to my bundle.

To achieve this I wrote a babel plugin to find imports using syntax 1 and convert to syntax 2 style imports.

const componentToFileMapping = require("./component-mapping");module.exports = function replace() {
const visitor = {
Program(path) {
const file = path.hub.file;
const imports = file.metadata.modules.imports;
imports.forEach(imp => {
if (imp.source === "@socialtables/icon-svg") {
imp.specifiers.forEach(spec => {
const imported = spec.imported;
const local = spec.local;
const binding = file.scope.getBinding(local);
binding.referencePaths.forEach(refPath => {
const type = refPath.node.type;
if (imported && imported !== "default") {
const fileName = componentToFileMapping[imported];
const name = refPath.hub.file.addImport(
`@socialtables/icon-svg/dist/icon-components/${fileName}`,
"default",
imported
).name;
refPath.replaceWith({ type, name });
}
});
});
}
});
},
ImportDeclaration(path) {
if (path.node.source.value === "@socialtables/icon-svg") {
path.remove();
}
}
}
return { visitor };
}

I’m sure I’ve made mistakes here and would love feedback if you see some but at the heart of it we simply search the program ast for non default imports of icon-svg components and replace them with default imports to the specific component file (a lot of inspiration for this came from babel-plugin-lodash). This helps us save on bundle size without having to change developer workflow, and not allowing one “mistake” to shoot us in the foot. (Quick note, I super duper wish I knew about astexplorer.net when I made this so uhh, definitely use that if you’re going to do something similar.

In one of our applications this improved the bundle size by 710kb. The bundle is still oversized and needs a lot of work, but the ultimate point I think I’m trying to make is people think of babel as an esnext => current javascript compiler when it can really be so much more.

without babel plugin
with babel plugin

I’ve recently been experimenting with using babel to turn runtime decision making in branched React components into build time decisions generating different bundles, and hope to write about this soon.

If you read this far, thank you, because I don’t think I would have 🌟🆒✌🏻

--

--