comment-parser 0.5.4 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{.eslintrc → .eslintrc.json} +0 -0
- package/.travis.yml +4 -4
- package/CHANGELOG.md +14 -0
- package/README.md +22 -7
- package/index.d.ts +152 -4
- package/index.js +26 -23
- package/package.json +18 -11
- package/parser.js +56 -69
- package/parsers.js +19 -17
- package/stringifier.js +57 -0
- package/tests/custom-parsers.spec.js +14 -12
- package/tests/files.spec.js +10 -8
- package/tests/parse.js +4 -2
- package/tests/parse.spec.js +45 -3
- package/tests/stringify.spec.js +80 -0
|
File without changes
|
package/.travis.yml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# v0.6.2
|
|
2
|
+
- document TypeScript definitions
|
|
3
|
+
|
|
4
|
+
# v0.6.1
|
|
5
|
+
- adjust strigifier indentation
|
|
6
|
+
|
|
7
|
+
# v0.6.0
|
|
8
|
+
- soft-drop node@6 support
|
|
9
|
+
- migrate to ES6 syntax
|
|
10
|
+
- allow to generate comments out of parsed data
|
|
11
|
+
|
|
12
|
+
# v0.5.5
|
|
13
|
+
- allow loose tag names, e.g. @.tag, @-tag
|
|
14
|
+
|
|
1
15
|
# v0.5.4
|
|
2
16
|
- allow quoted literal names, e.g. `@tag "My Var" description`
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Module provides `parse(s:String[, opts:Object]):Object` function which takes `/*
|
|
|
9
9
|
|
|
10
10
|
It is not trying to detect relations between tags or somehow recognize their meaning. Any tag can be used, as long as it satisfies the format.
|
|
11
11
|
|
|
12
|
-
```
|
|
12
|
+
```javascript
|
|
13
13
|
/**
|
|
14
14
|
* Singleline or multiline description text. Line breaks are preserved.
|
|
15
15
|
*
|
|
@@ -24,7 +24,7 @@ It is not trying to detect relations between tags or somehow recognize their mea
|
|
|
24
24
|
|
|
25
25
|
this would be parsed into following
|
|
26
26
|
|
|
27
|
-
```
|
|
27
|
+
```json
|
|
28
28
|
[{
|
|
29
29
|
"tags": [{
|
|
30
30
|
"tag": "some-tag",
|
|
@@ -78,7 +78,7 @@ By default dotted names like `name.subname.subsubname` will be expanded into nes
|
|
|
78
78
|
|
|
79
79
|
Below are examples of acceptable comment formats
|
|
80
80
|
|
|
81
|
-
```
|
|
81
|
+
```javascript
|
|
82
82
|
/** online comment */
|
|
83
83
|
|
|
84
84
|
/** first line
|
|
@@ -111,7 +111,7 @@ Each parser function takes string left after previous parsers applied and data p
|
|
|
111
111
|
|
|
112
112
|
Tag node data is build by merging result bits from all parsers. Here is some example that is not doing actual parsing but is demonstrating the flow:
|
|
113
113
|
|
|
114
|
-
```
|
|
114
|
+
```javascript
|
|
115
115
|
/**
|
|
116
116
|
* Source to be parsed below
|
|
117
117
|
* @tag {type} name Description
|
|
@@ -140,12 +140,12 @@ parse(source, {parsers: [
|
|
|
140
140
|
|
|
141
141
|
This would produce following:
|
|
142
142
|
|
|
143
|
-
```
|
|
143
|
+
```json
|
|
144
144
|
[{
|
|
145
145
|
"tags": [{
|
|
146
146
|
"tag": "tag",
|
|
147
147
|
"errors": [
|
|
148
|
-
"check_tag: Unrecognized tag
|
|
148
|
+
"check_tag: Unrecognized tag \"tag\""
|
|
149
149
|
],
|
|
150
150
|
"name": "name2",
|
|
151
151
|
"optional": false,
|
|
@@ -160,6 +160,21 @@ This would produce following:
|
|
|
160
160
|
}]
|
|
161
161
|
```
|
|
162
162
|
|
|
163
|
+
## Stringifying
|
|
164
|
+
|
|
165
|
+
One may also convert `comment-parser` JSON structures back into strings using
|
|
166
|
+
the `stringify` method (`stringify(o:(Object|Array) [, opts:Object]):String`).
|
|
167
|
+
|
|
168
|
+
This method accepts the JSON as its first argument and an optional options
|
|
169
|
+
object with an `indent` property set to either a string or a number that
|
|
170
|
+
will be used to determine the number of spaces of indent. The indent of the
|
|
171
|
+
start of the doc block will be one space less than the indent of each line of
|
|
172
|
+
asterisks for the sake of alignment as per usual practice.
|
|
173
|
+
|
|
174
|
+
The `stringify` export delegates to the specialized methods `stringifyBlocks`,
|
|
175
|
+
`stringifyBlock`, and `stringifyTag`, which are available on the `stringify`
|
|
176
|
+
function object.
|
|
177
|
+
|
|
163
178
|
## Packaging
|
|
164
179
|
|
|
165
180
|
`comment-parser` is CommonJS module and was primarely designed to be used with Node. Module `index.js` includes stream and file functionality. Use prser-only module in browser `comment-parser/parse.js`
|
|
@@ -168,4 +183,4 @@ This would produce following:
|
|
|
168
183
|
|
|
169
184
|
```
|
|
170
185
|
> npm info --registry https://registry.npmjs.org comment-parser contributors
|
|
171
|
-
```
|
|
186
|
+
```
|
package/index.d.ts
CHANGED
|
@@ -2,28 +2,176 @@
|
|
|
2
2
|
// Project: comment-parser
|
|
3
3
|
// Definitions by: Javier "Ciberman" Mora <https://github.com/jhm-ciberman/>
|
|
4
4
|
|
|
5
|
-
declare namespace
|
|
5
|
+
declare namespace parse {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* In case you need to parse tags in a different way you can specify custom parsers here.
|
|
9
|
+
* Each parser function takes string left after previous parsers were applied and the data produced by them.
|
|
10
|
+
* It should return either `undefined` or the new object, where `source` is the consumed substring.
|
|
11
|
+
*/
|
|
12
|
+
export type Parser = (str: string, data: any) => { source: string, data: any };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Represents a parsed doc comment.
|
|
16
|
+
*/
|
|
6
17
|
export interface Comment {
|
|
18
|
+
/**
|
|
19
|
+
* A list of tags that are present in this doc comment.
|
|
20
|
+
*/
|
|
7
21
|
tags: Tag[];
|
|
22
|
+
/**
|
|
23
|
+
* The starting line in the source code of this doc comment.
|
|
24
|
+
*/
|
|
8
25
|
line: number;
|
|
26
|
+
/**
|
|
27
|
+
* The description of this doc comment. Empty string if no description was specified.
|
|
28
|
+
*/
|
|
9
29
|
description: string;
|
|
30
|
+
/**
|
|
31
|
+
* The source of this doc comment, exactly as it appeared in the doc comment before parsing.
|
|
32
|
+
*/
|
|
10
33
|
source: string;
|
|
11
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a parsed tag. A tag has the following format:
|
|
38
|
+
* > @tag {type} [name=default] description
|
|
39
|
+
*/
|
|
12
40
|
export interface Tag {
|
|
41
|
+
/**
|
|
42
|
+
* The tag's kind, eg `param` or `return`.
|
|
43
|
+
*/
|
|
13
44
|
tag: string;
|
|
45
|
+
/**
|
|
46
|
+
* The name of this tag, ie the first word after the tag. Empty string if no name was specified.
|
|
47
|
+
*/
|
|
14
48
|
name: string;
|
|
49
|
+
/**
|
|
50
|
+
* `true` if the tag is optional (tag name enclosed in brackets), `false` otherwise.
|
|
51
|
+
*/
|
|
15
52
|
optional: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The type declaration of this tag that is enclosed in curly braces. Empty string if no type was specified.
|
|
55
|
+
*/
|
|
16
56
|
type: string;
|
|
57
|
+
/**
|
|
58
|
+
* The description of this tag that comes after the name. Empty string if no description was specified.
|
|
59
|
+
*/
|
|
17
60
|
description: string;
|
|
61
|
+
/**
|
|
62
|
+
* The line number where this tag starts
|
|
63
|
+
*/
|
|
18
64
|
line: number;
|
|
65
|
+
/**
|
|
66
|
+
* The source of this tag, exactly as it appeared in the doc comment before parsing.
|
|
67
|
+
*/
|
|
19
68
|
source: string;
|
|
69
|
+
/**
|
|
70
|
+
* The default value for this tag. `undefined` in case no default value was specified.
|
|
71
|
+
*/
|
|
72
|
+
default?: string;
|
|
73
|
+
/**
|
|
74
|
+
* A list of errors that occurred during the parsing of this tag. `undefined` if not error occurred.
|
|
75
|
+
*/
|
|
76
|
+
errors? : string[];
|
|
77
|
+
/**
|
|
78
|
+
* If `dotted_names` was set to `true`, a list of sub tags. `undefined` if `dotted_names` was set to `false` or if
|
|
79
|
+
* no sub tags exist.
|
|
80
|
+
*/
|
|
81
|
+
tags?: Tag[];
|
|
20
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Options for parsing doc comments.
|
|
86
|
+
*/
|
|
21
87
|
export interface Options {
|
|
22
|
-
|
|
23
|
-
|
|
88
|
+
/**
|
|
89
|
+
* In case you need to parse tags in a different way you can specify custom parsers here.
|
|
90
|
+
* Each parser function takes string left after previous parsers were applied and the data produced by them.
|
|
91
|
+
* It should return either `undefined` or the new object, where `source` is the consumed substring.
|
|
92
|
+
*
|
|
93
|
+
* If you specify custom parsers, the default parsers are overwritten, but you can access them via the constant
|
|
94
|
+
* `PARSERS`.
|
|
95
|
+
*/
|
|
96
|
+
parsers: Parser[];
|
|
97
|
+
/**
|
|
98
|
+
* By default dotted names like `name.subname.subsubname` will be expanded into nested sections, this can be
|
|
99
|
+
* prevented by passing `opts.dotted_names = false`.
|
|
100
|
+
*/
|
|
101
|
+
dotted_names: boolean;
|
|
102
|
+
join: string | number | boolean;
|
|
103
|
+
/**
|
|
104
|
+
* `true` to trim whitespace at the start of each line, `false` otherwise.
|
|
105
|
+
*/
|
|
106
|
+
trim: boolean;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Options for turning a parsed doc comment back into a string.
|
|
111
|
+
*/
|
|
112
|
+
export interface StringifyOptions {
|
|
113
|
+
/**
|
|
114
|
+
* Indentation for each line. If a string is passed, that string is used verbatim to indent each line. If a number
|
|
115
|
+
* is passed, indents each line with that many whitespaces.
|
|
116
|
+
*/
|
|
117
|
+
indent: string | number;
|
|
24
118
|
}
|
|
119
|
+
|
|
120
|
+
export interface Stringify {
|
|
121
|
+
/**
|
|
122
|
+
* One may also convert comment-parser JSON structures back into strings using this stringify method.
|
|
123
|
+
*
|
|
124
|
+
* This method accepts the JSON as its first argument and an optional options object with an indent property set to
|
|
125
|
+
* either a string or a number that will be used to determine the number of spaces of indent. The indent of the start
|
|
126
|
+
* of the doc block will be one space less than the indent of each line of asterisks for the sake of alignment as per
|
|
127
|
+
* usual practice.
|
|
128
|
+
*
|
|
129
|
+
* The stringify export delegates to the specialized methods `stringifyBlocks`, `stringifyBlock`, and
|
|
130
|
+
* `stringifyTag`, which are available on the stringify function object.
|
|
131
|
+
* @param comment A tag, comment or a list of comments to stringify.
|
|
132
|
+
* @param options Options to control how the comment or comments are stringified.
|
|
133
|
+
* @return The stringified doc comment(s).
|
|
134
|
+
*/
|
|
135
|
+
(comment: Tag | Comment | Comment[], options?: Partial<StringifyOptions>): string;
|
|
136
|
+
/**
|
|
137
|
+
* Similar to `stringify`, but only accepts a list of doc comments.
|
|
138
|
+
* @param comments A list of comments to stringify.
|
|
139
|
+
* @param options Options to control how the comments are stringified.
|
|
140
|
+
* @return The stringified doc comment(s).
|
|
141
|
+
*/
|
|
142
|
+
stringifyBlocks(comments: Comment[], options?: Partial<StringifyOptions>): string;
|
|
143
|
+
/**
|
|
144
|
+
* Similar to `stringify`, but only accepts a single doc comment.
|
|
145
|
+
* @param comment A comment to stringify.
|
|
146
|
+
* @param options Options to control how the comment is stringified.
|
|
147
|
+
* @return The stringified doc comment(s).
|
|
148
|
+
*/
|
|
149
|
+
stringifyBlock(comment: Comment, options?: Partial<StringifyOptions>): string;
|
|
150
|
+
/**
|
|
151
|
+
* Similar to `stringify`, but only accepts a single tag.
|
|
152
|
+
* @param tag A tag to stringify.
|
|
153
|
+
* @param options Options to control how the tag is stringified.
|
|
154
|
+
* @return The stringified doc comment(s).
|
|
155
|
+
*/
|
|
156
|
+
stringifyTag(tag: Tag, options?: Partial<StringifyOptions>): string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const stringify: parse.Stringify;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The default list of parsers that is used to parse comments.
|
|
163
|
+
*/
|
|
164
|
+
export const PARSERS: Record<"parse_tag" | "parse_type" | "parse_description" | "parse_name", Parser>
|
|
25
165
|
}
|
|
26
166
|
|
|
27
|
-
|
|
167
|
+
/**
|
|
168
|
+
* The main method of this module which takes comment string and returns an array of objects with parsed data.
|
|
169
|
+
* It is not trying to detect relations between tags or somehow recognize their meaning. Any tag can be used, as long
|
|
170
|
+
* as it satisfies the format.
|
|
171
|
+
* @param str A string with doc comments and source code to parse.
|
|
172
|
+
* @param opts Options to control how the source string is parsed.
|
|
173
|
+
* @return The parsed list of doc comments.
|
|
174
|
+
*/
|
|
175
|
+
declare function parse(str: string, opts?: Partial<parse.Options>): [parse.Comment];
|
|
28
176
|
|
|
29
177
|
export = parse;
|
package/index.js
CHANGED
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var util = require('util')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const stream = require('stream')
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
const parse = require('./parser')
|
|
8
|
+
|
|
9
|
+
const stringify = require('./stringifier')
|
|
9
10
|
|
|
10
11
|
module.exports = parse
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
module.exports.stringify = stringify
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
opts = opts || {}
|
|
16
|
-
stream.Transform.call(this, {objectMode: true})
|
|
17
|
-
this._extract = parse.mkextract(opts)
|
|
18
|
-
}
|
|
15
|
+
/* ------- Transform stream ------- */
|
|
19
16
|
|
|
20
|
-
|
|
17
|
+
class Parser extends stream.Transform {
|
|
18
|
+
constructor (opts) {
|
|
19
|
+
opts = opts || {}
|
|
20
|
+
super({ objectMode: true })
|
|
21
|
+
this._extract = parse.mkextract(opts)
|
|
22
|
+
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
_transform (data, encoding, done) {
|
|
25
|
+
let block
|
|
26
|
+
const lines = data.toString().split(/\n/)
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
while (lines.length) {
|
|
29
|
+
block = this._extract(lines.shift())
|
|
30
|
+
if (block) {
|
|
31
|
+
this.push(block)
|
|
32
|
+
}
|
|
30
33
|
}
|
|
31
|
-
}
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
done()
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
module.exports.stream = function stream (opts) {
|
|
@@ -40,15 +43,15 @@ module.exports.stream = function stream (opts) {
|
|
|
40
43
|
/* ------- File parser ------- */
|
|
41
44
|
|
|
42
45
|
module.exports.file = function file (file_path, done) {
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
let opts = {}
|
|
47
|
+
const collected = []
|
|
45
48
|
|
|
46
49
|
if (arguments.length === 3) {
|
|
47
50
|
opts = done
|
|
48
51
|
done = arguments[2]
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
return fs.createReadStream(file_path, {encoding: 'utf8'})
|
|
54
|
+
return fs.createReadStream(file_path, { encoding: 'utf8' })
|
|
52
55
|
.on('error', done)
|
|
53
56
|
.pipe(new Parser(opts))
|
|
54
57
|
.on('error', done)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comment-parser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Generic JSDoc-like comment parser. ",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -10,18 +10,23 @@
|
|
|
10
10
|
"dependencies": {},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"chai": "^4.2.0",
|
|
13
|
-
"eslint": "^
|
|
14
|
-
"eslint-config-standard": "^
|
|
15
|
-
"eslint-plugin-import": "^2.
|
|
16
|
-
"eslint-plugin-node": "^
|
|
17
|
-
"eslint-plugin-promise": "^
|
|
18
|
-
"eslint-plugin-standard": "^
|
|
19
|
-
"mocha": "^
|
|
20
|
-
"nodemon": "^1.
|
|
13
|
+
"eslint": "^6.0.1",
|
|
14
|
+
"eslint-config-standard": "^13.0.1",
|
|
15
|
+
"eslint-plugin-import": "^2.18.0",
|
|
16
|
+
"eslint-plugin-node": "^9.1.0",
|
|
17
|
+
"eslint-plugin-promise": "^4.2.1",
|
|
18
|
+
"eslint-plugin-standard": "^4.0.0",
|
|
19
|
+
"mocha": "^6.1.4",
|
|
20
|
+
"nodemon": "^1.19.1"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">= 6.0.0"
|
|
21
24
|
},
|
|
22
25
|
"scripts": {
|
|
23
|
-
"
|
|
24
|
-
"test": "
|
|
26
|
+
"lint:fix": "eslint --fix .",
|
|
27
|
+
"test:lint": "eslint .",
|
|
28
|
+
"test:unit": "mocha tests",
|
|
29
|
+
"test": "npm run test:lint && npm run test:unit",
|
|
25
30
|
"watch": "nodemon -q -i node_modules -x npm test"
|
|
26
31
|
},
|
|
27
32
|
"repository": {
|
|
@@ -36,6 +41,8 @@
|
|
|
36
41
|
"author": "Sergii Iavorskyi <yavorskiy.s@gmail.com> (https://github.com/yavorskiy)",
|
|
37
42
|
"contributors": [
|
|
38
43
|
"Alexej Yaroshevich (https://github.com/zxqfox)",
|
|
44
|
+
"Andre Wachsmuth (https://github.com/blutorange)",
|
|
45
|
+
"Brett Zamir (https://github.com/brettz9)",
|
|
39
46
|
"Dieter Oberkofler (https://github.com/doberkofler)",
|
|
40
47
|
"Evgeny Reznichenko (https://github.com/zxcabs)",
|
|
41
48
|
"Javier \"Ciberma\" Mora (https://github.com/jhm-ciberman)",
|
package/parser.js
CHANGED
|
@@ -1,36 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
'use strict'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
var MARKER_START_SKIP = '/***'
|
|
6
|
-
var MARKER_END = '*/'
|
|
4
|
+
const PARSERS = require('./parsers')
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var k, obj
|
|
12
|
-
var res = {}
|
|
13
|
-
var objs = Array.prototype.slice.call(arguments)
|
|
6
|
+
const MARKER_START = '/**'
|
|
7
|
+
const MARKER_START_SKIP = '/***'
|
|
8
|
+
const MARKER_END = '*/'
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
obj = objs[i]
|
|
17
|
-
for (k in obj) {
|
|
18
|
-
if (obj.hasOwnProperty(k)) {
|
|
19
|
-
res[k] = obj[k]
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return res
|
|
24
|
-
}
|
|
10
|
+
/* ------- util functions ------- */
|
|
25
11
|
|
|
26
12
|
function find (list, filter) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
var matchs = true
|
|
13
|
+
let i = list.length
|
|
14
|
+
let matchs = true
|
|
30
15
|
|
|
31
16
|
while (i--) {
|
|
32
|
-
for (k in filter) {
|
|
33
|
-
if (
|
|
17
|
+
for (const k in filter) {
|
|
18
|
+
if ({}.hasOwnProperty.call(filter, k)) {
|
|
34
19
|
matchs = (filter[k] === list[i][k]) && matchs
|
|
35
20
|
}
|
|
36
21
|
}
|
|
@@ -44,17 +29,17 @@ function find (list, filter) {
|
|
|
44
29
|
/**
|
|
45
30
|
* Parses "@tag {type} name description"
|
|
46
31
|
* @param {string} str Raw doc string
|
|
47
|
-
* @param {Array
|
|
32
|
+
* @param {Array<function>} parsers Array of parsers to be applied to the source
|
|
48
33
|
* @returns {object} parsed tag node
|
|
49
34
|
*/
|
|
50
35
|
function parse_tag (str, parsers) {
|
|
51
36
|
if (typeof str !== 'string' || str[0] !== '@') { return null }
|
|
52
37
|
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
const data = parsers.reduce(function (state, parser) {
|
|
39
|
+
let result
|
|
55
40
|
|
|
56
41
|
try {
|
|
57
|
-
result = parser(state.source,
|
|
42
|
+
result = parser(state.source, Object.assign({}, state.data))
|
|
58
43
|
} catch (err) {
|
|
59
44
|
state.data.errors = (state.data.errors || [])
|
|
60
45
|
.concat(parser.name + ': ' + err.message)
|
|
@@ -62,7 +47,7 @@ function parse_tag (str, parsers) {
|
|
|
62
47
|
|
|
63
48
|
if (result) {
|
|
64
49
|
state.source = state.source.slice(result.source.length)
|
|
65
|
-
state.data =
|
|
50
|
+
state.data = Object.assign(state.data, result.data)
|
|
66
51
|
}
|
|
67
52
|
|
|
68
53
|
return state
|
|
@@ -83,17 +68,17 @@ function parse_tag (str, parsers) {
|
|
|
83
68
|
* Parses comment block (array of String lines)
|
|
84
69
|
*/
|
|
85
70
|
function parse_block (source, opts) {
|
|
86
|
-
|
|
71
|
+
const trim = opts.trim
|
|
87
72
|
? function trim (s) { return s.trim() }
|
|
88
73
|
: function trim (s) { return s }
|
|
89
74
|
|
|
90
|
-
|
|
91
|
-
.map(
|
|
75
|
+
let source_str = source
|
|
76
|
+
.map((line) => { return trim(line.source) })
|
|
92
77
|
.join('\n')
|
|
93
78
|
|
|
94
79
|
source_str = trim(source_str)
|
|
95
80
|
|
|
96
|
-
|
|
81
|
+
const start = source[0].number
|
|
97
82
|
|
|
98
83
|
// merge source lines into tags
|
|
99
84
|
// we assume tag starts with "@"
|
|
@@ -101,13 +86,16 @@ function parse_block (source, opts) {
|
|
|
101
86
|
.reduce(function (tags, line) {
|
|
102
87
|
line.source = trim(line.source)
|
|
103
88
|
|
|
104
|
-
if (line.source.match(/^\s*@(\
|
|
105
|
-
tags.push({
|
|
89
|
+
if (line.source.match(/^\s*@(\S+)/)) {
|
|
90
|
+
tags.push({
|
|
91
|
+
source: [line.source],
|
|
92
|
+
line: line.number
|
|
93
|
+
})
|
|
106
94
|
} else {
|
|
107
|
-
|
|
95
|
+
const tag = tags[tags.length - 1]
|
|
108
96
|
if (opts.join !== undefined && opts.join !== false && opts.join !== 0 &&
|
|
109
97
|
!line.startWithStar && tag.source.length > 0) {
|
|
110
|
-
|
|
98
|
+
let source
|
|
111
99
|
if (typeof opts.join === 'string') {
|
|
112
100
|
source = opts.join + line.source.replace(/^\s+/, '')
|
|
113
101
|
} else if (typeof opts.join === 'number') {
|
|
@@ -122,33 +110,33 @@ function parse_block (source, opts) {
|
|
|
122
110
|
}
|
|
123
111
|
|
|
124
112
|
return tags
|
|
125
|
-
}, [{source: []}])
|
|
126
|
-
.map(
|
|
113
|
+
}, [{ source: [] }])
|
|
114
|
+
.map((tag) => {
|
|
127
115
|
tag.source = trim(tag.source.join('\n'))
|
|
128
116
|
return tag
|
|
129
117
|
})
|
|
130
118
|
|
|
131
119
|
// Block description
|
|
132
|
-
|
|
120
|
+
const description = source.shift()
|
|
133
121
|
|
|
134
122
|
// skip if no descriptions and no tags
|
|
135
123
|
if (description.source === '' && source.length === 0) {
|
|
136
124
|
return null
|
|
137
125
|
}
|
|
138
126
|
|
|
139
|
-
|
|
140
|
-
|
|
127
|
+
const tags = source.reduce(function (tags, tag) {
|
|
128
|
+
const tag_node = parse_tag(tag.source, opts.parsers)
|
|
141
129
|
|
|
142
130
|
if (!tag_node) { return tags }
|
|
143
131
|
|
|
144
132
|
tag_node.line = tag.line
|
|
145
133
|
tag_node.source = tag.source
|
|
146
134
|
|
|
147
|
-
if (opts.dotted_names && tag_node.name.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
if (opts.dotted_names && tag_node.name.includes('.')) {
|
|
136
|
+
let parent_name
|
|
137
|
+
let parent_tag
|
|
138
|
+
let parent_tags = tags
|
|
139
|
+
const parts = tag_node.name.split('.')
|
|
152
140
|
|
|
153
141
|
while (parts.length > 1) {
|
|
154
142
|
parent_name = parts.shift()
|
|
@@ -181,7 +169,7 @@ function parse_block (source, opts) {
|
|
|
181
169
|
}, [])
|
|
182
170
|
|
|
183
171
|
return {
|
|
184
|
-
tags
|
|
172
|
+
tags,
|
|
185
173
|
line: start,
|
|
186
174
|
description: description.source,
|
|
187
175
|
source: source_str
|
|
@@ -192,11 +180,11 @@ function parse_block (source, opts) {
|
|
|
192
180
|
* Produces `extract` function with internal state initialized
|
|
193
181
|
*/
|
|
194
182
|
function mkextract (opts) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
183
|
+
let chunk = null
|
|
184
|
+
let indent = 0
|
|
185
|
+
let number = 0
|
|
198
186
|
|
|
199
|
-
opts =
|
|
187
|
+
opts = Object.assign({}, {
|
|
200
188
|
trim: true,
|
|
201
189
|
dotted_names: false,
|
|
202
190
|
parsers: [
|
|
@@ -212,11 +200,11 @@ function mkextract (opts) {
|
|
|
212
200
|
* Return parsed block once fullfilled or null otherwise
|
|
213
201
|
*/
|
|
214
202
|
return function extract (line) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
let result = null
|
|
204
|
+
const startPos = line.indexOf(MARKER_START)
|
|
205
|
+
const endPos = line.indexOf(MARKER_END)
|
|
218
206
|
|
|
219
|
-
// if open marker detected and it's not skip one
|
|
207
|
+
// if open marker detected and it's not, skip one
|
|
220
208
|
if (startPos !== -1 && line.indexOf(MARKER_START_SKIP) !== startPos) {
|
|
221
209
|
chunk = []
|
|
222
210
|
indent = startPos + MARKER_START.length
|
|
@@ -224,12 +212,12 @@ function mkextract (opts) {
|
|
|
224
212
|
|
|
225
213
|
// if we are on middle of comment block
|
|
226
214
|
if (chunk) {
|
|
227
|
-
|
|
228
|
-
|
|
215
|
+
let lineStart = indent
|
|
216
|
+
let startWithStar = false
|
|
229
217
|
|
|
230
218
|
// figure out if we slice from opening marker pos
|
|
231
219
|
// or line start is shifted to the left
|
|
232
|
-
|
|
220
|
+
const nonSpaceChar = line.match(/\S/)
|
|
233
221
|
|
|
234
222
|
// skip for the first line starting with /** (fresh chunk)
|
|
235
223
|
// it always has the right indentation
|
|
@@ -244,8 +232,8 @@ function mkextract (opts) {
|
|
|
244
232
|
|
|
245
233
|
// slice the line until end or until closing marker start
|
|
246
234
|
chunk.push({
|
|
247
|
-
number
|
|
248
|
-
startWithStar
|
|
235
|
+
number,
|
|
236
|
+
startWithStar,
|
|
249
237
|
source: line.slice(lineStart, endPos === -1 ? line.length : endPos)
|
|
250
238
|
})
|
|
251
239
|
|
|
@@ -265,17 +253,16 @@ function mkextract (opts) {
|
|
|
265
253
|
/* ------- Public API ------- */
|
|
266
254
|
|
|
267
255
|
module.exports = function parse (source, opts) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
var lines = source.split(/\n/)
|
|
256
|
+
const blocks = []
|
|
257
|
+
const extract = mkextract(opts)
|
|
258
|
+
const lines = source.split(/\n/)
|
|
272
259
|
|
|
273
|
-
|
|
274
|
-
block = extract(
|
|
260
|
+
lines.forEach((line) => {
|
|
261
|
+
const block = extract(line)
|
|
275
262
|
if (block) {
|
|
276
263
|
blocks.push(block)
|
|
277
264
|
}
|
|
278
|
-
}
|
|
265
|
+
})
|
|
279
266
|
|
|
280
267
|
return blocks
|
|
281
268
|
}
|
package/parsers.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
1
3
|
function skipws (str) {
|
|
2
|
-
|
|
4
|
+
let i = 0
|
|
3
5
|
do {
|
|
4
6
|
if (str[i] !== ' ' && str[i] !== '\t') { return i }
|
|
5
7
|
} while (++i < str.length)
|
|
@@ -8,25 +10,25 @@ function skipws (str) {
|
|
|
8
10
|
|
|
9
11
|
/* ------- default parsers ------- */
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
const PARSERS = {}
|
|
12
14
|
|
|
13
15
|
PARSERS.parse_tag = function parse_tag (str) {
|
|
14
|
-
|
|
16
|
+
const result = str.match(/^\s*@(\S+)/)
|
|
15
17
|
|
|
16
18
|
if (!result) { throw new Error('Invalid `@tag`, missing @ symbol') }
|
|
17
19
|
|
|
18
20
|
return {
|
|
19
21
|
source: result[0],
|
|
20
|
-
data: {tag: result[1]}
|
|
22
|
+
data: { tag: result[1] }
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
PARSERS.parse_type = function parse_type (str, data) {
|
|
25
27
|
if (data.errors && data.errors.length) { return null }
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
let pos = skipws(str)
|
|
30
|
+
let res = ''
|
|
31
|
+
let curlies = 0
|
|
30
32
|
|
|
31
33
|
if (str[pos] !== '{') { return null }
|
|
32
34
|
|
|
@@ -41,20 +43,20 @@ PARSERS.parse_type = function parse_type (str, data) {
|
|
|
41
43
|
|
|
42
44
|
return {
|
|
43
45
|
source: str.slice(0, pos),
|
|
44
|
-
data: {type: res.slice(1, -1)}
|
|
46
|
+
data: { type: res.slice(1, -1) }
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
PARSERS.parse_name = function parse_name (str, data) {
|
|
49
51
|
if (data.errors && data.errors.length) { return null }
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
let pos = skipws(str)
|
|
54
|
+
let name = ''
|
|
55
|
+
let brackets = 0
|
|
56
|
+
let res = { optional: false }
|
|
55
57
|
|
|
56
58
|
// if it starts with quoted group assume it is a literal
|
|
57
|
-
|
|
59
|
+
const quotedGroups = str.slice(pos).split('"')
|
|
58
60
|
if (quotedGroups.length > 1 && quotedGroups[0] === '' && quotedGroups.length % 2 === 1) {
|
|
59
61
|
name = quotedGroups[1]
|
|
60
62
|
pos += name.length + 2
|
|
@@ -69,14 +71,14 @@ PARSERS.parse_name = function parse_name (str, data) {
|
|
|
69
71
|
|
|
70
72
|
if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets') }
|
|
71
73
|
|
|
72
|
-
res = {name: name, optional: false}
|
|
74
|
+
res = { name: name, optional: false }
|
|
73
75
|
|
|
74
76
|
if (name[0] === '[' && name[name.length - 1] === ']') {
|
|
75
77
|
res.optional = true
|
|
76
78
|
name = name.slice(1, -1)
|
|
77
79
|
|
|
78
80
|
if (name.indexOf('=') !== -1) {
|
|
79
|
-
|
|
81
|
+
const parts = name.split('=')
|
|
80
82
|
name = parts[0]
|
|
81
83
|
res.default = parts[1].replace(/^(["'])(.+)(\1)$/, '$2')
|
|
82
84
|
}
|
|
@@ -94,12 +96,12 @@ PARSERS.parse_name = function parse_name (str, data) {
|
|
|
94
96
|
PARSERS.parse_description = function parse_description (str, data) {
|
|
95
97
|
if (data.errors && data.errors.length) { return null }
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
const result = str.match(/^\s+((.|\s)+)?/)
|
|
98
100
|
|
|
99
101
|
if (result) {
|
|
100
102
|
return {
|
|
101
103
|
source: result[0],
|
|
102
|
-
data: {description: result[1] === undefined ? '' : result[1]}
|
|
104
|
+
data: { description: result[1] === undefined ? '' : result[1] }
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
package/stringifier.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const getIndent = (indent) => {
|
|
4
|
+
return typeof indent === 'number' ? ' '.repeat(indent) : indent
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = exports = function stringify (arg, opts) {
|
|
8
|
+
if (Array.isArray(arg)) {
|
|
9
|
+
return stringifyBlocks(arg, opts)
|
|
10
|
+
}
|
|
11
|
+
if (arg && typeof arg === 'object') {
|
|
12
|
+
if ('tag' in arg) {
|
|
13
|
+
return stringifyTag(arg, opts)
|
|
14
|
+
}
|
|
15
|
+
if ('tags' in arg) {
|
|
16
|
+
return stringifyBlock(arg, opts)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
throw new TypeError('Unexpected argument passed to `stringify`.')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const stringifyBlocks = exports.stringifyBlocks = function stringifyBlocks (
|
|
23
|
+
blocks, { indent = '' } = {}
|
|
24
|
+
) {
|
|
25
|
+
const indnt = getIndent(indent)
|
|
26
|
+
return blocks.reduce((s, block) => {
|
|
27
|
+
return s + stringifyBlock(block, { indent })
|
|
28
|
+
}, (indnt ? indnt.slice(0, -1) : '') + '/**\n') + indnt + '*/'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const stringifyBlock = exports.stringifyBlock = function stringifyBlock (
|
|
32
|
+
block, { indent = '' } = {}
|
|
33
|
+
) {
|
|
34
|
+
// block.line
|
|
35
|
+
const indnt = getIndent(indent)
|
|
36
|
+
return (block.description ? `${indnt}* ${block.description}\n${indnt}*\n` : '') +
|
|
37
|
+
block.tags.reduce((s, tag) => {
|
|
38
|
+
return s + stringifyTag(tag, { indent })
|
|
39
|
+
}, '')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const stringifyTag = exports.stringifyTag = function stringifyTag (
|
|
43
|
+
tag, { indent = '' } = {}
|
|
44
|
+
) {
|
|
45
|
+
const indnt = getIndent(indent)
|
|
46
|
+
const {
|
|
47
|
+
type, name, optional, description, tag: tagName, default: deflt //, line , source
|
|
48
|
+
} = tag
|
|
49
|
+
return indnt + `* @${tagName}` +
|
|
50
|
+
(type ? ` {${type}}` : '') +
|
|
51
|
+
(name ? ` ${
|
|
52
|
+
optional ? '[' : ''
|
|
53
|
+
}${name}${deflt ? `=${deflt}` : ''}${
|
|
54
|
+
optional ? ']' : ''
|
|
55
|
+
}` : '') +
|
|
56
|
+
(description ? ` ${description.replace(/\n/g, '\n' + indnt + '* ')}` : '') + '\n'
|
|
57
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* eslint no-unused-vars:off */
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const { expect } = require('chai')
|
|
5
|
+
const parse = require('./parse')
|
|
4
6
|
|
|
5
7
|
describe('parse() with custom tag parsers', function () {
|
|
6
8
|
function sample () {
|
|
@@ -11,7 +13,7 @@ describe('parse() with custom tag parsers', function () {
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
it('should use `opts.parsers`', function () {
|
|
14
|
-
|
|
16
|
+
const parsers = [
|
|
15
17
|
function everything (str) {
|
|
16
18
|
return {
|
|
17
19
|
source: str,
|
|
@@ -26,7 +28,7 @@ describe('parse() with custom tag parsers', function () {
|
|
|
26
28
|
}
|
|
27
29
|
]
|
|
28
30
|
|
|
29
|
-
expect(parse(sample, {parsers: parsers})[0])
|
|
31
|
+
expect(parse(sample, { parsers: parsers })[0])
|
|
30
32
|
.to.eql({
|
|
31
33
|
line: 1,
|
|
32
34
|
description: '',
|
|
@@ -44,17 +46,17 @@ describe('parse() with custom tag parsers', function () {
|
|
|
44
46
|
})
|
|
45
47
|
|
|
46
48
|
it('should merge parsers result', function () {
|
|
47
|
-
|
|
49
|
+
const parsers = [
|
|
48
50
|
function parser1 (str) {
|
|
49
51
|
return {
|
|
50
52
|
source: '',
|
|
51
|
-
data: {tag: 'tag'}
|
|
53
|
+
data: { tag: 'tag' }
|
|
52
54
|
}
|
|
53
55
|
},
|
|
54
56
|
function parser2 (str) {
|
|
55
57
|
return {
|
|
56
58
|
source: '',
|
|
57
|
-
data: {type: 'type'}
|
|
59
|
+
data: { type: 'type' }
|
|
58
60
|
}
|
|
59
61
|
},
|
|
60
62
|
function parser3 (str) {
|
|
@@ -68,7 +70,7 @@ describe('parse() with custom tag parsers', function () {
|
|
|
68
70
|
}
|
|
69
71
|
]
|
|
70
72
|
|
|
71
|
-
expect(parse(sample, {parsers: parsers})[0])
|
|
73
|
+
expect(parse(sample, { parsers: parsers })[0])
|
|
72
74
|
.to.eql({
|
|
73
75
|
line: 1,
|
|
74
76
|
description: '',
|
|
@@ -86,11 +88,11 @@ describe('parse() with custom tag parsers', function () {
|
|
|
86
88
|
})
|
|
87
89
|
|
|
88
90
|
it('should catch parser exceptions and populate `errors` field', function () {
|
|
89
|
-
|
|
91
|
+
const parsers = [
|
|
90
92
|
function parser1 (str) {
|
|
91
93
|
return {
|
|
92
94
|
source: '',
|
|
93
|
-
data: {tag: 'tag'}
|
|
95
|
+
data: { tag: 'tag' }
|
|
94
96
|
}
|
|
95
97
|
},
|
|
96
98
|
function parser2 (str) {
|
|
@@ -102,12 +104,12 @@ describe('parse() with custom tag parsers', function () {
|
|
|
102
104
|
function parser4 (str) {
|
|
103
105
|
return {
|
|
104
106
|
source: '',
|
|
105
|
-
data: {name: 'name'}
|
|
107
|
+
data: { name: 'name' }
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
]
|
|
109
111
|
|
|
110
|
-
expect(parse(sample, {parsers: parsers})[0])
|
|
112
|
+
expect(parse(sample, { parsers: parsers })[0])
|
|
111
113
|
.to.eql({
|
|
112
114
|
line: 1,
|
|
113
115
|
description: '',
|
package/tests/files.spec.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* eslint no-unused-vars:off */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const stream = require('readable-stream')
|
|
7
|
+
const { expect } = require('chai')
|
|
8
|
+
const parse = require('../index')
|
|
7
9
|
|
|
8
10
|
describe('File parsing', function () {
|
|
9
11
|
it('should parse the file by path', function (done) {
|
|
@@ -41,11 +43,11 @@ describe('File parsing', function () {
|
|
|
41
43
|
})
|
|
42
44
|
|
|
43
45
|
it('should return `Transform` stream', function (done) {
|
|
44
|
-
|
|
46
|
+
let count = 0
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
const readable = fs.createReadStream(path.resolve(__dirname, 'fixtures/sample.js'), { encoding: 'utf8' })
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
const writable = new stream.Writable({ objectMode: true })
|
|
49
51
|
writable._write = function (data, encoding, done) {
|
|
50
52
|
count++
|
|
51
53
|
done()
|
package/tests/parse.js
CHANGED
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
'use strict'
|
|
11
|
+
|
|
12
|
+
const parse = require('../index')
|
|
11
13
|
|
|
12
14
|
module.exports = function (func, opts) {
|
|
13
|
-
|
|
15
|
+
let str = func.toString()
|
|
14
16
|
str = str
|
|
15
17
|
.slice(str.indexOf('{') + 1, str.lastIndexOf('}'))
|
|
16
18
|
.replace(/\r\n/g, '\n')
|
package/tests/parse.spec.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* eslint no-unused-vars:off */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
'use strict'
|
|
4
|
+
const { expect } = require('chai')
|
|
5
|
+
const parse = require('./parse')
|
|
5
6
|
|
|
6
7
|
describe('Comment string parsing', function () {
|
|
7
8
|
it('should parse doc block with description', function () {
|
|
@@ -145,7 +146,7 @@ describe('Comment string parsing', function () {
|
|
|
145
146
|
})
|
|
146
147
|
|
|
147
148
|
it('should parse multiple doc blocks', function () {
|
|
148
|
-
|
|
149
|
+
const p = parse(function () {
|
|
149
150
|
/**
|
|
150
151
|
* Description first line
|
|
151
152
|
*/
|
|
@@ -459,6 +460,47 @@ describe('Comment string parsing', function () {
|
|
|
459
460
|
})
|
|
460
461
|
})
|
|
461
462
|
|
|
463
|
+
it('should tolerate loose tag names', function () {
|
|
464
|
+
expect(parse(function () {
|
|
465
|
+
/**
|
|
466
|
+
Description text
|
|
467
|
+
@.tag0 tagname Tag 0 description
|
|
468
|
+
@-tag1 tagname Tag 1 description
|
|
469
|
+
@+tag2 tagname Tag 2 description
|
|
470
|
+
*/
|
|
471
|
+
})[0])
|
|
472
|
+
.eql({
|
|
473
|
+
description: 'Description text',
|
|
474
|
+
source: 'Description text\n@.tag0 tagname Tag 0 description\n@-tag1 tagname Tag 1 description\n@+tag2 tagname Tag 2 description',
|
|
475
|
+
line: 1,
|
|
476
|
+
tags: [{
|
|
477
|
+
tag: '.tag0',
|
|
478
|
+
name: 'tagname',
|
|
479
|
+
optional: false,
|
|
480
|
+
description: 'Tag 0 description',
|
|
481
|
+
type: '',
|
|
482
|
+
line: 3,
|
|
483
|
+
source: '@.tag0 tagname Tag 0 description'
|
|
484
|
+
}, {
|
|
485
|
+
tag: '-tag1',
|
|
486
|
+
name: 'tagname',
|
|
487
|
+
optional: false,
|
|
488
|
+
description: 'Tag 1 description',
|
|
489
|
+
type: '',
|
|
490
|
+
line: 4,
|
|
491
|
+
source: '@-tag1 tagname Tag 1 description'
|
|
492
|
+
}, {
|
|
493
|
+
tag: '+tag2',
|
|
494
|
+
name: 'tagname',
|
|
495
|
+
optional: false,
|
|
496
|
+
description: 'Tag 2 description',
|
|
497
|
+
type: '',
|
|
498
|
+
line: 5,
|
|
499
|
+
source: '@+tag2 tagname Tag 2 description'
|
|
500
|
+
}]
|
|
501
|
+
})
|
|
502
|
+
})
|
|
503
|
+
|
|
462
504
|
it('should tolerate default value with whitespces `@tag {my.type} [name=John Doe]`', function () {
|
|
463
505
|
expect(parse(function () {
|
|
464
506
|
/**
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { expect } = require('chai')
|
|
4
|
+
|
|
5
|
+
const parser = require('../')
|
|
6
|
+
|
|
7
|
+
describe('Comment stringifying', function () {
|
|
8
|
+
it('should stringify doc block with description', function () {
|
|
9
|
+
const expected = `/**
|
|
10
|
+
* Singleline or multiline description text. Line breaks are preserved.
|
|
11
|
+
*
|
|
12
|
+
* @some-tag {Type} name Singleline or multiline description text
|
|
13
|
+
* @some-tag {Type} name.subname Singleline or multiline description text
|
|
14
|
+
* @some-tag {Type} name.subname.subsubname Singleline or
|
|
15
|
+
* multiline description text
|
|
16
|
+
* @some-tag {Type} [optionalName=someDefault]
|
|
17
|
+
* @another-tag
|
|
18
|
+
*/`
|
|
19
|
+
const parsed = parser(expected)
|
|
20
|
+
|
|
21
|
+
expect(parsed).to.be.an('array')
|
|
22
|
+
|
|
23
|
+
const stringified = parser.stringify(parsed)
|
|
24
|
+
|
|
25
|
+
expect(stringified).to.eq(expected)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should stringify indented doc block with description', function () {
|
|
29
|
+
const expected = ` /**
|
|
30
|
+
* Singleline or multiline description text. Line breaks are preserved.
|
|
31
|
+
*
|
|
32
|
+
* @some-tag {Type} name Singleline or multiline description text
|
|
33
|
+
* @some-tag {Type} name.subname Singleline or multiline description text
|
|
34
|
+
* @some-tag {Type} name.subname.subsubname Singleline or
|
|
35
|
+
* multiline description text
|
|
36
|
+
* @some-tag {Type} [optionalName=someDefault]
|
|
37
|
+
* @another-tag
|
|
38
|
+
*/`
|
|
39
|
+
const parsed = parser(expected)
|
|
40
|
+
|
|
41
|
+
expect(parsed).to.be.an('array')
|
|
42
|
+
|
|
43
|
+
const stringified = parser.stringify(parsed, { indent: ' ' })
|
|
44
|
+
|
|
45
|
+
expect(stringified).to.eq(expected)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should stringify numeric indented doc block with description', function () {
|
|
49
|
+
const expected = ` /**
|
|
50
|
+
* Singleline or multiline description text. Line breaks are preserved.
|
|
51
|
+
*
|
|
52
|
+
* @some-tag {Type} name Singleline or multiline description text
|
|
53
|
+
* @some-tag {Type} name.subname Singleline or multiline description text
|
|
54
|
+
* @some-tag {Type} name.subname.subsubname Singleline or
|
|
55
|
+
* multiline description text
|
|
56
|
+
* @some-tag {Type} [optionalName=someDefault]
|
|
57
|
+
* @another-tag
|
|
58
|
+
*/`
|
|
59
|
+
const parsed = parser(expected)
|
|
60
|
+
|
|
61
|
+
expect(parsed).to.be.an('array')
|
|
62
|
+
|
|
63
|
+
const stringified = parser.stringify(parsed, { indent: 4 })
|
|
64
|
+
|
|
65
|
+
expect(stringified).to.eq(expected)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should stringify numeric indented doc block without description', function () {
|
|
69
|
+
const expected = ` /**
|
|
70
|
+
* @param Foo
|
|
71
|
+
*/`
|
|
72
|
+
const parsed = parser(expected)
|
|
73
|
+
|
|
74
|
+
expect(parsed).to.be.an('array')
|
|
75
|
+
|
|
76
|
+
const stringified = parser.stringify(parsed, { indent: 4 })
|
|
77
|
+
|
|
78
|
+
expect(stringified).to.eq(expected)
|
|
79
|
+
})
|
|
80
|
+
})
|