diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index d91a2aa1a5020..22dd748b03e5e 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -2040,6 +2040,7 @@ pub struct MethodDefinition<'a> { pub r#type: MethodDefinitionType, #[ts] pub decorators: Vec<'a, Decorator<'a>>, + #[estree(via = MethodDefinitionKey)] pub key: PropertyKey<'a>, #[visit(args(flags = match self.kind { MethodDefinitionKind::Get => ScopeFlags::Function | ScopeFlags::GetAccessor, diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index afd3cbb3370e2..075396201bd7e 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -1514,7 +1514,7 @@ impl ESTree for MethodDefinition<'_> { state.serialize_field("end", &self.span.end); state.serialize_field("static", &self.r#static); state.serialize_field("computed", &self.computed); - state.serialize_field("key", &self.key); + state.serialize_field("key", &crate::serialize::MethodDefinitionKey(self)); state.serialize_field("kind", &self.kind); state.serialize_field("value", &self.value); state.serialize_ts_field("decorators", &self.decorators); diff --git a/crates/oxc_ast/src/serialize.rs b/crates/oxc_ast/src/serialize.rs index ff07679ece77d..5c1a42cd757ae 100644 --- a/crates/oxc_ast/src/serialize.rs +++ b/crates/oxc_ast/src/serialize.rs @@ -673,6 +673,52 @@ impl ESTree for FunctionFormalParameters<'_, '_> { } } +/// Serializer for `key` field of `MethodDefinition`. +/// +/// In TS-ESTree `"constructor"` in `class C { "constructor"() {} }` +/// is represented as an `Identifier`. +/// In Acorn and Espree, it's a `Literal`. +/// +#[ast_meta] +#[estree( + ts_type = "PropertyKey", + raw_deser = " + /* IF_JS */ + DESER[PropertyKey](POS_OFFSET.key) + /* END_IF_JS */ + + /* IF_TS */ + let key = DESER[PropertyKey](POS_OFFSET.key); + if (THIS.kind === 'constructor') { + key = { + type: 'Identifier', + start: key.start, + end: key.end, + name: 'constructor', + decorators: [], + optional: false, + typeAnnotation: null, + }; + } + key + /* END_IF_TS */ + " +)] +pub struct MethodDefinitionKey<'a, 'b>(pub &'b MethodDefinition<'a>); + +impl ESTree for MethodDefinitionKey<'_, '_> { + fn serialize(&self, serializer: S) { + let method = self.0; + if S::INCLUDE_TS_FIELDS && method.kind == MethodDefinitionKind::Constructor { + // `key` can only be either an identifier `constructor`, or string `"constructor"` + let span = method.key.span(); + IdentifierName { span, name: Atom::from("constructor") }.serialize(serializer); + } else { + method.key.serialize(serializer); + } + } +} + /// Serializer for `extends` field of `TSInterfaceDeclaration`. /// /// Serialize `extends` as an empty array if it's `None`. diff --git a/napi/parser/deserialize-ts.js b/napi/parser/deserialize-ts.js index 448cc4e1b4ade..9a135f035955a 100644 --- a/napi/parser/deserialize-ts.js +++ b/napi/parser/deserialize-ts.js @@ -932,14 +932,27 @@ function deserializeClassBody(pos) { } function deserializeMethodDefinition(pos) { + const kind = deserializeMethodDefinitionKind(pos + 72); + let key = deserializePropertyKey(pos + 48); + if (kind === 'constructor') { + key = { + type: 'Identifier', + start: key.start, + end: key.end, + name: 'constructor', + decorators: [], + optional: false, + typeAnnotation: null, + }; + } return { type: deserializeMethodDefinitionType(pos + 8), start: deserializeU32(pos), end: deserializeU32(pos + 4), static: deserializeBool(pos + 74), computed: deserializeBool(pos + 73), - key: deserializePropertyKey(pos + 48), - kind: deserializeMethodDefinitionKind(pos + 72), + key, + kind, value: deserializeBoxFunction(pos + 64), decorators: deserializeVecDecorator(pos + 16), override: deserializeBool(pos + 75), diff --git a/tasks/coverage/snapshots/estree_typescript.snap b/tasks/coverage/snapshots/estree_typescript.snap index 6c88f0b8338d0..239afe65ed7c3 100644 --- a/tasks/coverage/snapshots/estree_typescript.snap +++ b/tasks/coverage/snapshots/estree_typescript.snap @@ -2,7 +2,7 @@ commit: 15392346 estree_typescript Summary: AST Parsed : 10619/10725 (99.01%) -Positive Passed: 9037/10725 (84.26%) +Positive Passed: 9038/10725 (84.27%) Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts A class member cannot have the 'const' keyword. Mismatch: tasks/coverage/typescript/tests/cases/compiler/accessOverriddenBaseClassMember1.ts @@ -1055,7 +1055,6 @@ Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorD Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/constructorParameters/readonlyReadonly.ts readonly' modifier already seen. Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/constructorWithExpressionLessReturn.ts -Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/quotedConstructors.ts Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/superCalls/derivedClassParameterProperties.ts Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/superCalls/derivedClassSuperCallsWithThisArg.ts Mismatch: tasks/coverage/typescript/tests/cases/conformance/classes/constructorDeclarations/superCalls/derivedClassSuperStatementPosition.ts