Julio Sampaio Software Developer, with an affinity and curiosity for everything else. 🤔💡

Deep dive into Rome: Linting, compiling, and bundling

7 min read 2049

Diving into the Rome Tool

Rome is a JavaScript toolchain created with the promise of unifying the frontend development. We all know how difficult and tricky it can be to manage many different tools for stuff like bundling, minifying, beautifying, compiling, etc.

From the same mind behind Babel and Yarn, and following the same centralizing idea that other languages like Rust have spread, Rome was born aiming to become the all-in-one tool.

You can find the updated list of supported languages here. By the time of this writing, Rome offers support for JavaScript, TypeScript, JSON, HTML, Markdown, and CSS.

List of Rome Languages Supported

In this article, we’re going to dive a bit deeper into this toolchain and see, with some nice examples, how it performs against the most common tasks every frontend developer must address.

Setup

Its installation is pretty simple. You can install it from NPM or Yarn. For this article, we’re picking Yarn as default:

yarn add rome
yarn rome init

However, if you want to go with NPM, simply run the following:

npx rome init

This will create a new Rome project and initialize it with some files (we’ll see them in detail in a minute).

This is the output generated by the previous command execution:

New Rome Project

We made a custom demo for .
No really. Click here to check it out.

Code formatting

For the next few tests, make sure to have a index.js file created and the following content in it:

var first=["Log","Rocket"],second=["Log","Rocket","Plus"],both=first.concat(second);document.getElementById("demo").innerHTML=both;

Indenting your code is simply necessary. Tons of plugins and most IDEs provide this functionality ad-hoc. And so does Rome.

Via the command line, you can format a given file by hitting the following command:

yarn rome format index.js

Since we created the project via Yarn, the yarn command must be added at the beginning of every Rome command.

That’s a simple minified code snippet in JavaScript. Minified files are great ways to test a format command since we expect the code to be “unminified”. After the command ends, you may see the following output:

var first = ["Log", "Rocket"],
        second = ["Log", "Rocket", "Plus"],
        both = first.concat(second);
document.getElementById("demo").innerHTML = both;

Linting

Perhaps one of the most interesting and used features of Rome is its linting. Through a very intuitive and clear syntax, it shows the list of errors for your JavaScript files by simply running the rome lint command.

Rome also keeps a list of the current supported Lint rules. If a validation rule isn’t working for you, make sure to refer to this list to check if the rule is there.

Let’s test it out! Take the following code as an example:

function abc() {
console.log("what's going");
}

Here, we have two visible issues, the code indenting, and a non-used function. Let’s see how Rome analyzes this code snippet by running the command:

yarn rome lint

The following is the output:

Rome's Lint Execution Output

Each problem is listed as bulleted points, being each point preceded by a red x sign. In our code, the two problems we’ve mentioned were found.

In green, you’ll find the suggested fixes, like the appropriate code formatting for our console.log().

To fix this, Rome gives us some great options. The first one we’ll dive into is the check apply.

This option is great because it allows Rome to auto fix all the problems that Rome knows well and is confident in fixing for us.

So, run the following command:

yarn rome check --apply

Rome will execute a bunch of reviews over the whole codebase. Yes, since we have one single file, that’s okay. However, in larger codebases, you must be careful to choose only the files you want to check.

To do that, just place the file name after the check command:

yarn rome check index.js --apply

Once the command is finished, you can see that the code was automatically formatted. However, a problem still remains with the console output.

Visualization of Non-used Function Problem

Since Rome can’t find a safe and automatic way to fix this, it’ll log the error at the console.

Don’t worry, there’s another option that can help with that. It’s the check review.

This is also the safest option since it allows us to choose how and which issues we’d like to fix. So, run the following command:

yarn rome check --review

Once finished, Rome will prompt a series of options for each one of the problems that it couldn’t solve itself. This way, you can decide how the fix may be addressed.

All Options to Fix Problem

That’s very interactive. Just select one of the options and hit enter. Rome will always provide a default option of doing nothing in case you want to fix it yourself.

Leave the first option selected and hit enter. In the end, you may see that your code has changed:

// rome-ignore lint/js/noUnusedVariables
function abc() {
    console.log("what's going");
}

Rome has automatically formatted the code and, due to our selection, added a comment before the function to ignore this validation in future lint executions.

Nice, isn’t it?

The VS code extension

Alternatively, you can install the VS code Rome extension to get live errors as you type and format when you save your files. That’s pretty useful, especially if you’re seeking more productivity.

For that, go to your VS Extensions Marketplace and search for Rome. There’s not a lot of options, so it’s going to be easy to find it.

Hit the Install button and there it is.

A Screenshot of Installing the Rome Extension

Rome automatically displays an error message over the function name alerting for the unused nature of the recently created function.

Rome vs Code Extension

That’s a lot better since you can see all the errors while programming. It’s also what most developers look for when picking a linter.

As soon as you call the function, the error disappears:

function abc() {
    console.log("what's going");
}

abc();

A TypeScript example

It also works well with other types of files, like TypeScript. Take the following TypeScript example:

function abc(param) {
console.log("Hello TypeScript")

if (param == "abc") { return true;}

setTimeout(()=>{console.log("timeout!")}, 100)


}

Then, hit the yarn rome check index.ts --review command and Rome will show the following prompt.

A List of Lint Problems

This time, we have a real fix being suggested (rather than just adding a suppression comment). See in green how the code is going to be after the suggestion is applied.

In blue, you can find the explanations about why the code you’ve typed is not right, as well as some important observations like if the fix is safe (and under what conditions).

