返回资讯中心
外部精选
软件工程
#开发者实时流

Bun v1.4.4: now including `bun fmt`, a built-in formatter

powershell -c "irm bun.sh/install.ps1|iex"

Hacker News 最新jrop2026/10/23 · 22 天前30 分钟阅读

以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。

BlogBun v1.4.4

Jarred Sumner

·October 22, 2026

To install Bun

curlnpmpowershellscoopbrewdocker

curl

curl -fsSL https://bun.sh/install | bash

npm

powershell -c "irm bun.sh/install.ps1|iex"

scoop

brew tap oven-sh/bun

brew install bun

docker

docker pull oven/bun

docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun

bun upgrade

bun fmt: a formatter built into Bun#

bun fmt is an incredibly fast, Prettier-compatible code formatter that works with your existing Prettier or oxfmt config.

bun fmt

src/index.ts src/styles.css Formatted 2 files, 2 unchanged [5.59ms]

It formats JavaScript, TypeScript, JSX, Flow, CSS, SCSS, Less, HTML, Vue, Svelte, Angular, Lightning Web Components, MJML, JSON, JSONC, JSON5, YAML, TOML, Markdown, MDX, GraphQL and Handlebars. Embedded code is formatted too, like <style> in HTML, code blocks in Markdown and css... templates in JavaScript.

In Prettier's own repository (2,616 files, with their config), bun fmt --check takes 60 ms and Prettier takes 17 seconds:

That's 286x faster. On vitest, date-fns, monkeytype and h3, it's 234x to 425x faster than Prettier 3.9.9.

On projects that already use oxfmt, with their own config, it's 3x to 6x faster than oxfmt 0.74.0:

Both tools see the same files, exit 0 and leave the tree unchanged.

Reproduce these numbers

Each project is a fresh clone at a pinned commit. The oxfmt projects have no node_modules.

bun install -g oxfmt@0.74.0

git clone https://github.com/vitejs/vite && cd vite

git checkout 4ca25e1d3a24

hyperfine --warmup 2 "oxfmt ." "bun fmt ."

redocly-cli

git clone https://github.com/Redocly/redocly-cli && cd redocly-cli

git checkout 82da67d6ea5b

hyperfine --warmup 3 --runs 20 "oxfmt --check ." "bun fmt --check ."

RSSHub

git clone https://github.com/DIYgod/RSSHub && cd RSSHub

git checkout 8776927c78de

hyperfine --warmup 3 --runs 20 "oxfmt --check ." "bun fmt --check ."

fumadocs

git clone https://github.com/fuma-nama/fumadocs && cd fumadocs

git checkout bc86bbb0d406

hyperfine --warmup 3 --runs 20 "oxfmt --check ." "bun fmt --check ."

prettier

git clone https://github.com/prettier/prettier && cd prettier

git checkout df39b5b60a28

bun install --ignore-scripts

hyperfine --warmup 3 "bun fmt --no-script . --check" "node node_modules/prettier/bin/prettier.cjs . --check"

The same runs on one core, and on only vite's JavaScript and TypeScript files ('**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}'):

bun fmt wouldn't exist without Prettier, Biome and Oxc. It's a port of Prettier, and the JavaScript and TypeScript printer is a port of oxc_formatter, which is a port of Biome's formatter.

Thank you to the Prettier, Biome and Oxc teams for years of great work.

--check lists the files that need formatting without writing anything. It exits with code 1 if there are any.

bun fmt --check

src/index.ts src/styles.css 2 files need formatting. Run bun fmt to fix them.

bun fmt --staged and --changed#

--staged only formats the files you're about to commit and re-stages them, so you don't need lint-staged.

#!/bin/sh bun fmt --staged

--changed formats the files that differ from your default branch. Pass a ref to compare against something else.

bun fmt --changed

bun fmt --changed=origin/dev

Use your Prettier or oxfmt config#

