1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
/* eslint-disable prefer-named-capture-group -- Temporary */
import {
parse as commentParser,
tokenizers
} from 'comment-parser';
import parseInlineTags from './parseInlineTags.js';
const {
name: nameTokenizer,
tag: tagTokenizer,
type: typeTokenizer,
description: descriptionTokenizer
} = tokenizers;
export const hasSeeWithLink = (spec) => {
return spec.tag === 'see' && (/\{@link.+?\}/u).test(spec.source[0].source);
};
export const defaultNoTypes = [
'default', 'defaultvalue', 'description', 'example',
'file', 'fileoverview', 'license',
'overview', 'see', 'summary'
];
export const defaultNoNames = [
'access', 'author',
'default', 'defaultvalue',
'description',
'example', 'exception', 'file', 'fileoverview',
'kind',
'license', 'overview',
'return', 'returns',
'since', 'summary',
'throws',
'version', 'variation'
];
const optionalBrackets = /^\[(?<name>[^=]*)=[^\]]*\]/u;
const preserveTypeTokenizer = typeTokenizer('preserve');
const preserveDescriptionTokenizer = descriptionTokenizer('preserve');
const plainNameTokenizer = nameTokenizer();
const getTokenizers = ({
noTypes = defaultNoTypes,
noNames = defaultNoNames
} = {}) => {
// trim
return [
// Tag
tagTokenizer(),
// Type
(spec) => {
if (noTypes.includes(spec.tag)) {
return spec;
}
return preserveTypeTokenizer(spec);
},
// Name
(spec) => {
if (spec.tag === 'template') {
// const preWS = spec.postTag;
const remainder = spec.source[0].tokens.description;
const pos = remainder.search(/(?<![\s,])\s/u);
let name = pos === -1 ? remainder : remainder.slice(0, pos);
const extra = remainder.slice(pos);
let postName = '', description = '', lineEnd = '';
if (pos > -1) {
[, postName, description, lineEnd] = extra.match(/(\s*)([^\r]*)(\r)?/u);
}
if (optionalBrackets.test(name)) {
name = name.match(optionalBrackets)?.groups?.name;
spec.optional = true;
} else {
spec.optional = false;
}
spec.name = name;
const {tokens} = spec.source[0];
tokens.name = name;
tokens.postName = postName;
tokens.description = description;
tokens.lineEnd = lineEnd || '';
return spec;
}
if (noNames.includes(spec.tag) || hasSeeWithLink(spec)) {
return spec;
}
return plainNameTokenizer(spec);
},
// Description
(spec) => {
return preserveDescriptionTokenizer(spec);
}
];
};
/**
* Accepts a comment token and converts it into `comment-parser` AST.
* @param {PlainObject} commentNode
* @param {string} [indent=""] Whitespace
* @returns {PlainObject}
*/
const parseComment = (commentNode, indent = '') => {
// Preserve JSDoc block start/end indentation.
const [block] = commentParser(`${indent}/*${commentNode.value}*/`, {
// @see https://github.com/yavorskiy/comment-parser/issues/21
tokenizers: getTokenizers()
});
return parseInlineTags(block);
};
export {getTokenizers, parseComment};
|