benchmarks #

tsv is a Rust toolchain for parsing and formatting TypeScript/JS, CSS, and Svelte (source code). Its formatter closely follows Prettier and borrows its architectural patterns, and we're grateful for the hard work of Prettier's contributors. After correctness, tsv prioritizes performance and efficiency: this page measures its speed and size on real-world code, and the conformance page compares parser coverage on edge-case test suites.

This page compares tsv to Prettier and the JS parsers it can replace (Svelte's and acorn-typescript), and to Oxc and Biome, similar tools with more features, configurable styles, and wider language support. It also measures rsvelte, yuku-parser, swc, dprint, Malva, markup_fmt, and PostCSS; Language support lays out what each parses and formats.

tldr
#

tsv formats its three languages faster than Oxc and Biome in every in-process pairing here, and its CLI outpaces theirs using less memory in every shared scenario. Its artifacts are smaller than theirs too, except against oxc-parser's native build, since tsv publishes no native parse-only build yet.

Except in the CLI section, every timing here is in-process and one file at a time, isolating engine speed from multi-core parallelism. The corpus is 7,686 files of real-world code, the author's own plus framework source, third-party libraries, and widely benchmarked TypeScript (see Corpus), timed under Node v24.

  • Formatting TypeScript, tsv is ~2.2x faster than Oxfmt (native-vs-native), ~37x faster than Prettier (native Rust vs Prettier's JS; ~26x as wasm), and ~7.7x faster than Biome (wasm-vs-wasm).
  • Formatting Svelte, tsv is ~85x faster than Prettier (~58x as wasm) and ~10x faster than Biome (wasm-vs-wasm), and rsvelte currently has no in-process API.
  • Formatting CSS, tsv is ~3.7x faster than Oxfmt (native-vs-native), ~114x faster than Prettier (~80x as wasm), and ~12x faster than Biome (wasm-vs-wasm).
  • Parsing TypeScript to its default span-only AST (offsets only, like Oxc's), tsv is natively ~1.5x faster than oxc-parser (see Parse speed) and ~2.4x slower than yuku-parser (~3.4x as wasm). With {locations: true}, which adds acorn's per-node line/column loc, it is ~1.2x faster than acorn-typescript, the parser it can replace, and still ahead of Oxc and swc, whose ASTs carry none.
  • Parsing Svelte with {locations: true}, whose loc is a superset of Svelte's own, tsv is ~2.0x faster than svelte/compiler (JS) and ~3.0x faster than rsvelte (native-vs-native); its default span-only AST is ~2.6x faster than svelte/compiler.
  • Parsing CSS runs the other way, largely because Svelte has a lower fidelity CSS AST which tsv parses to more spec-faithful granularity: Svelte's own parseCss is ~1.9x faster than tsv's default parse, and PostCSS ~1.7x faster (see Parse speed). Under Bun it flips: tsv's default parse leads parseCss by ~1.2x, with parseCss ~1.8x slower there than under Node.
  • End to end as a CLI, tsv formats the JSX-free subset of a real TypeScript repo ~1.5x faster than Oxfmt and ~3.9x faster than Biome (~2.6x and ~6.6x as the bare binary, without Node in front), using less memory than either. These are wall-clock ratios, so they include each tool's multi-file parallelism (see the CLI section).
  • On a third-party Svelte corpus, tsv's CLI, again through its Node bin, is ~2.9x faster than rsvelte-fmt, another Rust Svelte formatter.

Language support
#

Support matrix for Parsers and Formatters (bold is timed on this page, faded is supported but not timed):

toolTS/JSJSX/TSXCSSSCSS/LessSvelteother
tsvparses, timed; formats, timednoneparses, timed; formats, timednoneparses, timed; formats, timed
Prettierformats, timedformats, not timedformats, timedformats, not timedformats, via prettier-plugin-svelte, timedJSON, YAML, Markdown, GraphQL, HTML, Vue, …
svelte/compiler, acorn-typescriptparses, timedparses, with its jsx option, not timedparses, timednoneparses, timed
Oxcparses, timed; formats, timedparses, not timed; formats, not timedformats, timedformats, not timedformats, via the Prettier it bundles, opt-in, timedJSON, TOML, YAML, GraphQL, Markdown; MDX, HTML, Vue, … via Prettier
Biomeformats, timedformats, not timedformats, timednoneformats, experimental, behind a flag, timedJSON, GraphQL, GritQL, HTML (opt-in), and experimental Vue, Astro
rsvelteformats, with Oxc's formatters, not timedformats, with Oxc's formatters, not timedformats, with Oxc's formatters, not timedformats, with Oxc's formatters, not timedparses, timed; formats, timedJSON, and the rest of what oxfmt formats with oxfmt installed
@dprint/typescriptformats, timedformats, not timednonenonenonedprint's other plugins add JSON, Markdown, …; Malva and markup_fmt are listed below
Malvanonenoneformats, timedformats, not timednoneSass
markup_fmtnonenonenonenoneformats, with dprint's TypeScript and Malva plugins, not timedHTML, Vue, Astro, Angular, and template dialects (Jinja, Twig, Vento, …)
yuku-parserparses, timedparses, not timednonenonenone
swcparses, timedparses, not timednonenonenoneFlow
PostCSSnonenoneparses, timedparses, via a separate syntax package, not timednone

1 via prettier-plugin-svelte; 2 with its jsx option; 3 via the Prettier it bundles, opt-in; 4 experimental, behind a flag; 5 with Oxc's formatters; 6 with dprint's TypeScript and Malva plugins; 7 via a separate syntax package.

Format speed
#

Each chart's ratios are relative to its highlighted row, and a negative ratio means that many times worse. Hover another row to compare against it instead.

Formatting 1,906 Svelte files

prettier 11,426 ms 1.00x
biome (wasm) 2,040 ms 5.60x
markup_fmt (wasm) rejects files
oxfmt (node napi) 11,594 ms −1.01x
rsvelte-fmt no API
tsv-wasm 196 ms 58.3x
tsv (node napi) 135 ms 84.8x

7 of 1,913 files (1.8% of the group's bytes) are left out of every row, because biome (wasm) fails them in this harness.

Formatting 5,697 TypeScript/JS files

prettier 27,593 ms 1.00x
biome (wasm) 8,047 ms 3.43x
dprint (wasm) 6,006 ms 4.59x
oxfmt (node napi) 1,617 ms 17.1x
tsv-wasm 1,044 ms 26.4x
tsv (node napi) 738 ms 37.4x

3 of 5,700 files (<0.1% of the group's bytes) are left out of every row, because some rows fail them in this harness (per row, overlapping: oxfmt (node napi) 2, biome (wasm) 3).

Formatting 71 CSS files

prettier 724 ms 1.00x
biome (wasm) 111 ms 6.54x
malva (wasm) 60 ms 12.0x
oxfmt (node napi) 23 ms 31.1x
tsv-wasm 9.0 ms 80.2x
tsv (node napi) 6.4 ms 113.9x

2 of 73 files (8.0% of the group's bytes) are left out of every row, because biome (wasm) fails them in this harness.

Parse speed
#

The parsers here don't return the same output, so each ratio compares different amounts of work. They differ in how much position data each node carries (offsets only, or loc too), in how finely they break down CSS (parseCss and PostCSS keep more of it coarse than tsv does), and in what they collect beyond the tree (Oxc also gathers comments and module records). Each tool is compared against tsv's closest output, and the notes cover what still differs.

tsv and oxc-parser, in its default mode, share a mechanism: both serialize the AST to JSON in Rust and hand it to the JS engine's JSON.parse, native and wasm alike. tsv is timed three ways:

  • tsv / tsv-wasm is what the parsers return by default: a span-only AST (start/end offsets, no loc) of Oxc's kind. It is the closest comparison with oxc-parser but not an equal one: Oxc's AST also writes out default-valued fields tsv omits (optional: false, decorators: [], typeAnnotation: null), about a third more bytes, and its parse also collects comments and module records. Oxc also has an experimental raw-transfer mode, untimed here, that skips the JSON and may well beat tsv natively.
  • +locations passes {locations: true}: the same parse with a loc (line/column) object on every node, rebuilt in JS from the offsets. That is acorn's shape on TypeScript, and on Svelte and CSS a superset of what their reference parsers carry. On the TypeScript corpus it takes ~1.2x the time of the default.
  • internal builds tsv's AST in Rust and stops: no serialization, nothing materialized in JS. These entries show raw engine speed. Their gap to the default entries is what serializing and handing off costs tsv, not a cross-tool comparison.

yuku-parser, a JS/TS parser written in Zig, emits the same span-only AST as Oxc, so it too compares against tsv's default entries. It hands JS a compact binary buffer that decodes into objects when its program is read, so the bench reads it to time the same fully materialized result. swc's AST (a Module root with span offsets) matches neither of tsv's outputs.

Parsing 1,913 Svelte files

svelte/compiler 792 ms 1.00x
biome no API
rsvelte-parse (node napi) 1,202 ms −1.52x
rsvelte-parse skip-expr-loc (node napi) 744 ms 1.06x
tsv-wasm 335 ms 2.37x
tsv (node napi) 300 ms 2.64x
tsv-wasm+locations 429 ms 1.84x
tsv+locations (node napi) 395 ms 2.01x
tsv-wasm internal 50 ms 15.7x
tsv internal (node napi) 33 ms 24.3x

Parsing 5,697 TypeScript/JS files

acorn-typescript 2,579 ms 1.00x
biome no API
oxc-parser (wasm) 2,651 ms −1.03x
oxc-parser (node napi) 2,524 ms 1.02x
swc (node napi) 3,360 ms −1.30x
yuku-parser (wasm) 541 ms 4.77x
yuku-parser (node napi) 698 ms 3.69x
tsv-wasm 1,836 ms 1.40x
tsv (node napi) 1,702 ms 1.52x
tsv-wasm+locations 2,216 ms 1.16x
tsv+locations (node napi) 2,075 ms 1.24x
tsv-wasm internal 262 ms 9.83x
tsv internal (node napi) 180 ms 14.4x

3 of 5,700 files (<0.1% of the group's bytes) are left out of every row, because some rows fail them in this harness (per row, overlapping: acorn-typescript 3, oxc-parser (node napi) 2, oxc-parser (wasm) 2, yuku-parser (node napi) 2, yuku-parser (wasm) 2, swc (node napi) 3).

Parsing 73 CSS files

svelte/compiler 13 ms 1.00x
biome no API
oxc-parser no API
postcss 15 ms −1.17x
swc no API
tsv-wasm 27 ms −2.05x
tsv (node napi) 25 ms −1.93x
tsv-wasm+locations 31 ms −2.38x
tsv+locations (node napi) 30 ms −2.28x
tsv-wasm internal 5.0 ms 2.61x
tsv internal (node napi) 3.4 ms 3.88x

End-to-end CLI benchmark
#

The numbers above time tsv's engine in-process, one file at a time. This section times the whole CLI end to end, on real code, as you'd experience it from the command line: process spawn, file discovery, I/O, and each tool's default multi-file parallelism, plus peak memory. It comes from a fork of Oxc's own bench-formatter that adds tsv. Upstream's other scenarios aren't shown: their corpora include JSX, which tsv doesn't parse, and two also format a repo's other languages and embedded code, one sorting imports and Tailwind classes too.

As in the charts above, each table's ratios are relative to its highlighted row: tsv via Node dispatcher where tsv faces other tools, and the bare tsv binary in the tsv-only table.

TypeScript repo: Outline repository (non-JSX JS/TS subset)

Every formatter scoped to the same JSX-free files, each at its default parallelism.

formattertimetime ratioCPU ratiopeak RSSRSS ratio
tsv45 ms1.69x1.09x20 MiB2.42x
tsv via Node dispatcher75 ms1.00x1.00x50 MiB1.00x
oxfmt116 ms−1.54x−1.96x226 MiB−4.56x
biome296 ms−3.92x−5.67x148 MiB−2.98x
prettier + oxc-parser6.8 s−89.7x−22.4x303 MiB−6.11x
prettier8.2 s−108.1x−35.3x454 MiB−9.15x

Corpus: commit 8cf997c (2026-07-14), 1,648 files. Each time is the mean of 10 runs after 3 warmups.

Large single file: TypeScript compiler parser.ts (~540KB)

With one input, no formatter can parallelize across files, so wall-clock time comes nearer to engine speed plus startup. Startup is each tool’s own process and thread-pool setup, plus Node’s for every row but the bare tsv binary: a fixed cost that weighs most on the fastest rows.

formattertimetime ratioCPU ratiopeak RSSRSS ratio
tsv16 ms2.85x3.00x14 MiB3.65x
tsv via Node dispatcher46 ms1.00x1.00x49 MiB1.00x
oxfmt61 ms−1.31x−1.91x112 MiB−2.26x
biome107 ms−2.30x−2.34x96 MiB−1.94x
prettier + oxc-parser975 ms−21.0x−27.3x197 MiB−3.98x
prettier1.6 s−34.6x−49.3x306 MiB−6.18x

Corpus: 539,588 bytes, sha256:dcddb577aa14, 1 file. Each time is the mean of 20 runs after 3 warmups.

Svelte corpus: third-party .svelte corpus (kit, svelte.dev, layerchart, svelte-ux, flowbite-svelte src/lib, svelte-maplibre, layercake)

Two Rust-native Svelte formatters head-to-head. rsvelte-fmt’s time includes the Oxfmt it launches for the file types it doesn’t format itself, which walks the corpus and finds none.

formattertimetime ratioCPU ratiopeak RSSRSS ratio
tsv25 ms2.20x1.15x12 MiB4.07x
tsv via Node dispatcher56 ms1.00x1.00x50 MiB1.00x
rsvelte-fmt163 ms−2.92x−5.15x153 MiB−3.08x

Corpus: fuzdev/corpora@63d1790 (collections tree 9b64dd2), snapshot ce81fe8 (2026-10-02), 1,091 files. Each time is the mean of 10 runs after 3 warmups.

tsv delivery paths: TypeScript compiler parser.ts (~540KB), through each tsv distribution

Every row is tsv, delivered three ways: the native binary; the same binary through @fuzdev/tsv’s Node dispatcher; and @fuzdev/tsv-wasm, tsv’s CLI reimplemented in JS over a wasm engine, the package for platforms without a prebuilt binary. The corpus is one file, so the gaps are launch and engine cost, not file parallelism. In time, tsv-wasm still finishes well ahead of both Prettier rows in the single-file table, but behind Oxfmt and Biome.

formattertimetime ratioCPU ratiopeak RSSRSS ratio
tsv16 ms1.00x1.00x14 MiB1.00x
tsv-npm-bun26 ms−1.58x−1.68x20 MiB−1.50x
tsv via Node dispatcher47 ms−2.85x−2.99x50 MiB−3.65x
tsv-wasm-bun96 ms−5.84x−33.5x127 MiB−9.36x
tsv-wasm158 ms−9.59x−27.0x122 MiB−8.99x

Corpus: 539,588 bytes, sha256:dcddb577aa14, 1 file. Each time is the mean of 20 runs after 3 warmups.

This section ran on October 18, 2026, from its harness at 52a8def, on the same machine and Node as the in-process runs (12 threads). The versions of the tools both sections time are listed under Benchmarking details; its native tsv rows run the @fuzdev/tsv-linux-x64-gnu binary, and its wasm row @fuzdev/tsv-wasm 0.7.0.

Binary size
#

The size of each tool's artifact, split by where it runs and grouped by what it does. Bars and ratios are raw bytes; the gz beside each estimates the download. tsv-wasm, tsv (napi), and tsv (ffi) are tsv's full builds: parser and formatter for Svelte, TypeScript/JS, and CSS in one artifact. The format-only and parse-only builds are subsets of them.

In the browser: wasm and JS
#

Full toolchain (parse + format)

prettier + parsers (js bundle) 2.2 MB 567 KB gz 1.00x
tsv-wasm 2.8 MB 1.1 MB gz −1.25x
biome (wasm) 46.9 MB 11.9 MB gz −21.2x

Formatter

malva (wasm) 1.5 MB 414 KB gz 1.00x
markup_fmt (wasm) 2.0 MB 557 KB gz −1.36x
prettier + svelte plugin (js bundle) 2.2 MB 567 KB gz −1.51x
tsv-format-wasm 2.5 MB 962 KB gz −1.68x
dprint (wasm) 4.2 MB 1.2 MB gz −2.85x
oxfmt (wasm) no wasm build

Parser

svelte + acorn typescript parsers (js bundle) 500 KB 125 KB gz 1.00x
yuku-parser (wasm) 699 KB 203 KB gz −1.40x
tsv-parse-wasm 1.0 MB 405 KB gz −2.04x
oxc-parser (wasm) 1.5 MB 481 KB gz −2.97x

Native builds
#

Full toolchain (parse + format)

tsv (ffi) 3.6 MB 1.7 MB gz 1.12x
tsv (napi) 4.1 MB 1.9 MB gz 1.00x
oxc-parser + oxfmt (napi) 10.9 MB 4.5 MB gz −2.70x

Formatter

tsv format (ffi) 3.3 MB 1.6 MB gz 1.22x
tsv (napi) 4.1 MB 1.9 MB gz 1.00x
oxfmt (napi) 8.8 MB 3.7 MB gz −2.18x
rsvelte-fmt (binary) 9.1 MB 3.6 MB gz −2.23x

Parser

yuku-parser (napi) 681 KB 286 KB gz 5.96x
tsv parse (ffi) 1.6 MB 711 KB gz 2.56x
oxc-parser (napi) 2.1 MB 881 KB gz 1.93x
tsv (napi) 4.1 MB 1.9 MB gz 1.00x
swc (napi) 10.5 MB 10.2 MB gz −2.60x
rsvelte compiler (napi) 17.8 MB 7.5 MB gz −4.39x

Cross-runtime
#

The same harness runs under three JS runtimes: Node, Deno, and Bun. The in-process charts above are the Node run, chosen as the default N-API host, not for speed. The native entry differs by runtime: Node and Bun load tsv's N-API addon; Deno loads its C-ABI library (the (ffi) build), which shares the addon's code but crosses a different binding boundary and is built differently. So a per-runtime delta on the same row comes from the JS engine, the host's N-API implementation, or that binding and build, not from tsv's algorithms.

Times are per sweep: one full pass over the group's timed file set, as in the charts above. Ratios are relative to node, negative when slower. The other tools' native rows are their npm N-API addons under all three runtimes.

A ≈ marks a delta inside the two measurements' combined noise: read it as parity. The check has two limits. Each side's noise is the spread of a row's few passes, each a fresh process, which is a thin estimate, so an unmarked delta near it isn't necessarily an effect. And it can't see variance between whole runs.

  • node 24.14.1
  • deno 2.9.7
  • bun 1.4.2

Format Svelte

implementationnodedenobundeno vs nodebun vs node
prettier11,426 ms10,856 ms9,256 ms1.05x1.23x
tsv (native)135 ms134 ms133 ms1.00x1.02x
tsv-wasm196 ms223 ms193 ms−1.14x1.02x
oxfmt (native)11,594 ms10,980 ms9,510 ms1.06x1.22x
biome (wasm)2,040 ms1,884 ms1,759 ms1.08x1.16x

Format TypeScript/JS

implementationnodedenobundeno vs nodebun vs node
prettier27,593 ms25,079 ms25,893 ms1.10x1.07x
tsv (native)738 ms704 ms710 ms1.05x1.04x
tsv-wasm1,044 ms1,181 ms1,032 ms−1.13x1.01x
oxfmt (native)1,617 ms1,612 ms1,588 ms≈1.00x1.02x
biome (wasm)8,047 ms8,267 ms7,287 ms−1.03x1.10x
dprint (wasm)6,006 ms6,812 ms5,813 ms−1.13x1.03x

Format CSS

implementationnodedenobundeno vs nodebun vs node
prettier724 ms698 ms592 ms1.04x1.22x
tsv (native)6.4 ms5.7 ms5.6 ms1.11x1.14x
tsv-wasm9.0 ms10 ms7.6 ms−1.13x1.18x
oxfmt (native)23 ms23 ms23 ms1.02x1.02x
biome (wasm)111 ms120 ms101 ms−1.08x1.10x
malva (wasm)60 ms65 ms70 ms−1.09x−1.16x

Parse Svelte

implementationnodedenobundeno vs nodebun vs node
svelte/compiler792 ms725 ms803 ms1.09x≈−1.01x
tsv (native)300 ms270 ms204 ms1.11x1.47x
tsv-wasm335 ms334 ms229 ms1.00x1.46x
tsv+locations (native)395 ms380 ms294 ms1.04x1.34x
tsv-wasm+locations429 ms445 ms321 ms−1.04x1.34x
tsv internal (native)33 ms32 ms29 ms1.02x1.11x
tsv-wasm internal50 ms56 ms48 ms−1.12x1.04x
rsvelte-parse (native)1,202 ms1,131 ms981 ms1.06x1.23x
rsvelte-parse skip-expr-loc (native)744 ms702 ms632 ms1.06x1.18x

Parse TypeScript/JS

implementationnodedenobundeno vs nodebun vs node
acorn-typescript2,579 ms1,975 ms2,438 ms1.31x1.06x
tsv (native)1,702 ms1,458 ms1,108 ms1.17x1.54x
tsv-wasm1,836 ms1,779 ms1,166 ms1.03x1.57x
tsv+locations (native)2,075 ms1,863 ms1,505 ms1.11x1.38x
tsv-wasm+locations2,216 ms2,171 ms1,590 ms1.02x1.39x
tsv internal (native)180 ms166 ms150 ms1.08x1.20x
tsv-wasm internal262 ms299 ms242 ms−1.14x1.09x
oxc-parser (native)2,524 ms2,266 ms1,559 ms1.11x1.62x
oxc-parser (wasm)2,651 ms2,509 ms1,950 ms1.06x1.36x
yuku-parser (native)698 ms810 ms587 ms−1.16x1.19x
yuku-parser (wasm)541 ms676 ms448 ms−1.25x1.21x
swc (native)3,360 ms ⚠2,958 ms2,419 ms1.14x1.39x

Parse CSS

implementationnodedenobundeno vs nodebun vs node
svelte/compiler13 ms13 ms24 ms1.03x−1.82x
tsv (native)25 ms22 ms20 ms1.15x1.27x
tsv-wasm27 ms26 ms17 ms≈1.01x1.60x
tsv+locations (native)30 ms28 ms25 ms1.07x1.16x
tsv-wasm+locations31 ms32 ms22 ms−1.05x1.42x
tsv internal (native)3.4 ms3.1 ms2.8 ms1.07x1.22x
tsv-wasm internal5.0 ms5.6 ms4.1 ms−1.12x1.23x
postcss15 ms15 ms21 ms1.02x−1.41x

Benchmarking details
#

Each row is the mean time per sweep, a pass over its group's timed file set. Within a group every tool is timed on the same files — the intersection of what every timed tool accepted, so a file one rejects drops out for everyone. The count above each chart is that intersection, and a chart that runs short of the corpus total says beneath it what was left out and which rows failed it.

These are warm numbers: every row runs warmup sweeps before it is timed, so a cold one-shot call pays more. Each native or wasm call also pays a string encode across the binding boundary, and a decode wherever it hands back text or JSON, which the JS tools skip.

Each row is timed in 3 passes, and each pass runs until it has both a few seconds of sweeps and at least 8 sweeps, so the slow rows stop at that floor in every pass. After outlier cleaning, rows keep from 19 to 4,155 timings across their passes. A steady reading over a few dozen sweeps is thinner evidence than one over hundreds, so the harness's check for unstable rows proves less for the slow rows, those that ran fewer than twice the floor's sweeps. Prettier, every format chart's default anchor, is among them in each format group:

  • Format Svelte: prettier, oxfmt, biome-wasm
  • Format TypeScript/JS: every row
  • Format CSS: prettier
  • Parse Svelte: svelte/compiler, tsv-wasm, tsv+locations, tsv-wasm+locations, rsvelte-parse, rsvelte-parse-skip-expr-loc
  • Parse TypeScript/JS: acorn-typescript, tsv, tsv-wasm, tsv+locations, tsv-wasm+locations, oxc-parser, oxc-parser-wasm, yuku-parser, yuku-parser-wasm, swc

Every pass of every row runs in a fresh process that loads that row's tool and nothing else, so no tool's code, garbage, or warmed-up state is shared with the next. Each pass takes a group's rows in a different order (forward, reversed, then rotated), so no row always runs right after the same one, and a row's number is the average of its passes. What no process boundary removes is the machine: nothing pins the process to a core or holds the laptop CPU's clock steady. Two fresh processes of the same row sat 1% apart at the median and 3% at the 95th percentile in this run, so a ratio inside that isn't a difference the run measured. A row whose passes sat 5% or more apart is flagged as unstable, and no row's timings shift more than 3% from the first half of a pass to its second.

One asymmetry isn't isolated. Oxfmt's programmatic format is async-only, so each call's timing includes an N-API task dispatch and a promise resolution that tsv's sync call doesn't pay. (Prettier's is async-only too, without the N-API hop.) Oxfmt's native work may also run off the JS thread: still one file at a time, since each call is awaited before the next, but not strictly one thread.

versions

  • svelte 5.57.2
  • acorn 8.19.0
  • @sveltejs/acorn-typescript 1.0.13
  • prettier 3.9.9
  • prettier-plugin-svelte 4.1.1
  • oxc-parser 0.153.0
  • @oxc-parser/binding-wasm32-wasi 0.142.0
  • oxfmt 0.72.0
  • yuku-parser 0.17.0
  • @yuku-core/wasm 0.17.0
  • @biomejs/wasm-bundler 2.5.15
  • @dprint/typescript 0.96.1
  • dprint-plugin-malva 0.16.0
  • markup 0.27.5
  • postcss 8.5.29
  • @rsvelte/fmt 0.7.25
  • @rsvelte/vite-plugin-svelte-native 0.3.17
  • rsvelte's upstream svelte 5.57.1
  • @swc/core 1.16.13

run

environment

  • AMD Ryzen 5 PRO 7530U with Radeon Graphics
  • linux/x86_64
  • node 24.14.1

Corpus
#

Every in-process timing on this page comes from 7,686 .svelte, .ts/.js, and .css files of real-world code, vendored in the fuzdev/corpora@976ba90 snapshot so one clone reproduces it. They're the author's libraries, apps, and sites (the fuz.dev ecosystem plus personal SvelteKit projects), and upstream framework source (Svelte, SvelteKit, and the svelte.dev site), community Svelte libraries and tooling (Flowbite Svelte's components, LayerChart, Layer Cake, Svelte UX, svelte-maplibre, and Svelte's language tools), and TypeScript other formatter benchmarks compare on (TypeScript's own parser, Storybook, and Continue). By bytes, code the author didn't write is ~57% of the Svelte, ~56% of the TypeScript/JS, and ~47% of the CSS.

  • Test files count as real code and stay in. Fixture files (formatter test suites, and fixture subtrees inside the measured repos) are out: deliberately tricky edge cases measure conformance, not typical throughput, and the conformance page covers them.
  • Standalone CSS files are rare in this ecosystem, so the CSS set adds the <style> blocks of the corpus's Svelte components, concatenated per corpus collection — 25 of its 73 entries. Even so, CSS is the weakest sample, with just 48 standalone files.
  • Read every ratio here as "on this corpus", not as a universal figure.
By source

Each source links its upstream at the commit the snapshot vendored.

sourcecommitfilesSvelteTypeScript/JSCSS
all sources7,6861,9135,70073
fuzdev/zzz c7f4d153261731521
fuzdev/fuz_app 2f76810695336611
fuzdev/fuz_blog f8a11c73821161
fuzdev/fuz_code 55de4336617463
fuzdev/fuz_css cf9f4bf170431252
fuzdev/fuz_docs 3ad0d026646191
fuzdev/fuz_repos d043dee9928701
fuzdev/fuz_mastodon d44985a251591
fuzdev/fuz_template 3f2464e181071
fuzdev/fuz_ui 87809cb2361281080
fuzdev/fuz_util 144fcfc14781381
fuzdev/mdz 9edec8e7021481
fuzdev/gro 777467f161101510
fuzdev/svelte-docinfo 48d7b6211922961
fuzdev/tsv.fuz.dev d91025a3317151
ryanatkn/ryanatkn.com f299ca05231201
ryanatkn/webdevladder.net 7f1873a3924141
ryanatkn/earbetter 5d2ecd97244262
ryanatkn/cosmicplayground fa3efae217131842
sveltejs/kit b27c82f22742230
sveltejs/svelte 5ccdfe341604160
sveltejs/svelte.dev apps/svelte.dev/src996bd6314560850
sveltejs/svelte.dev packages/repl/src996bd635317342
sveltejs/svelte.dev packages/site-kit/src996bd6370263311
themesberg/flowbite-svelte 85f20a03591831760
techniq/layerchart ec05fb15323361906
mhkeller/layercake e7b421e246177663
techniq/svelte-ux 11c79b82991981001
dimfeld/svelte-maplibre 05dd0b214990572
sveltejs/language-tools packages/language-server/srcdafb5f1880880
sveltejs/language-tools packages/svelte2tsx/srcdafb5f1680680
sveltejs/language-tools packages/typescript-plugin/srcdafb5f1260260
sveltejs/language-tools packages/svelte-vscode/srcdafb5f1260260
sveltejs/language-tools packages/svelte-check/srcdafb5f16060
sveltejs/language-tools packages/load-config/srcdafb5f11010
microsoft/TypeScript 5be33461010
storybookjs/storybook code/core/src50736881,08801,0871
storybookjs/storybook code/lib/cli-storybook/src507368811101110
storybookjs/storybook code/lib/codemod/src5073688110110
storybookjs/storybook code/lib/core-webpack/src5073688120120
storybookjs/storybook code/lib/create-storybook/src5073688750750
storybookjs/storybook code/lib/csf-plugin/src50736884040
storybookjs/storybook code/lib/eslint-plugin/src5073688470470
storybookjs/storybook code/lib/react-dom-shim/src50736881010
continuedev/continue cored0a3c0b60206020
continuedev/continue extensions/clid0a3c0b34903490
harvested <style> blocks 250025

tsv's numbers here are from builds without profile-guided optimization (PGO). PGO would make tsv faster but its binaries larger, and that size tradeoff has it on hold.