bun fmt reads .prettierrc*, prettier.config.*, "prettier" in package.json, overrides, .editorconfig and .prettierignore. It also reads .oxfmtrc.json, .oxfmtrc.jsonc and oxfmt.config.ts (or .mts). It accepts the same CLI flags as Prettier or oxfmt, and // prettier-ignore comments work.

"scripts": { "format": "prettier --write ." "format": "bun fmt" }

Import sorting is built in, so you can leave @trivago/prettier-plugin-sort-imports, @ianvs/prettier-plugin-sort-imports and prettier-plugin-organize-imports in plugins. Languages that need a Prettier plugin, like Astro, fall back to the Prettier installed in your project.

The command is bun fmt, like cargo fmt, go fmt, deno fmt and zig fmt. bun format still runs your format script.

bun fmt --doctor shows how Bun interprets your config and which packages you can uninstall.

All of Prettier's options are supported under the same names. Each one is also available as a flag, like --print-width 100 or --no-semi. bun fmt --init creates the [fmt] section.

[fmt] printWidth = 100 semi = false sortImports = true sortTailwindcss = true sortPackageJson = true jsdoc = true

[[fmt.overrides]] files = ["*.test.ts"] options = { printWidth = 120 }

sortImports, sortTailwindcss, sortPackageJson and jsdoc are off by default, and don't need a Prettier or oxfmt config. sortImports groups imports, sorts the names in { } and merges duplicate imports from the same module.

In a monorepo, bun fmt walks up from the working directory to the nearest bunfig.toml with a [fmt] section. Without a Prettier config, bun fmt also formats TOML. It skips *.min.js, *.min.css and lockfiles unless you pass them explicitly.

bun fmt does the work of prettier-plugin-tailwindcss, prettier-plugin-packagejson and prettier-plugin-svelte, so you don't need them installed. You can leave them in plugins, and their options are supported.

// Before <button className="text-white px-4 sm:px-8 py-2 bg-sky-700 hover:bg-sky-800" /> // After <button className="bg-sky-700 px-4 py-2 text-white hover:bg-sky-800 sm:px-8" />

Tailwind classes are sorted with your project's tailwindcss, version 3 or 4. If it isn't installed, they're left as they are.

We compared the output against Prettier 3.9.9 with version 0.8.1 of the plugin, on Tailwind 4.3.3 and 3.4.19.

About half of the differences are intentional. In className={header${open ? " active" : ""}}, the plugin trims " active" to "active", so the element ends up with the class headeractive. bun fmt keeps the space.

For package.json, keys are sorted in the same order as the plugin and values are left untouched. Of 13,670 real-world package.json files, 13,644 (99.8%) are byte-for-byte identical to the output of Prettier 3.9.9 with the plugin. 20 differ only in values that Bun preserves, and the plugin throws on the other 6.

.svelte files are formatted by default, and the output matches prettier-plugin-svelte 4.1.1. With a Prettier config, they're formatted if the config lists the plugin. To skip them, set svelte = false in [fmt] or pass --no-svelte.

If your config uses a plugin that bun fmt doesn't support, like prettier-plugin-jsdoc, it prints an error naming the plugin, skips the files the plugin applies to and exits with code 2.

Prettier's output changes the meaning of the code for these inputs. bun fmt preserves it.

bun fmt --debug-check re-parses each file after formatting and fails if the meaning changed, like Prettier's --debug-check.

Bun's CI runs Prettier 3.9.9's own formatting tests on every commit. A test passes when the output is byte-for-byte identical to Prettier's.

64 of the 83 differences are one intentional case: Prettier drops trailing blank lines from a YAML block scalar, which changes the string.

Another 90 tests are skipped. 66 of them use syntax proposals at stage 2 or below that only Babel parses, and 13 need a Prettier plugin.

With an oxfmt config, bun fmt matches oxfmt 0.72's output on 1,343 of oxfmt's 1,498 fixtures (89.7%).

On 330 open-source repositories, we ran each project's format command with its own formatter and with bun fmt. A run only counts as identical if every file is byte-for-byte the same. Next.js counts as a mismatch: 26,334 files are identical, but bun fmt rejects one file that uses a Babel-only proposal.

