## Summary
- replace Node.js `Buffer` storage with `Uint8Array` in the source-map encoder
- use `TextDecoder` to create the final mappings string
- use `Uint8Array#set` when growing the mappings buffer
- remove the `node:buffer` runtime dependency
This makes source-map generation runtime-neutral and removes a blocker for browser support for the codegen package.
## Performance
Indexed access, copying, and ASCII decoding have comparable performance. `Uint8Array` allocation requires zero-initialization, unlike `Buffer.allocUnsafe`, but the expected end-to-end impact is small.
## Summary
Replace the `Buffer`-backed base64 character-code table with a `Uint8Array`.
`BASE64_CODES` is only used as a lookup table mapping VLQ digits from `0–63` to ASCII byte values. It does not require any `Buffer`-specific APIs, and `Uint8Array` provides the same indexed byte semantics in both Node.js and browsers.
This reduces unnecessary Node-specific usage and is a small step toward making `oxc-codegen` browser-compatible. The hot encoding loop remains unchanged; only the one-time lookup-table initialization differs.
This does not remove the remaining `Buffer` dependency used for the source-map output buffer.
## Summary
Preserve source maps when `strip-ts` removes TypeScript-only printer regions from JS builds.
The plugin now uses Rolldown’s `magicString` transform context instead of a plain string replacement, so removed `/* IF TS */` regions retain correct mappings back to the original printer source.
Follow-on after #25585. Small optimization.
Sourcemaps can't be generated unless `options.sourceText` is provided. This is already checked at the start. So everywhere else can then rely on `state.sourceText != null`, rather than re-checking it on each write.
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.
Why do many code when few do trick?
Follow-up to #25901. Hand-rolled import checks can simply be replaced
with the shared `is_import_symbol` helper, this also helps improve the
accuracy of the code to catch aliased imports for one of them.
### Bug fixed
`is_vue_define_component_reference` (used by
`vue/no-deprecated-delete-set` to decide whether a `this.$set` /
`this.$delete` call sits inside a Vue component) gated on the **local**
name being `defineComponent` before walking up to the
`ImportDeclaration`, so an aliased import was never recognised:
```vue
<script>
import { defineComponent as dc } from 'vue'
dc({
mounted () {
this.$set(obj, key, value)
this.$delete(obj, key)
}
})
</script>
```
Neither call was reported before; both are now. Covered by a new test
case.
### Pure simplifications
- `is_imported_set_or_del_from_vue` (`no-deprecated-delete-set`) — 20
lines → two `is_import_symbol` calls.
- `is_vue_next_tick_import` (`utils/vue.rs`, used by
`vue/next-tick-style` and `vue/valid-next-tick`) — 18 lines → one call.
Both already matched by symbol id, so behavior is unchanged.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.
`no-react-children` decided whether an identifier came from `react` with
a hand-rolled helper that matched by **name**:
```rust
fn is_imported_from_react(local_name: &str, ctx: &LintContext) -> bool {
ctx.module_record().import_entries.iter().any(|entry| {
entry.module_request.name() == "react" && entry.local_name.name() == local_name
})
}
```
This replaces it with the existing shared helpers, `is_import_symbol`
and `is_import_from_module`.
### Bugs fixed
Four false positives, where a local binding shadowed the import:
```js
import { Children } from 'react';
function f(x) { const Children = { count: () => 0 }; return Children.count(x); }
import React from 'react';
function f(x) { const React = { Children: { map: () => {} } }; return React.Children.map(x); }
import { Children } from 'react';
export function f(Children) { return Children.count(1); }
import { count as Children } from 'react'; Children.toArray(x)
```
One false negative, an aliased import the local-name check couldn't see:
```js
import { Children as C } from 'react'; C.toArray(x)
```
All five are covered by new test cases.
### Performance
This has a minor downside in perf, but it's significantly less than a
millisecond difference and is probably worth the downside. We had
originally decided not to care about aliases when implementing this
rule, but this change allows us to reuse the existing util code for
checking where the import comes from, and fixes the aforementioned edge
cases, so I think this is a worthwhile change.
Measured with two `--features allocator` builds, paired and interleaved,
using `--debug=timings --threads=1` to isolate the rule's own time from
parse/semantic noise.
| corpus | base | new | change |
| --- | --- | --- | --- |
| sentry | 4.416ms | 4.589ms | +3.9% slower |
| material-ui | 3.111ms | 3.217ms | +3.4% slower |
| bluesky | 0.503ms | 0.540ms | +7.2% slower |
| actualbudget | 2.012ms | 2.105ms | +4.6% slower |
| synthetic, `Children` used heavily | 19.112ms | 15.443ms | 19.2%
faster |
There is a small, consistent regression on real code, and it's
deliberate. Dropping the `ident.name == "Children"` gate means every
`obj.method()` call in a file importing `react` now does two array
lookups instead of one string compare; removing the `O(imports)` scan
only pays off where `Children` is actually used, which is 27 files out
of 8.8K in sentry. The absolute cost is ≤0.17ms, and `no-react-children`
isn't a default rule anyway.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed
by me.
Ten call sites across nine rules checked whether an identifier is a
specific global with:
```rust
ctx.scoping().root_unresolved_references().contains_key(&ident.name)
```
That asks whether the **name** is unresolved **somewhere in the file**,
not whether **this reference** is global. Any file that shadows the
global in one scope and uses the real global in another gets a false
positive on the shadowed use:
```js
function f(myLogger) {
const console = myLogger;
console.log(1); // reported by `no-console` — this is the bug
}
console.log(2); // the real global, elsewhere in the file
```
`ctx.is_reference_to_global_variable(ident)` already exists for this and
is used at ~50 other call sites. It is `reference.symbol_id().is_none()`
— one field read instead of hashing the name into the
unresolved-references map — and it honours the user's `globals: { X:
"off" }` config.
### Rules fixed
| rule | shadowed code that was wrongly reported |
| --- | --- |
| `eslint/no-console` | `const console = myLogger; console.log(foo)` |
| `eslint/no-constant-binary-expression` | `const String = MyString;
String(x) ?? y` |
| `eslint/no-new-native-nonconstructor` | `const Symbol = FakeSymbol;
new Symbol()` |
| `eslint/prefer-object-spread` | `const Object = MyObject;
Object.assign({}, a)` |
| `eslint/symbol-description` | `const Symbol = FakeSymbol; Symbol()` |
| `eslint/valid-typeof` | `const undefined = u; typeof x === undefined`
|
| `promise/avoid-new` | `const Promise = FakePromise; new Promise(fn)` |
| `unicorn/new-for-builtins` | `const String = MyString; new
String('x')` |
| `unicorn/prefer-global-this` | `const window = fake; window.foo` |
Each rule gets a pass test in that shape. Both halves matter — without a
second, genuine use of the global the name lookup already returns false,
so a single-scope test would pass before the fix too.
Three of these ship a fix or suggestion (`prefer-global-this`,
`valid-typeof`, `no-console`), so the false positive was rewriting or
deleting code the user owns.
### Real-world impact
Base vs. branch over four corpora (vscode, sentry, material-ui, actual),
each rule isolated with `-A all -A nursery -D <rule> --format=json
--no-ignore --disable-nested-config`, diagnostics normalised to `(file,
span, code, message)`:
**0 added, 23 removed.** Every removal is `prefer-global-this` on a
genuinely shadowed `window`:
- `vscode/src/vs/base/browser/dompurify/dompurify.js` (16) — `let window
= arguments.length > 0 ? arguments[0] : getGlobal()`
- `material-ui/packages-internal/core-docs/src/Demo/DemoSandbox.tsx` (5)
— `function IsolatedDemo({ children, cssVarPrefix, colorSchemeNode,
window })`, a destructured prop
- `sentry/src/sentry/templates/sentry/error-page-embed.js` (2) —
`(function (window, document, JSON) { ... })(window, document, JSON)`,
the classic IIFE parameter
In each file the real global references outside the shadowing scope are
still reported.
### Notes
- `no-console` uses `ident.is_global_reference(ctx.scoping())` rather
than the context helper. An existing fail case pins that `globals: {
console: "off" }` must still report, and the context helper treats
`"off"` as "not a global". The other eight sites use the context helper,
so they now respect that config, but this one doesn't.
- Five upstream ESLint pass cases that were commented out in
`no-constant-binary-expression` now pass and are re-enabled (`function
Boolean(n) { return n; }; Boolean(x) ?? foo` and friends). Two others in
that list stay commented — they are `function foo(undefined) { ... }`,
which is a separate problem: the rule matches `undefined` by name via
`Expression::is_undefined()`. Left a TODO pointing at it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Signed-off-by: Cameron <cameron.clark@hey.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cameron <cameron.clark@hey.com>
#25292 made isolated declaration emit report a false TS9025 for a defaulted parameter whose annotation is an indexed access type. This breaks Vite's `UserConfig["base"]` case when Rolldown upgrades Oxc, even though the indexed property already includes `undefined`.
Oxc's isolated declaration emitter has no type checker, so it cannot safely resolve indexed accesses, type references, conditional types, or similar annotations. This change uses a syntax-only classification: widen or diagnose types whose syntax is conclusive, and preserve checker-dependent types rather than emit an unstable error. The union/intersection rules retain the distinct behavior of `any`, `never`, `unknown`, and `void`, and an explicit `undefined` union member is never duplicated.
Regression coverage includes the Rolldown/Vite case and a compact matrix of the relevant type algebra. The implementation and tests were reduced to one commit after review.
Validated with the isolated-declarations tests, strict Clippy, the all-features workspace suite, lint, docs, AST generation, and TypeScript 5.6.1-rc/6.0.3 type-algebra checks. The full local `just ready` run stopped only at a runner-dependent source-map snapshot because the repository expects Node 26.5.0 and this machine has 25.8.1; the remaining suite passed with that and the known terminal-color snapshot group skipped.
Follow-up to #25292. Related Rolldown failure: https://github.com/rolldown/rolldown/actions/runs/32158377691/job/95782148042?pr=10707
Disclosure: this change was prepared and verified with AI assistance (Codex).
add `boolean_context` to `minimize_not` so negation folding can happen
in-place when possible, reducing extra AST rebuilds and allocations and
`!0` / `!1` can now collapse directly to `1` / `0` in boolean context
The 22 React Compiler rules split out by #25500, plus `eslint/no-unreachable-loop`, all first shipped in oxlint 1.79.0 but were still marked `version = "next"`. I think the release PR must've failed to run this part of the release task?
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The shared React Compiler rule macro generated `react.dev` links for internal categories without public lint pages. Render those attributions without links and verify linked versus unlinked rule documentation.
Validated with `just ready`.
AI-assisted.
Follow-on after #25585. Small optimization.
Simplify `growMappingBuffer` by relying on existing invariants. Buffer always has at least 64 bytes capacity, and no caller needs to reserve more than 64 bytes. So doubling capacity is always sufficient.
Add a debug assert to ensure this invariant continues to hold.
Follow-on after #25585. Small optimization.
`location` passed to `recordSourceMapping` was either `false` or a small number. Swap `false` for `0`, so its always passed a number (SMI), removing the need for type-checks.
Follow-on after #25585. Small optimization.
`name` can only be `!== undefined` when `location === false`. So move that logic into that block, removing a branch from the fast path.
Follow-on after #25585. Small optimization.
No point setting `last` here, since in both cases its immediately overwritten before anything else can read it.
## Summary
Add direct public-API tests for `printSync(statement)`:
- JavaScript/TypeScript statement output
- source-map metadata for a JavaScript statement
Follow-on after #25585. Small optimization.
It's redundant to call `markWithMapNoName` with `decorators[0]` as `printDecorators` marks the same mapping immediately after.
Add benchmarks for source maps in `oxc-codegen`.
2 benchmarks:
1. Full end-to-end.
2. Without the actual sourcemap generation at the end.
The 2nd is to measure the overhead that pushing to the sourcemap arrays adds while printing in isolation.
## Summary
- replace manual test-registration loops with Vitest parameterized APIs
- preserve per-fixture skip behavior with `.for` and parameterized suite options
- retain imperative loops that exercise fixture contents rather than register tests
## Summary
- skip React Compiler transform and lint analysis for `node_modules` by default
- let explicit transform `sources` allowlists opt dependencies back in
- document the behavior and add regression coverage
## Validation
- focused Oxlint and transform-react checks
- Clippy with warnings denied
- `just ready` reached the all-features suite; three Oxlint ANSI-color snapshots differed in this environment
AI assistance: Codex was used to implement and verify this change.
## Summary
- automatically apply A-codegen to changes under packages/codegen
- keep the Rust and TypeScript code generators in the same functional area
## Naming
Reuse A-codegen because recent packages/codegen PRs already receive it manually. A-printer is stale, while names such as A-codegen-js or P-codegen would introduce a distinction or label prefix the repository does not currently use.
Follow-on after [#25585](/github/pr-or-issue/oxc-project/oxc/25585).
Rename `options.sourceFileName` to `sourceFilename`. We use both capitalizations internal Rust code, but always `filename` in external JS APIs (Oxlint plugins etc).
NOTE: this is not marked as a breaking change as it was merged before the feature was released.
Follow-on after #25585.
Nit. Make `printSync` return an object with consistent shape. With `sourcemap: false` it now returns `{ code: string, map: null }` instead of just `{ code: string }`.
Co-authored-by: Cameron <cameron.clark@hey.com>