# Technical details

> How tidy is built, why its output matches Prettier, and how that is kept true.

tidy is not a formatter that happens to resemble Prettier. It is a port of Prettier's printers, checked against Prettier on every change.

## The document printer

Prettier's design separates two jobs. A printer turns a syntax tree into a _document_: a tree of groups, line breaks, indents and concatenations that describes how the output _could_ be laid out. A second pass walks that document with a width and decides which groups fit on one line and which break.

tidy ports both, in `crates/tidy/src/doc`. Because the second pass is the same algorithm with the same measurements — down to the table of character widths, so that a CJK character or an emoji counts the same — a document built the same way lays out the same way.

That is why matching Prettier is feasible at all. The hard, subtle part is shared; each language only has to build the same document.

## The parsers

Each language needs a tree shaped the way Prettier's printer expects, which usually means porting the parser Prettier uses, not a faster one with a different shape:

| Language               | Parser                                                                   |
| ---------------------- | ------------------------------------------------------------------------ |
| JavaScript, TypeScript | oxc, through oxc's formatter and Bun's port                              |
| HTML, Vue              | angular-html-parser                                                      |
| Handlebars             | the Handlebars parser, simple-html-tokenizer and `@glimmer/syntax`       |
| CSS, SCSS, Less        | PostCSS, postcss-scss, postcss-less and their value and selector parsers |
| YAML                   | the `yaml` package's lexer and CST parser, plus yaml-unist-parser's tree |
| GraphQL                | graphql-js, through Bun's port                                           |
| TOML                   | taplo                                                                    |
| XML                    | `@xml-tools/parser`, rewritten as a hand-written lexer and parser        |
| Dockerfile             | dprint-plugin-dockerfile                                                 |
| INI                    | `prettier-plugin-ini`                                                    |
| Markdown               | its own, following Prettier's handling                                   |

A tree with a different shape produces a different document, and a different document lays out differently. Where a port changes the shape — XML's parser is hand-written rather than generated by chevrotain — it is written to reject the same input and produce the same tree.

## How it stays true

The test suite runs Prettier itself. For each corpus, every file goes through both tools and the bytes are compared. A difference fails the build unless it is a recorded [deviation](/docs/deviations/), and each deviation has its input in `crates/tidy/tests/deviations.json` with a test that fails if tidy stops printing what the entry says.

That is the real reason to trust the claim: it is not a promise, it is a test over 12,000 files.

It also means tidy tracks a Prettier version. tidy follows Prettier 3.9.9, and Markdown follows 3.9.10. When Prettier changes its output, tidy is behind until it is ported.

## Stability

Prettier's output is not always a fixed point. For some inputs, formatting twice gives a third result, because the first pass changes something the second pass measures differently.

Where that happens, tidy prints the result Prettier settles on. So `tidy check` does not report a file that Prettier itself would reformat on the next run. This is not counted as a deviation, because the two agree once Prettier has stopped changing its mind.

## Speed

Most of it is not clever. It is the absence of overhead:

- **No runtime start.** A Node process costs tens of milliseconds before it reads a file.
- **No garbage collector.** Trees are built in a bump allocator and dropped whole.
- **Buffers are reused.** A `Formatter` keeps its scratch space between files, so a 12,000-file run does not allocate 12,000 times.
- **Every core.** The walk and the formatting are spread across a thread pool. `--threads 1` turns that off.
- **The walk is tuned too.** `tidy_walk` is a separate crate, with a thread count that measured best per platform.

## Repository layout

| Crate              | What it is                                                                    |
| ------------------ | ----------------------------------------------------------------------------- |
| `crates/tidy`      | The library: every parser, printer, the document printer, and config reading. |
| `crates/tidy_cli`  | The `tidy` command.                                                           |
| `crates/tidy_napi` | The `tidyrs` npm package.                                                     |
| `crates/tidy_walk` | The parallel directory walk.                                                  |

## Licensing

tidy is MIT, and ports MIT, ISC and Apache-2.0 code. Every project it ports from is listed in [Credits](/docs/credits/), with the license.
