// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. use serde::Serialize; use swc_common; use swc_common::SourceMap; use swc_common::Spanned; use swc_ecma_ast; use super::function::function_to_function_def; use super::function::FunctionDef; use super::parser::DocParser; use super::ts_type::ts_entity_name_to_name; use super::ts_type::ts_type_ann_to_def; use super::ts_type::TsTypeDef; use super::Location; use super::ParamDef; #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ClassConstructorDef { pub js_doc: Option, pub accessibility: Option, pub name: String, pub params: Vec, pub location: Location, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ClassPropertyDef { pub js_doc: Option, pub ts_type: Option, pub readonly: bool, pub accessibility: Option, pub is_abstract: bool, pub is_static: bool, pub name: String, pub location: Location, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ClassMethodDef { pub js_doc: Option, // pub ts_type: Option, // pub readonly: bool, pub accessibility: Option, pub is_abstract: bool, pub is_static: bool, pub name: String, pub kind: swc_ecma_ast::MethodKind, pub function_def: FunctionDef, pub location: Location, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ClassDef { // TODO: decorators, type_params, super_type_params pub is_abstract: bool, pub constructors: Vec, pub properties: Vec, pub methods: Vec, pub super_class: Option, pub implements: Vec, } fn prop_name_to_string( source_map: &SourceMap, prop_name: &swc_ecma_ast::PropName, ) -> String { use swc_ecma_ast::PropName; match prop_name { PropName::Ident(ident) => ident.sym.to_string(), PropName::Str(str_) => str_.value.to_string(), PropName::Num(num) => num.value.to_string(), PropName::Computed(comp_prop_name) => { source_map.span_to_snippet(comp_prop_name.span).unwrap() } } } pub fn get_doc_for_class_decl( doc_parser: &DocParser, class_decl: &swc_ecma_ast::ClassDecl, ) -> (String, ClassDef) { let mut constructors = vec![]; let mut methods = vec![]; let mut properties = vec![]; let super_class: Option = match &class_decl.class.super_class { Some(boxed) => { use swc_ecma_ast::Expr; let expr: &Expr = &**boxed; match expr { Expr::Ident(ident) => Some(ident.sym.to_string()), _ => None, } } None => None, }; let implements: Vec = class_decl .class .implements .iter() .map(|expr| ts_entity_name_to_name(&expr.expr)) .collect(); for member in &class_decl.class.body { use swc_ecma_ast::ClassMember::*; match member { Constructor(ctor) => { let ctor_js_doc = doc_parser.js_doc_for_span(ctor.span()); let constructor_name = prop_name_to_string(&doc_parser.source_map, &ctor.key); let mut params = vec![]; for param in &ctor.params { use swc_ecma_ast::Pat; use swc_ecma_ast::PatOrTsParamProp::*; let param_def = match param { Pat(pat) => match pat { Pat::Ident(ident) => { let ts_type = ident .type_ann .as_ref() .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); ParamDef { name: ident.sym.to_string(), ts_type, } } _ => ParamDef { name: "".to_string(), ts_type: None, }, }, TsParamProp(_) => ParamDef { name: "".to_string(), ts_type: None, }, }; params.push(param_def); } let constructor_def = ClassConstructorDef { js_doc: ctor_js_doc, accessibility: ctor.accessibility, name: constructor_name, params, location: doc_parser .source_map .lookup_char_pos(ctor.span.lo()) .into(), }; constructors.push(constructor_def); } Method(class_method) => { let method_js_doc = doc_parser.js_doc_for_span(class_method.span()); let method_name = prop_name_to_string(&doc_parser.source_map, &class_method.key); let fn_def = function_to_function_def(doc_parser, &class_method.function); let method_def = ClassMethodDef { js_doc: method_js_doc, accessibility: class_method.accessibility, is_abstract: class_method.is_abstract, is_static: class_method.is_static, name: method_name, kind: class_method.kind, function_def: fn_def, location: doc_parser .source_map .lookup_char_pos(class_method.span.lo()) .into(), }; methods.push(method_def); } ClassProp(class_prop) => { let prop_js_doc = doc_parser.js_doc_for_span(class_prop.span()); let ts_type = class_prop .type_ann .as_ref() .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); use swc_ecma_ast::Expr; let prop_name = match &*class_prop.key { Expr::Ident(ident) => ident.sym.to_string(), _ => "".to_string(), }; let prop_def = ClassPropertyDef { js_doc: prop_js_doc, ts_type, readonly: class_prop.readonly, is_abstract: class_prop.is_abstract, is_static: class_prop.is_static, accessibility: class_prop.accessibility, name: prop_name, location: doc_parser .source_map .lookup_char_pos(class_prop.span.lo()) .into(), }; properties.push(prop_def); } // TODO: TsIndexSignature(_) => {} PrivateMethod(_) => {} PrivateProp(_) => {} } } let class_name = class_decl.ident.sym.to_string(); let class_def = ClassDef { is_abstract: class_decl.class.is_abstract, super_class, implements, constructors, properties, methods, }; (class_name, class_def) }