Skip to content

Commit

Permalink
feat: JSDoc @throws, @SInCE, @experimental, @internal tags (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats authored May 10, 2024
1 parent e6c05bc commit 2f2125a
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 23 deletions.
100 changes: 93 additions & 7 deletions src/js_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ use serde::Serialize;

lazy_static! {
static ref JS_DOC_TAG_MAYBE_DOC_RE: Regex = Regex::new(r"(?s)^\s*@(deprecated)(?:\s+(.+))?").unwrap();
static ref JS_DOC_TAG_DOC_RE: Regex = Regex::new(r"(?s)^\s*@(category|see|example|tags)(?:\s+(.+))").unwrap();
static ref JS_DOC_TAG_DOC_RE: Regex = Regex::new(r"(?s)^\s*@(category|see|example|tags|since)(?:\s+(.+))").unwrap();
static ref JS_DOC_TAG_NAMED_RE: Regex = Regex::new(r"(?s)^\s*@(callback|template|typeparam|typeParam)\s+([a-zA-Z_$]\S*)(?:\s+(.+))?").unwrap();
static ref JS_DOC_TAG_NAMED_TYPED_RE: Regex = Regex::new(r"(?s)^\s*@(prop(?:erty)?|typedef)\s+\{([^}]+)\}\s+([a-zA-Z_$]\S*)(?:\s+(.+))?").unwrap();
static ref JS_DOC_TAG_ONLY_RE: Regex = Regex::new(r"^\s*@(constructor|class|ignore|module|public|private|protected|readonly)").unwrap();
static ref JS_DOC_TAG_ONLY_RE: Regex = Regex::new(r"^\s*@(constructor|class|ignore|internal|module|public|private|protected|readonly|experimental)").unwrap();
static ref JS_DOC_TAG_PARAM_RE: Regex = Regex::new(
r"(?s)^\s*@(?:param|arg(?:ument)?)(?:\s+\{(?P<type>[^}]+)\})?\s+(?:(?:\[(?P<nameWithDefault>[a-zA-Z_$]\S*?)(?:\s*=\s*(?P<default>[^]]+))?\])|(?P<name>[a-zA-Z_$]\S*))(?:\s+(?P<doc>.+))?"
)
.unwrap();
static ref JS_DOC_TAG_RE: Regex = Regex::new(r"(?s)^\s*@(\S+)").unwrap();
static ref JS_DOC_TAG_RETURN_RE: Regex = Regex::new(r"(?s)^\s*@returns?(?:\s+\{([^}]+)\})?(?:\s+(.+))?").unwrap();
static ref JS_DOC_TAG_OPTIONAL_TYPE_AND_DOC_RE: Regex = Regex::new(r"(?s)^\s*@(returns?|throws|exception)(?:\s+\{([^}]+)\})?(?:\s+(.+))?").unwrap();
static ref JS_DOC_TAG_TYPED_RE: Regex = Regex::new(r"(?s)^\s*@(enum|extends|augments|this|type|default)\s+\{([^}]+)\}(?:\s+(.+))?").unwrap();
}

Expand Down Expand Up @@ -115,6 +115,8 @@ pub enum JsDocTag {
#[serde(default)]
doc: String,
},
/// `@experimental`
Experimental,
/// `@extends {type} comment`
Extends {
#[serde(rename = "type")]
Expand All @@ -124,6 +126,8 @@ pub enum JsDocTag {
},
/// `@ignore`
Ignore,
/// `@internal`
Internal,
/// `@module`
Module,
/// `@param`, `@arg` or `argument`, in format of `@param {type} name comment`
Expand Down Expand Up @@ -182,6 +186,13 @@ pub enum JsDocTag {
#[serde(skip_serializing_if = "Option::is_none", default)]
doc: Option<String>,
},
/// `@throws {type} comment` or `@exception {type} comment`
Throws {
#[serde(rename = "type")]
type_ref: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
doc: Option<String>,
},
/// `@typedef {type} name comment`
TypeDef {
name: String,
Expand All @@ -202,6 +213,10 @@ pub enum JsDocTag {
See {
doc: String,
},
/// `@since version`
Since {
doc: String,
},
Unsupported {
value: String,
},
Expand All @@ -213,7 +228,9 @@ impl From<String> for JsDocTag {
let kind = caps.get(1).unwrap().as_str();
match kind {
"constructor" | "class" => Self::Constructor,
"experimental" => Self::Experimental,
"ignore" => Self::Ignore,
"internal" => Self::Internal,
"module" => Self::Module,
"public" => Self::Public,
"private" => Self::Private,
Expand Down Expand Up @@ -280,6 +297,7 @@ impl From<String> for JsDocTag {
tags: doc.split(',').map(|i| i.trim().to_string()).collect(),
},
"see" => Self::See { doc },
"since" => Self::Since { doc },
_ => unreachable!("kind unexpected: {}", kind),
}
} else if let Some(caps) = JS_DOC_TAG_PARAM_RE.captures(&value) {
Expand All @@ -300,10 +318,17 @@ impl From<String> for JsDocTag {
default,
doc,
}
} else if let Some(caps) = JS_DOC_TAG_RETURN_RE.captures(&value) {
let type_ref = caps.get(1).map(|m| m.as_str().to_string());
let doc = caps.get(2).map(|m| m.as_str().to_string());
Self::Return { type_ref, doc }
} else if let Some(caps) =
JS_DOC_TAG_OPTIONAL_TYPE_AND_DOC_RE.captures(&value)
{
let kind = caps.get(1).unwrap().as_str();
let type_ref = caps.get(2).map(|m| m.as_str().to_string());
let doc = caps.get(3).map(|m| m.as_str().to_string());
match kind {
"return" | "returns" => Self::Return { type_ref, doc },
"throws" | "exception" => Self::Throws { type_ref, doc },
_ => unreachable!("kind unexpected: {}", kind),
}
} else {
Self::Unsupported { value }
}
Expand All @@ -325,6 +350,11 @@ mod tests {
serde_json::to_value(JsDoc::from("@class more".to_string())).unwrap(),
json!({ "tags": [ { "kind": "constructor" } ] }),
);
assert_eq!(
serde_json::to_value(JsDoc::from("@experimental more".to_string()))
.unwrap(),
json!({ "tags": [ { "kind": "experimental" } ] }),
);
assert_eq!(
serde_json::to_value(JsDoc::from("@ignore more".to_string())).unwrap(),
json!({ "tags": [ { "kind": "ignore" } ] }),
Expand Down Expand Up @@ -629,6 +659,15 @@ if (true) {
}]
})
);
assert_eq!(
serde_json::to_value(JsDoc::from("@since 1.0.0".to_string())).unwrap(),
json!({
"tags": [{
"kind": "since",
"doc": "1.0.0"
}]
})
);

assert_eq!(
serde_json::to_value(JsDoc::from(
Expand All @@ -637,6 +676,7 @@ if (true) {
const a = "a";
@category foo
@see bar
@since 1.0.0
"#
.to_string()
))
Expand All @@ -654,6 +694,9 @@ const a = "a";
}, {
"kind": "see",
"doc": "bar"
}, {
"kind": "since",
"doc": "1.0.0"
}]

})
Expand Down Expand Up @@ -798,6 +841,49 @@ const a = "a";
);
}

