You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(eslint-config): consider moving @stylistic formatting to bun fmt #59
@stylistic formatting rules (65 in the measured config, registered as style/*) all run in JavaScript under Bun's bun lint. Bun also has a formatter, bun fmt, that could take over formatting and leave ESLint to code-quality rules.
Proposal
Consider moving formatting from @stylistic (stylistic option in pleaseai()) to bun fmt, while keeping our style: 2-space indent, single quotes, no semicolons.
Prerequisites and questions
Check that bun fmt's Markdown output matches Prettier's. Markdown/JSON/YAML/TOML are a large share of files in docs-heavy repos.
Can bun fmt express the current stylistic settings and the antfu/* formatting rules (for example, antfu/top-level-function)?
How does this fit with @pleaseai/prettier-config and the please-style CLI setup flow?
How do editors (format on save) and lint-staged integrate?
Measure the speedup on a real repo.
Caveats
bun lint / bun fmt are not in a stable Bun release yet. Revisit once they ship.
Dropping @stylistic would change rule IDs and output for consumers. Treat it as opt-in first; making it the default is breaking.
Evaluation: moving @stylistic formatting to bun fmt
TL;DR:bun fmt is a native port of Prettier 3, not a configurable @stylistic replacement. It can express 2-space indent, single quotes, and no semicolons, and its Markdown and TS output matched Prettier 3.9.10 byte for byte on this repo. It cannot express several antfu conventions we enforce today: stroustrup braces, +/= at the start of a continued line, parens only for block-body arrows, comma delimiters in inline type literals, and single quotes even when a string has to escape them. On this repo, current ESLint reports 223 style/* errors on the output of bun fmt. The command is also not in a stable Bun release. Recommendation: don't migrate now. Revisit as an opt-in once bun fmt is stable. Details below.
bun fmt: 1.4.4-canary.1+5f5799ed1, unpacked to /tmp from the canary release. The latest stable release, 1.4.3, has no fmt command (error: Script not found "fmt"). The repo pins 1.3.14.
All formatting ran on copies under /tmp. Nothing in the repo was rewritten.
1. What bun fmt is
Languages: JS, TS, JSX, CSS, SCSS, Less, HTML, Vue, Svelte, Angular, JSON, YAML, TOML, Markdown, MDX, GraphQL, Handlebars. Astro and other plugin-provided languages are sent to the project's installed Prettier.
Contract: "The same output as Prettier." The docs say any difference from Prettier 3 is a bug, apart from a short list of intentional CLI and edge-case differences.
Config: a [fmt] section in bunfig.toml, or an existing .prettierrc / prettier.config.* / package.json#prettier / .oxfmtrc.json, plus .editorconfig (indent_*, tab_width, max_line_length, end_of_line). The options are Prettier's: printWidth, tabWidth, useTabs, semi, singleQuote, jsxSingleQuote, quoteProps, trailingComma, bracketSpacing, bracketSameLine, arrowParens, objectWrap, proseWrap, endOfLine, experimentalTernaries, experimentalOperatorPosition, and others. Extras: sortImports, sortTailwindcss, sortPackageJson, jsdoc, [[fmt.overrides]], ignorePatterns.
No brace-style, operator-placement-for-=, or delimiter options. Those are fixed by Prettier's printer.
CLI:--check (exit 1 if a file needs formatting, 2 on error), --staged (formats staged files and stages them again; the docs say "so you don't need lint-staged"), --changed[=ref], --stdin-filepath, --doctor.
Lint integration: under bun lint, prettier/prettier runs natively against bun fmt output, and there is a built-in bun/format rule. Under plain ESLint, neither exists. prettier/prettier falls back to the installed Prettier.
2. Coverage of what pleaseai() enforces
pleaseai() uses lessOpinionated: true and stylistic: { indent: 2, quotes: 'single', semi: false }. That turns on 65 style/* rules. It also turns on these rules, all gated by the same stylistic switch: antfu/consistent-list-newline, antfu/consistent-chaining, core curly: ['error', 'all'], import/newline-after-import, jsdoc/check-alignment, jsdoc/multiline-blocks, 10 jsonc/*, 12 yaml/*, and 11 toml/* layout rules. stylistic: false turns all of these off.
Method: I formatted the repo's 59 tracked JS/TS files with bun fmt, using {semi: false, singleQuote: true, arrowParens: 'avoid', printWidth: 120}. I then linted the output with the current config through eslint --stdin. The counts below are the errors that remained.
⚠️ Partial. Prettier picks the quote that needs fewer escapes, so 'Plug\'n\'Play' becomes "Plug'n'Play". quoteProps: 'consistent' is close to consistent-as-needed, but I didn't verify it.
85 quotes
Commas
comma-dangle (always-multiline), comma-style
✅ trailingComma = 'all'
0
Braces and blocks
brace-style (stroustrup), padded-blocks, curly
❌ brace-style: Prettier only prints } else {, which is 1TBS. padded-blocks ✅. curly is not formatting, but stylistic: false drops it, so we would have to add it back.
⚠️ Partial. Prettier puts operators at the end of the line. With experimentalOperatorPosition: 'start', binary operators move, but = doesn't. The other rules aren't flagged on this repo.
46 operator-linebreak, 11 with 'start'
Arrow parens
arrow-parens (as-needed, requireForBlockBody)
❌ Prettier only has always or avoid. Neither one means "parens only when the body is a block".
⚠️ Layout comes from Prettier's JSX printer. jsx-curly-brace-presence is not formatting, so it stays in ESLint.
Not verified. The repo has no linted JSX.
antfu/consistent-list-newline
➖ Superseded. Prettier decides by printWidth. It collapsed our multi-line imports and signatures onto one line.
0 errors, large churn
antfu/consistent-chaining
➖ Superseded. Prettier breaks chains of 3 or more calls.
0 errors
antfu/top-level-function
❌ Not formatting, and Prettier never changes const f = () => to function f(). It isn't gated by stylistic, so it keeps working in ESLint.
n/a
antfu/if-newline, antfu/curly
Not enabled for us. lessOpinionated: true replaces them with core curly: 'all'. For reference, Prettier keeps if (a) b() on one line, so if-newline would not be covered.
n/a
jsonc/*, yaml/* layout
10 + 12 rules
✅ Prettier formats JSON and YAML. Divergence: .jsonc gets trailing commas, which conflicts with jsonc/comma-dangle: never (apps/docs/wrangler.jsonc). It also re-indented the ast-grep __snapshots__/*.yml files, which are generated and would need ignorePatterns.
14 of 66 JSON/YAML files changed
toml/* layout
11 rules
⚠️ Only with bunfig.toml[fmt]. With a Prettier config present, bun fmt skips TOML.mise.toml and bunfig.toml failed with "No parser could be inferred".
Verified
jsdoc/check-alignment, jsdoc/multiline-blocks
⚠️ Only with jsdoc = true, which also rewrites prose (capitalization, tag order).
Not verified
perfectionist/sort-* (not gated)
Keep in ESLint. Don't turn on sortImports: its groups (builtin → external → alias → relative) differ from perfectionist's (types first), and the two would fight.
n/a
Summary: 5 rule families can't be expressed: brace-style, operator-linebreak, arrow-parens, member-delimiter-style, and quotes with avoidEscape: false. Together they produced 222 of the 223 leftover errors. Even with a Prettier-equivalent config, bun fmt would change 36 of 59 JS/TS files (2,886 changed lines) on this repo. Adopting it means adopting Prettier style, not porting ours.
Side note: @pleaseai/prettier-config sets bracketSpacing: false, which contradicts style/object-curly-spacing: always in pleaseai(). The two packages already disagree today, and bun fmt reads that config as is (verified through package.json#prettier).
3. Markdown parity with Prettier: verified on this repo
I formatted the repo's 59 tracked .md/.mdx files twice, once with no config and once with @pleaseai/prettier-config. Each time I ran Prettier 3.9.10 and bun fmt 1.4.4-canary.1 on separate copies, then ran diff -r.
Result: byte-identical in both configs (0 differing files). That includes MDX with JSX components, front matter, CJK tables, and code fences.
For contrast, the TS/JS files were also byte-identical to Prettier (59 files).
--doctor reported every file as "formatted natively". Bun did not hand any file to the installed Prettier.
Caveat: this is one small repo. It doesn't prove parity in general, and proseWrap was left at its default preserve.
Migration impact: nothing formats Markdown prose today. antfu only lints code blocks, and formatters is off. So bun fmt would be new formatting. It changed 53 of 59 Markdown files, mostly by aligning table columns and turning " into ' in front matter.
4. Speed (indicative, this repo, 20 cores, idle)
Task
Time
bun fmt --check, 59 TS/JS files
~9 ms
prettier --check, same files
~640 ms
bun fmt --check, 59 md/mdx files
~9 ms
prettier --check, same files
~1,040 ms
eslint . (no cache, whole repo)
4.2 to 4.6 s
Formatting rules' share of ESLint rule time (TIMING)
~335 ms of ~1,150 ms (~29%)
On this repo, dropping stylistic would save about 0.3 s of a roughly 4 s ESLint run. Most of the wall time goes to config load, parsing, and non-style rules. I didn't measure a large repo or bun lint. Both are worth redoing once bun lint/bun fmt are stable.
5. Integration impact
@pleaseai/prettier-config: this becomes the natural home for the formatter config, because bun fmt reads package.json#prettier: "@pleaseai/prettier-config" directly. But it would need to agree with ESLint: bracketSpacing is the existing mismatch, and arrowParens: 'always' contradicts style/arrow-parens today. Because a Prettier config disables TOML, we may prefer a bunfig.toml[fmt] snippet instead of, or in addition to, the Prettier config.
please-style CLI (packages/cli): the prettier-config tool installs prettier and sets package.json#prettier. A bun fmt path would need several changes:
a new tool or option that writes [fmt] to bunfig.toml, or reuses the Prettier config without installing prettier
a matching pleaseai({ stylistic: false }) template in eslint.config.mjs, with curly added back
a doctor check for Bun ≥ the first stable version with fmt
Bun-only projects benefit. npm/pnpm projects without Bun cannot use it.
Editors: the Editor docs tell VS Code users to set prettier.enable: false and formatOnSave: false and to fix through ESLint. Bun documents no editor extension or LSP for bun fmt. Since its output equals Prettier's, the practical setup is the Prettier extension with format-on-save, plus ESLint for code-quality fixes. That reverses the current guidance and needs prettier installed for the editor, which the docs themselves point out. Neovim would use conform.nvim with bun fmt --stdin-filepath.
lint-staged: the repo runs eslint --cache --fix on *.{js,ts,md,…} through husky. bun fmt --staged replaces the formatting half without lint-staged. ESLint stays for code-quality rules, so we would keep lint-staged for ESLint or move to bun lint --staged --fix later. Watch for this: bun fmt with an empty file list formats the whole directory. Use --staged or xargs -r, not xargs bun fmt.
Rule IDs: with stylistic: false, the 65 style/* rules disappear, along with antfu/consistent-list-newline, antfu/consistent-chaining, curly (unless we add it back), import/newline-after-import, 2 jsdoc/* rules, and 33 jsonc/yaml/toml layout rules. Consumers' eslint-disable style/… comments and rules: { 'style/…': … } overrides start reporting unknown-rule errors, and eslint.rules.customizations in editor settings goes stale.
Output: the code style changes visibly: 1TBS braces, trailing operators, ; in inline type literals, wrapping by printWidth. Expect a one-time reformat of most files (on this repo: 36 of 59 TS/JS, 53 of 59 Markdown, 14 of 66 JSON/YAML).
Breaking? As a default, yes. It would need a major bump. As an opt-in option, such as formatter: 'bun' (name TBD) that sets stylistic: false and keeps curly, it is non-breaking.
Recommendation
Don't change the default.bun fmt is canary-only, and it would replace the antfu style with Prettier style rather than speed it up.
When bun fmt ships stable, consider an opt-in option for teams that accept Prettier style. It would turn stylistic off, add curly back, and ship a matching [fmt]/Prettier config. Fix the bracketSpacing and arrowParens mismatch in @pleaseai/prettier-config first.
Markdown can be decided on its own. Parity looked solid here, and nothing formats Markdown prose today. A bun fmt (or Prettier) pass on *.md/*.mdx could be added independently of the JS/TS decision.
Open questions
Are we willing to switch to Prettier style (1TBS braces, trailing operators) in exchange for speed? If not, bun fmt is out of scope for JS/TS.
Should @pleaseai/prettier-config be aligned with pleaseai() (bracketSpacing: true, arrowParens: 'avoid') regardless of this proposal?
Do we want Markdown prose formatting at all? It touched 53 of 59 files here, mostly table alignment.
Where should the formatter config live: bunfig.toml[fmt] (formats TOML, Bun-only) or the Prettier config (works with the editor extension, skips TOML)?
Re-measure on a large docs-heavy repo with stable bun lint + bun fmt against eslint --fix.
Activity
amondnet commentedon Oct 15, 2026
Evaluation: moving
@stylisticformatting tobun fmtTL;DR:
bun fmtis a native port of Prettier 3, not a configurable@stylisticreplacement. It can express 2-space indent, single quotes, and no semicolons, and its Markdown and TS output matched Prettier 3.9.10 byte for byte on this repo. It cannot express several antfu conventions we enforce today:stroustrupbraces,+/=at the start of a continued line, parens only for block-body arrows, comma delimiters in inline type literals, and single quotes even when a string has to escape them. On this repo, current ESLint reports 223style/*errors on the output ofbun fmt. The command is also not in a stable Bun release. Recommendation: don't migrate now. Revisit as an opt-in oncebun fmtis stable. Details below.Environment
bunfig.toml[fmt],bun/formatrule, lint plugins.bun fmt:1.4.4-canary.1+5f5799ed1, unpacked to/tmpfrom thecanaryrelease. The latest stable release, 1.4.3, has nofmtcommand (error: Script not found "fmt"). The repo pins 1.3.14.@antfu/eslint-config9.5.1,@stylistic/eslint-plugin5.10.0, Prettier 3.9.10, ESLint 10.12.0, onmain(without feat(eslint-config): add canonicalPluginNames option for native bun lint rules #57)./tmp. Nothing in the repo was rewritten.1. What
bun fmtis[fmt]section inbunfig.toml, or an existing.prettierrc/prettier.config.*/package.json#prettier/.oxfmtrc.json, plus.editorconfig(indent_*,tab_width,max_line_length,end_of_line). The options are Prettier's:printWidth,tabWidth,useTabs,semi,singleQuote,jsxSingleQuote,quoteProps,trailingComma,bracketSpacing,bracketSameLine,arrowParens,objectWrap,proseWrap,endOfLine,experimentalTernaries,experimentalOperatorPosition, and others. Extras:sortImports,sortTailwindcss,sortPackageJson,jsdoc,[[fmt.overrides]],ignorePatterns.tabWidth = 2,singleQuote = true,semi = false.=, or delimiter options. Those are fixed by Prettier's printer.--check(exit 1 if a file needs formatting, 2 on error),--staged(formats staged files and stages them again; the docs say "so you don't needlint-staged"),--changed[=ref],--stdin-filepath,--doctor.bun lint,prettier/prettierruns natively againstbun fmtoutput, and there is a built-inbun/formatrule. Under plain ESLint, neither exists.prettier/prettierfalls back to the installed Prettier.2. Coverage of what
pleaseai()enforcespleaseai()useslessOpinionated: trueandstylistic: { indent: 2, quotes: 'single', semi: false }. That turns on 65style/*rules. It also turns on these rules, all gated by the samestylisticswitch:antfu/consistent-list-newline,antfu/consistent-chaining, corecurly: ['error', 'all'],import/newline-after-import,jsdoc/check-alignment,jsdoc/multiline-blocks, 10jsonc/*, 12yaml/*, and 11toml/*layout rules.stylistic: falseturns all of these off.Method: I formatted the repo's 59 tracked JS/TS files with
bun fmt, using{semi: false, singleQuote: true, arrowParens: 'avoid', printWidth: 120}. I then linted the output with the current config througheslint --stdin. The counts below are the errors that remained.bun fmtcovers it?array-bracket-spacing,arrow-spacing,block-spacing,comma-spacing,computed-property-spacing,generator-star-spacing,key-spacing,keyword-spacing,no-multi-spaces,no-trailing-spaces,no-whitespace-before-property,object-curly-spacing,rest-spread-spacing,semi-spacing,space-before-blocks,space-before-function-paren,space-in-parens,space-infix-ops,space-unary-ops,template-curly-spacing,template-tag-spacing,type-annotation-spacing,type-generic-spacing,type-named-tuple-spacing,yield-star-spacingindent,indent-binary-ops,no-tabs,no-mixed-spaces-and-tabsindent-binary-opssemisemi = falsequotes(avoidEscape: false),jsx-quotes,quote-props(consistent-as-needed)'Plug\'n\'Play'becomes"Plug'n'Play".quoteProps: 'consistent'is close toconsistent-as-needed, but I didn't verify it.quotescomma-dangle(always-multiline),comma-styletrailingComma = 'all'brace-style(stroustrup),padded-blocks,curlybrace-style: Prettier only prints} else {, which is 1TBS.padded-blocks✅.curlyis not formatting, butstylistic: falsedrops it, so we would have to add it back.brace-styleoperator-linebreak(before),multiline-ternary,dot-location,max-statements-per-line,new-parens,no-floating-decimal,wrap-iife,no-extra-parens(functions),no-mixed-operatorsexperimentalOperatorPosition: 'start', binary operators move, but=doesn't. The other rules aren't flagged on this repo.operator-linebreak, 11 with'start'arrow-parens(as-needed,requireForBlockBody)alwaysoravoid. Neither one means "parens only when the body is a block".avoid.alwaysbreaks the other half.member-delimiter-style(multiline none, single-line comma){ a: string; b: number }on one line.no-multiple-empty-lines,eol-last,lines-between-class-memberslines-between-class-members: Prettier keeps blank lines but doesn't add them.spaced-commentjsx-closing-bracket-location,jsx-closing-tag-location,jsx-curly-newline,jsx-curly-spacing,jsx-equals-spacing,jsx-first-prop-new-line,jsx-function-call-newline,jsx-indent-props,jsx-max-props-per-line,jsx-one-expression-per-line,jsx-tag-spacing,jsx-wrap-multilines,jsx-curly-brace-presencejsx-curly-brace-presenceis not formatting, so it stays in ESLint.antfu/consistent-list-newlineprintWidth. It collapsed our multi-line imports and signatures onto one line.antfu/consistent-chainingantfu/top-level-functionconst f = () =>tofunction f(). It isn't gated bystylistic, so it keeps working in ESLint.antfu/if-newline,antfu/curlylessOpinionated: truereplaces them with corecurly: 'all'. For reference, Prettier keepsif (a) b()on one line, soif-newlinewould not be covered.jsonc/*,yaml/*layout.jsoncgets trailing commas, which conflicts withjsonc/comma-dangle: never(apps/docs/wrangler.jsonc). It also re-indented the ast-grep__snapshots__/*.ymlfiles, which are generated and would needignorePatterns.toml/*layoutbunfig.toml[fmt]. With a Prettier config present,bun fmtskips TOML.mise.tomlandbunfig.tomlfailed with "No parser could be inferred".jsdoc/check-alignment,jsdoc/multiline-blocksjsdoc = true, which also rewrites prose (capitalization, tag order).perfectionist/sort-*(not gated)sortImports: its groups (builtin → external → alias → relative) differ from perfectionist's (types first), and the two would fight.Summary: 5 rule families can't be expressed:
brace-style,operator-linebreak,arrow-parens,member-delimiter-style, andquoteswithavoidEscape: false. Together they produced 222 of the 223 leftover errors. Even with a Prettier-equivalent config,bun fmtwould change 36 of 59 JS/TS files (2,886 changed lines) on this repo. Adopting it means adopting Prettier style, not porting ours.Side note:
@pleaseai/prettier-configsetsbracketSpacing: false, which contradictsstyle/object-curly-spacing: alwaysinpleaseai(). The two packages already disagree today, andbun fmtreads that config as is (verified throughpackage.json#prettier).3. Markdown parity with Prettier: verified on this repo
I formatted the repo's 59 tracked
.md/.mdxfiles twice, once with no config and once with@pleaseai/prettier-config. Each time I ran Prettier 3.9.10 andbun fmt1.4.4-canary.1 on separate copies, then randiff -r.--doctorreported every file as "formatted natively". Bun did not hand any file to the installed Prettier.proseWrapwas left at its defaultpreserve.formattersis off. Sobun fmtwould be new formatting. It changed 53 of 59 Markdown files, mostly by aligning table columns and turning"into'in front matter.4. Speed (indicative, this repo, 20 cores, idle)
bun fmt --check, 59 TS/JS filesprettier --check, same filesbun fmt --check, 59 md/mdx filesprettier --check, same fileseslint .(no cache, whole repo)TIMING)On this repo, dropping
stylisticwould save about 0.3 s of a roughly 4 s ESLint run. Most of the wall time goes to config load, parsing, and non-style rules. I didn't measure a large repo orbun lint. Both are worth redoing oncebun lint/bun fmtare stable.5. Integration impact
@pleaseai/prettier-config: this becomes the natural home for the formatter config, becausebun fmtreadspackage.json#prettier: "@pleaseai/prettier-config"directly. But it would need to agree with ESLint:bracketSpacingis the existing mismatch, andarrowParens: 'always'contradictsstyle/arrow-parenstoday. Because a Prettier config disables TOML, we may prefer abunfig.toml[fmt]snippet instead of, or in addition to, the Prettier config.please-styleCLI (packages/cli): theprettier-configtool installsprettierand setspackage.json#prettier. Abun fmtpath would need several changes:[fmt]tobunfig.toml, or reuses the Prettier config without installingprettierpleaseai({ stylistic: false })template ineslint.config.mjs, withcurlyadded backdoctorcheck for Bun ≥ the first stable version withfmtBun-only projects benefit. npm/pnpm projects without Bun cannot use it.
Editors: the Editor docs tell VS Code users to set
prettier.enable: falseandformatOnSave: falseand to fix through ESLint. Bun documents no editor extension or LSP forbun fmt. Since its output equals Prettier's, the practical setup is the Prettier extension with format-on-save, plus ESLint for code-quality fixes. That reverses the current guidance and needsprettierinstalled for the editor, which the docs themselves point out. Neovim would useconform.nvimwithbun fmt --stdin-filepath.lint-staged: the repo runs
eslint --cache --fixon*.{js,ts,md,…}through husky.bun fmt --stagedreplaces the formatting half without lint-staged. ESLint stays for code-quality rules, so we would keep lint-staged for ESLint or move tobun lint --staged --fixlater. Watch for this:bun fmtwith an empty file list formats the whole directory. Use--stagedorxargs -r, notxargs bun fmt.feat(eslint-config): add canonicalPluginNames option for native bun lint rules #57 (
canonicalPluginNames): this proposal is independent of it.stylistic: falseremovesstyle/*entirely, so the@stylistic→stylerename stops mattering.6. Migration impact
stylistic: false, the 65style/*rules disappear, along withantfu/consistent-list-newline,antfu/consistent-chaining,curly(unless we add it back),import/newline-after-import, 2jsdoc/*rules, and 33jsonc/yaml/tomllayout rules. Consumers'eslint-disable style/…comments andrules: { 'style/…': … }overrides start reporting unknown-rule errors, andeslint.rules.customizationsin editor settings goes stale.;in inline type literals, wrapping byprintWidth. Expect a one-time reformat of most files (on this repo: 36 of 59 TS/JS, 53 of 59 Markdown, 14 of 66 JSON/YAML).formatter: 'bun'(name TBD) that setsstylistic: falseand keepscurly, it is non-breaking.Recommendation
bun fmtis canary-only, and it would replace the antfu style with Prettier style rather than speed it up.bun fmtships stable, consider an opt-in option for teams that accept Prettier style. It would turnstylisticoff, addcurlyback, and ship a matching[fmt]/Prettier config. Fix thebracketSpacingandarrowParensmismatch in@pleaseai/prettier-configfirst.bun fmt(or Prettier) pass on*.md/*.mdxcould be added independently of the JS/TS decision.Open questions
bun fmtis out of scope for JS/TS.@pleaseai/prettier-configbe aligned withpleaseai()(bracketSpacing: true,arrowParens: 'avoid') regardless of this proposal?bunfig.toml[fmt](formats TOML, Bun-only) or the Prettier config (works with the editor extension, skips TOML)?bun lint+bun fmtagainsteslint --fix.