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.
File without changes
package/.travis.yml CHANGED
@@ -1,10 +1,10 @@
1
1
  language: node_js
2
2
  node_js:
3
- - "10"
4
- - "8"
5
- - "6"
3
+ - 12
4
+ - 10
5
+ - 8
6
6
  branches:
7
7
  except:
8
8
  - /^v\d+\.\d+\.\d+$/
9
9
  git:
10
- depth: 1
10
+ depth: 1
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
- ```javascript
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 \\"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
- var fs = require('fs')
5
- var stream = require('stream')
6
- var util = require('util')
4
+ const fs = require('fs')
5
+ const stream = require('stream')
7
6
 
8
- var parse = require('./parser')
7
+ const parse = require('./parser')
8
+
9
+ const stringify = require('./stringifier')
9
10
 
10
11
  module.exports = parse
11
12
 
12
- /* ------- Transform stream ------- */
13
+ module.exports.stringify = stringify
13
14
 
14
- function Parser (opts) {
15
- opts = opts || {}
16
- stream.Transform.call(this, {objectMode: true})
17
- this._extract = parse.mkextract(opts)
18
- }
15
+ /* ------- Transform stream ------- */
19
16
 
20
- util.inherits(Parser, stream.Transform)
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
- Parser.prototype._transform = function transform (data, encoding, done) {
23
- var block
24
- var lines = data.toString().split(/\n/)
24
+ _transform (data, encoding, done) {
25
+ let block
26
+ const lines = data.toString().split(/\n/)
25
27
 
26
- while (lines.length) {
27
- block = this._extract(lines.shift())
28
- if (block) {
29
- this.push(block)
28
+ while (lines.length) {
29
+ block = this._extract(lines.shift())
30
+ if (block) {
31
+ this.push(block)
32
+ }
30
33
  }
31
- }
32
34
 
33
- done()
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
- var opts = {}
44
- var collected = []
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.5.3",
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": "^4.7.1",
14
- "eslint-config-standard": "^10.2.1",
15
- "eslint-plugin-import": "^2.7.0",
16
- "eslint-plugin-node": "^5.1.1",
17
- "eslint-plugin-promise": "^3.5.0",
18
- "eslint-plugin-standard": "^3.0.1",
19
- "mocha": "^5.2.0",
20
- "nodemon": "^1.18.9"
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
- "pretest": "eslint .",
24
- "test": "mocha tests",
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
- var PARSERS = require('./parsers')
2
+ 'use strict'
3
3
 
4
- var MARKER_START = '/**'
5
- var MARKER_START_SKIP = '/***'
6
- var MARKER_END = '*/'
4
+ const PARSERS = require('./parsers')
7
5
 
8
- /* ------- util functions ------- */
9
-
10
- function merge (/* ...objects */) {
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
- for (var i = 0, l = objs.length; i < l; i++) {
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
- var k
28
- var i = list.length
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 (filter.hasOwnProperty(k)) {
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[function]} parsers Array of parsers to be applied to the source
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
- var data = parsers.reduce(function (state, parser) {
54
- var result
38
+ const data = parsers.reduce(function (state, parser) {
39
+ let result
55
40
 
56
41
  try {
57
- result = parser(state.source, merge({}, state.data))
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 = merge(state.data, result.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
- var trim = opts.trim
71
+ const trim = opts.trim
87
72
  ? function trim (s) { return s.trim() }
88
73
  : function trim (s) { return s }
89
74
 
90
- var source_str = source
91
- .map(function (line) { return trim(line.source) })
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
- var start = source[0].number
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*@(\w+)/)) {
105
- tags.push({source: [line.source], line: line.number})
89
+ if (line.source.match(/^\s*@(\S+)/)) {
90
+ tags.push({
91
+ source: [line.source],
92
+ line: line.number
93
+ })
106
94
  } else {
107
- var tag = tags[tags.length - 1]
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
- var source
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(function (tag) {
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
- var description = source.shift()
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
- var tags = source.reduce(function (tags, tag) {
140
- var tag_node = parse_tag(tag.source, opts.parsers)
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.indexOf('.') !== -1) {
148
- var parent_name
149
- var parent_tag
150
- var parent_tags = tags
151
- var parts = tag_node.name.split('.')
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: 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
- var chunk = null
196
- var indent = 0
197
- var number = 0
183
+ let chunk = null
184
+ let indent = 0
185
+ let number = 0
198
186
 
199
- opts = merge({}, {
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
- var result = null
216
- var startPos = line.indexOf(MARKER_START)
217
- var endPos = line.indexOf(MARKER_END)
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
- var lineStart = indent
228
- var startWithStar = false
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
- var nonSpaceChar = line.match(/\S/)
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: number,
248
- startWithStar: 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
- var block
269
- var blocks = []
270
- var extract = mkextract(opts)
271
- var lines = source.split(/\n/)
256
+ const blocks = []
257
+ const extract = mkextract(opts)
258
+ const lines = source.split(/\n/)
272
259
 
273
- for (var i = 0, l = lines.length; i < l; i++) {
274
- block = extract(lines.shift())
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
- var i = 0
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
- var PARSERS = {}
13
+ const PARSERS = {}
12
14
 
13
15
  PARSERS.parse_tag = function parse_tag (str) {
14
- var result = str.match(/^\s*@(\S+)/)
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
- var pos = skipws(str)
28
- var res = ''
29
- var curlies = 0
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
- var pos = skipws(str)
52
- var name = ''
53
- var brackets = 0
54
-
55
- while (pos < str.length) {
56
- brackets += (str[pos] === '[' ? 1 : (str[pos] === ']' ? -1 : 0))
57
- name += str[pos]
58
- pos++
59
- if (brackets === 0 && /\s/.test(str[pos])) { break }
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
- if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets') }
72
+ if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets') }
63
73
 
64
- var res = {name: name, optional: false}
74
+ res = { name: name, optional: false }
65
75
 
66
- if (name[0] === '[' && name[name.length - 1] === ']') {
67
- res.optional = true
68
- name = name.slice(1, -1)
76
+ if (name[0] === '[' && name[name.length - 1] === ']') {
77
+ res.optional = true
78
+ name = name.slice(1, -1)
69
79
 
70
- if (name.indexOf('=') !== -1) {
71
- var parts = name.split('=')
72
- name = parts[0]
73
- res.default = parts[1].replace(/^(["'])(.+)(\1)$/, '$2')
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
- var result = str.match(/^\s+((.|\s)+)?/)
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
- var expect = require('chai').expect
3
- var parse = require('./parse')
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
- var parsers = [
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
- var parsers = [
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
- var parsers = [
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: '',
@@ -1,9 +1,11 @@
1
1
  /* eslint no-unused-vars:off */
2
- var fs = require('fs')
3
- var path = require('path')
4
- var stream = require('readable-stream')
5
- var expect = require('chai').expect
6
- var parse = require('../index')
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
- var count = 0
46
+ let count = 0
45
47
 
46
- var readable = fs.createReadStream(path.resolve(__dirname, 'fixtures/sample.js'), {encoding: 'utf8'})
48
+ const readable = fs.createReadStream(path.resolve(__dirname, 'fixtures/sample.js'), { encoding: 'utf8' })
47
49
 
48
- var writable = new stream.Writable({objectMode: true})
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
- var parse = require('../index')
10
+ 'use strict'
11
+
12
+ const parse = require('../index')
11
13
 
12
14
  module.exports = function (func, opts) {
13
- var str = func.toString()
14
- return parse(str.slice(
15
- str.indexOf('{') + 1,
16
- str.lastIndexOf('}')
17
- ), opts)
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
  }
@@ -1,7 +1,8 @@
1
1
  /* eslint no-unused-vars:off */
2
2
 
3
- var expect = require('chai').expect
4
- var parse = require('./parse')
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
- var p = parse(function () {
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
+ })