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
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.
Enum config options were rendering as `string | null` in the generated documentation, hiding valid values from users.
## Changes
- **`schema_markdown.rs`**: Enhanced `anyOf` schema processing to detect and extract enum values from referenced types, rendering them as quoted string literals instead of generic type names
- **Snapshot update**: Regenerated test snapshot reflecting new enum value display
## Before/After
```markdown
# Before
type: `string | null`
# After
type: `"always" | "avoid" | null`
```
Affected fields: `arrowParens`, `endOfLine`, `trailingComma`, `quoteProps`, `objectWrap`, `embeddedLanguageFormatting`, and nested enums like `experimentalSortImports.order`.
Values are extracted programmatically from the JSON schema rather than hardcoded in doc comments.
<!-- START COPILOT ORIGINAL PROMPT -->
<details>
<summary>Original prompt</summary>
>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>docs(formatter): Improve config file reference docs to show possible enum values</issue_title>
> <issue_description>Improve the formatter's config file reference by rendering the possible enum values for the top-level config file attributes.
>
> <img width="500" height="161" alt="Image" src="https://github.com/user-attachments/assets/fd0a43ef-d1c8-4d0d-9c1c-32d9a303b563" />
>
> https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html
>
> Right now, there's no way to tell what the allowed values are for `arrowParens`, `endOfLine`, `trailingComma`, etc. They all render as `string | null`.
>
> We can temporarily/easily fix this by updating the doc comments to list all available options, but ideally we would fix this via the `schema_markdown.rs` file in `website_common/`.
>
> I tried fixing this by updating the oxfmtrc.rs file with a `schemars with` to remove the Option<> wrapping, like so:
>
> ```rs
> /// Include parentheses around a sole arrow function parameter. (Default: `"always"`)
> #[serde(skip_serializing_if = "Option::is_none")]
> #[schemars(with = "ArrowParensConfig")]
> pub arrow_parens: Option<ArrowParensConfig>,
> ```
>
> Which fixes the `type` rendering, but for some reason that stops printing the code comments, so... that's annoying.</issue_description>
>
> <agent_instructions>Display possible values of enum config options in the oxfmt config file docs. You can see the generated config file page by running `cargo run -p website_formatter schema-markdown` to print the generated docs page.
>
> Do not hardcode the possible values into the doc comments in oxfmtrc.rs. You should update tasks/website_common/src/schema_markdown.rs (and any other relevant files) to generate them from the json schema we have defined.</agent_instructions>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>
</details>
<!-- START COPILOT CODING AGENT SUFFIX -->
- Fixesoxc-project/oxc#16589
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
Based on https://github.com/oxc-project/oxc/pull/16826
## Summary
Add experimental Tailwind CSS class sorting support to Oxfmt via integration with `prettier-plugin-tailwindcss`.
This implementation uses a patch-based approach to export necessary functions from `prettier-plugin-tailwindcss`. We've requested better integration support from the Tailwind team at https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/430, and can improve the integration when that's available.
## Features
- ✅ Sorts Tailwind CSS classes in `className` and `class` attributes
- ✅ Sorts classes in custom JSX attributes (configurable)
- ✅ Sorts classes in function calls like `clsx()`, `cn()`, `tw()` (configurable)
- ✅ Supports template literals with expressions
- ✅ Supports Tailwind v3 config files and v4 stylesheets
- ✅ Preserves whitespace and duplicates (configurable)
- ✅ **Fixes [prettier-plugin-tailwindcss#426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426)** - Correctly handles strings in nested non-Tailwind call expressions
- ⏳ Non-JS file support (HTML, Vue, Svelte, etc.) - Coming in follow-up PR
- ⏳ Tailwind CSS config migration in `oxfmt --migrate=prettier` - Coming in follow-up PR
## Example Usage
### Basic Usage
Enable Tailwind CSS sorting with default options by setting `experimentalTailwindcss` to an empty object in your `.oxfmtrc.json`:
```json
{
"experimentalTailwindcss": {}
}
```
### With Configuration Options
```json
{
"experimentalTailwindcss": {
"config": "./tailwind.config.js",
"functions": ["clsx", "cn", "cva", "tw"],
"attributes": ["myCustomClass"],
"preserveWhitespace": false,
"preserveDuplicates": false
}
}
```
### Configuration Options
All options from `prettier-plugin-tailwindcss` are supported (with `tailwind` prefix removed):
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `config` | `string` | Path to your Tailwind CSS v3 configuration file. Paths are resolved relative to the Oxfmt configuration file. | `"./tailwind.config.js"` |
| `stylesheet` | `string` | Path to your Tailwind CSS v4 stylesheet. Paths are resolved relative to the Oxfmt configuration file. | `undefined` |
| `functions` | `string[]` | List of custom function names that contain Tailwind CSS classes (e.g., `["clsx", "cn", "cva", "tw"]`). | `[]` |
| `attributes` | `string[]` | List of custom JSX attributes that should be sorted (e.g., `["myCustomClass"]`). Note: `class` and `className` are always sorted by default. | `[]` |
| `preserveWhitespace` | `boolean` | Preserve whitespace around classes. | `false` |
| `preserveDuplicates` | `boolean` | Preserve duplicate classes. | `false` |
## Migration from prettier-plugin-tailwindcss
If you're currently using `prettier-plugin-tailwindcss`, you can migrate to Oxfmt's built-in support:
### Manual Migration (Current)
#### Before (Prettier)
```json
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js",
"tailwindFunctions": ["clsx", "cn"]
}
```
#### After (Oxfmt)
```json
{
"experimentalTailwindcss": {
"config": "./tailwind.config.js",
"functions": ["clsx", "cn"]
}
}
```
**Key differences:**
- Remove `tailwind` prefix from all option names (e.g., `tailwindConfig` → `config`)
- No need to install `prettier-plugin-tailwindcss` separately, it is bundled within Oxfmt
### Automated Migration (Follow-up PR)
Tailwind CSS configuration migration will be added to the existing `oxfmt --migrate=prettier` command in a follow-up PR. This will automatically convert your `prettier-plugin-tailwindcss` settings from `.prettierrc` to `.oxfmtrc.json`.
## Implementation Details
### Technical Approach
- Uses `pnpm patch` to export necessary functions from `prettier-plugin-tailwindcss`
- Implements Rust-side detection for Tailwind contexts (JSX attributes, function calls, template literals)
- Passes class strings to the JS plugin for sorting via external callback
- Optimized context tracking to avoid repeated AST traversal
### Improvements Over prettier-plugin-tailwindcss
- **Context-based quasi position tracking** ([tailwindcss.rs:273-276](https://github.com/oxc-project/oxc/blob/main/crates/oxc_formatter/src/utils/tailwindcss.rs#L273-L276)) - Stores template literal position in context stack instead of traversing AST, improving performance
- **Fixes [issue #426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426)** - Correctly handles strings in nested non-Tailwind call expressions (e.g., `value.includes("\n")` no longer gets corrupted)
- **Comprehensive edge case testing** - Added 58 tests covering edge cases, nested scenarios, error handling, and performance
## Changes
- Changed `experimentalTailwindcss` option from `boolean` to `TailwindcssOptions` object
- Added all configuration options from `prettier-plugin-tailwindcss` (without `tailwind` prefix)
- Exported necessary functions from `prettier-plugin-tailwindcss` via patch
- Added comprehensive test suite with 58 tests
- Improved documentation for all configuration options
## Test Plan
- [x] `pnpm test` passes (58 tests)
- [x] `cargo clippy` passes
- [x] `just fmt` passes
- [x] Basic Tailwind class sorting works with `class` and `className` attributes
- [x] `attributes` option sorts custom JSX attributes
- [x] `functions` option sorts string arguments in function calls (e.g., `clsx()`, `cn()`)
- [x] Template literals with expressions handle whitespace correctly
- [x] Edge cases: empty strings, Unicode, emoji, special characters
- [x] Nested scenarios: deeply nested template literals, function calls
- [x] Strings in nested non-Tailwind calls are preserved (fixes [#426](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/426))
- [x] Test in https://github.com/tailwindlabs/headlessui and https://github.com/dubinc/dub
## Follow-up Work
- **Non-JS file support** - HTML, Vue, Svelte, etc. by enabling original plugin when `experimentalTailwindcss` is set
- **Migration support** - Add Tailwind CSS config migration to existing `oxfmt --migrate=prettier` command
- **Better integration** - Improve when https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/430 is resolved
🤖 Generated with [Claude Code](https://claude.com/claude-code)