diff --git a/cli/js/40_lint_selector.js b/cli/js/40_lint_selector.js index 53be6cd81d..fc99e3825c 100644 --- a/cli/js/40_lint_selector.js +++ b/cli/js/40_lint_selector.js @@ -745,7 +745,7 @@ export function compileSelector(selector) { fn = matchNthChild(node, fn); break; case PSEUDO_HAS: - // FIXME + // TODO(@marvinhagemeister) // fn = matchIs(part, fn); throw new Error("TODO: :has"); case PSEUDO_NOT: diff --git a/cli/tools/lint/ast_buffer/swc.rs b/cli/tools/lint/ast_buffer/swc.rs index a14cf8a1e7..cee2f92e07 100644 --- a/cli/tools/lint/ast_buffer/swc.rs +++ b/cli/tools/lint/ast_buffer/swc.rs @@ -983,20 +983,8 @@ fn serialize_expr(ctx: &mut TsEsTreeBuilder, expr: &Expr) -> NodeRef { ctx.write_ts_as_expr(&node.span, expr, type_ann) } Expr::TsInstantiation(_) => { - // let raw = ctx.header(AstNode::TsInstantiation, parent, &node.span); - // let expr_pos = ctx.ref_field(AstProp::Expression); - // let type_args_pos = ctx.ref_field(AstProp::TypeArguments); - // let pos = ctx.commit_schema(raw); - - // let expr = serialize_expr(ctx, node.expr.as_ref(), pos); - - // let type_arg = serialize_ts_param_inst(ctx, node.type_args.as_ref(), pos); - - // ctx.write_ref(expr_pos, expr); - // ctx.write_ref(type_args_pos, type_arg); - - // pos - todo!() + // Invalid syntax + unreachable!() } Expr::TsSatisfies(node) => { let expr = serialize_expr(ctx, node.expr.as_ref()); @@ -1052,7 +1040,6 @@ fn serialize_prop_or_spread( let mut method = false; let mut kind = "init"; - // FIXME: optional let (key, value) = match prop.as_ref() { Prop::Shorthand(ident) => { shorthand = true; @@ -1095,8 +1082,8 @@ fn serialize_prop_or_spread( body: getter_prop.body.clone(), is_generator: false, is_async: false, - type_params: None, // FIXME - return_type: None, + type_params: None, + return_type: getter_prop.type_ann.clone(), }), }), ); @@ -1398,21 +1385,17 @@ fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { ctx.write_ts_enum(&node.span, node.declare, node.is_const, id, body) } Decl::TsModule(node) => { - let ident = match node.id { + 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!(), - }); + let body = node + .body + .as_ref() + .map(|body| serialize_ts_namespace_body(ctx, body)); ctx.write_ts_module_decl( &node.span, @@ -1425,6 +1408,38 @@ fn serialize_decl(ctx: &mut TsEsTreeBuilder, decl: &Decl) -> NodeRef { } } +fn serialize_ts_namespace_body( + ctx: &mut TsEsTreeBuilder, + node: &TsNamespaceBody, +) -> NodeRef { + match node { + TsNamespaceBody::TsModuleBlock(mod_block) => { + let items = mod_block + .body + .iter() + .map(|item| match item { + ModuleItem::ModuleDecl(decl) => serialize_module_decl(ctx, decl), + ModuleItem::Stmt(stmt) => serialize_stmt(ctx, stmt), + }) + .collect::>(); + + ctx.write_ts_module_block(&mod_block.span, items) + } + TsNamespaceBody::TsNamespaceDecl(node) => { + let ident = serialize_ident(ctx, &node.id); + let body = serialize_ts_namespace_body(ctx, &node.body); + + ctx.write_ts_module_decl( + &node.span, + node.declare, + node.global, + ident, + Some(body), + ) + } + } +} + fn serialize_ts_type_elem( ctx: &mut TsEsTreeBuilder, node: &TsTypeElement, @@ -1483,38 +1498,26 @@ fn serialize_ts_type_elem( ctx.write_ts_setter_sig(&sig.span, key, param) } - TsTypeElement::TsMethodSignature(_sig) => { - todo!() - // let raw = ctx.header(AstNode::TSMethodSignature, pos, &sig.span); - // let computed_pos = ctx.bool_field(AstProp::Computed); - // let optional_pos = ctx.bool_field(AstProp::Optional); - // let readonly_pos = ctx.bool_field(AstProp::Readonly); - // // TODO: where is this coming from? - // let _static_bos = ctx.bool_field(AstProp::Static); - // let kind_pos = ctx.str_field(AstProp::Kind); - // let key_pos = ctx.ref_field(AstProp::Key); - // let params_pos = - // ctx.ref_vec_field(AstProp::Params, sig.params.len()); - // let return_type_pos = ctx.ref_field(AstProp::ReturnType); - // let item_pos = ctx.commit_schema(raw); + TsTypeElement::TsMethodSignature(sig) => { + let key = serialize_expr(ctx, &sig.key); + let type_parms = + maybe_serialize_ts_type_param_decl(ctx, &sig.type_params); + let params = sig + .params + .iter() + .map(|param| serialize_ts_fn_param(ctx, param)) + .collect::>(); + let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); - // let key = serialize_expr(ctx, sig.key.as_ref()); - // let params = sig - // .params - // .iter() - // .map(|param| serialize_ts_fn_param(ctx, param)) - // .collect::>(); - // let return_type = maybe_serialize_ts_type_ann(ctx, &sig.type_ann); - - // ctx.write_bool(computed_pos, false); - // ctx.write_bool(optional_pos, false); - // ctx.write_bool(readonly_pos, false); - // ctx.write_str(kind_pos, "method"); - // ctx.write_ref(key_pos, key); - // ctx.write_refs(params_pos, params); - // ctx.write_maybe_ref(return_type_pos, return_type); - - // item_pos + ctx.write_ts_method_sig( + &sig.span, + sig.computed, + sig.optional, + key, + type_parms, + params, + return_type, + ) } TsTypeElement::TsIndexSignature(sig) => serialize_ts_index_sig(ctx, sig), } @@ -1634,7 +1637,10 @@ fn serialize_jsx_opening_element( ) -> NodeRef { let name = serialize_jsx_element_name(ctx, &node.name); - // FIXME: type args + let type_args = node + .type_args + .as_ref() + .map(|arg| serialize_ts_param_inst(ctx, arg)); let attrs = node .attrs @@ -1668,7 +1674,13 @@ fn serialize_jsx_opening_element( }) .collect::>(); - ctx.write_jsx_opening_elem(&node.span, node.self_closing, name, attrs) + ctx.write_jsx_opening_elem( + &node.span, + node.self_closing, + name, + attrs, + type_args, + ) } fn serialize_jsx_container_expr( @@ -1753,8 +1765,6 @@ fn serialize_pat(ctx: &mut TsEsTreeBuilder, pat: &Pat) -> NodeRef { ObjectPatProp::Assign(assign_pat_prop) => { let ident = serialize_ident(ctx, &assign_pat_prop.key.id); - // TODO(@marvinhagemeister): This seems wrong - let value = assign_pat_prop .value .as_ref() @@ -2292,9 +2302,14 @@ fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { ctx.write_ts_type_query(&node.span, expr_name, type_args) } - TsType::TsTypeLit(_) => { - // TODO: Not sure what this is - todo!() + TsType::TsTypeLit(node) => { + let members = node + .members + .iter() + .map(|member| serialize_ts_type_elem(ctx, member)) + .collect::>(); + + ctx.write_ts_type_lit(&node.span, members) } TsType::TsArrayType(node) => { let elem = serialize_ts_type(ctx, &node.elem_type); @@ -2318,7 +2333,10 @@ fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { ctx.write_ts_tuple_type(&node.span, children) } - TsType::TsOptionalType(_) => todo!(), + TsType::TsOptionalType(node) => { + let type_ann = serialize_ts_type(ctx, &node.type_ann); + ctx.write_ts_optional_type(&node.span, type_ann) + } TsType::TsRestType(node) => { let type_ann = serialize_ts_type(ctx, &node.type_ann); ctx.write_ts_rest_type(&node.span, type_ann) @@ -2355,7 +2373,10 @@ fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { let param = serialize_ts_type_param(ctx, &node.type_param); ctx.write_ts_infer_type(&node.span, param) } - TsType::TsParenthesizedType(_) => todo!(), + TsType::TsParenthesizedType(node) => { + // Not materialized in TSEstree + serialize_ts_type(ctx, &node.type_ann) + } TsType::TsTypeOperator(node) => { let type_ann = serialize_ts_type(ctx, &node.type_ann); @@ -2374,20 +2395,18 @@ fn serialize_ts_type(ctx: &mut TsEsTreeBuilder, node: &TsType) -> NodeRef { ctx.write_ts_indexed_access_type(&node.span, index, obj) } TsType::TsMappedType(node) => { - // let opt_pos = - // create_true_plus_minus_field(ctx, AstProp::Optional, node.optional); - // let readonly_pos = - // create_true_plus_minus_field(ctx, AstProp::Readonly, node.readonly); - let name = maybe_serialize_ts_type(ctx, &node.name_type); let type_ann = maybe_serialize_ts_type(ctx, &node.type_ann); let type_param = serialize_ts_type_param(ctx, &node.type_param); - // FIXME: true plus minus - // write_true_plus_minus(ctx, opt_pos, node.optional); - // write_true_plus_minus(ctx, readonly_pos, node.readonly); - - ctx.write_ts_mapped_type(&node.span, name, type_ann, type_param) + ctx.write_ts_mapped_type( + &node.span, + node.readonly, + node.optional, + name, + type_ann, + type_param, + ) } TsType::TsLitType(node) => serialize_ts_lit_type(ctx, node), TsType::TsTypePredicate(node) => { @@ -2458,7 +2477,12 @@ fn serialize_ts_entity_name( node: &TsEntityName, ) -> NodeRef { match &node { - TsEntityName::TsQualifiedName(_) => todo!(), + TsEntityName::TsQualifiedName(node) => { + let left = serialize_ts_entity_name(ctx, &node.left); + let right = serialize_ident_name(ctx, &node.right); + + ctx.write_ts_qualified_name(&node.span, left, right) + } TsEntityName::Ident(ident) => serialize_ident(ctx, ident), } } diff --git a/cli/tools/lint/ast_buffer/ts_estree.rs b/cli/tools/lint/ast_buffer/ts_estree.rs index f04da626ff..820711b289 100644 --- a/cli/tools/lint/ast_buffer/ts_estree.rs +++ b/cli/tools/lint/ast_buffer/ts_estree.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; use std::fmt::Display; use deno_ast::swc::common::Span; +use deno_ast::view::TruePlusMinus; use super::buffer::AstBufSerializer; use super::buffer::NodeRef; @@ -149,6 +150,7 @@ pub enum AstNode { TSTypeReference, TSThisType, TSLiteralType, + TSTypeLiteral, TSInferType, TSConditionalType, TSUnionType, @@ -174,6 +176,8 @@ pub enum AstNode { TSEmptyBodyFunctionExpression, TSParameterProperty, TSConstructSignatureDeclaration, + TSQualifiedName, + TSOptionalType, TSAnyKeyword, TSBigIntKeyword, @@ -1748,12 +1752,16 @@ impl TsEsTreeBuilder { self_closing: bool, name: NodeRef, attrs: Vec, + type_args: Option, ) -> NodeRef { let id = self.ctx.append_node(AstNode::JSXOpeningElement, span); self.ctx.write_bool(AstProp::SelfClosing, self_closing); self.ctx.write_ref(AstProp::Name, &id, name); self.ctx.write_ref_vec(AstProp::Attributes, &id, attrs); + self + .ctx + .write_maybe_ref(AstProp::TypeArguments, &id, type_args); self.ctx.commit_node(id) } @@ -2269,6 +2277,35 @@ impl TsEsTreeBuilder { self.ctx.commit_node(id) } + pub fn write_ts_method_sig( + &mut self, + span: &Span, + is_computed: bool, + is_optional: bool, + key: NodeRef, + type_params: Option, + params: Vec, + return_type: Option, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSMethodSignature, span); + + self.ctx.write_bool(AstProp::Computed, is_computed); + self.ctx.write_bool(AstProp::Optional, is_optional); + self.ctx.write_bool(AstProp::Readonly, false); + self.ctx.write_bool(AstProp::Static, false); + self.ctx.write_str(AstProp::Kind, "method"); + self.ctx.write_ref(AstProp::Key, &id, key); + self + .ctx + .write_maybe_ref(AstProp::TypeParameters, &id, type_params); + self.ctx.write_ref_vec(AstProp::Params, &id, params); + self + .ctx + .write_maybe_ref(AstProp::ReturnType, &id, return_type); + + self.ctx.commit_node(id) + } + pub fn write_ts_interface_heritage( &mut self, span: &Span, @@ -2417,12 +2454,16 @@ impl TsEsTreeBuilder { pub fn write_ts_mapped_type( &mut self, span: &Span, + readonly: Option, + optional: Option, name: Option, type_ann: Option, type_param: NodeRef, ) -> NodeRef { let id = self.ctx.append_node(AstNode::TSMappedType, span); + self.write_plus_minus_true(AstProp::Readonly, readonly); + self.write_plus_minus_true(AstProp::Optional, optional); self.ctx.write_maybe_ref(AstProp::NameType, &id, name); self .ctx @@ -2438,6 +2479,26 @@ impl TsEsTreeBuilder { self.ctx.commit_node(id) } + pub fn write_ts_type_lit( + &mut self, + span: &Span, + members: Vec, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSTypeLiteral, span); + self.ctx.write_ref_vec(AstProp::Members, &id, members); + self.ctx.commit_node(id) + } + + pub fn write_ts_optional_type( + &mut self, + span: &Span, + type_ann: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSOptionalType, span); + self.ctx.write_ref(AstProp::TypeAnnotation, &id, type_ann); + self.ctx.commit_node(id) + } + pub fn write_ts_type_ann( &mut self, span: &Span, @@ -2608,6 +2669,20 @@ impl TsEsTreeBuilder { self.ctx.commit_node(id) } + pub fn write_ts_qualified_name( + &mut self, + span: &Span, + left: NodeRef, + right: NodeRef, + ) -> NodeRef { + let id = self.ctx.append_node(AstNode::TSQualifiedName, span); + + self.ctx.write_ref(AstProp::Left, &id, left); + self.ctx.write_ref(AstProp::Right, &id, right); + + self.ctx.commit_node(id) + } + fn write_accessibility(&mut self, accessibility: Option) { if let Some(value) = accessibility { self.ctx.write_str(AstProp::Accessibility, &value); @@ -2615,6 +2690,19 @@ impl TsEsTreeBuilder { self.ctx.write_undefined(AstProp::Accessibility); } } + + fn write_plus_minus_true( + &mut self, + prop: AstProp, + value: Option, + ) { + match value { + Some(TruePlusMinus::Plus) => self.ctx.write_str(prop, "+"), + Some(TruePlusMinus::Minus) => self.ctx.write_str(prop, "-"), + Some(TruePlusMinus::True) => self.ctx.write_bool(prop, true), + _ => self.ctx.write_undefined(prop), + } + } } #[derive(Debug)] diff --git a/tests/unit/lint_plugin_test.ts b/tests/unit/lint_plugin_test.ts index cd874ff859..181bcbfb26 100644 --- a/tests/unit/lint_plugin_test.ts +++ b/tests/unit/lint_plugin_test.ts @@ -721,6 +721,24 @@ Deno.test("Plugin - Literal", async (t) => { await testSnapshot(t, "/foo/g", "Literal"); }); +Deno.test("Plugin - JSXElement + JSXOpeningElement + JSXClosingElement + JSXAttr", async (t) => { + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, '
', "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "
foo{2}
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, "
", "JSXElement"); + await testSnapshot(t, "", "JSXElement"); + await testSnapshot(t, " />", "JSXElement"); +}); + +Deno.test("Plugin - JSXFragment + JSXOpeningFragment + JSXClosingFragment", async (t) => { + await testSnapshot(t, "<>", "JSXFragment"); + await testSnapshot(t, "<>foo{2}", "JSXFragment"); +}); + Deno.test("Plugin - TSAsExpression", async (t) => { await testSnapshot(t, "a as any", "TSAsExpression"); await testSnapshot(t, '"foo" as const', "TSAsExpression"); @@ -755,6 +773,12 @@ Deno.test("Plugin - TSInterface", async (t) => { 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"); + + await testSnapshot( + t, + "interface A { a(arg?: any, ...args: any[]): any", + "TSInterface", + ); }); Deno.test("Plugin - TSSatisfiesExpression", async (t) => { @@ -787,3 +811,119 @@ Deno.test("Plugin - TSModuleDeclaration", async (t) => { "TSModuleDeclaration", ); }); + +Deno.test("Plugin - TSModuleDeclaration + TSModuleBlock", async (t) => { + await testSnapshot(t, "module A {}", "TSModuleDeclaration"); + await testSnapshot( + t, + "namespace A { namespace B {} }", + "TSModuleDeclaration", + ); +}); + +Deno.test("Plugin - TSQualifiedName", async (t) => { + await testSnapshot(t, "type A = a.b;", "TSQualifiedName"); + await testSnapshot( + t, + "declare module A { export function A(): void }", + "TSQualifiedName", + ); +}); + +Deno.test("Plugin - TSTypeLiteral", async (t) => { + await testSnapshot(t, "type A = { a: 1 };", "TSTypeLiteral"); +}); + +Deno.test("Plugin - TSOptionalType", async (t) => { + await testSnapshot(t, "type A = [number?]", "TSOptionalType"); +}); + +Deno.test("Plugin - TSRestType", async (t) => { + await testSnapshot(t, "type A = [...number[]]", "TSRestType"); +}); + +Deno.test("Plugin - TSConditionalType", async (t) => { + await testSnapshot( + t, + "type A = B extends C ? number : string;", + "TSConditionalType", + ); +}); + +Deno.test("Plugin - TSInferType", async (t) => { + await testSnapshot( + t, + "type A = T extends Array ? Item : T;", + "TSInferType", + ); +}); + +Deno.test("Plugin - TSTypeOperator", async (t) => { + await testSnapshot(t, "type A = keyof B", "TSTypeOperator"); + await testSnapshot(t, "declare const sym1: unique symbol;", "TSTypeOperator"); + await testSnapshot(t, "type A = readonly []", "TSTypeOperator"); +}); + +Deno.test("Plugin - TSMappedType", async (t) => { + await testSnapshot( + t, + "type A = { [P in keyof T]: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { -readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { +readonly [P in keyof T]: []; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]-?: boolean; };", + "TSMappedType", + ); + await testSnapshot( + t, + "type A = { [P in keyof T]+?: boolean; };", + "TSMappedType", + ); +}); + +Deno.test("Plugin - TSLiteralType", async (t) => { + await testSnapshot(t, "type A = true", "TSLiteralType"); + await testSnapshot(t, "type A = false", "TSLiteralType"); + await testSnapshot(t, "type A = 1", "TSLiteralType"); + await testSnapshot(t, "type A = 'foo''", "TSLiteralType"); +}); + +Deno.test("Plugin - TSTemplateLiteralType", async (t) => { + await testSnapshot( + t, + "type A = `a ${B}`", + "TSTemplateLiteralType", + ); +}); + +Deno.test("Plugin - TSTupleType + TSArrayType", async (t) => { + await testSnapshot(t, "type A = [number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [x: number]", "TSTupleType"); + await testSnapshot(t, "type A = [...x: number[]]", "TSTupleType"); +}); + +Deno.test("Plugin - TSTypeQuery", async (t) => { + await testSnapshot(t, "type A = typeof B", "TSTupleType"); +});