We can use help in a bunch of areas and any help is greatly appreciated!
- 🚀 Contributing
Important
If you are using any kind of AI assistance to contribute to Biome, it must be disclosed in the pull request.
If you relied on AI assistance to make a pull request, you must disclose it in the pull request, together with the extent of the usage. For example, if you used AI to generate docs or tests, you must say it. An example disclosure:
-
This PR was written primarily by Claude Code.
-
I consulted ChatGPT to understand the codebase but the solution was fully authored manually by myself.
Providing this information helps reviewers understand the context of the pull request and apply the right level of scrutiny, ensuring a smoother and more efficient review process.
AI assistance isn't always perfect, even when used with the utmost care.
Please be respectful to maintainers and disclose AI assistance.
If you have any questions, proposals, or feedback, open a GitHub discussion. Make sure your comment adds value: don't post a comment just to get attention.
Our Discord server is open for help and more ad-hoc discussion. All activity on the Discord is still moderated and will be strictly enforced under the project's Code of Conduct.
Remember that we are doing this project on our own time. We are humans: we like support, and we expect kindness :)
Our GitHub issues serve as a place for submitting bugs. Make sure that the bugs is not reported yet and is not fixed in the main branch. You can test on the main branch, thanks to the playground.
Alternatively, you can use our official CodeSandbox template.
You can work on the project locally by cloning the repository and installing the required tools or use the pre-configured GitHub Codespaces and jump right into the code.
Building this project requires a stable Rust toolchain, which can be installed using rustup.
Clone the repository and navigate to the tools directory:
git clone https://github.com/biomejs/biome
cd biomeYou can use cargo to run Biome CLI in development mode:
# This is like running "biome --help"
cargo biome-cli-dev --helpWe use Just to run scripts and tasks, to make our life easier.
You can install just using cargo:
cargo install justBut we highly recommend to install it using an OS package manager, so you won't need to prefix every command with cargo.
Once installed, run the following command to install the required tools:
just install-toolsThis command will install:
cargo-binstall, to install binary extensions forcargo.cargo-insta, acargoextension to manage snapshot testing inside the repository.tombi, a small tool for formatting TOML files.wasm-bindgen-cliandwasm-optfor managing the WASM build of Biome.
You'll also need to have pnpm installed on your machine, and run pnpm install from the root of the repository. pnpm is needed to create changesets
And you're good to go hack with Biome and Rust! 🚀
This Codespace comes pre-configured with the required tools and dependencies to work on the Biome project.
Note
A basic Codespace (32gb of disk space) might run out of disk space when building biome or running the full test suite. The pre-configured Codespace is therefore based on the premium image with 64gb of disk space.
You can either use cargo or just to run tests. For simplicity and running tests real quick, use cargo.
With cargo, you can run tests by using the test command:
# run tests
cargo test
# or use the shortcut
cargo tIf you run cargo t from the root, it will run all tests of the whole repository. If you're inside a crate folder, cargo will run tests of that crate:
cd crates/biome_cli
# it will run only the tests of the `biome_cli` crate
cargo tYou can run a single test with cargo by passing the test name after the test command:
cd crates/biome_js_formatter
cargo t quick_testThis will run the quick_test test inside the biome_js_formatter crate. You should see an output similar to this:
running 1 test
test quick_test::quick_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 224 filtered out; finished in 0.02sYou can also use just for running tests. With just, the scripts will use the same test runner we use in the CI.
just testIf you want to test the tests for a single crate:
just test-crate biome_cliRust has a concept of doctest. A doc test is a doc comment that runs some code. Usually, it looks something like this:
/// I am a doc test
/// ```
/// assert_eq!(true, true) // this is a doc test, and the assertion must pass
/// ```
fn some_fn() {
}The code inside the code blocks is run during the testing phase.
To run only the doctest, you can run the command:
just test-docIn some crates, we use snapshot testing. The majority of snapshot testing is done using insta. insta is already installed by the command just install-tools.
When a snapshot test fails, you can run:
cargo insta acceptto accept all the changes and update all the snapshots;cargo insta rejectto reject all the changes;cargo insta reviewto review snapshots singularly.
Sometimes you want to debug something when running tests. Like console.log, in JavaScript, in Rust you can use the macro dbg!() to print something during debugging something. Then, pass the option --show-output to cargo:
fn some_function() -> &'static str {
let some_variable = "some_variable";
dbg!(&some_variable);
some_variable
}
#[test]
fn test_some_function() {
let result = some_function();
assert_eq!(result, "some_variable")
}cargo t test_some_function --show-outputCreating a development binary is very useful in case you need to triage a reproduction, and you require more information like logging, trace logs, etc.
Additionally, you can use this binary when you need to debug issues related to LSP clients.
From the root of the repository, run the following command:
cargo build --bin biomecargo will create a binary called biome in the target/debug/ directory.
If you're debugging a CLI reproduction, copy the biome binary inside the root of the reproduction, and change any script that uses the npm package to use the binary instead:
{
"scripts": {
- "lint": "biome lint",
+ "lint": "./biome lint"
}
}If you're debugging an LSP reproduction, make sure that the client allows to use custom binary, like VSCode and Zed. Provide an absolute URL to the binary that was emitted.
{
"biome.lsp.bin": "/Users/john/www/biome/target/debug/biome"
}By default, the dev profile of the project removes the debugging information during compilation, which means that some information such as stacktraces aren't available.
If you need to debug the project, use the profile debugging. Running this profile might take some time as it will re-compile the dependencies by keeping certain information.
cargo t --profile debugging some_testUsually, the easiest way to create a production build is to use the --release flag, however Biome requires an environment variable called BIOME_VERSION to generate different code at compile time.
When you provide a BIOME_VERSION that is different from 0.0.0, the build will turn off all the nursery rules that are recommended. The value of BIOME_VERSION doesn't matter, as long as it's different from 0.0.0. This means that you'll have to provide a command similar to this:
BIOME_VERSION=0.0.1 cargo build --bin biome --releaseWhen you finished your work, and you are ready to commit and open a PR, there are few other things you would need to run and check:
just f(alias forjust format), formats Rust and TOML files.just l(alias forjust lint), run the linter for the whole project.- Code generation. The code generation of the repository is spread in the different parts of the codebase. Sometimes is needed and sometime it isn't:
- run
just gen-analyzerwhen you're working on the linter; - run
just gen-bindingsin case you worked around the workspace.
- run
Note
You can run just ready as well, although it's a command that runs the codegen of the whole repository, which will take some time
If you happen to create a new crate inside the workspace, use the command just new-crate, e.g.:
cargo new crates/biome_new_crate --libWhere biome_new_crate is going to be the name of the new crate. The --lib option tells cargo to create the crate as library, so you will probably see a src/lib.rs file.
To know the technical details of how our analyzer works, how to create a rule and how to write tests, please check our internal page
To know the technical details of how our parser works and how to write test, please check our internal page
To know the technical details of how our formatter works and how to write test, please check our internal page
Workspace dependencies are used, and many dependencies are defined in Cargo.toml in the root.
Internal crates are loaded with workspace = true for each crate. About dev-dependencies, we use path dependencies to avoid requiring the published version of these crates.
The npm module packages/@biomejs/biome contains Biome's Node.js API that supports different backends:
wasm-nodejs(WebAssembly)backend-jsonrpc(Connection to the daemon)
For testing and developing, you need to build these packages, following the steps:
- install pnpm via corepack by running
corepack enable; - install
wasm-bindgen-cliandwasm-optby runningjust install-tools; - run
pnpm --filter "@biomejs/backend-jsonrpc" build; - run the
pnpm --filter "@biomejs/js-api" build:wasm-devandpnpm --filter "@biomejs/js-api" buildcommands; - run
pnpm i --filter "@biomejs/js-api" --frozen-lockfileto link the WebAssembly bindings and the JSON-RPC bindings
The tests are run against the compiled files, which means that you need to run the
build script after you implemented features/bug fixes.
For more information on how to help with translation, please see the translation contribution guidelines for our docs.
Internally, the Biome team adheres as closely as possible to the conventional commit specification. The following this convention encourages commit best-practices and facilitates commit-powered features like changelog generation.
The following commit prefixes are supported:
build:, a change that affects the build system or external dependencieschore:, project housekeepingci:, a change that affects CIdocs:, a documentation updatefeat:, a new featurefix:, a bugfixperf:, project performancerefactor:, refactor of the code without change in functionalityrelease:, release of a new versionrevert:, revert a previous changetest:, a test update
Below are examples of well-formatted commits:
feat(compiler): implement parsing for new type of files
fix: fix nasty unhandled error
docs: fix link to website page
test(lint): add more cases to handle invalid rulesWe are using action-semantic-pull-request to lint the titles of pull requests. If the 'Lint Pull Request Titles' workflow fails, please correct the title.
When creating a new pull request, it's preferable to use a conventional commit-formatted title, as this title will be used as the default commit message on the squashed commit after merging. See the dedicated section about conventional commit format.
When creating a PR, follow the following instructions:
- if you fix a bug (code or documentation), send a PR to the maintenance branch
main. - if you add a new nursery rule, send a PR to the maintenance branch
main. Nursery rules don't follow semantic versioning. - if you promote a rule from nursery, send a PR to the
nextbranch. - if you implement a new feature that affects end users, send a PR to the
nextbranch. - if you implement a new feature that doesn't affect end users, send a PR to the maintenance
mainbranch.
This repository uses changesets to automate the releases of Biome's binaries, the JavaScript libraries and the creation of the CHANGELOG.md for each library.
If the PR you're about to open is a bugfix/feature visible to users of the Biome toolchain or of the published Biome crates, you are encouraged to provide a changeset . To create a changeset, use the following command (don't create it manually):
just new-changesetNote
The script uses pnpm under the hoods, so make sure to have ran pnpm i from the root of the repository before running this script.
The command will present a prompt where you need to choose the libraries involved by the PR, the type of change (major, minor or patch) for each library, and a description of the change. The description will be used as name of the file.
The command will create the changeset(s) in the .changeset folder. You're free to open the file, and add more information in it.
If you want to add headers, use #### or #####. Other kind of headers will mess up the final CHANGELOG and break upstream tools.
In the vast majority of cases, you want to choose the @biomejs/biome package, which represents the main package.
The frontmatter of the changeset will look like this:
---
"@biomejs/biome": patch
---
Description here...We are very strict about major changes in the @biomejs/biome package. To better understand type of your change for this package, please refer to our versioning page. Generally:
patch: any sort of change that fixes a bug.minor: new features available to the users.major: a change that breaks a user API.
When choosing minor or major, make sure your PR targets the next branch instead of main.
The description of the changeset should follow the these guidelines:
- Our changesets should be about user-facing changes. Internal changes don't need changesets. For example, if you refactored some code, but the user-facing behavior didn't change, you don't need a changeset.
- Be concise and clear. Changesets are not documentation, and they are not test cases.
They should give a quick overview of what changed, allowing the reader to dig deeper if they want to. A good changeset is usually between 1 and 3 sentences long. Longer changesets indicate to the user that they should pay more attention to the change, at least if it concerns a feature that is relevant to them. Consider the impact of your change on the user when writing the changeset. The key is to provide just enough information for the user to understand the change without overwhelming them with details.
- For a new lint rule, show an example of an invalid case in an inline code snippet for simple things or a code block for more complex examples. If it really helps demonstrate the rule, you can also show a valid case.
- For a change in an existing rule, clearly demonstrate what is now considered invalid that wasn't before, or vice versa. If it helps communicate the change greatly, show both invalid and valid cases.
- For a formatter change, show an example of the formatting change using a
diffcode block. - For parser changes, a brief inline example of what can now be parsed that couldn't before (or vice versa) is usually sufficient. If formatting on multiple lines improves clarity, use a code block.
- Use past tense when describing what you did, e.g. "Added new feature", "Fixed edge case".
- Use the present tense when describing Biome behavior, e.g. "Biome now supports ...".
- If you fixed a bug, please start with a link to the issue, e.g. "Fixed #4444: ...".
- If you reference a rule, please add the link to the rule on the website, e.g. "Added the rule
useAwesomeThing" (even if the website isn't updated at the time of writing, it will once the PR is merged). - Similarly, if you reference an assist, please add the link to the assist on the website, e.g. "Added the assist
awesomeAction". - End every sentence with a full stop (
.).
If in doubt, take a look at existing changesets, or the most recent entries in CHANGELOG.md, and use your best judgement.
If your PR involves new features, or changes to existing features, documentation must be updated as well. For rules, assists, and their options, this is done using inline rustdoc documentation.
When other documentation updates are required, such as new formatter options, a PR should be created against the next branch of our website. When doing so, please link the documentation PR from the PR that introduces the feature.
We follow the specs suggested by the official documentation:
Odd minor versions are dedicated to pre-releases, e.g. *.5.* .
Even minor versions are dedicated to official releases, e.g. *.6.*.
- Run the
betaworkflow. Input the upcoming version with an incremented number for each release (e.g.2.0.0-beta.1). - If you're a Core Contributor, approve the deployment.
Before starting release:
-
Make sure that all issues/PRs for the milestone are done: https://github.com/biomejs/biome/milestones
-
Replace all
version: "next"with the new version number in the metadata of the rules. This should be automated , but you can usescripts/update-next-version.shif you need to do it manually.
When releasing a new minor or major version of a Biome, follow these steps:
-
Create a PR from
nexttomain. Make sure that code conflicts are fixed and the new features have relative docs PR. -
Merge
nexttomain. Using merge commit is recommended here, do not use squash merge as it removes the commit history. -
Update to the same
versionin all crates if you publish crates, if applicable. (Cargo.tomlandcrates/**/Cargo.toml) -
Linter rules have a
versionmetadata directly defined in their implementation. This field is set to"next"for newly created rules. This field must be updated to the new version. -
Merge the
ci: releasePR, and the release workflow will run. Once these workflows finish compiling the final artefact, they need to be approved manually by a member of the Core Contributors. -
After releasing a major version number, you may want to update the
update-preview-version.mjsscript to make sure that future previews indicate a version number with a higher patch version than is currently indicated in thepackage.jsonmanifests. -
Make sure you create new
nextbranches for both the main repository and the website.
patch releases only require a merging of the ci: release PR, and should leave the next branches untouched.
We have several resources explaining about Biome. They will help you understand the project and codebase.
- Rust Dublin October 2023 - Biome - YouTube
- Rome, a Modern Toolchain! by Emanuele Stoppa - GitNation
- How to create a lint rule in Biome
Members are listed in alphabetical order. Members are free to use the full name, GitHub handle, or any other nickname they wish to be addressed. Members are free to disclose their pronouns.
- Carson McManus @dyc3
- Denis Bezrukov @denbezrukov
- Hiroki Ihoriya @unvalley
- Naoki Ikeguchi @siketyan
- Nicolas Hedger @nhedger
- Dani Guardiola @DaniGuardiola
- Justinas Delinda @minht11
- Madeline Gurriarán @SuperchupuDev
- Maikel @Netail
- Marat Dulin @mdevils
- Vladimir Ivanov_@vlad
- Vo Hoang Long @vohoanglong0107
- Yoshiaki Togami @togami2864
- Yusuke Abe @chansuke
- zoomdong @fireairforce
- Daiki Nishikawa @nissy-dev (Core contributor)
- Jon Egeland @faultyserver (Core contributor)
- Takayuki Maeda @TaKO8Ki (Maintainer)
- Yagiz Nizipli @anonrig (Maintainer)
- Vasu Singh @vasucp1207 (Maintainer)
- Victor Teles @victor-teles (Maintainer)
- Zheyu Zhang @ah-yu (Core Contributor)