[#]: subject: "Writing Javascript without a build system" [#]: via: "https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/" [#]: author: "Julia Evans https://jvns.ca/" [#]: collector: "lkxed" [#]: translator: " " [#]: reviewer: " " [#]: publisher: " " [#]: url: " " Writing Javascript without a build system ====== Hello! I’ve been writing some Javascript this week, and as always when I start a new frontend project, I was faced with the question: should I use a build system? I want to talk about what’s appealing to me about build systems, why I (usually) still don’t use them, and why I find it frustrating that some frontend Javascript libraries require that you use a build system. I’m writing this because most of the writing I see about JS assumes that you’re using a build system, and it can be hard to navigate for folks like me who write very simple small Javascript projects that don’t require a build system. #### what’s a build system? The idea is that you have a bunch of Javascript or Typescript code, and you want to translate it into different Javascript code before you put it on your website. Build systems can do lots of useful things, like: - combining 100s of JS files into one big bundle (for efficiency reasons) - translating Typescript into Javascript - typechecking Typescript - minification - adding polyfills to support older browsers - compiling JSX - treeshaking (remove unused JS code to reduce file sizes) - building CSS (like [tailwind][1] does) - and probably lots of other important things Because of this, if you’re building a complex frontend project today, probably you’re using a build system like webpack, rollup, esbuild, parcel, or vite. Lots of those features are appealing to me, and I’ve used build systems in the past for some of these reasons: [Mess With DNS][2] uses `esbuild` to translate Typescript and combine lots of files into one big file, for example. #### the goal: easily make changes to old tiny websites I make a lot [of][3][small][4][simple][5][websites][6], I have approximately 0 maintenance energy for any of them, and I change them very infrequently. My goal is that if I have a site that I made 3 or 5 years ago, I’d like to be able to, in 20 minutes: - get the source from github on a new computer - make some changes - put it on the internet But my experience with build systems (not just Javascript build systems!), is that if you have a 5-year-old site, often it’s a huge pain to get the site built again. And because most of my websites are pretty small, the _advantage_ of using a build system is pretty small – I don’t really need Typescript or JSX. I can just have one 400-line `script.js` file and call it a day. #### example: trying to build the SQL playground One of my sites (the [sql playground][5]) uses a build system (it’s using Vue). I last edited that project 2 years ago, on a different machine. Let’s see if I can still easily build it today on my machine. To start out, we have to run `npm install`. Here’s the output I get. ``` $ npm install [lots of output redacted] npm ERR! code 1 npm ERR! path /Users/bork/work/sql-playground.wizardzines.com/node_modules/grpc npm ERR! command failed npm ERR! command sh /var/folders/3z/g3qrs9s96mg6r4dmzryjn3mm0000gn/T/install-b52c96ad.sh npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/surface/init.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/avl/avl.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/backoff/backoff.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_args.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack_builder.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_trace.o npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channelz.o ``` There’s some kind of error building `grpc`. No problem. I don’t really need that dependency anyway, so I can just take 5 minutes to tear it out and rebuild. Now I can `npm install` and everything works. Now let’s try to build the project: ``` $ npm run build ? Building for production...Error: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:71:19) at Object.createHash (node:crypto:130:10) at module.exports (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/util/createHash.js:135:53) at NormalModule._initBuildHash (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:414:16) at handleParseError (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:467:10) at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:499:5 at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:356:12 at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:373:3 at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:214:10) at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:221:10) at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:236:3 at runSyncOrAsync (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:130:11) at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:232:2) at Array. (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:205:4) at Storage.finished (/Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:43:16) at /Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:79:9 ``` [This stack overflow answer][7] suggests running `export NODE_OPTIONS=--openssl-legacy-provider` to fix this error. That works, and finally I can `npm run build` to build the project. This isn’t really that bad (I only had to remove a dependency and pass a slightly mysterious node option!), but I would rather not be derailed by those build errors. #### for me, a build system isn’t worth it for small projects For me, a complicated Javascript build system just doesn’t seem worth it for small 500-line projects – it means giving up being able to easily update the project in the future in exchange for some pretty marginal benefits. #### esbuild seems a little more stable I want to give a quick shoutout to esbuild: I [learned about esbuild in 2021][8] and used for a project, and so far it does seem a more reliable way to build JS projects. I just tried to build an `esbuild` project that I last touched 8 months ago on a new computer, and it worked. But I can’t say for sure if I’ll be able to easily build that project in 2 years. Maybe it will, I hope so! #### not using a build system is usually pretty easy Here’s what the part of [nginx playground][6] code that imports all the libraries looks like: ``` ``` This project is also using Vue, but it just uses a `