The Maybe Future of Frontend Tooling

Disclaimer: I wrote this quite hastily in a little bit of spare time between the holidays. I hope you get the gist out of it. If not I’ll try to illustrate my idea with a more practical example in the future. Thank you! 👋

One thing which can be said for sure about JavaScript is that it has a rapidly evolving ecosystem and a diverse community. A big part of the JavaScript universe is dedicated to tooling. In the last year this topic got a little bit of bad publicity being too overwhelming. Remind yourself that you don’t need to keep track of every change and every new idea. Learn when you have time to learn and use what is useful for you, not for others. ✌️ I personally like this ecosystem a lot, because the people behind it are so creative — even if not every idea is worthwhile in practice. With this article I’d like to put my own vision of the future of JavaScript tooling into the ocean of ideas about frontend tooling.

Some History

First things first some background about me: I started following JavaScript and Frontend tooling since the introduction of Grunt. Grunt helped me a lot to learn Node and the terminal and it helped me taking my frontend project to the next level. It was not without failures so I kept track of every popular solution which came after that: Gulp, “npm scripts”, Broccoli and so on. We used a CSS preprocessor quite early and also adopted Babel when it was “quite” new. Currently we are happy users of TypeScript and Webpack. At our company we use a wrapper around all these tools so we can change the frameworks in the background without changing a lot of our interface to use them. Most changes aren’t noticed by our users. Ignoring building our projects for a moment the other parts of our tooling story focused around linters, tests and managing documentation and the project in general (changelogs, deploying, …).

The biggest impact in the JavaScript tooling world in the last years probably was the introduction of Babel. (For very good reasons!) Being a little bit “controversial” (because of its complexity at first glance) I really like Webpack and its loaders concept, because it allows me to solve problems I couldn’t solve before. But there is one thing which is lesser known which I think will have the biggest impact around tooling in general in the long run: the language server protocol. This protocol was developed by MicroSoft for TypeScript. It powers several features you know and love from advanced IDEs like refactoring, code completion and so on, but in an abstract way so that it can be easily adopted by other languages and so that it can be easily integrated in many different editors.

The Maybe Future

Linters

MicroSoft developed several language services implementing the language server protocol for VS Code (e.g. for HTML and CSS) and others do the same (e.g. for Rust and Java). Interestingly it seems that even support for linters in VS Code is implemented as a language service. IMHO this is an interesting idea! Think about that: You’re a library author which wants to move to a new major version and you want to deprecate a function. Currently a library author usually puts a console.warn into this function and if someone uses this function the developer sees this warning in the console. But you know this can be easily overlooked when this happens at runtime. That’s why we want to analyse our code statically and use linters or something like flow or TypeScript in the first place. However currently we would need to install a different dependency for that and configure it manually (like tslint-react which is not maintained by the React developers themselves).

Imagine that: TypeScript can easily pick up typings published either by the original library authors when they reference declarations inside the package.json in the typings field or by community maintained packages or in several other (more manual) ways. Wouldn’t it be nice if library authors could add custom linter rules and reference them in the package.json so they are automatically picked up by our linter language service? In the same way the TypeScript compiler picks up type declarations?
Or think about that: Most linter have auto-fix features nowadays. Wouldn’t it be nice if we could auto-fix our deprecation warning? E.g. because our function is renamed from foo to bar or because our import { foo } from ‘some-lib/foo’ is now published as a standalone package like import { foo } from ‘some-lib-foo’? This wouldn’t be possible with just a console.warn. This is similar to a codemod, but IMHO more friendlier to use. Besides deprecation warnings we could use a framework specific language service for helping the user follow best practices (warn if they use the API in a “wrong” way) or custom knowledge which can’t be covered by a generic language service.

Look for example into the source code of vscode-html-languageservice. It contains hard coded knowledge about Angular directives. It would be nicer if this would be a standalone language service. There even is a standalone language service for Angular developed by the Angular team. But you need to install it separately. Again… it would be nicer if this could be just picked up by a more generic language service just because you have Angular as a dependency and imported it in your project.

Compiler Plugins and Macros

Custom linter rules and IDE features like refactoring for specific libraries aside there is another “thing” which is currently maintained decoupled from the libraries themselves. These “things” I have in mind are compiler plugins like Babel plugins. The most popular probably is the JSX syntax for React. But many libs have Babel plugins to improve themselves. Two examples: there is a glamor specific Babel plugin to hoist styles for performance improvements and there is a Babel plugin for styled-components to add server side rendering and better debugging experiences. Wouldn’t it be nice if these could be just published with its original libraries? It would be nice if that could also be just referenced in your package.json so it can be easily picked up, if you import glamor or styled-components. We probably need to solve some more challenges to get so far. E.g. we don’t want conflicts between plugins. A solution to that can be sweet.js macros which can be imported locally and also allow the extension of our JavaScript syntax. It is possible to write JSX as a macro for example.

Two more problems come to my mind. First: I want prettier everywhere. If I write my own syntax extension like JSX I want to be responsible for pretty printing my syntax, too. prettier currently supports JSX and flow and soon TypeScript which is great, but it would be even nicer, if it can be extended. Second: I want static typing everywhere. Depending on your syntax extension this can be quite hard. TypeScript supports JSX, but again this is backed in support in TypeScript and only possible, because JSX is so popular. It wouldn’t be so easy to add TypeScript support to your syntax extension.

Loaders

Writing this another piece of my build pipeline comes to my mind which I’d like to support with static typing natively: WebPack loaders. If I use the url-loader for loading images I currently need to add this declaration somewhere, so TypeScript knows about the return type.

declare module ‘*.jpg’ {
const content: string;
export default content;
}

However this becomes troublesome when you have dynamic return values or interfaces. For example we use translations formatted in message format saved in .properties files like this:

hello.world=Say hello to "{label}"!

Currently we precompile these files to JavaScript and to TypeScript declarations which look like this:

/**
* `en_US`: Say hello to "{label}"!
* `de_DE`: Sag hallo zu "{label}"!
*/
export function helloWorld(data: { label: string }): string;

This is awesome. We can now just import helloWorld from our precompiled file and have code completion, proper type checking and we even see the translations if we hover over helloWorld in VS Code. But… we have to create intermediate files to solve this. We cannot just write a loader for WebPack and load our translations directly.

It would be nice if our loaders could speak to the TypeScript compiler directly.

Conclusion

I think language server will help us bring more IDE features to editors around us. I could imagine embedding language servers into our browser dev tools or in web editors like JSFiddle. You can even get some IntelliSense into GitHub today with the help of a Chrome extension!

In the beginning I said that WebPack is a little bit inconvenient for beginners, because of its complexity in the configuration step. That’s way frameworks like next.js are so popular. They avoid a lot of the configuration by using the file system as the API. When we can do the same for linters, compiler plugins, macros and language servers we will help library authors and consumers to get the best out of our web technologies.

I hope we can bring the different worlds of loaders, type systems, syntax extensions and pretty printers more closely to each others by finding the right low level APIs in the future.

Like what you read? Give Donald Pipowitch a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.