comment-parser 0.5.3 → 0.6.1
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.js +26 -23
- package/package.json +17 -11
- package/parser.js +56 -69
- package/parsers.js +40 -29
- package/stringifier.js +57 -0
- package/tests/custom-parsers.spec.js +14 -12
- package/tests/files.spec.js +10 -8
- package/tests/parse.js +8 -6
- package/tests/parse.spec.js +133 -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.1
|
|
2
|
+
- adjust strigifier indentation
|
|
3
|
+
|
|
4
|
+
# v0.6.0
|
|
5
|
+
- soft-drop node@6 support
|
|
6
|
+
- migrate to ES6 syntax
|
|
7
|
+
- allow to generate comments out of parsed data
|
|
8
|
+
|
|
9
|
+
# v0.5.5
|
|
10
|
+
- allow loose tag names, e.g. @.tag, @-tag
|
|
11
|
+
|
|
12
|
+
# v0.5.4
|
|
13
|
+
- allow quoted literal names, e.g. `@tag "My Var" description`
|
|
14
|
+
|
|
1
15
|
# v0.5.3
|
|
2
16
|
- corrected TypeScript definitions
|
|
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.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.1",
|
|
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,7 @@
|
|
|
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
|
+
"Brett Zamir (https://github.com/brettz9)",
|
|
39
45
|
"Dieter Oberkofler (https://github.com/doberkofler)",
|
|
40
46
|
"Evgeny Reznichenko (https://github.com/zxcabs)",
|
|
41
47
|
"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,36 +43,45 @@ 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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
let pos = skipws(str)
|
|
54
|
+
let name = ''
|
|
55
|
+
let brackets = 0
|
|
56
|
+
let res = { optional: false }
|
|
57
|
+
|
|
58
|
+
// if it starts with quoted group assume it is a literal
|
|
59
|
+
const quotedGroups = str.slice(pos).split('"')
|
|
60
|
+
if (quotedGroups.length > 1 && quotedGroups[0] === '' && quotedGroups.length % 2 === 1) {
|
|
61
|
+
name = quotedGroups[1]
|
|
62
|
+
pos += name.length + 2
|
|
63
|
+
// assume name is non-space string or anything wrapped into brackets
|
|
64
|
+
} else {
|
|
65
|
+
while (pos < str.length) {
|
|
66
|
+
brackets += (str[pos] === '[' ? 1 : (str[pos] === ']' ? -1 : 0))
|
|
67
|
+
name += str[pos]
|
|
68
|
+
pos++
|
|
69
|
+
if (brackets === 0 && /\s/.test(str[pos])) { break }
|
|
70
|
+
}
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets') }
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
res = { name: name, optional: false }
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
if (name[0] === '[' && name[name.length - 1] === ']') {
|
|
77
|
+
res.optional = true
|
|
78
|
+
name = name.slice(1, -1)
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
if (name.indexOf('=') !== -1) {
|
|
81
|
+
const parts = name.split('=')
|
|
82
|
+
name = parts[0]
|
|
83
|
+
res.default = parts[1].replace(/^(["'])(.+)(\1)$/, '$2')
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
}
|
|
76
87
|
|
|
@@ -85,12 +96,12 @@ PARSERS.parse_name = function parse_name (str, data) {
|
|
|
85
96
|
PARSERS.parse_description = function parse_description (str, data) {
|
|
86
97
|
if (data.errors && data.errors.length) { return null }
|
|
87
98
|
|
|
88
|
-
|
|
99
|
+
const result = str.match(/^\s+((.|\s)+)?/)
|
|
89
100
|
|
|
90
101
|
if (result) {
|
|
91
102
|
return {
|
|
92
103
|
source: result[0],
|
|
93
|
-
data: {description: result[1] === undefined ? '' : result[1]}
|
|
104
|
+
data: { description: result[1] === undefined ? '' : result[1] }
|
|
94
105
|
}
|
|
95
106
|
}
|
|
96
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,12 +7,14 @@
|
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
str.indexOf('{') + 1,
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
let str = func.toString()
|
|
16
|
+
str = str
|
|
17
|
+
.slice(str.indexOf('{') + 1, str.lastIndexOf('}'))
|
|
18
|
+
.replace(/\r\n/g, '\n')
|
|
19
|
+
return parse(str, opts)
|
|
18
20
|
}
|
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
|
/**
|
|
@@ -950,4 +992,92 @@ describe('Comment string parsing', function () {
|
|
|
950
992
|
}]
|
|
951
993
|
})
|
|
952
994
|
})
|
|
995
|
+
|
|
996
|
+
it('should keep optional names spaces (issue #41)`', function () {
|
|
997
|
+
expect(parse(function () {
|
|
998
|
+
/**
|
|
999
|
+
* @section [Brand Colors] Here you can find all the brand colors
|
|
1000
|
+
*/
|
|
1001
|
+
})[0])
|
|
1002
|
+
.to.eql({
|
|
1003
|
+
line: 1,
|
|
1004
|
+
source: '@section [Brand Colors] Here you can find all the brand colors',
|
|
1005
|
+
description: '',
|
|
1006
|
+
tags: [{
|
|
1007
|
+
tag: 'section',
|
|
1008
|
+
line: 2,
|
|
1009
|
+
type: '',
|
|
1010
|
+
name: 'Brand Colors',
|
|
1011
|
+
source: '@section [Brand Colors] Here you can find all the brand colors',
|
|
1012
|
+
optional: true,
|
|
1013
|
+
description: 'Here you can find all the brand colors'
|
|
1014
|
+
}]
|
|
1015
|
+
})
|
|
1016
|
+
})
|
|
1017
|
+
|
|
1018
|
+
it('should keep quotes in description (issue #41)`', function () {
|
|
1019
|
+
expect(parse(function () {
|
|
1020
|
+
/**
|
|
1021
|
+
* @section "Brand Colors" Here you can find all the brand colors
|
|
1022
|
+
*/
|
|
1023
|
+
})[0])
|
|
1024
|
+
.to.eql({
|
|
1025
|
+
line: 1,
|
|
1026
|
+
source: '@section "Brand Colors" Here you can find all the brand colors',
|
|
1027
|
+
description: '',
|
|
1028
|
+
tags: [{
|
|
1029
|
+
tag: 'section',
|
|
1030
|
+
line: 2,
|
|
1031
|
+
type: '',
|
|
1032
|
+
name: 'Brand Colors',
|
|
1033
|
+
source: '@section "Brand Colors" Here you can find all the brand colors',
|
|
1034
|
+
optional: false,
|
|
1035
|
+
description: 'Here you can find all the brand colors'
|
|
1036
|
+
}]
|
|
1037
|
+
})
|
|
1038
|
+
})
|
|
1039
|
+
|
|
1040
|
+
it('should use only quoted name (issue #41)`', function () {
|
|
1041
|
+
expect(parse(function () {
|
|
1042
|
+
/**
|
|
1043
|
+
* @section "Brand Colors" Here you can find "all" the brand colors
|
|
1044
|
+
*/
|
|
1045
|
+
})[0])
|
|
1046
|
+
.to.eql({
|
|
1047
|
+
line: 1,
|
|
1048
|
+
source: '@section "Brand Colors" Here you can find "all" the brand colors',
|
|
1049
|
+
description: '',
|
|
1050
|
+
tags: [{
|
|
1051
|
+
tag: 'section',
|
|
1052
|
+
line: 2,
|
|
1053
|
+
type: '',
|
|
1054
|
+
name: 'Brand Colors',
|
|
1055
|
+
source: '@section "Brand Colors" Here you can find "all" the brand colors',
|
|
1056
|
+
optional: false,
|
|
1057
|
+
description: 'Here you can find "all" the brand colors'
|
|
1058
|
+
}]
|
|
1059
|
+
})
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
it('should ignore inconsitent quoted groups (issue #41)`', function () {
|
|
1063
|
+
expect(parse(function () {
|
|
1064
|
+
/**
|
|
1065
|
+
* @section "Brand Colors Here you can find all the brand colors
|
|
1066
|
+
*/
|
|
1067
|
+
})[0])
|
|
1068
|
+
.to.eql({
|
|
1069
|
+
line: 1,
|
|
1070
|
+
source: '@section "Brand Colors Here you can find all the brand colors',
|
|
1071
|
+
description: '',
|
|
1072
|
+
tags: [{
|
|
1073
|
+
tag: 'section',
|
|
1074
|
+
line: 2,
|
|
1075
|
+
type: '',
|
|
1076
|
+
name: '"Brand',
|
|
1077
|
+
source: '@section "Brand Colors Here you can find all the brand colors',
|
|
1078
|
+
optional: false,
|
|
1079
|
+
description: 'Colors Here you can find all the brand colors'
|
|
1080
|
+
}]
|
|
1081
|
+
})
|
|
1082
|
+
})
|
|
953
1083
|
})
|
|
@@ -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
|
+
})
|