Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/oxc_ast/src/ast/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,7 @@ pub struct TSImportType<'a> {
pub span: Span,
pub argument: TSType<'a>,
pub options: Option<Box<'a, ObjectExpression<'a>>>,
#[estree(via = TSImportTypeQualifierConverter)]
pub qualifier: Option<TSImportTypeQualifier<'a>>,
pub type_arguments: Option<Box<'a, TSTypeParameterInstantiation<'a>>>,
}
Expand Down
5 changes: 4 additions & 1 deletion crates/oxc_ast/src/generated/derive_estree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2977,7 +2977,10 @@ impl ESTree for TSImportType<'_> {
state.serialize_field("type", &JsonSafeString("TSImportType"));
state.serialize_field("argument", &self.argument);
state.serialize_field("options", &self.options);
state.serialize_field("qualifier", &self.qualifier);
state.serialize_field(
"qualifier",
&crate::serialize::ts::TSImportTypeQualifierConverter(self),
);
state.serialize_field("typeArguments", &self.type_arguments);
state.serialize_span(self.span);
state.end();
Expand Down
115 changes: 115 additions & 0 deletions crates/oxc_ast/src/serialize/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,118 @@ impl ESTree for TSFunctionTypeParams<'_, '_> {
Concat2(&fn_type.this_param, fn_type.params.as_ref()).serialize(serializer);
}
}

/// Serializer for `qualifier` field of `TSImportType`.
///
/// Our AST represents the qualifier as `TSImportTypeQualifier` with `IdentifierName` nodes.
/// TS-ESTree represents it as `TSQualifiedName` with `Identifier` nodes.
#[ast_meta]
#[estree(
ts_type = "TSQualifiedName | IdentifierName | null",
raw_deser = "
let qualifier = DESER[Option<TSImportTypeQualifier>](POS_OFFSET.qualifier);
if (qualifier !== null) {
if (qualifier.type === 'IdentifierName') {
qualifier = {
type: 'Identifier',
decorators: [],
name: qualifier.name,
optional: false,
typeAnnotation: null,
start: qualifier.start,
end: qualifier.end,
};
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
// Convert TSImportTypeQualifiedName to TSQualifiedName
const convertQualifier = (q) => {
if (q.type === 'IdentifierName') {
return {
type: 'Identifier',
decorators: [],
name: q.name,
optional: false,
typeAnnotation: null,
start: q.start,
end: q.end,
};
} else if (q.type === 'TSImportTypeQualifiedName') {
return {
type: 'TSQualifiedName',
left: convertQualifier(q.left),
right: {
type: 'Identifier',
decorators: [],
name: q.right.name,
optional: false,
typeAnnotation: null,
start: q.right.start,
end: q.right.end,
},
start: q.start,
end: q.end,
};
}
return q;
};
qualifier = convertQualifier(qualifier);
}
}
qualifier
"
)]
pub struct TSImportTypeQualifierConverter<'a, 'b>(pub &'b TSImportType<'a>);

impl ESTree for TSImportTypeQualifierConverter<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match &self.0.qualifier {
None => None::<()>.serialize(serializer),
Some(qualifier) => {
TSImportTypeQualifierAsQualifiedName(qualifier).serialize(serializer);
}
}
}
}

struct TSImportTypeQualifierAsQualifiedName<'a, 'b>(&'b TSImportTypeQualifier<'a>);

impl ESTree for TSImportTypeQualifierAsQualifiedName<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
match self.0 {
TSImportTypeQualifier::Identifier(ident) => {
// Convert IdentifierName to Identifier
let mut state = serializer.serialize_struct();
state.serialize_field("type", &JsonSafeString("Identifier"));
state.serialize_field("decorators", &Vec::<Decorator>::new().as_slice());
state.serialize_field("name", &ident.name);
state.serialize_field("optional", &false);
state.serialize_field("typeAnnotation", &None::<()>);
state.serialize_span(ident.span);
state.end();
}
TSImportTypeQualifier::QualifiedName(name) => {
// Convert TSImportTypeQualifiedName to TSQualifiedName
let mut state = serializer.serialize_struct();
state.serialize_field("type", &JsonSafeString("TSQualifiedName"));
state.serialize_field("left", &TSImportTypeQualifierAsQualifiedName(&name.left));
state.serialize_field("right", &IdentifierAsIdentifier(&name.right));
state.serialize_span(name.span);
state.end();
}
}
}
}

struct IdentifierAsIdentifier<'a, 'b>(&'b IdentifierName<'a>);

