From 49a6f9738874228adcb2a0930ad7d7d7698804c5 Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Tue, 6 May 2025 02:34:24 +0000 Subject: [PATCH] perf(napi/parser): faster fixup of `BigInt`s and `RegExp`s (#10820) #10791 fixed the performance of `oxc-parser` NPM package by removing the reviver from `JSON.parse` call. However, it replaced it with a complete traversal of the AST on JS side to locate `Literal` nodes and update them. Instead, generate a list of paths to `Literal` nodes needing fixing on Rust side in `ESTree` serializer. e.g. for this program: ```js 123n; foo(/xyz/); ``` the fix paths are: ```json [ ["body", 0, "expression"], ["body", 1, "expression", "arguments", 2] ] ``` Having the location of nodes which need updating reduces work on JS side, as no need to search the entire AST. Running `pnpm run bench` (in `napi/parser`) locally shows between 8% and 15% speed-up from this change. --- crates/oxc_ast/src/serialize.rs | 50 +++++-- crates/oxc_estree/Cargo.toml | 2 +- crates/oxc_estree/src/serialize/config.rs | 30 ++++ crates/oxc_estree/src/serialize/mod.rs | 124 +++++++++++++++- crates/oxc_estree/src/serialize/sequences.rs | 44 ++++-- crates/oxc_estree/src/serialize/structs.rs | 41 +++++- napi/parser/generate-visitor-keys.mjs | 29 ---- napi/parser/generated/visitor-keys.cjs | 143 ------------------- napi/parser/generated/visitor-keys.mjs | 143 ------------------- napi/parser/package.json | 5 +- napi/parser/src/lib.rs | 8 +- napi/parser/src/types.rs | 4 +- napi/parser/wrap.cjs | 45 +++--- napi/parser/wrap.mjs | 48 +++---- pnpm-lock.yaml | 38 +---- tasks/benchmark/benches/parser.rs | 2 +- tasks/coverage/src/driver.rs | 2 +- 17 files changed, 309 insertions(+), 449 deletions(-) delete mode 100644 napi/parser/generate-visitor-keys.mjs delete mode 100644 napi/parser/generated/visitor-keys.cjs delete mode 100644 napi/parser/generated/visitor-keys.mjs diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index 4b635c50de281..df8bb30215c3a 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -5,20 +5,22 @@ use cow_utils::CowUtils; use crate::ast::*; use oxc_ast_macros::ast_meta; use oxc_estree::{ - CompactJSSerializer, CompactTSSerializer, ESTree, FlatStructSerializer, JsonSafeString, - LoneSurrogatesString, PrettyJSSerializer, PrettyTSSerializer, SequenceSerializer, Serializer, - StructSerializer, ser::AppendToConcat, + CompactFixesJSSerializer, CompactFixesTSSerializer, CompactJSSerializer, CompactTSSerializer, + ESTree, FlatStructSerializer, JsonSafeString, LoneSurrogatesString, PrettyFixesJSSerializer, + PrettyFixesTSSerializer, PrettyJSSerializer, PrettyTSSerializer, SequenceSerializer, + Serializer, StructSerializer, ser::AppendToConcat, }; use oxc_span::GetSpan; /// Main serialization methods for `Program`. /// -/// Note: 4 separate methods for the different serialization options, rather than 1 method -/// with behavior controlled by flags (e.g. `fn to_estree_json(&self, with_ts: bool, pretty: bool`) +/// Note: 8 separate methods for the different serialization options, rather than 1 method +/// with behavior controlled by flags +/// (e.g. `fn to_estree_json(&self, with_ts: bool, pretty: bool, fixes: bool)`) /// to avoid bloating binary size. /// /// Most consumers (and Oxc crates) will use only 1 of these methods, so we don't want to needlessly -/// compile all 4 serializers when only 1 is used. +/// compile all 8 serializers when only 1 is used. /// /// Initial capacity for serializer's buffer is an estimate based on our benchmark fixtures /// of ratio of source text size to JSON size. @@ -70,6 +72,34 @@ impl Program<'_> { self.serialize(&mut serializer); serializer.into_string() } + + /// Serialize AST to ESTree JSON, including TypeScript fields, with list of fixes. + pub fn to_estree_ts_json_with_fixes(&self) -> String { + let capacity = self.source_text.len() * JSON_CAPACITY_RATIO_COMPACT; + let serializer = CompactFixesTSSerializer::with_capacity(capacity); + serializer.serialize_with_fixes(self) + } + + /// Serialize AST to ESTree JSON, without TypeScript fields, with list of fixes. + pub fn to_estree_js_json_with_fixes(&self) -> String { + let capacity = self.source_text.len() * JSON_CAPACITY_RATIO_COMPACT; + let serializer = CompactFixesJSSerializer::with_capacity(capacity); + serializer.serialize_with_fixes(self) + } + + /// Serialize AST to pretty-printed ESTree JSON, including TypeScript fields, with list of fixes. + pub fn to_pretty_estree_ts_json_with_fixes(&self) -> String { + let capacity = self.source_text.len() * JSON_CAPACITY_RATIO_PRETTY; + let serializer = PrettyFixesTSSerializer::with_capacity(capacity); + serializer.serialize_with_fixes(self) + } + + /// Serialize AST to pretty-printed ESTree JSON, without TypeScript fields, with list of fixes. + pub fn to_pretty_estree_js_json_with_fixes(&self) -> String { + let capacity = self.source_text.len() * JSON_CAPACITY_RATIO_PRETTY; + let serializer = PrettyFixesJSSerializer::with_capacity(capacity); + serializer.serialize_with_fixes(self) + } } // -------------------- @@ -409,7 +439,9 @@ impl ESTree for BigIntLiteralBigint<'_, '_> { pub struct BigIntLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b BigIntLiteral<'a>); impl ESTree for BigIntLiteralValue<'_, '_> { - fn serialize(&self, serializer: S) { + fn serialize(&self, mut serializer: S) { + // Record that this node needs fixing on JS side + serializer.record_fix_path(); Null(()).serialize(serializer); } } @@ -431,7 +463,9 @@ impl ESTree for BigIntLiteralValue<'_, '_> { pub struct RegExpLiteralValue<'a, 'b>(#[expect(dead_code)] pub &'b RegExpLiteral<'a>); impl ESTree for RegExpLiteralValue<'_, '_> { - fn serialize(&self, serializer: S) { + fn serialize(&self, mut serializer: S) { + // Record that this node needs fixing on JS side + serializer.record_fix_path(); Null(()).serialize(serializer); } } diff --git a/crates/oxc_estree/Cargo.toml b/crates/oxc_estree/Cargo.toml index 5c48879fb22f3..d03161e8976b2 100644 --- a/crates/oxc_estree/Cargo.toml +++ b/crates/oxc_estree/Cargo.toml @@ -19,7 +19,7 @@ workspace = true doctest = false [dependencies] -oxc_data_structures = { workspace = true, features = ["code_buffer"], optional = true } +oxc_data_structures = { workspace = true, features = ["code_buffer", "stack"], optional = true } itoa = { workspace = true, optional = true } ryu-js = { workspace = true, optional = true } diff --git a/crates/oxc_estree/src/serialize/config.rs b/crates/oxc_estree/src/serialize/config.rs index 02643dde1bf35..6291480fd1ed5 100644 --- a/crates/oxc_estree/src/serialize/config.rs +++ b/crates/oxc_estree/src/serialize/config.rs @@ -2,6 +2,8 @@ pub trait Config { /// `true` if output should contain TS fields const INCLUDE_TS_FIELDS: bool; + /// `true` if should record paths to `Literal` nodes that need fixing on JS side + const FIXES: bool; fn new() -> Self; } @@ -11,6 +13,7 @@ pub struct ConfigTS; impl Config for ConfigTS { const INCLUDE_TS_FIELDS: bool = true; + const FIXES: bool = false; #[inline(always)] fn new() -> Self { @@ -23,6 +26,33 @@ pub struct ConfigJS; impl Config for ConfigJS { const INCLUDE_TS_FIELDS: bool = false; + const FIXES: bool = false; + + #[inline(always)] + fn new() -> Self { + Self + } +} + +/// Config for serializing AST with TypeScript fields, with fixes. +pub struct ConfigFixesTS; + +impl Config for ConfigFixesTS { + const INCLUDE_TS_FIELDS: bool = true; + const FIXES: bool = true; + + #[inline(always)] + fn new() -> Self { + Self + } +} + +/// Config for serializing AST without TypeScript fields, with fixes. +pub struct ConfigFixesJS; + +impl Config for ConfigFixesJS { + const INCLUDE_TS_FIELDS: bool = false; + const FIXES: bool = true; #[inline(always)] fn new() -> Self { diff --git a/crates/oxc_estree/src/serialize/mod.rs b/crates/oxc_estree/src/serialize/mod.rs index c07475f76355f..1905e137165a4 100644 --- a/crates/oxc_estree/src/serialize/mod.rs +++ b/crates/oxc_estree/src/serialize/mod.rs @@ -1,7 +1,11 @@ // Methods which are trivial or just delegate to other methods are marked `#[inline(always)]` #![expect(clippy::inline_always)] -use oxc_data_structures::code_buffer::CodeBuffer; +use std::mem; + +use itoa::Buffer as ItoaBuffer; + +use oxc_data_structures::{code_buffer::CodeBuffer, stack::NonEmptyStack}; mod blanket; mod config; @@ -10,7 +14,7 @@ mod primitives; mod sequences; mod strings; mod structs; -use config::{Config, ConfigJS, ConfigTS}; +use config::{Config, ConfigFixesJS, ConfigFixesTS, ConfigJS, ConfigTS}; use formatter::{CompactFormatter, Formatter, PrettyFormatter}; use sequences::ESTreeSequenceSerializer; use structs::ESTreeStructSerializer; @@ -43,6 +47,13 @@ pub trait Serializer: SerializerPrivate { /// Serialize sequence. fn serialize_sequence(self) -> Self::SequenceSerializer; + + /// Record path to current node in `fixes_buffer`. + /// + /// Used by serializers for the `value` field of `BigIntLiteral` and `RegExpLiteral`. + /// These nodes cannot be serialized to JSON, because JSON doesn't support `BigInt`s or `RegExp`s. + /// "Fix paths" can be used on JS side to locate these nodes and set their `value` fields correctly. + fn record_fix_path(&mut self); } /// Trait containing internal methods of [`Serializer`]s that we don't want to expose outside this crate. @@ -69,10 +80,24 @@ pub type PrettyTSSerializer = ESTreeSerializer; /// ESTree serializer which produces pretty JSON, excluding TypeScript fields. pub type PrettyJSSerializer = ESTreeSerializer; +/// ESTree serializer which produces compact JSON, including TypeScript fields. +pub type CompactFixesTSSerializer = ESTreeSerializer; + +/// ESTree serializer which produces compact JSON, excluding TypeScript fields. +pub type CompactFixesJSSerializer = ESTreeSerializer; + +/// ESTree serializer which produces pretty JSON, including TypeScript fields. +pub type PrettyFixesTSSerializer = ESTreeSerializer; + +/// ESTree serializer which produces pretty JSON, excluding TypeScript fields. +pub type PrettyFixesJSSerializer = ESTreeSerializer; + /// ESTree serializer. pub struct ESTreeSerializer { buffer: CodeBuffer, formatter: F, + trace_path: NonEmptyStack, + fixes_buffer: CodeBuffer, #[expect(unused)] config: C, } @@ -80,12 +105,56 @@ pub struct ESTreeSerializer { impl ESTreeSerializer { /// Create new [`ESTreeSerializer`]. pub fn new() -> Self { - Self { buffer: CodeBuffer::new(), formatter: F::new(), config: C::new() } + Self { + buffer: CodeBuffer::new(), + formatter: F::new(), + trace_path: NonEmptyStack::new(TracePathPart::Index(0)), + fixes_buffer: CodeBuffer::new(), + config: C::new(), + } } /// Create new [`ESTreeSerializer`] with specified buffer capacity. pub fn with_capacity(capacity: usize) -> Self { - Self { buffer: CodeBuffer::with_capacity(capacity), formatter: F::new(), config: C::new() } + Self { + buffer: CodeBuffer::with_capacity(capacity), + formatter: F::new(), + trace_path: NonEmptyStack::new(TracePathPart::Index(0)), + fixes_buffer: CodeBuffer::new(), + config: C::new(), + } + } + + /// Serialize `node` and output a `JSON` string containing + /// `{ "node": { ... }, "fixes": [ ... ]}`, where `node` is the serialized AST node, + /// and `fixes` is a list of paths to any `Literal`s which are `BigInt`s or `RegExp`s. + /// + /// The `value` field of these nodes cannot be serialized to JSON, because JSON doesn't support + /// `BigInt`s or `RegExp`s. The `fixes` paths can be used on JS side to locate these nodes + /// and set their `value` fields correctly. + pub fn serialize_with_fixes(mut self, node: &T) -> String { + const { + assert!( + C::FIXES, + "Cannot call `serialize_with_fixes` on a serializer without fixes enabled" + ); + } + + self.buffer.print_str(r#"{"node":"#); + + node.serialize(&mut self); + + debug_assert_eq!(self.trace_path.len(), 1); + debug_assert_eq!(self.trace_path[0], TracePathPart::DUMMY); + + self.buffer.print_str(r#","fixes":["#); + if !self.fixes_buffer.is_empty() { + let traces_buffer = mem::take(&mut self.fixes_buffer).into_string(); + self.buffer.print_str(&traces_buffer[1..]); + } + self.buffer.print_str("]}"); + + self.buffer.into_string() } /// Consume this [`ESTreeSerializer`] and convert buffer to string. @@ -119,6 +188,42 @@ impl<'s, C: Config, F: Formatter> Serializer for &'s mut ESTreeSerializer fn serialize_sequence(self) -> ESTreeSequenceSerializer<'s, C, F> { ESTreeSequenceSerializer::new(self) } + + /// Record path to current node in `fixes_buffer`. + /// + /// Used by serializers for the `value` field of `BigIntLiteral` and `RegExpLiteral`. + /// These nodes cannot be serialized to JSON, because JSON doesn't support `BigInt`s or `RegExp`s. + /// "Fix paths" can be used on JS side to locate these nodes and set their `value` fields correctly. + fn record_fix_path(&mut self) { + if !C::FIXES { + return; + } + + self.fixes_buffer.print_str(",["); + + // First part is a dummy, last part is `"value"`, so skip them + let parts = self.trace_path.as_slice(); + let parts = &parts[1..parts.len() - 1]; + for (index, part) in parts.iter().enumerate() { + if index > 0 { + self.fixes_buffer.print_ascii_byte(b','); + } + match *part { + TracePathPart::Key(key) => { + self.fixes_buffer.print_ascii_byte(b'"'); + self.fixes_buffer.print_str(key); + self.fixes_buffer.print_ascii_byte(b'"'); + } + TracePathPart::Index(index) => { + let mut buffer = ItoaBuffer::new(); + let s = buffer.format(index); + self.fixes_buffer.print_str(s); + } + } + } + + self.fixes_buffer.print_ascii_byte(b']'); + } } impl SerializerPrivate for &mut ESTreeSerializer { @@ -136,3 +241,14 @@ impl SerializerPrivate for &mut ESTreeSerializer (&mut self.buffer, &mut self.formatter) } } + +/// Element of a trace path. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TracePathPart { + Key(&'static str), + Index(usize), +} + +impl TracePathPart { + pub const DUMMY: Self = TracePathPart::Index(0); +} diff --git a/crates/oxc_estree/src/serialize/sequences.rs b/crates/oxc_estree/src/serialize/sequences.rs index 142aeb6916f5f..445411842ed9a 100644 --- a/crates/oxc_estree/src/serialize/sequences.rs +++ b/crates/oxc_estree/src/serialize/sequences.rs @@ -1,4 +1,6 @@ -use super::{Config, ESTree, ESTreeSerializer, Formatter, Serializer, SerializerPrivate}; +use super::{ + Config, ESTree, ESTreeSerializer, Formatter, Serializer, SerializerPrivate, TracePathPart, +}; /// Trait for sequence serializers. pub trait SequenceSerializer { @@ -15,25 +17,35 @@ pub trait SequenceSerializer { pub struct ESTreeSequenceSerializer<'s, C: Config, F: Formatter> { /// Serializer serializer: &'s mut ESTreeSerializer, - /// State of sequence. - /// Starts as `SequenceState::Empty`, transitions to `SequenceState::HasEntries` on first entry. - state: SequenceState, + /// Length of sequence + len: usize, } impl<'s, C: Config, F: Formatter> ESTreeSequenceSerializer<'s, C, F> { /// Create new [`ESTreeSequenceSerializer`]. pub(super) fn new(mut serializer: &'s mut ESTreeSerializer) -> Self { + // Push item to `trace_path`. It will be replaced with a `TracePathPart::Index` + // when serializing each item in the sequence, and popped off again in `end` method. + if C::FIXES { + serializer.trace_path.push(TracePathPart::DUMMY); + } + serializer.buffer_mut().print_ascii_byte(b'['); - Self { serializer, state: SequenceState::Empty } + + Self { serializer, len: 0 } } } impl SequenceSerializer for ESTreeSequenceSerializer<'_, C, F> { /// Serialize sequence entry. fn serialize_element(&mut self, value: &T) { + // Update last item in trace path to current sequence index + if C::FIXES { + *self.serializer.trace_path.last_mut() = TracePathPart::Index(self.len); + } + let (buffer, formatter) = self.serializer.buffer_and_formatter_mut(); - if self.state == SequenceState::Empty { - self.state = SequenceState::HasEntries; + if self.len == 0 { formatter.before_first_element(buffer); } else { buffer.print_ascii_byte(b','); @@ -41,25 +53,27 @@ impl SequenceSerializer for ESTreeSequenceSerializer<'_ } value.serialize(&mut *self.serializer); + self.len += 1; } /// Finish serializing sequence. fn end(mut self) { + // Pop entry for this sequence from `trace_path` + if C::FIXES { + // SAFETY: `trace_path` is pushed to in `new`, which is only way to create an `ESTreeSequenceSerializer`. + // This method consumes the `ESTreeSequenceSerializer`, so this method can't be called more + // times than `new`. So there must be an item to pop. + unsafe { self.serializer.trace_path.pop_unchecked() }; + } + let (buffer, formatter) = self.serializer.buffer_and_formatter_mut(); - if self.state == SequenceState::HasEntries { + if self.len > 0 { formatter.after_last_element(buffer); } buffer.print_ascii_byte(b']'); } } -/// State of [`ESTreeSequenceSerializer`]. -#[derive(Clone, Copy, PartialEq, Eq)] -enum SequenceState { - Empty, - HasEntries, -} - /// [`ESTree`] implementation for slices. impl ESTree for &[T] { fn serialize(&self, serializer: S) { diff --git a/crates/oxc_estree/src/serialize/structs.rs b/crates/oxc_estree/src/serialize/structs.rs index 46f62b1f9ea9c..023713d6640ca 100644 --- a/crates/oxc_estree/src/serialize/structs.rs +++ b/crates/oxc_estree/src/serialize/structs.rs @@ -2,7 +2,7 @@ use oxc_data_structures::code_buffer::CodeBuffer; use super::{ Config, ESTree, ESTreeSequenceSerializer, ESTreeSerializer, Formatter, Serializer, - SerializerPrivate, + SerializerPrivate, TracePathPart, }; /// Trait for struct serializers. @@ -13,7 +13,7 @@ pub trait StructSerializer { /// Serialize struct field. /// /// `key` must not contain any characters which require escaping in JSON. - fn serialize_field(&mut self, key: &str, value: &T); + fn serialize_field(&mut self, key: &'static str, value: &T); /// Serialize struct field which is TypeScript syntax. /// @@ -24,7 +24,7 @@ pub trait StructSerializer { /// i.e. the field is skipped. /// /// `key` must not contain any characters which require escaping in JSON. - fn serialize_ts_field(&mut self, key: &str, value: &T); + fn serialize_ts_field(&mut self, key: &'static str, value: &T); /// Finish serializing struct. fn end(self); @@ -44,7 +44,14 @@ pub struct ESTreeStructSerializer<'s, C: Config, F: Formatter> { impl<'s, C: Config, F: Formatter> ESTreeStructSerializer<'s, C, F> { /// Create new [`ESTreeStructSerializer`]. pub(super) fn new(mut serializer: &'s mut ESTreeSerializer) -> Self { + // Push item to `trace_path`. It will be replaced with a `TracePathPart::Key` + // when serializing each field in the struct, and popped off again in `end` method. + if C::FIXES { + serializer.trace_path.push(TracePathPart::DUMMY); + } + serializer.buffer_mut().print_ascii_byte(b'{'); + Self { serializer, state: StructState::Empty } } } @@ -56,7 +63,12 @@ impl StructSerializer for ESTreeStructSerializer<'_, C, /// Serialize struct field. /// /// `key` must not contain any characters which require escaping in JSON. - fn serialize_field(&mut self, key: &str, value: &T) { + fn serialize_field(&mut self, key: &'static str, value: &T) { + // Update last item in trace path to current key + if C::FIXES { + *self.serializer.trace_path.last_mut() = TracePathPart::Key(key); + } + let (buffer, formatter) = self.serializer.buffer_and_formatter_mut(); if self.state == StructState::Empty { self.state = StructState::HasFields; @@ -83,7 +95,7 @@ impl StructSerializer for ESTreeStructSerializer<'_, C, /// /// `key` must not contain any characters which require escaping in JSON. #[inline(always)] - fn serialize_ts_field(&mut self, key: &str, value: &T) { + fn serialize_ts_field(&mut self, key: &'static str, value: &T) { if C::INCLUDE_TS_FIELDS { self.serialize_field(key, value); } @@ -92,6 +104,15 @@ impl StructSerializer for ESTreeStructSerializer<'_, C, /// Finish serializing struct. fn end(self) { let mut serializer = self.serializer; + + // Pop entry for this struct from `trace_path` + if C::FIXES { + // SAFETY: `trace_path` is pushed to in `new`, which is only way to create an `ESTreeStructSerializer`. + // This method consumes the `ESTreeStructSerializer`, so this method can't be called more + // times than `new`. So there must be an item to pop. + unsafe { serializer.trace_path.pop_unchecked() }; + } + let (buffer, formatter) = serializer.buffer_and_formatter_mut(); if self.state == StructState::HasFields { formatter.after_last_element(buffer); @@ -161,6 +182,12 @@ impl<'p, P: StructSerializer> Serializer for FlatStructSerializer<'p, P> { panic!("Cannot flatten a sequence into a struct"); } } + + fn record_fix_path(&mut self) { + const { + panic!("Cannot call `record_fix_path` on a `FlatStructSerializer`"); + } + } } impl SerializerPrivate for FlatStructSerializer<'_, P> { @@ -187,7 +214,7 @@ impl StructSerializer for FlatStructSerializer<'_, P> { /// /// `key` must not contain any characters which require escaping in JSON. #[inline(always)] - fn serialize_field(&mut self, key: &str, value: &T) { + fn serialize_field(&mut self, key: &'static str, value: &T) { // Delegate to parent `StructSerializer` self.0.serialize_field(key, value); } @@ -202,7 +229,7 @@ impl StructSerializer for FlatStructSerializer<'_, P> { /// /// `key` must not contain any characters which require escaping in JSON. #[inline(always)] - fn serialize_ts_field(&mut self, key: &str, value: &T) { + fn serialize_ts_field(&mut self, key: &'static str, value: &T) { // Delegate to parent `StructSerializer` self.0.serialize_ts_field(key, value); } diff --git a/napi/parser/generate-visitor-keys.mjs b/napi/parser/generate-visitor-keys.mjs deleted file mode 100644 index 7c52887e06276..0000000000000 --- a/napi/parser/generate-visitor-keys.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import { visitorKeys as visitorKeysOriginal } from '@typescript-eslint/visitor-keys'; -import { writeFileSync } from 'node:fs'; -import { join as pathJoin } from 'node:path'; - -const PATH_CJS = pathJoin(import.meta.dirname, 'generated/visitor-keys.cjs'); -const PATH_MJS = pathJoin(import.meta.dirname, 'generated/visitor-keys.mjs'); - -// Add keys for `ParenthesizedExpression` and `TSParenthesizedType`, which TS-ESLint doesn't have -const visitorKeys = { - ...visitorKeysOriginal, - ParenthesizedExpression: ['expression'], - TSParenthesizedType: ['typeAnnotation'], -}; - -const keys = Object.entries(visitorKeys) - .filter(([, v]) => v?.length) - .map(([k, v]) => ` ${k}: [${v.map((v) => `'${v}'`).join(', ')}],`) - .join('\n'); - -const code = `// Auto-generated code, DO NOT EDIT DIRECTLY! -// To edit this generated file you have to edit \`napi/parser/generate-visitor-keys.mjs\`. - -const visitorKeys = { -${keys} -}; -`; - -writeFileSync(PATH_CJS, `${code}module.exports = visitorKeys;\n`); -writeFileSync(PATH_MJS, `${code}export default visitorKeys;\n`); diff --git a/napi/parser/generated/visitor-keys.cjs b/napi/parser/generated/visitor-keys.cjs deleted file mode 100644 index f8eac92f2ebca..0000000000000 --- a/napi/parser/generated/visitor-keys.cjs +++ /dev/null @@ -1,143 +0,0 @@ -// Auto-generated code, DO NOT EDIT DIRECTLY! -// To edit this generated file you have to edit `napi/parser/generate-visitor-keys.mjs`. - -const visitorKeys = { - ArrayExpression: ['elements'], - ArrayPattern: ['decorators', 'elements', 'typeAnnotation'], - ArrowFunctionExpression: ['typeParameters', 'params', 'returnType', 'body'], - AssignmentExpression: ['left', 'right'], - AssignmentPattern: ['decorators', 'left', 'right', 'typeAnnotation'], - AwaitExpression: ['argument'], - BinaryExpression: ['left', 'right'], - BlockStatement: ['body'], - BreakStatement: ['label'], - CallExpression: ['callee', 'typeArguments', 'arguments'], - CatchClause: ['param', 'body'], - ChainExpression: ['expression'], - ClassBody: ['body'], - ClassDeclaration: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], - ClassExpression: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], - ConditionalExpression: ['test', 'consequent', 'alternate'], - ContinueStatement: ['label'], - DoWhileStatement: ['body', 'test'], - ExperimentalRestProperty: ['argument'], - ExperimentalSpreadProperty: ['argument'], - ExportAllDeclaration: ['exported', 'source', 'attributes'], - ExportDefaultDeclaration: ['declaration'], - ExportNamedDeclaration: ['declaration', 'specifiers', 'source', 'attributes'], - ExportSpecifier: ['exported', 'local'], - ExpressionStatement: ['expression'], - ForInStatement: ['left', 'right', 'body'], - ForOfStatement: ['left', 'right', 'body'], - ForStatement: ['init', 'test', 'update', 'body'], - FunctionDeclaration: ['id', 'typeParameters', 'params', 'returnType', 'body'], - FunctionExpression: ['id', 'typeParameters', 'params', 'returnType', 'body'], - Identifier: ['decorators', 'typeAnnotation'], - IfStatement: ['test', 'consequent', 'alternate'], - ImportAttribute: ['key', 'value'], - ImportDeclaration: ['specifiers', 'source', 'attributes'], - ImportDefaultSpecifier: ['local'], - ImportExpression: ['source', 'options'], - ImportNamespaceSpecifier: ['local'], - ImportSpecifier: ['imported', 'local'], - JSXAttribute: ['name', 'value'], - JSXClosingElement: ['name'], - JSXElement: ['openingElement', 'children', 'closingElement'], - JSXExpressionContainer: ['expression'], - JSXFragment: ['openingFragment', 'children', 'closingFragment'], - JSXMemberExpression: ['object', 'property'], - JSXNamespacedName: ['namespace', 'name'], - JSXOpeningElement: ['name', 'typeArguments', 'attributes'], - JSXSpreadAttribute: ['argument'], - JSXSpreadChild: ['expression'], - LabeledStatement: ['label', 'body'], - LogicalExpression: ['left', 'right'], - MemberExpression: ['object', 'property'], - MetaProperty: ['meta', 'property'], - MethodDefinition: ['decorators', 'key', 'value'], - NewExpression: ['callee', 'typeArguments', 'arguments'], - ObjectExpression: ['properties'], - ObjectPattern: ['decorators', 'properties', 'typeAnnotation'], - Program: ['body'], - Property: ['key', 'value'], - PropertyDefinition: ['decorators', 'key', 'typeAnnotation', 'value'], - RestElement: ['decorators', 'argument', 'typeAnnotation'], - ReturnStatement: ['argument'], - SequenceExpression: ['expressions'], - SpreadElement: ['argument'], - StaticBlock: ['body'], - SwitchCase: ['test', 'consequent'], - SwitchStatement: ['discriminant', 'cases'], - TaggedTemplateExpression: ['tag', 'typeArguments', 'quasi'], - TemplateLiteral: ['quasis', 'expressions'], - ThrowStatement: ['argument'], - TryStatement: ['block', 'handler', 'finalizer'], - UnaryExpression: ['argument'], - UpdateExpression: ['argument'], - VariableDeclaration: ['declarations'], - VariableDeclarator: ['id', 'init'], - WhileStatement: ['test', 'body'], - WithStatement: ['object', 'body'], - YieldExpression: ['argument'], - AccessorProperty: ['decorators', 'key', 'typeAnnotation', 'value'], - Decorator: ['expression'], - TSAbstractAccessorProperty: ['decorators', 'key', 'typeAnnotation'], - TSAbstractMethodDefinition: ['key', 'value'], - TSAbstractPropertyDefinition: ['decorators', 'key', 'typeAnnotation'], - TSArrayType: ['elementType'], - TSAsExpression: ['expression', 'typeAnnotation'], - TSCallSignatureDeclaration: ['typeParameters', 'params', 'returnType'], - TSClassImplements: ['expression', 'typeArguments'], - TSConditionalType: ['checkType', 'extendsType', 'trueType', 'falseType'], - TSConstructorType: ['typeParameters', 'params', 'returnType'], - TSConstructSignatureDeclaration: ['typeParameters', 'params', 'returnType'], - TSDeclareFunction: ['id', 'typeParameters', 'params', 'returnType', 'body'], - TSEmptyBodyFunctionExpression: ['id', 'typeParameters', 'params', 'returnType'], - TSEnumBody: ['members'], - TSEnumDeclaration: ['id', 'body'], - TSEnumMember: ['id', 'initializer'], - TSExportAssignment: ['expression'], - TSExternalModuleReference: ['expression'], - TSFunctionType: ['typeParameters', 'params', 'returnType'], - TSImportEqualsDeclaration: ['id', 'moduleReference'], - TSImportType: ['argument', 'qualifier', 'typeArguments', 'options'], - TSIndexedAccessType: ['indexType', 'objectType'], - TSIndexSignature: ['parameters', 'typeAnnotation'], - TSInferType: ['typeParameter'], - TSInstantiationExpression: ['expression', 'typeArguments'], - TSInterfaceBody: ['body'], - TSInterfaceDeclaration: ['id', 'typeParameters', 'extends', 'body'], - TSInterfaceHeritage: ['expression', 'typeArguments'], - TSIntersectionType: ['types'], - TSLiteralType: ['literal'], - TSMappedType: ['key', 'constraint', 'nameType', 'typeAnnotation'], - TSMethodSignature: ['typeParameters', 'key', 'params', 'returnType'], - TSModuleBlock: ['body'], - TSModuleDeclaration: ['id', 'body'], - TSNamedTupleMember: ['label', 'elementType'], - TSNamespaceExportDeclaration: ['id'], - TSNonNullExpression: ['expression'], - TSOptionalType: ['typeAnnotation'], - TSParameterProperty: ['decorators', 'parameter'], - TSPropertySignature: ['typeAnnotation', 'key'], - TSQualifiedName: ['left', 'right'], - TSRestType: ['typeAnnotation'], - TSSatisfiesExpression: ['expression', 'typeAnnotation'], - TSTemplateLiteralType: ['quasis', 'types'], - TSTupleType: ['elementTypes'], - TSTypeAliasDeclaration: ['id', 'typeParameters', 'typeAnnotation'], - TSTypeAnnotation: ['typeAnnotation'], - TSTypeAssertion: ['typeAnnotation', 'expression'], - TSTypeLiteral: ['members'], - TSTypeOperator: ['typeAnnotation'], - TSTypeParameter: ['name', 'constraint', 'default'], - TSTypeParameterDeclaration: ['params'], - TSTypeParameterInstantiation: ['params'], - TSTypePredicate: ['typeAnnotation', 'parameterName'], - TSTypeQuery: ['exprName', 'typeArguments'], - TSTypeReference: ['typeName', 'typeArguments'], - TSUnionType: ['types'], - ParenthesizedExpression: ['expression'], - TSParenthesizedType: ['typeAnnotation'], -}; -module.exports = visitorKeys; diff --git a/napi/parser/generated/visitor-keys.mjs b/napi/parser/generated/visitor-keys.mjs deleted file mode 100644 index bcbf7a9b303c4..0000000000000 --- a/napi/parser/generated/visitor-keys.mjs +++ /dev/null @@ -1,143 +0,0 @@ -// Auto-generated code, DO NOT EDIT DIRECTLY! -// To edit this generated file you have to edit `napi/parser/generate-visitor-keys.mjs`. - -const visitorKeys = { - ArrayExpression: ['elements'], - ArrayPattern: ['decorators', 'elements', 'typeAnnotation'], - ArrowFunctionExpression: ['typeParameters', 'params', 'returnType', 'body'], - AssignmentExpression: ['left', 'right'], - AssignmentPattern: ['decorators', 'left', 'right', 'typeAnnotation'], - AwaitExpression: ['argument'], - BinaryExpression: ['left', 'right'], - BlockStatement: ['body'], - BreakStatement: ['label'], - CallExpression: ['callee', 'typeArguments', 'arguments'], - CatchClause: ['param', 'body'], - ChainExpression: ['expression'], - ClassBody: ['body'], - ClassDeclaration: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], - ClassExpression: ['decorators', 'id', 'typeParameters', 'superClass', 'superTypeArguments', 'implements', 'body'], - ConditionalExpression: ['test', 'consequent', 'alternate'], - ContinueStatement: ['label'], - DoWhileStatement: ['body', 'test'], - ExperimentalRestProperty: ['argument'], - ExperimentalSpreadProperty: ['argument'], - ExportAllDeclaration: ['exported', 'source', 'attributes'], - ExportDefaultDeclaration: ['declaration'], - ExportNamedDeclaration: ['declaration', 'specifiers', 'source', 'attributes'], - ExportSpecifier: ['exported', 'local'], - ExpressionStatement: ['expression'], - ForInStatement: ['left', 'right', 'body'], - ForOfStatement: ['left', 'right', 'body'], - ForStatement: ['init', 'test', 'update', 'body'], - FunctionDeclaration: ['id', 'typeParameters', 'params', 'returnType', 'body'], - FunctionExpression: ['id', 'typeParameters', 'params', 'returnType', 'body'], - Identifier: ['decorators', 'typeAnnotation'], - IfStatement: ['test', 'consequent', 'alternate'], - ImportAttribute: ['key', 'value'], - ImportDeclaration: ['specifiers', 'source', 'attributes'], - ImportDefaultSpecifier: ['local'], - ImportExpression: ['source', 'options'], - ImportNamespaceSpecifier: ['local'], - ImportSpecifier: ['imported', 'local'], - JSXAttribute: ['name', 'value'], - JSXClosingElement: ['name'], - JSXElement: ['openingElement', 'children', 'closingElement'], - JSXExpressionContainer: ['expression'], - JSXFragment: ['openingFragment', 'children', 'closingFragment'], - JSXMemberExpression: ['object', 'property'], - JSXNamespacedName: ['namespace', 'name'], - JSXOpeningElement: ['name', 'typeArguments', 'attributes'], - JSXSpreadAttribute: ['argument'], - JSXSpreadChild: ['expression'], - LabeledStatement: ['label', 'body'], - LogicalExpression: ['left', 'right'], - MemberExpression: ['object', 'property'], - MetaProperty: ['meta', 'property'], - MethodDefinition: ['decorators', 'key', 'value'], - NewExpression: ['callee', 'typeArguments', 'arguments'], - ObjectExpression: ['properties'], - ObjectPattern: ['decorators', 'properties', 'typeAnnotation'], - Program: ['body'], - Property: ['key', 'value'], - PropertyDefinition: ['decorators', 'key', 'typeAnnotation', 'value'], - RestElement: ['decorators', 'argument', 'typeAnnotation'], - ReturnStatement: ['argument'], - SequenceExpression: ['expressions'], - SpreadElement: ['argument'], - StaticBlock: ['body'], - SwitchCase: ['test', 'consequent'], - SwitchStatement: ['discriminant', 'cases'], - TaggedTemplateExpression: ['tag', 'typeArguments', 'quasi'], - TemplateLiteral: ['quasis', 'expressions'], - ThrowStatement: ['argument'], - TryStatement: ['block', 'handler', 'finalizer'], - UnaryExpression: ['argument'], - UpdateExpression: ['argument'], - VariableDeclaration: ['declarations'], - VariableDeclarator: ['id', 'init'], - WhileStatement: ['test', 'body'], - WithStatement: ['object', 'body'], - YieldExpression: ['argument'], - AccessorProperty: ['decorators', 'key', 'typeAnnotation', 'value'], - Decorator: ['expression'], - TSAbstractAccessorProperty: ['decorators', 'key', 'typeAnnotation'], - TSAbstractMethodDefinition: ['key', 'value'], - TSAbstractPropertyDefinition: ['decorators', 'key', 'typeAnnotation'], - TSArrayType: ['elementType'], - TSAsExpression: ['expression', 'typeAnnotation'], - TSCallSignatureDeclaration: ['typeParameters', 'params', 'returnType'], - TSClassImplements: ['expression', 'typeArguments'], - TSConditionalType: ['checkType', 'extendsType', 'trueType', 'falseType'], - TSConstructorType: ['typeParameters', 'params', 'returnType'], - TSConstructSignatureDeclaration: ['typeParameters', 'params', 'returnType'], - TSDeclareFunction: ['id', 'typeParameters', 'params', 'returnType', 'body'], - TSEmptyBodyFunctionExpression: ['id', 'typeParameters', 'params', 'returnType'], - TSEnumBody: ['members'], - TSEnumDeclaration: ['id', 'body'], - TSEnumMember: ['id', 'initializer'], - TSExportAssignment: ['expression'], - TSExternalModuleReference: ['expression'], - TSFunctionType: ['typeParameters', 'params', 'returnType'], - TSImportEqualsDeclaration: ['id', 'moduleReference'], - TSImportType: ['argument', 'qualifier', 'typeArguments', 'options'], - TSIndexedAccessType: ['indexType', 'objectType'], - TSIndexSignature: ['parameters', 'typeAnnotation'], - TSInferType: ['typeParameter'], - TSInstantiationExpression: ['expression', 'typeArguments'], - TSInterfaceBody: ['body'], - TSInterfaceDeclaration: ['id', 'typeParameters', 'extends', 'body'], - TSInterfaceHeritage: ['expression', 'typeArguments'], - TSIntersectionType: ['types'], - TSLiteralType: ['literal'], - TSMappedType: ['key', 'constraint', 'nameType', 'typeAnnotation'], - TSMethodSignature: ['typeParameters', 'key', 'params', 'returnType'], - TSModuleBlock: ['body'], - TSModuleDeclaration: ['id', 'body'], - TSNamedTupleMember: ['label', 'elementType'], - TSNamespaceExportDeclaration: ['id'], - TSNonNullExpression: ['expression'], - TSOptionalType: ['typeAnnotation'], - TSParameterProperty: ['decorators', 'parameter'], - TSPropertySignature: ['typeAnnotation', 'key'], - TSQualifiedName: ['left', 'right'], - TSRestType: ['typeAnnotation'], - TSSatisfiesExpression: ['expression', 'typeAnnotation'], - TSTemplateLiteralType: ['quasis', 'types'], - TSTupleType: ['elementTypes'], - TSTypeAliasDeclaration: ['id', 'typeParameters', 'typeAnnotation'], - TSTypeAnnotation: ['typeAnnotation'], - TSTypeAssertion: ['typeAnnotation', 'expression'], - TSTypeLiteral: ['members'], - TSTypeOperator: ['typeAnnotation'], - TSTypeParameter: ['name', 'constraint', 'default'], - TSTypeParameterDeclaration: ['params'], - TSTypeParameterInstantiation: ['params'], - TSTypePredicate: ['typeAnnotation', 'parameterName'], - TSTypeQuery: ['exprName', 'typeArguments'], - TSTypeReference: ['typeName', 'typeArguments'], - TSUnionType: ['types'], - ParenthesizedExpression: ['expression'], - TSParenthesizedType: ['typeAnnotation'], -}; -export default visitorKeys; diff --git a/napi/parser/package.json b/napi/parser/package.json index 385d08c9b12e5..b31e6e1c2f294 100644 --- a/napi/parser/package.json +++ b/napi/parser/package.json @@ -4,7 +4,7 @@ "main": "index.js", "browser": "wasm.mjs", "scripts": { - "build-dev": "node generate-visitor-keys.mjs && napi build --no-dts-cache --platform --js bindings.js", + "build-dev": "napi build --no-dts-cache --platform --js bindings.js", "build": "pnpm run build-dev --features allocator --release", "postbuild-dev": "node patch.mjs", "build-wasi": "pnpm run build-dev --release --target wasm32-wasip1-threads", @@ -43,8 +43,6 @@ "wasm.mjs", "bindings.js", "webcontainer-fallback.js", - "generated/visitor-keys.cjs", - "generated/visitor-keys.mjs", "generated/deserialize/js.js", "generated/deserialize/ts.js" ], @@ -58,7 +56,6 @@ "devDependencies": { "@codspeed/vitest-plugin": "^4.0.0", "@napi-rs/wasm-runtime": "^0.2.7", - "@typescript-eslint/visitor-keys": "^8.31.1", "@vitest/browser": "3.1.2", "esbuild": "^0.25.0", "playwright": "^1.51.0", diff --git a/napi/parser/src/lib.rs b/napi/parser/src/lib.rs index 28bc3ba95dc9c..474ef215e375a 100644 --- a/napi/parser/src/lib.rs +++ b/napi/parser/src/lib.rs @@ -103,7 +103,7 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption let mut comments = convert_utf8_to_utf16(&source_text, &mut program, &mut module_record, &mut errors); - let program = match ast_type { + let program_and_fixes = match ast_type { AstType::JavaScript => { // Add hashbang to start of comments if let Some(hashbang) = &program.hashbang { @@ -118,20 +118,20 @@ fn parse_with_return(filename: &str, source_text: String, options: &ParserOption ); } - program.to_estree_js_json() + program.to_estree_js_json_with_fixes() } AstType::TypeScript => { // Note: `@typescript-eslint/parser` ignores hashbangs, // despite appearances to the contrary in AST explorers. // So we ignore them too. // See: https://github.com/typescript-eslint/typescript-eslint/issues/6500 - program.to_estree_ts_json() + program.to_estree_ts_json_with_fixes() } }; let module = EcmaScriptModule::from(&module_record); - ParseResult { program, module, comments, errors } + ParseResult { program_and_fixes, module, comments, errors } } /// Parse synchronously. diff --git a/napi/parser/src/types.rs b/napi/parser/src/types.rs index 3b3c1e5caa035..4d3d8de9595eb 100644 --- a/napi/parser/src/types.rs +++ b/napi/parser/src/types.rs @@ -41,7 +41,7 @@ pub struct ParserOptions { #[napi] pub struct ParseResult { - pub(crate) program: String, + pub(crate) program_and_fixes: String, pub(crate) module: EcmaScriptModule, pub(crate) comments: Vec, pub(crate) errors: Vec, @@ -51,7 +51,7 @@ pub struct ParseResult { impl ParseResult { #[napi(getter, ts_return_type = "import(\"@oxc-project/types\").Program")] pub fn get_program(&mut self) -> String { - mem::take(&mut self.program) + mem::take(&mut self.program_and_fixes) } #[napi(getter)] diff --git a/napi/parser/wrap.cjs b/napi/parser/wrap.cjs index 5c3ad36e1fa5d..da482f3d0cd28 100644 --- a/napi/parser/wrap.cjs +++ b/napi/parser/wrap.cjs @@ -1,8 +1,6 @@ // Note: This code is repeated in `wrap.mjs`. // Any changes should be applied in that file too. -const visitorKeys = require('./generated/visitor-keys.cjs'); - module.exports.wrap = function wrap(result) { let program, module, comments, errors; return { @@ -25,39 +23,32 @@ module.exports.wrap = function wrap(result) { }; }; +// Set `value` field of `Literal`s which are `BigInt`s or `RegExp`s. +// +// Returned JSON contains an array `fixes` with paths to these nodes +// e.g. for `123n; foo(/xyz/)`, `fixes` will be +// `[["body", 0, "expression"], ["body", 1, "expression", "arguments", 2]]`. +// +// Walk down the AST to these nodes and alter them. +// Compiling the list of fixes on Rust side avoids having to do a full AST traversal on JS side +// to locate the likely very few `Literal`s which need fixing. function jsonParseAst(programJson) { - const program = JSON.parse(programJson); - transformNode(program); + const { node: program, fixes } = JSON.parse(programJson); + for (const fixPath of fixes) { + applyFix(program, fixPath); + } return program; } -function transformNode(node) { - if (!node) return; - if (Array.isArray(node)) { - for (const el of node) { - transformNode(el); - } - return; - } - - if (node.type === 'Literal') { - transformLiteral(node); - return; - } - - const keys = visitorKeys[node.type]; - if (!keys) return; - for (const key of keys) { - transformNode(node[key]); +function applyFix(program, fixPath) { + let node = program; + for (const key of fixPath) { + node = node[key]; } -} -function transformLiteral(node) { - // Set `value` field of `Literal`s for `BigInt`s and `RegExp`s. - // This is not possible to do on Rust side, as neither can be represented correctly in JSON. if (node.bigint) { node.value = BigInt(node.bigint); - } else if (node.regex) { + } else { try { node.value = RegExp(node.regex.pattern, node.regex.flags); } catch (_err) { diff --git a/napi/parser/wrap.mjs b/napi/parser/wrap.mjs index 5e86842e3aee4..9f2f31ec8cde4 100644 --- a/napi/parser/wrap.mjs +++ b/napi/parser/wrap.mjs @@ -1,8 +1,6 @@ // Note: This code is repeated in `wrap.cjs`. // Any changes should be applied in that file too. -import visitorKeys from './generated/visitor-keys.mjs'; - export function wrap(result) { let program, module, comments, errors; return { @@ -25,40 +23,34 @@ export function wrap(result) { }; } -// Used by napi/playground/patch.mjs +// Used by `napi/playground/patch.mjs`. +// +// Set `value` field of `Literal`s which are `BigInt`s or `RegExp`s. +// +// Returned JSON contains an array `fixes` with paths to these nodes +// e.g. for `123n; foo(/xyz/)`, `fixes` will be +// `[["body", 0, "expression"], ["body", 1, "expression", "arguments", 2]]`. +// +// Walk down the AST to these nodes and alter them. +// Compiling the list of fixes on Rust side avoids having to do a full AST traversal on JS side +// to locate the likely very few `Literal`s which need fixing. export function jsonParseAst(programJson) { - const program = JSON.parse(programJson); - transformNode(program); + const { node: program, fixes } = JSON.parse(programJson); + for (const fixPath of fixes) { + applyFix(program, fixPath); + } return program; } -function transformNode(node) { - if (!node) return; - if (Array.isArray(node)) { - for (const el of node) { - transformNode(el); - } - return; - } - - if (node.type === 'Literal') { - transformLiteral(node); - return; - } - - const keys = visitorKeys[node.type]; - if (!keys) return; - for (const key of keys) { - transformNode(node[key]); +function applyFix(program, fixPath) { + let node = program; + for (const key of fixPath) { + node = node[key]; } -} -function transformLiteral(node) { - // Set `value` field of `Literal`s for `BigInt`s and `RegExp`s. - // This is not possible to do on Rust side, as neither can be represented correctly in JSON. if (node.bigint) { node.value = BigInt(node.bigint); - } else if (node.regex) { + } else { try { node.value = RegExp(node.regex.pattern, node.regex.flags); } catch (_err) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7571ebb500fee..bffeaea6241a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,12 +6,6 @@ settings: catalogs: default: - '@napi-rs/cli': - specifier: 3.0.0-alpha.78 - version: 3.0.0-alpha.78 - typescript: - specifier: 5.8.3 - version: 5.8.3 vitest: specifier: 3.1.2 version: 3.1.2 @@ -91,9 +85,6 @@ importers: '@napi-rs/wasm-runtime': specifier: ^0.2.7 version: 0.2.9 - '@typescript-eslint/visitor-keys': - specifier: ^8.31.1 - version: 8.31.1 '@vitest/browser': specifier: 3.1.2 version: 3.1.2(playwright@1.52.0)(vite@6.3.4(@types/node@22.15.3))(vitest@3.1.2) @@ -1310,14 +1301,6 @@ packages: '@types/vscode@1.93.0': resolution: {integrity: sha512-kUK6jAHSR5zY8ps42xuW89NLcBpw1kOabah7yv38J8MyiYuOHxLQBi0e7zeXbQgVefDy/mZZetqEFC+Fl5eIEQ==} - '@typescript-eslint/types@8.31.1': - resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.31.1': - resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.2.2': resolution: {integrity: sha512-Gz/Sm64+Sq/vklJu1tt9t+4R2lvnud8NbTD/ZfpZtMiUX7YeVpCA8j6NSW8ptwcoLL+NmYANwqP8DV0q/bwl2w==} engines: {node: '>=18.0.0'} @@ -1840,10 +1823,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -4037,13 +4016,6 @@ snapshots: '@types/vscode@1.93.0': {} - '@typescript-eslint/types@8.31.1': {} - - '@typescript-eslint/visitor-keys@8.31.1': - dependencies: - '@typescript-eslint/types': 8.31.1 - eslint-visitor-keys: 4.2.0 - '@typespec/ts-http-runtime@0.2.2': dependencies: http-proxy-agent: 7.0.2 @@ -4478,6 +4450,10 @@ snapshots: css-what@6.1.0: {} + debug@4.4.0: + dependencies: + ms: 2.1.3 + debug@4.4.0(supports-color@8.1.1): dependencies: ms: 2.1.3 @@ -4628,8 +4604,6 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-visitor-keys@4.2.0: {} - estree-walker@3.0.3: dependencies: '@types/estree': 1.0.7 @@ -5615,7 +5589,7 @@ snapshots: vite-node@3.1.2(@types/node@22.15.3): dependencies: cac: 6.7.14 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 6.3.4(@types/node@22.15.3) @@ -5655,7 +5629,7 @@ snapshots: '@vitest/spy': 3.1.2 '@vitest/utils': 3.1.2 chai: 5.2.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 diff --git a/tasks/benchmark/benches/parser.rs b/tasks/benchmark/benches/parser.rs index d8749782a35ae..ba3c416bfd320 100644 --- a/tasks/benchmark/benches/parser.rs +++ b/tasks/benchmark/benches/parser.rs @@ -52,7 +52,7 @@ fn bench_estree(criterion: &mut Criterion) { span_converter.convert_program(&mut program); span_converter.convert_comments(&mut program.comments); - black_box(program.to_estree_ts_json()); + black_box(program.to_estree_ts_json_with_fixes()); program }); }); diff --git a/tasks/coverage/src/driver.rs b/tasks/coverage/src/driver.rs index 3a3d99ea67253..2d3ca695debe4 100644 --- a/tasks/coverage/src/driver.rs +++ b/tasks/coverage/src/driver.rs @@ -81,7 +81,7 @@ impl CompilerInterface for Driver { self.errors.push(OxcDiagnostic::error("SourceType must not be unambiguous.")); } // Make sure serialization doesn't crash; also for code coverage. - program.to_estree_ts_json(); + program.to_estree_ts_json_with_fixes(); ControlFlow::Continue(()) }