#[test]
fn test_js_doc_tag_throws() {
assert_eq!(
serde_json::to_value(JsDoc::from(
"@throws {string} maybe doc\n\nnew paragraph".to_string()
))
.unwrap(),
json!({
"tags": [{
"kind": "throws",
"type": "string",
"doc": "maybe doc\n\nnew paragraph",
}]
})
);
assert_eq!(
serde_json::to_value(JsDoc::from(
"@throws maybe doc\n\nnew paragraph".to_string()
))
.unwrap(),
json!({
"tags": [{
"kind": "throws",
"type": null,
"doc": "maybe doc\n\nnew paragraph",
}]
})
);
assert_eq!(
serde_json::to_value(JsDoc::from(
"@throws {string} maybe doc\n\nnew paragraph".to_string()
))
.unwrap(),
json!({
"tags": [{
"kind": "throws",
"type": "string",
"doc": "maybe doc\n\nnew paragraph",
}]
})
);
}

#[test]
fn test_js_doc_from_str() {
assert_eq!(
Expand Down
19 changes: 19 additions & 0 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ impl<'a> DocPrinter<'a> {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("example"))?;
self.format_jsdoc_tag_doc(w, doc, indent)
}
JsDocTag::Experimental => {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("experimental"))
}
JsDocTag::Extends { type_ref, doc } => {
writeln!(
w,
Expand All @@ -268,6 +271,9 @@ impl<'a> DocPrinter<'a> {
JsDocTag::Ignore => {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("ignore"))
}
JsDocTag::Internal => {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("internal"))
}
JsDocTag::Module => {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("module"))
}
Expand Down Expand Up @@ -395,6 +401,19 @@ impl<'a> DocPrinter<'a> {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("see"))?;
self.format_jsdoc_tag_doc(w, doc, indent)
}
JsDocTag::Since { doc } => {
writeln!(w, "{}@{}", Indent(indent), colors::magenta("since"))?;
self.format_jsdoc_tag_doc(w, doc, indent)
}
JsDocTag::Throws { type_ref, doc } => {
write!(w, "{}@{}", Indent(indent), colors::magenta("return"))?;
if let Some(type_ref) = type_ref {
writeln!(w, " {{{}}}", colors::italic_cyan(type_ref))?;
} else {
writeln!(w)?;
}
self.format_jsdoc_tag_maybe_doc(w, doc, indent)
}
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/util/swc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ pub fn module_export_name_value(

/// If the jsdoc has an `@internal` or `@ignore` tag.
pub fn has_ignorable_js_doc_tag(js_doc: &JsDoc) -> bool {
js_doc.tags.iter().any(|t| matches!(t, JsDocTag::Ignore) || matches!(t, JsDocTag::Unsupported { value } if value == "@internal" || value.starts_with("@internal ")))
js_doc
.tags
.iter()
.any(|t| *t == JsDocTag::Ignore || *t == JsDocTag::Internal)
}

#[cfg(test)]
Expand Down
12 changes: 4 additions & 8 deletions tests/specs/internal_tag.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ type Test = OtherPrivateType
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand Down Expand Up @@ -190,8 +189,7 @@ type Test = OtherPrivateType
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand All @@ -216,8 +214,7 @@ type Test = OtherPrivateType
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand Down Expand Up @@ -284,8 +281,7 @@ type Test = OtherPrivateType
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand Down
33 changes: 30 additions & 3 deletions tests/specs/jsdoc_tags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
* @param [c=1] additional doc
* @param [d] more doc
* @returns {string} returning doc
* @throws {number} throw doc
* @since 1.0.0
* @experimental
* @internal
*/
export function a(b, c, d) {}

# output.txt
Defined in file:///mod.ts:9:1
Defined in file:///mod.ts:13:1

function a(b, c, d): void
a is a function
Expand All @@ -27,6 +31,14 @@ function a(b, c, d): void
@return {string}
returning doc

@return {number}
throw doc

@since
1.0.0

@experimental
@internal


# output.json
Expand All @@ -36,9 +48,9 @@ function a(b, c, d): void
"name": "a",
"location": {
"filename": "file:///mod.ts",
"line": 9,
"line": 13,
"col": 0,
"byteIndex": 149
"byteIndex": 225
},
"declarationKind": "export",
"jsDoc": {
Expand Down Expand Up @@ -66,6 +78,21 @@ function a(b, c, d): void
"kind": "return",
"type": "string",
"doc": "returning doc"
},
{
"kind": "throws",
"type": "number",
"doc": "throw doc"
},
{
"kind": "since",
"doc": "1.0.0"
},
{
"kind": "experimental"
},
{
"kind": "internal"
}
]
},
Expand Down
3 changes: 1 addition & 2 deletions tests/specs/private_type_ignored_class_not_namespace.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ private namespace MyNamespace
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand Down
3 changes: 1 addition & 2 deletions tests/specs/private_type_re_export_referencing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ class Data
"jsDoc": {
"tags": [
{
"kind": "unsupported",
"value": "@internal"
"kind": "internal"
}
]
},
Expand Down

0 comments on commit 2f2125a

Please sign in to comment.