tidy v0.0.0

00

Prettier's style, at native speed.

tidy is a code formatter written in Rust. One binary prints what Prettier prints, for every language below. It does not shell out to Node — though there is a Node binding if you want one.

~/project
$curl -fsSL https://tidy.lukeed.com | sh

or build it with cargo — every way to install

01 / Speed

18.5s → 280msA 12,071-file corpus of every language, checked end to end.
JavaScript3,906 files
tidy 78 ms
bun fmt 100 ms
oxfmt 123 ms
prettier 109× slower 8,528 ms
TypeScript3,309 files
tidy 58 ms
bun fmt 85 ms
oxfmt 96 ms
prettier 130× slower 7,523 ms
JSON4,569 files
tidy 86 ms
bun fmt 149 ms
oxfmt 114 ms
prettier 41× slower 3,528 ms
Markdown2,834 files
tidy 37 ms
bun fmt 71 ms
oxfmt 80 ms
prettier 179× slower 6,618 ms

M1 Max, all cores, mean of both run orders. Bars compare the three native tools; Prettier is off that scale. every corpus and the method →

02 / Output

Byte for byte what Prettier prints

Not a lookalike style. tidy's tests run Prettier over the same corpus and compare the bytes, so a difference is a bug — unless it is one of the documented deviations.

routes.tsas written

import {readFile} from 'node:fs/promises'

type Route={
path:string,run:(req:Request)=>Promise<Response>}

const routes:Route[]=[
{path:'/',run:()=>page("index.html")},
  {path:"/up",run:async()=>new Response('ok')},
]

export function match(path:string){
return routes.find(r=>r.path===path)??null}

async function page(file:string){
const body=await readFile(file,"utf8")
  const type={"content-type":'text/html'}
return new Response(body,{headers:type})}

routes.tstidy up

import { readFile } from "node:fs/promises";

type Route = {
  path: string;
  run: (req: Request) => Promise<Response>;
};

const routes: Route[] = [
  { path: "/", run: () => page("index.html") },
  { path: "/up", run: async () => new Response("ok") },
];

export function match(path: string) {
  return routes.find((r) => r.path === path) ?? null;
}

async function page(file: string) {
  const body = await readFile(file, "utf8");
  const type = { "content-type": "text/html" };
  return new Response(body, { headers: type });
}

03 / Why

Matches Prettier

Every release is checked against Prettier 3.9.9 over a 12,000-file corpus: 100% of 3,906 JavaScript files and 3,308 of 3,309 TypeScript files.

Formats the code inside your code

Styles and scripts in HTML and Vue. Markdown code blocks and front matter. CSS, GraphQL, HTML and Markdown in JS template literals.

Configurable, if you need it

Reads tidy.json, tidy.jsonc or tidy.toml, on top of .editorconfig. The defaults are Prettier’s.

Stable output

Where Prettier would change its mind on the next run, tidy prints where it settles, so tidy check stays green in CI.

04 / Languages

Every language Prettier formats

Plus the ones its plugins cover, built into the same binary. Nothing to install, nothing to register.

  • JavaScript, TypeScript, JSXPrettier
  • HTML, Vue, Handlebars, MJMLPrettier
  • CSS, SCSS, LessPrettier
  • JSON, JSONC, JSON5Prettier
  • YAML, GraphQL, MarkdownPrettier
  • XML, SVG@prettier/plugin-xml
  • TOMLtaplo
  • Dockerfilesdprint-plugin-dockerfile
  • INI, .editorconfig, .npmrcprettier-plugin-ini

05 / Config

Configurable, if you need it

Each file takes the nearest tidy.json, tidy.jsonc or tidy.toml, on top of .editorconfig. Shared keys apply everywhere, and a language section overrides them. See every option.

tidy.json
{
  "width": 100,
  "js": { "semi": false, "quote": "single" },
  "markdown": { "wrap": "always" }
}

Then run it:

Shell
# print one file, formatted
$ tidy index.html

# list what isn't formatted; exits 1 if any
$ tidy check src

# rewrite everything in place
$ tidy up .

# only what this branch changed
$ tidy up --changed
Esc