diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index 5d66f649b9..a14cf8a1e7 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -61,7 +61,9 @@ use deno_ast::swc::ast::TsFnParam; use deno_ast::swc::ast::TsIndexSignature; use deno_ast::swc::ast::TsLit; use deno_ast::swc::ast::TsLitType; +use deno_ast::swc::ast::TsModuleName; use deno_ast::swc::ast::TsModuleRef; +use deno_ast::swc::ast::TsNamespaceBody; use deno_ast::swc::ast::TsParamPropParam; use deno_ast::swc::ast::TsThisTypeOrIdent; use deno_ast::swc::ast::TsType; @@ -965,8 +967,10 @@ fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef { Expr::TsConstAssertion(node) => { let expr = serialize_expr(ctx, node.expr.as_ref()); - // TODO(@marvinhagemeister): type_ann - ctx.write_ts_as_expr(&node.span, expr, NodeRef(0)) + let type_name = ctx.write_identifier(&node.span, "const", false, None); + let type_ann = ctx.write_ts_type_ref(&node.span, type_name, None); + + ctx.write_ts_as_expr(&node.span, expr, type_ann) } Expr::TsNonNull(node) => { let expr = serialize_expr(ctx, node.expr.as_ref()); @@ -1278,8 +1282,6 @@ fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { .decls .iter() .map(|decl| { - // TODO(@marvinhagemeister): Definite? - let ident = serialize_pat(ctx, &decl.name); let init = decl .init @@ -1309,8 +1311,6 @@ fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { .decls .iter() .map(|decl| { - // TODO(@marvinhagemeister): Definite? - let ident = serialize_pat(ctx, &decl.name); let init = decl .init @@ -1397,8 +1397,30 @@ fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { let body = ctx.write_ts_enum_body(&node.span, members); ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body) } - Decl::TsModule(_) => { - todo!() + Decl::TsModule(node) => { + let ident = match node.id { + TsModuleName::Ident(ident) => serialize_ident(ctx, &ident), + TsModuleName::Str(str_lit) => { + serialize_lit(ctx, &Lit::Str(str_lit.clone())) + } + }; + + let body = node.body.as_ref().map(|body| match body { + TsNamespaceBody::TsModuleBlock(mod_block) => { + let items = mod_block.body; + // TODO + ctx.write_ts_module_block(&mod_block.span, items) + } + TsNamespaceBody::TsNamespaceDecl(ts_namespace_decl) => todo!(), + }); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + body, + ) } } } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index 4c83a5fcab..f04da626ff 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -33,6 +33,8 @@ pub enum AstNode { TSNamespaceExportDeclaration, TSImportEqualsDeclaration, TSExternalModuleReference, + TSModuleDeclaration, + TSModuleBlock, // Decls ClassDeclaration, @@ -257,6 +259,7 @@ pub enum AstProp { Finalizer, Flags, Generator, + Global, Handler, Id, In, @@ -327,8 +330,6 @@ pub enum AstProp { Value, // Last value is used for max value } -// TODO: Feels like there should be an easier way to iterater over an -// enum in Rust and lowercase the first letter. impl Display for AstProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { @@ -382,6 +383,7 @@ impl Display for AstProp { AstProp::Finalizer => "finalizer", AstProp::Flags => "flags", AstProp::Generator => "generator", + AstProp::Global => "global", AstProp::Handler => "handler", AstProp::Id => "id", AstProp::In => "in", @@ -472,8 +474,6 @@ impl AstBufSerializer for TsEsTreeBuilder { } } -// TODO: Add a builder API to make it easier to convert from different source -// ast formats. impl TsEsTreeBuilder { pub fn new() -> Self { // Max values @@ -670,7 +670,7 @@ impl TsEsTreeBuilder { let id = self .ctx .append_node(AstNode::TSExternalModuleReference, span); - self.ctx.write_str(AstProp::Expression, expr); + self.ctx.write_ref(AstProp::Expression, &id, expr); self.ctx.commit_node(id) } @@ -1856,6 +1856,33 @@ impl TsEsTreeBuilder { self.ctx.commit_node(id) } + pub fn write_ts_module_decl( + &mut self, + span: &Span, + is_declare: bool, + is_global: bool, + ident: NodeRef, + body: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleDeclaration, span); + + self.ctx.write_bool(AstProp::Declare, is_declare); + self.ctx.write_bool(AstProp::Global, is_global); + self.ctx.write_ref(AstProp::Id, &id, ident); + self.ctx.write_maybe_ref(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + + pub fn write_ts_module_block( + &mut self, + span: &Span, + body: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSModuleBlock, span); + self.ctx.write_ref_vec(AstProp::Body, &id, body); + self.ctx.commit_node(id) + } + pub fn write_ts_class_implements( &mut self, span: &Span, diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index a6890a8862..cd874ff859 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -721,7 +721,24 @@ Deno.test("Plugin - Literal", async (t) => { await testSnapshot(t, "/foo/g", "Literal"); }); -Deno.test("Plugin - TS Interface", async (t) => { +Deno.test("Plugin - TSAsExpression", async (t) => { + await testSnapshot(t, "a as any", "TSAsExpression"); + await testSnapshot(t, '"foo" as const', "TSAsExpression"); +}); + +Deno.test("Plugin - TSEnumDeclaration", async (t) => { + await testSnapshot(t, "enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "const enum Foo {}", "TSEnumDeclaration"); + await testSnapshot(t, "enum Foo { A, B }", "TSEnumDeclaration"); + await testSnapshot(t, 'enum Foo { "a-b" }', "TSEnumDeclaration"); + await testSnapshot( + t, + "enum Foo { A = 1, B = 2, C = A | B }", + "TSEnumDeclaration", + ); +}); + +Deno.test("Plugin - TSInterface", async (t) => { await testSnapshot(t, "interface A {}", "TSInterface"); await testSnapshot(t, "interface A {}", "TSInterface"); await testSnapshot(t, "interface A extends Foo, Bar {}", "TSInterface"); @@ -736,4 +753,37 @@ Deno.test("Plugin - TS Interface", async (t) => { await testSnapshot(t, "interface A { (a: T): T }", "TSInterface"); await testSnapshot(t, "interface A { new (a: T): T }", "TSInterface"); await testSnapshot(t, "interface A { a: new (a: T) => T }", "TSInterface"); + await testSnapshot(t, "interface A { get a(): string }", "TSInterface"); + await testSnapshot(t, "interface A { set a(v: string) }", "TSInterface"); +}); + +Deno.test("Plugin - TSSatisfiesExpression", async (t) => { + await testSnapshot(t, "{} satisfies A", "TSSatisfiesExpression"); +}); + +Deno.test("Plugin - TSTypeAliasDeclaration", async (t) => { + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "type A = any", "TSTypeAliasDeclaration"); + await testSnapshot(t, "declare type A = any", "TSTypeAliasDeclaration"); +}); + +Deno.test("Plugin - TSNonNullExpression", async (t) => { + await testSnapshot(t, "a!", "TSNonNullExpression"); +}); + +Deno.test("Plugin - TSUnionType", async (t) => { + await testSnapshot(t, "type A = B | C", "TSUnionType"); +}); + +Deno.test("Plugin - TSIntersectionType", async (t) => { + await testSnapshot(t, "type A = B & C", "TSIntersectionType"); +}); + +Deno.test("Plugin - TSModuleDeclaration", async (t) => { + await testSnapshot(t, "module A;", "TSModuleDeclaration"); + await testSnapshot( + t, + "declare module A { export function A(): void }", + "TSModuleDeclaration", + ); });