mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
codex/parser-grouped-expression-allocation
2615 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
36d5e13fc2 |
refactor(formatter_core,formatter,oxfmt): rename embedded meta to child_context (#25428)
`parent_context` <-> `child_context` is more intuitive. |
||
|
|
6eae5c9803 |
docs(formatter,oxfmt): record embed-layer decisions in place (#25422)
Just notes. |
||
|
|
ab52a593f3 |
fix(formatter): format xxx-in-js inside JSDoc js fence (#25414)
Such as CSS-in-JS-in-JSDoc_Markdown-in-JS. 😇
|
||
|
|
1a2c64a356 |
fix(formatter,oxfmt): apply effective print width for JSDoc fence (#25413)
As JSDoc is always contained within the ` * ` fenced comments, the `printWidth` option should not have been used as is. The dedicated `jsdocPrintWidth` option has not yet been implemented. #24633 |
||
|
|
ce29325cfe |
refactor(oxfmt): consolidate embedded routing and service profiles (#25396)
Consolidated the criteria for determining which languages are handled by? - natively by Rust - passed to Prettier when formatting embedded code, as these were previously scattered across various places. |
||
|
|
8213e72f37 |
refactor(formatter,oxfmt): flatten embedded dispatch contract (#25391)
Due to historical reasons, the IR was passed as `[Doc, (Doc, ...Doc)]` only when processing `gql-in-js`, whilst for other cases such as `css-in-js` and `html-in-js`, it was always `[Doc]`. Now, every result is single `Doc`. |
||
|
|
465b723d04 |
refactor(formatter,oxfmt): unify host services as SessionServices on FormatSession (#25388)
- dispatcher - string_embedder - tailwind_sorter Group these 3 together as `SessionServices`, make them available to `FormatSession`, and pass them around. |
||
|
|
aa1334f1c8 |
refactor(oxfmt): use dispatcher for non-napi xxx-in-js (#25387)
Although everything was ready in #25331, it was still behind the `napi` gate, so removed it. |
||
|
|
2eaede9b0b |
fix(formatter_core): unify leading-BOM handlings (#25340)
Follow up on #25333. |
||
|
|
fd02a8956e |
feat(oxfmt): dispatch yaml-in-css(frontmatter) to oxc_formatter_yaml (#25336)
Using the `Dispatcher` set up so far, detect the YAML front matter and process it using `oxc_formatter_yaml`. |
||
|
|
567089f3c5 |
refactor(oxfmt): route JSDoc fence through the dispatcher (#25334)
Markdown code fences can also be written in JSDoc, and other languages can be formatted as inline code within them. |
||
|
|
a92c5410a1 |
fix(linter): preserve source text for JS plugin ignore fixes (#25280)
Closes #25278 ## Overview This PR resolves an oxlint language server crash that could occur when requesting code actions for diagnostics reported by JS plugins, particularly when the diagnostic appears after the beginning of a file. JS plugin diagnostics can now safely provide regular fixes as well as line-level and file-level disable actions. ## Root cause 1. In `run_external_rules()`, the `semantic` is being taken to run the external JS plugin rules, leaving the former `semantic` inside the context host empty: https://github.com/oxc-project/oxc/blob/e23dccf338d568ac0c2be3b1223f213c797923f7/crates/oxc_linter/src/lib.rs#L539 2. The language server enables `with_ignore_fixes` to generate ignore fixes, but inside the `add_ignore_fix()`, it doesn't check the `section_source_text`. 3. The context host with the empty `semantic` passes the empty source text to the `add_ignore_fix()` as `section_source_text`, and the empty string just causes the crash. https://github.com/oxc-project/oxc/blob/e23dccf338d568ac0c2be3b1223f213c797923f7/crates/oxc_linter/src/context/host.rs#L329-L334 ## What changed A new property `source_text` is added to the context sub host, which stores a reference to the real source text string. Even if the `semantic` is taken and becomes empty, the reference can still get the real string and make things work. ```diff pub struct ContextSubHost<'a> { /// Semantic information about the file being linted, which includes scopes, symbols and AST nodes. /// See [`Semantic`]. pub(super) semantic: Semantic<'a>, // ... + /// Stable source text for this script section + /// which remains available even after `semantic` is taken while running JS plugins. + pub(super) source_text: &'a str, /// The source text offset of the sub host pub(super) source_text_offset: u32, } ``` And I added a new utility method to get the `source_text` from the context sub host. ```rs #[inline] pub fn source_text(&self) -> &'a str { self.current_sub_host().source_text() } ``` When passing source text to `add_ignore_fix()`, just use the new `source_text()` method. ```diff if self.with_ignore_fixes { - let source_text = self.semantic().source_text(); + let source_text = self.source_text(); for diagnostic in &mut diagnostics { diagnostic.add_ignore_fix(self.current_sub_host().source_text_offset, source_text); } } // (the same as other occurs ``` Also, I added the necessary check for the `section_source_text` in `add_ignore_fix()`. ## Notes This is my first contribution to oxc project. Thanks for reviewing! |
||
|
|
52b146f025 |
refactor(formatter_core): share split_bom helper (#25333)
In order to standardise the handling of front matter, it was also necessary to standardise the handling of the BOM, which may appear before the FM. |
||
|
|
b3ad060cfd |
refactor(formatter): migrate embedded entries to FormatSession (#25332)
Apply `FormatSession` everywhere introduced in #25329. |
||
|
|
fb8affe04c |
refactor(oxfmt): make native dispatch registry available without napi (#25331)
Previously, as Prettier was required for embedded formatting, so embedding was only supported in `napi` builds. However, as the number of rewritten Rust components has increased recently, we have enabled embedded formatting in `pure` builds as well, thereby reducing the scope of the `napi` gate. |
||
|
|
11e0645dc9 |
refactor(oxfmt,formatter_core): add DispatchOutcome with recursion limit (#25330)
In the embedding format, issuing a `DispatchRequest` returns a `DispatchOutcome`. Additionally, by managing the nesting depth, made it possible to limit the number of recursion. |
||
|
|
8e779c946b |
refactor(formatter_core): finalize documents once at print time (#25328)
Call `propagate_expand()` inside `Formatted`, here is the only place to finalize IRs. |
||
|
|
c7583238a0 |
refactor(lsp): add ToolBuildResult struct for optional client message (#25386)
Added a new `ToolBuildResult` struct, so an optional `ClientMessage` can be passed to `Backend` (in a later PR). This allows us later to send the custom message to the clients interface when some config errors happened on the (re)creation. There is no message sent yet, this will be integrated in another PR too. This PR is just the base for the `ToolInterface`. |
||
|
|
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
|
||
|
|
4ec9189b26 |
perf(oxlint/lsp): avoid second lock for getting/removing unused directives (#25350)
Avoids a second lock for `.map` inside `DirectivesStore` when consuming the directives on `--lsp` side. |
||
|
|
6c0d01b8de |
perf(oxlint/lsp): preallocate fix-content vec (#25351)
After https://github.com/oxc-project/oxc/pull/24958 we know the size of `Vec<FixedContent>` , because we are only consuming now `Messages.fixes` which are directly mapped into `FixedContent` |
||
|
|
5c5cdcd522 |
feat(ast)!: narrow TSInterfaceHeritage::expression to TSTypeName (#24360)
## Summary
Narrow `TSInterfaceHeritage` from a general JavaScript `Expression` to `TSTypeName`.
```rust
pub struct TSInterfaceHeritage<'a> {
pub type_name: TSTypeName<'a>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
```
## Breaking Change
The Rust AST field changes from:
```rust
expression: Expression<'a>
```
to:
```rust
type_name: TSTypeName<'a>
```
## Why?
The current AST can represent shapes such as:
```ts
interface A extends foo() {}
interface A extends A + B {}
interface A extends new Foo() {}
interface A extends true {}
```
Which is an overly wide type since all of the above variants are invalid.
This PR tightens up the AST, making these invalid variants irrepresentable.
## How to migrate?
This should be a trivial migration:
1. instead of accessing `expression` on `TSInterfaceHeritage`, now access `type_name`
2. change any pattern matching on the `type_name` field to use `TypeName` (this is a smaller subset so should allow deleting code!)
closes https://github.com/oxc-project/backlog/issues/215
|
||
|
|
6b1c4795b2 |
fix(oxlint): normalize customized rule names (#25316)
## Summary - normalize LSP `rulesCustomization` keys using the linter config rule-name parser - support `eslint/` prefixes and existing plugin aliases such as `@typescript-eslint` - cover prefixed severity and autofix customization paths Closes #25311 |
||
|
|
ccb8fe89db |
feat(linter/jsdoc): implement no-blank-blocks rule (#25207)
this PR implements `jsdoc/no-blank-blocks` rule, issue #1170 |
||
|
|
9573937df3 |
fix(linter/typescript): validate ban-ts-comment description_format (#25320)
adds validation for the `descriptionFormat` regex option from #23594 and replaces manual config parsing with `DefaultRuleConfig` |
||
|
|
6be314f226 |
feat(ast)!: remove duplicated VariableDeclarator::kind (#25319)
## Summary
- Removes `VariableDeclarator::kind`.
- Makes `VariableDeclaration::kind` the single source of truth for every declarator in its declaration list.
- Makes contradictory parent and child declaration kinds unrepresentable.
## Breaking Change
The Rust AST changes from:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(skip)]
pub kind: VariableDeclarationKind,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
to:
```rust
pub struct VariableDeclaration<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub kind: VariableDeclarationKind,
pub declarations: Vec<'a, VariableDeclarator<'a>>,
#[ts]
pub declare: bool,
}
pub struct VariableDeclarator<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
#[estree(via = VariableDeclaratorId)]
pub id: BindingPattern<'a>,
#[ts]
#[estree(skip)]
pub type_annotation: Option<Box<'a, TSTypeAnnotation<'a>>>,
pub init: Option<Expression<'a>>,
#[ts]
pub definite: bool,
}
```
## Why?
A variable declaration has one declaration kind shared by every declarator in its declaration list:
```js
const first = 1, second = 2;
```
Previously, the AST stored that kind on both the parent declaration and every child declarator:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Const, ... },
VariableDeclarator { kind: Const, ... },
],
}
```
This allowed contradictory ASTs to be constructed:
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { kind: Var, ... },
],
}
```
Different consumers read different copies:
- Code generation used `VariableDeclaration::kind`.
- Semantic binding used `VariableDeclarator::kind`.
- Lint rules, transforms, and minifier passes used a mixture of both.
As a result, one AST could be emitted as `const` while its binding was treated as function-scoped `var`.
Transforms also had to keep the copies synchronized manually. For example, const-to-let normalization updated the parent and then looped through every declarator to update each child. Explicit resource management performed similar paired mutations for `using` declarations.
Keeping the kind only on `VariableDeclaration` removes this synchronization requirement and prevents consumers from observing conflicting declaration semantics.
## Declaration forms
The declaration kind now belongs exclusively to the node that owns the declaration list:
```js
let value;
```
```text
VariableDeclaration {
kind: Let,
declarations: [
VariableDeclarator { id: value, ... },
],
}
```
Multiple declarators share the same parent kind:
```js
const first = 1, second = 2;
```
```text
VariableDeclaration {
kind: Const,
declarations: [
VariableDeclarator { id: first, ... },
VariableDeclarator { id: second, ... },
],
}
```
## How to migrate
Code that previously read the kind from a declarator:
```rust
if declarator.kind.is_const() {
// ...
}
```
should now read it from the containing declaration:
```rust
if declaration.kind.is_const() {
// ...
}
```
Consumers operating through an AST node store should retrieve the parent `VariableDeclaration`:
```rust
let AstKind::VariableDeclaration(declaration) =
nodes.parent_kind(declarator.node_id())
else {
unreachable!();
};
let kind = declaration.kind;
```
Common migrations:
| Previous representation | New representation |
| --- | --- |
| `declarator.kind` | Containing `declaration.kind` |
| Match `VariableDeclarator::kind` during semantic binding | Read the parent `VariableDeclaration` from ancestry |
| Pass `kind` to `VariableDeclarator::new` | Remove the `kind` argument |
| Update parent and every child kind | Update only `VariableDeclaration::kind` |
| Recover kind after detaching a declarator | Capture or pass the parent kind before detaching it |
| Inspect a symbol’s declarator kind in lint rules | Resolve its parent declaration through the node store |
When constructing declarators, use the generated kindless `VariableDeclarator` builders and place them inside a `VariableDeclaration` carrying the required kind.
fixes https://github.com/oxc-project/backlog/issues/237
|
||
|
|
4abff11c80 |
perf(linter): avoid redundant message work in the agent reporter (#25315)
Generated with Claude Code, Opus 5. Reviewed and tested by me. Mainly just avoiding extra, unneeded allocations per-diagnostic. This shouldn't matter much at normal scales, but given that many people are using Claude Code, Codex, etc for setting up their linter config or to mass-resolve lint violations, I think the tradeoff in extra code here is probably worth it because there are definite cases where this will be getting triggered with large diagnostic counts. I will admit the `is_already_compact` method here is jank, though. --- Two small changes to `format_agent`, both removing work that was already done. **1. Borrow already-compact messages.** `compact_message` rebuilt every message into a fresh `String` by splitting on whitespace and rejoining, even though nearly every rule message is already compact and comes out byte-for-byte identical. It now checks first and borrows when there is nothing to collapse, falling back to the rebuild otherwise. **2. Reuse the message `Info` already rendered.** `Info::new` calls `diagnostic.to_string()` internally; `format_agent` discarded that and rendered it a second time. It now takes the message from `Info`. `Info` only fills the message in when the diagnostic has a resolvable label, so the direct render is kept for the label-less case — which the existing `reporter_error_without_label` test covers. ## Allocations Measured with a counting global allocator, `--threads=1` for deterministic counts, on a corpus of 20k small files producing exactly 2,400,000 diagnostics. `unix` is a control (untouched by this PR) and `--silent` is the no-formatter baseline. | format | main allocs | branch allocs | diff | change | | --- | ---: | ---: | ---: | ---: | | silent (baseline) | 55,672,894 | 55,672,894 | 0 | 0.00% | | unix (control) | 91,353,791 | 91,353,791 | 0 | 0.00% | | agent | 124,894,389 | 104,194,389 | -20,700,000 | -16.57% | Subtracting the baseline to isolate the formatter itself: | | main | branch | change | | --- | ---: | ---: | ---: | | agent formatter allocations | 69,221,495 | 48,521,495 | **-29.9%** | | per diagnostic | 28.84 | 20.22 | -8.62 | | bytes requested | 8.39 GB | 7.66 GB | -729.7 MB | Worth noting that the saving is much larger than the two `String`s the diff visibly removes. `diagnostic.to_string()` is not one allocation — rendering an `Error` goes through miette formatting, which allocates repeatedly — so removing the duplicate render is worth around 8 allocations per diagnostic, not one (we should maybe consider figuring out if we can fix that separately to reduce the allocations per diagnostic, but usually there aren't too many diagnostics so it's a minor improvement). ## Timing Paired-difference measurement: samples interleaved one-for-one between the two binaries and differenced pairwise, so drift cancels. `unix` again as control. | corpus | format | main | branch | diff | 95% CI | change | | --- | --- | ---: | ---: | ---: | :---: | ---: | | 20k small files (2.4M diagnostics) | agent | 3837ms | 3272ms | -564.4ms | [-590.7, -538.2]ms | -14.7% | | 20k small files (2.4M diagnostics) | unix (control) | 2846ms | 2832ms | -14.2ms | [-39.4, +10.9]ms | -0.5% | | vscode `-D all` (1.37M diagnostics) | agent | 13721ms | 13381ms | -339.8ms | [-511.6, -168.0]ms | -2.5% | | vscode `-D all` (1.37M diagnostics) | unix (control) | 12794ms | 12781ms | -12.9ms | [-70.1, +44.4]ms | -0.1% | ## Equivalence Confirmed byte-identical output from the formatter on main vs this branch over VS Code. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6bb5421c4a |
perf(linter): hoist current_dir out of the stylish reporter loop (#25313)
AI Disclosure: Generated with Claude Code, reviewed and tested by me. Just a micro-optimization. The stylish reporter calls `std::env::current_dir()` once per file group, in order to canonicalize each filename for display. That is a syscall, and the result is identical for every group, so this reads it once before the loop and borrows it. Output is unchanged — `current_dir()` is not expected to vary mid-run, and the value is used the same way. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
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
|
||
|
|
9a7c323500 |
perf(linter): compute diagnostic Info once per diagnostic in junit reporter (#25312)
AI Disclosure: Generated with Claude Code, Opus 5. Tested and reviewed by me for correctness. --- `format_junit` built an `Info` for every diagnostic twice: once to group diagnostics by filename, then again inside the render loop. `Info::new` reads the span out of the source, so this doubled the most expensive part of the reporter. Build it once and group by index instead. Also drop two other duplicated pieces of work in the same loop: the message was `xml_escape`d twice (once for the attribute, once for the description), and the rule name was re-derived via `code().to_string()` even though `Info` already carries it as `rule_id`. And the nested `Vec<String>` + `join` is now a single output buffer. Benchmarked with 3 runs x 2 format options (checkstyle as the control) x 3 repos, shows small reductions on junit in RSS thanks to this change, and 40-47% drops in runtime (`oxlint -D all --format=NAME`): | repo | diagnostics | format | main | branch | delta | change | RSS main | RSS branch | | --- | ---: | --- | ---: | ---: | ---: | ---: | ---: | ---: | | vscode | 1,366,572 | checkstyle | 11832ms | 11645ms | -187ms | -1.6% | 3272 MB | 3273 MB | | vscode | 1,366,572 | **junit** | 22022ms | 11611ms | -10410ms | **-47.3%** | 3325 MB | 3287 MB | | gitlab | 363,895 | checkstyle | 1749ms | 1723ms | -26ms | -1.5% | 966 MB | 971 MB | | gitlab | 363,895 | **junit** | 2895ms | 1718ms | -1177ms | **-40.7%** | 989 MB | 948 MB | | canvas-lms | 350,637 | checkstyle | 2207ms | 2155ms | -52ms | -2.3% | 917 MB | 915 MB | | canvas-lms | 350,637 | **junit** | 3944ms | 2139ms | -1805ms | **-45.8%** | 921 MB | 886 MB | On a more reasonable number of diagnostics (running on canvas-lms with the default ruleset) the numbers are less pronounced but still show a 17% drop: | repo | ruleset | diagnostics | format | main | branch | delta | change | | --- | --- | ---: | --- | ---: | ---: | ---: | ---: | | canvas-lms | default | 3,149 | checkstyle | 218ms | 219ms | +1ms | +0.5% | | canvas-lms | default | 3,149 | junit | 265ms | 219ms | -46ms | -17.3% | On a repo with 0 violations, the runtime difference for junit on main vs. this branch is entirely noise. Realistically this won't have much of an impact in a user's actual run, as most of the time a lint run results in one of: - 0 diagnostics - a single digit number of diagnostics in a junit run due to new violations introduced by code changes - or there are only a few hundred to a few thousand max (due to, for example, warnings which are considered safe and are only used for a "transition" period where a rule is soft-enabled until all existing violations of it are fixed). But I think it's worthwhile regardless to help on smaller #s of diagnostics. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d4a897ce22 |
feat(linter/eslint): implement one-var rule (#24470)
## Summary Adds a native Oxlint implementation of [ESLint's `one-var` rule](https://eslint.org/docs/latest/rules/one-var). This implementation is a port of the [eslint rule source](https://github.com/eslint/eslint/blob/main/lib/rules/one-var.js), test suite, and autofix logic. Related #479 ## AI Disclosure GPT 5.6 Sol was used to assist with implementation of this rule. --------- Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
5ab9340637 |
feat(linter/jsx-a11y/anchor-has-content): add options to match eslint (#24571)
## Description Eslint supports [a `components` option](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-has-content.md) for `jsx-a11y/anchor-has-content` that specified additional components to treat as anchor elements. This adds equivalent support for that option. Note: I am not certain if this is something that should instead be read off of [`settings.jsx-a11y.components`](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html#settings-jsx-a11y-components), as that could list `{ MyAnchor: 'a' }`. Doing that would enable implicit application of rules to custom components based on rendered entity, but that may be the wrong use case, as the custom component definition itself could be responsible for rendering required children. ## AI disclosure I used GPT 5.6 Sol to assist in implementation of this fix. I've reviewed all generated code and test coverage. --------- Signed-off-by: Cameron <cameron.clark@hey.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
1cf7ddea46 |
perf(oxlint): render JSON report into a single buffer (#25295)
AI Disclosure: Generated with Claude Code, Opus 5. Reviewed and tested by me. ## What `format_json` rendered each diagnostic into its own `String`, collected them into a `Vec<String>`, then `join`ed them. That allocates once per diagnostic and copies the entire report a second time to build the joined result. This renders straight into one output buffer instead. ## Why On the vscode corpus with `-D all` — 1.37M diagnostics, ~500 MB of JSON output: | | before | after | | --- | --- | --- | | `--format=json` wall clock | 13.3 s | 12.6 s | | peak RSS | 4668 MB | 3576 MB | Measured as best-of-3 with the two binaries interleaved, output redirected to `/dev/null`. ## Correctness Output is byte-identical before and after. Verified on `apps/oxlint/fixtures/cli/output_formatter_diagnostic` and on a 365k-diagnostic subset of vscode (170 MB of JSON compared byte-for-byte). Note that oxlint's diagnostic ordering is nondeterministic run-to-run under parallelism, so the comparison was run with `--threads=1`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
c9d1a5b603 |
perf(oxfmt): spawn tinypool lazily (#25298)
Partially fixes #25196 - Before: create `child_process` worker pool on startup - Spawned even when nothing needs JS side delegation (e.g. JS/TS-only projects) - For LSP, the idle workers stayed resident for the whole session - After: create the pool on the first format that actually delegates to JS side - Runs that never reach JS side spawn zero workers - As more formatters are rewritten in Rust, the share of such runs keeps growing Trade-off: the first JS-delegated format in an LSP session now pays the spawn cost. But that is what every one-shot CLI run already pays, so it is far from slow, and subsequent formats are unchanged. |
||
|
|
ab12665f34 |
feat(formatter_core): add hardlineWithoutBreakParent equivalent IR (#25273)
builder: `hard_line_break().without_expand_parent()` |
||
|
|
72bbbe240a | chore(deps): update dependency oxc-parser to ^0.143.0 (#25260) | ||
|
|
cc01bd7f0f |
chore(deps): update dependency @oxc-project/types to ^0.143.0 (#25255)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [@oxc-project/types](https://oxc.rs) ([source](https://redirect.github.com/oxc-project/oxc/tree/HEAD/npm/oxc-types)) | [`^0.142.0` → `^0.143.0`](https://renovatebot.com/diffs/npm/@oxc-project%2ftypes/0.142.0/0.143.0) |  |  |  |  | --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> |
||
|
|
9a423f2f48 |
release(apps): oxlint v1.77.0 && oxfmt v0.62.0 (#25251)
# Oxlint ### 💥 BREAKING CHANGES - |
||
|
|
b937e3e2f1 |
chore(deps): update napi (#25019)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | Type | Update | |---|---|---|---|---|---|---|---| | [@napi-rs/cli](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs)) | [`3.7.4` → `3.8.2`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.7.4/3.8.2) |  |  |  |  | pnpm.catalog.default | minor | | [napi](https://redirect.github.com/napi-rs/napi-rs) | `3.11.0` → `3.12.0` |  |  |  |  | workspace.dependencies | minor | | [napi-build](https://redirect.github.com/napi-rs/napi-rs) | `2.3.2` → `2.4.0` |  |  |  |  | workspace.dependencies | minor | | [napi-derive](https://redirect.github.com/napi-rs/napi-rs) | `3.6.0` → `3.6.2` |  |  |  |  | workspace.dependencies | patch | --- ### Release Notes <details> <summary>napi-rs/napi-rs (@​napi-rs/cli)</summary> ### [`v3.8.2`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.1...@napi-rs/cli@3.8.2) ### [`v3.8.1`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.8.0...@napi-rs/cli@3.8.1) ### [`v3.8.0`](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.0) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.7.4...@napi-rs/cli@3.8.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yODAuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjMuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
9d4592c72f |
fix(linter): reject duplicate JS plugin names (#25243)
## Summary - reject JS plugins whose normalized names collide - keep registered plugin names isolated per LSP workspace - add a CLI regression fixture covering two plugins that expose the same rule name Fixes #25164 |
||
|
|
eaa7c692d2 | docs(formatter_core): extract FORMATTER_POLICY (#25233) | ||
|
|
d0c45aaf7c |
chore(deps): update npm packages (#25205)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Adoption](https://docs.renovatebot.com/merge-confidence/) | [Passing](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---|---|---| | [@napi-rs/wasm-runtime](https://napi.rs/) ([source](https://redirect.github.com/napi-rs/napi-rs/tree/HEAD/wasm-runtime)) | [`1.1.6` → `1.2.1`](https://renovatebot.com/diffs/npm/@napi-rs%2fwasm-runtime/1.1.6/1.2.1) |  |  |  |  | | [degit](https://redirect.github.com/Rich-Harris/degit) | [`3.6.2` → `3.6.5`](https://renovatebot.com/diffs/npm/degit/3.6.2/3.6.5) |  |  |  |  | | [eslint](https://eslint.org) ([source](https://redirect.github.com/eslint/eslint)) | [`10.7.0` → `10.8.0`](https://renovatebot.com/diffs/npm/eslint/10.7.0/10.8.0) |  |  |  |  | | [playwright](https://playwright.dev) ([source](https://redirect.github.com/microsoft/playwright)) | [`1.61.1` → `1.62.1`](https://renovatebot.com/diffs/npm/playwright/1.61.1/1.62.1) |  |  |  |  | | [svelte](https://svelte.dev) ([source](https://redirect.github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.56.7` → `5.56.8`](https://renovatebot.com/diffs/npm/svelte/5.56.7/5.56.8) |  |  |  |  | --- ### Release Notes <details> <summary>napi-rs/napi-rs (@​napi-rs/wasm-runtime)</summary> ### [`v1.2.1`](https://redirect.github.com/napi-rs/napi-rs/blob/HEAD/wasm-runtime/CHANGELOG.md#121-2026-07-30) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.2.0...@napi-rs/wasm-runtime@1.2.1) ##### Bug Fixes - **wasm-runtime:** preserve emnapi v1 compatibility ([#​3426](https://redirect.github.com/napi-rs/napi-rs/issues/3426)) ([856326b](https://redirect.github.com/napi-rs/napi-rs/commit/856326b3051028b0d9992227f02167227f46b07b)) ### [`v1.2.0`](https://redirect.github.com/napi-rs/napi-rs/blob/HEAD/wasm-runtime/CHANGELOG.md#120-2026-07-28) [Compare Source](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.6...@napi-rs/wasm-runtime@1.2.0) ##### Features - **cli:** support non-threaded WASI targets ([#​3353](https://redirect.github.com/napi-rs/napi-rs/issues/3353)) ([b5ecfa3](https://redirect.github.com/napi-rs/napi-rs/commit/b5ecfa3d6a9fcf8e66c59e05dd4027b832b44d61)), closes [#​3352](https://redirect.github.com/napi-rs/napi-rs/issues/3352) [#​3400](https://redirect.github.com/napi-rs/napi-rs/issues/3400) [#​3405](https://redirect.github.com/napi-rs/napi-rs/issues/3405) #### [1.1.6](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.5...@napi-rs/wasm-runtime@1.1.6) (2026-06-24) **Note:** Version bump only for package [@​napi-rs/wasm-runtime](https://redirect.github.com/napi-rs/wasm-runtime) #### [1.1.5](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.4...@napi-rs/wasm-runtime@1.1.5) (2026-06-10) ##### Bug Fixes - **wasm-runtime:** preserve Buffer return values in fs-proxy ([#​3250](https://redirect.github.com/napi-rs/napi-rs/issues/3250)) ([c70f205](https://redirect.github.com/napi-rs/napi-rs/commit/c70f205a071c39acbd792b071bb95b2f5d2f39e8)) #### [1.1.4](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.3...@napi-rs/wasm-runtime@1.1.4) (2026-04-15) ##### Bug Fixes - **wasm-runtime:** preserve full bigint values in fs proxy codec ([#​3233](https://redirect.github.com/napi-rs/napi-rs/issues/3233)) ([0cb8eef](https://redirect.github.com/napi-rs/napi-rs/commit/0cb8eefad83938daa6a0f377f2708890d9f4b6bf)) #### [1.1.3](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.2...@napi-rs/wasm-runtime@1.1.3) (2026-04-08) **Note:** Version bump only for package [@​napi-rs/wasm-runtime](https://redirect.github.com/napi-rs/wasm-runtime) #### [1.1.2](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.1...@napi-rs/wasm-runtime@1.1.2) (2026-03-28) ##### Bug Fixes - ensure emnapi version is synced ([#​3151](https://redirect.github.com/napi-rs/napi-rs/issues/3151)) ([bc728d0](https://redirect.github.com/napi-rs/napi-rs/commit/bc728d0a289360b199d9dbd3b512a975f4d75d5a)) #### [1.1.1](https://redirect.github.com/napi-rs/napi-rs/compare/@napi-rs/wasm-runtime@1.1.0...@napi-rs/wasm-runtime@1.1.1) (2025-12-30) **Note:** Version bump only for package [@​napi-rs/wasm-runtime](https://redirect.github.com/napi-rs/wasm-runtime) </details> <details> <summary>Rich-Harris/degit (degit)</summary> ### [`v3.6.5`](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.4...v3.6.5) [Compare Source](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.4...v3.6.5) ### [`v3.6.4`](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.3...v3.6.4) [Compare Source](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.3...v3.6.4) ### [`v3.6.3`](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.2...v3.6.3) [Compare Source](https://redirect.github.com/Rich-Harris/degit/compare/v3.6.2...v3.6.3) </details> <details> <summary>eslint/eslint (eslint)</summary> ### [`v10.8.0`](https://redirect.github.com/eslint/eslint/releases/tag/v10.8.0) [Compare Source](https://redirect.github.com/eslint/eslint/compare/v10.7.0...v10.8.0) #### Features - [`2fee9bb`](https://redirect.github.com/eslint/eslint/commit/2fee9bb7416116cbed4d8c8100b1ae713b6356a1) feat: export `ConfigObject` from `eslint/config` ([#​21082](https://redirect.github.com/eslint/eslint/issues/21082)) (sethamus) #### Bug Fixes - [`6b8d2f7`](https://redirect.github.com/eslint/eslint/commit/6b8d2f7589b8a7c8b91b8ca2a2ef6d46178760d8) fix: escape reserved characters in rule id in `html` formatter ([#​21129](https://redirect.github.com/eslint/eslint/issues/21129)) (Francesco Trotta) - [`9091071`](https://redirect.github.com/eslint/eslint/commit/90910715011211a20d011d807d398a7005127f35) fix: prevent `no-unreachable-loop` crash when all loop types are ignored ([#​21116](https://redirect.github.com/eslint/eslint/issues/21116)) (Pixel) - [`e23fafe`](https://redirect.github.com/eslint/eslint/commit/e23fafe8d4b15355adef6cdebef414c3a2019454) fix: prefer-object-spread add semicolon when adding parenthesis ([#​21081](https://redirect.github.com/eslint/eslint/issues/21081)) (synthex-byte) - [`20b5ad0`](https://redirect.github.com/eslint/eslint/commit/20b5ad052360a443786a202e94624a3f81846511) fix: quadratic-time regex in `prefer-template` ([#​21096](https://redirect.github.com/eslint/eslint/issues/21096)) (Milos Djermanovic) - [`8b6f6c0`](https://redirect.github.com/eslint/eslint/commit/8b6f6c0b33411f34485512456d94f221daf7321f) fix: apply ignore configs to computed methods in class-methods-use-this ([#​21094](https://redirect.github.com/eslint/eslint/issues/21094)) (Pixel) - [`b2c608c`](https://redirect.github.com/eslint/eslint/commit/b2c608c014a396800a24a89343265d0616bee2d8) fix: NewExpression with parenthesized callee in `preserve-caught-error` ([#​21083](https://redirect.github.com/eslint/eslint/issues/21083)) (Francesco Trotta) #### Documentation - [`6ddf858`](https://redirect.github.com/eslint/eslint/commit/6ddf8587e5cfeeb7328332601bb76d98b8a198ea) docs: fix broken Specify Parser Options anchor link ([#​21106](https://redirect.github.com/eslint/eslint/issues/21106)) (Minsu) - [`784dfbe`](https://redirect.github.com/eslint/eslint/commit/784dfbe98f0222426fd200153a5ce2e5624151f0) docs: Clarify `no-eq-null` description ([#​21120](https://redirect.github.com/eslint/eslint/issues/21120)) (Park Harin) - [`7ec733a`](https://redirect.github.com/eslint/eslint/commit/7ec733a3ba5acb053a73ac656030a3879aa48fda) docs: Fix typos and grammar in glossary ([#​21095](https://redirect.github.com/eslint/eslint/issues/21095)) (Marry (Subin Yang)) - [`92bb13f`](https://redirect.github.com/eslint/eslint/commit/92bb13f13065db72d62454b9830d3ae52db15c76) docs: replace quake link ([#​21108](https://redirect.github.com/eslint/eslint/issues/21108)) (Jung Hyeon Jun) - [`68eb4a5`](https://redirect.github.com/eslint/eslint/commit/68eb4a57572409dbdcfeb3bc5c92ddbba46bc770) docs: fix broken Specify Globals anchor links in rule pages ([#​21103](https://redirect.github.com/eslint/eslint/issues/21103)) (Minsu) - [`d28f697`](https://redirect.github.com/eslint/eslint/commit/d28f697e0d32d7446c825fd89c362db028450134) docs: replace Code Climate CLI links with Qlty CLI links ([#​21099](https://redirect.github.com/eslint/eslint/issues/21099)) (Jung Hyeon Jun) - [`eccc68d`](https://redirect.github.com/eslint/eslint/commit/eccc68d42564e46bc9020d19ec52d2988f3b7bfa) docs: correct --suppressions-location option description ([#​21093](https://redirect.github.com/eslint/eslint/issues/21093)) (Ga eun Lee) - [`c5963f7`](https://redirect.github.com/eslint/eslint/commit/c5963f74bfa82a5b7ccc0607dcdcc695b8c97a31) docs: Update README (GitHub Actions Bot) #### Chores - [`4fbf46d`](https://redirect.github.com/eslint/eslint/commit/4fbf46d140120ae838db08cbe49544c985956490) test: pin `webpack` version to 5.108.4 ([#​21137](https://redirect.github.com/eslint/eslint/issues/21137)) (Francesco Trotta) - [`2d063e2`](https://redirect.github.com/eslint/eslint/commit/2d063e224e9bb0ccb0a28c120f7e380a8d62f320) chore: update HTTP URLs to HTTPS in JSDoc and comments ([#​21101](https://redirect.github.com/eslint/eslint/issues/21101)) (Bo Hyun Kim) - [`eccbe7b`](https://redirect.github.com/eslint/eslint/commit/eccbe7bc9257a6c99880468e452c8dfafbf47f74) test: add error locations to `no-class-assign` ([#​21123](https://redirect.github.com/eslint/eslint/issues/21123)) (devoil) - [`e7d1e43`](https://redirect.github.com/eslint/eslint/commit/e7d1e4373bf626f8da76d62ce024c28928bc99bc) ci: bump actions/setup-go from 6 to 7 ([#​21118](https://redirect.github.com/eslint/eslint/issues/21118)) (dependabot\[bot]) - [`e9d66d0`](https://redirect.github.com/eslint/eslint/commit/e9d66d0cde1a7752c29dee53ab7b62632835a87a) ci: bump actions/setup-node from 6 to 7 ([#​21119](https://redirect.github.com/eslint/eslint/issues/21119)) (dependabot\[bot]) - [`ee225b6`](https://redirect.github.com/eslint/eslint/commit/ee225b60c26f0cc5f4d71641888a5bd2ec5626d6) test: Add error location details to `no-eq-null` rule ([#​21117](https://redirect.github.com/eslint/eslint/issues/21117)) (Park Harin) - [`044a627`](https://redirect.github.com/eslint/eslint/commit/044a627fa3e28ee1410d515acc5378eb4b49f8ba) chore: update minimatch to ^10.2.5 ([#​21107](https://redirect.github.com/eslint/eslint/issues/21107)) (김채영) - [`fb09aa8`](https://redirect.github.com/eslint/eslint/commit/fb09aa8ff09730d3ccf68859e065f99666b52466) chore: update ecosystem plugins ([#​21115](https://redirect.github.com/eslint/eslint/issues/21115)) (ESLint Bot) - [`5abd878`](https://redirect.github.com/eslint/eslint/commit/5abd878740fe417fd8a910cac3741f2a0317b365) test: add error locations to `no-proto` ([#​21114](https://redirect.github.com/eslint/eslint/issues/21114)) (Gihyeon Jeong / 정기현) - [`9715887`](https://redirect.github.com/eslint/eslint/commit/9715887ec94a5ff936447d7b680d039fbe2f0541) test: Add error location details to `no-div-regex` ([#​21110](https://redirect.github.com/eslint/eslint/issues/21110)) (Park Harin) - [`a746ec6`](https://redirect.github.com/eslint/eslint/commit/a746ec6ea4a2249b1eb3f512264da5f4d5f7c886) test: add error locations to `no-new-wrappers` ([#​21109](https://redirect.github.com/eslint/eslint/issues/21109)) (Gihyeon Jeong / 정기현) - [`8dde645`](https://redirect.github.com/eslint/eslint/commit/8dde64570e240f3ef8af873d59a752db6d8519aa) test: add error locations to `no-ex-assign` ([#​21102](https://redirect.github.com/eslint/eslint/issues/21102)) (devoil) - [`13ab0ec`](https://redirect.github.com/eslint/eslint/commit/13ab0ec447650c079d8a5e5d67222f79e69c741e) test: add error locations to `no-label-var` ([#​21098](https://redirect.github.com/eslint/eslint/issues/21098)) (Gihyeon Jeong / 정기현) - [`a99906f`](https://redirect.github.com/eslint/eslint/commit/a99906ffb8342dde03336b5c6372b5658f21d5f2) test: Add error location details to `no-delete-var` rule ([#​21105](https://redirect.github.com/eslint/eslint/issues/21105)) (Park Harin) - [`c47e8dc`](https://redirect.github.com/eslint/eslint/commit/c47e8dc9bbfba797430db45ae08cb6f7392cfc9d) chore: add missing backticks to `languages/js/index.js` ([#​21104](https://redirect.github.com/eslint/eslint/issues/21104)) (beeen) - [`0174428`](https://redirect.github.com/eslint/eslint/commit/0174428dd7543d118cf67a823b98dd97032fe68c) chore: add missing backticks to `translate-cli-options.js` ([#​21097](https://redirect.github.com/eslint/eslint/issues/21097)) (dongkyu lee) - [`3d36589`](https://redirect.github.com/eslint/eslint/commit/3d36589a4917326500f0707bb41745160f387d00) chore: add missing backticks to `serialization.js` ([#​21091](https://redirect.github.com/eslint/eslint/issues/21091)) (이규환) - [`dcc9312`](https://redirect.github.com/eslint/eslint/commit/dcc9312c1081931de0fe1b555fbb6aa82fe696b9) test: add error locations to `eqeqeq` ([#​21090](https://redirect.github.com/eslint/eslint/issues/21090)) (Ga eun Lee) - [`2710b18`](https://redirect.github.com/eslint/eslint/commit/2710b182472cd1a95e4aff6186cb2c4fbadb4ee0) ci: Add explicit permissions to rebuild-docs-sites workflow ([#​21089](https://redirect.github.com/eslint/eslint/issues/21089)) (Marry (Subin Yang)) - [`5d2f866`](https://redirect.github.com/eslint/eslint/commit/5d2f8663ee60701e9036b0b0933a12efd9d93269) chore: update dependency prettier to v3.9.5 ([#​21086](https://redirect.github.com/eslint/eslint/issues/21086)) (renovate\[bot]) - [`d584e31`](https://redirect.github.com/eslint/eslint/commit/d584e31098f262563b97dc5d5f23a63a187879a0) chore: fix failing ecosystem test for `eslint-plugin-unicorn` ([#​21084](https://redirect.github.com/eslint/eslint/issues/21084)) (Francesco Trotta) - [`bf3eda0`](https://redirect.github.com/eslint/eslint/commit/bf3eda049bd690f1e9a5a0c4520a6329b3e4ec85) chore: update ecosystem plugins ([#​21079](https://redirect.github.com/eslint/eslint/issues/21079)) (ESLint Bot) </details> <details> <summary>microsoft/playwright (playwright)</summary> ### [`v1.62.1`](https://redirect.github.com/microsoft/playwright/compare/v1.62.0...26a9e470a7b3c7822084b09fb7f13902c5f37b51) [Compare Source](https://redirect.github.com/microsoft/playwright/compare/v1.62.0...v1.62.1) ### [`v1.62.0`](https://redirect.github.com/microsoft/playwright/releases/tag/v1.62.0) [Compare Source](https://redirect.github.com/microsoft/playwright/compare/v1.61.1...v1.62.0) ##### 🧱 New component testing model [Component testing](https://playwright.dev/docs/test-components) moves to a **stories and galleries** model. A **story** wraps your component in one specific scenario — hard-coded props, mock data, providers — and a **gallery** page that you serve renders stories on demand. The new [fixtures.mount()](https://playwright.dev/docs/api/class-fixtures#fixtures-mount) fixture navigates to the gallery, mounts a story by id, and returns a [Locator](https://playwright.dev/docs/api/class-locator) scoped to the story's root element: ```js test('click should expand', async ({ mount }) => { const component = await mount('components/Expandable/Stateful'); await component.getByRole('button').click(); await expect(component.getByTestId('expanded')).toHaveValue('true'); }); ``` Pass a story type as a template argument to type-check its props, and use `update(props)` / `unmount()` on the returned locator to re-render or tear down within a test. ##### 🛑 Cancel operations with AbortSignal Most operations and web-first assertions now accept a `signal` option that takes an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), letting you cancel long-running actions, navigations, waits, and assertions: ```js const controller = new AbortController(); setTimeout(() => controller.abort(), 1000); await page.getByRole('button', { name: 'Submit' }).click({ signal: controller.signal }); await expect(page.getByText('Done')).toBeVisible({ signal: controller.signal }); ``` Providing a signal does not disable the default timeout; pass `timeout: 0` to disable it. ##### 🖼️ WebP screenshots [expect(page).toHaveScreenshot()](https://playwright.dev/docs/api/class-pageassertions#page-assertions-to-have-screenshot-1) and [expect(locator).toHaveScreenshot()](https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-screenshot-1) can now store snapshots in the WebP format — just give the snapshot a `.webp` name: ```js // Visual comparisons store the golden snapshot as lossless WebP. await expect(page).toHaveScreenshot('homepage.webp'); // Standalone screenshots can trade quality for size with lossy WebP. await page.screenshot({ path: 'homepage.webp', quality: 50 }); ``` [page.screenshot()](https://playwright.dev/docs/api/class-page#page-screenshot) and [locator.screenshot()](https://playwright.dev/docs/api/class-locator#locator-screenshot) also accept `webp` as a `type`, where quality `100` (the default) is lossless and lower values use lossy compression. ##### 🧩 Custom test filtering with Reporter.preprocess() New [reporter.preprocess()](https://playwright.dev/docs/api/class-reporter#reporter-preprocess) hook runs after the configuration is resolved and before [reporter.onBegin()](https://playwright.dev/docs/api/class-reporter#reporter-on-begin), letting a reporter mark individual tests as skipped, excluded, fixed, or failing through a [TestRun](https://playwright.dev/docs/api/class-testrun) object: ```js class MyReporter { async preprocess({ config, suite, testRun }) { for (const test of suite.allTests()) { if (shouldSkip(test)) testRun.skip(test); } } } ``` ##### 🔁 Isolated retries New [testConfig.retryStrategy](https://playwright.dev/docs/api/class-testconfig#test-config-retry-strategy) controls when failed tests are retried. The default `'immediate'` retries as soon as a worker is free; `'isolated'` runs all retries at the end, one by one in a single worker, to minimize interference with the rest of the suite: ```js // playwright.config.ts export default defineConfig({ retries: 2, retryStrategy: 'isolated', }); ``` ##### New APIs ##### Browser and Context - New option [`credentials`](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state-option-credentials) includes the context's virtual WebAuthn [Credentials](https://playwright.dev/docs/api/class-credentials) (passkeys) in the storage state, so they can be persisted and re-seeded into later contexts. ##### Actions - New `scroll` option (`"auto"` | `"none"`) on actions to opt out of Playwright's automatic scroll-into-view. ##### Network - New [apiResponse.timing()](https://playwright.dev/docs/api/class-apiresponse#api-response-timing) returns resource timing information for an API response. ##### Evaluation - New [locator.waitForFunction()](https://playwright.dev/docs/api/class-locator#locator-wait-for-function) waits until a function — called with the matching element — returns a truthy value. - [page.evaluate()](https://playwright.dev/docs/api/class-page#page-evaluate) and related methods now accept functions as evaluate arguments. - [page.addInitScript()](https://playwright.dev/docs/api/class-page#page-add-init-script) / [browserContext.addInitScript()](https://playwright.dev/docs/api/class-browsercontext#browser-context-add-init-script) now accept functions as init-script arguments. ##### Command line & MCP - Playwright now bundles the [Playwright MCP](https://playwright.dev/docs/getting-started-mcp) server and [`playwright-cli`](https://playwright.dev/docs/getting-started-cli), runnable via `npx playwright mcp` and `npx playwright cli`. ##### Reporters - The HTML report's **Merge files** grouping — previously only a UI toggle — can now be enabled from the config with the new `mergeFiles` reporter option: ```js // playwright.config.ts export default defineConfig({ reporter: [['html', { mergeFiles: true }]], }); ``` ##### Announcements - ⚠️ Debian 11 is not supported anymore. ##### Browser Versions - Chromium 151.0.7922.34 - Mozilla Firefox 153.0 - WebKit 26.5 This version was also tested against the following stable channels: - Google Chrome 151 - Microsoft Edge 151 </details> <details> <summary>sveltejs/svelte (svelte)</summary> ### [`v5.56.8`](https://redirect.github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5568) [Compare Source](https://redirect.github.com/sveltejs/svelte/compare/svelte@5.56.7...svelte@5.56.8) ##### Patch Changes - fix: call `onerror` and provide a working `reset` when hydrating a failed boundary ([#​18556](https://redirect.github.com/sveltejs/svelte/pull/18556)) - fix: preserve select selection when spread attributes omit value ([#​18561](https://redirect.github.com/sveltejs/svelte/pull/18561)) </details> --- ### Configuration 📅 **Schedule**: (in timezone Asia/Shanghai) - Branch creation - "before 10am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/oxc-project/oxc). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zLjIiLCJ1cGRhdGVkSW5WZXIiOiI0NC4zLjIiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cameron <cameron.clark@hey.com> |
||
|
|
20f68e74a3 |
fix(oxlint,oxfmt): respect .gitignore for walk targets and explicit paths (#25133)
Fixes #25107. The `ignore` crate walker enforces gitignore's "everything under an ignored directory is ignored" rule only by pruning ignored directories during traversal. And it never filters its walk roots. So when a walk target (based on cwd or an explicit CLI target path) is already inside a gitignored tree, pruning never happens and a bare directory pattern like `generated` matches nothing file-level. (even though `git check-ignore` says they are ignored) See also BurntSushi/ripgrep#2595 ### Changes - oxc_config: added `GitignoreChecker` - a pre-walk check that replays git's resolution order over the ancestor chain - Matchers and `.git`/`.jj` probes are cached per directory - oxlint / oxfmt: filter all CLI targets through this check before walking ### Behavior changes - Explicitly passed gitignored files are now skipped (previously always linted/formatted) - `oxlint --no-ignore` no longer force-lints gitignored targets - The same behavior as `--no-ignore` does not disable during walk `.gitignore` <details> <summary>FYI: Current our ignore model overview</summary> Two independent families, AND-ed (a path excluded by any layer is skipped): | Layer | Source | oxlint | oxfmt | | -------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------- | | git-derived (always on, not user-disablable) | nested `.gitignore`, `.git/info/exclude` (during walk) | walker via shared `configure_walk_builder` | same (shared) | | | walk roots / explicit CLI targets | `GitignoreChecker` (this PR) | `GitignoreChecker` (this PR) | | tool-owned | ignore file | `.eslintignore` / `--ignore-path`; disabled by `--no-ignore` | `.prettierignore` / `--ignore-path`; no disable flag | | | CLI patterns | `--ignore-pattern`; disabled by `--no-ignore` | `!pattern` path args | | | config `ignorePatterns` | `LintIgnoreMatcher` (already containment-correct via `matched_path_or_any_parents`); always on | scope-local `is_path_ignored` (same); always on | NOTE: `ignorePatterns` never had this bug, only the walker-backed gitignore layer did; this PR brings it to the same level. A `!` negation in `ignorePatterns` cannot re-include gitignored paths (unchanged; the layers are independent). </details> |
||
|
|
5c0fa61ddb |
fix(linter/eslint/no-warning-comments): unify config structs and remove manual options docs (#25151)
this PR unifies 2 config structs for `eslint/no-warning-comments` and removes the manually written options doc, related issue: #14743. Doc after fix: <img width="919" height="1295" alt="image" src="https://github.com/user-attachments/assets/dab8f999-fffa-4a77-8d6d-39746cfa3860" /> |
||
|
|
067da8c4e7 |
feat(ast)!: store single parameter in TSIndexSignature::parameter (#25154)
|
||
|
|
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
|
||
|
|
ee47f21ce4 |
refactor(oxfmt): move Doc-to-IR postprocess() under prettier_compat (#25141)
Just relocated. |
||
|
|
acfbf46767 |
refactor(formatter_core): add CoreFormatOptions and FormatOptions::apply_core (#25138)
These common, core format options: - LineEnding - LineWidth - IndentWidth - IndentStyle should be defined in core, and to be shared. |
||
|
|
9dc7756729 |
docs(linter/typescript/no-unnecessary-condition): clarify options (#25110)
## Summary - document the exact allowConstantLoopConditions behavior, including the four directly allowed literals - explain how always differs from only-allowed-literals - add a checkTypePredicates example and regenerate the published configuration schema Closes #25098 |
||
|
|
a9e76e9e1f |
fix(oxfmt): rework tsx-in-vue support (#25106)
Follow-up to #25063. tsx-in-vue was supported in #25063 with a parse-retry (ts fails → retry as tsx), chosen to keep the common ts path single-parse. However, this leaves a hole: a `lang="tsx"` block without JSX parses fine as plain ts, so the retry never fires and the lone generic comma is removed: ```vue <script setup lang="tsx"> const identity = <T,>(x: T) => x; // → <T>(x: T) => x, breaks as TS17008 </script> ``` Since parse failure fundamentally cannot detect this, the only fix is to scan the `lang` declaration itself. The JS host now scans the SFC once per file and passes `_oxfmtVueScriptLang: "tsx"` down to `textToDoc()`. This makes the retry obsolete, so it is removed. This PR also settles the comma policy: keep it only where it is load-bearing. - JSX grammar (`<T>` would be a JSX opener) - and `.mts/.cts` (TS7060, #18928) and remove it everywhere else, keyed on the grammar, regardless of context. Deliberate divergence: Prettier keys on filepath and keeps the comma in ts-in-vue/svelte (while its own ts-in-md embed uses `dummy.ts` as `filepath` and removes it). Plain-TS sources now behave identically wherever they live. |