From 491b8e1cea76753397bdeb0aeb1598bc78d22c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 8 Apr 2020 17:03:42 +0200 Subject: [PATCH] feat(doc): handle function params and type params (#4672) --- cli/doc/class.rs | 48 ++++---- cli/doc/function.rs | 43 +++---- cli/doc/interface.rs | 101 ++++++---------- cli/doc/mod.rs | 3 + cli/doc/module.rs | 6 +- cli/doc/node.rs | 10 ++ cli/doc/params.rs | 83 +++++++++++++ cli/doc/parser.rs | 5 +- cli/doc/tests.rs | 244 ++++++++++++++++++++++++++++++++++++--- cli/doc/ts_type.rs | 177 ++++++++-------------------- cli/doc/ts_type_param.rs | 57 +++++++++ cli/doc/type_alias.rs | 18 ++- cli/doc/variable.rs | 12 +- 13 files changed, 518 insertions(+), 289 deletions(-) create mode 100644 cli/doc/params.rs create mode 100644 cli/doc/ts_type_param.rs diff --git a/cli/doc/class.rs b/cli/doc/class.rs index 82b6c441c2..410789041e 100644 --- a/cli/doc/class.rs +++ b/cli/doc/class.rs @@ -6,10 +6,15 @@ use serde::Serialize; use super::function::function_to_function_def; use super::function::FunctionDef; +use super::params::assign_pat_to_param_def; +use super::params::ident_to_param_def; +use super::params::pat_to_param_def; 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::ts_type_param::maybe_type_param_decl_to_type_param_defs; +use super::ts_type_param::TsTypeParamDef; use super::Location; use super::ParamDef; @@ -40,8 +45,6 @@ pub struct ClassPropertyDef { #[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, @@ -54,13 +57,14 @@ pub struct ClassMethodDef { #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ClassDef { - // TODO: decorators, type_params, super_type_params + // TODO(bartlomieju): decorators, super_type_params pub is_abstract: bool, pub constructors: Vec, pub properties: Vec, pub methods: Vec, pub super_class: Option, pub implements: Vec, + pub type_params: Vec, } fn prop_name_to_string( @@ -117,31 +121,20 @@ pub fn get_doc_for_class_decl( let mut params = vec![]; for param in &ctor.params { - use crate::swc_ecma_ast::Pat; use crate::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)); + Pat(pat) => pat_to_param_def(pat), + TsParamProp(ts_param_prop) => { + use swc_ecma_ast::TsParamPropParam; - ParamDef { - name: ident.sym.to_string(), - ts_type, + match &ts_param_prop.param { + TsParamPropParam::Ident(ident) => ident_to_param_def(ident), + TsParamPropParam::Assign(assign_pat) => { + assign_pat_to_param_def(assign_pat) } } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }, - TsParamProp(_) => ParamDef { - name: "".to_string(), - ts_type: None, - }, + } }; params.push(param_def); } @@ -162,8 +155,7 @@ pub fn get_doc_for_class_decl( 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 fn_def = function_to_function_def(&class_method.function); let method_def = ClassMethodDef { js_doc: method_js_doc, accessibility: class_method.accessibility, @@ -185,7 +177,7 @@ pub fn get_doc_for_class_decl( let ts_type = class_prop .type_ann .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); + .map(|rt| ts_type_ann_to_def(rt)); use crate::swc_ecma_ast::Expr; let prop_name = match &*class_prop.key { @@ -208,13 +200,16 @@ pub fn get_doc_for_class_decl( }; properties.push(prop_def); } - // TODO: + // TODO(bartlomieju): TsIndexSignature(_) => {} PrivateMethod(_) => {} PrivateProp(_) => {} } } + let type_params = maybe_type_param_decl_to_type_param_defs( + class_decl.class.type_params.as_ref(), + ); let class_name = class_decl.ident.sym.to_string(); let class_def = ClassDef { is_abstract: class_decl.class.is_abstract, @@ -223,6 +218,7 @@ pub fn get_doc_for_class_decl( constructors, properties, methods, + type_params, }; (class_name, class_def) diff --git a/cli/doc/function.rs b/cli/doc/function.rs index fbfd2d0154..4c101ab08e 100644 --- a/cli/doc/function.rs +++ b/cli/doc/function.rs @@ -1,11 +1,12 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::swc_ecma_ast; -use serde::Serialize; - -use super::parser::DocParser; +use super::params::pat_to_param_def; use super::ts_type::ts_type_ann_to_def; use super::ts_type::TsTypeDef; +use super::ts_type_param::maybe_type_param_decl_to_type_param_defs; +use super::ts_type_param::TsTypeParamDef; use super::ParamDef; +use crate::swc_ecma_ast; +use serde::Serialize; #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] @@ -14,57 +15,41 @@ pub struct FunctionDef { pub return_type: Option, pub is_async: bool, pub is_generator: bool, - // TODO: type_params, decorators + pub type_params: Vec, + // TODO(bartlomieju): decorators } pub fn function_to_function_def( - doc_parser: &DocParser, function: &swc_ecma_ast::Function, ) -> FunctionDef { let mut params = vec![]; for param in &function.params { - use crate::swc_ecma_ast::Pat; - - let param_def = match param { - 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, - }, - }; - + let param_def = pat_to_param_def(param); params.push(param_def); } let maybe_return_type = function .return_type .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); + .map(|rt| ts_type_ann_to_def(rt)); + + let type_params = + maybe_type_param_decl_to_type_param_defs(function.type_params.as_ref()); FunctionDef { params, return_type: maybe_return_type, is_async: function.is_async, is_generator: function.is_generator, + type_params, } } pub fn get_doc_for_fn_decl( - doc_parser: &DocParser, fn_decl: &swc_ecma_ast::FnDecl, ) -> (String, FunctionDef) { let name = fn_decl.ident.sym.to_string(); - let fn_def = function_to_function_def(doc_parser, &fn_decl.function); + let fn_def = function_to_function_def(&fn_decl.function); (name, fn_def) } diff --git a/cli/doc/interface.rs b/cli/doc/interface.rs index a09bd9c8a7..41ef0e7201 100644 --- a/cli/doc/interface.rs +++ b/cli/doc/interface.rs @@ -2,27 +2,29 @@ use crate::swc_ecma_ast; use serde::Serialize; +use super::params::ts_fn_param_to_param_def; use super::parser::DocParser; use super::ts_type::ts_type_ann_to_def; use super::ts_type::TsTypeDef; +use super::ts_type_param::maybe_type_param_decl_to_type_param_defs; +use super::ts_type_param::TsTypeParamDef; use super::Location; use super::ParamDef; #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct InterfaceMethodDef { - // TODO: type_params pub name: String, pub location: Location, pub js_doc: Option, pub params: Vec, pub return_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct InterfacePropertyDef { - // TODO: type_params pub name: String, pub location: Location, pub js_doc: Option, @@ -30,25 +32,27 @@ pub struct InterfacePropertyDef { pub computed: bool, pub optional: bool, pub ts_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct InterfaceCallSignatureDef { - // TODO: type_params pub location: Location, pub js_doc: Option, pub params: Vec, pub ts_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct InterfaceDef { - // TODO: extends, type params + // TODO(bartlomieju): extends pub methods: Vec, pub properties: Vec, pub call_signatures: Vec, + pub type_params: Vec, } fn expr_to_name(expr: &swc_ecma_ast::Expr) -> String { @@ -89,26 +93,7 @@ pub fn get_doc_for_ts_interface_decl( let mut params = vec![]; for param in &ts_method_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - 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, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } @@ -117,7 +102,11 @@ pub fn get_doc_for_ts_interface_decl( let maybe_return_type = ts_method_sig .type_ann .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); + .map(|rt| ts_type_ann_to_def(rt)); + + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_method_sig.type_params.as_ref(), + ); let method_def = InterfaceMethodDef { name, @@ -128,6 +117,7 @@ pub fn get_doc_for_ts_interface_decl( .into(), params, return_type: maybe_return_type, + type_params, }; methods.push(method_def); } @@ -141,33 +131,18 @@ pub fn get_doc_for_ts_interface_decl( let mut params = vec![]; for param in &ts_prop_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - 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, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } let ts_type = ts_prop_sig .type_ann .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); + .map(|rt| ts_type_ann_to_def(rt)); + + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_prop_sig.type_params.as_ref(), + ); let prop_def = InterfacePropertyDef { name, @@ -180,6 +155,7 @@ pub fn get_doc_for_ts_interface_decl( ts_type, computed: ts_prop_sig.computed, optional: ts_prop_sig.optional, + type_params, }; properties.push(prop_def); } @@ -188,33 +164,18 @@ pub fn get_doc_for_ts_interface_decl( let mut params = vec![]; for param in &ts_call_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - 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, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } let ts_type = ts_call_sig .type_ann .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)); + .map(|rt| ts_type_ann_to_def(rt)); + + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_call_sig.type_params.as_ref(), + ); let call_sig_def = InterfaceCallSignatureDef { js_doc: call_sig_js_doc, @@ -224,6 +185,7 @@ pub fn get_doc_for_ts_interface_decl( .into(), params, ts_type, + type_params, }; call_signatures.push(call_sig_def); } @@ -233,10 +195,15 @@ pub fn get_doc_for_ts_interface_decl( } } + let type_params = maybe_type_param_decl_to_type_param_defs( + interface_decl.type_params.as_ref(), + ); + let interface_def = InterfaceDef { methods, properties, call_signatures, + type_params, }; (interface_name, interface_def) diff --git a/cli/doc/mod.rs b/cli/doc/mod.rs index 4926dccd7b..b0f7cf60a8 100644 --- a/cli/doc/mod.rs +++ b/cli/doc/mod.rs @@ -6,9 +6,11 @@ pub mod interface; pub mod module; pub mod namespace; mod node; +pub mod params; pub mod parser; pub mod printer; pub mod ts_type; +pub mod ts_type_param; pub mod type_alias; pub mod variable; @@ -16,6 +18,7 @@ pub use node::DocNode; pub use node::DocNodeKind; pub use node::Location; pub use node::ParamDef; +pub use node::ParamKind; pub use parser::DocParser; #[cfg(test)] diff --git a/cli/doc/module.rs b/cli/doc/module.rs index ba62902dc4..d2284b36cf 100644 --- a/cli/doc/module.rs +++ b/cli/doc/module.rs @@ -38,8 +38,7 @@ pub fn get_doc_node_for_export_decl( } } Decl::Fn(fn_decl) => { - let (name, function_def) = - super::function::get_doc_for_fn_decl(doc_parser, fn_decl); + let (name, function_def) = super::function::get_doc_for_fn_decl(fn_decl); DocNode { kind: DocNodeKind::Function, name, @@ -55,8 +54,7 @@ pub fn get_doc_node_for_export_decl( } } Decl::Var(var_decl) => { - let (name, var_def) = - super::variable::get_doc_for_var_decl(doc_parser, var_decl); + let (name, var_def) = super::variable::get_doc_for_var_decl(var_decl); DocNode { kind: DocNodeKind::Variable, name, diff --git a/cli/doc/node.rs b/cli/doc/node.rs index 1be3ba7b1d..a6b11133ed 100644 --- a/cli/doc/node.rs +++ b/cli/doc/node.rs @@ -14,10 +14,20 @@ pub enum DocNodeKind { Namespace, } +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub enum ParamKind { + Identifier, + Rest, + Array, + Object, +} + #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ParamDef { pub name: String, + pub kind: ParamKind, pub ts_type: Option, } diff --git a/cli/doc/params.rs b/cli/doc/params.rs new file mode 100644 index 0000000000..04fd7f898e --- /dev/null +++ b/cli/doc/params.rs @@ -0,0 +1,83 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use crate::swc_ecma_ast; + +use super::ts_type::ts_type_ann_to_def; +use super::ParamDef; +use super::ParamKind; +use crate::swc_ecma_ast::Pat; +use crate::swc_ecma_ast::TsFnParam; + +pub fn ident_to_param_def(ident: &swc_ecma_ast::Ident) -> ParamDef { + let ts_type = ident.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt)); + + ParamDef { + name: ident.sym.to_string(), + kind: ParamKind::Identifier, + ts_type, + } +} + +fn rest_pat_to_param_def(rest_pat: &swc_ecma_ast::RestPat) -> ParamDef { + let name = match &*rest_pat.arg { + Pat::Ident(ident) => ident.sym.to_string(), + _ => "".to_string(), + }; + let ts_type = rest_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt)); + + ParamDef { + name, + kind: ParamKind::Rest, + ts_type, + } +} + +fn object_pat_to_param_def(object_pat: &swc_ecma_ast::ObjectPat) -> ParamDef { + let ts_type = object_pat + .type_ann + .as_ref() + .map(|rt| ts_type_ann_to_def(rt)); + + ParamDef { + name: "".to_string(), + kind: ParamKind::Object, + ts_type, + } +} + +fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef { + let ts_type = array_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt)); + + ParamDef { + name: "".to_string(), + kind: ParamKind::Array, + ts_type, + } +} + +pub fn assign_pat_to_param_def( + assign_pat: &swc_ecma_ast::AssignPat, +) -> ParamDef { + pat_to_param_def(&*assign_pat.left) +} + +pub fn pat_to_param_def(pat: &swc_ecma_ast::Pat) -> ParamDef { + match pat { + Pat::Ident(ident) => ident_to_param_def(ident), + Pat::Array(array_pat) => array_pat_to_param_def(array_pat), + Pat::Rest(rest_pat) => rest_pat_to_param_def(rest_pat), + Pat::Object(object_pat) => object_pat_to_param_def(object_pat), + Pat::Assign(assign_pat) => assign_pat_to_param_def(assign_pat), + _ => unreachable!(), + } +} + +pub fn ts_fn_param_to_param_def( + ts_fn_param: &swc_ecma_ast::TsFnParam, +) -> ParamDef { + match ts_fn_param { + TsFnParam::Ident(ident) => ident_to_param_def(ident), + TsFnParam::Array(array_pat) => array_pat_to_param_def(array_pat), + TsFnParam::Rest(rest_pat) => rest_pat_to_param_def(rest_pat), + TsFnParam::Object(object_pat) => object_pat_to_param_def(object_pat), + } +} diff --git a/cli/doc/parser.rs b/cli/doc/parser.rs index 7c67f8d090..4b37c5fae2 100644 --- a/cli/doc/parser.rs +++ b/cli/doc/parser.rs @@ -347,7 +347,7 @@ impl DocParser { return None; } let (name, function_def) = - super::function::get_doc_for_fn_decl(self, fn_decl); + super::function::get_doc_for_fn_decl(fn_decl); let (js_doc, location) = self.details_for_span(fn_decl.function.span); Some(DocNode { kind: DocNodeKind::Function, @@ -367,8 +367,7 @@ impl DocParser { if !var_decl.declare { return None; } - let (name, var_def) = - super::variable::get_doc_for_var_decl(self, var_decl); + let (name, var_def) = super::variable::get_doc_for_var_decl(var_decl); let (js_doc, location) = self.details_for_span(var_decl.span); Some(DocNode { kind: DocNodeKind::Variable, diff --git a/cli/doc/tests.rs b/cli/doc/tests.rs index 337f324667..fb2c696896 100644 --- a/cli/doc/tests.rs +++ b/cli/doc/tests.rs @@ -52,7 +52,7 @@ async fn export_fn() { * * Or not that many? */ -export function foo(a: string, b: number): void { +export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void { console.log("Hello world"); } "#; @@ -65,9 +65,11 @@ export function foo(a: string, b: number): void { "functionDef": { "isAsync": false, "isGenerator": false, + "typeParams": [], "params": [ { "name": "a", + "kind": "identifier", "tsType": { "keyword": "string", "kind": "keyword", @@ -76,12 +78,56 @@ export function foo(a: string, b: number): void { }, { "name": "b", + "kind": "identifier", "tsType": { "keyword": "number", "kind": "keyword", "repr": "number", }, }, + { + "name": "cb", + "kind": "identifier", + "tsType": { + "repr": "", + "kind": "fnOrConstructor", + "fnOrConstructor": { + "constructor": false, + "tsType": { + "keyword": "void", + "kind": "keyword", + "repr": "void" + }, + "typeParams": [], + "params": [{ + "kind": "rest", + "name": "cbArgs", + "tsType": { + "repr": "", + "kind": "array", + "array": { + "repr": "unknown", + "kind": "keyword", + "keyword": "unknown" + } + }, + }] + } + }, + }, + { + "name": "args", + "kind": "rest", + "tsType": { + "repr": "", + "kind": "array", + "array": { + "repr": "unknown", + "kind": "keyword", + "keyword": "unknown" + } + } + } ], "returnType": { "keyword": "void", @@ -98,6 +144,7 @@ export function foo(a: string, b: number): void { }, "name": "foo", }); + let actual = serde_json::to_value(entry).unwrap(); assert_eq!(actual, expected_json); @@ -107,6 +154,85 @@ export function foo(a: string, b: number): void { ); } +#[tokio::test] +async fn export_fn2() { + let source_code = r#" +interface AssignOpts { + a: string; + b: number; +} + +export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, ops: AssignOpts = {}): void { + console.log("Hello world"); +} +"#; + let loader = + TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]); + let entries = DocParser::new(loader).parse("test.ts").await.unwrap(); + assert_eq!(entries.len(), 1); + let entry = &entries[0]; + let expected_json = json!({ + "functionDef": { + "isAsync": false, + "isGenerator": false, + "typeParams": [], + "params": [ + { + "name": "", + "kind": "array", + "tsType": { + "repr": "", + "kind": "array", + "array": { + "repr": "number", + "kind": "keyword", + "keyword": "number" + } + } + }, + { + "name": "", + "kind": "object", + "tsType": null + }, + { + "name": "ops", + "kind": "identifier", + "tsType": { + "repr": "AssignOpts", + "kind": "typeRef", + "typeRef": { + "typeName": "AssignOpts", + "typeParams": null, + } + } + }, + ], + "returnType": { + "keyword": "void", + "kind": "keyword", + "repr": "void", + }, + }, + "jsDoc": null, + "kind": "function", + "location": { + "col": 0, + "filename": "test.ts", + "line": 7, + }, + "name": "foo", + }); + + let actual = serde_json::to_value(entry).unwrap(); + assert_eq!(actual, expected_json); + + assert!( + colors::strip_ansi_codes(super::printer::format(entries).as_str()) + .contains("foo") + ); +} + #[tokio::test] async fn export_const() { let source_code = @@ -180,6 +306,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { "isAbstract": false, "superClass": "Fizz", "implements": ["Buzz", "Aldrin"], + "typeParams": [], "constructors": [ { "jsDoc": "Constructor js doc", @@ -188,6 +315,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { "params": [ { "name": "name", + "kind": "identifier", "tsType": { "repr": "string", "kind": "keyword", @@ -195,12 +323,22 @@ export class Foobar extends Fizz implements Buzz, Aldrin { } }, { - "name": "", - "tsType": null + "name": "private2", + "kind": "identifier", + "tsType": { + "repr": "number", + "kind": "keyword", + "keyword": "number" + } }, { - "name": "", - "tsType": null + "name": "protected2", + "kind": "identifier", + "tsType": { + "repr": "number", + "kind": "keyword", + "keyword": "number" + } } ], "location": { @@ -308,6 +446,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { "typeName": "Promise" } }, + "typeParams": [], "isAsync": true, "isGenerator": false }, @@ -326,20 +465,21 @@ export class Foobar extends Fizz implements Buzz, Aldrin { "kind": "method", "functionDef": { "params": [], - "returnType": { - "repr": "void", - "kind": "keyword", - "keyword": "void" - }, - "isAsync": false, - "isGenerator": false + "returnType": { + "repr": "void", + "kind": "keyword", + "keyword": "void" }, - "location": { - "filename": "test.ts", - "line": 18, - "col": 4 - } + "isAsync": false, + "isGenerator": false, + "typeParams": [] + }, + "location": { + "filename": "test.ts", + "line": 18, + "col": 4 } + } ] } }); @@ -391,6 +531,7 @@ export interface Reader { "params": [ { "name": "buf", + "kind": "identifier", "tsType": { "repr": "Uint8Array", "kind": "typeRef", @@ -402,6 +543,7 @@ export interface Reader { }, { "name": "something", + "kind": "identifier", "tsType": { "repr": "unknown", "kind": "keyword", @@ -409,6 +551,7 @@ export interface Reader { } } ], + "typeParams": [], "returnType": { "repr": "Promise", "kind": "typeRef", @@ -426,7 +569,8 @@ export interface Reader { } ], "properties": [], - "callSignatures": [] + "callSignatures": [], + "typeParams": [], } }); let actual = serde_json::to_value(entry).unwrap(); @@ -438,6 +582,65 @@ export interface Reader { ); } +#[tokio::test] +async fn export_interface2() { + let source_code = r#" +export interface TypedIface { + something(): T +} + "#; + let loader = + TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]); + let entries = DocParser::new(loader).parse("test.ts").await.unwrap(); + assert_eq!(entries.len(), 1); + let entry = &entries[0]; + let expected_json = json!({ + "kind": "interface", + "name": "TypedIface", + "location": { + "filename": "test.ts", + "line": 2, + "col": 0 + }, + "jsDoc": null, + "interfaceDef": { + "methods": [ + { + "name": "something", + "location": { + "filename": "test.ts", + "line": 3, + "col": 4 + }, + "jsDoc": null, + "params": [], + "typeParams": [], + "returnType": { + "repr": "T", + "kind": "typeRef", + "typeRef": { + "typeParams": null, + "typeName": "T" + } + } + } + ], + "properties": [], + "callSignatures": [], + "typeParams": [ + { "name": "T" } + ], + } + }); + let actual = serde_json::to_value(entry).unwrap(); + assert_eq!(actual, expected_json); + + assert!( + colors::strip_ansi_codes(super::printer::format(entries).as_str()) + .contains("interface TypedIface") + ); +} + #[tokio::test] async fn export_type_alias() { let source_code = r#" @@ -459,6 +662,7 @@ export type NumberArray = Array; }, "jsDoc": "Array holding numbers", "typeAliasDef": { + "typeParams": [], "tsType": { "repr": "Array", "kind": "typeRef", @@ -751,6 +955,7 @@ async fn optional_return_type() { "params": [ { "name": "a", + "kind": "identifier", "tsType": { "keyword": "number", "kind": "keyword", @@ -758,6 +963,7 @@ async fn optional_return_type() { }, } ], + "typeParams": [], "returnType": null, "isAsync": false, "isGenerator": false @@ -841,6 +1047,7 @@ export function fooFn(a: number) { "params": [ { "name": "a", + "kind": "identifier", "tsType": { "keyword": "number", "kind": "keyword", @@ -848,6 +1055,7 @@ export function fooFn(a: number) { }, } ], + "typeParams": [], "returnType": null, "isAsync": false, "isGenerator": false diff --git a/cli/doc/ts_type.rs b/cli/doc/ts_type.rs index 9590c7e60e..8359f64cce 100644 --- a/cli/doc/ts_type.rs +++ b/cli/doc/ts_type.rs @@ -1,6 +1,8 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use super::params::ts_fn_param_to_param_def; +use super::ts_type_param::maybe_type_param_decl_to_type_param_defs; +use super::ts_type_param::TsTypeParamDef; use super::ParamDef; -use crate::swc_common::SourceMap; use crate::swc_ecma_ast; use crate::swc_ecma_ast::TsArrayType; use crate::swc_ecma_ast::TsConditionalType; @@ -36,12 +38,12 @@ use serde::Serialize; // * TsRestType(TsRestType), // * TsUnionOrIntersectionType(TsUnionOrIntersectionType), // * TsConditionalType(TsConditionalType), -// TsInferType(TsInferType), // * TsParenthesizedType(TsParenthesizedType), // * TsTypeOperator(TsTypeOperator), // * TsIndexedAccessType(TsIndexedAccessType), -// TsMappedType(TsMappedType), // * TsLitType(TsLitType), +// TsInferType(TsInferType), +// TsMappedType(TsMappedType), // TsTypePredicate(TsTypePredicate), // TsImportType(TsImportType), // } @@ -340,24 +342,7 @@ impl Into for &TsTypeLit { let mut params = vec![]; for param in &ts_method_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - Ident(ident) => { - let ts_type = - ident.type_ann.as_ref().map(|rt| (&*rt.type_ann).into()); - - ParamDef { - name: ident.sym.to_string(), - ts_type, - } - } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } @@ -366,10 +351,14 @@ impl Into for &TsTypeLit { .as_ref() .map(|rt| (&*rt.type_ann).into()); + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_method_sig.type_params.as_ref(), + ); let method_def = LiteralMethodDef { name: "".to_string(), params, return_type: maybe_return_type, + type_params, }; methods.push(method_def); } @@ -382,24 +371,7 @@ impl Into for &TsTypeLit { let mut params = vec![]; for param in &ts_prop_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - Ident(ident) => { - let ts_type = - ident.type_ann.as_ref().map(|rt| (&*rt.type_ann).into()); - - ParamDef { - name: ident.sym.to_string(), - ts_type, - } - } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } @@ -408,36 +380,23 @@ impl Into for &TsTypeLit { .as_ref() .map(|rt| (&*rt.type_ann).into()); + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_prop_sig.type_params.as_ref(), + ); let prop_def = LiteralPropertyDef { name, params, ts_type, computed: ts_prop_sig.computed, optional: ts_prop_sig.optional, + type_params, }; properties.push(prop_def); } TsCallSignatureDecl(ts_call_sig) => { let mut params = vec![]; for param in &ts_call_sig.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - Ident(ident) => { - let ts_type = - ident.type_ann.as_ref().map(|rt| (&*rt.type_ann).into()); - - ParamDef { - name: ident.sym.to_string(), - ts_type, - } - } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } @@ -446,7 +405,15 @@ impl Into for &TsTypeLit { .as_ref() .map(|rt| (&*rt.type_ann).into()); - let call_sig_def = LiteralCallSignatureDef { params, ts_type }; + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_call_sig.type_params.as_ref(), + ); + + let call_sig_def = LiteralCallSignatureDef { + params, + ts_type, + type_params, + }; call_signatures.push(call_sig_def); } // TODO: @@ -495,68 +462,37 @@ impl Into for &TsFnOrConstructorType { let mut params = vec![]; for param in &ts_fn_type.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - Ident(ident) => { - let ts_type: Option = - ident.type_ann.as_ref().map(|rt| { - let type_box = &*rt.type_ann; - (&*type_box).into() - }); - - ParamDef { - name: ident.sym.to_string(), - ts_type, - } - } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } + let type_params = maybe_type_param_decl_to_type_param_defs( + ts_fn_type.type_params.as_ref(), + ); + TsFnOrConstructorDef { constructor: false, - ts_type: (&*ts_fn_type.type_ann.type_ann).into(), + ts_type: ts_type_ann_to_def(&ts_fn_type.type_ann), params, + type_params, } } TsConstructorType(ctor_type) => { let mut params = vec![]; for param in &ctor_type.params { - use crate::swc_ecma_ast::TsFnParam::*; - - let param_def = match param { - Ident(ident) => { - let ts_type: Option = - ident.type_ann.as_ref().map(|rt| { - let type_box = &*rt.type_ann; - (&*type_box).into() - }); - - ParamDef { - name: ident.sym.to_string(), - ts_type, - } - } - _ => ParamDef { - name: "".to_string(), - ts_type: None, - }, - }; - + let param_def = ts_fn_param_to_param_def(param); params.push(param_def); } + let type_params = maybe_type_param_decl_to_type_param_defs( + ctor_type.type_params.as_ref(), + ); TsFnOrConstructorDef { constructor: true, - ts_type: (&*ctor_type.type_ann.type_ann).into(), - params: vec![], + ts_type: ts_type_ann_to_def(&ctor_type.type_ann), + params, + type_params, } } }; @@ -638,10 +574,10 @@ pub struct TsTypeOperatorDef { #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct TsFnOrConstructorDef { - // TODO: type_params pub constructor: bool, pub ts_type: TsTypeDef, pub params: Vec, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] @@ -664,35 +600,29 @@ pub struct TsIndexedAccessDef { #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct LiteralMethodDef { - // TODO: type_params pub name: String, - // pub location: Location, - // pub js_doc: Option, pub params: Vec, pub return_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct LiteralPropertyDef { - // TODO: type_params pub name: String, - // pub location: Location, - // pub js_doc: Option, pub params: Vec, pub computed: bool, pub optional: bool, pub ts_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct LiteralCallSignatureDef { - // TODO: type_params - // pub location: Location, - // pub js_doc: Option, pub params: Vec, pub ts_type: Option, + pub type_params: Vec, } #[derive(Debug, Serialize, Clone)] @@ -732,7 +662,6 @@ pub struct TsTypeDef { pub kind: Option, - // TODO: make this struct more conrete #[serde(skip_serializing_if = "Option::is_none")] pub keyword: Option, @@ -785,10 +714,7 @@ pub struct TsTypeDef { pub type_literal: Option, } -pub fn ts_type_ann_to_def( - source_map: &SourceMap, - type_ann: &TsTypeAnn, -) -> TsTypeDef { +pub fn ts_type_ann_to_def(type_ann: &TsTypeAnn) -> TsTypeDef { use crate::swc_ecma_ast::TsType::*; match &*type_ann.type_ann { @@ -808,16 +734,9 @@ pub fn ts_type_ann_to_def( TsConditionalType(conditional_type) => conditional_type.into(), TsIndexedAccessType(indexed_access_type) => indexed_access_type.into(), TsTypeLit(type_literal) => type_literal.into(), - _ => { - let repr = source_map - .span_to_snippet(type_ann.span) - .expect("Class prop type not found"); - let repr = repr.trim_start_matches(':').trim_start().to_string(); - - TsTypeDef { - repr, - ..Default::default() - } - } + _ => TsTypeDef { + repr: "".to_string(), + ..Default::default() + }, } } diff --git a/cli/doc/ts_type_param.rs b/cli/doc/ts_type_param.rs new file mode 100644 index 0000000000..4edb7dee1c --- /dev/null +++ b/cli/doc/ts_type_param.rs @@ -0,0 +1,57 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +use super::ts_type::TsTypeDef; +use crate::swc_ecma_ast::TsTypeParam; +use crate::swc_ecma_ast::TsTypeParamDecl; +use serde::Serialize; + +#[derive(Debug, Serialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct TsTypeParamDef { + pub name: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub constraint: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub default: Option, +} + +impl Into for &TsTypeParam { + fn into(self) -> TsTypeParamDef { + let name = self.name.sym.to_string(); + let constraint: Option = + if let Some(ts_type) = self.constraint.as_ref() { + let type_def: TsTypeDef = (&**ts_type).into(); + Some(type_def) + } else { + None + }; + let default: Option = + if let Some(ts_type) = self.default.as_ref() { + let type_def: TsTypeDef = (&**ts_type).into(); + Some(type_def) + } else { + None + }; + + TsTypeParamDef { + name, + constraint, + default, + } + } +} + +pub fn maybe_type_param_decl_to_type_param_defs( + maybe_type_param_decl: Option<&TsTypeParamDecl>, +) -> Vec { + if let Some(type_params_decl) = maybe_type_param_decl { + type_params_decl + .params + .iter() + .map(|type_param| type_param.into()) + .collect::>() + } else { + vec![] + } +} diff --git a/cli/doc/type_alias.rs b/cli/doc/type_alias.rs index ad9933978e..b26395c3f1 100644 --- a/cli/doc/type_alias.rs +++ b/cli/doc/type_alias.rs @@ -1,15 +1,16 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -use crate::swc_ecma_ast; -use serde::Serialize; - use super::parser::DocParser; use super::ts_type::TsTypeDef; +use super::ts_type_param::maybe_type_param_decl_to_type_param_defs; +use super::ts_type_param::TsTypeParamDef; +use crate::swc_ecma_ast; +use serde::Serialize; #[derive(Debug, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct TypeAliasDef { pub ts_type: TsTypeDef, - // TODO: type_params + pub type_params: Vec, } pub fn get_doc_for_ts_type_alias_decl( @@ -18,8 +19,13 @@ pub fn get_doc_for_ts_type_alias_decl( ) -> (String, TypeAliasDef) { let alias_name = type_alias_decl.id.sym.to_string(); let ts_type = type_alias_decl.type_ann.as_ref().into(); - - let type_alias_def = TypeAliasDef { ts_type }; + let type_params = maybe_type_param_decl_to_type_param_defs( + type_alias_decl.type_params.as_ref(), + ); + let type_alias_def = TypeAliasDef { + ts_type, + type_params, + }; (alias_name, type_alias_def) } diff --git a/cli/doc/variable.rs b/cli/doc/variable.rs index b8ebcfd72d..e7bc475d17 100644 --- a/cli/doc/variable.rs +++ b/cli/doc/variable.rs @@ -2,7 +2,6 @@ use crate::swc_ecma_ast; use serde::Serialize; -use super::parser::DocParser; use super::ts_type::ts_type_ann_to_def; use super::ts_type::TsTypeDef; @@ -13,12 +12,12 @@ pub struct VariableDef { pub kind: swc_ecma_ast::VarDeclKind, } +// TODO: change this function to return Vec<(String, VariableDef)> as single +// var declaration can have multiple declarators pub fn get_doc_for_var_decl( - doc_parser: &DocParser, var_decl: &swc_ecma_ast::VarDecl, ) -> (String, VariableDef) { assert!(!var_decl.decls.is_empty()); - // TODO: support multiple declarators let var_declarator = var_decl.decls.get(0).unwrap(); let var_name = match &var_declarator.name { @@ -27,10 +26,9 @@ pub fn get_doc_for_var_decl( }; let maybe_ts_type = match &var_declarator.name { - swc_ecma_ast::Pat::Ident(ident) => ident - .type_ann - .as_ref() - .map(|rt| ts_type_ann_to_def(&doc_parser.source_map, rt)), + swc_ecma_ast::Pat::Ident(ident) => { + ident.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt)) + } _ => None, };