Skip to main content

JavaScript Static Analysis with ESLint and Biome

Escrito por:
0 minutos de leitura

If you've been doing JavaScript for a bit, you've probably heard of ESLint as the tool of choice for static analysis of JavaScript code. But have you heard of Biome? The uprising Prettier contender for code formatting is making waves in the JavaScript ecosystem. Biome extends further from code formatting and provides a code quality linter, making it an alternative to ESLint.

First, a gentle introduction to code formatting and quality as tools and concerns for JavaScript developers. As you guessed, maintaining code quality and adhering to proper code formatting conventions is helpful to the success of any software project, especially when you're working in teams.

Code quality touches on how the code behaves during runtime. High-quality code is efficient, easy to understand, and bug-free. On the other hand, code formatting, which is part of code style, involves the visual organization of code, making it readable and understandable.

// Convention of preferred code formatting and quality
function add(a, b){
  return a + b;
}

// An example for what would be considered as
// poor code formatting, making it hard to read
// and error-prone
function add(c,d){return c+d;}

In the JavaScript ecosystem, several tools can help developers maintain code formatting and code quality. Among these tools, ESLint and Biome have emerged as popular choices for developers looking to streamline their codebase.

Introduction to Biome and ESLint

ESLint is an open source JavaScript linting utility that helps developers produce high-quality JavaScript code. It allows developers to discover problematic patterns or code that doesn’t adhere to certain style guidelines. It's highly customizable and can be configured to enforce various coding standards.

ESLint is very popular and pluggable, meaning you can add custom rules to enforce your coding standards. For example, you can enforce the use of semicolons at the end of each statement, or single quotes instead of double quotes for strings.

If you browsed through a JavaScript project, you might have seen an .eslintrc.js file that looks like this:

// .eslintrc.js
module.exports = {
    "rules": {
        "semi": ["error", "always"],
        "quotes": ["error", "double"]
    }
};

Biome, on the other hand, is a relatively new tool in the JavaScript ecosystem that focuses on code formatting and code quality linting. You might heard or used Prettier already. This is where Biome comes in. It's written in Rust and is designed to be faster. Biome is mostly opinionated but provides a small set of configurable options. Here's an example of a biome.json file:

{
  "javascript": {
    "formatter": {
      "indentWidth": 2,
      "lineWidth": 100,
      "trailingComma": "none",
      "semicolons": "always",
    }
  }
}

Currently, Biome is compatible with JavaScript, TypeScript, JSX, and JSON files.

The shift towards Biome from Prettier and ESLint's focus on code quality

Traditionally, many JavaScript developers have relied on Prettier for code formatting. However, with the advent of Biome, a shift is occurring. Biome's ultimate goal is speed and performance, and it's written in Rust, which is up to 35 times faster than Pettier when formatting over 170k lines of code spread across 2k files. Speed can make a significant difference in the development process when working in a large codebase.

Biome code formatting and code quality tool performance

This doesn't mean that ESLint is being phased out. On the contrary, ESLint remains a powerful tool for maintaining code quality. While Biome focuses on the appearance of the code, ESLint focuses on the code's behavior. These tools complement each other, with Biome taking care of code formatting, and ESLint ensuring the code adheres to best practices and is free of common errors.

Both Biome and ESLint are helpful tools in a JavaScript developer's toolkit. They each play an important role in maintaining code quality and formatting, leading to a more reliable and maintainable codebase.

Using Biome in a JavaScript project

Biome provides its functionality based on a command-line tool that performs static code analysis. One key advantage is its customizability, allowing developers to define their own rules or use an existing set of default rules. This flexibility helps enforce coding standards and best practices in any JavaScript project.

Here's an example of how you might use Biome with JavaScript:

# Install Biome as a development dependency
npm install --save-dev @biomejs/biome

Note that, upon installation, Biome will need to run an install script to fetch the necessary binaries for your platform. Then, continue:

# Initialize a new Biome configuration file
npx @biomejs/biome init

This creates a biome.json configuration file at the root of your project. You can then customize this file to define the rules and settings for your project but the default settings are as follows:

{
	"$schema": "https://biomejs.dev/schemas/1.5.3/schema.json",
	"organizeImports": {
		"enabled": true
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true
		}
	}
}

As you can see, the formatting rules are missing from the default configuration, which might be incompatible with your project's Prettier configuration. For me, the .prettierrc.js file looks like this:

module.exports = {
  printWidth: 100,
  tabWidth: 2,
  singleQuote: true,
  semi: false,
  trailingComma: 'none',
  useTabs: false,
  bracketSpacing: false
}

To make Biome as closely compatible as possible with the above Prettier code formatting rules, you can add the following to the biome.json file:

{
  "formatter": {
    "enabled": true,
    "indentStyle": "space",
    "indentWidth": 2,
    "lineWidth": 100,
    "quoteStyle": "single",
    "trailingComma": "none",
    "semicolons": "asNeeded",
    "bracketSpacing": true,
    "arrowParentheses": "asNeeded"
  }
}

We can now run Biome's formatter on our codebase:

npx @biomejs/biome format ./src

This results in only 1 formatting rule that is opinionated to Biome and not configurable, relating to function spacing:

