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
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
## Summary
- Adds Unicorn `prefer-export-from` to report import-then-re-export
patterns and suggest direct `export ... from` declarations.
- Ports upstream coverage for default, named, namespace, TypeScript,
import-attributes, and `checkUsedVariables` cases.
- Exposes the rule option in the oxlint JSON schema and generated
TypeScript config types.
Related #22686
AI-assisted: this PR was prepared with AI assistance; the contributor
remains responsible for reviewing and validating the changes.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cameron Clark <cameron.clark@hey.com>
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 `@`.
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.
- `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#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
- 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.
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.
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)
Fixes#15066 and closes#16757
- Added `insertFinalNewline: bool` option
- `true` by default = The same behavior with current `oxc_formatter` and Prettier
- Also respect `insert_final_newline` in `.editorconfig`
- Like others, `.oxfmtrc` takes precedence
This is a refactoring related to the `Oxfmtrc` struct.
- Clearly state that defined in `oxc_formatter` is temporary
- Also changed export path
- Remove useless `default` attribute from `Oxfmtrc`
- Defaults are resolved by `into_options()`
- Mark all `SortImportsConfig` fields as `Option`
- Aligned with `Oxfmtrc`, needed for Node.js API
- Move `from_file()` to `oxc_language_server`, which only used by here
- Update `into_format_options()` to `into_options()`
- Now it returns `FormatOptions` and `OxfmtOptions`
- Apply changes
- Also make all `Oxfmtrc` fields as `Option` to distinguish undefined and default
- Update doc comments
Fixes#15930
Previously `experimentalOperatorPosition` was suggested in `.oxfmtrc` completion. But it is not actually used(not implemented yet).
As a first step, just ignore it. + Report error => #16088
Fixes#15900
- Update default 80 > 100
- Set `printWidth: 80` for fixture tests
- Test diffs are all just adding header part for used options
- Also use `printWidth: 80` for `prettier_conformance`