0
0
Fork 0
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:
Bartek Iwańczuk 2020-05-06 14:48:48 +02:00 committed by GitHub
parent e513751ee9
commit e18aaf49cf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 322 additions and 15 deletions

View file

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

View file

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

View file

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

View file

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