feat(doc): handle function params and type params (#4672)

This commit is contained in:
Bartek Iwańczuk 2020-04-08 17:03:42 +02:00 committed by GitHub
parent fe17496831
commit 491b8e1cea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 518 additions and 289 deletions

View file

@ -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<String>,
// pub ts_type: Option<TsTypeDef>,
// pub readonly: bool,
pub accessibility: Option<swc_ecma_ast::Accessibility>,
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<ClassConstructorDef>,
pub properties: Vec<ClassPropertyDef>,
pub methods: Vec<ClassMethodDef>,
pub super_class: Option<String>,
pub implements: Vec<String>,
pub type_params: Vec<TsTypeParamDef>,
}
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: "<TODO>".to_string(),
ts_type: None,
},
},
TsParamProp(_) => ParamDef {
name: "<TODO>".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)

View file

@ -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<TsTypeDef>,
pub is_async: bool,
pub is_generator: bool,
// TODO: type_params, decorators
pub type_params: Vec<TsTypeParamDef>,
// 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: "<TODO>".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)
}

View file

@ -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<String>,
pub params: Vec<ParamDef>,
pub return_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfacePropertyDef {
// TODO: type_params
pub name: String,
pub location: Location,
pub js_doc: Option<String>,
@ -30,25 +32,27 @@ pub struct InterfacePropertyDef {
pub computed: bool,
pub optional: bool,
pub ts_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceCallSignatureDef {
// TODO: type_params
pub location: Location,
pub js_doc: Option<String>,
pub params: Vec<ParamDef>,
pub ts_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceDef {
// TODO: extends, type params
// TODO(bartlomieju): extends
pub methods: Vec<InterfaceMethodDef>,
pub properties: Vec<InterfacePropertyDef>,
pub call_signatures: Vec<InterfaceCallSignatureDef>,
pub type_params: Vec<TsTypeParamDef>,
}
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: "<TODO>".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: "<TODO>".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: "<TODO>".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)

View file

@ -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)]

View file

@ -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,

View file

@ -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<super::ts_type::TsTypeDef>,
}

83
cli/doc/params.rs Normal file
View file

@ -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(),
_ => "<TODO>".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),
}
}

View file

@ -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,

View file

@ -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": "<TODO>",
"tsType": null
"name": "private2",
"kind": "identifier",
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
},
{
"name": "<TODO>",
"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<T> {
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<number>;
},
"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

View file

@ -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<TsTypeDef> 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: "<TODO>".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<TsTypeDef> 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: "<TODO>".to_string(),
params,
return_type: maybe_return_type,
type_params,
};
methods.push(method_def);
}
@ -382,24 +371,7 @@ impl Into<TsTypeDef> 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: "<TODO>".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<TsTypeDef> 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: "<TODO>".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<TsTypeDef> 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<TsTypeDef> 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<TsTypeDef> =
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: "<TODO>".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<TsTypeDef> =
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: "<TODO>".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<ParamDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[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<String>,
pub params: Vec<ParamDef>,
pub return_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralPropertyDef {
// TODO: type_params
pub name: String,
// pub location: Location,
// pub js_doc: Option<String>,
pub params: Vec<ParamDef>,
pub computed: bool,
pub optional: bool,
pub ts_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralCallSignatureDef {
// TODO: type_params
// pub location: Location,
// pub js_doc: Option<String>,
pub params: Vec<ParamDef>,
pub ts_type: Option<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
}
#[derive(Debug, Serialize, Clone)]
@ -732,7 +662,6 @@ pub struct TsTypeDef {
pub kind: Option<TsTypeDefKind>,
// TODO: make this struct more conrete
#[serde(skip_serializing_if = "Option::is_none")]
pub keyword: Option<String>,
@ -785,10 +714,7 @@ pub struct TsTypeDef {
pub type_literal: Option<TsTypeLiteralDef>,
}
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: "<TODO>".to_string(),
..Default::default()
},
}
}

57
cli/doc/ts_type_param.rs Normal file
View file

@ -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<TsTypeDef>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default: Option<TsTypeDef>,
}
impl Into<TsTypeParamDef> for &TsTypeParam {
fn into(self) -> TsTypeParamDef {
let name = self.name.sym.to_string();
let constraint: Option<TsTypeDef> =
if let Some(ts_type) = self.constraint.as_ref() {
let type_def: TsTypeDef = (&**ts_type).into();
Some(type_def)
} else {
None
};
let default: Option<TsTypeDef> =
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<TsTypeParamDef> {
if let Some(type_params_decl) = maybe_type_param_decl {
type_params_decl
.params
.iter()
.map(|type_param| type_param.into())
.collect::<Vec<TsTypeParamDef>>()
} else {
vec![]
}
}

View file

@ -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<TsTypeParamDef>,
}
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)
}

View file

@ -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,
};