Memoization can hide a required `TypeError` after an optional chain
short-circuits on an earlier render. For `obj?.a.b(getArg())`, rendering
first with `obj = undefined` caches `undefined`; rendering again with
`obj = {}` can reuse that result because the reduced dependency `obj?.a`
is still `undefined`. The second render should throw when reading `.b`.
Keep non-optional continuation dependencies below optional prefixes
instead of letting the prefix subsume them. When a computed or call
continuation cannot be represented as a property path, retain the
original optional guard dependency. For conditionally evaluated chains
such as `getArg() ? obj?.a.b(getArg()) : null`, truncate an unhoistable
dependency before its last optional link. This tracks the guard without
moving a throwing read ahead of its condition. Fully optional chains
keep their existing dependency reduction.
Adds 11 focused Node runtime cases comparing compiled and original
JavaScript over two consecutive renders, with truthy and falsy
conditions. They cover static, computed, optional-call, nested,
conditional, and fully optional chains, plus method receivers. Output
snapshots cover the generated dependencies.
This bug predates and is independent of
https://github.com/oxc-project/oxc/pull/28144. The same static,
computed, and optional-call failures were reproduced with
`babel-plugin-react-compiler@1.0.0` and `@babel/core@8.0.9`; this
intentionally corrects behavior also present upstream.
Validation:
- `cargo +1.99.1 test -p oxc_react_compiler --all-features`: 18 unit
tests, 26 integration tests, and the full snapshot suite passed.
- `cargo +1.99.1 clippy -p oxc_react_compiler --all-targets
--all-features -- -D warnings` passed.
- `cargo +1.99.1 fmt -p oxc_react_compiler -- --check` and `git diff
--check` passed.
- An additional local differential matrix of 73 expressions and 18,688
render transitions passed with no skipped compilations.
- NAPI binding tests were not run locally.
AI assistance: the implementation, regression tests, and PR description
were prepared with AI assistance.
monitor-oxc's Test262 job fails on every run because `cargo coverage
runtime` no longer matches `runtime.snap`. The runtime suite skips the
cases V8's `test262.status` lists as failing, and it fetches that file
from V8's `main` branch at run time. On 2026-10-09 V8 landed
`03af3c3548` ("[scopes] Reject var arguments in parameter eval") and
removed the 88 `language/eval-code/direct/*declare-arguments*` entries.
Node 26.5 bundles V8 14.6.202, which predates that fix, so those 88
cases now run and fail with `Expected a SyntaxError to be thrown`.
The skip list exists to hide bugs of the engine that executes the cases,
and that engine is Node's V8, not V8 `main`. `main` is wrong in both
directions: it un-skips cases V8 fixed after Node's branch cut, and it
skips 236 entries for test262 files newer than the pinned checkout.
The list is now fetched at the V8 version reported by `node -p
process.versions.v8` (`14.6.202.34`, which V8 tags on GitHub), falling
back to `main` only when that tag does not exist. The gitignored cache
file carries the version in its name so a Node bump cannot serve a stale
list.
With the pinned list the snapshot only changes when the test262 pin or
`.node-version` moves. Regenerated against Node 26.5.0 it adds nothing
and drops: four `delete/super-property-uninitialized-this` cases that V8
fixed upstream after 14.6, the `typeof/symbol.js` timeout, and the two
`bigint-and-number-extremes` minify cases that #27656 fixed (the regular
CI does not run the runtime suite, so that PR could not update the
snapshot):
| skip list | cases | passed | new failures |
| --- | --- | --- | --- |
| V8 `main` today | 18369 | 17307 | 88 |
| V8 `14.6.202.34` | 18277 | 17306 | 0 |
Written with Claude Code; reviewed and tested by me.
Unused calls such as `const f = x => x + 1; f(1);` currently survive as
`(x => x + 1)(1)`. Track constant arguments for arrow IIFEs and strict
regular-function IIFEs so the existing constant folder and side-effect
analysis can remove the call. Parameter constants guide folding without
replacing bare parameter reads, avoiding duplicated argument literals.
The optimization preserves argument evaluation and respects parameter
writes, redeclarations, and direct eval. It conservatively skips
defaults, destructuring, rest parameters, and spread arguments, and
preserves the existing DCE-only IIFE behavior. Regular functions are
excluded when their own name is referenced or when they are non-strict,
since recursion and mapped arguments can invalidate the call's parameter
values.
Regression coverage includes multiple parameters, nested closures, side
effects, mixed BigInt arithmetic, argument duplication, recursion, and
strict versus sloppy arguments aliasing.
Fixes#27379.
Validation:
- `just fmt`
- `just test -p oxc_minifier` — 723 tests and 2 doctests passed; 33
existing tests ignored
- `just lint -p oxc_minifier`
- `pnpm run build-test` and `pnpm test` in `napi/minify` — 890 tests
passed; 14 skipped and 1 todo
- `just minsize` — size and allocation snapshots unchanged
- 586 runtime equivalence comparisons across 148 inputs through the Node
API, with and without mangling, in module and script modes
AI disclosure: Developed with OpenAI Codex.
Inlining constant template expressions can join a decimal digit onto a
preceding raw `\0`. The resulting `\00` is a forbidden octal escape
inside template literals, so DCE can emit JavaScript that fails to
parse. This surfaced in rolldown/rolldown#10661.
When a fold would create that boundary, serialize the null as `\x00` and
continue merging the template fragments. This preserves the cooked value
and the optimization, matches Terser and esbuild, and leaves
escaped-backslash cases unchanged.
## Example
Input:
```js
return `${messageId}\0${correlationId}\0${0}`;
```
Before:
```js
return `${messageId}\0${correlationId}\00`;
```
After:
```js
return `${messageId}\0${correlationId}\x000`;
```
Verified with `cargo test -p oxc_minifier` (643 passed, 35 ignored), the
DCE example's `--twice` idempotency check, a runtime equivalence check,
`just minsize` with unchanged size/allocation snapshots, Clippy with
warnings denied, and formatting. `just ready` additionally passed
dependency, typo, generation, formatting, workspace checks, and all
minifier tests; the remaining workspace run stopped on three unrelated
Oxlint ANSI-color snapshots because the environment sets `NO_COLOR=1`,
and a color-enabled rerun was blocked by the filesystem running out of
space.
Refs rolldown/rolldown#10661.
Implemented with AI assistance (OpenAI Codex). The contributor remains
responsible for reviewing, understanding, and submitting the change
under the Oxc AI usage policy.
The [Monitor Oxc
job](https://github.com/oxc-project/oxc/actions/runs/30413998931/job/90456346227?pr=25041)
fails for fork PRs because `pull_request` workflows do not receive
repository secrets, leaving the GitHub App client ID empty.
Keep the `run-monitor-oxc` label trigger for same-repository PRs. Fork
label events are skipped before secrets are used; a trusted maintainer
can trigger them with `/monitor-oxc run` instead. The comment path
verifies triage permission, rejects PRs pushed after the command, and
dispatches the exact head SHA. This follows [Vite's ecosystem CI
workflow](https://github.com/vitejs/vite/blob/main/.github/workflows/ecosystem-ci-trigger.yml).
Verified with `just ready`.
AI assistance was used to investigate the failure and prepare this
change.
Numbers with numeric separators (underscores) like
`123.000_000_000_000_000_000_000_0` were incorrectly having their
trailing zeros stripped, resulting in `123.0`.
Prettier preserves these numbers because its regex-based `\d` patterns
don't match underscores, so transformations like trailing-zero removal
can't match through separators. This adds an early bailout in
`format_trimmed_number` when the number contains underscores, only
applying lowercase and leading-zero normalization (`.1_1` → `0.1_1`).
Closes#18968
Co-authored-by: Yuji Sugiura <6259812+leaysgur@users.noreply.github.com>
Add critical information on how we test in each crate. Additionally, add
CLAUDE.md to fix the issue that Claude code doesn't read AGENTS.md
automatically.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Input:
```ts
class DeclareFields {
@dec declare name: string;
@dec declare output: number;
}
```
Before output:
```ts
class DeclareFields {}
```
After output:
```ts
class DeclareFields {}
babelHelpers.decorate([dec], DeclareFields.prototype, "name", void 0);
babelHelpers.decorate([dec], DeclareFields.prototype, "output", void 0);
```
The `declare` fields with decorators should be transformed, but they
would be removed in the typescript plugin before legacy decorators
transform them, so this PR is to adjust the transform order and allow
`declare` fields can be transformed
Optimize `Scoping::rename_symbol` to avoid calling `to_string`, since
the borrow checker is getting in the way, so the `set_symbol_name` and
`rename_binding` method implementations need to be combined.
Part of https://github.com/oxc-project/backlog/issues/163
Prepare #10612. I just found that this method has a `to_string`
allocation that can be avoided. However, it needs to combine
`set_symbol_name` and `rename_binding`, so move to `Semantic`.
If a class or its parent classes don't have a private field, we don't
need to transform the expression. Because we only transform expressions
that include a private field.
Co-authored-by: Dunqing <29533304+Dunqing@users.noreply.github.com>
close: #9169
NOTE: I didn't copy any `sourcemap` tests to here, because these are not
directly related to this plugin.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: 翠 / green <green@sapphi.red>
Found a bunch of failed tests related to we don't use
`classPrivateSetter` and `classPrivateGetter` to transform private
getter and private setter. The simplest way is to add alternative helper
functions for these two. The reason we don't use I have explained in
#8132
```shell
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-add.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «3») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-bitand.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «0») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-bitor.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «15») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-bitxor.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «257») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-div.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «0.5») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-exp.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «1000») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-lshift.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «96») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-mod.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «1») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-mult.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «6») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-rshift.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «3») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-srshift.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «3») to be true
tasks/coverage/test262/test/language/expressions/compound-assignment/left-hand-side-private-reference-accessor-property-sub.js
transform error: Test262Error: The expression should evaluate to the result Expected SameValue(«undefined», «1») to be true
```
related: #4937
There are no tests/snapshots updates and seems we don't have a way to
test it until #4937 is merged. But I tested it locally and it worked!
close: #3620
In `Babel`, the expected output is:
```ts
var x = 10;
var Foo = function(Foo) {
Foo[Foo['a'] = 10] = 'a';
Foo[Foo['b'] = 10] = 'b';
Foo[Foo['c'] = 30] = 'c';
return Foo;
}(Foo || {});
```
IMO, `Foo.b + x` is enough, because `x` is not a const variable. The
output same as with `typescript`
We have a conclusion that codegen will print whatever is in the AST,
instead of having an option to enable printing TypeScript syntax. I plan
to remove codegen's `enable_typescript` option after we strip out all
typescript AST in the transformer typescript plugin.
---------
Co-authored-by: Boshen <boshenc@gmail.com>
### Correctness
- named
- export
- default
- namespace
- no-unresolved
The above rules will prevent bugs or broken, so I changed these to
correctness
### Suspicious
- no-duplicates
- no-named-as-default-member
- no-named-as-default
I'm still not sure whether the `no-unresolved` rule should be changed to
`correctness`. Because I found that the rule reported a lot in the
AFFiNE. But in fact, they are all correct reports
---------
Co-authored-by: Boshen <boshenc@gmail.com>
The remaining test cases will perform better with a scope
implementation, and while we can implement them without the scope, it
still requires us to do what the scope did.
---------
Co-authored-by: Boshen <boshenc@gmail.com>
`codegen` relies on the modifier to decide whether to print or not. For
example, if declare is present, nothing will be printed if typescript is
not enabled.
We may need to solve this problem in a way that doesn't rely on
modifers.