## Summary
- Add `pub node_id: NodeId` as the first field in all AST structs (~190 structs across js.rs, ts.rs, jsx.rs, literal.rs)
- Update codegen to special-case `node_id`:
- `Dummy`: Use `NodeId::DUMMY` instead of `Dummy::dummy(allocator)`
- `CloneIn`: Use `NodeId::DUMMY` for regular clone, preserve ID for semantic clone
- `ContentEq`: Skip `node_id` from equality comparisons
- `ESTree`: Skip `node_id` from serialization
- `AstBuilder`: Exclude `node_id` from builder parameters, use `NodeId::DUMMY`
- Update pattern matching in minifier and transformer to use `..` for new field
- Update traverse script to include `NodeId` import
This is the foundational step for assigning unique IDs to AST nodes. The `node_id` defaults to `NodeId::DUMMY` (value 0) for uninitialized nodes.
## Test plan
- [x] `cargo build` succeeds
- [x] `cargo test` passes
- [x] `just ast` regenerates code correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add `decorators` field to `FormalParameterRest` to support decorators on rest parameters in TypeScript
- Parse decorators before checking for rest parameters in formal parameter lists
- Update semantic builder to visit decorators on rest parameters
Fixes parsing of `typescript/tests/cases/compiler/sourceMapValidationDecorators.ts`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Previously we had to store source text at start of buffers sent to JS via raw transfer.
#18376 made changes to how raw transfer deserializer handles strings, in order to support files containing a BOM. Building on that, we're now able to remove the requirement that source text be at start of the buffer entirely.
This PR changes the deserializer used in Oxlint JS plugins to accept source text being anywhere in the buffer, *as long as no other strings are after it*. In practice this just means that the source text must be allocated before anything else, which is easy to satisfy.
Now the source text can be allocated with just the usual safe `allocator.alloc_str(source_text)` method.
This change removes a ton of dodgy workarounds and unsafe code we used previously to get source text at the start of buffer. It makes the code less labyrinthine and far less likely a slip up can inadvertently introduce UB.
Note: In `napi/parser`, source text still *is* at start of the buffer, as that's simpler and more efficient when the source text is written into the buffer on JS side. This change only affects Oxlint.
## Summary
- Removes `ThisExpression` variant from `TSModuleReference` since `import x = this` is invalid TypeScript (error TS1359)
- Adds parser error when `this` is used in import equals declaration
- Updates codegen, transformer, and linter rules to handle the new enum structure
Closes#16841🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add detailed JSDoc documentation to `parse` and `parseSync` functions explaining their performance characteristics
- Clarify that `parseSync` is generally preferable since AST deserialization happens on the main thread anyway
- Recommend using worker threads with `parseSync` for parallelizing multiple files
Closes#15361🤖 Generated with [Claude Code](https://claude.ai/code)
Pure refactor.
Remove a few remaining instances of the pattern where we destructure global objects at top level e.g. `const { isArray } = Array;`.
In Oxlint, TSDown plugin already performs this optimization. In `napi/parser`, we intend to build the package with TSDown, and we'll apply the same optimization in build process then too.
Closes#12526.
Handle BOM on start of files in the same way that ESLint does - do not include it in the source text on JS side, but `context.sourceCode.hasBOM` evaluates to `true`.
Method:
* Alter `program.source_text` to trim off the BOM before passing AST to JS side.
* Add a `has_bom` flag to `RawTransferMetadata`.
* Add ability to add an offset in the conversion from UTF-8 to UTF-16 spans.
The result is that the file as it's seen on JS side is as if the BOM didn't exist (except for the `hasBOM` flag). Spans are converted accordingly in JS-side AST, and converted back when passing diagnostics back to Rust.
Previous raw transfer required that the source text start exactly at the start of the buffer. In linter, relax this restriction and handle when source text is stored elsewhere in the buffer.
This is necessary for stripping BOM from source (#18376), as then the source text used on JS side starts 3 bytes after the start of the buffer.
## Summary
- Use inline assignment pattern (`let x; { x: x = value }`) for `start` and `end` fields instead of declaring them in the preamble
- This allows minifiers to eliminate unused variables when RANGE is false
- Restores original file size for deserializers that don't use range fields
**Before (js.js with RANGE=false):**
```javascript
function deserializeIdentifierName(pos) {
let start = deserializeU32(pos),
end = deserializeU32(pos + 4);
return { type: "Identifier", name: deserializeStr(pos + 8), start, end };
}
```
**After (js.js with RANGE=false):**
```javascript
function deserializeIdentifierName(pos) {
return { type: "Identifier", name: deserializeStr(pos + 8), start: deserializeU32(pos), end: deserializeU32(pos + 4) };
}
```
Closes#14332
## Test plan
- [x] `just ast` regenerates deserializers correctly
- [x] `pnpm test --filter='@oxc-project/napi-parser'` passes (109 tests)
- [x] `cargo clippy -p oxc_ast_tools` passes
🤖 Generated with [Claude Code](https://claude.ai/code)
### 🐛 Bug Fixes
- f1e2dc0 semantic: No error in `check_function_redeclaration` for
CommonJS files (#18231) (overlookmotel)
- 645c3f0 transformer: Use `require` not `import` in CommonJS files
(#18226) (overlookmotel)
- ee9f6a4 mangler: Use `retain` instead of `truncate` to remove empty
frequency slots (#18225) (Dunqing)
### ⚡ Performance
- 52073d9 semantic: Use cheaper test for source type (#18235)
(overlookmotel)
Co-authored-by: overlookmotel <557937+overlookmotel@users.noreply.github.com>
#18089 added support in parser for `commonjs` source type.
Extend this support to the NAPI packages - accept `sourceType: 'commonjs'` in options for `oxc-parser` and `oxc-transform`.
## Summary
- Replace `type_parameter: Box<'a, TSTypeParameter<'a>>` with two dedicated fields:
- `key: BindingIdentifier<'a>` - the parameter name (e.g., `P` in `[P in keyof T]`)
- `constraint: TSType<'a>` - the constraint type (e.g., `keyof T`)
- Improves type safety by preventing invalid AST states - `TSTypeParameter` contains `const`, `in`, `out`, and `default` fields that are syntactically invalid in mapped types
- Matches TSESTree structure more closely
Closes#15101
## Test plan
- [x] All parser, codegen, semantic, and linter tests pass
- [x] Conformance tests show no regressions
- [x] Semantic snapshots updated to reflect the new structure
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add `ModuleKind::CommonJS` variant to support CommonJS module type in the parser
- Enable top-level `return` statements (CommonJS files are wrapped in a function)
- Enable top-level `new.target` (allowed in CommonJS function wrapper)
- Add proper errors for `import`/`export` statements in CommonJS
- Add proper errors for `import.meta` in CommonJS
The CommonJS source type can be set via:
- `SourceType::with_commonjs(true)` method
- Babel's `sourceType: "commonjs"` option
Closes#16200
## Test plan
- [x] Added test cases in `tasks/coverage/misc/pass/` for valid CommonJS features
- [x] Added test cases in `tasks/coverage/misc/fail/` for invalid CommonJS usage
- [x] Enabled Babel conformance tests for `sourceType: "commonjs"`
- [x] All existing tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Move `scope_id: Cell<Option<ScopeId>>` from `TSEnumDeclaration` to `TSEnumBody`
- Since `TSEnumBody` was introduced as a dedicated type in PR #10284, the scope logically belongs on the body rather than on the parent declaration node
Closes#10331
## Test plan
- [x] Unit tests pass (`just test`)
- [x] Conformance tests pass (`just conformance`)
- [x] Updated semantic test snapshots reflect scope now on `TSEnumBody`
🤖 Generated with [Claude Code](https://claude.ai/code)
When parsing tokens for JS plugins, the TypeScript parser was always using `ScriptKind.TSX`, regardless of file extension. This caused TypeScript to incorrectly parse generic arrow functions like `<T>() => {}` in `.ts` files, creating bogus `JsxText` tokens that overlap with comments.
This PR adds an `is_jsx` flag to `RawTransferMetadata`. Rust side sets the flag depending on source type, and JS side uses that information to pass the correct `ScriptKind` for the file to TypeScript parser.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
### 🚀 Features
- 10426af codegen: Print soft space between inline block comments on the
same line (#17799) (camc314)
- 2261e6e semantic: Improve error message to add `#` for private
identifiers (#17779) (Dunqing)
### 🐛 Bug Fixes
- 7422b7e parser/trivia: Correctly mark whether a block comment is on a
newline (#17754) (camc314)
- c32e8d5 codegen: Wrap `TSAsExpression` in parens when used with
in/instanceof operators (#17752) (camc314)
- 5755b2d semantic: Report duplicate private identifier for static and
instance elements (#17591) (camc314)
- 0600df3 isolated_declarations: Only print jsdoc comments (#17748)
(camc314)
- ef7e014 parser: Preserve `@__NO_SIDE_EFFECTS__` annotation with
parenthesized expressions (#17711) (camc314)
- 59a6228 parser: Detect TS1363 error for type-only imports with mixed
default and named/namespace bindings (#17712) (Copilot)
### ⚡ Performance
- 864f1fa semantic: Mark duplicate class element error reporting as cold
(#17746) (camc314)
- 3a452b8 semantic: Use smallvec for storing reference IDs (#17731)
(camchenry)
- d5979dc minifier: Do not allocate when checking to convert `const` to
`let` (#17730) (camchenry)
- 3f4429c parser: Do not re-allocate TS interface heritage (#17692)
(camchenry)
### 📚 Documentation
- 120a27c minifier: Add prettier-ignore for js-in-md part (#17687)
(leaysgur)
Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
fixes#11489
Note, in quite a few places i changed the span we report on to look something like this, the only reason for this is to reduce the diff in some of the snapshots
```
decl.type_annotation.as_ref().map_or_else(
|| decl.id.span(),
|type_annotation| decl.id.span().merge(type_annotation.span()),
),
```
By hoisting `let enter;` up out of the the `if` block, minifier is able to convert the `if` statement into a `&&` expression, resulting in tighter minified output.
This optimization is only visible in the bundled output, as that's where we do more aggressive minification.
Pure refactor. Generate the JS files containing `NODE_TYPE_IDS_MAP` as variants of a single source code file, instead of constructing to two different versions for parser and linter manually.
This makes changes in further PRs to come easier to implement.
Previously `u128` was not supported in `assert_layouts` generator, because Rust changed the alignment of `u128` from 8 to 16 - and therefore the alignment depended on Rust version.
That change happened some time ago now, and all Rust versions above our MSRV have `u128` with alignment of 16. So we can now support `u128` (and also `i128`, `NonZeroU128`, `NonZeroI128`).
Also support `u128` in `raw_transfer` and `raw_transfer_lazy` generators.
This will help with making lexer tokens serializable (#17025), since `Token` contains a `u128`.
### 💥 BREAKING CHANGES
- 083fea9 napi/parser: [**BREAKING**] Represent empty optional fields on
JS side as `null` (#16411) (overlookmotel)
### 🚀 Features
- 7a2afee parser: Add TS1174 error for classes extending multiple base
classes (#15993) (sapphi-red)
- da87812 semantic: Add TS2309 error for export assignment with other
exports (#15992) (sapphi-red)
- d6d2bcd minifier: Remove unused function calls that are marked by
`manual_pure_functions` (#16534) (sapphi-red)
- c90f053 minifier: Support `.` separated values for
`compress.treeshake.manualPureFunctions` (#16529) (sapphi-red)
- a607cc4 codegen: Preserve comments between CatchClause's param and
body (#16167) (copilot-swe-agent)
- 8c10694 semantic: Expose get_comment_at method (#16439) (camc314)
- 3981e7a ast: Add get_comment_at to lookup a comment by span (#16438)
(camc314)
### 🐛 Bug Fixes
- 699406a napi/parser: Move `ExportEntry::module_request` field to first
(#16412) (overlookmotel)
- 12bd794 napi/parser: Move `ExportEntry::module_request` field to last
(#16403) (overlookmotel)
### ⚡ Performance
- 790beeb napi/parser: Do not remove extraneous options on JS side
(#16447) (overlookmotel)
Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Enable `prefer-destructuring` rule in our linting setup.
Not related to JS plugins, I just like this rule, and it's useful not to make nitty comments on PRs, when linter can do it for me...
Similar to #16442.
There's no need to remove the `experimental*` properties from `options` object on JS side. They'll be ignored by NAPI-RS on Rust side anyway, so creating a new object on JS side is pointless overhead.
Follow-on after #16383 and #16403.
While working on those I discovered an option NAPI-RS has to represent `Option::None` as `null`, instead of omitting the field entirely.
Use that option on the structs used for transferring errors and module record over to JS.
I think this is likely more performant because:
1. NAPI-RS can create all properties of the objects using only the faster `node_api_create_object_with_properties` API.
2. It produces consistent object shapes, so JS engine can better optimize code using these objects.
It also brings the shape of the data perfectly into line between standard transfer and raw transfer, and is consistent with how empty fields in the AST are represented as `null`.
I would have used this option before if I'd known it existed.
### Breaking change
I've marked this as a breaking change because code consuming these objects would now need to check for empty fields with `value === null` instead of `value === undefined`.
However in practice, most people probably use `!value` or `value ?? ...`, so it's unlikely to affect many users. This only affects module record and errors anyway, not the AST itself, as we transfer that as JSON, not via NAPI.
NAPI-RS 3.6.0 makes a change to the ordering of objects. It now puts optional fields last.
This made tests in `napi/parser` for module record fail when we tried to update (#16383) because the `module_request` field of `ExportEntry` moves to last which breaks the snapshots.
The change in NAPI-RS is unlikely to be reverted, because it's a sizeable perf optimization: https://github.com/napi-rs/napi-rs/pull/2990
To work around this problem:
1. Move the field in the `ExportEntry` intermediate struct in `napi/parser` to last, so NAPI's output matches what you'd expect from the struct definition.
2. Alter the `#[estree]` attr on `ExportEntry` struct in `oxc_syntax` crate to match.
Note: The `ExportEntry` struct in `napi/parser` is just an intermediate structure used for serialization. So I think it's fine to fiddle with its field order. The actual `ExportEntry` struct used in the module record is in `oxc_syntax` crate, and it remains unaltered.