oxc icon indicating copy to clipboard operation
oxc copied to clipboard

formatter: implement a new `oxfmt` application

Open camchenry opened this issue 8 months ago • 1 comments

Although the formatter is in the very early stages of development, I think it would be useful to start working on an oxfmt executable which functions and is named similarly to oxlint.

In the long-term, we could implement some features that Prettier supports but for now I would say that we just support the following:

  • Can format all JS files passed as paths: oxfmt src -> format all files in src directory
  • Output the newly formatted code to standard output by default
  • Write formatted code in-place if --write is passed
  • Output some basic stats at the end: Formatted 100 files in 100ms.

This way, we can start working on benchmarking and see how we compare to prettier and Biome and where we can improve.

camchenry avatar Apr 23 '25 19:04 camchenry

Thank you for the initiative. Feel free to assign this to yourself if you are a core team member.

Boshen avatar Apr 24 '25 02:04 Boshen

How do we feel about a single top-level binary combining the linter and formatter? It's less analogous to eslint but similar to what biome does

DonIsaac avatar Apr 27 '25 19:04 DonIsaac

How do we feel about a single top-level binary combining the linter and formatter? It's less analogous to eslint but similar to what biome does

Why don't we have both ;-)

Boshen avatar Apr 28 '25 02:04 Boshen

How do we feel about a single top-level binary combining the linter and formatter? It's less analogous to eslint but similar to what biome does

From a user perspective, I think this would be good, like having parallel commands such as:

  • ox fmt
  • ox lint

Although I don't think we should do it all in one single binary. The oxlint binary is already 6MB on many architectures and adding the formatter would probably push it much higher. I think it could make sense to have a single ox binary that sends the command to the correct binary (if it is installed), i.e.: ox fmt <args> => oxfmt <args>, ox lint <args> => oxlint <args>. We'd also probably see faster compilation/tests by having separate binaries.

camchenry avatar Apr 28 '25 15:04 camchenry

+1 for parallel commands, i think it adds extra flexibility (user can use just 1, or the other without installing both, but for users that want both, they can use the unified version). i was thinking it could be similar to cargo perhaps e.g. cargo fmt effectivly just forwards it on to rustfmt.

It's also probably an easier lift to merge to two at a later date vs trying to split them.

camc314 avatar Apr 28 '25 15:04 camc314

I've tried to write up my thoughts (with the help of ai) into the different phases i think make sense, so let me know what y'll think of the following:

Phase 1

Goal

Implement a working CLI tool that formats JS/TS/JSX/TSX files via oxc_formatter, similar in structure to oxlint, with basic configuration and file ignoring.

Scope

  • Prettier compat: compatibility with prettier #10179
  • Inputs: Files or directories passed as CLI args.
  • File Matching: Only .js, .jsx, .ts, .tsx files.
  • Ignore Support: Respect .gitignore. (possibly out of scope, but ignores in config file?)
  • Configuration: oxfmt.json? reuse .oxlintrc.json, copy prettier's config files?
  • Perf: Use rayon to parallelize load
  • CLI Flags:
    • --check
    • --write (default)
    • --config
    • --ignore-path
  • Exit Codes:
    • 0 if formatting is correct.
    • 1 if --check fails due to unformatted files.
  • Logging: Basic console output to indicate formatted, unchanged, or errored files.

Out of Scope (for Phase 1)

The following features are intentionally deferred to future phases:

  • Formatting from stdin (--stdin, --stdin-filepath):
    Reason: Handling stdin formatting (especially preserving correct file context for parsing) adds complexity. It is less critical for an initial version focused on bulk project formatting.
  • Partial file formatting:
    Reason: Only full-file formatting will be supported initially. Incremental or range-based formatting can be added later if needed for editor integrations.
  • Advanced configuration merging or overrides:
    Reason: Initial support will only load a single config file. Hierarchical project config support can be considered later.
  • Caching of unchanged files:
    Reason: While caching improves performance, the first milestone focuses on correctness and stability first.

The first milestone is scoped narrowly to ensure quick iteration and feedback before expanding feature complexity.

Phase 2: Language Server Integration (LSP)

Goal

Integrate oxc-fmt into an LSP (Language Server Protocol) server to support on-save formatting and formatting-on-type in editors like VSCode.

Complexity Note:
Handling LSP formatting correctly requires special attention to cursor placement and partial document updates.
Incorrectly adjusting formatting boundaries could lead to poor user experience during typing. This phase would require careful API design for safe edits.

Phase 3: Nested Config Support

Goal

Support nested .oxc-fmt.json configuration files, similar to how ESLint and Prettier allow per-folder settings.

Note:
We could probable reuse and generalize the existing nested config logic from the linter. Possibly by moving it to a new shared crate (e.g., oxc_config_builder) to avoid duplication across oxlint and oxc-fmt.

camc314 avatar Apr 28 '25 19:04 camc314

@leaysgur will lead on this.

Current thought:

  • Optimize for the fast path, CLI should be still be in Rust, where we may read and format 10k+ files for large monorepos
  • Take prettier as a dependency, use it to format embedded languages, we still want its ecosystem and other language support
  • I think most of these are fairly easy to implement with the help of AI
  • Most users complain about configuration, IDE, eslint. We should spend more time on DX around these things.

I did a quick research by using gemini deep research:

https://docs.google.com/document/d/1hUn7d3XYMhqYUZy_xiIouXbuN2H5gS42OUGs2sHveSo/edit?usp=sharing

Boshen avatar Aug 19 '25 07:08 Boshen

Just a thought about performance, sorry if I'm missing the point here as I have no experience, but theoretically, linter and formatter might be doing some things the same way (like searching files and parsing) so if we aim to use both, it might make sense for the formatter to reuse some data from the linter instead of doing it twice?

tomoglavas avatar Aug 23 '25 14:08 tomoglavas

Work is initiated in https://github.com/oxc-project/oxc/tree/main/apps/oxfmt by @leaysgur

Boshen avatar Sep 09 '25 07:09 Boshen