Commit Graph

89 Commits

Author SHA1 Message Date
Boshen ac4785a139 perf(diagnostics): use fixed ANSI styles (#26130)
Replace dynamic diagnostic styles with fixed ANSI prefixes, reducing `oxfmt` and `oxlint` release text size.

AI assistance: Codex was used. The contributor remains responsible for this change.
2026-08-27 05:19:35 +00:00
leaysgur 673dc6db69 docs(oxfmt): move some docs from AGENTS.md to code comment (#25649)
Follow up on https://github.com/oxc-project/oxc/pull/25531#discussion_r3773878394
2026-08-14 09:48:31 +00:00
leaysgur a38f7b3d57 fix(oxlint,oxfmt): apply .gitignore only to walk targets, not explicitly named files (#25531)
Possibly fixes #25259, and partial revert of #25133.

---

- `.gitignore` applies only to discovery (walking directory targets, cwd, globs)
- An explicitly named file target is linted / formatted even when gitignored
  - = Restoring the pre-[#25133]() behavior for files, while fully keeping the [#25107]() fix for walk roots

| Target | Current | This PR |
| --- | --- | --- |
| Explicit file (`oxlint dist/a.js`) | skipped | linted |
| Explicit directory (`oxlint dist`) | skipped | skipped |
| cwd inside ignored tree | skipped | skipped |

The underlying principle:

> `.gitignore` is a message addressed to git, not to tools.
> Borrowing it to scope discovery is a useful heuristic, and a heuristic may narrow what the walk finds, but never refuse what the user names.
> Only tool's own ignore config can refuse. (which remain hard for the husky staged-file case).

This also re-aligns the CLI with stdin and LSP, which deliberately do not consult `.gitignore` (#19206).
👉🏻 This is the main motivation.

### Why not full revert

A directory target names where to search, not what to process.
The files under it are still discovered by the walk, so discovery rules apply.
Reverting that would bring back [#25107](), whose old behavior was not even a statable spec: whether running inside an ignored tree processed anything depended on the ignore pattern's form (bare `generated` did, `generated/**` didn't), an artifact of walker pruning, not a design.

### Regarding to `--no-ignore`

This flag remains orthogonal as a walk-level opt-out (`--no-ignore=vcs dist`) and for filling gap between existing tools.

However, with explicit files working again, I'm inclined to close those PRs and revisit if demand shows up.

For [#25259]()'s use case: name the files, e.g. `oxlint dist/foo.js` (or shell-expanded glob).
2026-08-14 03:35:34 +00:00
Boshen 030857b509 refactor(diagnostics): update oxc-miette to v4 (#25395)
Remove the miette `Report` wrapper and update oxc-miette to v4's narrowed diagnostic and source protocol.

AI-assisted by OpenAI Codex.
2026-08-10 07:48:02 +00:00
leaysgur a26f856402 refactor(oxfmt): rename external_formatter to external_services (#25429)
`ExternalFormatter` is merely a transport that calls the JS side via napi; it is not referred to as a formatter.
2026-08-09 15:07:40 +00:00
leaysgur 20f68e74a3 fix(oxlint,oxfmt): respect .gitignore for walk targets and explicit paths (#25133)
Fixes #25107.

The `ignore` crate walker enforces gitignore's "everything under an ignored directory is ignored" rule only by pruning ignored directories during traversal.

And it never filters its walk roots.
So when a walk target (based on cwd or an explicit CLI target path) is already inside a gitignored tree, pruning never happens and a bare directory pattern like `generated` matches nothing file-level. (even though `git check-ignore` says they are ignored)

See also BurntSushi/ripgrep#2595

### Changes

- oxc_config: added `GitignoreChecker`
  - a pre-walk check that replays git's resolution order over the ancestor chain
  - Matchers and `.git`/`.jj` probes are cached per directory
- oxlint / oxfmt: filter all CLI targets through this check before walking

### Behavior changes

- Explicitly passed gitignored files are now skipped (previously always linted/formatted)
- `oxlint --no-ignore` no longer force-lints gitignored targets
  - The same behavior as `--no-ignore` does not disable during walk `.gitignore`

<details>
<summary>FYI: Current our ignore model overview</summary>

Two independent families, AND-ed (a path excluded by any layer is skipped):

| Layer                                        | Source                                                 | oxlint                                                                                         | oxfmt                                                |
| -------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| git-derived (always on, not user-disablable) | nested `.gitignore`, `.git/info/exclude` (during walk) | walker via shared `configure_walk_builder`                                                     | same (shared)                                        |
|                                              | walk roots / explicit CLI targets                      | `GitignoreChecker` (this PR)                                                                   | `GitignoreChecker` (this PR)                         |
| tool-owned                                   | ignore file                                            | `.eslintignore` / `--ignore-path`; disabled by `--no-ignore`                                   | `.prettierignore` / `--ignore-path`; no disable flag |
|                                              | CLI patterns                                           | `--ignore-pattern`; disabled by `--no-ignore`                                                  | `!pattern` path args                                 |
|                                              | config `ignorePatterns`                                | `LintIgnoreMatcher` (already containment-correct via `matched_path_or_any_parents`); always on | scope-local `is_path_ignored` (same); always on      |

NOTE: `ignorePatterns` never had this bug, only the walker-backed gitignore layer did; this PR brings it to the same level.
A `!` negation in `ignorePatterns` cannot re-include gitignored paths (unchanged; the layers are independent).

</details>
2026-08-02 13:11:06 +00:00
leaysgur be7f6da49a feat(oxfmt): colorize diff file list in check mode (#25061)
Fixes #25018
2026-07-29 06:00:25 +00:00
leaysgur e81b2ff6c5 fix(oxlint,oxfmt): report error for invalid glob patterns (#24744)
Fixes #24612

- Update `fast-glob` to `1.1.0` which exposes `validate()` api
- Use it in `oxc_config`, apps will be validated through `deserialize`
- Oxfmt supports globs for CLI paths, also validated
2026-07-21 04:43:49 +00:00
leaysgur f6114d3f48 refactor(oxfmt): Drop unused resolvePlugins stub (#22518) 2026-05-18 06:31:22 +00:00
leaysgur 70ceea53d1 refactor(oxfmt): Move resolve_file_scope_config into core::config (#22442) 2026-05-15 03:20:29 +00:00
leaysgur 7d42a8beee refactor(oxfmt): Clean up codes and comments (#22404) 2026-05-15 03:20:29 +00:00
leaysgur 123c493797 perf(oxfmt): Reduce more syscalls (#22380)
Lesson learned from: #22232

The code itself in this PR was removed due to architectural changes, but we need to preserve its underlying intent.
2026-05-15 03:20:29 +00:00
leaysgur 441d7249e3 fix(oxfmt): Fix "race probe" logic with unit tests (#22378)
Race probe: A protective measure required to correctly locate the nearest config file without a pre-scan.

The `ignore` crate traverses directories in parallel. Because this behavior is somewhat akin to a DFS and is further influenced by factors like OS resource availability, it is unpredictable.

Consequently, when each file attempts to locate its nearest config, we must prevent it from "settling" for a config further up the directory tree and accidentally adopting the wrong one.
2026-05-15 03:20:28 +00:00
leaysgur 3e9c2bd87d refactor(oxfmt): Fix up config and walk modules (#22375) 2026-05-15 03:07:01 +00:00
leaysgur 21bb5d1c1d perf(oxfmt)!: Avoid config pre-scan (#22258)
Fixes #22250

> I'm benchmarking oxfmt on a big repo with around 50k directories.
> https://github.com/oxc-project/oxc/issues/22225
>
> The changes in this PR improve the times from 3.4s -> 2.8s, so 600ms savings 🎉
> https://github.com/oxc-project/oxc/pull/22258#issuecomment-4406579633

When I tested this on a local repo of about 20k, I didn't see as much improvement as reported, but there was definitely an improvement.

---

> [!WARNING]
> This PR changes the CLI behavior in the following nitch cases.

- When you have nested configs,
- and one of which is invalid,
- and you run it in "write" mode

Previously, the CLI would pre-scan the all config files, so an error would be reported immediately and no files would be formatted at all.

With this PR, while it will still report an error and abort upon finding an invalid config file, it will now proceed to format the files for any parts where a valid config can be applied.

In other words, it is highly unlikely that you will encounter this change.
2026-05-15 02:46:09 +00:00
leaysgur 6e8e8185c0 feat(oxfmt): Experimental .svelte support (#21700)
Part of #19715

Note that:

> Users are required to perform 2 steps below
> 1️⃣ Install svelte/compiler@^5.0.0 and/or @astrojs/compiler@^2.9.1 manually
> 2️⃣ Enable option in config
2026-05-11 01:24:37 +00:00
leaysgur 22db602be1 refactor(oxfmt): Update comments and utils in walk.rs (#22245) 2026-05-08 05:32:31 +00:00
Jovi De Croock b7566820f0 perf(oxfmt): optimize nested config prescan (#22232)
Fixes https://github.com/oxc-project/oxc/issues/22225

Optimizes `oxfmt` nested config discovery by replacing the sequential
per-directory config file probing with a parallel entry-based pre-scan.
Previously, `prescan_config_locations()` checked every directory for
each supported config filename, causing many `fs::metadata` calls in
larger repos. The new approach walks entries in parallel, only inspects
config-looking files, deduplicates their parent directories, and keeps
existing nested config loading semantics unchanged.

In an ideal world we could probably do this even more lazy but I wasn't
sure how to best change that with the current architecture, this fix is
deliberately minimal

> [!NOTE]
> This PR was authored by an me and reviewed by an LLM, which rightfully
decided to update the comment at the top
2026-05-08 12:23:47 +09:00
leaysgur 5c6c390d47 feat(oxfmt)!: Respect more git ignore options, align with Oxlint (#22210)
Fixes #22034.

Oxfmt now respects the same Git ignore rules as Git itself:

- Reads `.gitignore` from parent directories (up to the repository boundary)
- Reads `.git/info/exclude`

like Oxlint does.
2026-05-07 07:42:28 +00:00
leaysgur d4d2b8203e refactor(oxfmt): Use Arc<Path> to cheap clone (#21883) 2026-04-28 06:18:00 +00:00
leaysgur ba86c2edca refactor(oxfmt): Classify file kind and resolve option in 1 step (#21880)
We can't determine whether a file path should be formatted or ignored without resolving the options, including the `overrides`.

Since there's no longer any point in keeping these separate, merging them into one.
2026-04-28 05:58:45 +00:00
leaysgur 27e9beb813 refactor(oxfmt): Resolve options on walk phase (#21875)
Prepare for Svelte and Astro support.

In order to enable or disable the detection of `.svelte` files based on the presence of the `svelte` option with the `overrides` resolved, we need to resolve the final options during the walk phase.
2026-04-28 05:58:45 +00:00
leaysgur 89f5ef844a refactor(oxfmt): Intro FormatStrategyBuilder (#21697)
Prepare for svelte+astro plugin support.

Now `FormatStrategy` can be built by config, and its experimental settings.
2026-04-24 07:25:54 +00:00
leaysgur c15b00cad0 refactor(oxfmt): Clean up with oxc_config_discovery (#21695) 2026-04-24 04:39:37 +00:00
Yuji Sugiura 0dce3c67dd fix(oxfmt): Handle invalid overrides config without panic (#21661)
Fixes #21651

Currently, we validate the config in advance (so, the root won't panic).
However, validating `overrides` beforehand is difficult.

- Since they are cumulative, the final result depends on specific file
paths
- The merged result can sometimes lead to invalid combinations (e.g.
`sortImports`)

Essentially, we have no choice but to return an error when an invalid
option is encountered for each target file, much like how we detect
syntax errors.

But I don't think abandoning pre-validation entirely is a good idea.
We have nested configs now, and cases where `overrides` cause errors
shouldn't happen very frequently.

---

👉🏻 Keep the current pre-validation (error early if possible (walk root,
stdin), validate `overrides` in separate files and report errors
individually (walk overrides, lsp).

As for changes from the current state, ensure that it does not panic at
the very least.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-04-23 10:57:37 +09:00
leaysgur b095f4872a refactor(oxfmt): Share code between walk and stdin runners (#21628) 2026-04-22 04:22:26 +00:00
leaysgur 3bc54a96c5 feat(oxfmt): Respect nested config for --stdin-filepath (#21627)
Follow up on #21625

Now filepath in `--stdin-filepath` is not just a hint for parser detection.
2026-04-22 04:22:25 +00:00
leaysgur 9cb3e6ca64 refactor(oxfmt): Move stdin module under cli/ (#21626) 2026-04-22 04:22:25 +00:00
leaysgur 144f27a042 feat(oxfmt): Respect ignore settings for --stdin-filepath (#21625)
Fixes #20545
2026-04-22 03:29:24 +00:00
leaysgur df27b48cc0 perf(oxfmt): Skip ancestors check when no nested config found (#21517)
By doing this, v0.45 and v0.46 w/ `--disable-nested-config` having comparable performance.

```
❯ hyperfine -n 'v0.45 (root config only)' 'node ~/Codes/oxc/node_modules/oxfmt/dist/cli.js --check "**/*.{js,ts,tsx}"' -n 'v0.46 (root config only)' 'node ~/Codes/oxc/apps/oxfmt/dist/cli.js --check --disable-nested-config "**/*.{js,ts,tsx}"' --warmup 1 -i
Benchmark 1: v0.45 (root config only)
  Time (mean ± σ):     583.5 ms ±  17.5 ms    [User: 2126.3 ms, System: 1310.4 ms]
  Range (min … max):   556.8 ms … 610.6 ms    10 runs

  Warning: Ignoring non-zero exit code.

Benchmark 2: v0.46 (root config only)
  Time (mean ± σ):     575.5 ms ±  18.8 ms    [User: 2168.0 ms, System: 1340.9 ms]
  Range (min … max):   557.6 ms … 611.8 ms    10 runs

  Warning: Ignoring non-zero exit code.

Summary
  v0.46 (root config only) ran
    1.01 ± 0.05 times faster than v0.45 (root config only)
```
2026-04-17 04:40:39 +00:00
leaysgur 5aa7fe1b12 feat(oxfmt): Add --disable-nested-config CLI flag (#21514)
Fixes #21420
2026-04-17 01:20:46 +00:00
leaysgur dc045f9829 refactor(oxfmt): Move ignorePatterns related functions to ConfigResolver (#21491)
Since local ignores related to `ignorePatterns` can be derived from the config, consolidate them.

It's getting bloated, so I'll clean it up later.
2026-04-16 08:10:30 +00:00
leaysgur b6abde0c4d refactor(oxfmt): Reorder cli/walk.rs lines (#21490)
Just rearranged the order of the lines of code.
2026-04-16 08:10:30 +00:00
leaysgur 6f49fada82 fix(oxfmt): Respect nested config.ignorePatterns (#21489)
Previously, `ignorePatterns` could not skip directories in `filter_entry()` because doing so might hide nested configs beneath them.

This PR pre-scans config locations before the walk, so `filter_entry()` can safely skip `ignorePatterns` directories that have no nested config beneath them.

With all configs known upfront, the walk also resolves each file's scope dynamically from a pre-loaded scope map, replacing the sequential child-scope walks (`run_child_scope()` recursion) with a single parallel walk.

The pre-scan also preserves current streaming architecture (walk → channel → format workers), keeping format workers busy sooner than a batch-collect alternative would.
2026-04-16 08:10:30 +00:00
leaysgur 7c98d52196 fix(oxfmt): Do not panic on finding invalid nested config (#21461) 2026-04-15 08:27:50 +00:00
leaysgur 0be6dc795e refactor(oxfmt): Sync num of walker threads by --threads (#21409)
Previously, even if the `--threads` was specified, it could not be controlled.
2026-04-15 01:26:51 +00:00
leaysgur 5e1522a40f perf(oxfmt): Do not occupy the rayon thread solely for handover (#21408)
It was a waste that an idle thread was occupying a limited slot.
2026-04-15 01:26:50 +00:00
leaysgur 7fe01488a8 refactor(oxfmt): Rename cli/format -> cli/runner (#21407)
Just rename.
2026-04-15 01:26:50 +00:00
leaysgur 16713d58cd feat(oxfmt/cli): Support per-directory config (#21103)
Fixes #19509

Initially, I thought about sharing the implementation with Oxlint, but finally I  decided not to do it due to constraints and structural differences.

- Strategy
  - Oxlint: Collects all file paths into memory upfront
    - for cross-module analysis?
    - then filters by `ignorePatterns` afterward
  - Oxfmt: Streams files to the formatter as they are discovered while walking
    - Scope-based walk resolves config at walk time, avoiding a separate post-walk filtering pass
- Config
  - Oxlint: There is a difference in what can be configured in the root config versus the rest
  - Oxfmt: No difference
2026-04-15 01:26:50 +00:00
leaysgur 94fe774190 fix(oxfmt): Handle paths with consecutive leading slashes (#21155)
Fixes #21086
2026-04-08 03:18:26 +00:00
leaysgur 0d678345aa feat(oxfmt): Show hint for all files are ignored case (#21154)
Fixes #21107

Messages that were previously omitted when `no_error_on_unmatched_pattern` is specified will now be displayed, resulting in more consistent behavior.
2026-04-08 02:16:24 +00:00
leaysgur 85075cee59 refactor(oxfmt): Rework glob expansion (#21083)
- Before: Resolved beforehand and treated as a simple list of paths
  - = 2-pass
- After: Walked through like a standard path, with processing handled by a custom matcher
  - = 1-pass
  - To support per-directory config, we need to locate the config files during the directory walk; glob is no exception

This is an internal change and generally has no impact on users.
(In niche cases, the content of the log output will change, but it will result in more consistent.)
2026-04-07 06:59:22 +00:00
leaysgur cfbc1a3036 refactor(oxfmt): Use Copy for OutputMode enum (#20998) 2026-04-03 08:26:57 +00:00
leaysgur 9d45511e86 fix(oxfmt): Propagate file write errors instead of panicking (#20997)
For example, in cases where a read-only file is encountered.

- Before: this would trigger a panic
- After: it is reported as a write error, consistent with how parsing errors are handled
2026-04-03 08:26:57 +00:00
leaysgur 2938e8a107 refactor(oxfmt/cli): Rename FormatRunner > CliRunner (#20726)
Just renaming.
2026-03-25 08:22:29 +00:00
leaysgur 11a2b4530d fix(oxfmt): Skip vite.config.ts w/o .fmt field in auto-discovery (#20254)
Fixes #20237
2026-03-12 04:14:26 +00:00
leaysgur 95943aa6f2 feat(oxfmt): Support vite.config.* .fmt field (#20197)
NOTE: I'll do a self-review and add the test tomorrow.

<details>
<summary>Prompt</summary>

```
# Support `vite.config.*` as a Configuration File

## Target Files

- `vite.config.ts`
- `vite.config.mts`
- `vite.config.cts`
- `vite.config.js`
- `vite.config.mjs`
- `vite.config.cjs`

## Auto-Discovery Priority

When searching for a config file from `cwd` upwards, the following priority applies within the same directory:

1. Tool-specific JSON/JSONC config files (e.g. `.oxfmtrc.json`, `.oxlintrc.json`)
2. Tool-specific JS/TS config files (e.g. `oxfmt.config.ts`, `oxlint.config.ts`)
3. `vite.config.*`

## Extracting Configuration

The default export of `vite.config.*` is expected to contain tool-specific configuration under a dedicated field:

- oxfmt: `.fmt`
- oxlint: `.lint`

If the field does not exist, an error is returned (e.g. `Expected a 'fmt' field in the default export.`).

## Explicit `--config` Flag

When `--config vite.config.ts` is explicitly specified, the same behavior applies. The file name is used to determine whether to extract a nested field.

## LSP

`vite.config.*` files are added to the file watch patterns so that configuration changes trigger a reload.
```

</details>
2026-03-11 04:34:48 +00:00
leaysgur 172fc072f3 feat(oxfmt): .js/.ts config file support (#20135)
Fixes #19711, also resolves some of the demands for nested configs.
2026-03-11 04:34:48 +00:00
leaysgur 92f44900ee fix(oxfmt): Apply is_ignored_dir for glob paths too (#20056)
Fixes #20036
2026-03-06 04:18:28 +00:00
leaysgur 0f0ff51334 feat(oxfmt): Display default settings was used message in cli stats (#19939)
Fixes #19913
2026-03-04 09:00:42 +00:00