My thoughts about configuring JS environment
I recently started writing React component library for a sole purpose of learning. I thought that process of turning already written code into a module will be straightforward. Could I be more wrong? It took me a total of a few days to fully set up a good environment for my codebase and understand what I'm doing. In this post I'm going to sketch the landscape of obstacles I encountered along the way.
What is a JavaScritp module?
To begin discussion about JS modules, it would be wise to first describe what actually those modules are. First of all, there isn't (unfortunately) one standard way implementing module. For the sake of simplicity I will describe only some of them, which in my opinion are the most important. Those will be CommonJS
, UMD
and ESModules
.
UMD
A Universal Module Definition (UMD) is probably a most comfortable module handling for older readers (those who remember the glory days of JQuery). UMD module is one-file library that is meant to be included via <script>
tag in index.html
. It exposes it's functionalities by attaching an object to globalThis
(for example window.$
or window.React
). As it turns out there are still some projects that take advantage of this approach.
CommonJS
CommonJS are Node specific modules, which reader might associate with module.export
and require()
statements. An immediate question for me was: why should I care for Node modules, while creating purely browser-based library? The answer was quite obvious. Namely, Compatibility with older environments and build tools. As it turns out there are still many projects that use those modules.
ESModules
ESModules are native JavaScript modules that can be resolved by every modern browser. Keywords used within ESModules are import ... from "..."
and export {...}
. Using those modules allows bundlers to apply tree-shaking
mechanism, which ensures that only needed code will be imported.
Toolchain for writing a library
When writing a library, we aim to create the most general solutions possible to fit a wide range of use cases. For this reason, I decided to use a set of tools to organize my code and ensure a consistent style. I used TypeScript to describe interfaces of exported objects, Prettier and ESLint to maintain consistency, and Babel + Webpack to transpile and bundle the entire project. The downside of this toolchain is that every connection between tools requires a plugin, leading to nearly 500 packages just to parse my code — and I haven't even included any testing libraries yet.

Despite the connection between ESLint and Babel in the diagram, we actually need to configure TypeScript to work with Babel.
Configuring tools
What I find controversial about this pipeline is the extent to which each tool is configurable. ESLint, the most commonly used linter for JavaScript, has almost 200 parameters that can be set. If I had to choose between Python's Black linter, which has exactly zero parameters, and ESLint, I would certainly prefer Black. The Webpack configuration file for Create React App
, a boilerplate for creating React apps, at the time of writing this article, has over 800 lines. Even TypeScript, the language I chose, has its own configuration file. Last but not least, it is common to add logic via environment variables to the bundler, allowing it to behave differently for development and production builds. Additional variables might also be added to compile differently for various module types.
Conclusions
Given the amount of ecosystem and tool-specific knowledge required to prepare a custom environment for a TypeScript project, I think it can be overwhelming and discouraging for less experienced developers to experiment with the language. The current state of front-end development almost inevitably forces developers to use tools they don't fully understand.