mirror of
https://github.com/denoland/deno.git
synced 2024-10-29 08:58:01 -04:00
feat: deno doc handles default exports (#4873)
This commit is contained in:
parent
e513751ee9
commit
e18aaf49cf
4 changed files with 322 additions and 15 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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
|
||||
|
|
232
cli/doc/tests.rs
232
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) {
|
||||
|
|
Loading…
Reference in a new issue