./src/Utils.js format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ℹ Formatter would have printed the following content:

     2  2     3  3module.exports = {
     4    │ - ··parseUrl:·function(urlToScan{
        4 │ + ··parseUrl:·function·(urlToScan{
     5  5if (urlToScan === undefined) return urlToScan
     6  6    ····· │
    12 12return urlToScan
    13 13 │     },
    14    │ - ··trimUtmParams:·function(urlToTrim{
       14 │ + ··trimUtmParams:·function·(urlToTrim{

Another option is to ditch the Prettier configuration entirely and use Biome's formatting rules.

Combining formatting and linting with Biome

Biome also provides a linter that can be used to enforce code quality rules and could be considered an alternative to ESLint. The linter can be configured to enforce a set of default rules or custom rules defined in the biome.json configuration file.

To run the Biome linter, we'll use the biome check command to test for any violations of the rules defined in the biome.json default recommended set of rules.

# Run Biome on your code
npx @biomejs/biome check ./src

In my code repository, where I tried running Biome, I got the following output:

./src/Audit.js:30:7 lint/complexity/useOptionalChain  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Change to an optional chain.

    29if (
  > 30 │       vulnerableAudit.details &&
       │       ^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 31 │       vulnerableAudit.details.items &&
       │       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    32 │       vulnerableAudit.details.items.length > 0
    33 │     ) {

  ℹ Unsafe fix: Change to an optional chain.

     28  28     29  29if (
     30     │ - ······vulnerableAudit.details·&&
     31     │ - ······vulnerableAudit.details.items·&&
         30 │ + ······vulnerableAudit.details?.items·&&
     32  31 │         vulnerableAudit.details.items.length > 0
     33  32 │       ) {

./src/RenderJson.js:27:7 lint/complexity/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Prefer for...of instead of forEach.

    25 │       vulnerabilitiesResults.details.items.length > 0
    26 │     ) {
  > 27 │       vulnerabilitiesResults.details.items.forEach(vulnItem => {
       │       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  > 28 │         vulnerabilitiesCount += vulnItem.vulnCount
  > 29const vulnInfo = this.formatVulnerability(vulnItem)
  > 30 │         vulnerabilities.push(vulnInfo)
  > 31 │       })
       │       ^^
    32 │     }
    33
  ℹ forEach may lead to performance issues when working with large arrays. When combined with functions like filter or map, this causes multiple iterations over the same type.

The number of diagnostics exceeds the number allowed by Biome.
Diagnostics not shown: 15.
Checked 4 file(s) in 3ms
Found 35 error(s)
check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ Some errors were emitted while running checks.

So, indeed, many deviations from the ESLint and Prettier configuration I already have in place. You'll have to decide whether to use Biome's linter and opinionated formatting rules or apply as much as possible of your existing configuration through its set of rules and options.

The biome ci and biome explain Commands

The biome ci command is handy in a continuous integration (CI) environment. When run, it checks the code for violations of the rules defined in the biome.json configuration file. If any violations are found, it exits with a non-zero status code, indicating a failure.

Here's an example of using the biome ci command:

# Check your code using biome ci
npx @biomejs/biome ci ./src

The biome explain command, on the other hand, is used to get detailed information about a specific rule. It explains why the rule is essential and how violations can affect the quality of your code. It even prints out a code snippet to illustrate the rule in action.

For example, to understand the 'useArrowFunction' rule, you would run the following command:

# Understand a specific rule
npx @biomejs/biome explain useArrowFunction

This prints out a Markdown document on the console, explaining the rule in detail.

# useArrowFunction

Fix is Safe.

This rule is recommended.

# Description
Use arrow functions over function expressions.

An arrow function expression is a compact alternative to a regular function expression,
with an important distinction:

this is not bound to the arrow function. It inherits this from its parent scope.

This rule proposes turning all function expressions that are not generators (function) and don't use this into arrow functions.

## Examples

### Invalid

const z = function() {
return 0;
}

const delegatedFetch = async function(url) {
return await fetch(url);
}

const f = function() {
return this.prop;
}

Named function expressions are ignored:

const z = function z() {
return 0;
}

Function expressions that declare the type of `this` are  also ignored:

const z = function(this: A): number {
return 0;
}

Using Biome in VS Code

For Visual Studio Code (VS Code) users, Biome offers a dedicated extension that brings all the power and functionality of Biome directly into your IDE. This extension provides real-time linting, auto-formatting, and much more.

To use Biome in VS Code, follow these steps:

  1. Install the Biome extension from the VS Code marketplace.

  2. Open the Command Palette (View or Ctrl/⌘+⇧+P), select Format Document With, select Configure Default Formatter, and then select Biome.

Biome and the Node.js project

One significant recent development with the Node.js project code base is the open pull request by Yagiz Nizipli, which aims to add Biome as the default code formatter.

Integrating Biome into the Node.js project codebase is a big step toward ensuring consistency in code formatting. With a codebase as large as Node.js, run-time performance for code formatting and code quality linter is crucial to provide contributors with a fast feedback loop.

Conclusion

Does this mean ESLint and Prettier are going away? Not at all. ESLint is still a powerful tool for maintaining code quality; it's easily pluggable with custom rules and used extensively throughout many ecosystem projects. Prettier remains a popular choice, too for code formatting. However, Biome is making a solid case as a faster alternative to Prettier and a code quality linter, which could replace ESLint.

Embrace secure coding practices

Don't miss out on Snyk Learn resources to help you write secure, high-quality code.

If you value static code analysis for code formatting and linting, you'll find Snyk's VS Code extension very useful in providing real-time feedback on code security vulnerabilities and open source vulnerabilities. To further enhance your understanding and proficiency in secure coding practices, please sign up on Snyk. It's a platform dedicated to developer-first security and provides a wealth of resources to help you write secure, high-quality code.