mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
codex/comment-attachments
387 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
853ffabf34 |
fix(ecmascript): avoid charAt panic on 32-bit (#26244)
## Summary - Return `OutOfRange` when a `StringCharAt` position cannot fit in `usize`, avoiding a panic on 32-bit targets. - Cover the `u32` boundary in direct evaluation and minifier constant folding, alongside existing conversion and UTF-16 edge cases. |
||
|
|
894c8f9cd8 |
release(crates): oxc v0.148.0 (#26198)
### 💥 BREAKING CHANGES - |
||
|
|
b874f480c9 |
fix(ecmascript): Math.round only exact half ties (#26150)
[ECMA-262 §21.3.2.29, `Math.round`](https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.round). - If `0 < n < 0.5`, return `+0`. - If `-0.5 ≤ n < -0`, return `-0`. - Otherwise choose the closest integer, preferring `+∞` only for an actual tie. |
||
|
|
a625378066 |
fix(minifier): coerce omitted indexOf search argument (#26149)
## Summary - Apply `ToString(undefined)` when `String.prototype.indexOf` or `lastIndexOf` is called without a search argument. - Keep folding strings without the resulting `"undefined"` substring to `-1`. - Add focused helper and minifier regression coverage. ```js "undefined".indexOf(); // 0 "undefined".lastIndexOf(); // 0 "abcdef".indexOf(); // -1 "abcdef".lastIndexOf(); // -1 ``` |
||
|
|
dc7398beae |
fix(ecmascript): trim trailing whitespace in string to number (#26148)
## Summary `StringToNumber` previously removed ECMAScript whitespace only from the beginning of a string. Legal trailing whitespace therefore remained in the value passed to Rust’s numeric parsers, causing valid JavaScript numeric strings to produce `NaN`. For example: ```js "1 " * 2 // incorrectly folded to NaN "0x10 " - 0 // incorrectly folded to NaN ``` This changes `trim_start_matches` to `trim_matches`, applying the existing ECMAScript whitespace predicate to both ends of the string. After the change: ```js "1 " * 2 // folds to 2 "0x10 " - 0 // folds to 16 ``` The custom predicate remains important because ECMAScript and Rust define whitespace differently. Tests cover every ECMAScript whitespace character and verify that non-ECMAScript whitespace such as U+0085 is not accepted. |
||
|
|
4e258430cd |
release(crates): oxc v0.147.0 (#26046)
### 🚀 Features - |
||
|
|
0389c4010e |
release(crates): oxc v0.146.0 (#25912)
### 🚀 Features - |
||
|
|
4a4a1fb2e7 |
release(crates): oxc v0.145.0 (#25865)
### 💥 BREAKING CHANGES - |
||
|
|
568203e24f |
perf(ecmascript): use binary search for known globals (#25817)
Replaces the 639-arm known-global string match with a sorted static table and binary search. The table is guarded for sortedness, uniqueness, and representative lookups. Release `oxlint`: 12,491,264 → 12,474,736 bytes (−16,528 bytes, −0.13%); `__text` is reduced by 29,252 bytes. Prepared with AI assistance. |
||
|
|
f6000ac141 |
fix(ecmascript): fold ** with Number::exponentiate, not IEEE pow (#25644)
`f64::powf` implements IEEE 754 `pow`, which returns `1` for a base of `1` whatever the exponent. [`Number::exponentiate`](https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate) instead returns `NaN` when the exponent is `NaN` (step 1), and when the base has magnitude `1` and the exponent is infinite (steps 9-10). Constant folding called `powf` directly, so: ```js 1 ** Infinity // spec: NaN oxc folded to: 1 (-1) ** Infinity // spec: NaN oxc folded to: 1 1 ** -Infinity // spec: NaN oxc folded to: 1 1 ** NaN // spec: NaN oxc folded to: 1 ``` `1` is truthy and `NaN` is not, so an `if` guarded by such an expression takes the wrong branch. The one case the two agree on — an exponent of `0` returning `1` whatever the base, including `NaN ** 0` and `Infinity ** 0` — is unchanged, since `powf` already gets it right and the spec checks the exponent before the base. `cargo minsize` shows no snapshot change. Found by the minifier fuzzer (#25594). --- Written with AI assistance (Claude Code). Reviewed by me, and every expected value in the added test was checked against Node.js. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e82495ba54 |
fix(ecmascript): derive ToNumber of !x from ToBoolean (#25595)
Found by the semantic fuzzer added in #25594 (seed `15979`). ## What `ToNumber(!x)` was computed from `ToNumber(x)` instead of `ToBoolean(x)`: ```rust Expression::UnaryExpression(unary) if unary.operator.is_not() => { let number = unary.argument.to_number(ctx)?; Some(if number == 0.0 { 1.0 } else { 0.0 }) } ``` `!x` is `ToBoolean(x)` negated, so the two predicates must agree — and they don't. `NaN` is falsy but does not number to `0`, while `"0"`, `" "`, `"0.0"` and `[]` all number to `0` yet are truthy. ## Why it was not caught `minimize_conditional_expression` rewrites `a ? 1 : 0` into `+!!a`. That rewrite is correct, but when a parent expression evaluated the freshly built `+!!a` in the same pass, the broken branch flipped the result: ```js (NaN ? 1 : 0) || 2 // 2, minified to 0 ('0' ? 1 : 0) || 2 // 1, minified to 2 ([] ? 1 : 0) || 2 // 1, minified to 2 ``` A `!!x` written in the source is folded correctly by `fold_unary_expr` before any parent observes it, so only pass-generated nodes were affected. That is why existing tests and the conformance suites pass. refs https://github.com/oxc-project/oxc/pull/8267 ## How Derive the value from `to_boolean`. This is strictly more conservative: when `to_boolean` returns `None` the fold is skipped rather than answered wrongly. ## Testing - New regression test `test_conditional_to_unary_plus_uses_to_boolean` covering `NaN`, `"0"`, `" "`, `"0.0"` and `[]`. - `cargo test -p oxc_minifier -p oxc_ecmascript`, `cargo clippy --all-targets`, `cargo fmt --check` all pass. - `just minsize`: no snapshot change, so no size regression. ## AI disclosure This change was produced with AI assistance (Claude Code). I have reviewed and understand every line; the root cause and each minimal reproduction were verified by hand against Node.js. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
88b34f2db2 |
fix(clippy): remove unneeded unsafe (#25551)
ah lint only runs on linux so this didn't fail previously in CI |
||
|
|
48d5e9e47a |
refactor(ecmascript): use __jcvt() intrinsic on ARM64 for f64→i32 conversion (#24373)
- Simplify `cfg` by using `cfg_select` macro which is stabilized in 1.95 - Use `__jcvt()` intrinsic which is stabilized in 1.95 - Remove redundant `NAN` check since V8 has a set of test for it, which claims that it works properly for `NAN` and `Infinity`: https://github.com/v8/v8/blob/3b58a432df85d51bf1c439c30efcf0752de1076f/test/cctest/test-assembler-arm64.cc#L11416C1-L11468C48 Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
b6f1458dec |
release(crates): oxc v0.144.0 (#25474)
### 💥 BREAKING CHANGES - |
||
|
|
a33788e13e |
feat(ast)!: group class heritage into ClassHeritage (#25360)
## Summary
- Groups `Class::super_class` and `Class::super_type_arguments` into `Class::heritage`.
- Introduces `ClassHeritage` containing the superclass expression and its optional TypeScript type arguments.
- Makes superclass type arguments without a superclass expression unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub super_class: Option<Expression<'a>>,
#[ts]
pub super_type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
```
to:
```rust
pub struct Class<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub r#type: ClassType,
pub decorators: Vec<'a, Decorator<'a>>,
pub id: Option<BindingIdentifier<'a>>,
#[ts]
pub type_parameters: Option<Box<'a, TSTypeParameterDeclaration<'a>>>,
pub heritage: Option<ClassHeritage<'a>>,
#[ts]
pub implements: Vec<'a, TSClassImplements<'a>>,
pub body: Box<'a, ClassBody<'a>>,
#[ts]
pub r#abstract: bool,
#[ts]
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub struct ClassHeritage<'a> {
pub expression: Expression<'a>,
#[ts]
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Why?
ECMAScript defines the complete `extends` clause as `ClassHeritage`:
```text
ClassHeritage : extends LeftHandSideExpression
```
TypeScript additionally permits type arguments on the heritage expression:
```ts
class Derived extends Base<T> {}
```
Previously, the AST stored the expression and type arguments independently:
```text
Class {
super_class: Some(Base),
super_type_arguments: Some(<T>),
...
}
```
This allowed an invalid state to be constructed:
```text
Class {
super_class: None,
super_type_arguments: Some(<T>),
...
}
```
Superclass type arguments cannot exist without an `extends` expression.
The parser already treated these fields as a pair. It parsed a class heritage entry and then split its expression and type arguments into two independent fields. Builders and transforms therefore had to preserve their relationship manually.
Grouping them into `ClassHeritage` makes the syntactic dependency explicit and prevents consumers from observing or constructing contradictory class heritage.
## Class heritage forms
A class without an `extends` clause has no heritage:
```js
class Base {}
```
```text
Class {
heritage: None,
...
}
```
A class extending an expression has heritage without type arguments:
```js
class Derived extends Base {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: None,
}),
...
}
```
TypeScript type arguments belong to the same heritage structure:
```ts
class Derived extends Base<T> {}
```
```text
Class {
heritage: Some(ClassHeritage {
expression: Base,
type_arguments: Some(<T>),
}),
...
}
```
The heritage expression may be any valid class heritage expression:
```js
class Derived extends mixin(Base) {}
class NullDerived extends null {}
```
## How to migrate
Code that previously accessed the superclass expression directly:
```rust
if let Some(super_class) = &class.super_class {
// ...
}
```
can use the compatibility accessor:
```rust
if let Some(super_class) = class.super_class() {
// ...
}
```
Code that needs both parts should access the grouped heritage:
```rust
if let Some(heritage) = &class.heritage {
let expression = &heritage.expression;
let type_arguments = heritage.type_arguments.as_deref();
}
```
Mutating consumers should update the nested fields:
```rust
if let Some(heritage) = &mut class.heritage {
heritage.type_arguments = None;
}
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `class.super_class` | `class.heritage.as_ref().map(\|h\| &h.expression)` or `class.super_class()` |
| `class.super_type_arguments` | `class.heritage.as_ref()?.type_arguments` or `class.super_type_arguments()` |
| `class.super_class.is_some()` | `class.heritage.is_some()` |
| Mutate `class.super_class` | Mutate `class.heritage.expression` |
| Clear `class.super_type_arguments` | Clear `class.heritage.type_arguments` |
| Pass superclass and type arguments separately to a `Class` builder | Construct and pass `ClassHeritage` |
| Clone both fields independently | Clone `class.heritage` |
```ts
superClass: Expression | null;
superTypeArguments?: TSTypeParameterInstantiation | null;
```
No `heritage` property or `ClassHeritage` node is added to the ESTree output.
fixes https://github.com/oxc-project/backlog/issues/216
|
||
|
|
44fd320324 |
feat(ast)!: split TS external modules & Namespace Declarations (#25284)
## Summary
- Splits `TSModuleDeclaration` into two dedicated declaration nodes:
- `TSExternalModuleDeclaration` for string-literal modules and module augmentations.
- `TSNamespaceDeclaration` for identifier-based `module` and `namespace` declarations.
- Removes `TSModuleDeclaration`, `TSModuleDeclarationName`, `TSModuleDeclarationBody`, and `TSModuleDeclarationKind`.
- Makes invalid combinations of names, bodies, and declaration kinds unrepresentable.
- Ensures only namespace declarations introduce a normal binding.
- Updates all AST consumers, generators, traversal, formatting, code generation, semantic analysis, lint rules, transforms, and serialization.
- Keeps normal and eager-raw ESTree output compatible: both declarations remain `TSModuleDeclaration` nodes.
- Allows experimental lazy raw deserialization to expose the two new node types directly.
## Breaking Change
The Rust AST changes from:
```rust
pub enum Declaration<'a> {
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
TSModuleDeclaration(Box<'a, TSModuleDeclaration<'a>>) = 38,
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 39,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 40,
}
pub struct TSModuleDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: TSModuleDeclarationName<'a>,
pub body: Option<TSModuleDeclarationBody<'a>>,
pub kind: TSModuleDeclarationKind,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
```
to:
```rust
pub enum Declaration<'a> {
VariableDeclaration(Box<'a, VariableDeclaration<'a>>) = 32,
FunctionDeclaration(Box<'a, Function<'a>>) = 33,
ClassDeclaration(Box<'a, Class<'a>>) = 34,
TSTypeAliasDeclaration(Box<'a, TSTypeAliasDeclaration<'a>>) = 35,
TSInterfaceDeclaration(Box<'a, TSInterfaceDeclaration<'a>>) = 36,
TSEnumDeclaration(Box<'a, TSEnumDeclaration<'a>>) = 37,
TSExternalModuleDeclaration(Box<'a, TSExternalModuleDeclaration<'a>>) = 38,
TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 39,
TSGlobalDeclaration(Box<'a, TSGlobalDeclaration<'a>>) = 40,
TSImportEqualsDeclaration(Box<'a, TSImportEqualsDeclaration<'a>>) = 41,
}
pub struct TSExternalModuleDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: StringLiteral<'a>,
pub body: Option<Box<'a, TSModuleBlock<'a>>>,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub struct TSNamespaceDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub id: BindingIdentifier<'a>,
pub body: TSNamespaceDeclarationBody<'a>,
pub kind: TSNamespaceDeclarationKind,
pub declare: bool,
pub scope_id: Cell<Option<ScopeId>>,
}
pub enum TSNamespaceDeclarationBody<'a> {
TSNamespaceDeclaration(Box<'a, TSNamespaceDeclaration<'a>>) = 0,
TSModuleBlock(Box<'a, TSModuleBlock<'a>>) = 1,
}
pub enum TSNamespaceDeclarationKind {
Module = 0,
Namespace = 1,
}
```
The corresponding inherited `Statement` variants, `AstKind` variants, builders, and visitor methods have also changed.
## Why?
`TSModuleDeclaration` previously represented two declaration families with different syntax and semantics:
```ts
declare module "foo" {}
declare module "*.css";
module Foo {}
namespace Foo {}
namespace Foo.Bar {}
```
The shared representation allowed invalid states such as:
- A namespace with a string-literal name.
- An external module with an identifier name.
- An identifier namespace without a body.
- A dotted external module.
- An external module containing another module declaration as its body.
- A declaration whose `kind` disagreed with its name.
Consumers also had to inspect `id` before determining whether the node introduced a binding or represented an external module.
The two declaration families have stronger independent invariants:
### External modules
- Always have a string-literal name.
- Never introduce a normal binding identifier.
- May omit their body.
- Cannot use dotted names.
- Use module-body import and export rules.
### Namespace declarations
- Always have a binding identifier.
- Always have a body.
- May contain nested namespace declarations for dotted names.
- Explicitly preserve the `module` or `namespace` spelling.
- Introduce a namespace/module symbol.
- Use namespace-body restrictions.
Dedicated nodes encode these differences directly.
## Declaration forms
External modules use `TSExternalModuleDeclaration`:
```ts
declare module "foo" {}
declare module "*.css";
module "foo" {}
```
Identifier-based declarations use `TSNamespaceDeclaration`:
```ts
module Foo {}
namespace Foo {}
```
Dotted namespaces are represented as nested namespace declarations:
```ts
namespace Foo.Bar {}
```
```rust
TSNamespaceDeclaration {
id: Foo,
body: TSNamespaceDeclarationBody::TSNamespaceDeclaration(
TSNamespaceDeclaration {
id: Bar,
body: TSNamespaceDeclarationBody::TSModuleBlock(...),
// ...
}
),
// ...
}
```
Only `TSExternalModuleDeclaration::body` is optional.
## ESTree compatibility
Normal serialization and eager raw deserialization continue to emit the existing ESTree shape:
```ts
interface TSModuleDeclaration {
type: "TSModuleDeclaration";
id: BindingIdentifier | StringLiteral | TSQualifiedName;
body: TSModuleBlock | null;
kind: "module" | "namespace";
declare: boolean;
global: false;
}
```
Dotted namespaces continue to serialize as a single `TSModuleDeclaration` with a `TSQualifiedName`.
Experimental lazy raw deserialization is not compatibility-preserving. It now exposes `TSExternalModuleDeclaration` and `TSNamespaceDeclaration` directly, with separate constructors, layouts, and visitor type IDs.
## How to migrate
Code that previously matched all forms through one variant:
```rust
if let Declaration::TSModuleDeclaration(declaration) = declaration {
// ...
}
```
should now match the required declaration family:
```rust
match declaration {
Declaration::TSExternalModuleDeclaration(declaration) => {
// String-literal external module
}
Declaration::TSNamespaceDeclaration(declaration) => {
// Identifier-based module or namespace
}
_ => {}
}
```
The same applies to `Statement` and `AstKind`:
```rust
Statement::TSExternalModuleDeclaration(declaration)
Statement::TSNamespaceDeclaration(declaration)
AstKind::TSExternalModuleDeclaration(declaration)
AstKind::TSNamespaceDeclaration(declaration)
```
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `TSModuleDeclarationName::StringLiteral(id)` | `TSExternalModuleDeclaration::id` |
| `TSModuleDeclarationName::Identifier(id)` | `TSNamespaceDeclaration::id` |
| `body: None` | Only valid on `TSExternalModuleDeclaration` |
| `TSModuleDeclarationBody::TSModuleBlock` | External `body` or `TSNamespaceDeclarationBody::TSModuleBlock` |
| `TSModuleDeclarationBody::TSModuleDeclaration` | `TSNamespaceDeclarationBody::TSNamespaceDeclaration` |
| `TSModuleDeclarationKind::Module` | External module, or `TSNamespaceDeclarationKind::Module` |
| `TSModuleDeclarationKind::Namespace` | `TSNamespaceDeclarationKind::Namespace` |
| Inspect `id` to determine binding behavior | Match `TSNamespaceDeclaration` |
| `declaration.id()` for string modules | Now returns `None` |
| `visit_ts_module_declaration` | `visit_ts_external_module_declaration` or `visit_ts_namespace_declaration` |
When constructing nodes, use the generated `TSExternalModuleDeclaration` or `TSNamespaceDeclaration` builders rather than recreating the previous shared representation.
fixes https://github.com/oxc-project/backlog/issues/218
|
||
|
|
45a17c25d1 |
release(crates): oxc v0.143.0 (#25250)
### 💥 BREAKING CHANGES - |
||
|
|
1bdedd11ca |
feat(ast)!: introduce ExportDeclaration, ExportFromDeclaration (#25095)
## Summary
Split the overloaded Rust `ExportNamedDeclaration` into syntax-specific AST nodes:
- `ExportDeclaration` for exported declarations
- `ExportNamedDeclaration` for local exports
- `ExportFromDeclaration` for re-exports
## Breaking Change
The Rust AST changes from:
```rust
pub struct ExportNamedDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub declaration: Option<Declaration<'a>>,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub source: Option<StringLiteral<'a>>,
pub export_kind: ImportOrExportKind,
pub with_clause: Option<Box<'a, WithClause<'a>>>,
}
pub enum ModuleDeclaration<'a> {
// ...
ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
// ...
}
```
to:
```rust
pub struct ExportDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub declaration: Declaration<'a>,
}
pub struct ExportNamedDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub export_kind: ImportOrExportKind,
}
pub struct ExportFromDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub specifiers: Vec<'a, ExportSpecifier<'a>>,
pub source: StringLiteral<'a>,
pub export_kind: ImportOrExportKind,
pub with_clause: Option<Box<'a, WithClause<'a>>>,
}
pub enum ModuleDeclaration<'a> {
// ...
ExportDeclaration(Box<'a, ExportDeclaration<'a>>),
ExportNamedDeclaration(Box<'a, ExportNamedDeclaration<'a>>),
ExportFromDeclaration(Box<'a, ExportFromDeclaration<'a>>),
// ...
}
```
## Why?
The previous node represented three distinct syntax forms through optional and mutually exclusive fields:
```text
export const value = 1; -> ExportDeclaration
export { value }; -> ExportNamedDeclaration
export { value } from "mod"; -> ExportFromDeclaration
```
This allowed invalid combinations such as declarations with specifiers or sources, and import attributes without a source. The new nodes make these states unrepresentable and remove repeated `declaration`/`source` branching from consumers.
## How to migrate
Match the syntax-specific `ModuleDeclaration`, `Statement`, or `AstKind` variant instead of inspecting optional fields. Generated builders, visitors, traversal ancestors, and formatter wrappers now expose corresponding APIs for all three nodes.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `declaration: Some(declaration)` | `ExportDeclaration { declaration }` |
| `declaration.as_ref()` | `&export.declaration` |
| `source.is_none()` for local exports | Match `ExportNamedDeclaration` |
| `source: Some(source)` | `ExportFromDeclaration { source }` |
| `source.as_ref().unwrap()` | `&export.source` |
| `with_clause` | Only available on `ExportFromDeclaration` |
| Stored `export_kind` on exported declarations | `ExportDeclaration::export_kind()` |
| `ExportNamedDeclaration::boxed_plain_declaration(...)` | `ExportDeclaration::boxed(...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, None, ...)` | `ExportNamedDeclaration::boxed(..., specifiers, ...)` |
| `ExportNamedDeclaration::boxed(..., None, specifiers, Some(source), ...)` | `ExportFromDeclaration::boxed(..., specifiers, source, ...)` |
closes https://github.com/oxc-project/backlog/issues/219
|
||
|
|
8a47ff3743 |
fix(ecmascript): do not treat lone-surrogate strings as constants (#25084)
The value of a StringLiteral and the cooked value of a template quasi encode lone surrogates with `\u{FFFD}` escapes and mark the node with a `lone_surrogates` flag. ToJsString and constant evaluation returned the encoded text, and consumers materialized it into new string literals without the flag, printing the escape encoding as literal text: `"\ud800" + "y"` folded to a string containing a literal `d800`.
Return `None` for such strings and template quasis so folds keep the original nodes.
```js
// source
console.log("\ud800" + "y"); // node prints: �y
// before (miscompiled)
console.log(`�d800y`); // node prints: �d800y
// after
console.log(`\ud800`+`y`); // node prints: �y
```
|
||
|
|
c917f204a7 |
feat(ast)!: introduce ArrowFunctionBody enum (#24987)
## Summary
- Introduces `ArrowFunctionBody` to represent concise expression and block bodies explicitly.
- Removes the `ArrowFunctionExpression::expression` boolean.
- Makes invalid arrow body combinations unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct ArrowFunctionExpression<'a> {
pub expression: bool,
pub body: Box<'a, FunctionBody<'a>>,
}
```
to:
```rust
pub enum ArrowFunctionBody<'a> {
FunctionBody(Box<'a, FunctionBody<'a>>) = 64,
INHERIT(Expression<'a>),
}
pub struct ArrowFunctionExpression<'a> {
pub body: ArrowFunctionBody<'a>,
}
```
Concise arrow bodies are now stored directly as expressions instead of synthetic `FunctionBody` and `ExpressionStatement` nodes. Block bodies continue to use `FunctionBody`.
## Why?
The previous representation required consumers to maintain this implicit invariant:
```text
expression == true
→ no directives
→ exactly one statement
→ that statement is an ExpressionStatement
```
Invalid combinations could cause panics, skipped work, or fallback behavior. The enum encodes the grammar directly and makes those invalid states unrepresentable.
Concise expressions also now have `ArrowFunctionExpression` as their direct traversal ancestor, without synthetic `FunctionBody` or `ExpressionStatement` nodes.
## How to migrate
Use the arrow body accessors or match on `ArrowFunctionBody` rather than inspecting `expression` and synthetic statements.
Common field migrations:
| Previous representation | New representation |
| --- | --- |
| `arrow.expression` | `arrow.is_expression()` |
| `arrow.get_expression()` | `arrow.get_expression()` |
| `&arrow.body.statements` | `arrow.get_function_body().map(\|body\| &body.statements)` |
| `&mut arrow.body.statements` | `arrow.get_function_body_mut().map(\|body\| &mut body.statements)` |
| Synthetic expression statement in `arrow.body` | Direct `ArrowFunctionBody` expression variant |
| `arrow.body = function_body` | `arrow.body = ArrowFunctionBody::FunctionBody(function_body)` |
| Expression-to-block conversion | Create a `FunctionBody` containing a return statement |
| Block-to-expression conversion | Assign the expression directly to `arrow.body` |
| Builder `(expression, body)` arguments | Pass a single `ArrowFunctionBody` |
closes https://github.com/oxc-project/backlog/issues/211
|
||
|
|
3f36b16556 |
refactor(ecmascript): impl MayHaveSideEffects for SequenceExpression (#25074)
simple refactor to extract out `SequenceExpression` side effects such that it now implements `MayHaveSideEffects` to allow us to call `SequenceExpression::may_have_side_effects` |
||
|
|
5acdae22cb |
feat(ecmascript): skip transparent TS expressions (#25072)
This will be used by the linter. |
||
|
|
fbe8d318ff |
fix(minifier): respect targets when folding RegExp source (#24790)
RegExp literal `.source` folding used a hard-coded approximation of ES2015 support. It rejected syntax that configured targets support, such as the `u` flag for ES2015, while allowing newer syntax without checking the configured engines. The same target compatibility mapping already exists in `oxc_ecmascript` for RegExp constructor analysis. Expose one parsed-syntax predicate from that layer and reuse it for `.source` folding. This removes the duplicate policy and the minifier's direct `oxc_regular_expression` dependency. An unspecified target remains conservative, while an explicit supporting target unlocks the fold. Covered by `test_fold_regex_source` with adjacent unsupported and supported targets for `y`, `u`, `s`, `d`, `v`, named groups, Unicode property escapes, lookbehind, duplicate named groups, and pattern modifiers. Verified with `cargo test -p oxc_ecmascript --features side_effects`, `cargo test -p oxc_minifier` (631 passed, 42 ignored), `just minsize` (no minsize or allocation snapshot changes), affected-crate clippy with warnings denied, and `just fmt`. Follow-up to #24712 and [its non-blocking review](https://github.com/oxc-project/oxc/pull/24712#discussion_r3619967432). Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and submitting these changes under the repository AI usage policy. |
||
|
|
fc702c1fa9 |
release(crates): oxc v0.142.0 (#24977)
### 💥 BREAKING CHANGES - |
||
|
|
a086d91c83 |
perf(ecmascript): avoid number literal allocations (#24889)
The shared number-literal serializer can be called repeatedly by consumers that only need the emitted length. Its existing `Cow<str>` implementation allocates candidate strings for some hexadecimal and exponent forms, which would add avoidable allocation traffic to those callers. Build candidates in an inline stack string while preserving the callback API and generated output. In allocation tracking, the unoptimized consumer added 56 system allocations for `antd.js` and 129 for `kitchen-sink.tsx`; this version removes those increases. This PR depends on #24876. #24875 adds the minifier consumer. Verified with `cargo test -p oxc_ecmascript -p oxc_codegen`, Clippy, formatting, and `just allocs` with no standalone snapshot changes. AI assistance: Codex was used to implement and verify this change. |
||
|
|
dba374912a |
refactor(ecmascript): share number literal serialization (#24876)
Codegen is not the only consumer that needs the exact emitted form of a number, but the serializer currently lives in the codegen crate. Other consumers would otherwise need to duplicate its formatting policy or depend on codegen. Move the callback helper extracted in #24888 from `oxc_codegen` to `oxc_ecmascript`, then reuse it from codegen. The serialization algorithm is unchanged; this PR only makes the helper public and relocates its dependencies. This PR depends on #24888. #24889 handles allocation-free candidate construction, and #24875 adds the minifier consumer. Verified independently with `cargo test -p oxc_ecmascript -p oxc_codegen`, Clippy, and formatting. AI assistance: Codex was used to implement and verify this change. |
||
|
|
07e9acca08 |
fix(ecmascript): keep side effects in parent of toString calls (#24846)
Detect the side effect of`f()` in `(f(), 5).toString()`. |
||
|
|
16a40ad48c |
fix(ecmascript): detect side effects in unused arguments to charCodeAt (#24845)
Detect the side effect of`f()` in `'abcde'.charCodeAt(0, f())`. |
||
|
|
133bb35dee |
fix(ecmascript): detect side effects of foo().#bar correctly when PropertyReadSideEffects::None (#24769)
Side effects of`foo().#bar` was not detected correctly when PropertyReadSideEffects::None is used. Repro: https://stackblitz.com/edit/oxc-project-website-l6tidw4j?file=src%2Fmain.js refs https://github.com/oxc-project/oxc/pull/24707#discussion_r3614105531 |
||
|
|
baf022d74d |
refactor(ast)!: remove old AstBuilder methods (#24563)
Closes the migration tracked in #23043. The new builder API (methods on the AST types themselves) has been in place for a while, all Oxc crates are migrated, and every crate which uses `AstBuilder` already enables `disable_old_builder` in its tests. The module docs said "after a few weeks, we will remove `AstBuilder`'s own methods entirely" — this does that. ## What's removed - `crates/oxc_ast/src/generated/ast_builder.rs` and `builder/methods.rs` — `AstBuilder`'s own node and primitive constructors. - `AstBuilderGenerator`, which produced the former. - The `disable_old_builder` Cargo feature, plus the dev-dependency hacks it required across 33 manifests. Eight crates depended on `oxc_ast` *purely* to turn the feature on; those deps and their `cargo-shear` ignore entries are gone too. ## One thing worth reviewing `AstBuilderGenerator` owned the `#[builder(default)]` / `#[builder(skip)]` attr registration that `BuilderMethodsGenerator` only *reads* — its doc comment said as much. Deleting it naively would have silently dropped `skip`/`default` handling for the 12 AST types using those attrs, so `attrs()` and `parse_attr()` move to `BuilderMethodsGenerator`. The evidence this was faithful: after regenerating, `builder_methods.rs` and every other generated file are **byte-identical**. The only generated change in the diff is the deletion. ## Breaking changes - `AstBuilder`'s own methods are gone. Use the AST type methods: `Statement::new_expression_statement(span, expr, &builder)` instead of `builder.statement_expression(span, expr)`. - `AstBuilder` is no longer `Copy`/`Clone`, and its `allocator` field is private — use the `allocator` method from `GetAllocator`. - `AstBuilder` and `NONE` are no longer re-exported from the crate root; import them from `oxc_ast::builder`. (The `disable_old_builder` feature already gated these off, so nothing in-tree used the root path.) - The `disable_old_builder` feature no longer exists — remove it from any manifest that sets it. A migration note covering all of the above is in `crates/oxc_ast/src/builder/mod.rs`. --- Implemented with AI assistance (Claude Code); design, review, and verification driven by the author per the repo AI-usage policy. |
||
|
|
14533a3dc1 |
release(crates): oxc v0.141.0 (#24714)
### 💥 BREAKING CHANGES - |
||
|
|
9a59e1961d |
refactor(minifier): reuse is_typed_array_constructor from ecma (#24765)
refactor/reuse of `is_typed_array_constructor` from `oxc_ecmascript` instead of redefining it it `oxc_minifier` as `is_typed_array_name` |
||
|
|
79e6d775fb |
fix(minifier): preserve RegExp feature detection (#24712)
> **PR stack (3 of 3)** > 1. #24739 > 2. #24742 > 3. **#24712 (this PR)** > > Review #24739 and #24742 first. This PR is based on #24742, so GitHub shows only the RegExp behavior change. Stacked on #24742, which exposes strict engine-target capabilities to side-effect analysis. RegExp constructor calls used for engine feature detection were removed by DCE when their pattern was valid according to the current Oxc RegExp parser. A pattern can be valid at build time but still throw a `SyntaxError` on the configured runtime, so removing the call changes the result of surrounding `try`/`catch` checks. RegExp purity now requires both syntactic validity and support from every configured target. Consumers without concrete targets remain conservative, while an explicit target allows the minifier to remove constructors known to be safe for those engines. Target-independent flag validation and pattern compatibility checks now live in `oxc_regular_expression`; side-effect analysis only maps target support to unsupported flags and patterns. String patterns, modern flags, named groups, property escapes, lookbehind, modifiers, invalid flags, and lone-surrogate strings are covered. A RegExp literal with replacement flags is removable only when the target supports both those flags and the dedicated `ES2015RegExpConstructorCanAlterFlags` compatibility feature. Explicitly targeting `esnext` opts into current features. ## Example Input: ```js export function supportsUnicodeRegExp() { try { new RegExp("\\p{Ll}", "u"); return true; } catch { return false; } } ``` Before: ```js export function supportsUnicodeRegExp() { try { return true; } catch { return false; } } ``` After: ```js export function supportsUnicodeRegExp() { try { new RegExp("\\p{Ll}", "u"); return true; } catch { return false; } } ``` Verified with all `oxc_regular_expression`, `oxc_compat`, `oxc_ecmascript`, and `oxc_minifier` tests; the no-feature/`side_effects`/`constant_evaluation` `oxc_ecmascript` matrix; affected-crate clippy; formatting; and diff checks. Fixes rolldown/rolldown#10279. Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and submitting these changes under the repository AI usage policy. |
||
|
|
01c3611d9b |
refactor(ecmascript): expose engine targets to side effects (#24742)
> **PR stack (2 of 3)** > 1. #24739 > 2. **#24742 (this PR)** > 3. #24712 > > Review #24739 first. This PR is based on its branch, so GitHub shows only the engine-target plumbing. Stacked on #24739, which introduces the `side_effects` and `constant_evaluation` feature boundary for `oxc_ecmascript`. Side-effect analysis sometimes needs to know whether runtime syntax is supported by every configured engine. `MayHaveSideEffectsContext` currently has no access to target information, while `EngineTargets::has_feature` is intentionally permissive about missing compatibility data because it answers whether a transformation is needed. `MayHaveSideEffectsContext::engine_targets()` exposes the configured targets through a general-purpose accessor. Its default is `None`, so consumers without target information remain conservative, while the minifier supplies its existing `CompressOptions::target`. `EngineTargets::supports_es_feature` provides the corresponding strict capability query. Every configured engine must have compatibility data and meet the required version; empty targets and missing rows are not treated as proof of support. The existing transformation-oriented `has_feature` behavior remains unchanged. Because `oxc_linter` enables `side_effects`, this also adds `oxc_compat` to the oxlint and language-server build graphs. It does not change side-effect decisions by itself; #24712 consumes the new capability for target-aware RegExp validation. Verified with all `oxc_compat` tests, the no-feature/`side_effects`/`constant_evaluation` `oxc_ecmascript` matrix, a minifier check, formatting, and Clippy. Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and maintaining these changes. |
||
|
|
9a48d2308f |
refactor(ecmascript)!: gate analysis APIs (#24739)
> **PR stack (1 of 3)** > 1. **#24739 (this PR)** > 2. #24742 > 3. #24712 > > Review and merge in this order. The `oxc_ecmascript` crate currently compiles constant evaluation and side-effect analysis for every consumer, even when a consumer only needs the small shared ECMAScript value primitives. This makes optional analysis code and its dependencies part of unrelated builds. This introduces two explicit features: - `side_effects` enables side-effect analysis and its RegExp support. - `constant_evaluation` enables constant evaluation and implies `side_effects`, because side-effect analysis uses constant folding for several purity decisions. `ConstantValue`, `ValueType`, and `DetermineValueType` move to feature-independent root modules so `GlobalContext` remains available without either analysis feature. Existing paths through `constant_evaluation` remain re-exported when that feature is enabled. This is a prerequisite for #24742, which exposes engine targets to side-effect analysis without making compatibility data part of every `oxc_ecmascript` consumer. This is a breaking Cargo feature change for direct consumers of `oxc_ecmascript`: code using side-effect analysis or constant evaluation must enable the corresponding feature. Verified locally with the no-feature, `side_effects`, and `constant_evaluation` configurations, crate tests, dependent linter and minifier tests, rustdoc with warnings denied, and `just ready`. The release oxlint binary on this commit is 13,350,096 bytes. Implemented with AI assistance (OpenAI Codex and Claude). The contributor remains responsible for reviewing, understanding, and maintaining these changes. |
||
|
|
e750a8243a |
fix(ecmascript): fix false negative for may_have_side_effects on dynamic property access (#24709)
``[][`length${foo()}`]`` was treated as side effect free. But `foo()` can have side effects.
|
||
|
|
54cc121250 |
feat(ast)!: split MetaProperty into ImportMeta and NewTarget (#24557)
## Summary
- replace the generic Rust MetaProperty AST node with dedicated ImportMeta and NewTarget nodes
- preserve the ESTree MetaProperty shape and generated public TypeScript types
- update parser, formatter, codegen, lint, transform, minifier, traversal, and React compiler consumers
## Summary
Remove `MetaProperty` AST node, in favour of two separate nodes (`ImportMeta` and `NewTarget`)
## Breaking Change
The Rust AST field changes from:
```rust
pub struct MetaProperty<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub meta: IdentifierName<'a>,
pub property: IdentifierName<'a>,
}
```
to:
```rust
/// /// `import.meta` in `console.log(import.meta);`.
pub struct ImportMeta {
pub node_id: Cell<NodeId>,
pub span: Span,
}
/// `new.target` in `function F() { return new.target; }`.
pub struct NewTarget {
pub node_id: Cell<NodeId>,
pub span: Span,
}
```
## Why?
The current AST could represent shapes such as:
```
foo.bar
```
By constructing the node manually. However this doesn't make sense, and is invalid.
## How to migrate?
1. Replace any `Expression::MetaProperty` matches/checks with the dedicated `Expression::ImportMeta` and `Expression::NewTarget` varients
2. Replace any `new_meta_property` construction with the new `new_import_meta` or `new_new_target` APIs.
3. Inspecting the `meta`/`property` identifier names is no longer required.
|
||
|
|
8e0ed2ebb9 |
release(crates): oxc v0.140.0 (#24453)
### 🚀 Features - |
||
|
|
69b2dfc181 |
release(crates): oxc v0.139.0 (#24218)
### 🚀 Features - |
||
|
|
bd49aff795 |
perf(ecmascript): avoid heap-allocating Math.min/max/imul operands (#23941)
## Summary `try_fold_math_variadic` constant-folds `Math.min` / `Math.max` / `Math.imul(...)` by collecting the operand values into a `Vec<f64>`. That is a **system allocation on every foldable call**, on the minifier's constant-folding hot path. The evaluation context already exposes the arena (`ConstantEvaluationCtx: GetAllocator`, and the module already imports `ArenaVec`), so collect into an `ArenaVec` instead. The list is transient and only iterated / indexed (`.iter()`, `.first()`, `.get()`), so the change is behaviour-identical — the system allocation is simply gone. ## Results — `cargo allocs` (minifier system allocations) | fixture | before | after | | --- | ---: | ---: | | kitchen-sink.tsx | 2576 | **2312** (−264) | | antd.js | 1040 | **870** (−170) | | pdf.mjs | 2569 | **2476** (−93) | | App.tsx | 78 | 72 | ## Tests - `cargo test -p oxc_ecmascript --lib --tests` — pass - `cargo test -p oxc_minifier --lib --tests` — 539 pass ## AI disclosure Prepared with AI assistance (Claude). I located the hotspot with a per-call-site allocation profile across real files, confirmed the list is transient/iterated-only, and verified the reduction with `cargo allocs` plus the test suites. --------- Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
e2744150c1 |
fix(minifier): don't drop global calls that throw despite pure arguments (#23917)
## What
`is_pure_global_method_call` (and the bare-global path) in `CallExpression::may_have_side_effects` treated a call as side-effect-free whenever its arguments were, ignoring that several of these functions **throw** at runtime. Unused calls that throw were therefore dropped as dead code. terser and esbuild both keep them.
Guard the cases oxc can prove:
| Call | Throws | Before | After |
| --- | --- | --- | --- |
| `String.raw()`, `Symbol.keyFor()`, `Symbol.keyFor(42)` | `TypeError` (missing/invalid required arg) | dropped | kept |
| `URL.canParse()` | `TypeError` (required first arg) | dropped | kept |
| `String.fromCodePoint(-1 / 1.5 / 0x110000)` | `RangeError` (code point outside `[0, 0x10FFFF]`) | dropped | kept |
| `Math.abs(10n)`, `Date.UTC(10n)`, `String.fromCharCode(10n)`, `isNaN(10n)`, … | `TypeError` (`ToNumber(BigInt)`) | dropped | kept |
Safe optimizations are preserved — `URL.canParse("x")`, `String.fromCodePoint(65)`, `String.fromCharCode(65)`, `Math.abs(1)` still drop. `String.raw` / `Symbol.keyFor` are removed from the pure-method list (never provably safe); the rest get argument guards.
### A Symbol argument is left droppable, on purpose
A Symbol argument also throws (`ToString` / `ToNumber` on a Symbol), but oxc has no `ValueType::Symbol` and `to_primitive` never resolves a pure expression to `Symbol`, so a Symbol throw is **never provable**. Guarding it would mean keeping every `Math.abs(x)` / `parseInt(x)` on an undetermined argument — a large minification regression. esbuild makes the same trade-off (it drops `Math.abs(Symbol.iterator)` too), so only the provable cases are guarded.
oxc already guards exactly this for the `Number` / `Symbol` / `BigInt` / `Error` constructors; this brings the method-call path in line. The assumption (including the non-provable-Symbol carve-out) is documented in `crates/oxc_minifier/docs/ASSUMPTIONS.md`.
Covered by `test_throwing_global_calls` in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; cross-checked against terser and esbuild; `minsize` unchanged.
---
Stacked on #23916.
|
||
|
|
60e716087d |
feat(minifier): drop side-effect-free IIFEs whose result is unused (#23967)
## Summary
- `CallExpression::may_have_side_effects` now looks through an IIFE — a call whose callee is a function/arrow literal: it is side-effect-free when its arguments, parameters, and every body statement are. Previously every such call was assumed effectful, so the minifier kept dead wrappers like `var unused = (function () { function test() {} return test })()`. Closes #23777.
- This is a plain improvement to side-effect detection in `oxc_ecmascript` — no DCE awareness there. The "preserve IIFE structure in DCE-only mode" policy (match Rollup / esbuild `--tree-shaking=true`, see `preserve_iife_in_dce_mode`) lives in the minifier, in one helper (`has_side_effects_or_preserved_iife`) shared by every unused-removal site that can face an IIFE call, so only full minification actually drops it.
- Conservative — skips `async`/generator callees (different invocation semantics) and any non-identifier parameter (defaults run user code, destructuring reads properties / drives the iterator protocol).
- Covered by `remove_unused_expression.rs::test_remove_side_effect_free_iife` (full-mode positive + negative) and `dead_code_elimination.rs::preserve_iife_in_dce_mode` (DCE-mode preservation: bare, unused-binding, sequence, and `for`-init positions). `minsize` unchanged, `allocs` slightly down.
## Example
```js
// input
var unused = (function () {
function test() {}
return test
})()
```
```js
// full minify, before — kept as a bare expression statement
(function () {
function test() {}
return test;
})();
// full minify, after
// (empty)
```
In dce-only / tree-shaking mode the IIFE structure is preserved (matches Rollup / esbuild `--tree-shaking=true`).
|
||
|
|
8bf69a3076 |
release(crates): oxc v0.138.0 (#23934)
### 💥 BREAKING CHANGES - |
||
|
|
dcc9a73b12 |
fix(minifier): treat Object.isExtensible/isFrozen/isSealed as pure (#23916)
## What `Object.isExtensible`, `Object.isFrozen`, and `Object.isSealed` were grouped with the throwing introspection methods (`Object.keys`, `getOwnPropertyDescriptor`, …) in `CallExpression::may_have_side_effects`, so a call with a missing / `null` / `undefined` argument was conservatively kept. Unlike those methods, the three `is*` methods never `ToObject` their target — per [spec](https://tc39.es/ecma262/#sec-object.isextensible) a non-object receiver returns a primitive (`false` / `true`) instead of throwing, so the call is side-effect-free and an unused one can be dropped. ```js // in Object.isExtensible(); Object.isFrozen(x); Object.isSealed(); Object.keys(); // out Object.isFrozen(x), Object.keys(); ``` `isExtensible()` / `isSealed()` drop (pure); `isFrozen(x)` stays — an undetermined target could be a `Proxy`, whose `[[IsExtensible]]` / `[[OwnPropertyKeys]]` traps are observable — and `keys()` stays (it throws on the `undefined` receiver). Covered by new cases in `crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs`; `minsize` unchanged. Closes #23779 |
||
|
|
3d61dea7e2 |
docs(all): correct capitalization in comments (#23887)
Just fix capitalization in comments. |
||
|
|
f2091b3846 |
feat(ast): unify old and new AstBuilders (#23875)
Part of #23043. Recent PRs have introduced a new `AstBuilder` type, and migrated all Oxc's crates over to it. To allow downstream consumers to incrementally migrate from old `AstBuilder` to new one, unify the two implementations into a single `AstBuilder` type. Remove the `builder_new` Cargo feature and instead add a new feature `disable_old_builder`. When enabled, this feature removes all the methods from `AstBuilder`, and makes `AstBuilder` non-`Copy`. Consumers should migrate, and then enable this feature to ensure later changes don't use the methods which are slated for removal. Enable the `disable_old_builder` feature in all Oxc crates which use `AstBuilder` _in tests_. This ensures we don't go back to using the deprecated methods, while not forcing the feature on consumers (e.g. Rolldown) who use e.g. `oxc_transformer` but also use `AstBuilder` directly. |
||
|
|
8de5122230 |
feat(ecmascript)!: switch to new AstBuilder (#23834)
Part of #23043. Migrate `oxc_ecmascript` over to new `AstBuilder`. This is a breaking change for users of `oxc_ecmascript` crate. `ConstantEvaluationCtx` no longer has an `ast` method to get an `AstBuilder`. Instead, any type that implements `ConstantEvaluationCtx` must also implement `oxc_allocator::GetAllocator` and `oxc_ast::builder::GetAstBuilder` - the latter proving a `builder` method which returns a `&AstBuilder`. I'll aim to fix this to remove the breaking change in a follow-on PR (before next release). |
||
|
|
f65d8e5291 |
refactor(all): shorten redundant std::vec::Vec (#23748)
Pure refactor. #23748 made all usages of arena-allocated `Vec` be named `ArenaVec`. This makes plain `Vec` unambiguously refer to heap-allocated `Vec` from standard library. Shorten `std::vec::Vec` to just `Vec` in various places. |
||
|
|
d1f18cda21 |
refactor(all): import arena Box and Vec as ArenaBox / ArenaVec (#23747)
Pure refactor. Use `ArenaBox` and `ArenaVec` everywhere for the arena-allocated types from `oxc_allocator`, instead of plain `Box` and `Vec`. This disambiguates when `Vec` is `oxc_allocator::Vec` and when it's `std::vec::Vec`, making code clearer to follow. Previously it was easy to confuse the two, and such confusion led to UB in `Traverse` (fixed in #23745). Ditto other arena types - `ArenaHashMap`, `ArenaHashSet`, `ArenaStringBuilder`. The only exceptions are: 1. Inside `oxc_allocator` crate itself. 2. AST type definitions. 3. Generated code (migrated separately in #23749). |
||
|
|
6d56d57535 |
release(crates): oxc v0.137.0 (#23591)
### 💥 BREAKING CHANGES - |