summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/doc/class.rs34
-rw-r--r--cli/doc/module.rs1
-rw-r--r--cli/doc/parser.rs70
-rw-r--r--cli/doc/tests.rs232
4 files changed, 322 insertions, 15 deletions
diff --git a/cli/doc/class.rs b/cli/doc/class.rs
index 2370dd18f..8ebb49f65 100644
--- a/cli/doc/class.rs
+++ b/cli/doc/class.rs
@@ -85,15 +85,15 @@ fn prop_name_to_string(
}
}
-pub fn get_doc_for_class_decl(
+pub fn class_to_class_def(
doc_parser: &DocParser,
- class_decl: &swc_ecma_ast::ClassDecl,
-) -> (String, ClassDef) {
+ class: &swc_ecma_ast::Class,
+) -> ClassDef {
let mut constructors = vec![];
let mut methods = vec![];
let mut properties = vec![];
- let extends: Option<String> = match &class_decl.class.super_class {
+ let extends: Option<String> = match &class.super_class {
Some(boxed) => {
use crate::swc_ecma_ast::Expr;
let expr: &Expr = &**boxed;
@@ -105,14 +105,13 @@ pub fn get_doc_for_class_decl(
None => None,
};
- let implements: Vec<String> = class_decl
- .class
+ let implements: Vec<String> = class
.implements
.iter()
.map(|expr| ts_entity_name_to_name(&expr.expr))
.collect();
- for member in &class_decl.class.body {
+ for member in &class.body {
use crate::swc_ecma_ast::ClassMember::*;
match member {
@@ -207,19 +206,26 @@ pub fn get_doc_for_class_decl(
}
}
- let type_params = maybe_type_param_decl_to_type_param_defs(
- class_decl.class.type_params.as_ref(),
- );
- let class_name = class_decl.ident.sym.to_string();
- let class_def = ClassDef {
- is_abstract: class_decl.class.is_abstract,
+ let type_params =
+ maybe_type_param_decl_to_type_param_defs(class.type_params.as_ref());
+
+ ClassDef {
+ is_abstract: class.is_abstract,
extends,
implements,
constructors,
properties,
methods,
type_params,
- };
+ }
+}
+
+pub fn get_doc_for_class_decl(
+ doc_parser: &DocParser,
+ class_decl: &swc_ecma_ast::ClassDecl,
+) -> (String, ClassDef) {
+ let class_name = class_decl.ident.sym.to_string();
+ let class_def = class_to_class_def(doc_parser, &class_decl.class);
(class_name, class_def)
}
diff --git a/cli/doc/module.rs b/cli/doc/module.rs
index 2de9c7ca8..ab137904b 100644
--- a/cli/doc/module.rs
+++ b/cli/doc/module.rs
@@ -16,6 +16,7 @@ pub fn get_doc_node_for_export_decl(
let js_doc = doc_parser.js_doc_for_span(export_span);
let location = doc_parser.ast_parser.get_span_location(export_span).into();
+ eprintln!("decl {:#?}", export_decl);
match &export_decl.decl {
Decl::Class(class_decl) => {
let (name, class_def) =
diff --git a/cli/doc/parser.rs b/cli/doc/parser.rs
index 2a15daa59..0ab3c8d34 100644
--- a/cli/doc/parser.rs
+++ b/cli/doc/parser.rs
@@ -4,6 +4,7 @@ use crate::swc_common::comments::CommentKind;
use crate::swc_common::Span;
use crate::swc_ecma_ast;
use crate::swc_ecma_ast::Decl;
+use crate::swc_ecma_ast::DefaultDecl;
use crate::swc_ecma_ast::ModuleDecl;
use crate::swc_ecma_ast::Stmt;
use crate::swc_util::AstParser;
@@ -200,6 +201,74 @@ impl DocParser {
export_decl,
)]
}
+ ModuleDecl::ExportDefaultDecl(export_default_decl) => {
+ let (js_doc, location) =
+ self.details_for_span(export_default_decl.span);
+ let name = "default".to_string();
+
+ let doc_node = match &export_default_decl.decl {
+ DefaultDecl::Class(class_expr) => {
+ let class_def =
+ crate::doc::class::class_to_class_def(self, &class_expr.class);
+ DocNode {
+ kind: DocNodeKind::Class,
+ name,
+ location,
+ js_doc,
+ class_def: Some(class_def),
+ function_def: None,
+ variable_def: None,
+ enum_def: None,
+ type_alias_def: None,
+ namespace_def: None,
+ interface_def: None,
+ }
+ }
+ DefaultDecl::Fn(fn_expr) => {
+ let function_def =
+ crate::doc::function::function_to_function_def(&fn_expr.function);
+ DocNode {
+ kind: DocNodeKind::Function,
+ name,
+ location,
+ js_doc,
+ class_def: None,
+ function_def: Some(function_def),
+ variable_def: None,
+ enum_def: None,
+ type_alias_def: None,
+ namespace_def: None,
+ interface_def: None,
+ }
+ }
+ DefaultDecl::TsInterfaceDecl(interface_decl) => {
+ let (_, interface_def) =
+ crate::doc::interface::get_doc_for_ts_interface_decl(
+ self,
+ interface_decl,
+ );
+ DocNode {
+ kind: DocNodeKind::Interface,
+ name,
+ location,
+ js_doc,
+ class_def: None,
+ function_def: None,
+ variable_def: None,
+ enum_def: None,
+ type_alias_def: None,
+ namespace_def: None,
+ interface_def: Some(interface_def),
+ }
+ }
+ };
+
+ vec![doc_node]
+ }
+ ModuleDecl::ExportDefaultExpr(export_default_expr) => {
+ eprintln!("export default expr {:#?}", export_default_expr);
+ vec![]
+ }
_ => vec![],
}
}
@@ -386,6 +455,7 @@ impl DocParser {
if let swc_ecma_ast::ModuleItem::ModuleDecl(module_decl) = node {
let r = match module_decl {
ModuleDecl::ExportNamed(named_export) => {
+ eprintln!("export named {:#?}", named_export);
if let Some(src) = &named_export.src {
let src_str = src.value.to_string();
named_export
diff --git a/cli/doc/tests.rs b/cli/doc/tests.rs
index 2317c3707..d30736f0d 100644
--- a/cli/doc/tests.rs
+++ b/cli/doc/tests.rs
@@ -1064,6 +1064,234 @@ declare namespace RootNs {
.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#"
@@ -1120,6 +1348,8 @@ async fn reexports() {
* JSDoc for bar
*/
export const bar = "bar";
+
+export default 42;
"#;
let reexport_source_code = r#"
import { bar } from "./nested_reexport.ts";
@@ -1130,7 +1360,7 @@ import { bar } from "./nested_reexport.ts";
export const foo = "foo";
"#;
let test_source_code = r#"
-export { foo as fooConst } from "./reexport.ts";
+export { default, foo as fooConst } from "./reexport.ts";
/** JSDoc for function */
export function fooFn(a: number) {