Most of the Prettier mismatches come from an older version of Prettier, Prettier plugins or Markdown files.

Cold runs with 8 threads on a Linux x64 machine, best of 3. Both versions report the same number of errors on each project.

Peak memory on MUI's docs drops from 11.2 GB to 1.3 GB. The other four use up to 0.3 GB more.

Of 76 other repositories, 69 are more than 10% faster. Bun's own src/js is slower: 0.20 s, up from 0.11 s.

With bun check -b, projects share parsed declaration files, module resolution caches and the list of emitted files. Each project used to keep its own copy, so check time grew much faster than the number of projects.

bun check -b on generated monorepos with skipLibCheck: true, best of 6 runs on a 16-core M4 Max:

In the last one, peak memory dropped from 1,987 MB to 872 MB.

bun install converts a Yarn 2+ (Berry) yarn.lock to bun.lock and preserves every pinned version. Bun v1.4.3 only migrated Yarn v1 lockfiles, so switching a Berry project to Bun re-resolved every dependency.

bun install

bun install v1.4.4 [59.59ms] migrated lockfile from yarn.lock

Workspaces, npm: aliases, resolutions, and git, tarball and file: dependencies are preserved.

patch: dependencies become patchedDependencies in package.json.

catalog: ranges are read from .yarnrc.yml. The catalogs are copied into package.json.

The migration is all-or-nothing. If Bun can't preserve everything yarn.lock pins, it prints the reason and falls back to resolving from package.json. For example:

bun install retries failed requests up to five times. In Bun v1.4.3, all five retries happened within 5 ms, so a brief registry outage would fail the install.

Now it waits 50 ms before the first retry and doubles the delay each time: 50, 100, 200, 400 and 800 ms, with ±20% jitter.

Installing one package from a misbehaving registry, and how long the command took:

To tolerate outages longer than 1.5 seconds, increase the initial delay:

waits 250 ms, 500 ms, 1 s, 2 s and 4 s

BUN_CONFIG_HTTP_RETRY_BACKOFF=250 bun install

BUN_CONFIG_HTTP_RETRY_BACKOFF=0 retries immediately, like before.

Retry-After headers are respected, up to 60 seconds.

429 and 408 responses are retried.

133 bun install issues closed#

We had 597 open package manager pull requests. 464 landed in full and 40 landed partially.

Every one of these PRs had tests that failed in the previous version of Bun and now pass. This nearly doubled bun install's test suite.

The hoisted linker links packages on up to 8 threads on macOS and Linux. It helps most when the cache is warm and node_modules is missing or incomplete, like a CI job that restores Bun's cache.

Installing 1,000 packages with 30 files each on Linux, with everything already cached:

Packages like sharp, @swc/core, lightningcss and rollup publish one optional dependency per platform, and mark each Linux build with "libc": ["glibc"] or "libc": ["musl"]. Bun previously ignored that field, so every Linux install downloaded both builds. Now it only installs the one that matches your system, like npm and pnpm.

bun install --libc musl # for an Alpine image, from a glibc machine

bun install --libc '*' # both builds, like before

The field is saved in bun.lock. Lockfiles written by older versions of Bun don't have it, so existing packages pick it up when they're updated or when you regenerate the lockfile.

When nothing in your project provides a package's peer dependency, Bun installs a version that satisfies the peer range. Before, the package got whatever version an unrelated package happened to install.

{ "dependencies": { "@modelcontextprotocol/sdk": "1.20.0", "@remix-run/express": "2.17.1" } }

@remix-run/express has a peer dependency on express@^4.20.0. The MCP SDK depends on express@5.

Installing elysia alongside @jest/schemas had the same problem: elysia got @sinclair/typebox@0.27 and failed to load.

How bun install picks a peer dependency's version:

When an install has to resolve dependencies, --verbose prints every peer dependency that ended up out of range.

Versions already in bun.lock don't change. To re-resolve a peer, remove the package that needs it and add it back.

bun update --latest only needs to run once. In Bun v1.4.3, updating reac

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →