From a9923f3f93f8786388d84313666f0fc11113830f Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Tue, 14 Apr 2020 00:07:06 +0200 Subject: [PATCH] fix(doc): expose optionality in function params and class members (#4738) --- cli/doc/class.rs | 4 ++++ cli/doc/interface.rs | 2 ++ cli/doc/node.rs | 1 + cli/doc/params.rs | 4 ++++ cli/doc/printer.rs | 21 ++++++++++++++++++--- cli/doc/tests.rs | 42 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 66 insertions(+), 8 deletions(-) diff --git a/cli/doc/class.rs b/cli/doc/class.rs index 410789041e..3d66a094ea 100644 --- a/cli/doc/class.rs +++ b/cli/doc/class.rs @@ -35,6 +35,7 @@ pub struct ClassPropertyDef { pub ts_type: Option, pub readonly: bool, pub accessibility: Option, + pub optional: bool, pub is_abstract: bool, pub is_static: bool, pub name: String, @@ -46,6 +47,7 @@ pub struct ClassPropertyDef { pub struct ClassMethodDef { pub js_doc: Option, pub accessibility: Option, + pub optional: bool, pub is_abstract: bool, pub is_static: bool, pub name: String, @@ -159,6 +161,7 @@ pub fn get_doc_for_class_decl( let method_def = ClassMethodDef { js_doc: method_js_doc, accessibility: class_method.accessibility, + optional: class_method.is_optional, is_abstract: class_method.is_abstract, is_static: class_method.is_static, name: method_name, @@ -189,6 +192,7 @@ pub fn get_doc_for_class_decl( js_doc: prop_js_doc, ts_type, readonly: class_prop.readonly, + optional: class_prop.is_optional, is_abstract: class_prop.is_abstract, is_static: class_prop.is_static, accessibility: class_prop.accessibility, diff --git a/cli/doc/interface.rs b/cli/doc/interface.rs index 41ef0e7201..0e4173d18e 100644 --- a/cli/doc/interface.rs +++ b/cli/doc/interface.rs @@ -17,6 +17,7 @@ pub struct InterfaceMethodDef { pub name: String, pub location: Location, pub js_doc: Option, + pub optional: bool, pub params: Vec, pub return_type: Option, pub type_params: Vec, @@ -115,6 +116,7 @@ pub fn get_doc_for_ts_interface_decl( .source_map .lookup_char_pos(ts_method_sig.span.lo()) .into(), + optional: ts_method_sig.optional, params, return_type: maybe_return_type, type_params, diff --git a/cli/doc/node.rs b/cli/doc/node.rs index a6b11133ed..0846db0dd8 100644 --- a/cli/doc/node.rs +++ b/cli/doc/node.rs @@ -28,6 +28,7 @@ pub enum ParamKind { pub struct ParamDef { pub name: String, pub kind: ParamKind, + pub optional: bool, pub ts_type: Option, } diff --git a/cli/doc/params.rs b/cli/doc/params.rs index 04fd7f898e..0869c19695 100644 --- a/cli/doc/params.rs +++ b/cli/doc/params.rs @@ -13,6 +13,7 @@ pub fn ident_to_param_def(ident: &swc_ecma_ast::Ident) -> ParamDef { ParamDef { name: ident.sym.to_string(), kind: ParamKind::Identifier, + optional: ident.optional, ts_type, } } @@ -27,6 +28,7 @@ fn rest_pat_to_param_def(rest_pat: &swc_ecma_ast::RestPat) -> ParamDef { ParamDef { name, kind: ParamKind::Rest, + optional: false, ts_type, } } @@ -40,6 +42,7 @@ fn object_pat_to_param_def(object_pat: &swc_ecma_ast::ObjectPat) -> ParamDef { ParamDef { name: "".to_string(), kind: ParamKind::Object, + optional: object_pat.optional, ts_type, } } @@ -50,6 +53,7 @@ fn array_pat_to_param_def(array_pat: &swc_ecma_ast::ArrayPat) -> ParamDef { ParamDef { name: "".to_string(), kind: ParamKind::Array, + optional: array_pat.optional, ts_type, } } diff --git a/cli/doc/printer.rs b/cli/doc/printer.rs index 0fa9d01115..25277a6367 100644 --- a/cli/doc/printer.rs +++ b/cli/doc/printer.rs @@ -112,6 +112,9 @@ fn render_params(params: Vec) -> String { if !params.is_empty() { for param in params { rendered += param.name.as_str(); + if param.optional { + rendered += "?"; + } if let Some(ts_type) = param.ts_type { rendered += ": "; rendered += render_ts_type(ts_type).as_str(); @@ -187,7 +190,9 @@ fn render_ts_type(ts_type: doc::ts_type::TsTypeDef) -> String { } } } - TsTypeDefKind::Optional => "_optional_".to_string(), + TsTypeDefKind::Optional => { + format!("{}?", render_ts_type(*ts_type.optional.unwrap())) + } TsTypeDefKind::Parenthesized => { format!("({})", render_ts_type(*ts_type.parenthesized.unwrap())) } @@ -339,7 +344,7 @@ fn format_class_details(node: doc::DocNode) -> String { }) { details.push_str(&add_indent( format!( - "{}{}{}\n", + "{}{}{}{}\n", colors::magenta( match node .accessibility @@ -350,6 +355,11 @@ fn format_class_details(node: doc::DocNode) -> String { } ), colors::bold(node.name.clone()), + if node.optional { + "?".to_string() + } else { + "".to_string() + }, if let Some(ts_type) = node.ts_type.clone() { format!(": {}", render_ts_type(ts_type)) } else { @@ -368,7 +378,7 @@ fn format_class_details(node: doc::DocNode) -> String { let function_def = node.function_def.clone(); details.push_str(&add_indent( format!( - "{}{}{}({}){}\n", + "{}{}{}{}({}){}\n", colors::magenta( match node .accessibility @@ -384,6 +394,11 @@ fn format_class_details(node: doc::DocNode) -> String { _ => "".to_string(), }), colors::bold(node.name.clone()), + if node.optional { + "?".to_string() + } else { + "".to_string() + }, render_params(function_def.params), if let Some(return_type) = function_def.return_type { format!(": {}", render_ts_type(return_type)) diff --git a/cli/doc/tests.rs b/cli/doc/tests.rs index fb2c696896..9a9ac8e02a 100644 --- a/cli/doc/tests.rs +++ b/cli/doc/tests.rs @@ -52,7 +52,7 @@ async fn export_fn() { * * Or not that many? */ -export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void { +export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void { console.log("Hello world"); } "#; @@ -70,6 +70,7 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. { "name": "a", "kind": "identifier", + "optional": false, "tsType": { "keyword": "string", "kind": "keyword", @@ -79,6 +80,7 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. { "name": "b", "kind": "identifier", + "optional": true, "tsType": { "keyword": "number", "kind": "keyword", @@ -88,6 +90,7 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. { "name": "cb", "kind": "identifier", + "optional": false, "tsType": { "repr": "", "kind": "fnOrConstructor", @@ -102,6 +105,7 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. "params": [{ "kind": "rest", "name": "cbArgs", + "optional": false, "tsType": { "repr": "", "kind": "array", @@ -118,6 +122,7 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. { "name": "args", "kind": "rest", + "optional": false, "tsType": { "repr": "", "kind": "array", @@ -148,9 +153,13 @@ export function foo(a: string, b: number, cb: (...cbArgs: unknown[]) => void, .. let actual = serde_json::to_value(entry).unwrap(); assert_eq!(actual, expected_json); + assert!(colors::strip_ansi_codes( + super::printer::format(entries.clone()).as_str() + ) + .contains("Hello there")); assert!( colors::strip_ansi_codes(super::printer::format(entries).as_str()) - .contains("Hello there") + .contains("b?: number") ); } @@ -180,6 +189,7 @@ export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, { "name": "", "kind": "array", + "optional": false, "tsType": { "repr": "", "kind": "array", @@ -193,11 +203,13 @@ export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, { "name": "", "kind": "object", + "optional": false, "tsType": null }, { "name": "ops", "kind": "identifier", + "optional": false, "tsType": { "repr": "AssignOpts", "kind": "typeRef", @@ -270,7 +282,7 @@ async fn export_class() { let source_code = r#" /** Class doc */ export class Foobar extends Fizz implements Buzz, Aldrin { - private private1: boolean; + private private1?: boolean; protected protected1: number; public public1: boolean; public2: number; @@ -284,7 +296,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { } /** Sync bar method */ - bar(): void { + bar?(): void { // } } @@ -316,6 +328,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { { "name": "name", "kind": "identifier", + "optional": false, "tsType": { "repr": "string", "kind": "keyword", @@ -325,6 +338,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { { "name": "private2", "kind": "identifier", + "optional": false, "tsType": { "repr": "number", "kind": "keyword", @@ -334,6 +348,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { { "name": "protected2", "kind": "identifier", + "optional": false, "tsType": { "repr": "number", "kind": "keyword", @@ -358,6 +373,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { }, "readonly": false, "accessibility": "private", + "optional": true, "isAbstract": false, "isStatic": false, "name": "private1", @@ -376,6 +392,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { }, "readonly": false, "accessibility": "protected", + "optional": false, "isAbstract": false, "isStatic": false, "name": "protected1", @@ -394,6 +411,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { }, "readonly": false, "accessibility": "public", + "optional": false, "isAbstract": false, "isStatic": false, "name": "public1", @@ -412,6 +430,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { }, "readonly": false, "accessibility": null, + "optional": false, "isAbstract": false, "isStatic": false, "name": "public2", @@ -426,6 +445,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { { "jsDoc": "Async foo method", "accessibility": null, + "optional": false, "isAbstract": false, "isStatic": false, "name": "foo", @@ -459,6 +479,7 @@ export class Foobar extends Fizz implements Buzz, Aldrin { { "jsDoc": "Sync bar method", "accessibility": null, + "optional": true, "isAbstract": false, "isStatic": false, "name": "bar", @@ -487,6 +508,11 @@ export class Foobar extends Fizz implements Buzz, Aldrin { 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")); + assert!( colors::strip_ansi_codes(super::printer::format(entries).as_str()) .contains("class Foobar extends Fizz implements Buzz, Aldrin") @@ -501,7 +527,7 @@ async fn export_interface() { */ export interface Reader { /** Read n bytes */ - read(buf: Uint8Array, something: unknown): Promise + read?(buf: Uint8Array, something: unknown): Promise } "#; let loader = @@ -527,11 +553,13 @@ export interface Reader { "line": 7, "col": 4 }, + "optional": true, "jsDoc": "Read n bytes", "params": [ { "name": "buf", "kind": "identifier", + "optional": false, "tsType": { "repr": "Uint8Array", "kind": "typeRef", @@ -544,6 +572,7 @@ export interface Reader { { "name": "something", "kind": "identifier", + "optional": false, "tsType": { "repr": "unknown", "kind": "keyword", @@ -613,6 +642,7 @@ export interface TypedIface { "col": 4 }, "jsDoc": null, + "optional": false, "params": [], "typeParams": [], "returnType": { @@ -956,6 +986,7 @@ async fn optional_return_type() { { "name": "a", "kind": "identifier", + "optional": false, "tsType": { "keyword": "number", "kind": "keyword", @@ -1048,6 +1079,7 @@ export function fooFn(a: number) { { "name": "a", "kind": "identifier", + "optional": false, "tsType": { "keyword": "number", "kind": "keyword",