Plus, every time Rome finds a good way to fix something, it shows the word “FIXABLE” at the top of the issue’s title.

At the end of the execution, the code will look like this:

// rome-ignore lint/js/noUnusedVariables
function abc(param) {
    console.log("Hello TypeScript");

    if (param === "abc") {
        return true;
    }

    setTimeout(
        () => {
            console.log("timeout!");
        },
        100,
    );
}

Exploring other lint rules

Let’s take the previous example and step it up a bit. Look at the following code:

function abc(param) {
const myConst = "abc"
var myVar = "abc"

try { console.log("Hello TypeScript") } catch (e) { e; e = new Exception(); }

if (param == "abc") { return true;}


}

Here, a couple of premeditated problems were placed:

  • The known, not used function and param
  • Constant and variable not used
  • Reassigning an exception within a catch block
  • Making use of an unknown/not imported class (Exception)
  • Comparing two strings with the wrong operator (==)
  • Code not formatted properly

Wow, that’s a lot. Let’s see what Rome tells us about our code by running the yarn rome check index.ts command.

List of Rome Problems

We’ve omitted the lint problems we’ve seen before, but the list is bigger than that. Well, that’s a good way to prove that Rome is powerful enough to deserve a place within your favorite linters.

Compiling

Rome also compiles your code. Let’s say you have some TypeScript code and want to compile it into JavaScript:

interface User {
    id: number;
    name: string;
}

class Account {
    id: number;
    name: string;

    constructor(id: number, name: string) {
        this.id = id;
        this.name = name;
    }
}

const user: User = new Account("LogRocket", 1);

console.log(user);

Most of this code isn’t understandable by JavaScript, which means that it needs to be converted prior to use. With Rome, simply run the following command:

yarn rome compile index.ts

And this will be the output:

class Account {
        constructor(id, name) {
                this.id = id;
                this.name = name;
        }
}

const user = new Account("LogRocket", 1);

console.log(user);

Rome configs

As you may have noticed, when you start a new Rome project, a new file called rome.rjson is autogenerated under the config folder.

This is the current content:

// For configuration documentation see http://romefrontend.dev/#project-configuration
name: "rome"
root: true

The first attribute is your project name, and the second tells Rome that it can search for nested projects existing into the current one in order to apply Rome rules.

This file is where Rome stores the customized configurations for the toolchain. And it’s also the place you may come when needing to fine-tune the way Rome is doing things.

You can edit the file directly or manage the configurations via the command line. For example, let’s say you want to change the name of the project and disable it as a root project. These are the equivalent commands for both operations:

yarn rome config set name "logrocket-rome"
yarn rome config disable root

The changes will be committed to the rome.rjson file as well.

If you want to ignore some specific paths or file extensions in your Lint configs, simply run the following command:

yarn rome config push lint.ignore "*.ts" "/build" "/output"

The syntax is pretty intuitive, so now your TypeScript files and everything within the build and output folders will be ignored by Rome linter. This is the rome.rjson contents after the above commands:

// For configuration documentation see http://romefrontend.dev/#project-configuration
name: "logrocket-rome"
lint: {
    ignore: [
        "*.ts"
        "/build"
        "/output"
    ]
}
root: false

Bundling

This an experimental feature Rome offers. Since there’s no intention of providing backward compatibility, be careful with its use.

Rome aims to provide a way to build a standalone JavaScript bundle file for a package of your application in substitution of what Webpack does today.

Let’s take the index.js example. Then, run the following command:

yarn rome bundle index.js bundle.js

The first argument is related to the source file to be bundled, while the second relates to the destination file name. It’s important to provide the name of a file that doesn’t exist yet.

Below, you can check the output of this command:

Generating a Bundle File

Note that three files are generated under the bundle.js folder:

  • index.js — this is the bundle file
  • index.js.map — the source map file
  • bundlebuddy.json — the map (in JSON) of all code files used to generate this bundle

And the generated bundle file contents will look something like this:

(function(res) {
    if (typeof module !== "undefined") {
        module.exports = res;
    }
    return res;
})(
(function(global) {
  'use strict';
  // logrocket-rome/index.js
  var ___R$logrocket$rome$index_js = {};
var ___R$$priv$logrocket$rome$index_js$first = ["Log", "Rocket"],
        ___R$$priv$logrocket$rome$index_js$second = ["Log", "Rocket", "Plus"],
        ___R$$priv$logrocket$rome$index_js$both = ___R$$priv$logrocket$rome$index_js$first.concat(
            ___R$$priv$logrocket$rome$index_js$second,
        );
    document.getElementById("demo").innerHTML = ___R$$priv$logrocket$rome$index_js$both;

  return ___R$logrocket$rome$index_js;
})(typeof global !== 'undefined' ? global : typeof window !== 'undefined' ? window : this));
//# sourceMappingURL=index.js.map

Conclusion

Rome is just at the beginning of its life, however, it’s promising enough to earn lots of attention and expectations from the community.

Rome also has some nice features that are still in the testing stage, like testing. Which will allow developers to run tests of files whose names are under the .test.* pattern. When you put it all together, you can spot how rich such an environment can become. You have linting, compiling, formatting, bundling, and testing altogether in one single place.

Make sure to follow up closely with the official docs of Rome to see what’s coming.

How about you? Have you tested Rome already? What are your thoughts?

Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.

LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.

https://logrocket.com/signup/

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — .

Julio Sampaio Software Developer, with an affinity and curiosity for everything else. 🤔💡

Leave a Reply