Due to historical reasons, the IR was passed as `[Doc, (Doc, ...Doc)]` only when processing `gql-in-js`, whilst for other cases such as `css-in-js` and `html-in-js`, it was always `[Doc]`.
Now, every result is single `Doc`.
Partially fixes#25196
- Before: create `child_process` worker pool on startup
- Spawned even when nothing needs JS side delegation (e.g. JS/TS-only projects)
- For LSP, the idle workers stayed resident for the whole session
- After: create the pool on the first format that actually delegates to JS side
- Runs that never reach JS side spawn zero workers
- As more formatters are rewritten in Rust, the share of such runs keeps growing
Trade-off: the first JS-delegated format in an LSP session now pays the spawn cost.
But that is what every one-shot CLI run already pays, so it is far from slow, and subsequent formats are unchanged.
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#20525, (thanks @oxura !)
```sh
# Many files that route to the Prettier `formatFile` callback and fail to parse
mkdir -p /tmp/repro
for i in $(seq 1 500); do printf '<template><div></template>' > /tmp/repro/broken$i.vue; done
# Run repeatedly with default concurrency
for run in $(seq 1 10); do node apps/oxfmt/dist/cli.js --write /tmp/repro; done
```
Within a few runs it crashes non-deterministically (exit codes varied: 133 / 138 / 139, and sometimes 2 = clean error report), with the same fatal as #20525:
```
# Fatal error in , line 0
# Check failed: object_ != kGlobalHandleZapValue. (or: node->IsInUse())
...
napi_reference_unref
<napi::error::Error as ...Drop>::drop
core::ptr::drop_in_place<napi::error::Error>
oxfmt::core::external_formatter::wrap_format_file::{{closure}}
```
Each broken file makes the `formatFile` callback reject; the resulting `napi::Error` is dropped on the TSFN await path and reaches `napi_reference_unref` during teardown → V8 fatal check.
After this fix (callback resolves errors as data), the same workload runs clean.
New place to share code for use in `src-js`, similar to how `crates/oxc_config` works.
Initially, I considered creating an `apps/oxc_cli` and moving `crates/oxc_config` there. However, it felt unnatural deps for `crates/oxc_linter` -> `apps/oxc_cli`, so I decided to keep the Rust code as is for now and share only the JS code.
Since `apps/shared` exports `.ts` files directly, each app is responsible for bundling them via `@oxapps/shared`. Tests still live in `apps/shared`, so separate CI is also added and hooked by each app.
To start, I've only shared the function that reports TS incompatibility errors. This is just for verification purposes and will be fixed in a later PR.
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)
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.
Part of #15180
gql-in-js is the simplest among other xxx-in-js cases.
It works by:
- splitting the template with `${}`, converting each part into an IR using:
- `prettier({ parser:"graphql" })` and `oxc_formatter` respectively
- and then interleaving them
However, for this to work, two things are essential:
- Obtaining a formatted `Doc` from Prettier, rather than just a formatted `string`.
- Converting the `Doc` JSON into `oxc_formatter`'s IR.
The diffs primarily arise from the implementation of these two.
### TODOs
- [x] Implement by Claude
- [x] Self review and fix up
- [x] Check bench, ecosystem
- `experimentalSortImports` 👉🏻 `sortImports`
- `experimentalSortPackageJson` 👉🏻 `sortPackageJson`
- `experimentalTailwindcss` 👉🏻 `sortTailwindcss`
To avoid breaking changes, the old name is retained as an alias.
# Summary
- Explicitly pass process.env to Tinypool's env option when spawning
child processes in the external formatter worker pool
- Filters out undefined values from process.env to satisfy the
Record<string, string> type requirement
# Context
When Tinypool uses the child_process runtime, child processes may not
always reliably inherit the parent's environment variables. This
explicitly forwards the environment to ensure child processes have
access to the same environment (e.g., PATH, NODE_ENV, etc.).
---------
Signed-off-by: Yuji Sugiura <6259812+leaysgur@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Yuji Sugiura <6259812+leaysgur@users.noreply.github.com>
Fixes#19194
- Changed from passing file name as `filepath` to using absolute path
- For Tailwind's sorter, retrieve it via `options.filepath`
- Ensure `filepath` is passed in all code paths
- Also, path resolution should be handled at a higher flow
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
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
Follow up on #17648
Move the `experimentalTailwindcss` to `tailwindXxx` option mapping logic from JS to Rust side (`populate_prettier_config`).
Now, consolidate all Prettier option processing in one place (`populate_prettier_config`).
This may also slightly improve performance by reducing JS runtime overhead a bit.
Previously, both `oxc_formatter` (SUPPORTED_TAGS) and `oxfmt` JS (TAG_TO_PARSER) had knowledge about supported embedded language tags.
This created duplication and unclear responsibility boundaries.
- Remove `SUPPORTED_TAGS` and `is_supported_tag()` from `oxc_formatter`
- Add `tag_to_prettier_parser()` in `oxfmt` Rust side as single source of truth
- Change JS callback signature from `tagName` to `parserName`
- Simplify JS `formatEmbeddedCode()` to just call Prettier with given parser
This PR adds support for migrating `prettier-plugin-tailwindcss` configuration when running `oxfmt --migrate prettier`.
### Example
Prettier config (input):
```json
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js",
"tailwindFunctions": ["clsx", "cn"],
"tailwindAttributes": ["myClass"],
"tailwindPreserveWhitespace": true
}
```
Oxfmt config (output):
```json
{
"printWidth": 80,
"experimentalTailwindcss": {
"config": "./tailwind.config.js",
"functions": ["clsx", "cn"],
"attributes": ["myClass"],
"preserveWhitespace": true
},
"ignorePatterns": []
}
```
### Notes
- If the plugin is listed but no Tailwind options are specified, `experimentalTailwindcss` will be set to an empty object `{}` to enable the feature
- Tailwind options without the plugin in the plugins array will NOT be migrated (they are skipped)
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)