tsv
introduction #
tsv is a toolchain for TypeScript/JS, CSS, and Svelte in Rust. The first release has a formatter that closely follows Prettier + prettier-plugin-svelte, and a drop-in replacement for Svelte's parser + acorn + acorn-typescript.
Compared to Oxc, Biome, and SWC, tsv is a set of focused tools, not a generic language platform, so the focus is web standards and there's no support for JSX/SCSS/etc, beyond Svelte as the only JS framework. The extensibility story is currently limited to using its Rust crates as libraries; bridging to JS or WASM plugins is an open question, but may not be supported.
tsv prioritizes, in order:
- correctness (Svelte and TypeScript conformance, spec adherence for HTML/CSS/JS)
- speed
- binary size and memory usage
- extensibility (valued but deprioritized)
See the benchmarks for stats. Compared to Oxc and Biome, tsv is significantly faster, smaller, and uses less memory to parse and format its supported languages
This is an early release, and reports and feedback are appreciated - see the issues and discussions.
AI disclosure: this codebase is mostly LLM-generated, and the usual caveats apply. The first release took 7 months and ~1800 manual commits. It's a high-effort project that prioritizes quality.
These docs are a work in progress. For design details see the readme.
Install #
tsv ships as WASM packages on npm, a CLI with a formatter and parser:
npm i -D @fuzdev/tsv_wasm
npx tsv format src
npx tsv parse src/foo.svelte For smaller builds, the formatter and parser also ship solo:
npm i -D @fuzdev/tsv_format_wasm
npm i -D @fuzdev/tsv_parse_wasm See the benchmarks for size and performance details.
Native builds are not yet available but are coming in v0.2, see issue 139.
Usage #
All three packages share the same API. The full package exports both halves:
import {format_svelte, parse_svelte, type Root} from '@fuzdev/tsv_wasm';
const formatted = format_svelte('<script>\nconst x=1\n<\/script>');
const ast: Root = parse_svelte('<script>const x = 1;<\/script>'); The formatter alone:
import {format_svelte} from '@fuzdev/tsv_format_wasm';
const formatted = format_svelte('<script>\nconst x=1\n<\/script>'); The parser alone:
import {parse_svelte, type Root} from '@fuzdev/tsv_parse_wasm';
const ast: Root = parse_svelte('<script>const x = 1;<\/script>'); format_typescript, format_css, parse_typescript, and parse_css work the same way, and the parsers return Svelte-compatible JSON ASTs
with bundled TS types. Everything works zero-config in Node.js, Bun, and Deno (sync
auto-init); browsers and bundlers call await init() once first.
Span-only parsing #
For TypeScript and Svelte, the parsers also emit a span-only AST that drops the per-node loc (line/column) object — Svelte also drops name_loc — for a ~46%
smaller, faster-to-materialize result, mirroring acorn's locations: false. Line
and column stay derivable from the start/end offsets plus your source,
so nothing is lost when you have the source.
import {parse_typescript_no_locations, reconstruct_locations} from '@fuzdev/tsv_parse_wasm';
// span-only AST: start/end offsets, no per-node loc (~46% smaller)
const ast = parse_typescript_no_locations('const x = 1;');
// derive line/column back when you need it, no re-parse
reconstruct_locations(ast, 'const x = 1;'); reconstruct_locations(ast, source) walks the tree and adds loc back, mutating in place — exact for TypeScript, approximate for Svelte (it skips the parser's
own name_loc and a couple of position quirks). For sparse lookups, create_locator(source) reuses one line table across calls. CSS has no loc, so there's no span-only variant for it.
Source code #
- github.com/fuzdev/tsv - the formatter, parser, wasm bindings, CLI, etc
- github.com/fuzdev/tsv.fuz.dev - this website
playground #
Try tsv's formatter and parser live in the playground.
benchmarks #
tsv is a toolchain for TypeScript/JS, CSS, and Svelte in Rust. Performance is its priority after correctness, and the numbers compare favorably to Oxc and Biome for its supported languages.
Binary size #
Rather than supporting many languages, tsv focuses on Svelte/HTML, TypeScript/JS, and CSS. This lets it be smaller when it's all you need, a quality that's more relevant when used in the browser via wasm:
wasm
native
Speedier than Prettier #
tsv is heavily inspired by and borrows architectural patterns from Prettier. We're very grateful for the hard work of its contributors. tsv offers a speedup over Prettier:
| Svelte 1194 of 1240 files | TypeScript 3701 of 4025 files | CSS 165 of 193 files | |
|---|---|---|---|
| native | 12.4x | 10.3x | 17.3x |
| wasm | 9.06x | 7.66x | 12.9x |
Format #
tsv's formatter is similar to Oxfmt and Biome. Today it can format Svelte, TypeScript, and CSS, plus HTML and JS (as strict-mode TypeScript):
Formatting 1194 of 1240 svelte files files handled / total · speed
Formatting 3701 of 4025 typescript files files handled / total · speed
Formatting 165 of 193 css files files handled / total · speed
Parse #
The parse rows that build a full JS AST are directly comparable: tsv and oxc-parser both serialize the AST to JSON in Rust and deserialize it in JS, native and wasm alike. The tsv-internal and tsv_wasm-internal rows are tsv's parse-only numbers - they build the native AST but skip JS-side materialization, so they show raw in-engine speed rather than a cross-tool comparison.
Parsing 1192 of 1240 svelte files files handled / total · speed
Parsing 3603 of 4025 typescript files files handled / total · speed
Parsing 159 of 193 css files files handled / total · speed
Benchmarking details #
All numbers are single-threaded: every library formats or parses one file at a time, measured sequentially with no cross-file parallelism. These are per-file, single-core latency and throughput numbers - not the multi-core batch throughput a CLI gets when it formats many files at once, which most of these tools (tsv included) can do.
What's measured: around 5,500 files (~15 MB) of .svelte/.html, .ts/.js, and .css, from three sources: the fuz.dev libraries and apps, upstream framework
source (Svelte, SvelteKit, and the svelte.dev site), and formatter conformance fixtures
(Prettier's and prettier-plugin-svelte's own test suites - deliberately tricky edge cases, not
typical code, so they skew the corpus toward hard cases).