Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: use biome as formatter #50672

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

build: use biome as formatter #50672

wants to merge 1 commit into from

Conversation

anonrig
Copy link
Member

@anonrig anonrig commented Nov 11, 2023

Since ESLint is deprecating formatter rules, I propose using Biome as the formatter.

This is pretty much in progress. I'm not sure if Biome supports all existing rules.

Fixes #50449

Todos

  • Add Biome to Github workflow
  • Update Makefile
  • Add Biome as a dependency

Feature parity

eol-last ✅

image

image

linebreak-style ❌ (planned but not yet implemented, however git can automatically convert these for you in the .gitattributes file, like this)

no-extra-parens ✅

biome acts like the all eslint option, node currently uses the functions one which is a subset of all

image

no-multi-spaces ✅

image

image

no-multiple-empty-lines ⚠ (biome only supports the max: 1 eslint option, node uses max: 2)

image

image

no-tabs ✅ (set the biome formatter setting indentStyle to "space", doesnt work within comments)

image

image

no-trailing-spaces ✅

image

image

no-whitespace-before-property ✅

image

image

object-curly-newline ⚠ (click to see explanation)

biome will keep the braces in the same line if the object is empty

if you are destructuring, it will try to keep it in the same line unless that would exceed the defined lineWidth

if the object is not empty, it will keep the linebreak (or lack of linebreak) the user provided in most cases

image

image

object-curly-spacing ✅

image

image

one-var-declaration-per-line ✅

image

image

operator-linebreak ✅ (tries to keep on the same line unless it exceeds lineWidth, if not it matches the "after" option which node uses)

image

image

padding-line-between-statements ❌ (click to see explanation)

node uses the { blankLine: 'always', prev: 'function', next: 'function' } config for the option, which (i guess) makes eslint always add a blank newline if two functions are declarated in a row. biome doesn't add a blank newline unless you already had added one

image

image

quote-props ⚠ (node uses the "consistent" eslint option, biome matches the "as-needed" option)

image

image

quotes' avoidEscape feature ✅

image

image

image

rest-spread-spacing ✅

image

image

space-before-blocks ✅

image

image

