1use nom::{
2 bytes::complete::{tag, take_while},
3 error::ErrorKind,
4 IResult, InputIter, Slice,
5};
6use swc_common::{Span, Spanned};
7
8pub use self::input::Input;
9use crate::ast::*;
10
11pub mod ast;
12mod input;
13
14pub fn parse(i: Input) -> IResult<Input, JsDoc> {
15 let i = skip(i);
16
17 let mut tags = Vec::new();
18 let lo = i.span().lo;
19 let (description, mut i) = take_while(|c| c != '@')(i)?;
20 let description = trim(description).into();
21
22 i = skip(i);
23
24 while i.starts_with('@') {
25 let (input, tag) = parse_tag_item(i)?;
26 i = input;
27 tags.push(tag);
28 i = skip(i);
29 }
30
31 let hi = i.span().hi;
32 Ok((
33 i,
34 JsDoc {
35 span: Span::new(lo, hi),
36 tags,
37 description,
38 },
39 ))
40}
41
42pub fn parse_tag_item(i: Input) -> IResult<Input, TagItem> {
43 let i = skip(i);
44 let (_, i) = tag("@")(i)?;
45
46 let (mut i, tag_name) = parse_word(i)?;
47 i = skip_ws(i);
48
49 let span = tag_name.span();
50 let tag = match &*tag_name.value {
51 "abstract" | "virtual" => Tag::Abstract(AbstractTag { span }),
52
53 "access" => {
54 let (input, access) = parse_one_of(i, &["private", "protected", "package", "public"])?;
55 i = input;
56 Tag::Access(AccessTag { span, access })
57 }
58
59 "alias" => {
60 let (input, name_path) = parse_name_path(i)?;
61 i = input;
62 Tag::Alias(AliasTag { span, name_path })
63 }
64
65 "async" => Tag::Async(AsyncTag { span }),
66
67 "augments" | "extends" => {
68 let (input, name_path) = parse_name_path(i)?;
69 i = input;
70 Tag::Augments(AugmentsTag {
71 span,
72 class: name_path,
73 })
74 }
75
76 "author" => {
77 let (input, author) = parse_line(i)?;
78 i = input;
79 Tag::Author(AuthorTag { span, author })
80 }
81
82 "borrows" => {
83 let (input, from) = parse_name_path(i)?;
84 let (_, input) = tag("as")(input)?;
85 let (input, to) = parse_name_path(input)?;
86 i = input;
87 Tag::Borrows(BorrowsTag { span, from, to })
88 }
89
90 "callback" => {
91 let (input, name_path) = parse_name_path(i)?;
92 i = input;
93 Tag::Callback(CallbackTag { span, name_path })
94 }
95
96 "class" | "constructor" => {
97 let (input, ty) = parse_opt_str(i)?;
99 let (input, name) = parse_opt_str(input)?;
100 i = input;
101
102 Tag::Class(ClassTag { span, ty, name })
103 }
104
105 "classdesc" => {
106 let (input, desc) = parse_line(i)?;
107 i = input;
108
109 Tag::ClassDesc(JSDocClassDescTag { span, desc })
110 }
111
112 "constant" | "const" => {
113 let (input, ty) = parse_opt_str(i)?;
115 let (input, name) = parse_opt_str(input)?;
116 i = input;
117 Tag::Const(ConstTag { span, ty, name })
118 }
119
120 "constructs" => {
121 let (input, name) = parse_line(i)?;
122 i = input;
123 Tag::Constructs(ConstructsTag { span, name })
124 }
125
126 "copyright" => {
127 let (input, text) = parse_line(i)?;
128 i = input;
129 Tag::Copyright(CopyrightTag { span, text })
130 }
131
132 "default" | "defaultvalue" => {
133 let (input, value) = parse_line(i)?;
134 i = input;
135 Tag::Default(DefaultTag { span, value })
136 }
137
138 "deprecated" => {
139 let (input, text) = parse_line(i)?;
140 i = input;
141 Tag::Deprecated(DeprecatedTag { span, text })
142 }
143
144 "description" | "desc" => {
145 let (input, text) = parse_line(i)?;
146 i = input;
147 Tag::Description(DescriptionTag { span, text })
148 }
149
150 "enum" => {
151 let (input, ty) = parse_type(i)?;
152 i = input;
153 Tag::Enum(EnumTag { span, ty })
154 }
155
156 "event" => {
157 let (input, ty) = parse_line(i)?;
159 i = input;
160 Tag::Unknown(UnknownTag { span, extras: ty })
161 }
162
163 "example" => {
164 let (text, input) = take_while(|c| c != '@')(i)?;
165 i = input;
166 Tag::Example(ExampleTag {
167 span,
168 text: text.into(),
169 })
170 }
171
172 "exports" => {
173 let (input, text) = parse_line(i)?;
174 i = input;
175 Tag::Exports(ExportsTag {
176 span,
177 module_name: text,
178 })
179 }
180
181 "external" | "host" => {
182 let (input, name) = parse_line(i)?;
183 i = input;
184 Tag::External(ExternalTag { span, name })
185 }
186
187 "file" | "fileoverview" | "overview" => {
188 let (input, text) = parse_line(i)?;
189 i = input;
190 Tag::File(FileTag { span, text })
191 }
192
193 "fires" | "emits" => {
194 let (input, ty) = parse_line(i)?;
196 i = input;
197 Tag::Unknown(UnknownTag { span, extras: ty })
198 }
199
200 "function" | "func" | "method" => {
201 let (input, name) = parse_opt_str(i)?;
202 i = input;
203 Tag::Function(FunctionTag { span, name })
204 }
205
206 "generator" => Tag::Generator(GeneratorTag { span }),
207
208 "hideconstructor" => Tag::HideConstructor(HideConstructorTag { span }),
209
210 "ignore" => Tag::Ignore(IgnoreTag { span }),
211
212 "implements" => {
213 let (input, class) = parse_type(i)?;
214 i = input;
215 Tag::Implements(ImplementsTag { span, class })
216 }
217
218 "inheritdoc" => Tag::InheritDoc(InheritDocTag { span }),
219
220 "inner" => Tag::Inner(InnerTag { span }),
221
222 "instance" => Tag::Instance(InstanceTag { span }),
223
224 "interface" => {
225 let (input, name) = parse_opt_str(i)?;
226 i = input;
227 Tag::Interface(InterfaceTag { span, name })
228 }
229
230 "kind" => {
231 let (input, name) = parse_one_of(
232 i,
233 &[
234 "class",
235 "constant",
236 "event",
237 "external",
238 "file",
239 "function",
240 "member",
241 "mixin",
242 "module",
243 "namespace",
244 "typedef",
245 ],
246 )?;
247 i = input;
248 Tag::Kind(KindTag { span, name })
249 }
250
251 "lends" => {
252 let (input, name) = parse_name_path(i)?;
253 i = input;
254 Tag::Lends(LendsTag { span, name })
255 }
256
257 "license" => {
258 let (input, identifier) = parse_line(i)?;
259 i = input;
260 Tag::License(LicenseTag { span, identifier })
261 }
262
263 "listens" => {
264 let (input, event_name) = parse_line(i)?;
265 i = input;
266 Tag::Listens(ListensTag { span, event_name })
267 }
268
269 "member" | "var" => {
270 let (input, ty) = parse_word(i)?;
271 let (input, name) = parse_word(input)?;
272 i = input;
273 Tag::Member(MemberTag { span, ty, name })
274 }
275
276 "memberof" | "memberof!" => {
277 let (input, parent_name_path) = parse_name_path(i)?;
278 i = input;
279 Tag::MemberOf(MemberOfTag {
280 span,
281 parent_name_path,
282 })
283 }
284
285 "mixes" => {
286 let (input, name_path) = parse_name_path(i)?;
287 i = input;
288 Tag::Mixes(MixesTag { span, name_path })
289 }
290
291 "mixin" => {
292 let (input, name) = parse_line(i)?;
293 i = input;
294 Tag::Mixin(MixinTag { span, name })
295 }
296
297 "module" => {
298 let (input, ty) = parse_word(i)?;
299 let (input, name) = parse_line(input)?;
300 i = input;
301 Tag::Module(ModuleTag { span, ty, name })
302 }
303
304 "name" => {
305 let (input, name_path) = parse_name_path(i)?;
306 i = input;
307 Tag::Name(NameTag { span, name_path })
308 }
309
310 "namespace" => {
311 let (input, ty) = parse_opt_type(i)?;
312 let (input, name) = parse_opt_str(input)?;
313 i = input;
314 Tag::Namespace(NamespaceTag { span, ty, name })
315 }
316
317 "override" => Tag::Override(OverrideTag { span }),
318
319 "package" => {
320 let (input, ty) = parse_opt_type(i)?;
321 i = input;
322 Tag::Package(PackageTag { span, ty })
323 }
324
325 "param" | "arg" | "argument" => {
326 let (input, ty) = parse_opt_type(i)?;
327 let (input, name) = parse_opt_word(input)?;
328 let (input, desc) = parse_line(input)?;
329 i = input;
330 Tag::Parameter(ParameterTag {
331 span,
332 ty,
333 name,
334 desc,
335 })
336 }
337
338 "private" => {
339 let (input, ty) = parse_opt_type(i)?;
340 i = input;
341 Tag::Private(PrivateTag { span, ty })
342 }
343
344 "property" | "prop" => {
345 let (input, ty) = parse_opt_type(i)?;
346 let (input, name_path) = parse_name_path(input)?;
347 let (input, desc) = parse_line(input)?;
348 i = input;
349 Tag::Property(PropertyTag {
350 span,
351 ty,
352 name_path,
353 desc,
354 })
355 }
356
357 "protected" => {
358 let (input, ty) = parse_opt_type(i)?;
359 i = input;
360 Tag::Protected(ProtectedTag { span, ty })
361 }
362
363 "public" => Tag::Public(PublicTag { span }),
364
365 "readonly" => Tag::Readonly(ReadonlyTag { span }),
366
367 "requires" => {
368 let (input, name_path) = parse_name_path(i)?;
369 i = input;
370 Tag::Requires(RequiresTag { span, name_path })
371 }
372
373 "returns" | "return" => {
374 let (input, ty) = parse_opt_type(i)?;
375 let (input, description) = parse_line(input)?;
376 i = input;
377 Tag::Return(ReturnTag {
378 span,
379 ty,
380 description,
381 })
382 }
383
384 "see" => {
385 let (input, text) = parse_line(i)?;
386 i = input;
387 Tag::See(SeeTag { span, text })
388 }
389
390 "since" => {
391 let (input, version) = parse_line(i)?;
392 i = input;
393 Tag::Since(SinceTag { span, version })
394 }
395
396 "static" => Tag::Static(StaticTag { span }),
397
398 "summary" => {
399 let (input, text) = parse_line(i)?;
400 i = input;
401 Tag::Summary(SummaryTag { span, text })
402 }
403
404 "this" => {
405 let (input, name_path) = parse_name_path(i)?;
406 i = input;
407 Tag::This(ThisTag { span, name_path })
408 }
409
410 "throws" => {
411 let (input, text) = parse_line(i)?;
412 i = input;
413 Tag::Throw(ThrowTag { span, text })
414 }
415
416 "todo" => {
417 let (input, text) = parse_line(i)?;
418 i = input;
419 Tag::Todo(TodoTag { span, text })
420 }
421
422 "tutorial" => {
423 let (input, text) = parse_line(i)?;
424 i = input;
425 Tag::Tutorial(TutorialTag { span, text })
426 }
427
428 "type" => {
429 let (input, name) = parse_line(i)?;
430 i = input;
431 Tag::Type(TypeTag { span, name })
432 }
433
434 "typedef" => {
435 let (input, ty) = parse_opt_type(i)?;
436 let (input, name_path) = parse_name_path(input)?;
437 i = input;
438 Tag::TypeDef(TypeDefTag {
439 span,
440 ty,
441 name_path,
442 })
443 }
444
445 "variation" => {
446 let (input, number) = parse_line(i)?;
447 i = input;
448 Tag::Variation(VariationTag { span, number })
449 }
450
451 "version" => {
452 let (input, value) = parse_line(i)?;
453 i = input;
454 Tag::Version(VersionTag { span, value })
455 }
456
457 "yields" | "yield" => {
458 let (input, value) = parse_opt_type(i)?;
459 let (input, description) = parse_line(input)?;
460 i = input;
461 Tag::Yield(YieldTag {
462 span,
463 value,
464 description,
465 })
466 }
467
468 _ => {
469 let (input, extras) = parse_str(i)?;
470 i = input;
471 Tag::Unknown(UnknownTag { span, extras })
472 }
473 };
474
475 Ok((
476 i,
477 TagItem {
478 span,
479 tag_name,
480 tag,
481 },
482 ))
483}
484
485fn parse_opt_str(i: Input) -> IResult<Input, Option<Text>> {
486 let (i, res) = parse_line(i)?;
487
488 if res.value.is_empty() {
489 Ok((i, None))
490 } else {
491 Ok((i, Some(res)))
492 }
493}
494
495fn parse_str(i: Input) -> IResult<Input, Text> {
496 parse_line(i)
497}
498
499fn parse_type(i: Input) -> IResult<Input, Text> {
500 parse_line(i)
501}
502
503fn trim(i: Input) -> Input {
506 let prev_len = i.len();
507 let new_str = i.trim_start();
508
509 let start = prev_len - new_str.len();
510
511 let end = i.trim_end().len();
512
513 if start >= end {
514 return i;
515 }
516
517 i.slice(start..end)
518}
519
520fn parse_opt_type(i: Input) -> IResult<Input, Option<Text>> {
521 let i = skip_ws(i);
522 if i.starts_with('{') {
523 if let Some(pos) = i.find('}') {
524 let ret = i.slice(..pos + 1);
525 let i = i.slice(pos + 1..);
526 return Ok((i, Some(ret.into())));
527 }
528 }
529
530 parse_opt_word(i)
531}
532
533fn parse_one_of<'i>(i: Input<'i>, list: &[&str]) -> IResult<Input<'i>, Text> {
534 for &item in list {
535 if i.starts_with(item) {
536 let res = tag::<&str, Input<'_>, (_, ErrorKind)>(item)(i);
537 match res {
538 Ok(v) => return Ok((v.1, v.0.into())),
539 Err(..) => continue,
540 }
541 }
542 }
543
544 Err(nom::Err::Error(nom::error::Error::new(i, ErrorKind::Tag)))
545}
546
547fn parse_name_path(mut i: Input) -> IResult<Input, NamePath> {
548 let lo = i.span().lo;
549 let mut components = Vec::new();
550
551 loop {
552 let (input, component) = parse_word(i)?;
553 components.push(component);
554 i = input;
555
556 let (_, input) = match tag(".")(i) {
557 Ok(v) => v,
558 Err(err) => {
559 if components.is_empty() {
560 return Err(err);
561 }
562
563 return Ok((
564 i,
565 NamePath {
566 span: Span::new(lo, i.span().hi),
567 components,
568 },
569 ));
570 }
571 };
572 i = input;
573 }
574}
575
576fn parse_opt_word(i: Input) -> IResult<Input, Option<Text>> {
577 let (i, v) = parse_word(i)?;
578 if v.value.is_empty() {
579 return Ok((i, None));
580 }
581
582 Ok((i, Some(v)))
583}
584
585fn parse_word(i: Input) -> IResult<Input, Text> {
586 let res = i.iter_indices().find(|(_, c)| {
587 !(('a' <= *c && *c <= 'z') || ('A' <= *c && *c <= 'Z' || *c == '<' || *c == '>'))
588 });
589
590 if let Some((idx, _)) = res {
591 let rest = i.slice(idx + 1..);
592 let ret = i.slice(..idx);
593
594 Ok((rest, ret.into()))
595 } else {
596 Ok((Input::empty(), i.into()))
598 }
599}
600
601fn parse_line(i: Input) -> IResult<Input, Text> {
602 let i = skip_ws(i);
603 let res = i.iter_indices().find(|(_, c)| *c == '\n' || *c == '\r');
604
605 if let Some((idx, _)) = res {
606 let rest = i.slice(idx + 1..);
607 let ret = i.slice(..idx);
608
609 Ok((rest, ret.into()))
610 } else {
611 Ok((i, Input::empty().into()))
612 }
613}
614
615fn skip_ws(i: Input) -> Input {
619 let mut index = 0;
620
621 for (idx, c) in i.char_indices() {
622 if c == ' ' {
623 index = idx + 1;
624 continue;
625 }
626
627 break;
628 }
629
630 i.slice(index..)
631}
632
633fn skip(i: Input) -> Input {
635 let mut at_line_start = true;
638 let mut index = 0;
639
640 for (idx, c) in i.char_indices() {
641 if at_line_start && c == '*' {
642 at_line_start = false;
643 index = idx + 1;
644 continue;
645 }
646
647 if c == '\r' || c == '\n' {
648 at_line_start = true;
649 index = idx + 1;
650 continue;
651 }
652
653 if c == ' ' {
654 index = idx + 1;
655 continue;
656 }
657
658 break;
659 }
660
661 i.slice(index..)
662}
663
664#[cfg(test)]
665mod tests {
666 use swc_common::BytePos;
667
668 use super::*;
669
670 fn input(s: &str) -> Input {
671 Input::new(BytePos(0), BytePos(s.len() as _), s)
672 }
673
674 #[test]
675 fn issue_1058() {
676 let (i, ret) = parse(input(
677 r#"
678 *
679 * @param list - LinkedList<T>
680 * @example <caption>Linkedlists.compareWith</caption>
681 * import { LinkedList } from './js_test/linkedlist.ts'
682 * const testArr = [1, 2, 3, 4, 5, 6, 78, 9, 0, 65];
683 * const firstList = new LinkedList<number>();
684 * const secondList = new LinkedList<number>();
685 * for (let data of testArr) {
686 * firstList.insertNode(data);
687 * secondList.insertNode(data);
688 * }
689 * const result = firstList.compareWith(secondList);
690 * assert(result);
691 * @returns boolean
692 "#,
693 ))
694 .unwrap();
695
696 assert_eq!(&*skip(i), "");
697 assert_eq!(ret.tags.len(), 3);
698 }
699
700 #[test]
701 fn access_tag() {
702 let (i, ret) = parse(input(
703 " Give x another name.
704 @alias myObject
705 @namespace
706 ",
707 ))
708 .unwrap();
709
710 assert_eq!(&*ret.description.value, "Give x another name.");
711 assert_eq!(ret.tags.len(), 2);
712 assert_eq!(&*skip(i), "");
713 }
714
715 #[test]
716 fn abstract_tag() {
717 let (i, ret) = parse_tag_item(input(
718 "
719 * @abstract ",
720 ))
721 .unwrap();
722
723 match ret.tag {
724 Tag::Abstract(..) => {}
725 _ => panic!("Invalid tag: {:?}", ret.tag),
726 }
727
728 assert_eq!(&*skip(i), "");
729 }
730
731 #[test]
732 fn yield_tag_1() {
733 let (i, ret) = parse_tag_item(input(
734 "
735 *
736 * @yields {number} The next number in the Fibonacci sequence.
737 ",
738 ))
739 .unwrap();
740
741 match ret.tag {
742 Tag::Yield(tag) => {
743 assert_eq!(tag.value.as_ref().map(|v| &*v.value), Some("{number}"));
744 assert_eq!(
745 &*tag.description.value,
746 "The next number in the Fibonacci sequence."
747 );
748 }
749 _ => panic!("Invalid tag: {:?}", ret.tag),
750 }
751
752 assert_eq!(&*skip(i), "");
753 }
754
755 #[test]
756 fn trim_1() {
757 assert_eq!(&*trim(input(" foo ")), "foo");
758 assert_eq!(&*trim(input("foo ")), "foo");
759 assert_eq!(&*trim(input(" foo")), "foo");
760 }
761
762 #[test]
763 fn skip_1() {
764 let i = skip(input(
765 "
766 *
767 * @yields ",
768 ));
769
770 assert_eq!(&*i, "@yields ");
771 }
772
773 #[test]
774 fn one_of_1() {
775 let (rest, ret) = parse_one_of(input("foo bar\nbaz"), &["fo", "bar"]).unwrap();
776
777 assert_eq!(&*ret.value, "fo");
778 assert_eq!(&*rest, "o bar\nbaz");
779 }
780
781 #[test]
782 fn line_1() {
783 let (rest, ret) = parse_line(input("foo bar\nbaz")).unwrap();
784
785 assert_eq!(&*ret.value, "foo bar");
786 assert_eq!(&*rest, "baz");
787 }
788
789 #[test]
790 fn word_1() {
791 let (rest, ret) = parse_word(input("foo bar\nbaz")).unwrap();
792
793 assert_eq!(&*ret.value, "foo");
794 assert_eq!(&*rest, "bar\nbaz");
795 }
796}