mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
main
21864
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
255c5cca26 |
fix(formatter): measure Tailwind class argument after collapsing whitespace (#28745)
## Summary Follow-up to #28744, same kind of issue in a different place. With `sortTailwindcss` enabled, an assignment whose right side is a Tailwind function call can take two runs to reach a stable output, because the "short argument" check measures the argument's **source** text while the printed text has its whitespace collapsed. Input (`{"sortTailwindcss": {"functions": ["cn"]}}`, default `printWidth: 100`): ```ts const vaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = cn("p-4 flex"); ``` 1st run: ```ts const vaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = cn( "flex p-4", ); ``` 2nd run (also what Prettier + prettier-plugin-tailwindcss prints in one run): ```ts const vaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = cn("flex p-4"); ``` The same happens for `a.b = cn(...)`, `{ key: cn(...) }` and `cn(...).trim()`. ### Cause `is_poorly_breakable_member_or_call_chain` uses `is_short_argument` (Prettier's `isLoneShortArgument`) to check if the only argument is at most `printWidth / 4` wide. For a string or template literal it measures the source text. When the argument is sorted as Tailwind classes, whitespace is collapsed when printed, so the source can be "long" while the printed text is "short". Prettier sees the already rewritten value, so it decides on the printed text. ### Fix When the call is a Tailwind function call and `preserveWhitespace` is off, measure the string / template literal argument after collapsing whitespace (`collapsed_class_list_width`). ### Known limitation: duplicate classes Duplicate removal also shortens the printed text, e.g. `cn("flex flex flex flex flex p-4")` still takes two runs. I didn't handle it, because the sorter removes duplicates only for classes known to Tailwind CSS (`sortClassList` in prettier-plugin-tailwindcss: `if (order !== null) seenClasses.add(cls)`), and the formatter can't know which classes are known while building the IR. The options I could think of: 1. Leave it (this PR). Duplicates are rare in real code, and the result is still valid. It just settles on the 2nd run. 2. Also dedupe all classes when measuring. This fixes the common case but can measure too short when unknown classes are duplicated. 3. Make the sorted text available before the layout decision, which is a larger change to how sorting is batched. I went with 1 to keep this small. If you prefer another approach, please let me know and I'll update it. ## Test - Added cases to `apps/oxfmt/test/api/sort_tailwindcss.test.ts`: - string and template literal arguments with extra whitespace format in one run, and a 2nd run doesn't change the output. These fail without this fix. - `preserveWhitespace: true`: the source text is still measured (unchanged). - `apps/oxfmt`: `vitest run` → 553 passed - `cargo test -p oxc_formatter`, `CARGO_BUILD_WARNINGS=deny cargo lint -p oxc_formatter`, `cargo fmt --check`, `node --run fmt` pass - I also formatted 7 code shapes × 5 class strings × identifier lengths 1–100 twice and compared the runs. All whitespace cases are now stable; only the duplicate-class cases above remain. --------- Co-authored-by: leaysgur <6259812+leaysgur@users.noreply.github.com> |
||
|
|
ea76490ca5 |
fix(transformer/class-properties): preserve private method values in assignments (#28788)
Compound assignments and updates to private methods call the method body while reading its value. For example, `this.#method ||= rhs()` calls `#method`, evaluates `rhs()` when the method returns `undefined`, and throws instead of returning the method function. Distinguish method reads from accessor getter calls when lowering assignments and updates. Method reads now return the function through the existing brand check. Regression coverage includes static and instance methods, all compound operators, prefix/postfix updates, failed brand checks, and receivers obtained from function calls. Validation: - 39 Rust unit/integration tests and 53 Node binding tests passed. - Clippy and Rust formatting checks passed. - Transform conformance adds one passing output fixture and one passing executable fixture; existing failures are unchanged. - Transformer coverage has no new failures against Test262, Babel, TypeScript, and local cases. - Cross-target probes passed at ES2021, ES2020, and ES2015. AI assistance was used to implement and test this change. |
||
|
|
e6c4feffcc |
fix(formatter): decide template and object key layouts from collapsed Tailwind classes (#28791)
Follow-up to #28744, same root cause in other places. With `sortTailwindcss` on and `preserveWhitespace` off, Tailwind class strings and templates are printed with their whitespace collapsed, but these layout decisions read the source text, so formatting took two runs: ```ts // Sole template argument of a Tailwind call const a = clsx(` p-4 flex items-center ${active ? "bg-white" : "bg-black"} text-black `); // Arrow body tagged with a Tailwind function const b = () => tw` p-4 flex items-center bg-white text-black `; // Short object key (tabWidth: 4) const c = clsx({ "a b": someLongCondition }); ``` - `is_multiline_template_starting_on_same_line` now returns `false` for a template whose line breaks are collapsed - It reads the current Tailwind context (what `TemplateElement` reads) or the one a Tailwind tag pushes, so the arrow body, sole argument and member chain routing callers follow the print - `write_tailwind_string_literal` returns its printed width, and `write_member_name` uses it for the short key decision - An upper bound: the sorter also drops duplicates of known classes - `tailwind_context_for_callee` is shared by the call and tagged template pushes and the layout check, like `tailwind_context_for_jsx_attribute` in #28744 Outputs match Prettier + prettier-plugin-tailwindcss and are idempotent. Not covered here: `is_short_argument` (#28745), Tailwind calls through `MemberChain` never pushing the context, and computed keys. AI assistance (Claude Code) was used for this change. I reviewed and tested it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0b441649eb |
fix(formatter): decide JSX opening element layout from collapsed Tailwind class string (#28744)
## Summary
With `sortTailwindcss` enabled (and `preserveWhitespace` off),
formatting a JSX element whose Tailwind class string spans multiple
lines is not idempotent. It takes two runs to reach a stable output.
Input (`{"sortTailwindcss": {}}`):
```tsx
const A = (
<div
className="p-4
flex"
>
Hello
</div>
);
```
1st run:
```tsx
const A = (
<div
className="flex p-4"
>
Hello
</div>
);
```
2nd run:
```tsx
const A = <div className="flex p-4">Hello</div>;
```
### Cause
`FormatOpeningElement` decides its layout from the attribute's
**source** string value:
- `SingleStringAttribute` is used only if the value has no line break.
- Otherwise the attributes group is forced to expand if any string value
has a line break.
But for a Tailwind attribute without `preserveWhitespace`,
`write_tailwind_string_literal` collapses all whitespace (including line
breaks) into single spaces. So the printed value has no line break while
the layout was decided as if it had one. The next run sees a single-line
value and joins the element.
prettier-plugin-tailwindcss rewrites the attribute value in the AST
before printing, so Prettier makes the layout decision on the collapsed
value and gets the 2nd-run output in one pass.
### Fix
Decide the layout from whether the **printed** value has a line break. A
Tailwind attribute (per `is_tailwind_jsx_attribute`) with
`preserveWhitespace` off is treated as single-line. Nothing changes when
`sortTailwindcss` is off, with `preserveWhitespace: true`, or for
non-Tailwind attributes.
## Test
- Added 3 cases to `apps/oxfmt/test/api/sort_tailwindcss.test.ts`:
- multiline class string in a single-attribute and a multi-attribute
element: formats in one run, and a 2nd run doesn't change the output.
Fails without this fix.
- `preserveWhitespace: true`: still keeps line breaks and expands the
element (unchanged).
- multiline non-Tailwind attribute (`title`) next to a `className`:
still expands the element (unchanged).
- `apps/oxfmt`: `vitest run` → 553 passed
- `cargo test -p oxc_formatter`, `CARGO_BUILD_WARNINGS=deny cargo lint
-p oxc_formatter`, `cargo fmt --check`, `node --run fmt` pass
- I didn't run `conformance:prettier` (fixtures not downloaded locally).
It doesn't enable `sortTailwindcss`, so this code path behaves the same
as before there.
- Found while migrating a real Next.js app to oxfmt: 42 files needed a
second `oxfmt --write` run.
---------
Co-authored-by: leaysgur <6259812+leaysgur@users.noreply.github.com>
|
||
|
|
2766d07f6b |
perf(oxfmt): spawn Prettier workers on demand (#28789)
Follow-up to #28517 and #28787.
When the CLI needs Prettier and no worker is free, it spawns one worker,
up to `--threads` (default: the number of logical CPU cores). Spawned
workers stay alive for later files. Before this PR, the first file that
needs Prettier starts every worker.
## Results
`oxfmt --check` on AMD Ryzen 9 7940HS (16 threads), `main` at
|
||
|
|
45d86c303b |
perf(lexer): combine per-token annotation resets (#28492)
Keep annotation range ends together so the lexer can reset both ranges with one store per token while preserving comment ranges across lookahead. Refresh stale misc suite counts after the fixtures added on main. AI-assisted. |
||
|
|
d497fbf2ae |
perf(lexer): match keywords with byte patterns (#28494)
Match keyword suffixes as byte patterns to eliminate comparison-library calls. Initial M3 Max measurements showed about 3.2% higher parser throughput. `just c`, allocations, lint, and formatting pass. AI-assisted (Codex). |
||
|
|
b8ba9ae45f |
fix(formatter): print decorators on rest parameters (#28731)
oxfmt deletes a decorator on a rest parameter. This happens with the
default settings, and oxfmt reports no error.
```ts
class C {
m(@Dec() ...args: string[]) {}
}
// before
class C {
m(...args: string[]) {}
}
```
The parser and codegen support these decorators (#18938, #20132), but
the formatter printed only the rest element and its type annotation.
This PR prints the decorators in the same way as for `FormalParameter`.
The new fixture `ts/parameters/rest-decorators.ts` covers single and
multiple decorators, `@(await p)`, a decorator after another parameter,
and long parameter lists. The output matches Prettier 3.9.10 for every
case.
---
AI assistance was used for this change. I reviewed and tested it.
---------
Co-authored-by: leaysgur <6259812+leaysgur@users.noreply.github.com>
|
||
|
|
8432de6b27 |
fix(transformer/class-properties): apply a compound assignment to a static private field once (#28606)
If the target is `es2021` or lower, a compound assignment to a static
private field applies the operator twice, unless the left side of
`.#prop` is the class name. For example, `this.#count += k` is affected,
but `Counter.#count += k` is not.
```js
class Counter {
static #count = 10;
static add(k) {
this.#count += k;
return this.#count;
}
}
console.log(Counter.add(1)); // 11
```
The transformed code prints `21`, because it keeps `+=` and also adds
the old value on the right side:
```js
// before
_count._ += _assertClassBrand(Counter, this, _assertClassBrand(Counter, this, _count)._ + k);
// after
_count._ = _assertClassBrand(Counter, this, _assertClassBrand(Counter, this, _count)._ + k);
```
`Counter.#count += k` uses a separate branch that sets the operator to
`=`. The branch for `this.#count += k` did not set the operator. This PR
adds that line.
The new fixture `oxc/private-static-compound-assignment` covers `this`,
an object from a function call, and an object without the private field.
Its `exec.js` fails on main with `expected 21 to be 11`.
---
AI assistance was used for this change. I reviewed and tested it.
|
||
|
|
07627f7a9b |
perf(oxfmt): use worker_threads for tinypool again (#28787)
napi-rs/napi-rs#3670 fixed deadlock issue in our usecase, so now we can use it again. |
||
|
|
7e02d2cece |
refactor(lexer): reorder imports (#28785)
Pure style change. Reorder imports to our conventional order. Some got out of order once `#[cfg]` gates were removed in #28772. |
||
|
|
e9bdc066ab |
refactor(lexer): replace super::* imports (#28784)
Pure style change. Remove `use super::*;` statements from `disambiguate` and explicitly import all types and functions from where they come from. This makes it easier to trace dependencies. |
||
|
|
7b2eb1a339 |
refactor(lexer): convert pair! macro to a function (#28786)
Pure refactor. Remove `pair!` macro in `compress` and replace it with a function. This produces identical codegen, but is IMO a bit clearer to read because you get type annotations in IDE. Also reorder a few lines and add line breaks to make the code clearer. |
||
|
|
4c47b4d330 |
refactor(lexer): remove defunct #![allow] attrs from lanes.rs (#28776)
Pure refactor. `lanes` is a submodule of `pipeline`, and `pipeline/mod.rs` has the same `#![allow]` attributes, which apply to all its submodules. So the ones in `lanes.rs` have no effect. Remove them. |
||
|
|
b555e3703a |
perf(lexer): reduce allocations in macros (#28777)
Tiny optimization to macros. `Cpu::from_ident` finds the CPU feature set named by an identifier (e.g. the `Avx2` in `#[cpu_version(Avx2)]`). It compared the `Ident` with each feature set's name in turn. Inside a proc macro, comparing a `proc_macro2::Ident` with a `&str` converts the `Ident` to a `String` every time. So that was up to 3 allocations. Convert it to a `String` once, and compare that instead. This only affects how fast the macros run, not their output. |
||
|
|
0a710083ae |
fix(lexer): prevent dead code warnings when compiling crate for WASM (#28775)
Fix `dead_code` warnings when compiling `oxc_lexer` for WASM, which were introduced by #28772 and earlier PRs. |
||
|
|
f8fa6b2a03 |
refactor(lexer): order variants of DynCpu per architecture (#28774)
Define `DynCpu` separately for each architecture, so its variants can be in a different order on each. `Generic` is always first, then the SIMD feature set which the platform has, then the variants which can't exist on the platform: - x86_64: `Generic`, `Avx2`, `Neon`. - aarch64: `Generic`, `Neon`, `Avx2`. - Other platforms: `Generic`, `Avx2`, `Neon`. `Generic` is always 0, and the platform's SIMD feature set is 1. Uninhabited variants don't exist at runtime, so `DynCpu` is like a `bool`. On aarch64 with Neon enabled at compile time, and on other platforms, only one variant can exist, so `DynCpu` is zero-sized. Each variant which can't exist on the platform is `#[expect(dead_code)]`, with no `cfg_attr` needed. This alters codegen slightly in a few places, but those changes are entirely incidental - same instructions but in a different order, and only in code which only runs once per lex. |
||
|
|
84a5cd9906 |
refactor(lexer): remove cpu_select! macro (#28773)
Pure refactor - no changes to codegen. Now that `DynCpu` has all its variants on every platform (#28769), and all arms are compiled everywhere (#28772), `cpu_select!` macro does nothing which a plain `match` doesn't. Replace it with `match to_dyn(cpu) { ... }` everywhere, and remove the macro. A `match` is just Rust, so there's no macro syntax to learn, and rustfmt and Rust Analyzer handle it fully. Arms which do the same thing can also be combined with `_`, which `cpu_select!` didn't allow: ```rust // Before cpu_select!(cpu, { Avx2(cpu) => bm_any_avx2(bm, nw, cpu), Neon(_) => bm_any_generic(bm, nw), Generic(_) => bm_any_generic(bm, nw), }) // After match to_dyn(cpu) { DynCpu::Avx2(cpu) => bm_any_avx2(bm, nw, cpu), _ => bm_any_generic(bm, nw), } ``` This uses the `to_dyn` function, not the `Cpu::to_dyn` method. The method is an extra `#[inline(always)]` function around `to_dyn`, which rustc's MIR inliner can't inline into code which is generic over the CPU type. That changes the codegen of some callers. Those codegen changes may be incidental and have no perf impact, but impossible to know for sure without benchmarking, so using `to_dyn` is the conservative choice to avoid any accidental perf regressions. |
||
|
|
187b995274 |
feat(lexer): compile all code on all platforms (#28772)
Remove all `#[cfg(target_arch = ...)]` attributes from the lexer, except in the `cpu` and `simd` modules. The code for every CPU feature set (AVX2, Neon and generic) is now compiled, type-checked and linted on every platform, including the tests. Rust Analyzer sees all of the code for all platforms "as one". So e.g. AVX2 code can be worked on with full IDE support on an ARM Mac, and `cargo check` / `cargo clippy` on either kind of machine catches errors in the code for the other. Code for a feature set which the platform doesn't have still can't run, because that feature set's type is uninhabited there (see #28769). The compiler removes it. The macros change to support this: - `#[cpu_version]` no longer adds `#[cfg(target_arch = ...)]`. Its `#[target_feature(...)]` becomes `#[cfg_attr(target_arch = ..., target_feature(...))]`, because enabling another platform's target features is an error. - `#[cpu_multiversion]` generates all 3 entry points (`__avx2`, `__neon` and `__generic`) on every platform, with their target features in a `cfg_attr` in the same way. - `cpu_select!` compiles all 3 arms on every platform, instead of replacing the bodies of other platforms' arms with `unreachable!()`. On a platform where a feature set's type is uninhabited, its `#[cpu_multiversion]` entry point starts with `unreachable!()`, before it calls the function body: ```rust #[inline(never)] #[cfg_attr(target_arch = "aarch64", target_feature(enable = "neon"))] #[allow(unreachable_code)] unsafe fn __neon(src: &[u8], n: usize, cpu: Neon) -> usize { #[cfg(not(target_arch = "aarch64"))] unreachable!(); __impl(src, n, cpu) } ``` Without it, the copies of every function for that feature set (e.g. `__impl::<Neon>` on x86_64) would be part of the call graph, even though they can never run. That changes how rustc's MIR inliner treats other code, which changes the codegen of functions which only have a generic version. rustc removes code after `unreachable!()` early enough that this doesn't happen. The call to `__impl` stays, so the entry point still uses its parameters. Without it, clippy reports lints on their types which don't apply, e.g. `ptr_arg` warning on a `&mut Vec` parameter. With these workarounds, this PR makes no difference to generated ASM - it's a pure refactor. |
||
|
|
b8ea9641c8 |
refactor(lexer): move prefetch function into simd module (#28771)
Pure refactor - no change to codegen. Preparatory step for compiling all of the lexer's code on all platforms (#28772). `prefetch` was in `pipeline/coalesce`, and was the only code outside `simd` which used `std::arch::x86_64` intrinsics directly. Move it into `simd/prefetch.rs`, as a public free function. |
||
|
|
e920957d12 |
refactor(lexer): make Vec128 and Vec256 exist for all CPU types on all platforms (#28770)
Pure refactor - no change to codegen. Preparatory step for compiling all of the lexer's code on all platforms (#28772). Previously, the `simd/avx2` module was only compiled on x86_64, and `simd/neon` only on aarch64. So `Vec128<Avx2>` and `Vec256<Avx2>` only existed on x86_64, and `Vec128<Neon>` and `Vec256<Neon>` only on aarch64. Other platforms had no vector types at all. Compile both modules on all platforms. On other platforms, a vector wraps a `VecData128` / `VecData256` instead of the platform's SIMD type, which has the same size and alignment: ```rust type Vec128Inner = cfg_select! { target_arch = "x86_64" => __m128i, _ => VecData128, }; ``` There, the CPU type is uninhabited, so the vector can never be created. Add a `#[cpu_impl]` attribute macro, for each `impl` block of a CPU type's vector methods. It wraps each method's body in `cfg_select!`, so the body is only compiled on that CPU's platform, and is `unreachable!()` elsewhere. Signatures don't change, so the methods exist on every platform, and code which calls them compiles everywhere. ```rust #[cpu_impl(Avx2)] impl Vec256<Avx2> { pub fn movemask(self) -> u32 { // SAFETY: `Vec256` type guarantees platform supports AVX2 unsafe { _mm256_movemask_epi8(self.inner).cast_unsigned() } } } ``` expands to: ```rust #[cfg_attr( not(target_arch = "x86_64"), allow(dead_code, unused_variables, clippy::unused_self, clippy::needless_pass_by_value) )] impl Vec256<Avx2> { pub fn movemask(self) -> u32 { cfg_select! { target_arch = "x86_64" => { // SAFETY: `Vec256<Avx2>` type guarantees platform supports AVX2 unsafe { _mm256_movemask_epi8(self.inner).cast_unsigned() } } _ => unreachable!(), } } } ``` The `simd` tests are compiled on all platforms too. |
||
|
|
6d5be86ea1 |
refactor(lexer): make CPU types and all DynCpu variants exist on all platforms (#28769)
Pure refactor - no change to codegen. Preparatory step for compiling all of the lexer's code on all platforms (#28772). Previously, `Avx2` only existed on x86_64, and `Neon` only on aarch64, and the variants of `DynCpu` had `#[cfg]` attributes to match. `Generic` existed on all platforms, but was uninhabited on aarch64 with Neon enabled. Make `Avx2` and `Neon` work the same way as `Generic`. All these types now exists on every platform. Each has a private field which is `()` on its own platform, and `Never` (an enum with no variants) everywhere else: ```rust pub struct Avx2( cfg_select! { target_arch = "x86_64" => (), _ => Never, }, ); ``` On platforms which can't support the CPU feature set, the type is uninhabited, so it can never be created, and the compiler removes code which handles it. `DynCpu` has all 3 variants on every platform. Variants which can't exist on a platform are `#[expect(dead_code)]` there. The field is private, so only code in the `cpu` module knows that a type is uninhabited. A `match` anywhere else needs an arm for every variant, including ones which can't exist. So `cpu_select!` and `#[cpu_multiversion]` add arms for them, e.g. `DynCpu::Neon(_) => unreachable!()` on x86_64. |
||
|
|
b03fadad8d |
refactor(lexer): add all common methods to Vec128Impl and Vec256Impl traits (#28768)
Pure refactor - no change to codegen. Preparatory step for compiling all of the lexer's code on all platforms (#28772). Previously, the methods of `Vec128` and `Vec256` were inherent methods, defined separately for each CPU type in the `avx2` and `neon` modules. Code which is generic over the CPU type couldn't call them. Move every method which has both AVX2 and Neon versions into the `Vec128Impl` and `Vec256Impl` traits (introduced in #28766) - 31 methods for `Vec128`, and 36 for `Vec256`. Each CPU type implements the traits in its own module. `Vec128<C>` and `Vec256<C>` also have generic inherent methods with the same names, which call the trait methods. So callers don't need the traits in scope, and call sites don't change. The structs, the traits and the generic methods are in new `simd/vec128.rs` and `simd/vec256.rs` files. The `!`, `&`, `|` and `^` operators, and the `Operand128` / `Operand256` traits, are generic over the CPU type too. The operators move from `bit_traits.rs` into `vec128.rs` and `vec256.rs`. The `CpuType` alias, which picked the platform's CPU type with `#[cfg]`, is removed. Methods which only one CPU has stay inherent methods of that CPU's types: - AVX2 only: `movemask` and `Vec256::shuffle_u32s`. - Neon only: `shl_u8`, `shr_u8`, `pairwise_add_u8` and `test_u8`. `Vec256::shuffle_u8s` stays separate for each CPU too, because it does different things on each: 2 lookups in 16-entry tables on AVX2, 1 lookup in a 32-entry table on Neon. Neon's `Vec256` gets the methods it didn't have yet, which work on its 2 `Vec128` halves. Tests of the common methods move from `avx2/tests.rs` and `neon/tests.rs` into `simd/tests.rs`, and run with the CPU type which the platform has. `avx2/tests.rs` and `neon/tests.rs` keep the tests for methods which only one CPU has, or which behave differently on each. |
||
|
|
f0d84e7514 |
docs(lexer): add module doc comments to all files in simd directory (#28767)
Comments-only change. Add a top-level doc comment to each file in the `simd` directory which didn't have one. |
||
|
|
d6c045cc80 |
feat(lexer): introduce aligned VecData types (#28766)
Introduce `VecData128` and `VecData256` types, in `simd/data.rs`. They're byte arrays aligned on 16 and 32 bytes, for lookup tables and other data which is read into SIMD vectors. - `Vec128::read` / `Vec256::read` read one into a vector, with an aligned load. - `Vec128::write` / `Vec256::write` write a vector into one, with an aligned store. - `VecData128::read(cpu)` / `VecData256::read(cpu)` are the same as `Vec128::read` / `Vec256::read`, but read better for tables, e.g. `PH_A.read(cpu)`. Convert the lookup tables which SIMD code reads into vectors: `PH_A`, `PH_B`, `PH_T`, `MERGED_LUTS`, `WORD_LUTS`, `BITS` (Neon), `QCOMPACT` and `BCOMPACT`. The aligned read and store operations have minimal advantage over their unaligned versions. The realbenefit is ensuring that loads/stores do not touch multiple cache lines, which has a perf cost. The change to aligned types has a positive impact for `QCOMPACT` and `BCOMPACT`, which were previously under-aligned, so reads could cross cache lines. Now that they're made up of aligned `VecData256` / `VecData128` blocks, every load is guaranteed to be within a single cache line. `VecData128::read` and `VecData256::read` are generic over the CPU type. To support that, `CpuSimd` gets `Vec128` and `Vec256` associated types, and there are new `Vec128Impl` and `Vec256Impl` traits, which only contain `read` for now. Both types deref to a byte array, so they can be indexed like an array, except in `const` contexts. `as_bytes_mut` and `as_u32s_mut` are for building tables in a `const` or `static`. |
||
|
|
ae43c804e4 |
fix(linter/unicorn/prefer-single-call): remove duplicate fix status (#28763)
Noticed that `unicorn/prefer-single-call` rule declared both `pending` and `fix` as its fix status, this PR removes `pending` and also changes to `fix` to `conditional_fix` since the fix is skipped in 2 cases. Also small change to `declare_oxc_lint` so that it rejects a second fix capability |
||
|
|
62a8e7818e |
chore(deps): update dependency oxlint to v1.91.0 (#28760)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxlint](https://oxc.rs/docs/guide/usage/linter) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`1.90.0` → `1.91.0`](https://renovatebot.com/diffs/npm/oxlint/1.90.0/1.91.0) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/oxc (oxlint)</summary> ### [`v1.91.0`](https://redirect.github.com/oxc-project/oxc/blob/HEAD/npm/oxlint/CHANGELOG.md#1910---2026-11-02) [Compare Source](https://redirect.github.com/oxc-project/oxc/compare/oxlint_v1.90.0...oxlint_v1.91.0) ##### 🚀 Features - [`419c1b0`](https://redirect.github.com/oxc-project/oxc/commit/419c1b0) linter: Add allow and allowPattern options to react/capitalized-calls (Christian Vuerings) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDQuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjIwNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |
||
|
|
b904d51667 |
fix(react-compiler): avoid panic on non-ASCII ref-like names (#28758)
Reading `object_name[..1]` panics when a ref-like identifier starts with a multibyte character, such as `한글Ref`. Use `.chars().next()` to preserve the ASCII name check without slicing through a UTF-8 character. Ports the fix and regression fixture from https://github.com/facebook/react/pull/38025. Fixes #28278. |
||
|
|
a8a621a46d |
fix(linter/eslint/no-fallthrough): ignore unreachable exits from infinite loops (#28755)
Cases ending in `while (true)`, `do ... while (true)`, or `for (;;)` cannot fall through unless a reachable break exits the loop. Oxlint reports these cases even though ESLint accepts them. Traverse CFG edges within each switch and skip the normal exit from statically true loop conditions. Preserve reachable break paths and unused fallthrough comment diagnostics, with regression coverage for async polling, nested breaks, parenthesized conditions, and unreachable breaks. Linter tests and lint pass. The broader Oxlint suite fails in type-aware suppression tests: one produces different suppression counts, and another aborts because its expected suppression file is missing. Fixes #28579 |
||
|
|
7a17a80d9e |
fix(linter/eslint/prefer-spread): report calls on non-member expressions (#28753)
`prefer-spread` now reports `(c ? a : b).apply(null, args)` and `m!.apply(null, args)`, matching ESLint. Check whether the applied expression is a member expression rather than requiring an identifier, while retaining receiver checks for method calls. Fixes #28728 |
||
|
|
28442f7246 |
fix(oxlint): prevent --init from overwriting existing configs (#28754)
`oxlint --init` now exits with an error if any supported config filename already exists in the current directory, preserving existing configs and avoiding conflicting ones. Exclusive file creation also prevents overwrites if `.oxlintrc.json` appears after the check. Fixes #28741 |
||
|
|
0ae350d7d8 |
fix(linter/unicorn/no-zero-fractions): preserve exponent case (#28752)
Trim trailing zeros from the fractional part while preserving the exponent suffix verbatim. This avoids false positives on literals such as `4.6566128731E+100` and fixes `1.0E+10` to `1E+10`, matching Unicorn 62. Fixes #28743 |
||
|
|
4d8d482157 |
chore(deps): update dependency oxfmt to ^0.76.0 (#28750)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [oxfmt](https://oxc.rs/docs/guide/usage/formatter) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxfmt)) | [`^0.75.0` → `^0.76.0`](https://renovatebot.com/diffs/npm/oxfmt/0.75.0/0.76.0) |  |  |  |  | --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDIuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjIwMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |
||
|
|
3dbf1409ab |
fix(parser): treat escaped contextual keywords as names, not modifiers (#28748)
Treat contextual keywords spelled with a Unicode escape (`st\u0061tic`,
`\u0061sync`, `g\u0065t`, …) as property names rather than modifiers.
Such a token is an IdentifierName and cannot match a keyword terminal,
so `class A { st\u0061tic
x }` declares two fields named `static` and `x`. Previously it was
parsed as `static x` and reported "Keywords cannot contain escape
characters".
`try_parse_modifier` and `parse_contextual_modifier` now skip escaped
tokens. Invalid forms such as `class X { st\u0061tic y() {} }` are still
rejected. The error is now reported at the token after the name, as
Babel does, which accounts for the updated parser snapshots.
The new `misc/pass` fixture covers fields named `static`, `async`,
`get`, `set`, and `accessor` that end at a line break. The rejected
forms are already covered by Babel and test262.
Validated with scoped Rust checks and full parser conformance.
---------
Co-authored-by: Cameron <cameron.clark@hey.com>
|
||
|
|
b5b3f3fc88 |
fix(linter/eslint/no-misleading-character-class): detect more combining characters (#28733)
fixes https://github.com/oxc-project/oxc/issues/28729 Generated by Claude Sonnet 5.5 🤖 The upstream rules uses regex to detect if it is a combining character with the `\p` shorthand: https://github.com/eslint/eslint/blob/main/lib/rules/utils/unicode/is-combining-character.js#L11 Because there is no similar utility in rust, we currently have a self-managed list of character detection. This list now get extended to detect more characters for other languages like Arabic, Hebrew, Thai ... Verified manually that the failing test cases also fails upstream. Did not verfify if the list is really valid and covers all possible combining characters. I hope that the AI did no mistakes and included characters which are still valid. |
||
|
|
74d50c1f32 |
perf(linter): overlap import graph batches instead of polling on a pool worker (#28323)
<!-- CURSOR_AGENT_PR_BODY_BEGIN --> With `--import-plugin`, `main` builds the module graph in batches of `threads × 4` entry files and waits for each batch and its dependency closure to finish before starting the next. The waiter is a pool worker that polls with `try_recv` + `yield_now`. In the profiles, the other workers spend much of the run spinning in rayon's work-stealing loop, the spinning grows toward the end of the run, and the run ends with a long tail where only a few cores are busy. The graph loop now runs on the calling thread (`rayon::in_place_scope`) and blocks on `recv`. A new batch can start while earlier ones are still open. When one open batch imports from another, union-find merges them and they finish together, so at most `3 × group_size − 1` entry files (plus their dependencies) are in flight. Without the import plugin, the loop keeps `rayon::scope` and is unchanged; scope selection and module resolution share one resolver predicate. The language server (`run_source`) and the rule test harness (`run_test_source`) choose the scope the same way, so with the import plugin they also run the graph loop on the calling thread. The speedup comes mostly from less total CPU work and a shorter tail, not from higher core utilization. samply profiles (4 threads, `-A all --import-plugin`, median of 5 runs): | Corpus | Wall | CPU | Spent in `steal` / `find_work` / `try_recv` | |---|---|---|---| | next.js | 799 → 482 ms | 1,728 → 1,272 ms | 25.3% → 0.1% of samples (438 → 2 ms) | | VS Code | 1,479 → 1,183 ms | 4,240 → 3,887 ms | 9.6% → 1.3% (402 → 51 ms) | On `main`, one worker is on-CPU for almost the whole run (next.js: 3,098 samples vs 899–1,493 on the other three workers). With this PR the four workers are even (1,124–1,405), and the top of the inverted call tree is lexer, allocator, and semantic code instead of crossbeam's deque. <img width="1290" height="358" alt="next.js: main vs this PR" src="https://github.com/user-attachments/assets/ffd00f15-4ad9-46e1-bd60-ca23c0efa696" /> <img width="1290" height="358" alt="VS Code: main vs this PR" src="https://github.com/user-attachments/assets/b917dbee-21ce-4262-b374-93af0df0beb7" /> Thread timelines from Firefox Profiler, `main` above and this PR below, same time scale. ## Results `-c empty.json --disable-nested-config --silent -A all --import-plugin`. VS Code `src` at `1306698e` (11,260 files) and next.js `packages/` at `3bccd4df` (3,404 files). Time is median (min … max). Peak RSS is the median of `wait4` max RSS. Δ time is the median of paired per-round differences. | Machine | Threads | Corpus | main | this PR | Δ time | Δ peak RSS | |---|---|---|---|---|---:|---:| | Linux x86-64, 8 vCPU | 4 | VS Code | 1,673 ms (1,566 … 2,282) | 1,403 ms (1,231 … 1,754) | −19.7% | +3.8% | | | 4 | next.js | 744 ms (649 … 949) | 444 ms (396 … 904) | −40.4% | +16.7% | | | 8 | VS Code | 960 ms (832 … 1,256) | 771 ms (661 … 900) | −18.5% | +5.6% | | | 8 | next.js | 626 ms (539 … 821) | 426 ms (374 … 509) | −30.1% | +14.1% | | Apple M4 Max (12P + 4E) | 8 | VS Code | 440 ms (427 … 494) | 359 ms (354 … 404) | −18.3% | +4.4% | | | 8 | next.js | 263 ms (248 … 282) | 191 ms (183 … 202) | −28.0% | +4.2% | | | 16 | VS Code | 378 ms (362 … 571) | 328 ms (321 … 530) | −12.6% | +8.9% | | | 16 | next.js | 238 ms (228 … 306) | 168 ms (164 … 249) | −29.1% | +2.6% | Linux: 20 rounds per row. M4 Max: 40 rounds (two passes of 20). In every 20-round set, this PR was faster in at least 17 rounds. CPU time also drops on the 8-thread Linux run: user + sys 1.73 → 1.26 s on next.js and 4.10 → 3.72 s on VS Code. Without `--import-plugin`, time and RSS are within noise on both machines: paired medians range from −6% to +3%, and this PR was faster in 16–26 of 40 rounds. On the M4 Max, lint output (`-D correctness --import-plugin -f unix`, sorted) is byte-identical to `main`. ## Memory Peak RSS is 3–17% higher with the import plugin. The largest increase is next.js on Linux; on the M4 Max the same corpus is +3% to +4%. Three things contribute: - Overlapping batches keep up to `3 × group_size − 1` entry files and their dependencies in flight, compared with one batch on `main`. - Lint jobs are spawned from the calling thread, so they go to rayon's global FIFO queue behind parses that are already queued. ASTs are linted and freed later. - The per-file arena allocators keep their capacity and the pool does not shrink, so the higher peak persists until exit. On next.js, `MIMALLOC_PURGE_DELAY=0` reduced the gap from 86 MB to 28 MB in an earlier measurement. Runs without the import plugin are unaffected. A lint-first scheduler did not reduce peak RSS on 4 cores. A cap on in-flight work is left for a follow-up. Linking `loaded_modules` stays serial on the graph thread. A parallel iterator started from a non-worker thread would queue behind pending parses and block the loop. ## Tests A unit test covers merging into a larger dependency group when `group_id` changes: it checks the new root, the accumulated counts and paths, and that the donor is cleared. Other `ModuleGroups` tests cover adding a group, path compression, and refusing to merge a finished group. The `import_plugin_multi_group` CLI fixture (9 files, `-D import/no-cycle`) matches `main`. <details> <summary>Reproduce</summary> ```sh cargo build --release -p oxlint --bin oxlint --no-default-features --features allocator echo '{}' > empty.json # Linux: taskset -c 0-3 or 0-7. macOS: --threads 8 or --threads 16. oxlint -c empty.json --disable-nested-config --silent -A all --import-plugin . ``` Run from VS Code `src` (`1306698e`) or next.js `packages/` (`3bccd4df`). Each build has its own `CARGO_TARGET_DIR`. Binaries run round-robin in rotating order with 2 warmups each. On Linux, rounds where other processes used more than 0.5 core were redone. On macOS, each round waited until other processes used less than 1.5 cores. Profiles: `samply record --rate 4000` pinned to separate cores, 1 warmup and 5 runs per binary. `main` is `31cbe378d4`. Timings were taken on an earlier revision of this branch (`be6ef780c8`), before the branch was squashed into one commit. Changes made after that revision were not re-timed: tests and refactors, a deduplicated resolver check, the same scope selection for the language server and rule tests, one fewer `Arc` clone per already-queued import, and a simpler debug check. </details> <!-- CURSOR_AGENT_PR_BODY_END --> |
||
|
|
e3b1bb4e44 |
fix(transformer/class-properties): keep the value of a private accessor assignment (#28716)
If the target is `es2021` or lower, `this.#x += k`, `this.#x ??= k`, and
`++this.#x` on a private accessor give the wrong value. They should give
the new value of `#x`, but they give the return value of the setter
instead. Most setters return nothing, so the value is usually
`undefined`.
```js
class Counter {
#n = 1;
get #count() { return this.#n; }
set #count(v) { this.#n = v; }
add(k) { return (this.#count += k); }
}
console.log(new Counter().add(2)); // 3
```
The transformed code prints `undefined`, because the result of the
expression is the setter call:
```js
// before
return _set_count.call(_assertClassBrand(_Counter_brand, this), _get_count.call(_assertClassBrand(_Counter_brand, this)) + k);
// after
return _toSetter(_set_count.bind(_assertClassBrand(_Counter_brand, this)))._ = _get_count.call(_assertClassBrand(_Counter_brand, this)) + k;
```
Plain `=` and postfix `x++` are already correct, and plain `=` already
uses `_toSetter`. This PR uses the same form if the value of the
expression is used. If the expression is a statement, the output does
not change.
I did not use a temp var, because a temp var in a default parameter is
shared between calls, and a recursive setter overwrites it.
The new fixture `oxc/private-accessor-assignment-value` covers instance
and static accessors, all the operators above, an object from a function
call, arrow function bodies, and the recursive case. Its `exec.js` fails
on main with `expected 'from setter' to be 3`.
---
AI assistance was used for this change. I reviewed and tested it.
---------
Co-authored-by: qing deng <dengqing0821@gmail.com>
|
||
|
|
ae7474e1f2 |
fix(minifier): keep array concat chain reads before later side effects (#28261)
Each call in an array `concat` chain reads its arguments before the next call evaluates its own. Merging the calls moves those reads later, so a later argument can change an earlier one first:
```js
function f(arr) { return [].concat(arr).concat(arr.pop()); }
```
With `arr = [1, 2, 3]` this returns `[1, 2, 3, 3]`, but main merges it into `[].concat(arr, arr.pop())`, which returns `[1, 2, 3]`.
A chain now merges only when no later call has an argument with side effects after an earlier call has an argument that `concat` may read. Primitives and array literals don't count as reads. This is the array half of #27580; the string half landed in #27098.
When the merge is refused, the innermost call still folds its leading literals into its array: `[1].concat(1, arr).concat(arr.pop())` → `[1, 1].concat(arr).concat(arr.pop())`.
## Size
`echarts.js` grows by 8 B minified and 6 B gzipped; nothing else in `minsize` changes. The chain `[].concat(ecIns._componentsViews).concat(ecIns._chartsViews)` no longer merges, because with the default `propertyReadSideEffects` a getter on `_chartsViews` could change `_componentsViews`. With `propertyReadSideEffects: false` it merges as before.
monitor-oxc: 48 files larger (+960 B raw, +76 B gzip), 8 B per `concat` call that no longer merges.
## Not covered
With `propertyWriteSideEffects: false`, `arr[0] = 9` counts as free of side effects, so `[].concat(arr).concat(arr[0] = 9)` still merges. The string guard has the same gap; it needs a separate fix.
Part of #26242.
Implemented with AI assistance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_018ogydZW3PPvmx3orpBQot4
|
||
|
|
ec327aa1ec |
fix(semantic): keep block type bindings separate from hoisted vars (#28601)
Fixes #28600. `var I; interface I {}` in a nested block merged the interface into the hoisted `var`, so the type moved out of the block. TypeScript keeps the interface in the block. #27936 fixed the opposite order, `interface I {} var I;`. ## Cause Hoisting a `var` records it in `hoisting_variables` for each intermediate scope. `check_redeclaration` falls back to that map, and `declare_symbol_on_scope` then reused the returned symbol, even for a type declaration. ## Changes - `declare_symbol_on_scope`: a declaration with no value flags only reuses a symbol bound in the target scope. Otherwise it creates a new symbol there. - `check_redeclaration`: a binding in the scope and a hoisted `var` are now checked separately. Without this, the block's type binding would hide the conflict and the first change would lose the error in `{ var X; interface X {} let X; }`. - `check_redeclaration`: only the value flags of a hoisted symbol are compared. The symbol can also carry type flags from a declaration in the outer scope, and those do not conflict with a declaration in the block. The second change also fixes a missed error in `{ interface X {} var X; let X; }`. tsc rejects both forms with TS2481. The value-flag restriction also fixes a false error on main: `type X = number; { var X = 0; interface X {} }` was reported as a redeclaration, but tsc accepts it. An uninstantiated `namespace` in a block also has no value flags, so it now gets its own block symbol too. tsc already rejects namespaces in blocks (TS1235). ## Tests - `tests/integration/symbols.rs`: symbol separation and reference resolution, including the switch case, plus the redeclaration errors and the valid programs where an outer type shares a symbol with the hoisted `var`. - `tests/fixtures/oxc/ts/interfaces/hoisted-var-then-interface.ts`: a new snapshot of the scope placement. No existing semantic snapshots changed. --- AI assistance was used for this change. I reviewed and tested it. --------- Co-authored-by: qing deng <dengqing0821@gmail.com> |
||
|
|
3fd78d7451 |
fix(codegen): preserve names in duplicate source mappings (#26051)
When an unnamed parent node and a mangled identifier begin at the same source position, codegen deduplicated the identifier mapping. This retained location data but discarded the original identifier name, leaving consumers unable to restore inferred function names in source-mapped Node stack traces.
```mermaid
flowchart LR
A[AssignmentExpression mapping<br/>unnamed] --> B[Token at source position P<br/>name: none]
C[Identifier mapping<br/>name: longName] --> D{Same source position P?}
D -->|before| E[Drop duplicate<br/>names: empty]
D -->|after| F[Enrich existing token<br/>name: longName]
```
### Changes
- Cache whether the last emitted token has a name, keeping duplicate-position handling cheap in the source-map builder hot path.
- Enrich an unnamed duplicate token when a later identifier mapping supplies the original name.
- Add a unit regression test for the builder behavior.
- Extend the existing Node source-map stack-trace snapshot test with a mangled ES module fixture.
The Node fixture verifies:
```text
function longFunctionName(longName) { ... }
↓ mangled
function e(e) { ... }
```
and asserts that the source map includes both original names while Node restores the inferred inner frame as:
```text
at longName (/project/input.js:6:15)
```
|
||
|
|
406bc50d1d |
chore(deps): update oxc to ^0.157.0 (#28734)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [@oxc-project/types](https://oxc.rs) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types)) | [`^0.156.0` → `^0.157.0`](https://renovatebot.com/diffs/npm/@oxc-project%2ftypes/0.156.0/0.157.0) |  |  |  |  | | [oxc-parser](https://oxc.rs/docs/guide/usage/parser) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/napi/parser)) | [`^0.156.0` → `^0.157.0`](https://renovatebot.com/diffs/npm/oxc-parser/0.156.0/0.157.0) |  |  |  |  | --- ### Release Notes <details> <summary>oxc-project/oxc (oxc-parser)</summary> ### [`v0.157.0`](https://redirect.github.com/oxc-project/oxc/blob/HEAD/napi/parser/CHANGELOG.md#01570---2026-11-02) ##### 🐛 Bug Fixes - [`3421516`](https://redirect.github.com/oxc-project/oxc/commit/3421516) parser: Preserve default import names in reexports ([#​28442](https://redirect.github.com/oxc-project/oxc/issues/28442)) (Boshen) - [`ed7df7d`](https://redirect.github.com/oxc-project/oxc/commit/ed7df7d) ast/estree: Include comments in TS Program span ([#​28326](https://redirect.github.com/oxc-project/oxc/issues/28326)) (camc314) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMDIuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjIwMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |
||
|
|
f3fcdcc61b |
release(apps): oxlint v1.91.0 && oxfmt v0.76.0 (#28718)
# Oxlint ### 🚀 Features -oxlint_v1.91.0 oxfmt_v0.76.0 |
||
|
|
f799f174b5 |
release(crates): oxc v0.157.0 (#28719)
### 🚀 Features -crates_v0.157.0 |
||
|
|
f8982585aa |
test(lsp): remove duplicate dynamic watchers capability test (#28585)
<!-- claude-projects-attribution: {"github_login":"Brooooooklyn"} -->
_Requested by **Brooklyn** · [project thread](https://claude.ai/code/project/chan_018s9RxRdtH424j2EhXdzV3N?thread=cmsg_018s9RxRdtH424j2EhXdzV3NQxbeR8Pt7qaqSQwoq9rEFo)_
<!-- routine:prune-tests:duplicate-lsp-capability-test -->
Before: `capabilities::test::test_dynamic_watchers_intellij` and `test_dynamic_watchers_vscode` in `crates/oxc_language_server/src/capabilities.rs` have been byte-identical since both were added in #11075: the same `ClientCapabilities` with `workspace.did_change_watched_files.dynamic_registration: Some(true)`, and the same `assert!(capabilities.dynamic_watchers)`.
After: the IntelliJ copy is gone. Only lines inside `#[cfg(test)] mod test` change.
### Totals
- Files touched: 1.
- Cases deleted: **1 of 111** collected by `cargo test -p oxc_language_server -- --list` (110 run, 1 ignored); after: 110.
- Cases merged: 0. Net lines: **−17**.
- Runtime saved: 0.09 ms, the removed case's duration.
### Why it is safe
- Mutation M1, `capabilities.rs:41`: `dynamic_registration.is_some_and(...)` → `relative_pattern_support.is_some_and(...)`. Both capability tests fail identically; since the bodies are identical, every mutation affects both the same way.
- Still covered by `test_dynamic_watchers_vscode`. The 4 `tests::test_suite` dynamic-watcher tests also notice M1, but by hanging (see below).
Kept after defense: none.
| suite | before | after | delta |
|---|---|---|---|
| `oxc_language_server` (lib) | 111 (110 pass, 1 ignored) | 110 (109 pass, 1 ignored) | −1 |
0 failures before and after; the same test is ignored in both runs.
### What ran and what did not
- Ran at `d14dbd1e1` and on this branch: `cargo test -p oxc_language_server` (with `--list`, and JSON output with `--report-time`); M1, reverted; `cargo clippy -p oxc_language_server --all-targets -- --deny warnings`; `rustfmt --check --edition 2024` on the file; `git diff --check`. All clean.
- Not run: the rest of the workspace and the coverage/conformance suites.
Open pull requests: none of the open PRs touches `capabilities.rs`. Age: the test dates from
|
||
|
|
564559dd11 |
perf(linter/prefer-const): avoid collecting resolved references (#28732)
Two checks in `prefer_const.rs` collected resolved references into temporary vectors before scanning them. Borrow the resolved reference ID slices and map IDs to references in each existing pass, removing both allocations while preserving reference order, predicates, and behavior. |
||
|
|
563cf449a9 |
chore(runtime): sync helpers with Babel 8.0.8 (#28172)
Sync the complete published `@babel/runtime@8.0.8` helper tree so future updates have a recorded upstream baseline. Import all 95 CommonJS/ESM helper pairs, the MIT license, conditional exports, and `sideEffects: false`. Preserve 26 legacy Babel helper pairs, three TypeScript decorator helper pairs, and existing import paths. 189 of the 191 upstream files are byte-identical. The two `setFunctionName` implementations retain Oxc's existing behavior and match main. Add a hash manifest, a repeatable sync script, CJS/ESM tests in CI, and an async-generator execution regression. No new helper optimizations or correctness patches are included. Validation: upstream hashes, compatibility preservation, sync idempotence and drift refusal, formatting, lint, syntax, and whitespace checks passed. Runtime tests were not rerun after restoring this tree because automatic approval blocked helper execution; the newer Babel fixture revision pinned by main was not run locally. AI assistance: Claude. |
||
|
|
303d9e803f |
docs(formatter): strengthen start-token-no-double-parens rationale (#28711)
The leading parens only guard a statement or arrow body start, so
Prettier prints the same object lookup idiom without them elsewhere
(`const get = ({ a: 1, b: 2 } as const)[key];`).
List that sibling and the assignment arrow body in `Why`, and replace
the `export default` example with a measured one.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
dbcc358aee |
fix(formatter): keep arrow conditional body parens before a leading function/class (#28710)
`should_add_parens` skipped the parens of an arrow's conditional body
when it starts with an object, function or class, assuming the leading
node prints its own.
Only an object does in an arrow body, so `() => (function () {}).a ? b :
c` printed with no parens at all.
Matches Prettier: skip them only for a leading object.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
b6f1ac9b05 |
perf(formatter_markdown): bump parser to 0.0.4 and skip unread work (#28713)
- bump oxc-markdown-parser 0.0.4 which skips autolink literal attempts that cannot match - Compute a text's next words only under `proseWrap: always`, the only mode that reads them - Print link destinations with nothing to escape as is About 15% CPU instruction reduction. |
||
|
|
633ee960da |
perf(formatter_css): use u32 spans (#28708)
- bump oxc-css-parser 0.0.19 which changes `Span` from `usize` to `u32` - update conversion codes Peak memory is reduced 5% ~ 20% in large CSS file. |