space-before-function-paren ⚠ (works almost the same as node's config except biome matches the "always" eslint option for anonymous functions)

image

image

space-in-parens ✅

image

image

space-infix-ops ✅

image

image

space-unary-ops ✅

image

image

image

spaced-comment ❌ planned but not yet supported

image

image

@nodejs-github-bot nodejs-github-bot added the tools Issues and PRs related to the tools directory. label Nov 11, 2023
@anonrig anonrig force-pushed the add-biome branch 2 times, most recently from 3c46f50 to 6ec8286 Compare November 11, 2023 22:16
@bricss
Copy link

bricss commented Nov 11, 2023

Or as suggest 💡-> Deprecation of formatting rules blog post:

If the idea of using a dedicated source code formatter doesn’t appeal to you, you can also use @stylistic/eslint-plugin-js

biome.json Outdated Show resolved Hide resolved
@anonrig anonrig force-pushed the add-biome branch 4 times, most recently from 139c534 to c3e0525 Compare November 12, 2023 21:24
@tniessen
Copy link
Member

I don't see any discussion leading up to this choice. rome (and now biome) still seems to have a small userbase compared to prettier etc. Stylistic, which @bricss mentioned, will likely also see tremendous growth. Aside from benchmarks, I assume, what made you choose this particular tool?

@anonrig anonrig force-pushed the add-biome branch 3 times, most recently from f589685 to 0b7f606 Compare November 12, 2023 21:56
@anonrig
Copy link
Member Author

anonrig commented Nov 12, 2023

We currently do not use a formatter in Node. We have several incomplete formatting rules, and that's it. If you clone and run the formatter on the repository, you'll see that the recommended changes (currently there is 14,000 errors) are recommendations that shouldn't be here at the first place.

For example,

  • We don't have any formatter running on JSON files at the moment
  • The indentation rule in Eslint extremely basic and is open to errors.

To make things much more solid, here's screenshot of what Biome recommends. IMHO, I don't understand how Eslint indentation rule is implemented and how the previous code is allowed.

image

Aside from benchmarks, I assume, what made you choose this particular tool?

There is more to what Biome offers especially in the linter area where we could potentially tap in the future. But here's my take:

  • Biome implements most of the prettier rules, and takes the overhead of maintaining different rules.
  • Performance: It takes 1 second to analyze formatting of 15,000 files in Node.js repository.
  • It offers auto fixing capability.

Some of the Biome folks can answer this in more depth...

@ovflowd
Copy link
Member

ovflowd commented Nov 12, 2023

I'm also curious about what led to the choice of Biome. (I have never heard of it and am interested in what it brings to the table).

Genuinely speaking, I'm curious about the maintenance part, popularity, overall usage, and performance. Tools like prettier are standard because they're known well. Prettier is opinionated so I wonder how our stance differs here.

Now, I wonder how this conflicts with our remark-preset-lint-node (in other words, with our linting configuration for Markdown files?)

I was never a fan of having ESLint taking care or formatting rules, and I'm happy that they're removing that. It's definitely a good move.

Yet, I'm curious how the adoption of Biome would play on the core, and I'm hesitant to think that running the lint on the whole Node.js codebase with Biome would imply. I'm concerned about how this might make backporting, git-blame, and other git operations harder because the stylistic and formatting rules differ. Have we accounted for that?

Anyhow, by no means I'm against this, I just have this feeling that we should be careful with whatever we adopt. Thanks for taking the initiative, Yagiz.

@joyeecheung
Copy link
Member

joyeecheung commented Nov 13, 2023

For the choice I have a few ideas for evaluation:

  1. Does it support git-clang-format style diff formatting? So that we can run it incrementally, instead of on the entire code base. This would also reduce backport conflicts, and keep the git blame history useful. We did that with the C++ part of the code base and it worked well in this regard. Not having it can be a deal breaker if we have another choice that supports it, in my personal opinion.
  2. How close can the formatted result be to our status quo? The closer the better, IMO. Again, this would reduce the churn and conflicts.

@joyeecheung
Copy link
Member

joyeecheung commented Nov 13, 2023

Although, for 1 mentioned above, we can also roll our own version of https://github.com/llvm-mirror/clang/blob/master/tools/clang-format/git-clang-format to run it on top of any JS formatter. Just need to make sure that it can format thes file in a way for a wrapper like this to work. Or it would still be better if the formatter support something like this out of the box

@ematipico
Copy link

ematipico commented Nov 13, 2023

Thank you @anonrig, for considering Biome.

Some of the Biome folks can answer this in more depth...

I am going to piggyback and share more information about Biome, so you can make an informative decision about what you think is best for the project:

  • Biome offers many features, not just a formatter. You can decide which tool to use, how to use it, and configure it based on your needs. You can gradually opt-in features without installing new tools (linter rules, import sorting, VCS integration, etc.);
  • Biome is fast. Imagine how much time you can save in CI; you have one less problem to care about, so you can focus more on testing times and build times in your CI;
  • Biome doesn't require Node.js to function. The npm package is a tiny wrapper to pass the CLI arguments to the binary. If you want, you can run Biome using the CI action;
  • Biome formatter was designed with Prettier in mind, so Biome's formatting style tries to resemble Prettier's (and as mentioned already, Prettier is widely adopted);
  • Biome has LSP built-in, and you can use first-party or third-party plugins, for better DX: https://biomejs.dev/guides/integrate-in-editor/
  • The fact that the LSP is built-in means that you get fixes in the CLI and LSP in the same release

Happy to answer any questions

@bricss
Copy link

bricss commented Nov 13, 2023

IMO, Biome 🧫 doesn't look like a best possible choice, even tho it would be blazingly fast ⚡ coz it has quite poor selection of rules, especially for code styles, and most of the rules (if not all) is very rigid 🪨 and not customizable 🪛 at all 🫥

@kibertoad
Copy link
Contributor

@bricss Can you elaborate on what do you mean by "poor selection of rules"?
Lack of customizability can be seen as an advantage, as long as a style close to status quo can be preserved. There is value in opinionated formatters.

@joyeecheung
Copy link
Member

joyeecheung commented Nov 13, 2023

I would say that performance should probably be that last thing we consider for a formatter. We just should not format the entire code base in one go and should do it incrementally, git-clang-format style, to reduce conflicts in backports and to preserve git blame history. Normally PRs just don't involve that many JS files being changed - in the rare occasions that they do, they are probably importing third-party dependencies, for which the formatter should be turned off.

@ovflowd
Copy link
Member

ovflowd commented Nov 13, 2023

I would say that performance should probably be that last thing we consider for a formatter. We just should not format the entire code base in one go and should do it incrementally, git-clang-format style, to reduce conflicts in backports and to preserve git blame history. Normally PRs just don't involve that many JS files being changed - in the rare occasions that they do, they are probably importing third-party dependencies, for which the formatter should be turned off.

Agreed here. Performance is a good thing, but what matters the most here (which I also agree) is having a consistent ruleset that supports as closely as the current styling. This is something I talked about yesterday with @anonrig as backporting, git history, git blame, and others could be strongly affected. Supporting also our "git-clang-format" style would be preferable.

In the end, if this adoption results in huge diffs, that's far from what we need and want here. So the obvious question is, can Biome, or any other formatter support our stylistic rules?

@bricss
Copy link

bricss commented Nov 13, 2023

@kibertoad By comparison of those two rulesets Biome -> Lint Rules vs ESLint Stylistic -> Rules is should be clear ⚖️
Or in general Biome -> Lint Rules vs ESLint -> Rules 🕵️
I spent lotsa time crafting that thing -> Ultra refined ESLint config📜 bcos status quo wasn't enough for example 🐦‍⬛
Not saying Biome 🧫 is bad or something, it just doesn't seem like a best tool 🧰 to do the job, at a time ⌚
It's a question of freedom 🗽 of choices and flexibility of advanced tune-ups 🎛️ vs opinionated formatters lobbying efforts 👔

@SuperchupuDev
Copy link
Contributor

@bricss you're comparing ESLint's formatting lint rules to Biome's style lint rules (such as noVar etc, that don't impact code formatting), which are completely different categories. There is no thing such as "formatting" lint rules in Biome as the formatter is a separate thing. I agree that Biome doesn't have as many lint rules as ESLint (yet 👀), but this PR proposes using Biome's formatter (not the linter), which doesn't use any kind of lint rules. If this PR gets accepted, node would still use ESLint for non-stylistic rules

