Four years of JavaScript churn: Mopidy.js 1.0

by Stein Magnus Jodal

Yesterday, I released v1.0.0 of the Mopidy.js JavaScript library. Even though the library serves as the basis for most Mopidy web clients and has been in active use since 2013, this was the first release in almost four years.

Four years is quite a long time in the world of JavaScript.

In the beginning of October, I stopped bundling Mopidy.js with the Mopidy Python project and thus had to update the Mopidy.js documentation. While doing so, I tried to run the test suite, written using Buster.JS, which has been abandoned for a few years. To no avail, npm and Yarn were not able to install the library’s development dependencies.

After four years of standing still while the fast-moving world of JavaScript sped on, I seemed forced to spend some time on modernizing the library and it’s development tooling to ensure that it would be maintainable going forward.

I asked a couple of coworkers for advice on what to replace Buster.JS with that could test a library in both Node.js and the browser, and what they would use instead of Browserify today. I was quickly pointed towards Jest for testing and Parcel for zero-configuration builds.

Porting the full test suite–three times the size of the library–to Jest was work, but mostly mechanical work. The only part I needed to read a bit up on was Jest’s mocking capabilities compared to Sinon.JS.

Modernizing development tooling

After getting the test suite running again the modernization continued.

JSHint and a list of rules were replaced with ESLint and Airbnb’s style guide.

All code was reformatted with Prettier. This required almost no configuration, once figuring out how to make ESLint and Prettier stay friends. However, the experience of getting an entirely consistent code style without any effort is priceless. I can accept most differences from my previous styles in exchange for not having to do the work myself and not having to enforce that my contributors and coworkers do the same. Formatters like Prettier, Black, gofmt, and rustfmt are here to stay and will probably be an integral part of all new programming languages.

The Mopidy.js code was originally hand-written ES5 using all the hard-learned tricks of the trade from David Herman’s splendid book Effective JavaScript. At least it was splendid in 2013. Now it was converted to a modern ES6 class with a single click in VS Code, passing all tests bar one: you cannot instantiate an ES6 class without the new keyword.

Removing Buster.JS and JSHint reduced the project’s Grunt setup a bit. The move from Browserify for browser builds and Uglify for minification to Parcel lead to the removal of Grunt entirely, and the replacement of a monitor height or two of Grunt config with the command parcel build src/mopidy.js.

Reducing runtime dependencies

The time had now come to runtime dependencies.

The faye-websocket package was replaced with the ws package, which made it possible to replace our own Node.js/browser compatibility layer for WebSocket usage with isomorphic-ws.

Since the last release of Mopidy.js in 2015, ES6’s Promise implementation has become quite universally available. Thus, when could be replaced with Promise, shrinking the minified library from 42 kB to 12 kB.

BANE, an event emitter library that was part of the now-dead Buster.JS testing tool, was replaced with the EventEmitter implementation from Node.js’ standard library. Parcel helpfully and entirely automatically included the EventEmitter implementation from Node.js as part of the web bundle, without having to add a dependency on one of the npm packages that has extracted this lib from the Node.js standard library.

Making demo applications

Once everything was modernized, I added two demo applications to the project.

The web-based application is served by Parcel’s web server. The configuration needed? Add parcel examples/web.html as the start script in package.json and I was done: yarn start now runs the demo web app on http://localhost:1234, with automatic code reloading on source changes.

For the Node.js console application, I quickly remembered that while promises are better than callbacks, they can still quickly become quite mind-boggling to work with. Enter async/await, spreading like wildfire from language to language the last few years, and fully available in Node.js since 2017. You just have to remember to create all the promises you need, and only then await them. This makes it possible for the promises to be fulfilled in parallel instead of sequentially, avoiding that the sequential round trip times to the server add up.

Somewhere around here I read a bit up on TypeScript and tested out porting the library to TypeScript with great help from VS Code. Due to Mopidy.js’ tiny static API and quite large dynamic API, automatically generated based on the API description retrieved from the server, this didn’t seem a worthy path to go down this time around. However, the experience was a good one, and I’ll probably revisit TypeScript in the future on other projects.

Finally, the Mopidy.js docs were moved from the Mopidy docs to the Mopidy.js project and updated accordingly. Being used to writing docs in Sphinx, Markdown’s lack of features like generating a table of contents can feel limiting. Of course, several VS Code extensions can automatically generate a ToC and keep it up to date every time you save your file.

Wrapping up

In the late 1990s, I tweaked JavaScript-based web calculators. In the 2000s I surfed with JavaScript disabled and required apps we built to work without it. From 2011 through 2015, I spent a small majority of my time in frontend JavaScript. Since the summer of 2016, I’ve done very little, if any, JavaScript.

It seems that my relationship with and usage of JavaScript varies like the tide, even if on another time scale. After a while away, JavaScript as of late 2018 seems to be in a lot better place than in 2015. The language is nicer. The chore of project setup and maintenance is reduced. Tooling is better, and way better integrated. I’ve felt the JavaScript fatigue before, but from my perspective, it seems like many things are stabilizing in a quite good place.

I’d go as far as claiming that the exercise of modernizing Mopidy.js and its development tooling was, at times, fun and inspiring.

Here’s to four more years of Mopidy.js. Then I might be back with a port to WebAssembly, implemented in Rust, or a language yet to be designed. We’ll see!