Skip to content

Commit 845f393

Browse files
committed
docs(oxfmt,formatter,formatter_json,formatter_core): add/update AGENTS.md (#22873)
- Update: apps/oxfmt - Add: crates/oxc_formatter_core,oxc_formatter,oxc_formatter_json
1 parent 705c0e9 commit 845f393

7 files changed

Lines changed: 234 additions & 10 deletions

File tree

‎apps/oxfmt/AGENTS.md‎

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ The `oxfmt` implemented under this directory serves several purposes.
66

77
- Pure Rust CLI
88
- Minimum feature set, CLI usage only, no LSP, no Stdin support
9-
- Formats JS/TS and TOML files, no xxx-in-js support
9+
- Formats JS/TS, JSON and TOML files, no xxx-in-js support
1010
- Entry point: `main()` in `src/main.rs`
1111
- Build with `cargo build --no-default-features`
1212
- JS/Rust hybrid CLI using `napi-rs`
1313
- Full feature set like CLI, Stdin, LSP, and more
14-
- Format many file types with embedded language formatting support
14+
- Format many file types with embedded language formatting support like Prettier
1515
- Entry point: `src-js/cli.ts` which uses `run_cli()` from `src/main_napi.rs`
1616
- Build with `pnpm build`
1717
- Node.js API using napi-rs
@@ -20,7 +20,7 @@ The `oxfmt` implemented under this directory serves several purposes.
2020

2121
When making changes, consider the impact on all paths.
2222

23-
## Platform Considerations
23+
### Platform considerations
2424

2525
Oxfmt is built for multiple platforms (Linux, macOS, Windows) and architectures.
2626

@@ -31,6 +31,26 @@ When working with file paths in CLI code, be aware of Windows path differences:
3131
- Avoid hardcoding `/` as a path separator; prefer `Path::join()`
3232
- Windows uses `\` as a path separator and has drive letter prefixes (e.g., `C:\`)
3333

34+
### Formatter implementations
35+
36+
Oxfmt utilizes different implementations depending on the file extension and filename:
37+
38+
- Tier 1: Rust implementations using `oxc_formatter` or `oxc_formatter_json` found in this repository
39+
- Tier 2: Rust implementations using external libraries like `oxc_toml`
40+
- Tier 3: Delegations to Prettier via NAPI-JS calls (e.g., for Vue or Markdown)
41+
- Tier 4: Delegations to Prettier that require additional plugins (e.g., for Svelte)
42+
43+
Consequently, managing these various formatter implementations and handling their respective options are also part of Oxfmt's responsibilities.
44+
45+
### CLI implementations
46+
47+
Oxfmt shares code with Oxlint regarding its CLI implementation.
48+
49+
- Rust implementation: `crates/oxc_config`
50+
- JS implementation: `apps/shared`
51+
52+
Please exercise extra caution when making changes to these files.
53+
3454
## Verification
3555

3656
```sh
@@ -44,14 +64,15 @@ Also run `clippy` for the same configurations and resolve all warnings.
4464
Run tests with:
4565

4666
```sh
67+
# Run unit test in Rust
68+
cargo t
4769
# Run E2E test
48-
pnpm build-test && pnpm t
70+
pnpm build-dev && pnpm t
4971
# Update snapshots
5072
pnpm t -u
73+
5174
# Run conformance test for xxx-in-js and js-in-xxx
5275
pnpm conformance
53-
# Run unit test in Rust
54-
cargo t
5576
```
5677

5778
To manually verify the CLI behavior after building:
@@ -67,18 +88,18 @@ cat <file> | node ./dist/cli.js --config=<cfg> --stdin-filepath=<file>
6788
OXC_LOG=debug node ./dist/cli.js --threads=1 <file>
6889
```
6990

70-
NOTE: `pnpm build-test` combines `pnpm build-js` and `pnpm build-napi`, so you don't need to run them separately.
91+
NOTE: `pnpm build-dev` combines `pnpm build-js` and `pnpm build-napi`, so you don't need to run them separately.
7192

7293
To compare formatting output with Prettier:
7394

7495
```sh
75-
# Use a shared config file (e.g., fmt.json) because oxfmt and Prettier have different default printWidth
96+
# Use a shared config file (e.g., fmt.json) because Oxfmt and Prettier have different default printWidth.
7697
# Example fmt.json: { "printWidth": 80 }
7798
cat <file> | node ./dist/cli.js --config=fmt.json --stdin-filepath=<file>
7899
npx prettier --config=fmt.json <file>
79100
```
80101

81-
## Test Organization (`test/` directory)
102+
## Test organization (`test/` directory)
82103

83104
Tests are organized into specific domains, each with its own structure.
84105

@@ -114,7 +135,7 @@ Each test directory follows the 1:1:1 rule:
114135

115136
Shared helpers are in `utils.ts` at the `test/lsp/` level.
116137

117-
## After updating `Oxfmtrc` (Under `src/core/oxfmtrc`)
138+
## After updating `Oxfmtrc` (`src/core/oxfmtrc.rs`)
118139

119140
When modifying the `Oxfmtrc` struct (and configuration options):
120141

‎crates/oxc_formatter/AGENTS.md‎

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Coding agent guides for `crates/oxc_formatter`
2+
3+
## Overview
4+
5+
Prettier compatible JS/TS formatter (`oxfmt`'s Tier 1 backend), ported from [Biome](https://github.com/biomejs/biome).
6+
It turns a parsed AST into an IR ("Document") of `FormatElement`s, then prints that IR via the shared `oxc_formatter_core` Printer.
7+
8+
- Built on `oxc_formatter_core` for the language-agnostic IR + Printer + builders + macros
9+
- See `crates/oxc_formatter_core/AGENTS.md` for the IR/pipeline details
10+
- This crate holds only the JS/TSX-specific layer
11+
- Parses with `oxc_parser`
12+
- Comments and other JS-specific concerns live here, not in core
13+
14+
### Public API
15+
16+
Call-sites use text-in entry points;
17+
The AST-wrapping IR primitives (`AstNode`, `Format`, `Buffer`, …) are `pub(crate)` and not part of the contract.
18+
19+
- `format`: Format a whole file (text-in)
20+
- `format_fragment`: Format a JS/TS fragment for js-in-xxx embedding, parameterized by `FragmentContext`
21+
- Drives context-dependent decisions like forced parentheses / quote style
22+
- The formatter knows nothing about Prettier/Vue vocabulary, callers pass wrapped source
23+
- `format_program`: Special-purpose AST-in entry point
24+
- `ExternalCallbacks` (in `external_formatter.rs`): Callbacks for embedded-doc / Tailwind formatting delegated back to the host
25+
26+
### Generated code
27+
28+
`ast_nodes/generated/` and the `Format` glue are generated by `tasks/ast_tools`.
29+
After changing AST shapes or the generators, regenerate with `just ast`, never hand-edit files under `generated/`.
30+
31+
## JS formatter specific features
32+
33+
### Sort imports (`ir_transform/sort_imports/`)
34+
35+
- Inspired by `eslint-plugin-perfectionist`, but not a 1:1 match (default group definitions differ too)
36+
- Implemented purely as a Rust IR transform; requires no JS callback
37+
38+
### Format JSDoc (`formatter/jsdoc/`)
39+
40+
- Derived from `prettier-plugin-jsdoc`, but not fully compatible
41+
- See `prettier_conformance/jsdoc` for the covered behavior
42+
43+
### Sort Tailwind CSS
44+
45+
- Derived from `prettier-plugin-tailwindcss`
46+
- Classes are collected during IR construction and sorted in one batch when the IR is stringified
47+
- Requires `ExternalCallbacks` (the sort itself is delegated to the host via `TailwindCallback`)
48+
49+
### Embedded language formatting
50+
51+
- Two directions: xxx-in-js (e.g. css/graphql/html in template literals) and js-in-xxx (e.g. vue/svelte)
52+
- Both work by Oxfmt injecting Prettier calls through `ExternalCallbacks`
53+
- This crate stays unaware of Prettier and only invokes the supplied callbacks
54+
55+
## Fixing IR construction
56+
57+
- Always keep the big picture in mind so a fix is not a one-off patch
58+
- Comments and parentheses are especially prone to side effects, handle them with care
59+
- We aim for Prettier compatibility, but the implementation strategy differs:
60+
- Prettier pre-classifies comments per context, whereas oxc_formatter decides on the spot
61+
- Biome works on a CST rather than an AST, so its code and strategy differ in detail too
62+
63+
Above all, prioritize consistency, and always consider whether the divergence is a Prettier bug.
64+
65+
## Verification
66+
67+
```sh
68+
cargo c -p oxc_formatter
69+
cargo c -p oxc_formatter --features detect_code_removal
70+
```
71+
72+
Run `clippy` for the same configurations and resolve all warnings.
73+
74+
### Fixtures tests
75+
76+
Snapshot tests driven by fixture files under `tests/fixtures/{js,ts}/`.
77+
`build.rs` auto-discovers every `.{js,jsx,ts,tsx}` file and generates a test function per file; options are resolved from the nearest `options.json` up the directory tree. See `tests/README.md` for the full workflow.
78+
79+
```sh
80+
# Run all fixtures tests
81+
cargo test -p oxc_formatter --test mod
82+
# Run a subset by module path (directory structure = module hierarchy)
83+
cargo test -p oxc_formatter fixtures::js::comments
84+
# Review / accept snapshots after intentional changes
85+
cargo insta test --accept -p oxc_formatter --test mod
86+
```
87+
88+
Add a case by dropping a new file into `tests/fixtures/`, no manual registration needed.
89+
90+
### Prettier conformance
91+
92+
Compares output against Prettier's snapshots and tracks failures (not passes); results live in `tasks/prettier_conformance/snapshots/`.
93+
94+
```sh
95+
cargo run -p oxc_prettier_conformance
96+
```
97+
98+
### Embedded conformance (`apps/oxfmt`)
99+
100+
The embedded-language features (xxx-in-js / js-in-xxx) are validated end-to-end through the Oxfmt.
101+
102+
Requires a dev build first.
103+
104+
```sh
105+
pnpm --dir apps/oxfmt build-dev
106+
pnpm --dir apps/oxfmt conformance
107+
```

‎crates/oxc_formatter/CLAUDE.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Coding agent guides for `crates/oxc_formatter_core`
2+
3+
## Overview
4+
5+
Language-agnostic formatting infrastructure, ported from [Biome](https://github.com/biomejs/biome)'s `biome_formatter` crate.
6+
7+
Every language-specific formatter in the oxc ecosystem (`oxc_formatter` for JS/TS, `oxc_formatter_json`, and future CSS/GraphQL/etc.) builds on this crate.
8+
It owns the IR and the printing pipeline; it knows nothing about any concrete language (no comments, no quote rules, those live in the consumer crates).
9+
10+
### The IR ("Document") and pipeline
11+
12+
Formatting is two stages:
13+
14+
1. A consumer crate walks its AST and builds an IR, a tree of `FormatElement`s using the `builders` and the `write!` / `format_args!` macros
15+
2. The `Printer` consumes that IR plus `PrinterOptions` and produces the output string, deciding line breaks, indentation, and group expansion
16+
17+
Key IR pieces are all exported from the crate root.
18+
19+
### Generic context design
20+
21+
The core is parameterized over a consumer-supplied context so it stays language-agnostic:
22+
23+
- `FormatContext` trait: no lifetime parameter
24+
- (avoids `oxc_allocator`'s `'ast` propagating through struct bounds and blocking anonymous lifetimes)
25+
- The allocator lives on `FormatState`, not the context
26+
- `FormatOptions` trait: `indent_style()`, `indent_width()`, `line_width()`, `line_ending()`, `as_print_options() -> PrinterOptions`
27+
- Core option types: `IndentStyle`, `IndentWidth`, `LineWidth`, `LineEnding`, `Expand`, `BracketSpacing`
28+
- `Format<'ast, C>` trait + `FormatState<'ast, C>`, `Formatted<'ast, C>`, `Formatter<'buf, 'ast, C>`, `Buffer<'ast, C>`
29+
- All generic over the context `C`, consumers add a `C` bound only on `impl` blocks
30+
- Not on struct definitions, and typically define a `type FooFormatter<…> = Formatter<…, FooContext<…>>` alias to keep lifetimes aligned
31+
32+
## Cargo features
33+
34+
`test_harness` exposes `test_support/` (fixture test generation) to downstream crates.
35+
Consumers still need their own `insta` dev-dep so the recorded `source:` header points to the consumer crate.
36+
37+
## Verification
38+
39+
```sh
40+
cargo c -p oxc_formatter_core
41+
cargo c -p oxc_formatter_core --features test_harness
42+
```
43+
44+
Run `clippy` for both configurations and resolve all warnings.
45+
46+
This crate has basic tests only of its own, it is exercised through the conformance/snapshot tests of its consumers.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Coding agent guides for `crates/oxc_formatter_json`
2+
3+
## Overview
4+
5+
Prettier compatible JSON/JSONC/JSON5 formatter (`oxfmt`'s Tier 1 backend), using the `oxc_formatter_core` APIs.
6+
7+
- Built on `oxc_formatter_core` for the language-agnostic IR + Printer + builders + macros
8+
- See `crates/oxc_formatter_core/AGENTS.md` for the IR/pipeline details
9+
- This crate holds only the JSON-specific layer
10+
- Parses with `oxc_parser`, not `serde_json`
11+
- For Prettier, JSON is not spec compliant JSON
12+
- They are subsets of JS expression syntax, so the comments, unquoted key, etc... are allowed as input
13+
14+
### `JsonVariant`
15+
16+
All variants share lenient parsing (comments, trailing commas, single quotes, unquoted keys all parse regardless of variant).
17+
18+
What differs is the output formatting. See the doc comments on `JsonVariant` in `src/options.rs` for the per-variant rules.
19+
20+
## Verification
21+
22+
```sh
23+
cargo c -p oxc_formatter_json
24+
```
25+
26+
Run `clippy` and resolve all warnings.
27+
28+
### Fixtures tests
29+
30+
Snapshot tests driven by fixture files under `tests/fixtures/json/`.
31+
`build.rs` auto-generates a test case from every `.{json,jsonc,json5}` file using the core `test_support` harness.
32+
33+
```sh
34+
cargo test -p oxc_formatter_json
35+
# Review / accept snapshots after intentional changes
36+
cargo insta review -p oxc_formatter_json
37+
```
38+
39+
Add a case by dropping a new file into `tests/fixtures/json/`, the build script picks it up.
40+
41+
### Prettier conformance
42+
43+
Compares output against Prettier's snapshots and tracks failures (not passes); results live in `tasks/prettier_conformance/snapshots/`. The `json` language is part of the shared conformance binary.
44+
45+
```sh
46+
cargo run -p oxc_prettier_conformance
47+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

0 commit comments

Comments
 (0)