Fixes#26165
- `side_effect` is predefined selector, it cannot be used as custom group name
- respect custom group which uses `side_effect` as side-effect-import correctly
- fix metadata detection bug
Closes the migration tracked in #23043.
The new builder API (methods on the AST types themselves) has been in place for a while, all Oxc crates are migrated, and every crate which uses `AstBuilder` already enables `disable_old_builder` in its tests. The module docs said "after a few weeks, we will remove `AstBuilder`'s own methods entirely" — this does that.
## What's removed
- `crates/oxc_ast/src/generated/ast_builder.rs` and `builder/methods.rs` — `AstBuilder`'s own node and primitive constructors.
- `AstBuilderGenerator`, which produced the former.
- The `disable_old_builder` Cargo feature, plus the dev-dependency hacks it required across 33 manifests. Eight crates depended on `oxc_ast` *purely* to turn the feature on; those deps and their `cargo-shear` ignore entries are gone too.
## One thing worth reviewing
`AstBuilderGenerator` owned the `#[builder(default)]` / `#[builder(skip)]` attr registration that `BuilderMethodsGenerator` only *reads* — its doc comment said as much. Deleting it naively would have silently dropped `skip`/`default` handling for the 12 AST types using those attrs, so `attrs()` and `parse_attr()` move to `BuilderMethodsGenerator`.
The evidence this was faithful: after regenerating, `builder_methods.rs` and every other generated file are **byte-identical**. The only generated change in the diff is the deletion.
## Breaking changes
- `AstBuilder`'s own methods are gone. Use the AST type methods: `Statement::new_expression_statement(span, expr, &builder)` instead of `builder.statement_expression(span, expr)`.
- `AstBuilder` is no longer `Copy`/`Clone`, and its `allocator` field is private — use the `allocator` method from `GetAllocator`.
- `AstBuilder` and `NONE` are no longer re-exported from the crate root; import them from `oxc_ast::builder`. (The `disable_old_builder` feature already gated these off, so nothing in-tree used the root path.)
- The `disable_old_builder` feature no longer exists — remove it from any manifest that sets it.
A migration note covering all of the above is in `crates/oxc_ast/src/builder/mod.rs`.
---
Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy.
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
Part of #23043.
We don't enable `oxc_ast`'s `disable_old_builder` Cargo feature in any of our published crates, or crates that are depended on by published crates.
Tasks are private to the repo, so enable this feature on them, to ensure no usage of legacy `AstBuilder` methods sneak in during the transition period before we delete these methods.
It sounds stupid to create a snapshot from an already existing file.
Works on https://github.com/oxc-project/oxc/issues/22955 showed they will always match.
The PR changes the tests so it just checks if the existing file matches the output, without snapshots.
Currently, our config file is Prettier-compatible, which is not easy to tell which options apply to which languages.
(I've felt this way for a while, so) I've added some notes to clarify.
Closes#22371, follows https://github.com/azat-io/eslint-plugin-perfectionist/pull/732
---
As a baseline, while the result effectively follows the behavior of a perfectionist, there is no fundamental reason why it must be done that way.
Node.js subpaths are more flexible and aren't necessarily "internal", you can also alias external modules.
However, I do think it's consistent in the sense that it treats them the same way as other conventions like `@`.
When a property references a type via `$ref` and both have doc comments, the markdown renderer was dropping the property-level description.
```rs
/// A set of glob patterns. // ← type-level
pub struct GlobSet(Vec<String>);
```
```rs
pub struct OxfmtOverrideConfig {
/// Glob patterns to match files for this override. // ← property-level
pub files: GlobSet,
}
```
Before:
```
#### overrides[n].files
A set of glob patterns.
```
After:
```
#### overrides[n].files
Glob patterns to match files for this override.
A set of glob patterns.
```
At present, some definitions may seem redundant, but since unifying them at the prop-level seemed inconvenient for linter use cases, I decided to provide both.
The intended convention is generic invariants on the type-level, contextual purpose on the property-level.
uses oxlint's `GlobSet` for overrides over `Vec<String>`.
This helpe move out some shared normalization code from oxlint and oxfmt to the shared config crate.
Hopefully this change is ok for you @leaysgur - I was originally hoping to share somethign else, but it seems trickier than I expected, so I just moved this out instead, which feels to me a good change
Currently, fields defined as `key: Option<T>` are represented in TS as `key?: T | null`.
However, passing `null` actually just treats it as `None`, which only creates noise.
Claude's summary:
- Enhanced `schema_markdown.rs` to properly handle `allOf` schemas
created by the `RemoveRefSiblings` visitor
- When a `$ref` has sibling properties (like `description`), schemars'
draft-07 visitor wraps it in an `allOf`
- The renderer now resolves through `allOf` structures to find the
actual referenced schema
----
This fixes the problem discovered in
https://github.com/oxc-project/oxc/pull/19387#issuecomment-3999084412.
This actually improves the generated docs for a few rules:
- `typescript/no-deprecated`
- `no-floating-promises`
- `react/forbid-dom-props`
- `typescript/no-misused-spread`
- `typescript/only-throw-error`
- `typescript/prefer-readonly-parameter-types`
- `typescript/prefer-nullish-coalescing`
- `typescript/restrict-template-expressions`
Basically, any rule that has a struct nested inside an array has better
docs now, as the docs generator actually handles their schema correctly
now.
AI Disclosure: Generated by Claude Code.
Examples:
no-deprecated
````md
## Configuration
This rule accepts a configuration object with the following properties:
### allow
type: `array`
default: `[]`
An array of type or value specifiers that are allowed to be used even if deprecated.
Use this to allow specific deprecated APIs that you intentionally want to continue using.
#### allow[n]
type: `object | string`
Type or value specifier for matching specific declarations
Supports four types of specifiers:
1. **String specifier** (deprecated): Universal match by name
```json
"Promise"
```
2. **File specifier**: Match types/values declared in local files
```json
{ "from": "file", "name": "MyType" }
{ "from": "file", "name": ["Type1", "Type2"] }
{ "from": "file", "name": "MyType", "path": "./types.ts" }
```
3. **Lib specifier**: Match TypeScript built-in lib types
```json
{ "from": "lib", "name": "Promise" }
{ "from": "lib", "name": ["Promise", "PromiseLike"] }
```
4. **Package specifier**: Match types/values from npm packages
```json
{ "from": "package", "name": "Observable", "package": "rxjs" }
{ "from": "package", "name": ["Observable", "Subject"], "package":
"rxjs" }
```
##### allow[n].from
type: `"file"`
##### allow[n].name
type: `array | string`
Name specifier that can be a single string or array of strings
###### allow[n].name[n]
type: `string`
##### allow[n].path
type: `string`
Optional file path to specify where the types or values must be declared.
If omitted, all files will be matched.
````
react/forbid-dom-props
````md
## Configuration
Configuration for the `forbid-dom-props` rule.
This rule accepts a configuration object with the following properties:
### forbid
type: `array`
An array of prop names or objects that are forbidden on DOM elements.
Each array element can be a string with the property name, or an object
with `propName`, an optional `disallowedFor` array of DOM node names,
and an optional custom `message`.
Examples:
- `["error", { "forbid": ["id", "style"] }]`
- `["error", { "forbid": [{ "propName": "className", "message": "Use class instead" }] }]`
- `["error", { "forbid": [{ "propName": "style", "disallowedFor": ["div", "span"] }] }]`
#### forbid[n]
type: `object | string`
A forbidden prop, either as a plain prop name string or with options.
##### forbid[n].disallowedFor
type: `string[]`
A list of DOM element names (e.g. `["div", "span"]`) on which this
prop is forbidden. If empty or omitted, the prop is forbidden on all
DOM elements.
##### forbid[n].message
type: `string`
A custom message to display when this prop is used.
##### forbid[n].propName
type: `string`
The name of the prop to forbid.
````
---------
Co-authored-by: Claude <noreply@anthropic.com>
- `experimentalSortImports` 👉🏻 `sortImports`
- `experimentalSortPackageJson` 👉🏻 `sortPackageJson`
- `experimentalTailwindcss` 👉🏻 `sortTailwindcss`
To avoid breaking changes, the old name is retained as an alias.
Fixes#17830
Introduce new variant for `groups` like:
```json
// "newlinesBetween": true, // Default
"groups": [
["value-builtin", "value-external"],
{ "newlinesBetween": false }, // 👈🏻
"value-parent",
"value-sibling"
]
```
This allows you to specify line breaks globally between groups while reverting them at any position.
Conversely, you can also add line breaks at any position while specifying `newlinesBetween: false`.
Fixes#19264
The original issue only mentioned `modifiers`, but I've implemented `selector` as well.
While the original implementation also supports more attributes, I believe this is sufficient for now.
Fixes#18726
Support glob matching instead of prefix matching.
The original used regex, but there was no necessity for it, and this approach offers better performance.
In any case, it will be a braking change.
```js
// before: Matches react, reactify, react-router, etc
"elementNamePattern": ["react"]
// after: Matches react only
// before: Matches loot-core, loot-core/foo, etc
"elementNamePattern": ["loot-core"]
// after: Matches only loot-core, should update to
"elementNamePattern": ["loot-core/*"]
```
Fixes#13705
- Support glob patterns like `*.js`, `**/*.ts`, and `{a,b}.js` in CLI paths
- Globs are pre-expanded to make `ignore` crate respects `.gitignore`
- Existing cases that don't use glob will NOT be affected in terms of performance
This also fixes bug that `oxfmt a.js a.js` formats the same file 2 times 🙈
Fixes#16608 partially.
This PR overrides Prettier's default JS/TS formatting process with a custom transform-plugin.
As a result, `oxc_formatter` will be used in the `script` section of Vue, code block of Markdown, etc. And also for example, sort-imports and other features are now available.
After verification, we found that if we override `babel`, it is also applied to some expression parts of Vue.
This was unexpected and more time is needed to do things properly.
Therefore, as a first support, only enables for `markdown` and `mdx`.
Changes:
- Changed `tinypool` mode from `worker_threads` to `child_process`
- Calling NAPI (API) again via `worker_threads` from NAPI (CLI) causes a hang...
- Implemented `prettier-plugin-oxfmt` using the existing `format()` API
- What's truly desired is something equivalent to `textToDoc()`, but this is the only option currently we have
- Handling of options corresponding to Prettier's `embed()` processing flow
- Plugins only support primitive types
- NAPI also cannot handle options that cannot be serialized to JSON
Following PRs will cover:
- Improvements to option handling
- Tests
Make insta stop logging massive diffs to the console for no reason. Regenerated with `cargo insta test --force-update-snapshots`
This was caused by the update to insta here: https://github.com/oxc-project/oxc/pull/18817
Added Biome-to-oxfmt configuration migration, based on the Prettier
implementation.
I wasn't sure if I should exclude settings that match oxfmt's defaults
from the generated config, but I'm happy to do so if you’d like
- Use `Pick<Options, ...>` from Prettier to reuse fully compatible option types
- Fix misleading documentation for Tailwind `attributes` and `functions`:
- Clarify they use exact match (not prefix)
- Correct `attributes` default from `["class", "className"]` to `[]`
- `class`/`className` are hardcoded, option is for additional
#17076
### Authorship & Use of AI
All code changes in this pull request are completed by a human (myself).
ChatGPT is used **only** to assist with writing comments and this PR
description.
### Commit Structure
The changes are split into small, meaningful commits.
Each commit should be reviewable independently.
## Current State of the Implementation
For easier comparison, I forked
[actual](https://github.com/nilptr/actual) and made a few auxiliary
changes on a separate branch
[nilptr/feat/oxfmt-custom-groups](https://github.com/nilptr/actual/tree/nilptr/feat/oxfmt-custom-groups).
The auxiliary changes include:
- Upgraded `oxfmt` to address the incompatible binding API.
- Updated `eslint.config.mjs` to lint and fix .ts files.
- Updated `.oxfmtrc` to enable `experimentalSortImports`, keeping it
consistent with `eslint.config.mjs`.
### How to Compare?
1. Clone `git@github.com:nilptr/actual.git` and check out
`nilptr/feat/oxfmt-custom-groups` branch.
2. Run `yarn install`
3. Run `NAPI_RS_NATIVE_LIBRARY_PATH=/path/to/binding.node yarn
fmt:oxfmt`
The native binding can be obtained in one of the following ways:
- Download the
[oxfmt.darwin-arm64.node.zip](https://github.com/user-attachments/files/24402116/oxfmt.darwin-arm64.node.zip)
built by me (if you trust it, or scan it with
[VirusTotal](https://www.virustotal.com/gui/home/upload)).
- Build it by yourself: clone my repo, run `pnpm build`, and locate the
.node binding file under `apps/oxfmt/src-js`.
4. Inspect the results with `git diff` — the diff shows the differences
between perfectionist and oxfmt
### Observed Differences
There are 9 differences in total:
- **4 differences** are unrelevant to `experimentalSortImports`. (be the
way, they are introduced after I rebased main branch)
- **5 differences** are relevant to `experimentalSortImports`, but are
not caused by the group-matching logic, which is the focus of this PR.
- 4 of them are blank lines after side-effect imports being removed
- 1 of them is the wrong `ImportLineMetadata.source`
<img width="4064" height="2152" alt="image"
src="https://github.com/user-attachments/assets/8b4cb860-19d6-42b9-a0a4-6db0b3e40194"
/>
<img width="4064" height="2152" alt="image"
src="https://github.com/user-attachments/assets/e2616d7b-b4b2-4dbc-a0d3-ea15d1c0177e"
/>
------
I believe we can start review now.
While the review is ongoing, I will also try to see whether the observed
differences can be fixed in this PR.
---------
Co-authored-by: Yuji Sugiura <y.sugiura.0316@gmail.com>
The initial goal was to extract the schema generation utility from `oxfmtrc.rs`.
However, I found that `oxc_linter` also defines a similar function, so I unified them into `website_common`.
- Added `generate_schema_json<T>()` to `website_common`
- Removed duplicated implementations from `oxfmtrc.rs` and `oxlintrc.rs`
- Moved `test_schema_json` from `oxc_linter` to `website_linter` too
NOTE: When running `cargo t -p oxc_linter`, it previously generated `npm/oxlint/configuration_schema.json`. Now use `just linter-schema-json` instead.
## Summary
- Filter out `| null` from type annotations in website documentation markdown
- Keeps JSON schema files unchanged (they still have proper null types for validation)
- Makes documentation cleaner since users typically omit optional fields rather than setting them to null
**Before:** `type: boolean | null`
**After:** `type: boolean`
## Test plan
- [x] `cargo test -p website_formatter test_schema_markdown`
- [x] `cargo test -p website_linter test_schema_markdown`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Before this PR, `Oxfmtrc` was defined in `oxc_formatter` to avoid the circular reference `oxc_language_server` > `oxfmt` > `oxc_language_server`....
But now, `oxc_language_server` does not depend on `Oxfmtrc` since LSP implementation has moved to `apps/oxfmt`.
Therefore, by this PR, `Oxfmtrc` is moved to its proper location, in `apps/oxfmt`.
- Moved `Oxfmtrc` from `oxc_formatter` to `apps/oxfmt`
- Re-exposed from `apps/oxfmt`
- Now only `tasks/website_formatter` depends on it
- Also aligned JSON schema generation with `oxlint` by `just formatter-schema-json`
NOTE: In `oxc_formatter`, there are 2 places (`ir_transform` and `fixtures` tests) where we manually convert options from JSON. I'd like to fix this too in the future.