Skip to content

Commit 47e2c59

Browse files
committed
fix(estree): fix serialization of TSImportTypeQualifier (#12801)
1 parent 50b91ac commit 47e2c59

File tree

7 files changed

+218
-13
lines changed

7 files changed

+218
-13
lines changed

crates/oxc_ast/src/ast/ts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,7 @@ pub struct TSImportType<'a> {
13591359
pub span: Span,
13601360
pub argument: TSType<'a>,
13611361
pub options: Option<Box<'a, ObjectExpression<'a>>>,
1362+
#[estree(via = TSImportTypeQualifierConverter)]
13621363
pub qualifier: Option<TSImportTypeQualifier<'a>>,
13631364
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
13641365
}

crates/oxc_ast/src/generated/derive_estree.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2977,7 +2977,10 @@ impl ESTree for TSImportType<'_> {
29772977
state.serialize_field("type", &JsonSafeString("TSImportType"));
29782978
state.serialize_field("argument", &self.argument);
29792979
state.serialize_field("options", &self.options);
2980-
state.serialize_field("qualifier", &self.qualifier);
2980+
state.serialize_field(
2981+
"qualifier",
2982+
&crate::serialize::ts::TSImportTypeQualifierConverter(self),
2983+
);
29812984
state.serialize_field("typeArguments", &self.type_arguments);
29822985
state.serialize_span(self.span);
29832986
state.end();

crates/oxc_ast/src/serialize/ts.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,118 @@ impl ESTree for TSFunctionTypeParams<'_, '_> {
401401
Concat2(&fn_type.this_param, fn_type.params.as_ref()).serialize(serializer);
402402
}
403403
}
404+
405+
/// Serializer for `qualifier` field of `TSImportType`.
406+
///
407+
/// Our AST represents the qualifier as `TSImportTypeQualifier` with `IdentifierName` nodes.
408+
/// TS-ESTree represents it as `TSQualifiedName` with `Identifier` nodes.
409+
#[ast_meta]
410+
#[estree(
411+
ts_type = "TSQualifiedName | IdentifierName | null",
412+
raw_deser = "
413+
let qualifier = DESER[Option<TSImportTypeQualifier>](POS_OFFSET.qualifier);
414+
if (qualifier !== null) {
415+
if (qualifier.type === 'IdentifierName') {
416+
qualifier = {
417+
type: 'Identifier',
418+
decorators: [],
419+
name: qualifier.name,
420+
optional: false,
421+
typeAnnotation: null,
422+
start: qualifier.start,
423+
end: qualifier.end,
424+
};
425+
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
426+
// Convert TSImportTypeQualifiedName to TSQualifiedName
427+
const convertQualifier = (q) => {
428+
if (q.type === 'IdentifierName') {
429+
return {
430+
type: 'Identifier',
431+
decorators: [],
432+
name: q.name,
433+
optional: false,
434+
typeAnnotation: null,
435+
start: q.start,
436+
end: q.end,
437+
};
438+
} else if (q.type === 'TSImportTypeQualifiedName') {
439+
return {
440+
type: 'TSQualifiedName',
441+
left: convertQualifier(q.left),
442+
right: {
443+
type: 'Identifier',
444+
decorators: [],
445+
name: q.right.name,
446+
optional: false,
447+
typeAnnotation: null,
448+
start: q.right.start,
449+
end: q.right.end,
450+
},
451+
start: q.start,
452+
end: q.end,
453+
};
454+
}
455+
return q;
456+
};
457+
qualifier = convertQualifier(qualifier);
458+
}
459+
}
460+
qualifier
461+
"
462+
)]
463+
pub struct TSImportTypeQualifierConverter<'a, 'b>(pub &'b TSImportType<'a>);
464+
465+
impl ESTree for TSImportTypeQualifierConverter<'_, '_> {
466+
fn serialize<S: Serializer>(&self, serializer: S) {
467+
match &self.0.qualifier {
468+
None => None::<()>.serialize(serializer),
469+
Some(qualifier) => {
470+
TSImportTypeQualifierAsQualifiedName(qualifier).serialize(serializer);
471+
}
472+
}
473+
}
474+
}
475+
476+
struct TSImportTypeQualifierAsQualifiedName<'a, 'b>(&'b TSImportTypeQualifier<'a>);
477+
478+
impl ESTree for TSImportTypeQualifierAsQualifiedName<'_, '_> {
479+
fn serialize<S: Serializer>(&self, serializer: S) {
480+
match self.0 {
481+
TSImportTypeQualifier::Identifier(ident) => {
482+
// Convert IdentifierName to Identifier
483+
let mut state = serializer.serialize_struct();
484+
state.serialize_field("type", &JsonSafeString("Identifier"));
485+
state.serialize_field("decorators", &Vec::<Decorator>::new().as_slice());
486+
state.serialize_field("name", &ident.name);
487+
state.serialize_field("optional", &false);
488+
state.serialize_field("typeAnnotation", &None::<()>);
489+
state.serialize_span(ident.span);
490+
state.end();
491+
}
492+
TSImportTypeQualifier::QualifiedName(name) => {
493+
// Convert TSImportTypeQualifiedName to TSQualifiedName
494+
let mut state = serializer.serialize_struct();
495+
state.serialize_field("type", &JsonSafeString("TSQualifiedName"));
496+
state.serialize_field("left", &TSImportTypeQualifierAsQualifiedName(&name.left));
497+
state.serialize_field("right", &IdentifierAsIdentifier(&name.right));
498+
state.serialize_span(name.span);
499+
state.end();
500+
}
501+
}
502+
}
503+
}
504+
505+
struct IdentifierAsIdentifier<'a, 'b>(&'b IdentifierName<'a>);
506+
507+
impl ESTree for IdentifierAsIdentifier<'_, '_> {
508+
fn serialize<S: Serializer>(&self, serializer: S) {
509+
let mut state = serializer.serialize_struct();
510+
state.serialize_field("type", &JsonSafeString("Identifier"));
511+
state.serialize_field("decorators", &Vec::<Decorator>::new().as_slice());
512+
state.serialize_field("name", &self.0.name);
513+
state.serialize_field("optional", &false);
514+
state.serialize_field("typeAnnotation", &None::<()>);
515+
state.serialize_span(self.0.span);
516+
state.end();
517+
}
518+
}

