Skip to content

fuzdev/tsv

Repository files navigation

tsv

a formatter, parser, and future linter + more for Svelte, TypeScript, and CSS - tsv.fuz.dev

tsv is a toolchain for Svelte, TypeScript, and CSS, written in Rust. The first release has a near-Prettier formatter, similar to prettier-plugin-svelte, and a drop-in replacement for Svelte's parser + acorn + acorn-typescript.

For benchmarks including performance and binary size, visit tsv.fuz.dev.

This is an early release, and reports and feedback are appreciated - see the issues and discussions.

AI disclosure: this codebase was generated with machine agents and the usual LLM caveats apply. The first release took 7 months and ~1800 manual commits. It's a high-effort project that emphasizes quality.

Install

tsv ships three WASM packages to npm (native builds will arrive with the v0.2 release):

npm i @fuzdev/tsv_wasm            # or pick a subset: @fuzdev/tsv_format_wasm / @fuzdev/tsv_parse_wasm
npx tsv format src                # if installed locally
npx @fuzdev/tsv_wasm format src   # try the formatter without installing
import {format_svelte} from '@fuzdev/tsv_format_wasm';
const formatted = format_svelte('<script>\nconst   x=1\n</script>');
import {parse_svelte, type Root} from '@fuzdev/tsv_parse_wasm';
const ast: Root = parse_svelte('<script>const x = 1;</script>');

Both halves import the same way from @fuzdev/tsv_wasm. Works without setup in Node.js/Bun/Deno (sync auto-init); browsers and bundlers must call await init() once first. See the package READMEs for the full API and CLI flags:

There are no prebuilt native binaries yet - the npm packages are all WASM. For native speed today, build the C FFI library from source (deno task build:ffi, producing target/release/libtsv_ffi.so/.dylib/.dll) and bind it from anything that speaks C FFI. Deno's FFI is used in the benchmarks.

Design

  • supports Svelte, TypeScript/JS, CSS (and planned HTML/JSON)
  • formatting is similar Prettier and prettier-plugin-svelte for the common case, but intentionally diverges in some cases and fixes numerous bugs (see docs/conformance_prettier.md)
  • tsv can generate a public JSON AST that should exactly match Svelte 5's modern AST with acorn and acorn-typescript (see docs/conformance_svelte.md), but has its own internal optimal AST
  • non-configurable: formatter settings are fixed at Prettier's defaults except printWidth: 100, useTabs: true, singleQuote: true, and trailingComma: 'none' (no trailing comma on multiline lists, matching the Svelte project's own Prettier config), and there are no config files or CLI options for formatting style; tsv is opinionated like gofmt and Python's Black, see CLAUDE.md § Configuration
  • tsv format discovery is gitignore-aware, honoring .gitignore and .formatignore hierarchically (nested files like git, unlike Prettier; also .formatignore is original to tsv) plus a compatible .prettierignore (but relative to repo root if available, not cwd like Prettier's default) (gitignore syntax)
  • Rust-only implementation that never embeds or calls a JS runtime, for performance; JS reaches tsv through the WASM bindings, and native N-API bindings are not yet published
  • outputs optimal artifacts as an invariant, not a preference: runtime speed and compiled code size are first-class goals for every shipped artifact, and heavier future layers (incremental parsing, CST for LSP) will be feature-gated so they don't regress the artifacts that exist today
  • JS and TS always parse as modules in strict mode - sloppy-mode-only syntax like with is rejected, while strict-mode early errors (e.g. duplicate params, reserved-word bindings) still parse for now, with enforcement deferred to a future diagnostics layer; Svelte and TypeScript are inherently strict, so this only matters for standalone JS scripts
  • pushes complexity and mess to the printer and JSON conversion, out of the parser and internal AST, keeping the model clean for the other planned tools

Each language is a self-contained Rust crate exposing the same parse/format/convert_ast functions over its own concrete types - there's no central Language trait, registry, or enum dispatch ("closed scope, open convention"). That means no dynamic dispatch, and WASM builds tree-shake at the link level: the parse build excludes the printers, and the format build excludes the JSON-AST conversion layer. Languages tree-shake the same way - a build binding only TypeScript would exclude Svelte and CSS entirely - though the published packages include all three. Future LSP/incremental features will be later feature-gated layers that don't bloat these artifacts - see docs/architecture.md

tsv's goal is to be an optimal toolchain for Svelte and TypeScript, and avoiding bloat is a key characteristic. The scope trade is depth over breadth: a closed language set with an expanding tool set, instead of more frameworks. Hard non-goals:

  • other frameworks' markup - no Vue, Astro, JSX/TSX, etc (unlike Biome and friends); Svelte is the only component language (it's in the name tsv)
  • CSS preprocessor and vendor dialects - no SCSS, LESS, CSS Modules, or IE hacks; tsv parses standard and Svelte CSS only
  • JS plugins - follows from never embedding a JS runtime; linter extensibility, if any, will be WASM plugins and/or pattern-based rules
  • no style config settings, so on-disk state and caller params never change the output for a given input
  • full Prettier conformance - see discussion 1