@SuperchupuDev
Copy link
Contributor

I would say that performance should probably be that last thing we consider for a formatter. We just should not format the entire code base in one go and should do it incrementally, git-clang-format style, to reduce conflicts in backports and to preserve git blame history. Normally PRs just don't involve that many JS files being changed - in the rare occasions that they do, they are probably importing third-party dependencies, for which the formatter should be turned off.

@joyeecheung I believe Biome has an incremental option planned, where it would only format/lint the files/lines that were changed. Is that what you're looking for?

@cjihrig
Copy link
Contributor

cjihrig commented Nov 13, 2023

If the idea of using a dedicated source code formatter doesn’t appeal to you, you can also use @stylistic/eslint-plugin-js

Why don't we try this and then reevaluate if we actually need another tool?

Copy link
Member

@richardlau richardlau left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm -1 on this as it stands -- it involves adding 90mb of pre-compiled arch-specific (and on that note only x64 and arm64 architectures are supported) binaries to the repository. This will be problematic to downstream rebuilders (e.g. Linux distros) who prefer to build everything (including tooling) from source code.

@bricss
Copy link

bricss commented Nov 13, 2023

FYI that Node should not be bound by IDE-specific linters.

True story, but it's not the answer to how to get developers to use formatters or linters 👮 before they submit changes 📮

@jsumners
Copy link
Contributor

jsumners commented Nov 13, 2023

Sorry for the out-of-order-ness of these replies. I accidentally posted them under an account I didn't intend to. These are all me and no one else:

We currently do not use a formatter in Node.

It's actually rather frustrating. Most of my commits in a recent PR were fixing stuff the linter in CI complained about because there wasn't a tool "fixing" the problem on save.

@bricss Can you elaborate on what do you mean by "poor selection of rules"? Lack of customizability can be seen as an advantage, as long as a style close to status quo can be preserved. There is value in opinionated formatters.

Agreed. I dislike some of the rules gofmt applies, but it prevents endless conversations about what is acceptable or not in style. Everyone gets to say "the tool decided" and move on with life.

@jsumners
Copy link
Contributor

It's actually rather frustrating. Most of my commits in a recent PR were fixing stuff the linter in CI complained about because there wasn't a tool "fixing" the problem on save.

That seems like a workflow problem and not specific to any tool.

In my opinion it's a project configuration issue. It's entirely possible to ship the project in such a way that editors will recognize the formatting rules of the project and apply them. The way this project is setup it requires extra effort.

@cjihrig
Copy link
Contributor

cjihrig commented Nov 13, 2023

@jsumners I don't disagree with you, but that isn't what this issue is about.

@jsumners
Copy link
Contributor

Okay, I think they are quite closely related, but 🤷‍♂️

'default-case-last': 'error',
'dot-location': ['error', 'property'],
'dot-notation': 'error',
'eol-last': 'error',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'eol-last': 'error',

you forgot to remove this formatting rule biome already matches with

@joyeecheung
Copy link
Member

joyeecheung commented Nov 13, 2023

We currently do not have a consistent coding style. Edit: I want to emphasize "consistent" here.

You can just consider this a bug in the formatter, but that doesn't make it less of a formatter. Otherwise as long as a formatter has a bug, it is...no longer a formatter? If ESlint cannot catch the indent rule being violated, does that make it...not a linter?

At the end of the day, I am happy with a formatter that can help me keep the linter quite, buggy or not. For example the screenshot in #50672 (comment) does not look that significant to me - if I find this in a code review, I would not bother to ask folks to "fix the indentation" as long as the linter is happy with it. And if that formatting gets in the way of backporting or git blame, then I'd rather not format the code at all and let it be.

And about the consistent part, I think that will just be what happens with a code base this size. To really have consistent style, you need to run a formatter in the entire code base, which introduce friction in backporting and git blames. As long as we format our code incrementally, some old code would always be in some inconsistent style until they get touched, if they ever gets touched. I think a cleaner git history is definitely worth giving up on the consistency of styles.

@ematipico
Copy link

To really have consistent style, you need to run a formatter in the entire code base, which introduce friction in backporting and git blames. As long as we format our code incrementally, some old code would always be in some inconsistent style until they get touched, if they ever gets touched

It's possible to mitigate this by using git-blame-ignore-revs. It's a bit of a pain with the revs though on GitHub, especially with "squash and merge", because you get the final commit after the merge, so you end up doing two PRs.

Copy link
Member

@tniessen tniessen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update-biome.sh script would permanently add almost 100 MiB to the repository for every single update. That seems like something we absolutely should not do.

(Non-blocking part.) I think #50714 is a good short term solution to fix #50449. @targos is right in that this PR is orthogonal, and with @joyeecheung's suggestion, the git diff should be minimal.

@tniessen
Copy link
Member

Incremental formatting is tricky. It works reasonably well with clang-format, but even there, it often cascades and ends up in large diffs even for tiny changes.

@joyeecheung
Copy link
Member

joyeecheung commented Nov 15, 2023

It's possible to mitigate this by using git-blame-ignore-revs. It's a bit of a pain with the revs though on GitHub, especially with "squash and merge", because you get the final commit after the merge, so you end up doing two PRs.

I think that still isn't quite enough for this code base. For context we have LTS lines (we even have two, at least for now) that need constant backporting from the main branch. A massive whitespace change would make backporting much harder because we already don't have the hands to backport patches soon enough and in correct order to older releases. Unless we have enough hands to keep all the LTS branches up-to-date regarding pending backports (which I guess we don't), the "before formatting" and "after formatting" conflicts could get out of hand quickly. If we format the LTS lines, then all the pending-to-backport patches that landed before formatting happened on the main branch would be a nightmare (remember that we don't have the hands to backport them in the correct order). If we don't format the LTS lines, then all the patches landed after formatting would be a nightmare. It would be easy to miss minute functional changes among all these whitespace changes when resolving the conflicts. Even when we are looking into an alternative release schedule, the alternative would still have at least one LTS release that needs some form of backporting nonetheless (maybe less frequent but it's still necessary for LTS), and before the alternative release schedule comes into effect, we still have two existing LTS lines to maintain.

To me if we want to switch to another JS formatter, we should switch to one that supports git-clang-format style incremental formatting (or something like #50714 that does not actually update the formatting, just the disable comments). If they don't support this, we should not consider them until they support it. If we want to go with the least effort route, we can just write a python wrapper for git-clang-format to work with JavaScript which it already supports and is used by Chromium/V8.

@anonrig
Copy link
Member Author

anonrig commented Nov 29, 2023

Removing tsc-agenda label. The consensus was towards waiting for Biome to add support for incremental formatting to avoid adoption issues. I'll also update the pull request to include the dependency just as we add the clang-format

@anonrig
Copy link
Member Author

anonrig commented Feb 27, 2024

@tniessen @richardlau I've addressed the concerns regarding the installation flow of Biome and rebased the current pull request. Would you mind reviewing it again?

pinging @nodejs/tsc for visibility

@anonrig anonrig force-pushed the add-biome branch 6 times, most recently from e1f943c to ea675e7 Compare February 27, 2024 19:50
"enabled": true,
"indentWidth": 2,
"indentStyle": "space",
"lineEnding": "lf"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: not needed, it's the default value

BIOME_FORMAT_START="$(git merge-base HEAD refs/remotes/origin/$GITHUB_BASE_REF)"

tools/biome/node_modules/.bin/biome \
check . \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this job is specifically meant only for formatting, then it's best to use the format command. If in the future you enable more tools, this command will apply/check everything.

@tniessen tniessen dismissed their stale review April 11, 2024 08:23

My concern has allegedly been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tools Issues and PRs related to the tools directory.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ESLint is going to deprecate formatting rules