summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/colors.rs21
-rw-r--r--cli/doc/class.rs178
-rw-r--r--cli/doc/display.rs90
-rw-r--r--cli/doc/function.rs8
-rw-r--r--cli/doc/interface.rs105
-rw-r--r--cli/doc/mod.rs5
-rw-r--r--cli/doc/module.rs3
-rw-r--r--cli/doc/node.rs20
-rw-r--r--cli/doc/params.rs275
-rw-r--r--cli/doc/parser.rs34
-rw-r--r--cli/doc/printer.rs861
-rw-r--r--cli/doc/tests.rs2377
-rw-r--r--cli/doc/ts_type.rs283
-rw-r--r--cli/doc/ts_type_param.rs14
-rw-r--r--cli/flags.rs35
-rw-r--r--cli/main.rs14
16 files changed, 2734 insertions, 1589 deletions
diff --git a/cli/colors.rs b/cli/colors.rs
index ccbcc926a..870f7f435 100644
--- a/cli/colors.rs
+++ b/cli/colors.rs
@@ -3,7 +3,7 @@ use regex::Regex;
use std::env;
use std::fmt;
use std::io::Write;
-use termcolor::Color::{Ansi256, Black, Magenta, Red, White};
+use termcolor::Color::{Ansi256, Black, Blue, Green, Magenta, Red, White};
use termcolor::{Ansi, ColorSpec, WriteColor};
#[cfg(windows)]
@@ -54,7 +54,10 @@ pub fn red_bold(s: &str) -> impl fmt::Display {
pub fn green_bold(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
- style_spec.set_fg(Some(Ansi256(10))).set_bold(true);
+ style_spec
+ .set_fg(Some(Green))
+ .set_bold(true)
+ .set_intense(true);
style(&s, style_spec)
}
@@ -102,7 +105,7 @@ pub fn red(s: &str) -> impl fmt::Display {
pub fn green(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
- style_spec.set_fg(Some(Ansi256(10)));
+ style_spec.set_fg(Some(Green)).set_intense(true);
style(&s, style_spec)
}
@@ -124,6 +127,12 @@ pub fn gray(s: &str) -> impl fmt::Display {
style(&s, style_spec)
}
+pub fn italic_gray(s: &str) -> impl fmt::Display {
+ let mut style_spec = ColorSpec::new();
+ style_spec.set_fg(Some(Ansi256(8))).set_italic(true);
+ style(&s, style_spec)
+}
+
pub fn italic_bold_gray(s: &str) -> impl fmt::Display {
let mut style_spec = ColorSpec::new();
style_spec
@@ -132,3 +141,9 @@ pub fn italic_bold_gray(s: &str) -> impl fmt::Display {
.set_italic(true);
style(&s, style_spec)
}
+
+pub fn intense_blue(s: &str) -> impl fmt::Display {
+ let mut style_spec = ColorSpec::new();
+ style_spec.set_fg(Some(Blue)).set_intense(true);
+ style(&s, style_spec)
+}
diff --git a/cli/doc/class.rs b/cli/doc/class.rs
index af1964ac9..d8fa29ded 100644
--- a/cli/doc/class.rs
+++ b/cli/doc/class.rs
@@ -1,5 +1,10 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-use crate::swc_common::SourceMap;
+use crate::colors;
+use crate::doc::display::{
+ display_abstract, display_accessibility, display_async, display_generator,
+ display_method, display_optional, display_readonly, display_static,
+ SliceDisplayer,
+};
use crate::swc_common::Spanned;
use crate::swc_ecma_ast;
use serde::Serialize;
@@ -7,18 +12,21 @@ use serde::Serialize;
use super::function::function_to_function_def;
use super::function::FunctionDef;
use super::interface::expr_to_name;
-use super::params::assign_pat_to_param_def;
-use super::params::ident_to_param_def;
-use super::params::pat_to_param_def;
+use super::params::{
+ assign_pat_to_param_def, ident_to_param_def, pat_to_param_def,
+ prop_name_to_string, ts_fn_param_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::{
+ maybe_type_param_instantiation_to_type_defs, ts_type_ann_to_def, 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;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassConstructorDef {
@@ -29,6 +37,18 @@ pub struct ClassConstructorDef {
pub location: Location,
}
+impl Display for ClassConstructorDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}{}({})",
+ display_accessibility(self.accessibility),
+ colors::magenta("constructor"),
+ SliceDisplayer::new(&self.params, ", ", false),
+ )
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassPropertyDef {
@@ -43,6 +63,48 @@ pub struct ClassPropertyDef {
pub location: Location,
}
+impl Display for ClassPropertyDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}{}{}{}{}{}",
+ display_abstract(self.is_abstract),
+ display_accessibility(self.accessibility),
+ display_static(self.is_static),
+ display_readonly(self.readonly),
+ colors::bold(&self.name),
+ display_optional(self.optional),
+ )?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct ClassIndexSignatureDef {
+ pub readonly: bool,
+ pub params: Vec<ParamDef>,
+ pub ts_type: Option<TsTypeDef>,
+}
+
+impl Display for ClassIndexSignatureDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}[{}]",
+ display_readonly(self.readonly),
+ SliceDisplayer::new(&self.params, ", ", false)
+ )?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassMethodDef {
@@ -57,32 +119,41 @@ pub struct ClassMethodDef {
pub location: Location,
}
+impl Display for ClassMethodDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}{}{}{}{}{}{}{}({})",
+ display_abstract(self.is_abstract),
+ display_accessibility(self.accessibility),
+ display_static(self.is_static),
+ display_async(self.function_def.is_async),
+ display_method(self.kind),
+ display_generator(self.function_def.is_generator),
+ colors::bold(&self.name),
+ display_optional(self.optional),
+ SliceDisplayer::new(&self.function_def.params, ", ", false),
+ )?;
+ if let Some(return_type) = &self.function_def.return_type {
+ write!(f, ": {}", return_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ClassDef {
- // TODO(bartlomieju): decorators, super_type_params
+ // TODO(bartlomieju): decorators
pub is_abstract: bool,
pub constructors: Vec<ClassConstructorDef>,
pub properties: Vec<ClassPropertyDef>,
+ pub index_signatures: Vec<ClassIndexSignatureDef>,
pub methods: Vec<ClassMethodDef>,
pub extends: Option<String>,
- pub implements: Vec<String>,
+ pub implements: Vec<TsTypeDef>,
pub type_params: Vec<TsTypeParamDef>,
-}
-
-fn prop_name_to_string(
- source_map: &SourceMap,
- prop_name: &swc_ecma_ast::PropName,
-) -> String {
- use crate::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 super_type_params: Vec<TsTypeDef>,
}
pub fn class_to_class_def(
@@ -92,6 +163,7 @@ pub fn class_to_class_def(
let mut constructors = vec![];
let mut methods = vec![];
let mut properties = vec![];
+ let mut index_signatures = vec![];
let extends: Option<String> = match &class.super_class {
Some(boxed) => {
@@ -105,11 +177,11 @@ pub fn class_to_class_def(
None => None,
};
- let implements: Vec<String> = class
+ let implements = class
.implements
.iter()
- .map(|expr| ts_entity_name_to_name(&expr.expr))
- .collect();
+ .map(|expr| expr.into())
+ .collect::<Vec<TsTypeDef>>();
for member in &class.body {
use crate::swc_ecma_ast::ClassMember::*;
@@ -117,8 +189,10 @@ pub fn class_to_class_def(
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.ast_parser.source_map, &ctor.key);
+ let constructor_name = prop_name_to_string(
+ &ctor.key,
+ Some(&doc_parser.ast_parser.source_map),
+ );
let mut params = vec![];
@@ -126,14 +200,23 @@ pub fn class_to_class_def(
use crate::swc_ecma_ast::ParamOrTsParamProp::*;
let param_def = match param {
- Param(param) => pat_to_param_def(&param.pat),
+ Param(param) => pat_to_param_def(
+ &param.pat,
+ Some(&doc_parser.ast_parser.source_map),
+ ),
TsParamProp(ts_param_prop) => {
use swc_ecma_ast::TsParamPropParam;
match &ts_param_prop.param {
- TsParamPropParam::Ident(ident) => ident_to_param_def(ident),
+ TsParamPropParam::Ident(ident) => ident_to_param_def(
+ ident,
+ Some(&doc_parser.ast_parser.source_map),
+ ),
TsParamPropParam::Assign(assign_pat) => {
- assign_pat_to_param_def(assign_pat)
+ assign_pat_to_param_def(
+ assign_pat,
+ Some(&doc_parser.ast_parser.source_map),
+ )
}
}
}
@@ -153,10 +236,11 @@ pub fn class_to_class_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.ast_parser.source_map,
&class_method.key,
+ Some(&doc_parser.ast_parser.source_map),
);
- let fn_def = function_to_function_def(&class_method.function);
+ 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,
@@ -199,8 +283,26 @@ pub fn class_to_class_def(
};
properties.push(prop_def);
}
+ TsIndexSignature(ts_index_sig) => {
+ let mut params = vec![];
+ for param in &ts_index_sig.params {
+ let param_def = ts_fn_param_to_param_def(param, None);
+ params.push(param_def);
+ }
+
+ let ts_type = ts_index_sig
+ .type_ann
+ .as_ref()
+ .map(|rt| (&*rt.type_ann).into());
+
+ let index_sig_def = ClassIndexSignatureDef {
+ readonly: ts_index_sig.readonly,
+ params,
+ ts_type,
+ };
+ index_signatures.push(index_sig_def);
+ }
// TODO(bartlomieju):
- TsIndexSignature(_) => {}
PrivateMethod(_) => {}
PrivateProp(_) => {}
_ => {}
@@ -210,14 +312,20 @@ pub fn class_to_class_def(
let type_params =
maybe_type_param_decl_to_type_param_defs(class.type_params.as_ref());
+ let super_type_params = maybe_type_param_instantiation_to_type_defs(
+ class.super_type_params.as_ref(),
+ );
+
ClassDef {
is_abstract: class.is_abstract,
extends,
implements,
constructors,
properties,
+ index_signatures,
methods,
type_params,
+ super_type_params,
}
}
diff --git a/cli/doc/display.rs b/cli/doc/display.rs
new file mode 100644
index 000000000..9da04363f
--- /dev/null
+++ b/cli/doc/display.rs
@@ -0,0 +1,90 @@
+// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+use crate::colors;
+use crate::swc_ecma_ast;
+use std::fmt::{Display, Formatter, Result};
+
+pub(crate) struct Indent(pub i64);
+
+impl Display for Indent {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ for _ in 0..self.0 {
+ write!(f, " ")?;
+ }
+ Ok(())
+ }
+}
+
+pub(crate) struct SliceDisplayer<'a, T: Display>(&'a [T], &'a str, bool);
+
+impl<'a, T: Display> SliceDisplayer<'a, T> {
+ pub fn new(
+ slice: &'a [T],
+ separator: &'a str,
+ trailing: bool,
+ ) -> SliceDisplayer<'a, T> {
+ SliceDisplayer(slice, separator, trailing)
+ }
+}
+
+impl<T: Display> Display for SliceDisplayer<'_, T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ if self.0.is_empty() {
+ return Ok(());
+ }
+
+ write!(f, "{}", self.0[0])?;
+ for v in &self.0[1..] {
+ write!(f, "{}{}", self.1, v)?;
+ }
+
+ if self.2 {
+ write!(f, "{}", self.1)?;
+ }
+
+ Ok(())
+ }
+}
+
+pub(crate) fn display_abstract(is_abstract: bool) -> impl Display {
+ colors::magenta(if is_abstract { "abstract " } else { "" })
+}
+
+pub(crate) fn display_accessibility(
+ accessibility: Option<swc_ecma_ast::Accessibility>,
+) -> impl Display {
+ colors::magenta(
+ match accessibility.unwrap_or(swc_ecma_ast::Accessibility::Public) {
+ swc_ecma_ast::Accessibility::Public => "",
+ swc_ecma_ast::Accessibility::Protected => "protected ",
+ swc_ecma_ast::Accessibility::Private => "private ",
+ },
+ )
+}
+
+pub(crate) fn display_async(is_async: bool) -> impl Display {
+ colors::magenta(if is_async { "async " } else { "" })
+}
+
+pub(crate) fn display_generator(is_generator: bool) -> impl Display {
+ colors::magenta(if is_generator { "*" } else { "" })
+}
+
+pub(crate) fn display_method(method: swc_ecma_ast::MethodKind) -> impl Display {
+ colors::magenta(match method {
+ swc_ecma_ast::MethodKind::Getter => "get ",
+ swc_ecma_ast::MethodKind::Setter => "set ",
+ _ => "",
+ })
+}
+
+pub(crate) fn display_optional(is_optional: bool) -> impl Display {
+ colors::magenta(if is_optional { "?" } else { "" })
+}
+
+pub(crate) fn display_readonly(is_readonly: bool) -> impl Display {
+ colors::magenta(if is_readonly { "readonly " } else { "" })
+}
+
+pub(crate) fn display_static(is_static: bool) -> impl Display {
+ colors::magenta(if is_static { "static " } else { "" })
+}
diff --git a/cli/doc/function.rs b/cli/doc/function.rs
index e8abf56f9..ede8bdbbd 100644
--- a/cli/doc/function.rs
+++ b/cli/doc/function.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::params::pat_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;
@@ -20,12 +21,14 @@ pub struct FunctionDef {
}
pub fn function_to_function_def(
+ doc_parser: &DocParser,
function: &swc_ecma_ast::Function,
) -> FunctionDef {
let mut params = vec![];
for param in &function.params {
- let param_def = pat_to_param_def(&param.pat);
+ let param_def =
+ pat_to_param_def(&param.pat, Some(&doc_parser.ast_parser.source_map));
params.push(param_def);
}
@@ -47,9 +50,10 @@ pub fn function_to_function_def(
}
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(&fn_decl.function);
+ let fn_def = function_to_function_def(&doc_parser, &fn_decl.function);
(name, fn_def)
}
diff --git a/cli/doc/interface.rs b/cli/doc/interface.rs
index 9e3c1fc90..d3caa8748 100644
--- a/cli/doc/interface.rs
+++ b/cli/doc/interface.rs
@@ -1,10 +1,11 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+use crate::colors;
+use crate::doc::display::{display_optional, display_readonly, SliceDisplayer};
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_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;
@@ -12,6 +13,8 @@ use super::ts_type_param::TsTypeParamDef;
use super::Location;
use super::ParamDef;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceMethodDef {
@@ -24,6 +27,22 @@ pub struct InterfaceMethodDef {
pub type_params: Vec<TsTypeParamDef>,
}
+impl Display for InterfaceMethodDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}{}({})",
+ colors::bold(&self.name),
+ display_optional(self.optional),
+ SliceDisplayer::new(&self.params, ", ", false),
+ )?;
+ if let Some(return_type) = &self.return_type {
+ write!(f, ": {}", return_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfacePropertyDef {
@@ -37,6 +56,44 @@ pub struct InterfacePropertyDef {
pub type_params: Vec<TsTypeParamDef>,
}
+impl Display for InterfacePropertyDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}{}",
+ colors::bold(&self.name),
+ display_optional(self.optional),
+ )?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct InterfaceIndexSignatureDef {
+ pub readonly: bool,
+ pub params: Vec<ParamDef>,
+ pub ts_type: Option<TsTypeDef>,
+}
+
+impl Display for InterfaceIndexSignatureDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}[{}]",
+ display_readonly(self.readonly),
+ SliceDisplayer::new(&self.params, ", ", false)
+ )?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceCallSignatureDef {
@@ -50,10 +107,11 @@ pub struct InterfaceCallSignatureDef {
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct InterfaceDef {
- pub extends: Vec<String>,
+ pub extends: Vec<TsTypeDef>,
pub methods: Vec<InterfaceMethodDef>,
pub properties: Vec<InterfacePropertyDef>,
pub call_signatures: Vec<InterfaceCallSignatureDef>,
+ pub index_signatures: Vec<InterfaceIndexSignatureDef>,
pub type_params: Vec<TsTypeParamDef>,
}
@@ -84,6 +142,7 @@ pub fn get_doc_for_ts_interface_decl(
let mut methods = vec![];
let mut properties = vec![];
let mut call_signatures = vec![];
+ let mut index_signatures = vec![];
for type_element in &interface_decl.body.body {
use crate::swc_ecma_ast::TsTypeElement::*;
@@ -95,7 +154,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_method_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(
+ param,
+ Some(&doc_parser.ast_parser.source_map),
+ );
params.push(param_def);
}
@@ -131,7 +193,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_prop_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(
+ param,
+ Some(&doc_parser.ast_parser.source_map),
+ );
params.push(param_def);
}
@@ -164,7 +229,10 @@ pub fn get_doc_for_ts_interface_decl(
let mut params = vec![];
for param in &ts_call_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(
+ param,
+ Some(&doc_parser.ast_parser.source_map),
+ );
params.push(param_def);
}
@@ -189,9 +257,27 @@ pub fn get_doc_for_ts_interface_decl(
};
call_signatures.push(call_sig_def);
}
+ TsIndexSignature(ts_index_sig) => {
+ let mut params = vec![];
+ for param in &ts_index_sig.params {
+ let param_def = ts_fn_param_to_param_def(param, None);
+ params.push(param_def);
+ }
+
+ let ts_type = ts_index_sig
+ .type_ann
+ .as_ref()
+ .map(|rt| (&*rt.type_ann).into());
+
+ let index_sig_def = InterfaceIndexSignatureDef {
+ readonly: ts_index_sig.readonly,
+ params,
+ ts_type,
+ };
+ index_signatures.push(index_sig_def);
+ }
// TODO:
TsConstructSignatureDecl(_) => {}
- TsIndexSignature(_) => {}
}
}
@@ -199,17 +285,18 @@ pub fn get_doc_for_ts_interface_decl(
interface_decl.type_params.as_ref(),
);
- let extends: Vec<String> = interface_decl
+ let extends = interface_decl
.extends
.iter()
- .map(|expr| ts_entity_name_to_name(&expr.expr))
- .collect();
+ .map(|expr| expr.into())
+ .collect::<Vec<TsTypeDef>>();
let interface_def = InterfaceDef {
extends,
methods,
properties,
call_signatures,
+ index_signatures,
type_params,
};
diff --git a/cli/doc/mod.rs b/cli/doc/mod.rs
index 81cc2f9fb..49f8d7453 100644
--- a/cli/doc/mod.rs
+++ b/cli/doc/mod.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
pub mod class;
+mod display;
pub mod r#enum;
pub mod function;
pub mod interface;
@@ -17,9 +18,9 @@ pub mod variable;
pub use node::DocNode;
pub use node::DocNodeKind;
pub use node::Location;
-pub use node::ParamDef;
-pub use node::ParamKind;
+pub use params::ParamDef;
pub use parser::DocParser;
+pub use printer::DocPrinter;
#[cfg(test)]
mod tests;
diff --git a/cli/doc/module.rs b/cli/doc/module.rs
index 2de9c7ca8..79b1b92be 100644
--- a/cli/doc/module.rs
+++ b/cli/doc/module.rs
@@ -35,7 +35,8 @@ pub fn get_doc_node_for_export_decl(
}
}
Decl::Fn(fn_decl) => {
- let (name, function_def) = super::function::get_doc_for_fn_decl(fn_decl);
+ let (name, function_def) =
+ super::function::get_doc_for_fn_decl(doc_parser, fn_decl);
DocNode {
kind: DocNodeKind::Function,
name,
diff --git a/cli/doc/node.rs b/cli/doc/node.rs
index 0f97ed65a..690221ed0 100644
--- a/cli/doc/node.rs
+++ b/cli/doc/node.rs
@@ -14,24 +14,6 @@ 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 optional: bool,
- pub ts_type: Option<super::ts_type::TsTypeDef>,
-}
-
#[derive(Debug, Serialize, Clone, PartialEq)]
pub struct Location {
pub filename: String,
@@ -82,7 +64,7 @@ pub struct Reexport {
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct ModuleDoc {
- pub exports: Vec<DocNode>,
+ pub definitions: Vec<DocNode>,
pub reexports: Vec<Reexport>,
}
diff --git a/cli/doc/params.rs b/cli/doc/params.rs
index 0869c1969..3e7967c81 100644
--- a/cli/doc/params.rs
+++ b/cli/doc/params.rs
@@ -1,58 +1,228 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+use super::display::{display_optional, SliceDisplayer};
+use super::ts_type::{ts_type_ann_to_def, TsTypeDef};
+use crate::swc_common::SourceMap;
use crate::swc_ecma_ast;
+use crate::swc_ecma_ast::{ObjectPatProp, Pat, TsFnParam};
+use serde::Serialize;
+use std::fmt::{Display, Formatter, Result as FmtResult};
-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;
+#[derive(Debug, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+#[serde(tag = "kind")]
+pub enum ParamDef {
+ #[serde(rename_all = "camelCase")]
+ Array {
+ elements: Vec<Option<ParamDef>>,
+ optional: bool,
+ ts_type: Option<TsTypeDef>,
+ },
+ #[serde(rename_all = "camelCase")]
+ Assign {
+ left: Box<ParamDef>,
+ right: String,
+ ts_type: Option<TsTypeDef>,
+ },
+ #[serde(rename_all = "camelCase")]
+ Identifier {
+ name: String,
+ optional: bool,
+ ts_type: Option<TsTypeDef>,
+ },
+ #[serde(rename_all = "camelCase")]
+ Object {
+ props: Vec<ObjectPatPropDef>,
+ optional: bool,
+ ts_type: Option<TsTypeDef>,
+ },
+ #[serde(rename_all = "camelCase")]
+ Rest {
+ arg: Box<ParamDef>,
+ ts_type: Option<TsTypeDef>,
+ },
+}
+
+impl Display for ParamDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ match self {
+ Self::Array {
+ elements,
+ optional,
+ ts_type,
+ } => {
+ write!(f, "[")?;
+ if !elements.is_empty() {
+ if let Some(v) = &elements[0] {
+ write!(f, "{}", v)?;
+ }
+ for maybe_v in &elements[1..] {
+ write!(f, ", ")?;
+ if let Some(v) = maybe_v {
+ write!(f, "{}", v)?;
+ }
+ }
+ }
+ write!(f, "]")?;
+ write!(f, "{}", display_optional(*optional))?;
+ if let Some(ts_type) = ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+ Self::Assign { left, ts_type, .. } => {
+ write!(f, "{}", left)?;
+ if let Some(ts_type) = ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ // TODO(SyrupThinker) As we cannot display expressions the value is just omitted
+ // write!(f, " = {}", right)?;
+ Ok(())
+ }
+ Self::Identifier {
+ name,
+ optional,
+ ts_type,
+ } => {
+ write!(f, "{}{}", name, display_optional(*optional))?;
+ if let Some(ts_type) = ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+ Self::Object {
+ props,
+ optional,
+ ts_type,
+ } => {
+ write!(
+ f,
+ "{{{}}}{}",
+ SliceDisplayer::new(&props, ", ", false),
+ display_optional(*optional)
+ )?;
+ if let Some(ts_type) = ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+ Self::Rest { arg, ts_type } => {
+ write!(f, "...{}", arg)?;
+ if let Some(ts_type) = ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
-pub fn ident_to_param_def(ident: &swc_ecma_ast::Ident) -> ParamDef {
+#[derive(Debug, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+#[serde(tag = "kind")]
+pub enum ObjectPatPropDef {
+ Assign { key: String, value: Option<String> },
+ KeyValue { key: String, value: Box<ParamDef> },
+ Rest { arg: Box<ParamDef> },
+}
+
+impl Display for ObjectPatPropDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ match self {
+ Self::KeyValue { key, .. } => {
+ // The internal identifier does not need to be exposed
+ write!(f, "{}", key)
+ }
+ Self::Assign { key, value } => {
+ if let Some(_value) = value {
+ // TODO(SyrupThinker) As we cannot display expressions the value is just omitted
+ write!(f, "{}", key)
+ } else {
+ write!(f, "{}", key)
+ }
+ }
+ Self::Rest { arg } => write!(f, "...{}", arg),
+ }
+ }
+}
+
+pub fn ident_to_param_def(
+ ident: &swc_ecma_ast::Ident,
+ _source_map: Option<&SourceMap>,
+) -> ParamDef {
let ts_type = ident.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
- ParamDef {
+ ParamDef::Identifier {
name: ident.sym.to_string(),
- kind: ParamKind::Identifier,
optional: ident.optional,
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(),
- };
+fn rest_pat_to_param_def(
+ rest_pat: &swc_ecma_ast::RestPat,
+ source_map: Option<&SourceMap>,
+) -> ParamDef {
let ts_type = rest_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
- ParamDef {
- name,
- kind: ParamKind::Rest,
- optional: false,
+ ParamDef::Rest {
+ arg: Box::new(pat_to_param_def(&*rest_pat.arg, source_map)),
ts_type,
}
}
-fn object_pat_to_param_def(object_pat: &swc_ecma_ast::ObjectPat) -> ParamDef {
+fn object_pat_prop_to_def(
+ object_pat_prop: &ObjectPatProp,
+ source_map: Option<&SourceMap>,
+) -> ObjectPatPropDef {
+ match object_pat_prop {
+ ObjectPatProp::Assign(assign) => ObjectPatPropDef::Assign {
+ key: assign.key.sym.to_string(),
+ value: assign.value.as_ref().map(|_| "<UNIMPLEMENTED>".to_string()),
+ },
+ ObjectPatProp::KeyValue(keyvalue) => ObjectPatPropDef::KeyValue {
+ key: prop_name_to_string(&keyvalue.key, source_map),
+ value: Box::new(pat_to_param_def(&*keyvalue.value, source_map)),
+ },
+ ObjectPatProp::Rest(rest) => ObjectPatPropDef::Rest {
+ arg: Box::new(pat_to_param_def(&*rest.arg, source_map)),
+ },
+ }
+}
+
+fn object_pat_to_param_def(
+ object_pat: &swc_ecma_ast::ObjectPat,
+ source_map: Option<&SourceMap>,
+) -> ParamDef {
+ let props = object_pat
+ .props
+ .iter()
+ .map(|prop| object_pat_prop_to_def(prop, source_map))
+ .collect::<Vec<_>>();
let ts_type = object_pat
.type_ann
.as_ref()
.map(|rt| ts_type_ann_to_def(rt));
- ParamDef {
- name: "".to_string(),
- kind: ParamKind::Object,
+ ParamDef::Object {
+ props,
optional: object_pat.optional,
ts_type,
}
}
-fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef {
+fn array_pat_to_param_def(
+ array_pat: &swc_ecma_ast::ArrayPat,
+ source_map: Option<&SourceMap>,
+) -> ParamDef {
+ let elements = array_pat
+ .elems
+ .iter()
+ .map(|elem| elem.as_ref().map(|e| pat_to_param_def(e, source_map)))
+ .collect::<Vec<Option<_>>>();
let ts_type = array_pat.type_ann.as_ref().map(|rt| ts_type_ann_to_def(rt));
- ParamDef {
- name: "".to_string(),
- kind: ParamKind::Array,
+ ParamDef::Array {
+ elements,
optional: array_pat.optional,
ts_type,
}
@@ -60,28 +230,61 @@ fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef {
pub fn assign_pat_to_param_def(
assign_pat: &swc_ecma_ast::AssignPat,
+ source_map: Option<&SourceMap>,
) -> ParamDef {
- pat_to_param_def(&*assign_pat.left)
+ let ts_type = assign_pat
+ .type_ann
+ .as_ref()
+ .map(|rt| ts_type_ann_to_def(rt));
+
+ ParamDef::Assign {
+ left: Box::new(pat_to_param_def(&*assign_pat.left, source_map)),
+ right: "<UNIMPLEMENTED>".to_string(),
+ ts_type,
+ }
}
-pub fn pat_to_param_def(pat: &swc_ecma_ast::Pat) -> ParamDef {
+pub fn pat_to_param_def(
+ pat: &swc_ecma_ast::Pat,
+ source_map: Option<&SourceMap>,
+) -> 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),
+ Pat::Ident(ident) => ident_to_param_def(ident, source_map),
+ Pat::Array(array_pat) => array_pat_to_param_def(array_pat, source_map),
+ Pat::Rest(rest_pat) => rest_pat_to_param_def(rest_pat, source_map),
+ Pat::Object(object_pat) => object_pat_to_param_def(object_pat, source_map),
+ Pat::Assign(assign_pat) => assign_pat_to_param_def(assign_pat, source_map),
_ => unreachable!(),
}
}
pub fn ts_fn_param_to_param_def(
ts_fn_param: &swc_ecma_ast::TsFnParam,
+ source_map: Option<&SourceMap>,
) -> 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),
+ TsFnParam::Ident(ident) => ident_to_param_def(ident, source_map),
+ TsFnParam::Array(array_pat) => {
+ array_pat_to_param_def(array_pat, source_map)
+ }
+ TsFnParam::Rest(rest_pat) => rest_pat_to_param_def(rest_pat, source_map),
+ TsFnParam::Object(object_pat) => {
+ object_pat_to_param_def(object_pat, source_map)
+ }
+ }
+}
+
+pub fn prop_name_to_string(
+ prop_name: &swc_ecma_ast::PropName,
+ source_map: Option<&SourceMap>,
+) -> String {
+ use crate::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
+ .map(|sm| sm.span_to_snippet(comp_prop_name.span).unwrap())
+ .unwrap_or_else(|| "<UNAVAILABLE>".to_string()),
}
}
diff --git a/cli/doc/parser.rs b/cli/doc/parser.rs
index 3746e3dfb..59fa2b734 100644
--- a/cli/doc/parser.rs
+++ b/cli/doc/parser.rs
@@ -44,13 +44,15 @@ pub trait DocFileLoader {
pub struct DocParser {
pub ast_parser: AstParser,
pub loader: Box<dyn DocFileLoader>,
+ pub private: bool,
}
impl DocParser {
- pub fn new(loader: Box<dyn DocFileLoader>) -> Self {
+ pub fn new(loader: Box<dyn DocFileLoader>, private: bool) -> Self {
DocParser {
loader,
ast_parser: AstParser::new(),
+ private,
}
}
@@ -70,7 +72,7 @@ impl DocParser {
self.get_doc_nodes_for_module_body(module.body.clone());
let reexports = self.get_reexports_for_module_body(module.body);
let module_doc = ModuleDoc {
- exports: doc_entries,
+ definitions: doc_entries,
reexports,
};
Ok(module_doc)
@@ -90,7 +92,7 @@ impl DocParser {
source_code: &str,
) -> Result<Vec<DocNode>, ErrBox> {
let module_doc = self.parse_module(file_name, &source_code)?;
- Ok(module_doc.exports)
+ Ok(module_doc.definitions)
}
async fn flatten_reexports(
@@ -187,10 +189,10 @@ impl DocParser {
let mut flattenned_reexports = self
.flatten_reexports(&module_doc.reexports, file_name)
.await?;
- flattenned_reexports.extend(module_doc.exports);
+ flattenned_reexports.extend(module_doc.definitions);
flattenned_reexports
} else {
- module_doc.exports
+ module_doc.definitions
};
Ok(flattened_docs)
@@ -231,8 +233,10 @@ impl DocParser {
}
}
DefaultDecl::Fn(fn_expr) => {
- let function_def =
- crate::doc::function::function_to_function_def(&fn_expr.function);
+ let function_def = crate::doc::function::function_to_function_def(
+ self,
+ &fn_expr.function,
+ );
DocNode {
kind: DocNodeKind::Function,
name,
@@ -292,7 +296,7 @@ impl DocParser {
pub fn get_doc_node_for_decl(&self, decl: &Decl) -> Option<DocNode> {
match decl {
Decl::Class(class_decl) => {
- if !class_decl.declare {
+ if !self.private && !class_decl.declare {
return None;
}
let (name, class_def) =
@@ -313,11 +317,11 @@ impl DocParser {
})
}
Decl::Fn(fn_decl) => {
- if !fn_decl.declare {
+ if !self.private && !fn_decl.declare {
return None;
}
let (name, function_def) =
- super::function::get_doc_for_fn_decl(fn_decl);
+ super::function::get_doc_for_fn_decl(self, fn_decl);
let (js_doc, location) = self.details_for_span(fn_decl.function.span);
Some(DocNode {
kind: DocNodeKind::Function,
@@ -334,7 +338,7 @@ impl DocParser {
})
}
Decl::Var(var_decl) => {
- if !var_decl.declare {
+ if !self.private && !var_decl.declare {
return None;
}
let (name, var_def) = super::variable::get_doc_for_var_decl(var_decl);
@@ -354,7 +358,7 @@ impl DocParser {
})
}
Decl::TsInterface(ts_interface_decl) => {
- if !ts_interface_decl.declare {
+ if !self.private && !ts_interface_decl.declare {
return None;
}
let (name, interface_def) =
@@ -378,7 +382,7 @@ impl DocParser {
})
}
Decl::TsTypeAlias(ts_type_alias) => {
- if !ts_type_alias.declare {
+ if !self.private && !ts_type_alias.declare {
return None;
}
let (name, type_alias_def) =
@@ -402,7 +406,7 @@ impl DocParser {
})
}
Decl::TsEnum(ts_enum) => {
- if !ts_enum.declare {
+ if !self.private && !ts_enum.declare {
return None;
}
let (name, enum_def) =
@@ -423,7 +427,7 @@ impl DocParser {
})
}
Decl::TsModule(ts_module) => {
- if !ts_module.declare {
+ if !self.private && !ts_module.declare {
return None;
}
let (name, namespace_def) =
diff --git a/cli/doc/printer.rs b/cli/doc/printer.rs
index b563a6adc..2e55c752a 100644
--- a/cli/doc/printer.rs
+++ b/cli/doc/printer.rs
@@ -12,540 +12,463 @@
use crate::colors;
use crate::doc;
-use crate::doc::ts_type::TsTypeDefKind;
+use crate::doc::display::{
+ display_abstract, display_async, display_generator, Indent, SliceDisplayer,
+};
use crate::doc::DocNodeKind;
use crate::swc_ecma_ast;
+use std::fmt::{Display, Formatter, Result as FmtResult};
-pub fn format(doc_nodes: Vec<doc::DocNode>) -> String {
- format_(doc_nodes, 0)
+pub struct DocPrinter<'a> {
+ doc_nodes: &'a [doc::DocNode],
+ details: bool,
+ private: bool,
}
-pub fn format_details(node: doc::DocNode) -> String {
- let mut details = String::new();
+impl<'a> DocPrinter<'a> {
+ pub fn new(
+ doc_nodes: &[doc::DocNode],
+ details: bool,
+ private: bool,
+ ) -> DocPrinter {
+ DocPrinter {
+ doc_nodes,
+ details,
+ private,
+ }
+ }
- details.push_str(&format!(
- "{}",
- colors::gray(&format!(
- "Defined in {}:{}:{} \n\n",
- node.location.filename, node.location.line, node.location.col
- ))
- ));
+ pub fn format(&self, w: &mut Formatter<'_>) -> FmtResult {
+ if self.details {
+ self.format_details(w, self.doc_nodes, 0)
+ } else {
+ self.format_summary(w, self.doc_nodes, 0)
+ }
+ }
- details.push_str(&format_signature(&node, 0));
+ fn format_summary(
+ &self,
+ w: &mut Formatter<'_>,
+ doc_nodes: &[doc::DocNode],
+ indent: i64,
+ ) -> FmtResult {
+ let mut sorted = Vec::from(doc_nodes);
+ sorted.sort_unstable_by(|a, b| {
+ let kind_cmp = self.kind_order(&a.kind).cmp(&self.kind_order(&b.kind));
+ if kind_cmp == core::cmp::Ordering::Equal {
+ a.name.cmp(&b.name)
+ } else {
+ kind_cmp
+ }
+ });
- let js_doc = node.js_doc.clone();
- if let Some(js_doc) = js_doc {
- details.push_str(&format_jsdoc(js_doc, 1));
- }
- details.push_str("\n");
+ for node in sorted {
+ self.format_signature(w, &node, indent)?;
- let maybe_extra = match node.kind {
- DocNodeKind::Class => Some(format_class_details(node)),
- DocNodeKind::Enum => Some(format_enum_details(node)),
- DocNodeKind::Namespace => Some(format_namespace_details(node)),
- _ => None,
- };
+ if let Some(js_doc) = &node.js_doc {
+ self.format_jsdoc(w, js_doc, indent + 1, self.details)?;
+ }
- if let Some(extra) = maybe_extra {
- details.push_str(&extra);
- }
+ writeln!(w)?;
- details
-}
+ if DocNodeKind::Namespace == node.kind {
+ self.format_summary(
+ w,
+ &node.namespace_def.as_ref().unwrap().elements,
+ indent + 1,
+ )?;
-fn kind_order(kind: &doc::DocNodeKind) -> i64 {
- match kind {
- DocNodeKind::Function => 0,
- DocNodeKind::Variable => 1,
- DocNodeKind::Class => 2,
- DocNodeKind::Enum => 3,
- DocNodeKind::Interface => 4,
- DocNodeKind::TypeAlias => 5,
- DocNodeKind::Namespace => 6,
- }
-}
+ writeln!(w)?;
+ };
+ }
-fn format_signature(node: &doc::DocNode, indent: i64) -> String {
- match node.kind {
- DocNodeKind::Function => format_function_signature(&node, indent),
- DocNodeKind::Variable => format_variable_signature(&node, indent),
- DocNodeKind::Class => format_class_signature(&node, indent),
- DocNodeKind::Enum => format_enum_signature(&node, indent),
- DocNodeKind::Interface => format_interface_signature(&node, indent),
- DocNodeKind::TypeAlias => format_type_alias_signature(&node, indent),
- DocNodeKind::Namespace => format_namespace_signature(&node, indent),
+ Ok(())
}
-}
-fn format_(doc_nodes: Vec<doc::DocNode>, indent: i64) -> String {
- let mut sorted = doc_nodes;
- sorted.sort_unstable_by(|a, b| {
- let kind_cmp = kind_order(&a.kind).cmp(&kind_order(&b.kind));
- if kind_cmp == core::cmp::Ordering::Equal {
- a.name.cmp(&b.name)
- } else {
- kind_cmp
+ fn format_details(
+ &self,
+ w: &mut Formatter<'_>,
+ doc_nodes: &[doc::DocNode],
+ indent: i64,
+ ) -> FmtResult {
+ for node in doc_nodes {
+ write!(
+ w,
+ "{}",
+ colors::italic_gray(&format!(
+ "Defined in {}:{}:{} \n\n",
+ node.location.filename, node.location.line, node.location.col
+ ))
+ )?;
+
+ self.format_signature(w, &node, indent)?;
+
+ let js_doc = &node.js_doc;
+ if let Some(js_doc) = js_doc {
+ self.format_jsdoc(w, js_doc, indent + 1, self.details)?;
+ }
+ writeln!(w)?;
+
+ match node.kind {
+ DocNodeKind::Class => self.format_class_details(w, node)?,
+ DocNodeKind::Enum => self.format_enum_details(w, node)?,
+ DocNodeKind::Interface => self.format_interface_details(w, node)?,
+ DocNodeKind::Namespace => self.format_namespace_details(w, node)?,
+ _ => {}
+ }
}
- });
- let mut output = String::new();
+ Ok(())
+ }
- for node in sorted {
- output.push_str(&format_signature(&node, indent));
- if let Some(js_doc) = node.js_doc {
- output.push_str(&format_jsdoc(js_doc, indent));
+ fn kind_order(&self, kind: &doc::DocNodeKind) -> i64 {
+ match kind {
+ DocNodeKind::Function => 0,
+ DocNodeKind::Variable => 1,
+ DocNodeKind::Class => 2,
+ DocNodeKind::Enum => 3,
+ DocNodeKind::Interface => 4,
+ DocNodeKind::TypeAlias => 5,
+ DocNodeKind::Namespace => 6,
}
- output.push_str("\n");
- if DocNodeKind::Namespace == node.kind {
- output.push_str(&format_(
- node.namespace_def.as_ref().unwrap().elements.clone(),
- indent + 1,
- ));
- output.push_str("\n");
- };
}
- output
-}
-
-fn render_params(params: Vec<doc::ParamDef>) -> String {
- let mut rendered = String::from("");
- if !params.is_empty() {
- for param in params {
- rendered += param.name.as_str();
- if param.optional {
- rendered += "?";
+ fn format_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ match node.kind {
+ DocNodeKind::Function => self.format_function_signature(w, node, indent),
+ DocNodeKind::Variable => self.format_variable_signature(w, node, indent),
+ DocNodeKind::Class => self.format_class_signature(w, node, indent),
+ DocNodeKind::Enum => self.format_enum_signature(w, node, indent),
+ DocNodeKind::Interface => {
+ self.format_interface_signature(w, node, indent)
+ }
+ DocNodeKind::TypeAlias => {
+ self.format_type_alias_signature(w, node, indent)
}
- if let Some(ts_type) = param.ts_type {
- rendered += ": ";
- rendered += render_ts_type(ts_type).as_str();
+ DocNodeKind::Namespace => {
+ self.format_namespace_signature(w, node, indent)
}
- rendered += ", ";
}
- rendered.truncate(rendered.len() - 2);
}
- rendered
-}
-fn render_ts_type(ts_type: doc::ts_type::TsTypeDef) -> String {
- if ts_type.kind.is_none() {
- return "<UNIMPLEMENTED>".to_string();
- }
- let kind = ts_type.kind.unwrap();
- match kind {
- TsTypeDefKind::Array => {
- format!("{}[]", render_ts_type(*ts_type.array.unwrap()))
- }
- TsTypeDefKind::Conditional => {
- let conditional = ts_type.conditional_type.unwrap();
- format!(
- "{} extends {} ? {} : {}",
- render_ts_type(*conditional.check_type),
- render_ts_type(*conditional.extends_type),
- render_ts_type(*conditional.true_type),
- render_ts_type(*conditional.false_type)
- )
- }
- TsTypeDefKind::FnOrConstructor => {
- let fn_or_constructor = ts_type.fn_or_constructor.unwrap();
- format!(
- "{}({}) => {}",
- if fn_or_constructor.constructor {
- "new "
- } else {
- ""
- },
- render_params(fn_or_constructor.params),
- render_ts_type(fn_or_constructor.ts_type),
- )
- }
- TsTypeDefKind::IndexedAccess => {
- let indexed_access = ts_type.indexed_access.unwrap();
- format!(
- "{}[{}]",
- render_ts_type(*indexed_access.obj_type),
- render_ts_type(*indexed_access.index_type)
- )
+ // TODO(SyrupThinker) this should use a JSDoc parser
+ fn format_jsdoc(
+ &self,
+ w: &mut Formatter<'_>,
+ jsdoc: &str,
+ indent: i64,
+ details: bool,
+ ) -> FmtResult {
+ for line in jsdoc.lines() {
+ // Only show the first paragraph when summarising
+ // This should use the @summary JSDoc tag instead
+ if !details && line.is_empty() {
+ break;
+ }
+
+ writeln!(w, "{}{}", Indent(indent), colors::gray(&line))?;
}
- TsTypeDefKind::Intersection => {
- let intersection = ts_type.intersection.unwrap();
- let mut output = "".to_string();
- if !intersection.is_empty() {
- for ts_type in intersection {
- output += render_ts_type(ts_type).as_str();
- output += " & "
- }
- output.truncate(output.len() - 3);
+
+ Ok(())
+ }
+
+ fn format_class_details(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ ) -> FmtResult {
+ let class_def = node.class_def.as_ref().unwrap();
+ for node in &class_def.constructors {
+ writeln!(w, "{}{}", Indent(1), node,)?;
+ if let Some(js_doc) = &node.js_doc {
+ self.format_jsdoc(w, &js_doc, 2, self.details)?;
}
- output
}
- TsTypeDefKind::Keyword => ts_type.keyword.unwrap(),
- TsTypeDefKind::Literal => {
- let literal = ts_type.literal.unwrap();
- match literal.kind {
- doc::ts_type::LiteralDefKind::Boolean => {
- format!("{}", literal.boolean.unwrap())
- }
- doc::ts_type::LiteralDefKind::String => {
- "\"".to_string() + literal.string.unwrap().as_str() + "\""
- }
- doc::ts_type::LiteralDefKind::Number => {
- format!("{}", literal.number.unwrap())
- }
+ for node in class_def.properties.iter().filter(|node| {
+ self.private
+ || node
+ .accessibility
+ .unwrap_or(swc_ecma_ast::Accessibility::Public)
+ != swc_ecma_ast::Accessibility::Private
+ }) {
+ writeln!(w, "{}{}", Indent(1), node,)?;
+ if let Some(js_doc) = &node.js_doc {
+ self.format_jsdoc(w, &js_doc, 2, self.details)?;
}
}
- TsTypeDefKind::Optional => {
- format!("{}?", render_ts_type(*ts_type.optional.unwrap()))
+ for index_sign_def in &class_def.index_signatures {
+ writeln!(w, "{}{}", Indent(1), index_sign_def)?;
}
- TsTypeDefKind::Parenthesized => {
- format!("({})", render_ts_type(*ts_type.parenthesized.unwrap()))
+ for node in class_def.methods.iter().filter(|node| {
+ self.private
+ || node
+ .accessibility
+ .unwrap_or(swc_ecma_ast::Accessibility::Public)
+ != swc_ecma_ast::Accessibility::Private
+ }) {
+ writeln!(w, "{}{}", Indent(1), node,)?;
+ if let Some(js_doc) = &node.js_doc {
+ self.format_jsdoc(w, js_doc, 2, self.details)?;
+ }
}
- TsTypeDefKind::Rest => {
- format!("...{}", render_ts_type(*ts_type.rest.unwrap()))
+ writeln!(w)
+ }
+
+ fn format_enum_details(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ ) -> FmtResult {
+ let enum_def = node.enum_def.as_ref().unwrap();
+ for member in &enum_def.members {
+ writeln!(w, "{}{}", Indent(1), colors::bold(&member.name))?;
}
- TsTypeDefKind::This => "this".to_string(),
- TsTypeDefKind::Tuple => {
- let tuple = ts_type.tuple.unwrap();
- let mut output = "[".to_string();
- if !tuple.is_empty() {
- for ts_type in tuple {
- output += render_ts_type(ts_type).as_str();
- output += ", "
- }
- output.truncate(output.len() - 2);
+ writeln!(w)
+ }
+
+ fn format_interface_details(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ ) -> FmtResult {
+ let interface_def = node.interface_def.as_ref().unwrap();
+
+ for property_def in &interface_def.properties {
+ writeln!(w, "{}{}", Indent(1), property_def)?;
+ if let Some(js_doc) = &property_def.js_doc {
+ self.format_jsdoc(w, js_doc, 2, self.details)?;
}
- output += "]";
- output
}
- TsTypeDefKind::TypeLiteral => {
- let mut output = "".to_string();
- let type_literal = ts_type.type_literal.unwrap();
- for node in type_literal.call_signatures {
- output += format!(
- "({}){}, ",
- render_params(node.params),
- if let Some(ts_type) = node.ts_type {
- format!(": {}", render_ts_type(ts_type))
- } else {
- "".to_string()
- }
- )
- .as_str()
- }
- for node in type_literal.methods {
- output += format!(
- "{}({}){}, ",
- node.name,
- render_params(node.params),
- if let Some(return_type) = node.return_type {
- format!(": {}", render_ts_type(return_type))
- } else {
- "".to_string()
- }
- )
- .as_str()
- }
- for node in type_literal.properties {
- output += format!(
- "{}{}, ",
- node.name,
- if let Some(ts_type) = node.ts_type {
- format!(": {}", render_ts_type(ts_type))
- } else {
- "".to_string()
- }
- )
- .as_str()
+ for method_def in &interface_def.methods {
+ writeln!(w, "{}{}", Indent(1), method_def)?;
+ if let Some(js_doc) = &method_def.js_doc {
+ self.format_jsdoc(w, js_doc, 2, self.details)?;
}
- if !output.is_empty() {
- output.truncate(output.len() - 2);
- }
- "{ ".to_string() + output.as_str() + " }"
- }
- TsTypeDefKind::TypeOperator => {
- let operator = ts_type.type_operator.unwrap();
- format!("{} {}", operator.operator, render_ts_type(operator.ts_type))
- }
- TsTypeDefKind::TypeQuery => {
- format!("typeof {}", ts_type.type_query.unwrap())
}
- TsTypeDefKind::TypeRef => {
- let type_ref = ts_type.type_ref.unwrap();
- let mut final_output = type_ref.type_name;
- if let Some(type_params) = type_ref.type_params {
- let mut output = "".to_string();
- if !type_params.is_empty() {
- for ts_type in type_params {
- output += render_ts_type(ts_type).as_str();
- output += ", "
- }
- output.truncate(output.len() - 2);
- }
- final_output += format!("<{}>", output).as_str();
- }
- final_output
+ for index_sign_def in &interface_def.index_signatures {
+ writeln!(w, "{}{}", Indent(1), index_sign_def)?;
}
- TsTypeDefKind::Union => {
- let union = ts_type.union.unwrap();
- let mut output = "".to_string();
- if !union.is_empty() {
- for ts_type in union {
- output += render_ts_type(ts_type).as_str();
- output += " | "
- }
- output.truncate(output.len() - 3);
+ writeln!(w)
+ }
+
+ fn format_namespace_details(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ ) -> FmtResult {
+ let elements = &node.namespace_def.as_ref().unwrap().elements;
+ for node in elements {
+ self.format_signature(w, &node, 1)?;
+ if let Some(js_doc) = &node.js_doc {
+ self.format_jsdoc(w, js_doc, 2, false)?;
}
- output
}
+ writeln!(w)
}
-}
-fn add_indent(string: String, indent: i64) -> String {
- let mut indent_str = String::new();
- for _ in 0..(indent * 2) {
- indent_str += " ";
- }
- indent_str += string.as_str();
- indent_str
-}
+ fn format_class_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ let class_def = node.class_def.as_ref().unwrap();
+ write!(
+ w,
+ "{}{}{} {}",
+ Indent(indent),
+ display_abstract(class_def.is_abstract),
+ colors::magenta("class"),
+ colors::bold(&node.name),
+ )?;
+ if !class_def.type_params.is_empty() {
+ write!(
+ w,
+ "<{}>",
+ SliceDisplayer::new(&class_def.type_params, ", ", false)
+ )?;
+ }
-// TODO: this should use some sort of markdown to console parser.
-fn format_jsdoc(jsdoc: String, indent: i64) -> String {
- let lines = jsdoc.split("\n\n").map(|line| line.replace("\n", " "));
+ if let Some(extends) = &class_def.extends {
+ write!(w, " {} {}", colors::magenta("extends"), extends)?;
+ }
+ if !class_def.super_type_params.is_empty() {
+ write!(
+ w,
+ "<{}>",
+ SliceDisplayer::new(&class_def.super_type_params, ", ", false)
+ )?;
+ }
- let mut js_doc = String::new();
+ if !class_def.implements.is_empty() {
+ write!(
+ w,
+ " {} {}",
+ colors::magenta("implements"),
+ SliceDisplayer::new(&class_def.implements, ", ", false)
+ )?;
+ }
- for line in lines {
- js_doc.push_str(&add_indent(format!("{}\n", line), indent + 1));
+ writeln!(w)
}
- format!("{}", colors::gray(&js_doc))
-}
-
-fn format_class_details(node: doc::DocNode) -> String {
- let mut details = String::new();
-
- let class_def = node.class_def.unwrap();
- for node in class_def.constructors {
- details.push_str(&add_indent(
- format!(
- "{} {}({})\n",
- colors::magenta("constructor"),
- colors::bold(&node.name),
- render_params(node.params),
- ),
- 1,
- ));
- }
- for node in class_def.properties.iter().filter(|node| {
- node
- .accessibility
- .unwrap_or(swc_ecma_ast::Accessibility::Public)
- != swc_ecma_ast::Accessibility::Private
- }) {
- details.push_str(&add_indent(
- format!(
- "{}{}{}{}\n",
- colors::magenta(
- match node
- .accessibility
- .unwrap_or(swc_ecma_ast::Accessibility::Public)
- {
- swc_ecma_ast::Accessibility::Protected => "protected ",
- _ => "",
- }
- ),
- colors::bold(&node.name),
- if node.optional {
- "?".to_string()
- } else {
- "".to_string()
- },
- if let Some(ts_type) = node.ts_type.clone() {
- format!(": {}", render_ts_type(ts_type))
- } else {
- "".to_string()
- }
- ),
- 1,
- ));
- }
- for node in class_def.methods.iter().filter(|node| {
- node
- .accessibility
- .unwrap_or(swc_ecma_ast::Accessibility::Public)
- != swc_ecma_ast::Accessibility::Private
- }) {
- let function_def = node.function_def.clone();
- details.push_str(&add_indent(
- format!(
- "{}{}{}{}({}){}\n",
- colors::magenta(
- match node
- .accessibility
- .unwrap_or(swc_ecma_ast::Accessibility::Public)
- {
- swc_ecma_ast::Accessibility::Protected => "protected ",
- _ => "",
- }
- ),
- colors::magenta(match node.kind {
- swc_ecma_ast::MethodKind::Getter => "get ",
- swc_ecma_ast::MethodKind::Setter => "set ",
- _ => "",
- }),
- colors::bold(&node.name),
- if node.optional { "?" } else { "" },
- render_params(function_def.params),
- if let Some(return_type) = function_def.return_type {
- format!(": {}", render_ts_type(return_type))
- } else {
- "".to_string()
- }
- ),
- 1,
- ));
+ fn format_enum_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ writeln!(
+ w,
+ "{}{} {}",
+ Indent(indent),
+ colors::magenta("enum"),
+ colors::bold(&node.name)
+ )
}
- details.push_str("\n");
- details
-}
-fn format_enum_details(node: doc::DocNode) -> String {
- let mut details = String::new();
- let enum_def = node.enum_def.unwrap();
- for member in enum_def.members {
- details
- .push_str(&add_indent(format!("{}\n", colors::bold(&member.name)), 1));
+ fn format_function_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ let function_def = node.function_def.as_ref().unwrap();
+ write!(
+ w,
+ "{}{}{}{} {}",
+ Indent(indent),
+ display_async(function_def.is_async),
+ colors::magenta("function"),
+ display_generator(function_def.is_generator),
+ colors::bold(&node.name)
+ )?;
+ if !function_def.type_params.is_empty() {
+ write!(
+ w,
+ "<{}>",
+ SliceDisplayer::new(&function_def.type_params, ", ", false)
+ )?;
+ }
+ write!(
+ w,
+ "({})",
+ SliceDisplayer::new(&function_def.params, ", ", false)
+ )?;
+ if let Some(return_type) = &function_def.return_type {
+ write!(w, ": {}", return_type)?;
+ }
+ writeln!(w)
}
- details.push_str("\n");
- details
-}
-fn format_namespace_details(node: doc::DocNode) -> String {
- let mut ns = String::new();
+ fn format_interface_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ let interface_def = node.interface_def.as_ref().unwrap();
+ write!(
+ w,
+ "{}{} {}",
+ Indent(indent),
+ colors::magenta("interface"),
+ colors::bold(&node.name)
+ )?;
+
+ if !interface_def.type_params.is_empty() {
+ write!(
+ w,
+ "<{}>",
+ SliceDisplayer::new(&interface_def.type_params, ", ", false)
+ )?;
+ }
+
+ if !interface_def.extends.is_empty() {
+ write!(
+ w,
+ " {} {}",
+ colors::magenta("extends"),
+ SliceDisplayer::new(&interface_def.extends, ", ", false)
+ )?;
+ }
- let elements = node.namespace_def.unwrap().elements;
- for node in elements {
- ns.push_str(&format_signature(&node, 1));
+ writeln!(w)
}
- ns.push_str("\n");
- ns
-}
-fn format_function_signature(node: &doc::DocNode, indent: i64) -> String {
- let function_def = node.function_def.clone().unwrap();
- add_indent(
- format!(
- "{} {}({}){}\n",
- colors::magenta("function"),
+ fn format_type_alias_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ let type_alias_def = node.type_alias_def.as_ref().unwrap();
+ write!(
+ w,
+ "{}{} {}",
+ Indent(indent),
+ colors::magenta("type"),
colors::bold(&node.name),
- render_params(function_def.params),
- if let Some(return_type) = function_def.return_type {
- format!(": {}", render_ts_type(return_type).as_str())
- } else {
- "".to_string()
- }
- ),
- indent,
- )
-}
+ )?;
+
+ if !type_alias_def.type_params.is_empty() {
+ write!(
+ w,
+ "<{}>",
+ SliceDisplayer::new(&type_alias_def.type_params, ", ", false)
+ )?;
+ }
-fn format_class_signature(node: &doc::DocNode, indent: i64) -> String {
- let class_def = node.class_def.clone().unwrap();
- let extends_suffix = if let Some(extends) = class_def.extends {
- format!(" {} {}", colors::magenta("extends"), colors::bold(&extends))
- } else {
- String::from("")
- };
-
- let implements = &class_def.implements;
- let implements_suffix = if !implements.is_empty() {
- format!(
- " {} {}",
- colors::magenta("implements"),
- colors::bold(&implements.join(", "))
- )
- } else {
- String::from("")
- };
+ writeln!(w, " = {}", type_alias_def.ts_type)
+ }
- add_indent(
- format!(
- "{} {}{}{}\n",
- colors::magenta("class"),
- colors::bold(&node.name),
- extends_suffix,
- implements_suffix,
- ),
- indent,
- )
-}
+ fn format_namespace_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ writeln!(
+ w,
+ "{}{} {}",
+ Indent(indent),
+ colors::magenta("namespace"),
+ colors::bold(&node.name)
+ )
+ }
-fn format_variable_signature(node: &doc::DocNode, indent: i64) -> String {
- let variable_def = node.variable_def.clone().unwrap();
- add_indent(
- format!(
- "{} {}{}\n",
+ fn format_variable_signature(
+ &self,
+ w: &mut Formatter<'_>,
+ node: &doc::DocNode,
+ indent: i64,
+ ) -> FmtResult {
+ let variable_def = node.variable_def.as_ref().unwrap();
+ write!(
+ w,
+ "{}{} {}",
+ Indent(indent),
colors::magenta(match variable_def.kind {
swc_ecma_ast::VarDeclKind::Const => "const",
swc_ecma_ast::VarDeclKind::Let => "let",
swc_ecma_ast::VarDeclKind::Var => "var",
}),
colors::bold(&node.name),
- if let Some(ts_type) = variable_def.ts_type {
- format!(": {}", render_ts_type(ts_type))
- } else {
- "".to_string()
- }
- ),
- indent,
- )
-}
-
-fn format_enum_signature(node: &doc::DocNode, indent: i64) -> String {
- add_indent(
- format!("{} {}\n", colors::magenta("enum"), colors::bold(&node.name)),
- indent,
- )
-}
-
-fn format_interface_signature(node: &doc::DocNode, indent: i64) -> String {
- let interface_def = node.interface_def.clone().unwrap();
- let extends = &interface_def.extends;
- let extends_suffix = if !extends.is_empty() {
- format!(
- " {} {}",
- colors::magenta("extends"),
- colors::bold(&extends.join(", "))
- )
- } else {
- String::from("")
- };
- add_indent(
- format!(
- "{} {}{}\n",
- colors::magenta("interface"),
- colors::bold(&node.name),
- extends_suffix
- ),
- indent,
- )
-}
-
-fn format_type_alias_signature(node: &doc::DocNode, indent: i64) -> String {
- add_indent(
- format!("{} {}\n", colors::magenta("type"), colors::bold(&node.name)),
- indent,
- )
+ )?;
+ if let Some(ts_type) = &variable_def.ts_type {
+ write!(w, ": {}", ts_type)?;
+ }
+ writeln!(w)
+ }
}
-fn format_namespace_signature(node: &doc::DocNode, indent: i64) -> String {
- add_indent(
- format!(
- "{} {}\n",
- colors::magenta("namespace"),
- colors::bold(&node.name)
- ),
- indent,
- )
+impl<'a> Display for DocPrinter<'a> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ self.format(f)
+ }
}
diff --git a/cli/doc/tests.rs b/cli/doc/tests.rs
index 7efc3857b..e46fff621 100644
--- a/cli/doc/tests.rs
+++ b/cli/doc/tests.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use super::DocParser;
+use super::DocPrinter;
use crate::colors;
use serde_json::json;
@@ -41,360 +42,334 @@ impl DocFileLoader for TestLoader {
}
}
+macro_rules! doc_test {
+ ( $name:ident, $source:expr; $block:block ) => {
+ doc_test!($name, $source, false, false; $block);
+ };
+
+ ( $name:ident, $source:expr, details; $block:block ) => {
+ doc_test!($name, $source, true, false; $block);
+ };
+
+ ( $name:ident, $source:expr, private; $block:block ) => {
+ doc_test!($name, $source, false, true; $block);
+ };
+
+ ( $name:ident, $source:expr, details, private; $block:block ) => {
+ doc_test!($name, $source, true, true; $block);
+ };
+
+ ( $name:ident, $source:expr, $details:expr, $private:expr; $block:block ) => {
+ #[tokio::test]
+ async fn $name() {
+ let source_code = $source;
+ let details = $details;
+ let private = $private;
+
+ let loader =
+ TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
+ let entries = DocParser::new(loader, private)
+ .parse("test.ts")
+ .await
+ .unwrap();
+
+ let doc = DocPrinter::new(&entries, details, private).to_string();
+ #[allow(unused_variables)]
+ let doc = colors::strip_ansi_codes(&doc);
+
+ $block
+ }
+ };
+}
+
+macro_rules! contains_test {
+ ( $name:ident, $source:expr;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, false, false; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, details;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, true, false; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, private;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, false, true; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, details, private;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ contains_test!($name, $source, true, true; $($contains),* $(;$($notcontains),*)?);
+ };
+
+ ( $name:ident, $source:expr, $details:expr, $private:expr;
+ $( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
+ doc_test!($name, $source, $details, $private; {
+ $(
+ assert!(doc.contains($contains));
+ )*
+ $(
+ $(
+ assert!(!doc.contains($notcontains));
+ )*
+ )?
+ });
+ };
+}
+
+macro_rules! json_test {
+ ( $name:ident, $source:expr; $json:tt ) => {
+ json_test!($name, $source, false; $json);
+ };
+
+ ( $name:ident, $source:expr, private; $json:tt ) => {
+ json_test!($name, $source, true; $json);
+ };
+
+ ( $name:ident, $source:expr, $private:expr; $json:tt ) => {
+ doc_test!($name, $source, false, $private; {
+ let actual = serde_json::to_value(&entries).unwrap();
+ let expected_json = json!($json);
+ assert_eq!(actual, expected_json);
+ });
+ };
+}
+
#[tokio::test]
-async fn export_fn() {
- let source_code = r#"/**
-* @module foo
-*/
+async fn reexports() {
+ let nested_reexport_source_code = r#"
+/**
+ * JSDoc for bar
+ */
+export const bar = "bar";
+
+export default 42;
+"#;
+ let reexport_source_code = r#"
+import { bar } from "./nested_reexport.ts";
/**
-* Hello there, this is a multiline JSdoc.
-*
-* It has many lines
-*
-* Or not that many?
-*/
-export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void {
- /**
- * @todo document all the things.
- */
- console.log("Hello world");
+ * JSDoc for const
+ */
+export const foo = "foo";
+"#;
+ let test_source_code = r#"
+export { default, foo as fooConst } from "./reexport.ts";
+
+/** JSDoc for function */
+export function fooFn(a: number) {
+ return a;
}
"#;
- 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": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "string",
- "kind": "keyword",
- "repr": "string",
- },
- },
- {
- "name": "b",
- "kind": "identifier",
- "optional": true,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- },
- {
- "name": "cb",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "fnOrConstructor",
- "fnOrConstructor": {
- "constructor": false,
- "tsType": {
- "keyword": "void",
- "kind": "keyword",
- "repr": "void"
- },
- "typeParams": [],
- "params": [{
- "kind": "rest",
- "name": "cbArgs",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- },
- }]
- }
- },
- },
- {
- "name": "args",
- "kind": "rest",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- }
- }
- ],
- "returnType": {
- "keyword": "void",
- "kind": "keyword",
- "repr": "void",
+ let loader = TestLoader::new(vec![
+ ("file:///test.ts".to_string(), test_source_code.to_string()),
+ (
+ "file:///reexport.ts".to_string(),
+ reexport_source_code.to_string(),
+ ),
+ (
+ "file:///nested_reexport.ts".to_string(),
+ nested_reexport_source_code.to_string(),
+ ),
+ ]);
+ let entries = DocParser::new(loader, false)
+ .parse_with_reexports("file:///test.ts")
+ .await
+ .unwrap();
+ assert_eq!(entries.len(), 2);
+
+ let expected_json = json!([
+ {
+ "kind": "variable",
+ "name": "fooConst",
+ "location": {
+ "filename": "file:///reexport.ts",
+ "line": 7,
+ "col": 0
},
+ "jsDoc": "JSDoc for const",
+ "variableDef": {
+ "tsType": null,
+ "kind": "const"
+ }
},
- "jsDoc": "Hello there, this is a multiline JSdoc.\n\nIt has many lines\n\nOr not that many?",
- "kind": "function",
- "location": {
- "col": 0,
- "filename": "test.ts",
- "line": 12,
- },
- "name": "foo",
- });
-
- let actual = serde_json::to_value(entry).unwrap();
+ {
+ "kind": "function",
+ "name": "fooFn",
+ "location": {
+ "filename": "file:///test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "JSDoc for function",
+ "functionDef": {
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ }
+ ],
+ "typeParams": [],
+ "returnType": null,
+ "isAsync": false,
+ "isGenerator": false
+ }
+ }
+ ]);
+ let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
assert!(colors::strip_ansi_codes(
- super::printer::format(entries.clone()).as_str()
+ DocPrinter::new(&entries, false, false).to_string().as_str()
)
- .contains("Hello there"));
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("b?: number")
- );
-}
-
-#[tokio::test]
-async fn format_type_predicate() {
- let source_code = r#"
-export function isFish(pet: Fish | Bird): pet is Fish {
- return (pet as Fish).swim !== undefined;
-}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- super::printer::format(entries);
+ .contains("function fooFn(a: number)"));
}
#[tokio::test]
-async fn export_fn2() {
+async fn filter_nodes_by_name() {
+ use super::find_nodes_by_name_recursively;
let source_code = r#"
-interface AssignOpts {
- a: string;
- b: number;
+export namespace Deno {
+ export class Buffer {}
+ export function test(options: object): void;
+ export function test(name: string, fn: Function): void;
+ export function test(name: string | object, fn?: Function): void {}
}
-export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, ops: AssignOpts = {}): void {
- console.log("Hello world");
+export namespace Deno {
+ export namespace Inner {
+ export function a(): void {}
+ export const b = 100;
+ }
}
"#;
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",
- "optional": false,
- "tsType": {
- "repr": "",
- "kind": "array",
- "array": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- }
- },
- {
- "name": "",
- "kind": "object",
- "optional": false,
- "tsType": null
- },
- {
- "name": "ops",
- "kind": "identifier",
- "optional": false,
- "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 entries = DocParser::new(loader, false)
+ .parse("test.ts")
+ .await
+ .unwrap();
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno".to_string());
+ assert_eq!(found.len(), 2);
+ assert_eq!(found[0].name, "Deno".to_string());
+ assert_eq!(found[1].name, "Deno".to_string());
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("foo")
- );
-}
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.test".to_string());
+ assert_eq!(found.len(), 3);
+ assert_eq!(found[0].name, "test".to_string());
+ assert_eq!(found[1].name, "test".to_string());
+ assert_eq!(found[2].name, "test".to_string());
-#[tokio::test]
-async fn export_const() {
- let source_code = r#"
-/** Something about fizzBuzz */
-export const fizzBuzz = "fizzBuzz";
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.Inner.a".to_string());
+ assert_eq!(found.len(), 1);
+ assert_eq!(found[0].name, "a".to_string());
-export const env: {
- /** get doc */
- get(key: string): string | undefined;
+ let found =
+ find_nodes_by_name_recursively(entries.clone(), "Deno.test.a".to_string());
+ assert_eq!(found.len(), 0);
- /** set doc */
- set(key: string, value: string): void;
+ let found = find_nodes_by_name_recursively(entries, "a.b.c".to_string());
+ assert_eq!(found.len(), 0);
}
-"#;
- 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(), 2);
- let expected_json = json!([
- {
- "kind":"variable",
- "name":"fizzBuzz",
- "location":{
- "filename":"test.ts",
- "line":3,
- "col":0
- },
- "jsDoc":"Something about fizzBuzz",
- "variableDef":{
- "tsType":null,
- "kind":"const"
+
+mod serialization {
+ use super::*;
+
+ json_test!(declare_namespace,
+ r#"
+/** Namespace JSdoc */
+declare namespace RootNs {
+ declare const a = "a";
+
+ /** Nested namespace JSDoc */
+ declare namespace NestedNs {
+ declare enum Foo {
+ a = 1,
+ b = 2,
+ c = 3,
+ }
}
- },
- {
- "kind":"variable",
- "name":"env",
- "location":{
- "filename":"test.ts",
- "line":5,
- "col":0
+}
+ "#;
+ [{
+ "kind": "namespace",
+ "name": "RootNs",
+ "location": {
+ "filename": "test.ts",
+ "line": 3,
+ "col": 0
},
- "jsDoc":null,
- "variableDef":{
- "tsType":{
- "repr":"",
- "kind":"typeLiteral",
- "typeLiteral":{
- "methods":[{
- "name":"get",
- "params":[
+ "jsDoc": "Namespace JSdoc",
+ "namespaceDef": {
+ "elements": [
+ {
+ "kind": "variable",
+ "name": "a",
+ "location": {
+ "filename": "test.ts",
+ "line": 4,
+ "col": 12
+ },
+ "jsDoc": null,
+ "variableDef": {
+ "tsType": null,
+ "kind": "const"
+ }
+ },
+ {
+ "kind": "namespace",
+ "name": "NestedNs",
+ "location": {
+ "filename": "test.ts",
+ "line": 7,
+ "col": 4
+ },
+ "jsDoc": "Nested namespace JSDoc",
+ "namespaceDef": {
+ "elements": [
{
- "name":"key",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
- }
- }
- ],
- "returnType":{
- "repr":"",
- "kind":"union",
- "union":[
- {
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
+ "kind": "enum",
+ "name": "Foo",
+ "location": {
+ "filename": "test.ts",
+ "line": 8,
+ "col": 6
},
- {
- "repr":"undefined",
- "kind":"keyword",
- "keyword":"undefined"
- }
- ]
- },
- "typeParams":[]
- }, {
- "name":"set",
- "params":[
- {
- "name":"key",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
- }
- },
- {
- "name":"value",
- "kind":"identifier",
- "optional":false,
- "tsType":{
- "repr":"string",
- "kind":"keyword",
- "keyword":"string"
+ "jsDoc": null,
+ "enumDef": {
+ "members": [
+ {
+ "name": "a"
+ },
+ {
+ "name": "b"
+ },
+ {
+ "name": "c"
+ }
+ ]
}
}
- ],
- "returnType":{
- "repr":"void",
- "kind":"keyword",
- "keyword":"void"
- },
- "typeParams":[]
- }
- ],
- "properties":[],
- "callSignatures":[]
+ ]
}
- },
- "kind":"const"
- }
+ }
+ ]
}
- ]
- );
-
- let actual = serde_json::to_value(entries.clone()).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("Something about fizzBuzz")
- );
-}
+ }]);
-#[tokio::test]
-async fn export_class() {
- let source_code = r#"
+ json_test!(export_class,
+ r#"
/** Class doc */
export class Foobar extends Fizz implements Buzz, Aldrin {
private private1?: boolean;
@@ -415,12 +390,8 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
//
}
}
-"#;
- 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 expected_json = json!({
+ "#;
+ [{
"kind": "class",
"name": "Foobar",
"location": {
@@ -432,8 +403,26 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
"classDef": {
"isAbstract": false,
"extends": "Fizz",
- "implements": ["Buzz", "Aldrin"],
+ "implements": [
+ {
+ "repr": "Buzz",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Buzz"
+ }
+ },
+ {
+ "repr": "Aldrin",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Aldrin"
+ }
+ }
+ ],
"typeParams": [],
+ "superTypeParams": [],
"constructors": [
{
"jsDoc": "Constructor js doc",
@@ -556,6 +545,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
}
}
],
+ "indexSignatures": [],
"methods": [
{
"jsDoc": "Async foo method",
@@ -618,25 +608,591 @@ export class Foobar extends Fizz implements Buzz, Aldrin {
}
]
}
- });
- let entry = &entries[0];
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ }]);
- assert!(colors::strip_ansi_codes(
- super::printer::format_details(entry.clone()).as_str()
- )
- .contains("bar?(): void"));
+ json_test!(export_const,
+ r#"
+/** Something about fizzBuzz */
+export const fizzBuzz = "fizzBuzz";
+
+export const env: {
+ /** get doc */
+ get(key: string): string | undefined;
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("class Foobar extends Fizz implements Buzz, Aldrin")
+ /** set doc */
+ set(key: string, value: string): void;
+}
+ "#;
+ [
+ {
+ "kind":"variable",
+ "name":"fizzBuzz",
+ "location":{
+ "filename":"test.ts",
+ "line":3,
+ "col":0
+ },
+ "jsDoc":"Something about fizzBuzz",
+ "variableDef":{
+ "tsType":null,
+ "kind":"const"
+ }
+ },
+ {
+ "kind":"variable",
+ "name":"env",
+ "location":{
+ "filename":"test.ts",
+ "line":5,
+ "col":0
+ },
+ "jsDoc":null,
+ "variableDef":{
+ "tsType":{
+ "repr":"",
+ "kind":"typeLiteral",
+ "typeLiteral":{
+ "methods":[{
+ "name":"get",
+ "params":[
+ {
+ "name":"key",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ }
+ ],
+ "returnType":{
+ "repr":"",
+ "kind":"union",
+ "union":[
+ {
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ },
+ {
+ "repr":"undefined",
+ "kind":"keyword",
+ "keyword":"undefined"
+ }
+ ]
+ },
+ "typeParams":[]
+ }, {
+ "name":"set",
+ "params":[
+ {
+ "name":"key",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ },
+ {
+ "name":"value",
+ "kind":"identifier",
+ "optional":false,
+ "tsType":{
+ "repr":"string",
+ "kind":"keyword",
+ "keyword":"string"
+ }
+ }
+ ],
+ "returnType":{
+ "repr":"void",
+ "kind":"keyword",
+ "keyword":"void"
+ },
+ "typeParams":[]
+ }
+ ],
+ "properties":[],
+ "callSignatures":[],
+ "indexSignatures": []
+ }
+ },
+ "kind":"const"
+ }
+ }
+ ]
);
+
+ json_test!(export_default_class,
+ r#"
+/** Class doc */
+export default class Foobar {
+ /** Constructor js doc */
+ constructor(name: string, private private2: number, protected protected2: number) {}
+}
+ "#;
+ [{
+ "kind": "class",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 3,
+ "col": 0
+ },
+ "jsDoc": "Class doc",
+ "classDef": {
+ "isAbstract": false,
+ "extends": null,
+ "implements": [],
+ "typeParams": [],
+ "superTypeParams": [],
+ "constructors": [
+ {
+ "jsDoc": "Constructor js doc",
+ "accessibility": null,
+ "name": "constructor",
+ "params": [
+ {
+ "name": "name",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "string",
+ "kind": "keyword",
+ "keyword": "string"
+ }
+ },
+ {
+ "name": "private2",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ },
+ {
+ "name": "protected2",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ }
+ ],
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 4
+ }
+ }
+ ],
+ "properties": [],
+ "indexSignatures": [],
+ "methods": []
+ }
+ }]);
+
+ json_test!(export_default_fn,
+ r#"
+export default function foo(a: number) {
+ return a;
}
+ "#;
+ [{
+ "kind": "function",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 2,
+ "col": 15
+ },
+ "jsDoc": null,
+ "functionDef": {
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ }
+ ],
+ "typeParams": [],
+ "returnType": null,
+ "isAsync": false,
+ "isGenerator": false
+ }
+ }]);
-#[tokio::test]
-async fn export_interface() {
- let source_code = r#"
+ json_test!(export_default_interface,
+ r#"
+/**
+ * Interface js doc
+ */
+export default interface Reader {
+ /** Read n bytes */
+ read?(buf: Uint8Array, something: unknown): Promise<number>
+}
+ "#;
+ [{
+ "kind": "interface",
+ "name": "default",
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "Interface js doc",
+ "interfaceDef": {
+ "extends": [],
+ "methods": [
+ {
+ "name": "read",
+ "location": {
+ "filename": "test.ts",
+ "line": 7,
+ "col": 4
+ },
+ "optional": true,
+ "jsDoc": "Read n bytes",
+ "params": [
+ {
+ "name": "buf",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "Uint8Array",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Uint8Array"
+ }
+ }
+ },
+ {
+ "name": "something",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "unknown",
+ "kind": "keyword",
+ "keyword": "unknown"
+ }
+ }
+ ],
+ "typeParams": [],
+ "returnType": {
+ "repr": "Promise",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": [
+ {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ ],
+ "typeName": "Promise"
+ }
+ }
+ }
+ ],
+ "properties": [],
+ "callSignatures": [],
+ "indexSignatures": [],
+ "typeParams": []
+ }
+ }]);
+
+ json_test!(export_enum,
+ r#"
+/**
+ * Some enum for good measure
+ */
+export enum Hello {
+ World = "world",
+ Fizz = "fizz",
+ Buzz = "buzz",
+}
+ "#;
+ [{
+ "kind": "enum",
+ "name": "Hello",
+ "location": {
+ "filename": "test.ts",
+ "line": 5,
+ "col": 0
+ },
+ "jsDoc": "Some enum for good measure",
+ "enumDef": {
+ "members": [
+ {
+ "name": "World"
+ },
+ {
+ "name": "Fizz"
+ },
+ {
+ "name": "Buzz"
+ }
+ ]
+ }
+ }]);
+
+ json_test!(export_fn,
+ r#"/**
+* @module foo
+*/
+
+/**
+* Hello there, this is a multiline JSdoc.
+*
+* It has many lines
+*
+* Or not that many?
+*/
+export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void {
+ /**
+ * @todo document all the things.
+ */
+ console.log("Hello world");
+}
+ "#;
+ [{
+ "functionDef": {
+ "isAsync": false,
+ "isGenerator": false,
+ "typeParams": [],
+ "params": [
+ {
+ "name": "a",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "keyword": "string",
+ "kind": "keyword",
+ "repr": "string",
+ },
+ },
+ {
+ "name": "b",
+ "kind": "identifier",
+ "optional": true,
+ "tsType": {
+ "keyword": "number",
+ "kind": "keyword",
+ "repr": "number",
+ },
+ },
+ {
+ "name": "cb",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "",
+ "kind": "fnOrConstructor",
+ "fnOrConstructor": {
+ "constructor": false,
+ "tsType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void"
+ },
+ "typeParams": [],
+ "params": [{
+ "arg": {
+ "name": "cbArgs",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": {
+ "repr": "",
+ "kind": "array",
+ "array": {
+ "repr": "unknown",
+ "kind": "keyword",
+ "keyword": "unknown"
+ }
+ },
+ }]
+ }
+ },
+ },
+ {
+ "arg": {
+ "name": "args",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": {
+ "array": {
+ "keyword": "unknown",
+ "kind": "keyword",
+ "repr": "unknown"
+ },
+ "kind": "array",
+ "repr": ""
+ }
+ }
+ ],
+ "returnType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void",
+ },
+ },
+ "jsDoc": "Hello there, this is a multiline JSdoc.\n\nIt has many lines\n\nOr not that many?",
+ "kind": "function",
+ "location": {
+ "col": 0,
+ "filename": "test.ts",
+ "line": 12,
+ },
+ "name": "foo",
+ }]);
+
+ json_test!(export_fn2,
+ 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");
+}
+ "#;
+ [{
+ "functionDef": {
+ "isAsync": false,
+ "isGenerator": false,
+ "typeParams": [],
+ "params": [
+ {
+ "elements": [
+ {
+ "name": "e",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ null,
+ {
+ "name": "f",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ {
+ "arg": {
+ "name": "g",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest",
+ "tsType": null
+ }
+ ],
+ "kind": "array",
+ "optional": false,
+ "tsType": {
+ "repr": "",
+ "kind": "array",
+ "array": {
+ "repr": "number",
+ "kind": "keyword",
+ "keyword": "number"
+ }
+ }
+ },
+ {
+ "kind": "object",
+ "optional": false,
+ "props": [
+ {
+ "kind": "assign",
+ "key": "c",
+ "value": null
+ },
+ {
+ "kind": "keyValue",
+ "key": "d",
+ "value": {
+ "name": "asdf",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ }
+ },
+ {
+ "kind": "assign",
+ "key": "i",
+ "value": "<UNIMPLEMENTED>"
+ },
+ {
+ "arg": {
+ "name": "rest",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": null
+ },
+ "kind": "rest"
+ }
+ ],
+ "tsType": null
+ },
+ {
+ "kind": "assign",
+ "left": {
+ "name": "ops",
+ "kind": "identifier",
+ "optional": false,
+ "tsType": {
+ "repr": "AssignOpts",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeName": "AssignOpts",
+ "typeParams": null,
+ }
+ }
+ },
+ "right": "<UNIMPLEMENTED>",
+ "tsType": null
+ }
+ ],
+ "returnType": {
+ "keyword": "void",
+ "kind": "keyword",
+ "repr": "void",
+ },
+ },
+ "jsDoc": null,
+ "kind": "function",
+ "location": {
+ "col": 0,
+ "filename": "test.ts",
+ "line": 7,
+ },
+ "name": "foo",
+ }]);
+
+ json_test!(export_interface,
+ r#"
interface Foo {
foo(): void;
}
@@ -651,12 +1207,7 @@ export interface Reader extends Foo, Bar {
read?(buf: Uint8Array, something: unknown): Promise<number>
}
"#;
- 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": "Reader",
"location": {
@@ -666,7 +1217,24 @@ export interface Reader extends Foo, Bar {
},
"jsDoc": "Interface js doc",
"interfaceDef": {
- "extends": ["Foo", "Bar"],
+ "extends": [
+ {
+ "repr": "Foo",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Foo"
+ }
+ },
+ {
+ "repr": "Bar",
+ "kind": "typeRef",
+ "typeRef": {
+ "typeParams": null,
+ "typeName": "Bar"
+ }
+ }
+ ],
"methods": [
{
"name": "read",
@@ -721,31 +1289,18 @@ export interface Reader extends Foo, Bar {
],
"properties": [],
"callSignatures": [],
+ "indexSignatures": [],
"typeParams": [],
}
- });
- 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 Reader extends Foo, Bar")
- );
-}
+ }]);
-#[tokio::test]
-async fn export_interface2() {
- let source_code = r#"
+ json_test!(export_interface2,
+ 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": {
@@ -780,32 +1335,19 @@ export interface TypedIface<T> {
],
"properties": [],
"callSignatures": [],
+ "indexSignatures": [],
"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#"
+ json_test!(export_type_alias,
+ r#"
/** Array holding numbers */
export type NumberArray = Array<number>;
"#;
- 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": "typeAlias",
"name": "NumberArray",
"location": {
@@ -831,76 +1373,10 @@ export type NumberArray = Array<number>;
}
}
}
- });
- 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("Array holding numbers")
- );
-}
-
-#[tokio::test]
-async fn export_enum() {
- let source_code = r#"
-/**
- * Some enum for good measure
- */
-export enum Hello {
- World = "world",
- Fizz = "fizz",
- Buzz = "buzz",
-}
- "#;
- 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": "enum",
- "name": "Hello",
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 0
- },
- "jsDoc": "Some enum for good measure",
- "enumDef": {
- "members": [
- {
- "name": "World"
- },
- {
- "name": "Fizz"
- },
- {
- "name": "Buzz"
- }
- ]
- }
- });
- let actual = serde_json::to_value(entry).unwrap();
- assert_eq!(actual, expected_json);
+ }]);
- assert!(colors::strip_ansi_codes(
- super::printer::format_details(entry.clone()).as_str()
- )
- .contains("World"));
- assert!(colors::strip_ansi_codes(
- super::printer::format(entries.clone()).as_str()
- )
- .contains("Some enum for good measure"));
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("enum Hello")
- );
-}
-
-#[tokio::test]
-async fn export_namespace() {
- let source_code = r#"
+ json_test!(export_namespace,
+ r#"
/** Namespace JSdoc */
export namespace RootNs {
export const a = "a";
@@ -915,12 +1391,7 @@ export namespace RootNs {
}
}
"#;
- 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": "namespace",
"name": "RootNs",
"location": {
@@ -984,453 +1455,23 @@ export namespace RootNs {
}
]
}
- });
- 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("namespace RootNs")
- );
-}
-
-#[tokio::test]
-async fn declare_namespace() {
- let source_code = r#"
-/** Namespace JSdoc */
-declare namespace RootNs {
- declare const a = "a";
-
- /** Nested namespace JSDoc */
- declare namespace NestedNs {
- declare enum Foo {
- a = 1,
- b = 2,
- c = 3,
- }
- }
-}
- "#;
- 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": "namespace",
- "name": "RootNs",
- "location": {
- "filename": "test.ts",
- "line": 3,
- "col": 0
- },
- "jsDoc": "Namespace JSdoc",
- "namespaceDef": {
- "elements": [
- {
- "kind": "variable",
- "name": "a",
- "location": {
- "filename": "test.ts",
- "line": 4,
- "col": 12
- },
- "jsDoc": null,
- "variableDef": {
- "tsType": null,
- "kind": "const"
- }
- },
- {
- "kind": "namespace",
- "name": "NestedNs",
- "location": {
- "filename": "test.ts",
- "line": 7,
- "col": 4
- },
- "jsDoc": "Nested namespace JSDoc",
- "namespaceDef": {
- "elements": [
- {
- "kind": "enum",
- "name": "Foo",
- "location": {
- "filename": "test.ts",
- "line": 8,
- "col": 6
- },
- "jsDoc": null,
- "enumDef": {
- "members": [
- {
- "name": "a"
- },
- {
- "name": "b"
- },
- {
- "name": "c"
- }
- ]
- }
- }
- ]
- }
- }
- ]
- }
- });
- 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("namespace RootNs")
- );
-}
-
-#[tokio::test]
-async fn export_default_fn() {
- let source_code = r#"
-export default function foo(a: number) {
- return a;
-}
- "#;
- 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": "function",
- "name": "default",
- "location": {
- "filename": "test.ts",
- "line": 2,
- "col": 15
- },
- "jsDoc": null,
- "functionDef": {
- "params": [
- {
- "name": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- }
- ],
- "typeParams": [],
- "returnType": null,
- "isAsync": false,
- "isGenerator": false
- }
- });
- 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("function default(a: number)")
- );
-}
-
-#[tokio::test]
-async fn export_default_class() {
- let source_code = r#"
-/** Class doc */
-export default class Foobar {
- /** Constructor js doc */
- constructor(name: string, private private2: number, protected protected2: number) {}
-}
-"#;
- 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 expected_json = json!({
- "kind": "class",
- "name": "default",
- "location": {
- "filename": "test.ts",
- "line": 3,
- "col": 0
- },
- "jsDoc": "Class doc",
- "classDef": {
- "isAbstract": false,
- "extends": null,
- "implements": [],
- "typeParams": [],
- "constructors": [
- {
- "jsDoc": "Constructor js doc",
- "accessibility": null,
- "name": "constructor",
- "params": [
- {
- "name": "name",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "string",
- "kind": "keyword",
- "keyword": "string"
- }
- },
- {
- "name": "private2",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- },
- {
- "name": "protected2",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- }
- ],
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 4
- }
- }
- ],
- "properties": [],
- "methods": []
- }
- });
- let entry = &entries[0];
- 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("class default")
- );
-}
-
-#[tokio::test]
-async fn export_default_interface() {
- let source_code = r#"
-/**
- * Interface js doc
- */
-export default interface Reader {
- /** Read n bytes */
- read?(buf: Uint8Array, something: unknown): Promise<number>
-}
- "#;
- 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": "default",
- "location": {
- "filename": "test.ts",
- "line": 5,
- "col": 0
- },
- "jsDoc": "Interface js doc",
- "interfaceDef": {
- "extends": [],
- "methods": [
- {
- "name": "read",
- "location": {
- "filename": "test.ts",
- "line": 7,
- "col": 4
- },
- "optional": true,
- "jsDoc": "Read n bytes",
- "params": [
- {
- "name": "buf",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "Uint8Array",
- "kind": "typeRef",
- "typeRef": {
- "typeParams": null,
- "typeName": "Uint8Array"
- }
- }
- },
- {
- "name": "something",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "repr": "unknown",
- "kind": "keyword",
- "keyword": "unknown"
- }
- }
- ],
- "typeParams": [],
- "returnType": {
- "repr": "Promise",
- "kind": "typeRef",
- "typeRef": {
- "typeParams": [
- {
- "repr": "number",
- "kind": "keyword",
- "keyword": "number"
- }
- ],
- "typeName": "Promise"
- }
- }
- }
- ],
- "properties": [],
- "callSignatures": [],
- "typeParams": [],
- }
- });
- 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 default")
- );
-}
+ }]);
-#[tokio::test]
-async fn optional_return_type() {
- let source_code = r#"
+ json_test!(optional_return_type,
+ r#"
export function foo(a: number) {
return a;
}
"#;
- 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": "function",
- "name": "foo",
- "location": {
- "filename": "test.ts",
- "line": 2,
- "col": 2
- },
- "jsDoc": null,
- "functionDef": {
- "params": [
- {
- "name": "a",
- "kind": "identifier",
- "optional": false,
- "tsType": {
- "keyword": "number",
- "kind": "keyword",
- "repr": "number",
- },
- }
- ],
- "typeParams": [],
- "returnType": null,
- "isAsync": false,
- "isGenerator": false
- }
- });
- 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("function foo(a: number)")
- );
-}
-
-#[tokio::test]
-async fn reexports() {
- let nested_reexport_source_code = r#"
-/**
- * JSDoc for bar
- */
-export const bar = "bar";
-
-export default 42;
-"#;
- let reexport_source_code = r#"
-import { bar } from "./nested_reexport.ts";
-
-/**
- * JSDoc for const
- */
-export const foo = "foo";
-"#;
- let test_source_code = r#"
-export { default, foo as fooConst } from "./reexport.ts";
-
-/** JSDoc for function */
-export function fooFn(a: number) {
- return a;
-}
-"#;
- let loader = TestLoader::new(vec![
- ("file:///test.ts".to_string(), test_source_code.to_string()),
- (
- "file:///reexport.ts".to_string(),
- reexport_source_code.to_string(),
- ),
- (
- "file:///nested_reexport.ts".to_string(),
- nested_reexport_source_code.to_string(),
- ),
- ]);
- let entries = DocParser::new(loader)
- .parse_with_reexports("file:///test.ts")
- .await
- .unwrap();
- assert_eq!(entries.len(), 2);
-
- let expected_json = json!([
- {
- "kind": "variable",
- "name": "fooConst",
- "location": {
- "filename": "file:///reexport.ts",
- "line": 7,
- "col": 0
- },
- "jsDoc": "JSDoc for const",
- "variableDef": {
- "tsType": null,
- "kind": "const"
- }
- },
- {
+ [{
"kind": "function",
- "name": "fooFn",
+ "name": "foo",
"location": {
- "filename": "file:///test.ts",
- "line": 5,
- "col": 0
+ "filename": "test.ts",
+ "line": 2,
+ "col": 2
},
- "jsDoc": "JSDoc for function",
+ "jsDoc": null,
"functionDef": {
"params": [
{
@@ -1449,30 +1490,17 @@ export function fooFn(a: number) {
"isAsync": false,
"isGenerator": false
}
- }
- ]);
- let actual = serde_json::to_value(entries.clone()).unwrap();
- assert_eq!(actual, expected_json);
-
- assert!(
- colors::strip_ansi_codes(super::printer::format(entries).as_str())
- .contains("function fooFn(a: number)")
+ }]
);
-}
-#[tokio::test]
-async fn ts_lit_types() {
- let source_code = r#"
+ json_test!(ts_lit_types,
+ r#"
export type boolLit = false;
export type strLit = "text";
export type tplLit = `text`;
export type numLit = 5;
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
- let actual = serde_json::to_value(entries).unwrap();
- let expected_json = json!([
+ "#;
+ [
{
"kind": "typeAlias",
"name": "boolLit",
@@ -1555,70 +1583,491 @@ export type numLit = 5;
}
}
]);
- assert_eq!(actual, expected_json);
}
-#[tokio::test]
-async fn filter_nodes_by_name() {
- use super::find_nodes_by_name_recursively;
- let source_code = r#"
-export namespace Deno {
- export class Buffer {}
- export function test(options: object): void;
- export function test(name: string, fn: Function): void;
- export function test(name: string | object, fn?: Function): void {}
+mod printer {
+ use super::*;
+
+ contains_test!(abstract_class,
+ "export abstract class Class {}",
+ details;
+ "abstract class Class"
+ );
+
+ contains_test!(abstract_class_abstract_method,
+ r#"
+export abstract class Class {
+ abstract method() {}
}
+ "#,
+ details;
+ "abstract method()"
+ );
-export namespace Deno {
- export namespace Inner {
- export function a(): void {}
- export const b = 100;
- }
+ contains_test!(class_async_method,
+ r#"
+export class Class {
+ async amethod(v) {}
}
-"#;
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
+ "#,
+ details;
+ "async amethod(v)"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno".to_string());
- assert_eq!(found.len(), 2);
- assert_eq!(found[0].name, "Deno".to_string());
- assert_eq!(found[1].name, "Deno".to_string());
+ contains_test!(class_constructor,
+ r#"
+export class Class {
+ constructor(a, b) {}
+}
+ "#,
+ details;
+ "constructor(a, b)"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.test".to_string());
- assert_eq!(found.len(), 3);
- assert_eq!(found[0].name, "test".to_string());
- assert_eq!(found[1].name, "test".to_string());
- assert_eq!(found[2].name, "test".to_string());
+ const CLASS_SOURCE: &str = r#"
+export class C {
+ /** a doc */
+ a() {}
+ f: number;
+}
+ "#;
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.Inner.a".to_string());
- assert_eq!(found.len(), 1);
- assert_eq!(found[0].name, "a".to_string());
+ contains_test!(class_details,
+ CLASS_SOURCE,
+ details;
+ "class C",
+ "a()",
+ "f: number"
+ );
- let found =
- find_nodes_by_name_recursively(entries.clone(), "Deno.test.a".to_string());
- assert_eq!(found.len(), 0);
+ contains_test!(class_details_all_with_private,
+ r#"
+export class Class {
+ private pri() {}
+ protected pro() {}
+ public pub() {}
+}
+ "#,
+ details,
+ private;
+ "private pri()",
+ "protected pro()",
+ "pub()"
+ );
- let found = find_nodes_by_name_recursively(entries, "a.b.c".to_string());
- assert_eq!(found.len(), 0);
+ contains_test!(class_details_only_non_private_without_private,
+ r#"
+export class Class {
+ private pri() {}
+ protected pro() {}
+ public pub() {}
}
+ "#,
+ details;
+ "protected pro()",
+ "pub()"
+ );
-#[tokio::test]
-async fn generic_instantiated_with_tuple_type() {
- let source_code = r#"
+ contains_test!(class_declaration,
+ "export class Class {}";
+ "class Class"
+ );
+
+ contains_test!(class_extends,
+ "export class Class extends Object {}";
+ "class Class extends Object"
+ );
+
+ contains_test!(class_extends_implements,
+ "export class Class extends Object implements Iterator, Iterable {}";
+ "class Class extends Object implements Iterator, Iterable"
+ );
+
+ contains_test!(class_generic_extends_implements,
+ "export class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B> {}";
+ "class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B>"
+ );
+
+ contains_test!(class_getter_and_setter,
+ r#"
+export class Class {
+ get a(): void {}
+ set b(_v: void) {}
+}
+ "#,
+ details;
+ "get a(): void",
+ "set b(_v: void)"
+ );
+
+ contains_test!(class_index_signature,
+ r#"
+export class C {
+ [key: string]: number;
+}
+ "#,
+ details;
+ "[key: string]: number"
+ );
+
+ contains_test!(class_implements,
+ "export class Class implements Iterator {}";
+ "class Class implements Iterator"
+ );
+
+ contains_test!(class_implements2,
+ "export class Class implements Iterator, Iterable {}";
+ "class Class implements Iterator, Iterable"
+ );
+
+ contains_test!(class_method,
+ r#"
+export class Class {
+ method(v) {}
+}
+ "#,
+ details;
+ "method(v)"
+ );
+
+ contains_test!(class_property,
+ r#"
+export class Class {
+ someproperty: bool;
+ optproperty: bigint;
+}
+ "#,
+ details;
+ "someproperty: bool",
+ "optproperty: bigint"
+ );
+
+ contains_test!(class_readonly_index_signature,
+ r#"
+export class C {
+ readonly [key: string]: number;
+}
+ "#,
+ details;
+ "readonly [key: string]: number"
+ );
+
+ contains_test!(class_static_property,
+ r#"
+export class Class {
+ static property = "";
+}
+ "#,
+ details;
+ "static property"
+ );
+
+ contains_test!(class_summary,
+ CLASS_SOURCE;
+ "class C";
+ "a()",
+ "f: number"
+ );
+
+ contains_test!(class_readonly_property,
+ r#"
+export class Class {
+ readonly property = "";
+}
+ "#,
+ details;
+ "readonly property"
+ );
+
+ contains_test!(class_private_property,
+ r#"
+export class Class {
+ private property = "";
+}
+ "#,
+ details,
+ private;
+ "private property"
+ );
+
+ contains_test!(const_declaration,
+ "export const Const = 0;";
+ "const Const"
+ );
+
+ contains_test!(enum_declaration,
+ "export enum Enum {}";
+ "enum Enum"
+ );
+
+ const EXPORT_SOURCE: &str = r#"
+export function a() {}
+function b() {}
+export class C {}
+class D {}
+export interface E {}
+interface F {}
+export namespace G {}
+namespace H {}
+ "#;
+
+ contains_test!(exports_all_with_private,
+ EXPORT_SOURCE,
+ private;
+ "function a()",
+ "class C",
+ "interface E",
+ "namespace G",
+ "function b()",
+ "class D",
+ "interface F",
+ "namespace H"
+ );
+
+ contains_test!(exports_only_exports_without_private,
+ EXPORT_SOURCE;
+ "function a()",
+ "class C",
+ "interface E",
+ "namespace G";
+ "function b()",
+ "class D",
+ "interface F",
+ "namespace H"
+ );
+
+ contains_test!(function_async,
+ "export async function a() {}";
+ "async function a()"
+ );
+
+ contains_test!(function_array_deconstruction,
+ "export function f([a, b, ...c]) {}";
+ "function f([a, b, ...c])"
+ );
+
+ contains_test!(function_async_generator,
+ "export async function* ag() {}";
+ "async function* ag()"
+ );
+
+ contains_test!(function_declaration,
+ "export function fun() {}";
+ "function fun()"
+ );
+
+ contains_test!(function_generator,
+ "export function* g() {}";
+ "function* g()"
+ );
+
+ contains_test!(function_generic,
+ "export function add<T>(a: T, b: T) { return a + b; }";
+ "function add<T>(a: T, b: T)"
+ );
+
+ contains_test!(function_object_deconstruction,
+ "export function f({ a, b, ...c }) {}";
+ "function f({a, b, ...c})"
+ );
+
+ /* TODO(SyrupThinker) NYI
+ contains_test!(function_type_predicate,
+ r#"
+ export function isFish(pet: Fish | Bird): pet is Fish {
+ return (pet as Fish).swim !== undefined;
+ }
+ "#;
+ "pet is Fish"
+ );
+ */
+
+ contains_test!(generic_instantiated_with_tuple_type,
+ r#"
interface Generic<T> {}
export function f(): Generic<[string, number]> { return {}; }
- "#;
+ "#;
+ "Generic<[string, number]>"
+ );
- let loader =
- TestLoader::new(vec![("test.ts".to_string(), source_code.to_string())]);
- let entries = DocParser::new(loader).parse("test.ts").await.unwrap();
+ contains_test!(type_literal_declaration,
+ "export type T = {}";
+ "{ }"
+ );
- assert!(colors::strip_ansi_codes(
- crate::doc::printer::format(entries).as_str()
- )
- .contains("Generic<[string, number]>"))
+ contains_test!(type_literal_index_signature,
+ "export type T = { [key: string]: number; }";
+ "[key: string]: number"
+ );
+
+ contains_test!(type_literal_readonly_index_signature,
+ "export type T = { readonly [key: string]: number; }";
+ "readonly [key: string]: number"
+ );
+
+ contains_test!(interface_declaration,
+ "export interface Interface {}";
+ "interface Interface"
+ );
+
+ contains_test!(interface_extends,
+ "export interface Interface extends Iterator {}";
+ "interface Interface extends Iterator"
+ );
+
+ contains_test!(interface_extends2,
+ "export interface Interface extends Iterator, Iterable {}";
+ "interface Interface extends Iterator, Iterable"
+ );
+
+ contains_test!(interface_generic,
+ "export interface Interface<T> {}";
+ "interface Interface<T>"
+ );
+
+ contains_test!(interface_generic_extends,
+ "export interface Interface<V> extends Iterable<V> {}";
+ "interface Interface<V> extends Iterable<V>"
+ );
+
+ contains_test!(interface_index_signature,
+ r#"
+export interface Interface {
+ [index: number]: Interface;
+}
+ "#,
+ details;
+ "[index: number]: Interface"
+ );
+
+ contains_test!(interface_method,
+ r#"
+export interface I {
+ m(a, b);
+ mo?(c);
+}
+ "#,
+ details;
+ "m(a, b)",
+ "mo?(c)"
+ );
+
+ contains_test!(interface_property,
+ r#"
+export interface I {
+ p: string;
+ po?: number;
+}
+ "#,
+ details;
+ "p: string",
+ "po?: number"
+ );
+
+ contains_test!(interface_readonly_index_signature,
+ r#"
+export interface Interface {
+ readonly [index: number]: Interface;
+}
+ "#,
+ details;
+ "readonly [index: number]: Interface"
+ );
+
+ const JSDOC_SOURCE: &str = r#"
+/**
+ * A is a class
+ *
+ * Nothing more
+ */
+export class A {}
+/**
+ * B is an interface
+ *
+ * Should be
+ */
+export interface B {}
+/**
+ * C is a function
+ *
+ * Summarised
+ */
+export function C() {}
+ "#;
+
+ contains_test!(jsdoc_details,
+ JSDOC_SOURCE,
+ details;
+ "A is a class",
+ "B is an interface",
+ "C is a function",
+ "Nothing more",
+ "Should be",
+ "Summarised"
+ );
+
+ contains_test!(jsdoc_summary,
+ JSDOC_SOURCE;
+ "A is a class",
+ "B is an interface",
+ "C is a function";
+ "Nothing more",
+ "Should be",
+ "Summarised"
+ );
+
+ contains_test!(namespace_declaration,
+ "export namespace Namespace {}";
+ "namespace Namespace"
+ );
+
+ const NAMESPACE_SOURCE: &str = r#"
+export namespace Namespace {
+ /**
+ * Doc comment 1
+ *
+ * Details 1
+ */
+ export function a() {}
+ /**
+ * Doc comment 2
+ *
+ * Details 2
+ */
+ export class B {}
+}
+ "#;
+
+ contains_test!(namespace_details,
+ NAMESPACE_SOURCE,
+ details;
+ "namespace Namespace",
+ "function a()",
+ "class B",
+ "Doc comment 1",
+ "Doc comment 2";
+ "Details 1",
+ "Details 2"
+ );
+
+ contains_test!(namespace_summary,
+ NAMESPACE_SOURCE;
+ "namespace Namespace",
+ "function a()",
+ "class B",
+ "Doc comment 1",
+ "Doc comment 2";
+ "Details 1",
+ "Details 2"
+ );
+
+ contains_test!(type_alias,
+ "export type A = number";
+ "type A = number"
+ );
+
+ contains_test!(type_generic_alias,
+ "export type A<T> = T";
+ "type A<T> = T"
+ );
}
diff --git a/cli/doc/ts_type.rs b/cli/doc/ts_type.rs
index 7bb494541..36502ee5e 100644
--- a/cli/doc/ts_type.rs
+++ b/cli/doc/ts_type.rs
@@ -1,30 +1,23 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+use super::display::{display_readonly, SliceDisplayer};
use super::interface::expr_to_name;
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::colors;
+use crate::doc;
use crate::swc_ecma_ast;
-use crate::swc_ecma_ast::TsArrayType;
-use crate::swc_ecma_ast::TsConditionalType;
-use crate::swc_ecma_ast::TsFnOrConstructorType;
-use crate::swc_ecma_ast::TsIndexedAccessType;
-use crate::swc_ecma_ast::TsKeywordType;
-use crate::swc_ecma_ast::TsLit;
-use crate::swc_ecma_ast::TsLitType;
-use crate::swc_ecma_ast::TsOptionalType;
-use crate::swc_ecma_ast::TsParenthesizedType;
-use crate::swc_ecma_ast::TsRestType;
-use crate::swc_ecma_ast::TsThisType;
-use crate::swc_ecma_ast::TsTupleType;
-use crate::swc_ecma_ast::TsType;
-use crate::swc_ecma_ast::TsTypeAnn;
-use crate::swc_ecma_ast::TsTypeLit;
-use crate::swc_ecma_ast::TsTypeOperator;
-use crate::swc_ecma_ast::TsTypeQuery;
-use crate::swc_ecma_ast::TsTypeRef;
-use crate::swc_ecma_ast::TsUnionOrIntersectionType;
+use crate::swc_ecma_ast::{
+ TsArrayType, TsConditionalType, TsExprWithTypeArgs, TsFnOrConstructorType,
+ TsIndexedAccessType, TsKeywordType, TsLit, TsLitType, TsOptionalType,
+ TsParenthesizedType, TsRestType, TsThisType, TsTupleType, TsType, TsTypeAnn,
+ TsTypeLit, TsTypeOperator, TsTypeParamInstantiation, TsTypeQuery, TsTypeRef,
+ TsUnionOrIntersectionType,
+};
use serde::Serialize;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+
// pub enum TsType {
// * TsKeywordType(TsKeywordType),
// * TsThisType(TsThisType),
@@ -316,7 +309,37 @@ impl Into<TsTypeDef> for &TsTypeRef {
};
TsTypeDef {
- repr: type_name.to_string(),
+ repr: type_name.clone(),
+ type_ref: Some(TsTypeRefDef {
+ type_name,
+ type_params,
+ }),
+ kind: Some(TsTypeDefKind::TypeRef),
+ ..Default::default()
+ }
+ }
+}
+
+impl Into<TsTypeDef> for &TsExprWithTypeArgs {
+ fn into(self) -> TsTypeDef {
+ let type_name = ts_entity_name_to_name(&self.expr);
+
+ let type_params = if let Some(type_params_inst) = &self.type_args {
+ let mut ts_type_defs = vec![];
+
+ for type_box in &type_params_inst.params {
+ let ts_type: &TsType = &(*type_box);
+ let def: TsTypeDef = ts_type.into();
+ ts_type_defs.push(def);
+ }
+
+ Some(ts_type_defs)
+ } else {
+ None
+ };
+
+ TsTypeDef {
+ repr: type_name.clone(),
type_ref: Some(TsTypeRefDef {
type_name,
type_params,
@@ -348,6 +371,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut methods = vec![];
let mut properties = vec![];
let mut call_signatures = vec![];
+ let mut index_signatures = vec![];
for type_element in &self.members {
use crate::swc_ecma_ast::TsTypeElement::*;
@@ -357,7 +381,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut params = vec![];
for param in &ts_method_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@@ -384,7 +408,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
let mut params = vec![];
for param in &ts_prop_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@@ -409,7 +433,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
TsCallSignatureDecl(ts_call_sig) => {
let mut params = vec![];
for param in &ts_call_sig.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@@ -429,9 +453,27 @@ impl Into<TsTypeDef> for &TsTypeLit {
};
call_signatures.push(call_sig_def);
}
+ TsIndexSignature(ts_index_sig) => {
+ let mut params = vec![];
+ for param in &ts_index_sig.params {
+ let param_def = ts_fn_param_to_param_def(param, None);
+ params.push(param_def);
+ }
+
+ let ts_type = ts_index_sig
+ .type_ann
+ .as_ref()
+ .map(|rt| (&*rt.type_ann).into());
+
+ let index_sig_def = LiteralIndexSignatureDef {
+ readonly: ts_index_sig.readonly,
+ params,
+ ts_type,
+ };
+ index_signatures.push(index_sig_def);
+ }
// TODO:
TsConstructSignatureDecl(_) => {}
- TsIndexSignature(_) => {}
}
}
@@ -439,6 +481,7 @@ impl Into<TsTypeDef> for &TsTypeLit {
methods,
properties,
call_signatures,
+ index_signatures,
};
TsTypeDef {
@@ -475,7 +518,7 @@ impl Into<TsTypeDef> for &TsFnOrConstructorType {
let mut params = vec![];
for param in &ts_fn_type.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@@ -494,7 +537,7 @@ impl Into<TsTypeDef> for &TsFnOrConstructorType {
let mut params = vec![];
for param in &ctor_type.params {
- let param_def = ts_fn_param_to_param_def(param);
+ let param_def = ts_fn_param_to_param_def(param, None);
params.push(param_def);
}
@@ -619,6 +662,21 @@ pub struct LiteralMethodDef {
pub type_params: Vec<TsTypeParamDef>,
}
+impl Display for LiteralMethodDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}({})",
+ self.name,
+ SliceDisplayer::new(&self.params, ", ", false)
+ )?;
+ if let Some(return_type) = &self.return_type {
+ write!(f, ": {}", return_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralPropertyDef {
@@ -630,6 +688,15 @@ pub struct LiteralPropertyDef {
pub type_params: Vec<TsTypeParamDef>,
}
+impl Display for LiteralPropertyDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(f, "{}", self.name)?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct LiteralCallSignatureDef {
@@ -638,12 +705,46 @@ pub struct LiteralCallSignatureDef {
pub type_params: Vec<TsTypeParamDef>,
}
+impl Display for LiteralCallSignatureDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(f, "({})", SliceDisplayer::new(&self.params, ", ", false))?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Serialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct LiteralIndexSignatureDef {
+ pub readonly: bool,
+ pub params: Vec<ParamDef>,
+ pub ts_type: Option<TsTypeDef>,
+}
+
+impl Display for LiteralIndexSignatureDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(
+ f,
+ "{}[{}]",
+ display_readonly(self.readonly),
+ SliceDisplayer::new(&self.params, ", ", false)
+ )?;
+ if let Some(ts_type) = &self.ts_type {
+ write!(f, ": {}", ts_type)?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TsTypeLiteralDef {
pub methods: Vec<LiteralMethodDef>,
pub properties: Vec<LiteralPropertyDef>,
pub call_signatures: Vec<LiteralCallSignatureDef>,
+ pub index_signatures: Vec<LiteralIndexSignatureDef>,
}
#[derive(Debug, PartialEq, Serialize, Clone)]
@@ -753,3 +854,133 @@ pub fn ts_type_ann_to_def(type_ann: &TsTypeAnn) -> TsTypeDef {
},
}
}
+
+impl Display for TsTypeDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ if self.kind.is_none() {
+ return write!(f, "{}", colors::red("<UNIMPLEMENTED>"));
+ }
+
+ let kind = self.kind.as_ref().unwrap();
+ match kind {
+ TsTypeDefKind::Array => write!(f, "{}[]", &*self.array.as_ref().unwrap()),
+ TsTypeDefKind::Conditional => {
+ let conditional = self.conditional_type.as_ref().unwrap();
+ write!(
+ f,
+ "{} {} {} ? {} : {}",
+ &*conditional.check_type,
+ colors::magenta("extends"),
+ &*conditional.extends_type,
+ &*conditional.true_type,
+ &*conditional.false_type
+ )
+ }
+ TsTypeDefKind::FnOrConstructor => {
+ let fn_or_constructor = self.fn_or_constructor.as_ref().unwrap();
+ write!(
+ f,
+ "{}({}) => {}",
+ colors::magenta(if fn_or_constructor.constructor {
+ "new "
+ } else {
+ ""
+ }),
+ SliceDisplayer::new(&fn_or_constructor.params, ", ", false),
+ &fn_or_constructor.ts_type,
+ )
+ }
+ TsTypeDefKind::IndexedAccess => {
+ let indexed_access = self.indexed_access.as_ref().unwrap();
+ write!(
+ f,
+ "{}[{}]",
+ &*indexed_access.obj_type, &*indexed_access.index_type
+ )
+ }
+ TsTypeDefKind::Intersection => {
+ let intersection = self.intersection.as_ref().unwrap();
+ write!(f, "{}", SliceDisplayer::new(&intersection, " & ", false))
+ }
+ TsTypeDefKind::Keyword => {
+ write!(f, "{}", colors::cyan(self.keyword.as_ref().unwrap()))
+ }
+ TsTypeDefKind::Literal => {
+ let literal = self.literal.as_ref().unwrap();
+ match literal.kind {
+ doc::ts_type::LiteralDefKind::Boolean => write!(
+ f,
+ "{}",
+ colors::yellow(&literal.boolean.unwrap().to_string())
+ ),
+ doc::ts_type::LiteralDefKind::String => write!(
+ f,
+ "{}",
+ colors::green(&format!("\"{}\"", literal.string.as_ref().unwrap()))
+ ),
+ doc::ts_type::LiteralDefKind::Number => write!(
+ f,
+ "{}",
+ colors::yellow(&literal.number.unwrap().to_string())
+ ),
+ }
+ }
+ TsTypeDefKind::Optional => {
+ write!(f, "{}?", &*self.optional.as_ref().unwrap())
+ }
+ TsTypeDefKind::Parenthesized => {
+ write!(f, "({})", &*self.parenthesized.as_ref().unwrap())
+ }
+ TsTypeDefKind::Rest => write!(f, "...{}", &*self.rest.as_ref().unwrap()),
+ TsTypeDefKind::This => write!(f, "this"),
+ TsTypeDefKind::Tuple => {
+ let tuple = self.tuple.as_ref().unwrap();
+ write!(f, "[{}]", SliceDisplayer::new(&tuple, ", ", false))
+ }
+ TsTypeDefKind::TypeLiteral => {
+ let type_literal = self.type_literal.as_ref().unwrap();
+ write!(
+ f,
+ "{{ {}{}{}{}}}",
+ SliceDisplayer::new(&type_literal.call_signatures, "; ", true),
+ SliceDisplayer::new(&type_literal.methods, "; ", true),
+ SliceDisplayer::new(&type_literal.properties, "; ", true),
+ SliceDisplayer::new(&type_literal.index_signatures, "; ", true),
+ )
+ }
+ TsTypeDefKind::TypeOperator => {
+ let operator = self.type_operator.as_ref().unwrap();
+ write!(f, "{} {}", operator.operator, &operator.ts_type)
+ }
+ TsTypeDefKind::TypeQuery => {
+ write!(f, "typeof {}", self.type_query.as_ref().unwrap())
+ }
+ TsTypeDefKind::TypeRef => {
+ let type_ref = self.type_ref.as_ref().unwrap();
+ write!(f, "{}", colors::intense_blue(&type_ref.type_name))?;
+ if let Some(type_params) = &type_ref.type_params {
+ write!(f, "<{}>", SliceDisplayer::new(type_params, ", ", false))?;
+ }
+ Ok(())
+ }
+ TsTypeDefKind::Union => {
+ let union = self.union.as_ref().unwrap();
+ write!(f, "{}", SliceDisplayer::new(union, " | ", false))
+ }
+ }
+ }
+}
+
+pub fn maybe_type_param_instantiation_to_type_defs(
+ maybe_type_param_instantiation: Option<&TsTypeParamInstantiation>,
+) -> Vec<TsTypeDef> {
+ if let Some(type_param_instantiation) = maybe_type_param_instantiation {
+ type_param_instantiation
+ .params
+ .iter()
+ .map(|type_param| type_param.as_ref().into())
+ .collect::<Vec<TsTypeDef>>()
+ } else {
+ vec![]
+ }
+}
diff --git a/cli/doc/ts_type_param.rs b/cli/doc/ts_type_param.rs
index 4edb7dee1..0483708b6 100644
--- a/cli/doc/ts_type_param.rs
+++ b/cli/doc/ts_type_param.rs
@@ -3,6 +3,7 @@ use super::ts_type::TsTypeDef;
use crate::swc_ecma_ast::TsTypeParam;
use crate::swc_ecma_ast::TsTypeParamDecl;
use serde::Serialize;
+use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
@@ -16,6 +17,19 @@ pub struct TsTypeParamDef {
pub default: Option<TsTypeDef>,
}
+impl Display for TsTypeParamDef {
+ fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
+ write!(f, "{}", self.name)?;
+ if let Some(constraint) = &self.constraint {
+ write!(f, " extends {}", constraint)?;
+ }
+ if let Some(default) = &self.default {
+ write!(f, " = {}", default)?;
+ }
+ Ok(())
+ }
+}
+
impl Into<TsTypeParamDef> for &TsTypeParam {
fn into(self) -> TsTypeParamDef {
let name = self.name.sym.to_string();
diff --git a/cli/flags.rs b/cli/flags.rs
index 313d8ff1a..80e856581 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -23,6 +23,7 @@ pub enum DenoSubcommand {
buf: Box<[u8]>,
},
Doc {
+ private: bool,
json: bool,
source_file: Option<String>,
filter: Option<String>,
@@ -598,12 +599,14 @@ fn doc_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
unstable_arg_parse(flags, matches);
let source_file = matches.value_of("source_file").map(String::from);
+ let private = matches.is_present("private");
let json = matches.is_present("json");
let filter = matches.value_of("filter").map(String::from);
flags.subcommand = DenoSubcommand::Doc {
source_file,
json,
filter,
+ private,
};
}
@@ -915,6 +918,9 @@ fn doc_subcommand<'a, 'b>() -> App<'a, 'b> {
Output documentation to standard output:
deno doc ./path/to/module.ts
+Output private documentation to standard output:
+ deno doc --private ./path/to/module.ts
+
Output documentation in JSON format:
deno doc --json ./path/to/module.ts
@@ -932,6 +938,12 @@ Show documentation for runtime built-ins:
.help("Output documentation in JSON format.")
.takes_value(false),
)
+ .arg(
+ Arg::with_name("private")
+ .long("private")
+ .help("Output private documentation")
+ .takes_value(false),
+ )
// TODO(nayeemrmn): Make `--builtin` a proper option. Blocked by
// https://github.com/clap-rs/clap/issues/1794. Currently `--builtin` is
// just a possible value of `source_file` so leading hyphens must be
@@ -2910,6 +2922,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
+ private: false,
json: true,
source_file: Some("path/to/module.ts".to_string()),
filter: None,
@@ -2928,6 +2941,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
+ private: false,
json: false,
source_file: Some("path/to/module.ts".to_string()),
filter: Some("SomeClass.someField".to_string()),
@@ -2941,6 +2955,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
+ private: false,
json: false,
source_file: None,
filter: None,
@@ -2955,6 +2970,7 @@ mod tests {
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Doc {
+ private: false,
json: false,
source_file: Some("--builtin".to_string()),
filter: Some("Deno.Listener".to_string()),
@@ -2962,6 +2978,25 @@ mod tests {
..Flags::default()
}
);
+
+ let r = flags_from_vec_safe(svec![
+ "deno",
+ "doc",
+ "--private",
+ "path/to/module.js"
+ ]);
+ assert_eq!(
+ r.unwrap(),
+ Flags {
+ subcommand: DenoSubcommand::Doc {
+ private: true,
+ json: false,
+ source_file: Some("path/to/module.js".to_string()),
+ filter: None,
+ },
+ ..Flags::default()
+ }
+ );
}
#[test]
diff --git a/cli/main.rs b/cli/main.rs
index 9669d0938..088d65ecd 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -521,6 +521,7 @@ async fn doc_command(
source_file: Option<String>,
json: bool,
maybe_filter: Option<String>,
+ private: bool,
) -> Result<(), ErrBox> {
let global_state = GlobalState::new(flags.clone())?;
let source_file = source_file.unwrap_or_else(|| "--builtin".to_string());
@@ -546,7 +547,7 @@ async fn doc_command(
}
let loader = Box::new(global_state.file_fetcher.clone());
- let doc_parser = doc::DocParser::new(loader);
+ let doc_parser = doc::DocParser::new(loader, private);
let parse_result = if source_file == "--builtin" {
doc_parser.parse_source("lib.deno.d.ts", get_types(flags.unstable).as_str())
@@ -576,13 +577,9 @@ async fn doc_command(
eprintln!("Node {} was not found!", filter);
std::process::exit(1);
}
- let mut details = String::new();
- for node in nodes {
- details.push_str(doc::printer::format_details(node).as_str());
- }
- details
+ format!("{}", doc::DocPrinter::new(&nodes, true, private))
} else {
- doc::printer::format(doc_nodes)
+ format!("{}", doc::DocPrinter::new(&doc_nodes, false, private))
};
write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(ErrBox::from)
@@ -720,7 +717,8 @@ pub fn main() {
source_file,
json,
filter,
- } => doc_command(flags, source_file, json, filter).boxed_local(),
+ private,
+ } => doc_command(flags, source_file, json, filter, private).boxed_local(),
DenoSubcommand::Eval {
print,
code,