Deferred rather than refused:

  • internal AST stabilization - not any time soon; the public JSON AST is the stable surface, tracking Svelte's
  • N-API native bindings - npm is WASM-only for now

tsv is derived from:

  • Svelte
  • TypeScript
  • Prettier and prettier-plugin-svelte
  • HTML/CSS/JS

tsv currently supports:

  • formatter matching Prettier + prettier-plugin-svelte (with intentional divergences)
  • parser, drop-in for Svelte+acorn+acorn-typescript

Future features (unknown order):

  • CSS error recovery (recover past invalid CSS per the spec instead of failing the parse - doesn't add dialect support)
  • type stripper (easy, probably soon)
  • module lexer (easy, probably soon)
  • minifier
  • LSP
  • later
    • linter (type aware, all Rust, maybe WASM plugins and/or pattern-based rules for extensibility)
    • Svelte compiler (exact mirror)
    • bundler
    • typechecker isn't off the table
    • more? see the issues and discussions, suggestions welcome

Docs

Developing

Dev dependencies:

  • Rust - rustc, cargo
  • Deno - see deno.json for the tasks
    • uses npm: imports from svelte, typescript, acorn, @sveltejs/acorn-typescript, prettier, prettier-plugin-svelte

Rust dependencies are kept fairly minimal. See CLAUDE.md § Rust Crates for the full list.

# Build workspace (recommended - uses deno tasks)
deno task build          # dev build
deno task dev            # watch mode (requires: cargo install cargo-watch)
deno task check          # all checks (typecheck, test, lint, fmt)

# Or build directly with cargo
cargo build --workspace
cargo check --workspace  # fast syntax check (no codegen)
cargo test --workspace   # all tests including fixture validation

# Run CLI
cargo run -p tsv_cli parse --content "const x = 1;" --parser typescript
cargo run -p tsv_cli format --content "<div>test</div>" --parser svelte

For the full reference see CLAUDE.md § Commands.

Project structure

Multi-crate workspace with clean separation of concerns:

tsv/
├── Cargo.toml         # workspace root
├── crates/
│   ├── tsv_lang/      # foundation (Span, Location, ParseError)
│   ├── tsv_html/      # HTML classification and whitespace rules
│   ├── tsv_ignore/    # gitignore-aware discovery matcher (.gitignore/.formatignore/.prettierignore)
│   ├── tsv_discover/  # file-discovery policy (build-output heuristic + safety nets) over tsv_ignore
│   ├── tsv_ts/        # TypeScript parser/formatter (standalone)
│   ├── tsv_css/       # CSS parser/formatter (standalone)
│   ├── tsv_svelte/    # Svelte parser/formatter (uses tsv_ts + tsv_css)
│   ├── tsv_cli/       # unified CLI (binary: `tsv`)
│   ├── tsv_debug/     # dev utilities (binary: `tsv_debug`, uses Deno)
│   ├── tsv_ffi/       # C FFI bindings
│   └── tsv_wasm/      # WebAssembly bindings
└── tests/             # workspace-level integration tests

Each language crate exports a consistent API:

  • parse(source) -> Result<AST>
  • format(ast, source) -> String
  • convert_ast(ast, source) -> PublicAST (default-on convert cargo feature; turn off for parse+format-only builds)

See CLAUDE.md for detailed structure and full command reference.

Credits

tsv is an implementation of various software designs and specs:

Software:

Web Standards:

Claude Code was instrumental to this project, and tsv wouldn't exist without LLMs. Source code of projects similar to tsv was not used by agents or consulted by the author unless listed above.

License

MIT

The code for the following projects was sometimes read by AI agents while producing tsv, so their license information is included for completeness:

Svelte Copyright (c) 2016-2026 Svelte Contributors MIT - https://github.com/sveltejs/svelte/blob/main/LICENSE.md

Prettier Copyright © James Long and contributors MIT - https://github.com/prettier/prettier/blob/main/LICENSE

About

formatter, parser, and future linter + more for TypeScript, Svelte, and CSS, written in Rust

Resources

License

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors