In fact, the current situation is even more dire. In the old days, we would be deeply suspect of any 3rd party library that tried to bring in its own dependencies. The few indirect dependencies that ended up compiled into our products were subjected to the same scrutiny as the direct dependencies that required them. Contrast that to your current “packages-lock.json” file or “node_modules” directory.
Let me tell you about my “node_modules” directory. For a vanilla Vue.js SPA project with recommended webpack/babel build pipeline, configured with Vuex and Vue Router, Vuetify, plus 16 additional dependencies that I intentionally added for UI features, running “npm install” creates 509Mb of dependencies across 1185 packages.
What are these 1185 dependencies? I don’t know. What are they doing? I can’t afford the time to find out. They mostly seem to be dev dependencies, so although they could be up to all kinds of no good, it’s mostly me that’s at risk (but by extension, any of them could probably try to inject code into the final bundle, which would be executed by end-users). I am also nervous that some of these dependencies (which I didn’t put there), throw deprecation and security warnings when I do an “npm install”.
I could also moan about cumulative weeks lost to babel or webpack misconfiguration thermonuclear explosions, or about the misery of trying to use Chrome dev tools to debug babel’s version of async-await code.
Perhaps the strangest realization I’ve had over the last two years of diving head first back into web development is that the typical frontend developer doesn’t seem to realize there’s anything wrong with the current situation. It’s as if there’s an utter generation gap between the developers who suffered (and fixed) poor tooling in the 1990s, and those suffering through it today.
For now, let me draw a line under the modern Node.js/webpack/babel SPA build pipeline by saying that IMO it may be the worst dev experience I’ve encountered so far, with dramatic consequences for my productivity.
The Solution (?)
There must be a better way. Why can’t we build a real SPA using vanilla ES6 in the web browser? Ruby on Rails creator DHH agrees:
Preemptive defense: I know bundling tools like webpack will always be able perform some additional optimization, like tree-shaking, but I could also argue that the root problem there is bloat due to poor dependency discipline.
Taken together, this means that in theory we should be able to write a pure ES6 SPA today without Node.js, a “bundler” or a “transpiler”. Inspired, and in a fit of frustration, I decided to try to port my entire SPA codebase back to ES6. It didn’t quite work out (yet). The high-level reason is that it turns out that not only have we collectively used our Node.js build pipeline to solve the original three problems, but we’ve gone further and solved some other problems too, and our frameworks and tools now have some inconvenient assumptions built-in.
Consider any code or dependencies that try to access information about the Node/Webpack execution environment. The webpack “module” API and the Node “util” and “process” APIs do not exist (and would not make sense) in code running in a web browser. In a conversion to browser ES6, you need to eliminate any dependency on these utilities.