impl ESTree for IdentifierAsIdentifier<'_, '_> {
fn serialize<S: Serializer>(&self, serializer: S) {
let mut state = serializer.serialize_struct();
state.serialize_field("type", &JsonSafeString("Identifier"));
state.serialize_field("decorators", &Vec::<Decorator>::new().as_slice());
state.serialize_field("name", &self.0.name);
state.serialize_field("optional", &false);
state.serialize_field("typeAnnotation", &None::<()>);
state.serialize_span(self.0.span);
state.end();
}
}
49 changes: 48 additions & 1 deletion napi/parser/generated/deserialize/js.js
Original file line number Diff line number Diff line change
Expand Up @@ -1874,11 +1874,58 @@ function deserializeTSTypeQuery(pos) {
}

function deserializeTSImportType(pos) {
let qualifier = deserializeOptionTSImportTypeQualifier(pos + 32);
if (qualifier !== null) {
if (qualifier.type === 'IdentifierName') {
qualifier = {
type: 'Identifier',
decorators: [],
name: qualifier.name,
optional: false,
typeAnnotation: null,
start: qualifier.start,
end: qualifier.end,
};
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
// Convert TSImportTypeQualifiedName to TSQualifiedName
const convertQualifier = (q) => {
if (q.type === 'IdentifierName') {
return {
type: 'Identifier',
decorators: [],
name: q.name,
optional: false,
typeAnnotation: null,
start: q.start,
end: q.end,
};
} else if (q.type === 'TSImportTypeQualifiedName') {
return {
type: 'TSQualifiedName',
left: convertQualifier(q.left),
right: {
type: 'Identifier',
decorators: [],
name: q.right.name,
optional: false,
typeAnnotation: null,
start: q.right.start,
end: q.right.end,
},
start: q.start,
end: q.end,
};
}
return q;
};
qualifier = convertQualifier(qualifier);
}
}
return {
type: 'TSImportType',
argument: deserializeTSType(pos + 8),
options: deserializeOptionBoxObjectExpression(pos + 24),
qualifier: deserializeOptionTSImportTypeQualifier(pos + 32),
qualifier,
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 48),
start: deserializeU32(pos),
end: deserializeU32(pos + 4),
Expand Down
49 changes: 48 additions & 1 deletion napi/parser/generated/deserialize/ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -2005,11 +2005,58 @@ function deserializeTSTypeQuery(pos) {
}

function deserializeTSImportType(pos) {
let qualifier = deserializeOptionTSImportTypeQualifier(pos + 32);
if (qualifier !== null) {
if (qualifier.type === 'IdentifierName') {
qualifier = {
type: 'Identifier',
decorators: [],
name: qualifier.name,
optional: false,
typeAnnotation: null,
start: qualifier.start,
end: qualifier.end,
};
} else if (qualifier.type === 'TSImportTypeQualifiedName') {
// Convert TSImportTypeQualifiedName to TSQualifiedName
const convertQualifier = (q) => {
if (q.type === 'IdentifierName') {
return {
type: 'Identifier',
decorators: [],
name: q.name,
optional: false,
typeAnnotation: null,
start: q.start,
end: q.end,
};
} else if (q.type === 'TSImportTypeQualifiedName') {
return {
type: 'TSQualifiedName',
left: convertQualifier(q.left),
right: {
type: 'Identifier',
decorators: [],
name: q.right.name,
optional: false,
typeAnnotation: null,
start: q.right.start,
end: q.right.end,
},
start: q.start,
end: q.end,
};
}
return q;
};
qualifier = convertQualifier(qualifier);
}
}
return {
type: 'TSImportType',
argument: deserializeTSType(pos + 8),
options: deserializeOptionBoxObjectExpression(pos + 24),
qualifier: deserializeOptionTSImportTypeQualifier(pos + 32),
qualifier,
typeArguments: deserializeOptionBoxTSTypeParameterInstantiation(pos + 48),
start: deserializeU32(pos),
end: deserializeU32(pos + 4),
Expand Down
2 changes: 1 addition & 1 deletion npm/oxc-types/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1351,7 +1351,7 @@ export interface TSImportType extends Span {
type: 'TSImportType';
argument: TSType;
options: ObjectExpression | null;
qualifier: TSImportTypeQualifier | null;
qualifier: TSQualifiedName | IdentifierName | null;
typeArguments: TSTypeParameterInstantiation | null;
}

Expand Down
10 changes: 1 addition & 9 deletions tasks/coverage/snapshots/estree_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,10 @@ commit: 81c95189

estree_typescript Summary:
AST Parsed : 8575/8575 (100.00%)
Positive Passed: 8568/8575 (99.92%)
Mismatch: tasks/coverage/typescript/tests/cases/compiler/importTypeTypeofClassStaticLookup.ts

Positive Passed: 8572/8575 (99.97%)
Mismatch: tasks/coverage/typescript/tests/cases/conformance/jsx/jsxReactTestSuite.tsx

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

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

Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeAmbient.ts

Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeGenericTypes.ts

Mismatch: tasks/coverage/typescript/tests/cases/conformance/types/import/importTypeLocal.ts

Loading