napi/parser/generated/deserialize/js.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1874,11 +1874,58 @@ function deserializeTSTypeQuery(pos) {
18741874
}
18751875

18761876
function deserializeTSImportType(pos) {
1877+
let qualifier = deserializeOptionTSImportTypeQualifier(pos + 32);
1878+
if (qualifier !== null) {
1879+
if (qualifier.type === 'IdentifierName') {
1880+
qualifier = {
1881+
type: 'Identifier',
1882+
decorators: [],
1883+
name: qualifier.name,
1884+
optional: false,
1885+
typeAnnotation: null,
1886+
start: qualifier.start,
1887+
end: qualifier.end,
1888+
};
1889+
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
1890+
// Convert TSImportTypeQualifiedName to TSQualifiedName
1891+
const convertQualifier = (q) => {
1892+
if (q.type === 'IdentifierName') {
1893+
return {
1894+
type: 'Identifier',
1895+
decorators: [],
1896+
name: q.name,
1897+
optional: false,
1898+
typeAnnotation: null,
1899+
start: q.start,
1900+
end: q.end,
1901+
};
1902+
} else if (q.type === 'TSImportTypeQualifiedName') {
1903+
return {
1904+
type: 'TSQualifiedName',
1905+
left: convertQualifier(q.left),
1906+
right: {
1907+
type: 'Identifier',
1908+
decorators: [],
1909+
name: q.right.name,
1910+
optional: false,
1911+
typeAnnotation: null,
1912+
start: q.right.start,
1913+
end: q.right.end,
1914+
},
1915+
start: q.start,
1916+
end: q.end,
1917+
};
1918+
}
1919+
return q;
1920+
};
1921+
qualifier = convertQualifier(qualifier);
1922+
}
1923+
}
18771924
return {
18781925
type: 'TSImportType',
18791926
argument: deserializeTSType(pos + 8),
18801927
options: deserializeOptionBoxObjectExpression(pos + 24),
1881-
qualifier: deserializeOptionTSImportTypeQualifier(pos + 32),
1928+
qualifier,
18821929
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 48),
18831930
start: deserializeU32(pos),
18841931
end: deserializeU32(pos + 4),

napi/parser/generated/deserialize/ts.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2005,11 +2005,58 @@ function deserializeTSTypeQuery(pos) {
20052005
}
20062006

20072007
function deserializeTSImportType(pos) {
2008+
let qualifier = deserializeOptionTSImportTypeQualifier(pos + 32);
2009+
if (qualifier !== null) {
2010+
if (qualifier.type === 'IdentifierName') {
2011+
qualifier = {
2012+
type: 'Identifier',
2013+
decorators: [],
2014+
name: qualifier.name,
2015+
optional: false,
2016+
typeAnnotation: null,
2017+
start: qualifier.start,
2018+
end: qualifier.end,
2019+
};
2020+
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
2021+
// Convert TSImportTypeQualifiedName to TSQualifiedName
2022+
const convertQualifier = (q) => {
2023+
if (q.type === 'IdentifierName') {
2024+
return {
2025+
type: 'Identifier',
2026+
decorators: [],
2027+
name: q.name,
2028+
optional: false,
2029+
typeAnnotation: null,
2030+
start: q.start,
2031+
end: q.end,
2032+
};
2033+
} else if (q.type === 'TSImportTypeQualifiedName') {
2034+
return {
2035+
type: 'TSQualifiedName',
2036+
left: convertQualifier(q.left),
2037+
right: {
2038+
type: 'Identifier',
2039+
decorators: [],
2040+
name: q.right.name,
2041+
optional: false,
2042+
typeAnnotation: null,
2043+
start: q.right.start,
2044+
end: q.right.end,
2045+
},
2046+
start: q.start,
2047+
end: q.end,
2048+
};
2049+
}
2050+
return q;
2051+
};
2052+
qualifier = convertQualifier(qualifier);
2053+
}
2054+
}
20082055
return {
20092056
type: 'TSImportType',
20102057
argument: deserializeTSType(pos + 8),
20112058
options: deserializeOptionBoxObjectExpression(pos + 24),
2012-
qualifier: deserializeOptionTSImportTypeQualifier(pos + 32),
2059+
qualifier,
20132060
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 48),
20142061
start: deserializeU32(pos),
20152062
end: deserializeU32(pos + 4),

npm/oxc-types/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ export interface TSImportType extends Span {
13511351
type: 'TSImportType';
13521352
argument: TSType;
13531353
options: ObjectExpression | null;
1354-
qualifier: TSImportTypeQualifier | null;
1354+
qualifier: TSQualifiedName | IdentifierName | null;
13551355
typeArguments: TSTypeParameterInstantiation | null;
13561356
}
13571357

tasks/coverage/snapshots/estree_typescript.snap

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,10 @@ commit: 81c95189
22

33
estree_typescript Summary:
44
AST Parsed : 8575/8575 (100.00%)
5-
Positive Passed: 8568/8575 (99.92%)
6-
Mismatch: tasks/coverage/typescript/tests/cases/compiler/importTypeTypeofClassStaticLookup.ts
7-
5+
Positive Passed: 8572/8575 (99.97%)
86
Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/jsxReactTestSuite.tsx
97

108
Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/tsxReactEmitEntities.tsx
119

1210
Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/tsxReactEmitNesting.tsx
1311

14-
Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeAmbient.ts
15-
16-
Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeGenericTypes.ts
17-
18-
Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeLocal.ts
19-

0 commit comments

Comments
 (0)