Front End JS Development Without Transpiling Your Source Code

Using Google Chrome, ES modules, and import maps

Fabiano Taioli
Jul 9 · 9 min read
Photo by Farzad Nazifi on Unsplash

What is Transpiling?

Transpiling is indenting the process of traducing some source code from a programming language and producing the equivalent code in another programming language (or two different dialects/versions of the same language).

In front end browser developing, transpiling is largely used to develop an application using language other than JS and which has the original code translated to JavaScript (for example TypeScript, CoffeeScript, Closure, Java, C#, etc.).

Another use case is to translate your native JavaScript code from one version to another (ECMAScript version) so you can use features that are not yet supported by browsers (or some of them).


The Cost of Transpiling

Using transpiling during development can be really tempting; all those feature-full languages and syntactic sugar to choose from!

All of them come with the promise to make you develop better apps with fewer bugs in less time. But are these promises true? And what’s the cost?

JavaScript has a reputation of a bad language, especially to build complex and big apps. But JavaScript has grown a lot in recent years and gets better and better with every new language specification release (ECMAScript version).

Browser adoption of new features is more rapid than it used to be and you don’t have to wait long to see an important new feature supported by Google Chrome and other browsers.

In the end, today’s JavaScript is a powerful and solid language.

JavaScript is an interpreted dynamic language. Compared to compiled languages, such as TypeScript, Java, Closure, C#, etc., you don’t have to wait for compilation to see your application running and debug it.

This can be seen as a negligible time when you start to dev your new app, but it is going to grow and grow as your app gets bigger and more complex. If you choose to transpile, you lose this advantage, and not only this advantage.

Another problem with transpiling is that the code the browser runs is not the code you have written. So, debugging and optimizing performance can become really tricky. Yes, you have source maps, but it’s not the same thing.

Source maps map positions in the code, but they don’t map how two variable names relate to each other. Optimization can make the compiled source code not match the behavior you’d expect from the code you wrote.¹

Google Chrome Inspector is one of the most powerful and efficient software debuggers currently available and it’s truly a shame to add unnecessary complexity to it with source maps and to not unleash its power directly on the code you have written.

After many projects based on transpiling, used during development, I can assure you that working directly on your raw code in Inspector (without source maps mediations or other translations) is a much better experience.

Today, it is possible to write, run, and debug your front end JavaScript code directly in Google Chrome and transpile it only at production and delivery time.

So, you say it would be better to develop my front end without transpiling but how I can do this? How I can use non-ES-ready dependencies? And what if I want to have static typing check?

Let’s see an example using React/JSX — The Endgame app.


ES Modules in Google Chrome

Today, Chrome has no problem understanding import/export in your JavaScript code. You can split your application into ES module files and directly run it on the browser without any problems.

The problems arise when you want to use a third-party library.

The de facto standard of JavaScript dependency management comes from package management in Node.js.

You can use a package utility such as Yarn, npm, etc., to require your libs and have the theme available in a local folder (usually node_modules).

Unfortunately, not all available packages are ES-ready. The majority of packages use the Node.js module syntax (they use “require” not “import”). Even ES-ready modules probably depend on non-ES ones.

To bypass this inconvenience and be able to directly use all of the existing packages, we can transpile our dependencies in equivalent ES modules (which we call web modules).

This transpiling only has effect on dependencies (not our source code) and is executed once only when we add a new library.


ES Overlay

Not all dependencies can be automatically translated to ES modules. In some case, we have to program how to port the lib to ES module (manually naming the exports).

To do this, we create middleware packages that import the desired lib (any valid Node.js lib) and export it as ES.

Then, we use rollup to transpile the ES middleware dependencies into web modules, ready to be used in Chrome.

To make this step easy, we have developed a couple of utility scripts: overlay and web- modules (feel free to adapt them to your needs).

Chrome loads ES module with the bare name ‘react’ from the web_modules folder because of the import map declared in HTML. An ES overlay module is created and used to build the web modes for packages which are not ES ready.

Note that the ES overlay is not necessary for packages that are already ES compatible. For a list of already compatible packages consult the pika list.


Overlay Script

The first script overlay (script source code) is used to initialize our middleware ES modules.

For example, in our project we want to use React, so we start adding it using Yarn to our dependencies:

At this point, the two dependencies are stated in package.json and are locally available in node_modules but we are not able to use them in our ES modules, yet.

Code like this would not work because React is not an ES module and we have not told the browser where to search for it.

Now, we’re going to make an overlay for the two libs.

The utility does two things:

  • Create middleware packages files inside the @es folder. Every package is composed of a package.json and an index.js. These files are good as is (by default they export everything from the original dep), but in some case s you have to edit the index.js and manually specify a named import/export.
  • Add the overlayed packages to package.json under the “webmodules” attribute.

Every middleware ES module is composed of a package.json file which declares the original dependencies in an index.js file, which imports/exports all the original dependency names.

For example:

This form is valid for the majority of packages. For certain packages you may be forced to manually declare names to import/export.

Web Modules Script

Now that we have declared our ES modules, we want them transpiled to usable ones. To do this, we run a web modules script (script source code).

The source code from this script is a hack of the pika/web project².

The difference from pika is that we can manage every kind of dependency, not only the ones that are ES-ready.

The script reads info about web modules that need to be transpiled from package.json.

Then, use the Yarn link functionality (you can easily change it to use npm if you prefer) to make ES middleware modules (generated by an overlay script) available to Node scripts, linking them inside node_modules.

Finally, the script runs rollup to transpile the modules and outputs them into the web_modules folder.


Tell Chrome Where to Find Modules

At this point, we have our dependencies ready to be used inside web_modules in a correct ES modules format.

But how will Chrome know where to search from them, when we import using bare specifiers (e.g. import React from ‘react’)?

We are going to use a new (still experimental) Google Chrome functionality, called import maps ³ ⁴.

To enable it, open Google Chrome’s flags config page (by typing this URL: chrome://flags) and enable ‘Built-in module infra and import maps’.

Using import maps, you can tell the browser where to find bare imports, declaring them in a special script tag in your HTML.

For convenience, our web-modules script automatically updates the import maps tab in all HTML files.

Sample of import map declaration in index.html

The Serve Script

We need a web server to make all the files available to Chrome and run our app.

We create a serve script which uses Browsersync to start a local web server and publish our project to localhost:3000.

If you have a look at serve.js below, we have mapped the ‘/web_modules’ URLs to our web_modules folder.


What About Using JSX

JSX is a syntax sugar to write HTML-like statements in JS files, instead of directly calling the React createElement function. Files that use JSX need to be transpiled.

To use JSX but keep our project’s main files non-transpiled, we isolate our JSX usage to what we call views files.

We create a views folder and put our JSX files inside. Then, we add a script that transpiles all the JSX files and puts the output (valid ES modules) inside a ./tmp folder.

The .tmp folder is declared in Browsersync as it was a secondary root folder and so paths not found in main root (app folder) are searched for in the .tmp folder.

By doing this, we can now import our transpiled JSX files into the main non-transpiled source code.

It’s important to understand that the transpiling will happen only when we change something in JSX and only for JSX files.


Final Project Structure

  • .tmp: Temporary folder, not revisioned. Contains transpiled JSX files. Searched by the Browsersync server as secondary root.
  • @es: Contains our middleware ES modules used to translate non-ES Node modules to ES ready for the web. Revisioned folder. Generated by overlay scripts and can have custom code when needed. Folder content is linked inside the node_modules folder by a Yarn link (or npm link).
  • app: Contains application source files and assets. All files are revisioned. The folder is the main root published by the Browsersync server. All source files here are not transpiled.
  • scripts: Node.js script files used as utility tools to manage the project. The directory is revisioned.
  • views: JSX revisioned files. JSX is here transpiled into the .tmp folder
  • .babelrc: Babel configuration file. Contains configurations of JSX transpiling. Note that Babel is used only for JSX.

The rest of the content should be obvious.


Running the Example App: Endgame

Quite simple:


Take It to Production

This article targets the development phase of your app. But what about publishing it for production?

Imports maps are not currently available for use in production; you’ll have to transpile your project to a more supported JavaScript form. But this is not a hard thing to do.

You can use Babel, together with rollup or webpack, to have your source code transpiled to a target browser functionality level as usual. Transpiling for production is not an issue; continuously transpiling during the development is the problem.


What If I Want Static Type Checking

Believe it or not, it is possible to have TypeScript check your native JavaScript source code and have all of the TypeScript advantages in your IDE without abandoning raw JavaScript.

I’m currently doing this and I’m planning a quick how-to, covering TypeScript in JSDoc and Atom integration, soon.


Conclusion

Developing and directly seeing your raw code run on Chrome is a real pleasure.

Chrome Inspector is an incredible debugger and bypassing source maps makes it unbeatable.

Optimize your code and place your breakpoint without fear. Saving your modified code to see the result in the browser, without waiting for any additional build time, is great.

With the right setup you can even validate your code with TypeScript and have your IDE complete your code without renouncing pure JavaScript.

Drawbacks? You have to create and maintain the ES overlay modules for packages not natively ES-ready, but it really is only a small effort.


Source Code

The endgame example project is available on GitHub:

https://github.com/FbN/endgame


Citations and Links

[1] Matt Zeunert: How do source maps work?
https://www.mattzeunert.com/2016/02/14/how-do-source-maps-work.html

[2] Pika Web Project
https://github.com/pikapkg/web

[3] Google Chrome Import Maps Status
https://chromestatus.com/feature/5315286962012160

[4] Google Chrome Import Maps Design Specifications
https://chromestatus.com/feature/5315286962012160

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade