diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index ea86610efb7da..d2c73eae3d148 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1755,7 +1755,7 @@ pub struct FormalParameters<'a> { #[generate_derive(CloneIn, GetSpan, GetSpanMut, ContentEq, ESTree)] // Pluralize as `FormalParameterList` to avoid naming clash with `FormalParameters`. #[plural(FormalParameterList)] -#[estree(no_type, field_order(pattern, decorators, accessibility, readonly, r#override))] +#[estree(no_type)] pub struct FormalParameter<'a> { #[estree(skip)] pub span: Span, diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index 576497bdd5851..19e436419c7ed 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -406,9 +406,9 @@ impl ESTree for CallExpression<'_> { state.serialize_field("start", &self.span.start); state.serialize_field("end", &self.span.end); state.serialize_field("callee", &self.callee); - state.serialize_ts_field("typeParameters", &self.type_parameters); state.serialize_field("arguments", &self.arguments); state.serialize_field("optional", &self.optional); + state.serialize_ts_field("typeParameters", &self.type_parameters); state.end(); } } @@ -1749,11 +1749,11 @@ impl ESTree for ExportNamedDeclaration<'_> { state.serialize_field("declaration", &self.declaration); state.serialize_field("specifiers", &self.specifiers); state.serialize_field("source", &self.source); - state.serialize_ts_field("exportKind", &self.export_kind); state.serialize_field( "attributes", &crate::serialize::ExportNamedDeclarationWithClause(self), ); + state.serialize_ts_field("exportKind", &self.export_kind); state.end(); } } diff --git a/napi/parser/deserialize-ts.js b/napi/parser/deserialize-ts.js index 2932bfd02771c..03b0e38987bf3 100644 --- a/napi/parser/deserialize-ts.js +++ b/napi/parser/deserialize-ts.js @@ -207,9 +207,9 @@ function deserializeCallExpression(pos) { start: deserializeU32(pos), end: deserializeU32(pos + 4), callee: deserializeExpression(pos + 8), - typeParameters: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24), arguments: deserializeVecArgument(pos + 32), optional: deserializeBool(pos + 64), + typeParameters: deserializeOptionBoxTSTypeParameterInstantiation(pos + 24), }; } @@ -1024,8 +1024,8 @@ function deserializeExportNamedDeclaration(pos) { declaration: deserializeOptionDeclaration(pos + 8), specifiers: deserializeVecExportSpecifier(pos + 24), source: deserializeOptionStringLiteral(pos + 56), - exportKind: deserializeImportOrExportKind(pos + 96), attributes: withClause === null ? [] : withClause.withEntries, + exportKind: deserializeImportOrExportKind(pos + 96), }; } diff --git a/npm/oxc-types/types.d.ts b/npm/oxc-types/types.d.ts index fdb4631a2ec66..327c05d83b76b 100644 --- a/npm/oxc-types/types.d.ts +++ b/npm/oxc-types/types.d.ts @@ -156,9 +156,9 @@ export interface PrivateFieldExpression extends Span { export interface CallExpression extends Span { type: 'CallExpression'; callee: Expression; - typeParameters: TSTypeParameterInstantiation | null; arguments: Array; optional: boolean; + typeParameters: TSTypeParameterInstantiation | null; } export interface NewExpression extends Span { @@ -731,8 +731,8 @@ export interface ExportNamedDeclaration extends Span { declaration: Declaration | null; specifiers: Array; source: StringLiteral | null; - exportKind: ImportOrExportKind; attributes: Array; + exportKind: ImportOrExportKind; } export interface ExportDefaultDeclaration extends Span { diff --git a/tasks/ast_tools/src/derives/estree.rs b/tasks/ast_tools/src/derives/estree.rs index 3cd4e3ebcd8a6..1682fb30bd9e6 100644 --- a/tasks/ast_tools/src/derives/estree.rs +++ b/tasks/ast_tools/src/derives/estree.rs @@ -7,6 +7,7 @@ use quote::quote; use crate::{ Result, + codegen::{Codegen, DeriveId}, schema::{Def, EnumDef, FieldDef, File, Schema, StructDef, TypeDef, VariantDef, Visibility}, utils::create_safe_ident, }; @@ -58,6 +59,12 @@ impl Derive for DeriveESTree { } } + /// Initialize `estree.field_order` on structs. + fn prepare(&self, schema: &mut Schema, codegen: &Codegen) { + let derive_id = codegen.get_derive_id_by_name(self.trait_name()); + prepare_field_orders(schema, derive_id); + } + fn prelude(&self) -> TokenStream { quote! { #![allow(unused_imports, clippy::match_same_arms, clippy::semicolon_if_nothing_returned)] @@ -115,7 +122,7 @@ fn parse_estree_attr(location: AttrLocation, part: AttrPart) -> Result<()> { } field_indices.push(field_index); } - struct_def.estree.field_indices = Some(field_indices); + struct_def.estree.field_indices = field_indices; } AttrPart::String("ts_alias", value) => struct_def.estree.ts_alias = Some(value), AttrPart::String("add_ts_def", value) => struct_def.estree.add_ts_def = Some(value), @@ -215,6 +222,81 @@ fn parse_ts_attr(location: AttrLocation, part: &AttrPart) -> Result<()> { Ok(()) } +/// Initialize `estree.field_order` on all structs. +fn prepare_field_orders(schema: &mut Schema, estree_derive_id: DeriveId) { + // Note: Outside the loop to avoid allocating temporary `Vec`s on each turn of the loop. + // Instead, reuse this `Vec` over and over. + let mut field_indices_temp = vec![]; + + for type_id in schema.types.indices() { + let Some(struct_def) = schema.types[type_id].as_struct() else { continue }; + if !struct_def.generates_derive(estree_derive_id) { + continue; + } + + if struct_def.estree.field_indices.is_empty() { + // No field order specified with `#[estree(field_order(...))]`. + // Set default order: + // 1. Fields without `#[ts]` attr, in definition order. + // 2. Fields with `#[ts]` attr, in definition order. + // 3. Added fields `#[estree(add_fields(...)]`, in definition order. + let mut field_indices = vec![]; + let ts_field_indices = &mut field_indices_temp; + for (field_index, field) in struct_def.fields.iter().enumerate() { + if !should_skip_field(field, schema) { + let field_index = u8::try_from(field_index).unwrap(); + if field.estree.is_ts { + ts_field_indices.push(field_index); + } else { + field_indices.push(field_index); + } + } + } + field_indices.append(ts_field_indices); + + let fields_len = struct_def.fields.len(); + for (index, _) in struct_def.estree.add_fields.iter().enumerate() { + let field_index = u8::try_from(fields_len + index).unwrap(); + field_indices.push(field_index); + } + + let struct_def = schema.struct_def_mut(type_id); + struct_def.estree.field_indices = field_indices; + } else { + // Custom field order specified with `#[estree(field_order(...))]`. + // Verify does not miss any fields, no fields marked `#[estree(skip)]` are included. + let unskipped_field_indices = &mut field_indices_temp; + for (field_index, field) in struct_def.fields.iter().enumerate() { + if !should_skip_field(field, schema) { + let field_index = u8::try_from(field_index).unwrap(); + unskipped_field_indices.push(field_index); + } + } + + let fields_len = struct_def.fields.len(); + for &field_index in &struct_def.estree.field_indices { + if (field_index as usize) < fields_len { + assert!( + unskipped_field_indices.contains(&field_index), + "Skipped field `{}` included in `#[estree(field_order)]`: `{}`", + struct_def.fields[field_index as usize].name(), + struct_def.name() + ); + } + } + + assert!( + struct_def.estree.field_indices.len() + == unskipped_field_indices.len() + struct_def.estree.add_fields.len(), + "`#[estree(field_order)]` misses fields: `{}`", + struct_def.name() + ); + + unskipped_field_indices.clear(); + } + } +} + /// Generate implementation of `ESTree` for a struct or enum. fn generate_impl_for_type(type_def: StructOrEnum, schema: &Schema) -> TokenStream { let body = match type_def { @@ -294,25 +376,13 @@ impl<'s> StructSerializerGenerator<'s> { /// Generate code to serialize all fields in a struct. fn generate_stmts_for_struct(&mut self, struct_def: &StructDef, self_path: &TokenStream) { - if let Some(field_indices) = &struct_def.estree.field_indices { - // Specified field order - serialize in this order - for &field_index in field_indices { - let field_index = field_index as usize; - if let Some(field) = struct_def.fields.get(field_index) { - self.generate_stmts_for_field(field, struct_def, self_path); - } else { - let (field_name, converter_name) = - &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; - self.generate_stmt_for_added_field(field_name, converter_name, self_path); - } - } - } else { - // No specified field order - serialize in original order - for field in &struct_def.fields { + for &field_index in &struct_def.estree.field_indices { + let field_index = field_index as usize; + if let Some(field) = struct_def.fields.get(field_index) { self.generate_stmts_for_field(field, struct_def, self_path); - } - - for (field_name, converter_name) in &struct_def.estree.add_fields { + } else { + let (field_name, converter_name) = + &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; self.generate_stmt_for_added_field(field_name, converter_name, self_path); } } diff --git a/tasks/ast_tools/src/generators/raw_transfer.rs b/tasks/ast_tools/src/generators/raw_transfer.rs index 0cdde1d864919..5d39332089d2c 100644 --- a/tasks/ast_tools/src/generators/raw_transfer.rs +++ b/tasks/ast_tools/src/generators/raw_transfer.rs @@ -236,30 +236,13 @@ impl<'s> StructDeserializerGenerator<'s> { } fn generate_struct_fields(&mut self, struct_def: &StructDef, struct_offset: u32) { - if let Some(field_indices) = &struct_def.estree.field_indices { - // Specified field order - serialize in this order - for &field_index in field_indices { - let field_index = field_index as usize; - if let Some(field) = struct_def.fields.get(field_index) { - self.generate_struct_field_owned(field, struct_def, struct_offset); - } else { - let (field_name, converter_name) = - &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; - self.generate_struct_field_added( - struct_def, - field_name, - converter_name, - struct_offset, - ); - } - } - } else { - // No specified field order - serialize in original order - for field in &struct_def.fields { + for &field_index in &struct_def.estree.field_indices { + let field_index = field_index as usize; + if let Some(field) = struct_def.fields.get(field_index) { self.generate_struct_field_owned(field, struct_def, struct_offset); - } - - for (field_name, converter_name) in &struct_def.estree.add_fields { + } else { + let (field_name, converter_name) = + &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; self.generate_struct_field_added( struct_def, field_name, diff --git a/tasks/ast_tools/src/generators/typescript.rs b/tasks/ast_tools/src/generators/typescript.rs index cccb010b0e2e2..32a52030902f2 100644 --- a/tasks/ast_tools/src/generators/typescript.rs +++ b/tasks/ast_tools/src/generators/typescript.rs @@ -125,33 +125,9 @@ fn generate_ts_type_def_for_struct( let mut output_as_type = false; - if let Some(field_indices) = &struct_def.estree.field_indices { - // Specified field order - output in this order - for &field_index in field_indices { - let field_index = field_index as usize; - if let Some(field) = struct_def.fields.get(field_index) { - generate_ts_type_def_for_struct_field( - struct_def, - field, - &mut fields_str, - &mut extends, - &mut output_as_type, - schema, - ); - } else { - let (field_name, converter_name) = - &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; - generate_ts_type_def_for_added_struct_field( - field_name, - converter_name, - &mut fields_str, - schema, - ); - } - } - } else { - // No specified field order - output in original order - for field in &struct_def.fields { + for &field_index in &struct_def.estree.field_indices { + let field_index = field_index as usize; + if let Some(field) = struct_def.fields.get(field_index) { generate_ts_type_def_for_struct_field( struct_def, field, @@ -160,9 +136,9 @@ fn generate_ts_type_def_for_struct( &mut output_as_type, schema, ); - } - - for (field_name, converter_name) in &struct_def.estree.add_fields { + } else { + let (field_name, converter_name) = + &struct_def.estree.add_fields[field_index - struct_def.fields.len()]; generate_ts_type_def_for_added_struct_field( field_name, converter_name, diff --git a/tasks/ast_tools/src/schema/extensions/estree.rs b/tasks/ast_tools/src/schema/extensions/estree.rs index 8843ad508e262..efe6f1faaaa37 100644 --- a/tasks/ast_tools/src/schema/extensions/estree.rs +++ b/tasks/ast_tools/src/schema/extensions/estree.rs @@ -15,7 +15,7 @@ pub struct ESTreeStruct { /// * Actual struct field: index of the field. /// * Added field: `struct_def.fields.len() + added_field_index`. /// Does not include `type` field, if it's automatically added. - pub field_indices: Option>, + pub field_indices: Vec, /// TS alias. /// e.g. `#[estree(ts_alias = "null")]` means this type won't have a type def generated, /// and any struct / enum referencing it will substitute `null` as the type.