I love working with JavaScript, and I’m grateful for the people who make the tools without which I couldn’t work.

Still, I find myself puzzled every time I have to spend hours on plumbing instead of code, and reminds me of the fragility of the ecosystem.

⬇️
A couple days ago, a PR caused the bundle size to jump in one of the entry points of a project. This was odd, as the change was totally unrelated.

After looking into the built artefact, I noticed that it had code that wasn’t supposed to be here, and that it wasn’t even using.
For context, we’re talking about a Ruby project with JavaScript in it, so the Webpack + tooling is all custom and hand-maintained. No hidden scripts or services.

This is important to know for the rest of the story.
I look at my entry point and start climbing the dependency chain. I notice that the main module, which is a React component, imports two utilities.

These utilities are re-exported from a barrel, so that we can import them in a single line.

import { util1, util2 } from './utils'
Looking closer at the bundled code, it seems that the extra code comes from utils. As if everything re-exported module in the barrel was imported.

Odd, given that I remember setting up tree-shaking and ensuring it worked. Could something be coming in the way?
I look back at Webpack’s tree-shaking guide. One by one, I rule out every possible issue.

We do use ES modules everywhere.

We do use production mode when bundling.

We do have sideEffects set to false in package.json.

We do disable transpilation to CJS in our Babel config.
I remember some fuss around how to import or export ES modules, without reminding exactly why.

I start wondering, is it bad to re-export? Should it be done module per module instead of using `export * from ...ˋ?

The mystery thickens.
I research and find nothing. Not in Webpack’s docs, GitHub issues or StackOverflow.

It looks like the full barrel is imported as a single object. The bundle contains Object.defineProperty calls with the imported code.

But how, with CommonJS transpilation disabled in Babel?
I pair with a coworker to bring new eyes on the problem. We review everything, test with modules re-exported one by one, nothing works.

Until...
Until he notices something in my Webpack config file.

— "Why do you use ts-loder?", he asks.
— "Because part of the codebase is in TypeScript. I installed the TypeScript setup following the Webpack documentation."

But why would a loader be a problem?
— "You have @babel/preset-typescript installed, you might not need an extra laoder. Babel should be able to handle your TypeScript files."

Interesting. I installed the preset as Jest docs asked because it needs it to transpile TS before running. I didn’t think much about it.
But indeed, if it can transpile for Jest, babel-loader should be able to handle .ts and .tsx files without the need for another loader.

We comment out the ts-loader rule and add .ts and .tsx to the babel-loader rule.

We run a build.
It works. No extra code. The bundle contains only what we import. I’m feeling a lot lighter, and my bundle too.

Okay, so what the heck with ts-loader? The Webpack docs didn’t warn about anything in the TypeScript guide. What is it doing?
I think back of the CommonJS transpilation issue, which is known in Babel. Could ts-loder be doing it as well?

Okay. This loader uses tsc, the TypeScript compiler under the hood. Let’s look into that.
After digging a bit in the long list of TypeScript configuration options, I find that by default, the `module` option is set to "CommonJS" when the `target` is set to "es5".

This is my case.

This is why.
Via ts-loader, tsc was turning my code into CommonJS before Webpack performed tree-shaking. Because exported CJS modules are single objects, it couldn’t do anything and bailed.

Known issue, unknown culprit.
What’s the moral of this story? Hard to say. I’m definitely not blaming any of the aforementioned tools. I’m responsible, this is my codebase, and I should have looked deeper into the stuff I installed.

Yet, with so much moving parts, it’s so easy to make such mistakes.
It’s crazy to think that a simple tool can have such high repercussions. Especially when it’s recommended by official docs. I’m not talking about copy-pasting StackOverflow snippets here.

This isn’t just about dependencies. It’s about a flawed ecosystem.
Webpack and Babel are wonderful tools made and maintained by smart people who are taking on their own time to allow us to work.

But these tools exist because shipping efficient JavaScript to the browser is hard.
I don’t like plumbing, I don’t like having to learn tools so that I can ship code. It doesn’t make me a better engineer. Every time I mentored a back-end friend on front end, that’s always where they got confused.

Plumbing keeps us away from what matters.
The ecosystem is fragile. Too many tools with too many caveats and interoperability issues.

One unknown default setting can cause major issues. And this is keeping developers away from making software.
What I’m saying isn’t new, but this story illustrates it well. I hope the JavaScript ecosystem moves in the right direction for this, so that we can spend more time coding and less time piping.

Ah, and in the meantime, install bundlesize 😏 https://github.com/siddharthkp/bundlesize
You can follow @frontstuff_io.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled:

By continuing to use the site, you are consenting to the use of cookies as explained in our Cookie Policy to improve your experience.