## Summary
- update workspace test dependencies to Vitest 5.0.0
- pin Vitest and related tooling to Vite 8.2.2
- migrate removed sequential and benchmark APIs
- update the pnpm lockfile
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).
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>
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
## Summary
- auto-discover `oxfmt.config.mts` and `oxlint.config.mts`
- watch `.mts` configs in the language server and reject `.ts`/`.mts` conflicts
- add focused CLI and LSP coverage
Patterns in `ignorePatterns` use gitignore-style matching rooted at the config file's directory, so a `..` component can never match anything.
Reject such patterns at config load time instead of silently accepting them, and document the scoping.
Refs: #24276
A bit outdated, updated it to reflect the latest situation.
- Support `svelte` migration
- Better (but not yet complete) `overrides` detection
- Remove warning about `embeddedLanguageFormatting` (already supported)
- etc
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.
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>
- Normalized tests that were ambiguous as to whether they were fixtures or scenarios based
- Specified this rule in AGENTS.md
- Made the test name report in the CLI clearer
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.
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
Fixes#20416 for Oxfmt
- Gate `vite.config.ts` recognition behind `VP_VERSION` env var
- With it, only vite.config.ts is recognized
- Without it, excluded from config candidates (.oxfmtrc.json, oxfmt.config.ts are used)
- Refactor JS config loader into `loadJsConfig` / `loadVitePlusConfig` with shared `importJsConfig` helper
- `loadVitePlusConfig` should be updated to use `resolveConfig` from `vite-plus` soon
- Split tests into `vite_config` (non VP mode) and `vite_plus` (VP mode)
Fixes#21107
Messages that were previously omitted when `no_error_on_unmatched_pattern` is specified will now be displayed, resulting in more consistent behavior.
- 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.)
Part of #15180, closes#18891
Angular-in-JS consists of 2 cases: `template` and `styles`.
- template: Similar to html-in-js, use the `parser: angular`
- styles: Same as css-in-js (already supported)
Part of #15180
The basic flow is the same as css-in-js: replace `${expr}` with placeholder strings, let Prettier format the HTML, then substitute back with formatted `expr`s.
However, there are a few key differences:
### `options.htmlWhitespaceSensitivity`
This HTML-specific option also affects html-in-js (template wrapping strategy, hug detection), so it needed to be held in `oxc_formatter::FormatOptions`.
### `prettier.__internal.formatOptionsHiddenDefaults`
Prettier adds hidden fields to the `options` object during its internal print phase for embedded formatters.
However, `__debug.printToDoc()` runs `normalizeFormatOptions()` which strips unknown keys. Unlike internal `textToDoc()` (which uses `passThrough: true`), `printToDoc()` does not preserve them.
To work around this, we pre-register keys in `formatOptionsHiddenDefaults` so they survive normalization.
### NOTE: Not fully supported cases
#### js-in-html-in-js
Currently, the process of converting Docs to our IR does not support `conditionalGroup`.
Since Prettier only uses this for YAML and JS formatting.
YAML is never embedded, and we would like to use `oxc_formatter` for JS.
However, applying `oxc_formatter` to the **js**-in-html-in-js case was not straightforward. Therefore, I have implemented a fallback to a simpler formatting process for that case for now.
#### `label({ embed, hug })` equivalent
With current implementation, `is_huggable_html_embed_single_arg()` always hugs via early-return, ignoring line width.
Prettier uses `label({ embed, hug })` + `conditionalGroup` to try hugged first and fall back to expanded.
We have the equivalent `BestFitting` 3-variant flow in `write_grouped_arguments`, but `ExpandParent` from HTML template's `hardline` propagates through `will_break` / `propagate_expands` / fit check, forcing the expanded variant to always be chosen.
To fix, we need to add new IR for boundary (prevents `ExpandParent` propagation in `will_break` / `propagate_expands` / fit check), then add HTML embeds to `can_group_expression_argument` so they enter the existing `BestFitting` 3-variant flow.
However, I'm not sure this is worth to add. So just leave it for now.
Current status:
- oxfmt: #20326 was merged
- oxlint: After discussion in #20327, it was decided NOT to merge
First, correct this inconsistency.
Since #20326 includes a change that displays errors from the JS side as-is, we will keep that part.
If oxfmt.config.ts fails to load, it is the user's responsibility, so reporting it as an error is correct.
However, this does not apply to vite.config.ts, so we ignore it.
Also refactored to report error message from JS.