The JavaScript Ecosystem is Delightfully Weird

A landscape vaguely inspired by Salvador Dali's The Persistence of Memory  including a melted balloon clock, craters, a disembodied hand, mountains, and a JS logo in the sky
Image by Annie Ruygt

Note, I’m not saying that JavaScript is weird, though it definitely is weird. But that’s not the point of this blog post.

Bear with me, instead of starting with how JavaScript ecosystem is weird, I’m going to start with why the JavaScript ecosystem is weird.

Historical Background

Less that 10 years ago, JavaScript sucked bad. It had no imports, no classes, no async, no arrow functions, no template literals, no destructuring assignment, no default parameters, no rest/spread parameters. And the environment it predominately ran in, namely the browser’s DOM, sucked too. JQuery made it suck less. It still sucked, but was — at that point in time — relatively sane.

Bundling JS to run in the browser was the first sign of weirdness. In that process you would also want to both minimize and tree shake the source, and perhaps even code split. In general the process involved reading a number of JavaScript sources as input and then producing one or more JavaScript sources as output. This meant that the code you were executing wasn’t the code you wrote. Sourcemaps helped.

Then CoffeeScript came along. Instead of writing in JavaScript, you would write in a language which was compiled into JavaScript. This is a bit different than languages like Elixir and Kotlin which compile into the same byte codes as another language, CoffeeScript actually compiles into the other language. C++ started out this way.

Then came ECMAScript 6 in 2015. JavaScript improved rapidly in the next few years. This eventually mostly displaced CoffeeScript, but presented a different problem: for a while the implementations were not keeping up so transpilers like Babel came along that compiled current and future versions of JavaScript into older versions of JavaScript that ran on supported environments. Currently esbuild is rapidly rising in popularity as a Javascript bundler/transpiler.

Along the way, emscripten came along which compiled actual machine code into a subset of JavaScript, though these days the new target for this tool is generally Wasm.

Lately the pace of innovation in JavaScript has slowed, and JavaScript implementations are doing a better job of keeping up, so you would think that the need for transpilers would be waning, particularly on the server side where there is no need for bundlers. But that’s not happening. And the reason why is an interesting story.

Nobody Writes JavaScript Any More

OK, the title above is clearly hyperbole, but I’ll describe a number of the many ways that people aren’t writing JavaScript any more.

If you write a Rails application, you write it in Ruby. If you write a Django application, you write it in Python. Phoenix, Elixir. Lavavel, PHP. Rails gets a lot of flack for doing magic using meta-programming, and Elixir has macros, but all of the above stay within the boundaries of what can be done by the language.

JavaScript, however, is different. While it nominally is standardized by EMCA TC39, if you are using a popular framework like Next.JS, Remix, or Svelte you are not coding in ECMAScript as standardized by ECMA TC39. Four examples:

  • Once upon a time, nearly 20 years ago, the ECMA committee standardized E4X that enabled XML to be treated as a data type. This lost favor, got deprecated and archived. Years later what once was Facebook (now Meta) had a similar need and invented JSX. It differs from E4X in that it compiles into JS.
  • One thing that ECMA TC39 has never standardized is type annotations. Undeterred, Microsoft did it anyway with TypeScript. It, too, compiles into JS.
  • Svelte has their own compiler that even deliberately misuses the JavaScript label syntax to enable marking a statement as reactive.
  • It doesn’t stop there. When a bundler/transpiler encounters an import statement, they don’t necessarily presume that the file being imported is JavaScript or even any of the variants mentioned above. If configured properly and you want to import a CSS or PNG file, it will happily do so for you.

I mentioned earlier that Rails gets a lot of flack for its use of meta programming. Nobody bats an eye at any of the “abuses” of the JavaScript language mentioned above. The JavaScript ecosystem is a Big Tent party.

“use server”;

The latest abuse of the bundler is by React Server Components (RSC). First demoed with express, it is now adopted by Next.js.

What “use server" and "use client" do, other than being a valid JavaScript statements that do absolutely nothing, is change the meaning of the code that follows them. This has gotten mixed reviews, but in my mind is very much in the spirit of "use strict"which also changes the meaning of the code that follows.

While JSX often compiles to JS, the Server React DOM APIs enable compilation to HTML. RSC goes a different way, and compiles into a stream of tagged JSON. This is all very transparent to you, but what it does enable is a different style of programming. One that many are comparing to PHP and even Rails:

It is not clear to me whether these comparisons are meant in a positive way, but I will say that from my perspective it is a very good thing.

From a fly.io perspective, RSC enabling an Update (Refetch) Sequence is very much of interest. We’ve always been especially shiny for frameworks that benefit from geographic distribution, like Elixir’s LiveView, Laravel’s Livewire and Ruby on Rail’s Hotwire. We want those kinds of frameworks to succeed, because the better they do, the more valuable we are. Now we can add React’s RSC to that list.

Returning to the topic at hand, the fact that such a feature is only made possible through cooperation with bundlers — a statement tantamount to saying a change to the JavaScript language itself — is profound and, dare I say it, delightful.

Another Dimension

Dan Abramov gave a talk at RemixConf entitled React from Another Dimension:

In Dan’s talk he imagines an alternate universe in which React was first implemented in the late 90s on the server and still managed to converge to where it is today. During the talk he launches a Windows 95 emulator and runs Internet Explorer (specifically, IE6) with React. He even manages to get nine out of ten steps working using that operating system and browser combination.

The mind bending parts of this presentation are where he first utilizes use server to implement a client side form action, and then later launches a client side alert from the server using use client.

And he closes by saying that this requires new generation routers and new generation bundlers.

And to think all of this is made possible by the fact that the JavaScript you write not only isn’t the JavaScript you run, but under closer examination isn’t even JavaScript at all.