https://github.com/select/fjson-fmt
Prettier-style check+write JSON formatter using the FracturedJson algorithm (Rust engine compiled to WASM), built to run alongside oxfmt.
https://github.com/select/fjson-fmt
Last synced: 9 days ago
JSON representation
Prettier-style check+write JSON formatter using the FracturedJson algorithm (Rust engine compiled to WASM), built to run alongside oxfmt.
- Host: GitHub
- URL: https://github.com/select/fjson-fmt
- Owner: select
- License: other
- Created: 2026-06-07T18:47:03.000Z (14 days ago)
- Default Branch: master
- Last Pushed: 2026-06-07T20:41:03.000Z (14 days ago)
- Last Synced: 2026-06-13T01:34:10.231Z (9 days ago)
- Language: Rust
- Homepage: https://select.github.io/fjson-fmt/
- Size: 1.61 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# fjson-fmt
A **Prettier-style `--check` + `--write` formatter for JSON** built on the
[FracturedJson](https://j-brooke.github.io/FracturedJson/) algorithm - compact,
human-readable JSON with smart line breaks and table-like alignment.
The formatting engine is the Rust crate
[`fracturedjson`](https://github.com/fcoury/fracturedjson-rs) (MIT), **vendored
into `crate/` and compiled to WebAssembly** via `wasm-pack`. The CLI is a thin
Node.js layer that adds the check/write workflow, globbing, and config discovery
that no existing FracturedJson port provides.
It turns this:
```json
{
"Isotopes": {
"Hydrogen": [1, 2, 3],
"Carbon": [11, 12, 13, 14],
"Molybdenum": [92, 94, 95, 96, 97, 98, 100]
},
"ElementProperties": [
{ "symbol": "C", "number": 6, "mass": { "amu": 12, "round": 12 }, "phase": "solid" },
{ "symbol": "O", "number": 8, "mass": { "amu": 16, "round": 16 } },
{ "symbol": "Fe", "number": 26, "mass": { "amu": 56, "round": 56 }, "phase": "solid" }
]
}
```
using `npx fjson-fmt --stdin < example.json`, into this - compact, but with fields aligned like a table:
```json
{
"Isotopes" : {
"Hydrogen" : [ 1, 2, 3 ],
"Carbon" : [11, 12, 13, 14 ],
"Molybdenum": [92, 94, 95, 96, 97, 98, 100]
},
"ElementProperties": [
{ "symbol": "C", "number": 6, "mass": {"amu": 12, "round": 12}, "phase": "solid" },
{ "symbol": "O", "number": 8, "mass": {"amu": 16, "round": 16} },
{ "symbol": "Fe", "number": 26, "mass": {"amu": 56, "round": 56}, "phase": "solid" }
]
}
```
Long arrays of arrays get packed multiple items per line, neatly aligned:
```json
{
"Bonds": [
[ 6, 8], [ 6, 8], [ 8, 1], [ 8, 1], [ 6, 1], [ 6, 1], [ 6, 1], [ 6, 6], [ 6, 6], [ 6, 7], [ 7, 1],
[ 7, 1], [ 6, 16], [16, 1], [15, 8], [15, 8], [15, 8], [11, 17], [11, 17], [12, 8], [12, 8], [20, 9],
[20, 9], [19, 17], [19, 17], [13, 8], [13, 8], [13, 8], [14, 8], [14, 8], [ 5, 9], [ 5, 9]
]
}
```
It is designed to run **alongside [oxfmt](https://oxc.rs/docs/guide/usage/formatter)**
(which owns JS/TS/CSS/etc.), since neither oxfmt nor oxlint can format `.json`
with FracturedJson.
## Browser playground
**[▶ Try it live: select.github.io/fjson-fmt](https://select.github.io/fjson-fmt/)**
A zero-backend playground lives in [`docs/`](docs/) and is served via GitHub
Pages. The same Rust engine is built for the browser
(`wasm-pack build --target web`) and loaded as an ES module — the whole thing
is ~185 KB of WASM and runs entirely client-side, with a claymorphism UI and a
light/dark theme toggle.
[](https://select.github.io/fjson-fmt/)
```sh
npm run build:web # rebuild docs/pkg from the crate
npx serve docs # preview locally, then open the printed URL
```
Deployed automatically on each `v*.*.*` release tag
([`deploy-to-pages.yml`](.github/workflows/deploy-to-pages.yml)). Force a theme
with `?theme=light` or `?theme=dark`.
## Install
```sh
npm i -D fjson-fmt # ships prebuilt WASM; no Rust toolchain needed
```
## Usage
```sh
# Format files in place (default)
fjson-fmt "**/*.json"
# Verify formatting (CI) - exits 1 if anything would change
fjson-fmt --check "**/*.json"
# List files that differ (no writing)
fjson-fmt --list-different "**/*.json"
# stdin → stdout
cat data.json | fjson-fmt --stdin --indent 2
# Format a file to stdout without modifying it
npx fjson-fmt --stdin < example.json
```
Run alongside oxfmt:
```jsonc
// package.json
{
"scripts": {
"fmt": "oxfmt && fjson-fmt \"**/*.json\"",
"fmt:check": "oxfmt --check && fjson-fmt --check \"**/*.json\""
}
}
```
## Programmatic API
The package is **isomorphic** - the same import works in Node and in the
browser (or any bundler). Conditional `exports` route to a Node build (WASM
loaded synchronously) or a web build (WASM fetched + instantiated once).
```js
import { init, format, formatSync, engineVersion } from "fjson-fmt";
// Portable: `format` is async and initializes the engine on first use.
const pretty = await format('{"a":[1,2,3],"bb":[44,55]}', {
max_total_line_length: 120,
indent_spaces: 2,
});
// Sync path: available immediately in Node; in the browser call `await init()`
// first (it's idempotent), then `formatSync` is synchronous.
await init();
const out = formatSync(input, { number_list_alignment: "right" });
```
- **Node** resolves the `"node"` export (synchronous WASM); `formatSync` works
with no `init()`.
- **Browsers / bundlers** (Vite, webpack 5, Rollup) resolve the `"browser"`
export; the `.wasm` is located via `new URL(..., import.meta.url)`, so no
extra loader config is needed.
Option keys are the canonical snake_case `FracturedJsonOptions` names (see
[`crate/src/options.rs`](crate/src/options.rs)); TypeScript types ship with the
package.
## Options
CLI flags (override config files):
| Flag | Effect |
|------|--------|
| `--write` | Format in place (default) |
| `--check` | Verify; exit 1 if any file would change |
| `-l, --list-different` | List differing files; exit 1 if any |
| `--stdin` | Read stdin, write formatted result to stdout |
| `-c, --config ` | Explicit config file |
| `--no-config` | Skip config discovery |
| `--indent ` | Spaces per indent (default 4) |
| `--tabs` | Indent with tabs |
| `--max-line ` | Max total line length (default 120) |
| `--eol ` | Line ending style |
| `--comments ` | Comment policy |
| `--trailing` | Allow trailing commas in input |
| `--no-final-newline` | Don't append a trailing newline |
## Config files
Discovered by walking up from each file's directory (like fracjson):
`.fracturedjson`, `.fracturedjson.jsonc`, `.fracturedjson.json`. Comments and
trailing commas are allowed. Keys map to `FracturedJsonOptions` and are
case-insensitive (snake_case, camelCase, PascalCase, and fracjson long names
all work):
```jsonc
{
// .fracturedjson.jsonc
"indent_spaces": 2,
"max_total_line_length": 100,
"comment_policy": "preserve",
"allow_trailing_commas": true
}
```
Full option list: see `crate/src/options.rs`.
## Development
Requires Rust + the `wasm32-unknown-unknown` target + `wasm-pack`, plus
[pnpm](https://pnpm.io) for the Node tooling.
```sh
rustup target add wasm32-unknown-unknown
cargo install wasm-pack # or use the binary installer
pnpm install # dev dependencies (release-it, etc.)
pnpm build:wasm # rebuilds pkg/ from crate/
pnpm test # node --test (CLI/engine glue, no toolchain)
pnpm test:engine # cargo test - FracturedJson engine suite (needs Rust)
pnpm test:all # both of the above
```
### Releasing
Releases are cut with [release-it](https://github.com/release-it/release-it)
(conventional-changelog, angular preset). It runs the full test suite, bumps the
version, updates `CHANGELOG.md`, tags, pushes, creates a GitHub Release, and
publishes to npm. The `release` script injects a `GITHUB_TOKEN` from the `gh`
CLI automatically, so you just run:
```sh
pnpm release # interactive; or: pnpm release --ci patch|minor|major
```
### Engine test suite
The Rust engine is verified by the FracturedJson port's integration test suite
(vendored from `fcoury/fracturedjson-rs`, which itself tracks j-brooke's
cross-implementation "universal" tests). It lives in `crate/tests/` and runs
against the standard shared fixtures in `test/StandardJsonFiles/` and
`test/FilesWithComments/` (from `j-brooke/FracturedJsonJs`):
```sh
pnpm test:engine # or: cargo test --manifest-path crate/Cargo.toml
```
The prebuilt `pkg/` (WASM + JS glue) is committed so the CLI works without a
Rust toolchain.
## License
MIT. The vendored engine in `crate/src/*.rs` (except `lib.rs`) is from
`fcoury/fracturedjson-rs`, MIT © Felipe Coury - see `crate/UPSTREAM-LICENSE`.