├── .editorconfig ├── .github └── workflows │ └── test.workflow.yml ├── .gitignore ├── .mocharc.yml ├── .npmignore ├── History.md ├── Readme.md ├── bin └── dox ├── doc_examples ├── ctx.js ├── generatePersonInfo.js ├── utils.js ├── write1.js ├── write2.js └── write_tags.js ├── index.d.ts ├── index.js ├── lib ├── api.js └── dox.js ├── package-lock.json ├── package.json └── test ├── dox.jsdoccomplex.test.js ├── dox.multilinetags.test.js ├── dox.test.js └── fixtures ├── a.js ├── alias.js ├── asterik.js ├── b.js ├── c.js ├── classes.js ├── d-mixed.js ├── d-spaces.js ├── d-tabs.js ├── d.js ├── enums.js ├── event.js ├── functions.js ├── issue169.js ├── jsdoc-complex-types.js ├── jshint.js ├── literal-inline.js ├── multilinetags.js ├── nodescription.js ├── prototypes-inline.js ├── prototypes.js ├── single-multiline.js ├── single.js ├── singleline.js ├── skip_prefix.js ├── slash.js ├── string-quotes.js ├── throws.js ├── titles.js ├── types.js └── uncommented.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{ts, js}] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_size = 2 7 | indent_style = space 8 | 9 | [*.{yml, yaml, json}] 10 | indent_style = space 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.github/workflows/test.workflow.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | types: [opened, synchronize, reopened, ready_for_review] 9 | branches: 10 | - master 11 | 12 | jobs: 13 | tests: 14 | runs-on: ${{ matrix.os }} 15 | if: github.event.pull_request.draft == false 16 | 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest] 20 | node: [12, 14, 16] 21 | 22 | steps: 23 | - name: Check out repository 24 | uses: actions/checkout@v3 25 | 26 | - uses: actions/setup-node@v3 27 | with: 28 | node-version: ${{ matrix.node }} 29 | 30 | - name: Update NPM to latest 31 | run: npm install npm@latest -g 32 | 33 | - name: Print Node.js and NPM version 34 | run: | 35 | node -v 36 | npm -v 37 | 38 | - name: Install dependencies 39 | run: npm ci 40 | 41 | - name: Run tests 42 | timeout-minutes: 5 43 | run: npm test 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Storedocs 2 | .DS_Store 3 | docs 4 | node_modules 5 | yarn.lock 6 | -------------------------------------------------------------------------------- /.mocharc.yml: -------------------------------------------------------------------------------- 1 | ui: exports 2 | reporter: dot 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs 3 | support 4 | test 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 1.0.0 / 2022-09-06 2 | ================== 3 | 4 | * jsdoctypeparser finally updated to latest version 5 | 6 | 0.9.1 / 2022-04-26 7 | ================== 8 | 9 | * Dependency bumps for commander and markdown-it to address security vulnerabilities 10 | 11 | 0.9.0 / 2016-08-13 12 | ================== 13 | 14 | * Markdown is now generated using the `markdown-it` package, which conforms to Commonmark standards. 15 | * Fix: Dox now recognizes ES6 template strings as strings and does not attempt to parse their contents. 16 | 17 | * Deps: added markdown-it@7.0.0 18 | * Deps: removed marked 19 | * DevDeps: mocha@3.0.2 20 | * DevDeps: should@11.0.0 21 | 22 | 0.8.1 / 2016-03-29 23 | ================== 24 | 25 | * Fix: Dox will no longer falsely enter or exit string blocks when encountering an escaped quote or double-quote 26 | * Deps: commander@2.9.0 27 | * Deps: marked@0.3.5 28 | 29 | 0.8.0 / 2015-05-27 30 | ================== 31 | 32 | * Fix: Tags with whitespace between the tag start and the previous line ending are now parsed correctly. 33 | * Deps: commander@2.8.1 34 | * Deps: jsdoctypeparser@1.2.0 35 | - Better compatibility for type declarations, but may result in changes to output with invalid types. 36 | 37 | 0.7.1 / 2015-04-03 38 | ================== 39 | 40 | Context parsing has been re-factored into an array of functions that are iterated over until a match is found. This array is exposed as `dox.contextPatternMatchers`, allowing for extension with new contexts without needing to edit the dox source. 41 | 42 | * Fix: ES6 classes extended from sub-properties (such as Backbone.View) are now properly matched 43 | 44 | 0.7.0 / 2015-03-24 45 | ================== 46 | 47 | * Add context parsing for some ES6 syntax: 48 | - classes 49 | - class constructors 50 | - class methods 51 | - assignments via `let` or `const` 52 | * Add support for @description tag 53 | * Add context match for returned closure 54 | * Add: Tags without descriptions now have an `html` property containing a markdown parse of the tag's contents 55 | * Fix: more agnostic to code style when parsing contexts (eg, no longer ignores functions without spaces between function name and parenthesis) 56 | * Fix: No longer incorrectly tries to parse strings inside comments, causing large chunks of a file to be ignored. 57 | * Fix: No longer parses double slash in a string literal as being a comment start. 58 | * Deps: commander@2.7.1 59 | 60 | 0.6.1 / 2014-11-27 61 | ================== 62 | 63 | * Tag descriptions now contain markdown and obey raw option 64 | 65 | 0.6.0 / 2014-11-27 66 | ================== 67 | 68 | * Add complex jsdoc annotations 69 | * Add support for more tags 70 | * Add typesDescription field 71 | * Fix "skipPrefixes incorrectly assumes option.raw=false" 72 | * Fix "White spaces in the tag type string break the parsing of tags" 73 | 74 | 0.5.3 / 2014-10-06 75 | ================== 76 | 77 | * Add `--skipSingleStar` option to ignore `/* ... */` comments 78 | * Merge #106: make the other context regex like the general method one 79 | 80 | 0.5.2 / 2014-10-05 81 | ================== 82 | 83 | * Support event tags, add `isEvent` parameter to comment object 84 | * Removed obsolete make rules 85 | 86 | 0.5.1 / 2014-09-07 87 | ================== 88 | 89 | * Fixed: `*/*` breaks parsing 90 | 91 | 0.5.0 / 2014-09-04 92 | ================== 93 | 94 | * Marked options can be set via `dox.setMarkedOptions` 95 | * Comment blocks include `line` and `codeStart` to mark the first line of the comment block and the first line of the code context. 96 | * Ignores jshint, jslint and eslint directives. This can be overridden or added to via the `skipPrefixes` option and the `--skipPrefixes` command line flag, which takes a comma separated list of prefixes. 97 | * The code field trims extra indentation based on the indentation of the first line of code. 98 | * Set the `isConstructor` property when a `@constructor` tag is present and change `ctx.type` to constructor. 99 | * Recognizes the following code contexts: 100 | - `Foo.prototype.bar;` (property) 101 | - `Foo.prototype = {` (prototype) 102 | - `foo: function () {` (method) 103 | - `foo: bar` (property) 104 | - `get foo () {` (property) 105 | - `set foo () {` (property) 106 | * When a comment is present to identify the definition of an object literal, comments for the object's members will include a `ctx.constructor` property identifying the parent object. 107 | * Fixed: Multi-line comments with no space following the star are parsed correctly. 108 | - Example: `/*comment*/` 109 | * Fixed: A code context of `Foo.prototype.bar = null;` is parsed correctly. 110 | * `@param` tags include an `optional` attribute 111 | * `@returns` is recognized as an alias for `@return` 112 | * Support comments without descriptions (ex: `/** @name Foo **/`) 113 | * Fixed: Crash with the `--api` flag when no headers are generated. 114 | * Fixed: `--api` output includes aliases. 115 | 116 | 0.4.6 / 2014-07-09 117 | ================== 118 | 119 | * do not wrap @example contents with markdown 120 | 121 | 0.4.5 / 2014-07-09 122 | ================== 123 | 124 | * use marked for markdown rendering 125 | * multiline tags support (@example) 126 | * support for @template, @property, @define, @public, @private, @protected, 127 | @lends, @extends, @implements, @enum, @typedef 128 | 129 | 0.4.4 / 2013-07-28 130 | ================== 131 | 132 | * add support for variable names containing "$". fix #102 133 | 134 | 0.4.3 / 2013-03-18 135 | ================== 136 | 137 | * fix dox(1) --version. Closes #91 138 | * fix ctx.string on properties of a prototype 139 | * add support tab-indented comments 140 | 141 | 0.4.2 / 2013-01-18 142 | ================== 143 | 144 | * Prevent error when using --api & comments have no example code. 145 | 146 | 0.4.1 / 2012-11-11 147 | ================== 148 | 149 | * change # to . in --api 150 | 151 | 0.4.0 / 2012-11-09 152 | ================== 153 | 154 | * add TOC to --api. Closes #72 155 | * add gfm code blocks. Closes #71 156 | * remove implicit titles. Closes #70 157 | 158 | 0.3.3 / 2012-10-16 159 | ================== 160 | 161 | * fix --api .receiver 162 | 163 | 0.3.2 / 2012-10-01 164 | ================== 165 | 166 | * add dox --api 167 | 168 | 0.3.1 / 2012-04-25 169 | ================== 170 | 171 | * Fixed annoying title bug 172 | 173 | 0.3.0 / 2012-03-27 174 | ================== 175 | 176 | * Added __@memberOf__ [olivernn] 177 | * Added __@arguments__ [olivernn] 178 | * Added __@borrows__ [olivernn] 179 | 180 | 0.2.0 / 2012-02-23 181 | ================== 182 | 183 | * Added `-r, --raw` support. Closes #48 184 | 185 | 0.1.3 / 2011-12-08 186 | ================== 187 | 188 | * Added: allow arbitrary tags [logicalparadox] 189 | * Fixed function whitespace [TooTallNate] 190 | 191 | 0.1.2 / 2011-10-22 192 | ================== 193 | 194 | * remove html escaping for now 195 | 196 | 0.1.1 / 2011-10-10 197 | ================== 198 | 199 | * Fixed: colons in comment lines not intended as headers [Evan Owen] 200 | 201 | 0.0.5 / 2011-03-02 202 | ================== 203 | 204 | * Adding "main" to package descriptor since "directories" are no longer supported. 205 | 206 | 0.0.4 / 2011-01-20 207 | ================== 208 | 209 | * Added `--intro` support for including an intro file written in markdown [Alex Young] 210 | 211 | 0.0.3 / 2010-07-15 212 | ================== 213 | 214 | * Linked h2s 215 | 216 | 0.0.2 / 2010-07-15 217 | ================== 218 | 219 | * Collapsing files, click to open. Closes #19 220 | * Fixed ribbon position instead of absolute 221 | * Removed menu 222 | * Removed node-discount dependency, using markdown-js 223 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Dox 2 | 3 | [![Tests](https://github.com/tj/dox/actions/workflows/test.workflow.yml/badge.svg)](https://github.com/tj/dox/actions/workflows/test.workflow.yml) 4 | 5 | Dox is a JavaScript documentation generator written with [node](http://nodejs.org). Dox no longer generates an opinionated structure or style for your docs, it simply gives you a JSON representation, allowing you to use _markdown_ and _JSDoc_-style tags. 6 | 7 | ## Installation 8 | 9 | Install from npm: 10 | 11 | ```sh 12 | npm install -g dox 13 | ``` 14 | 15 | ## Usage Examples 16 | 17 | `dox(1)` operates over stdio: 18 | 19 | ```sh 20 | $ dox < utils.js 21 | ...JSON... 22 | ``` 23 | 24 | to inspect the generated data you can use the `--debug` flag, which is easier to read than the JSON output: 25 | 26 | ```sh 27 | dox --debug < utils.js 28 | ``` 29 | 30 | [utils.js](./doc_examples/utils.js): 31 | 32 | ```js 33 | /** 34 | * Escape the given `html`. 35 | * 36 | * @example 37 | * utils.escape('') 38 | * // => '<script></script>' 39 | * 40 | * @param {String} html string to be escaped 41 | * @return {String} escaped html 42 | * @api public 43 | */ 44 | 45 | exports.escape = function(html){ 46 | return String(html) 47 | .replace(/&(?!\w+;)/g, '&') 48 | .replace(//g, '>'); 50 | }; 51 | ``` 52 | 53 | output: 54 | 55 | ```json 56 | [ 57 | { 58 | "tags": [ 59 | { 60 | "type": "param", 61 | "string": "{String} html", 62 | "name": "html", 63 | "description": "", 64 | "types": [ 65 | "String" 66 | ], 67 | "typesDescription": "String", 68 | "optional": false, 69 | "nullable": false, 70 | "nonNullable": false, 71 | "variable": false, 72 | "html": "

{String} html

" 73 | }, 74 | { 75 | "type": "return", 76 | "string": "{String}", 77 | "types": [ 78 | "String" 79 | ], 80 | "typesDescription": "String", 81 | "optional": false, 82 | "nullable": false, 83 | "nonNullable": false, 84 | "variable": false, 85 | "description": "", 86 | "html": "

{String}

" 87 | }, 88 | { 89 | "type": "api", 90 | "string": "private", 91 | "visibility": "private", 92 | "html": "

private

" 93 | } 94 | ], 95 | "description": { 96 | "full": "

Escape the given html.

", 97 | "summary": "

Escape the given html.

", 98 | "body": "" 99 | }, 100 | "isPrivate": true, 101 | "isConstructor": false, 102 | "isClass": false, 103 | "isEvent": false, 104 | "ignore": false, 105 | "line": 2, 106 | "codeStart": 10, 107 | "code": "exports.escape = function(html){\n return String(html)\n .replace(/&(?!\\w+;)/g, '&')\n .replace(//g, '>');\n};", 108 | "ctx": { 109 | "type": "method", 110 | "receiver": "exports", 111 | "name": "escape", 112 | "string": "exports.escape()" 113 | } 114 | } 115 | ] 116 | ``` 117 | 118 | This output can then be passed to a template for rendering. Look below at the "Properties" section for details. 119 | 120 | ## Usage 121 | 122 | ```txt 123 | 124 | Usage: dox [options] 125 | 126 | Options: 127 | 128 | -h, --help output usage information 129 | -V, --version output the version number 130 | -r, --raw output "raw" comments, leaving the markdown intact 131 | -a, --api output markdown readme documentation 132 | -s, --skipPrefixes [prefixes] skip comments prefixed with these prefixes, separated by commas 133 | -d, --debug output parsed comments for debugging 134 | -S, --skipSingleStar set to false to ignore `/* ... */` comments 135 | 136 | Examples: 137 | 138 | # stdin 139 | $ dox > myfile.json 140 | 141 | # operates over stdio 142 | $ dox < myfile.js > myfile.json 143 | 144 | ``` 145 | 146 | ### Programmatic Usage 147 | 148 | ```js 149 | var dox = require('dox'), 150 | code = "..."; 151 | 152 | var obj = dox.parseComments(code); 153 | 154 | // [{ tags:[ ... ], description, ... }, { ... }, ...] 155 | ``` 156 | 157 | ## Properties 158 | 159 | A "comment" is comprised of the following detailed properties: 160 | 161 | - tags 162 | - description 163 | - isPrivate 164 | - isEvent 165 | - isConstructor 166 | - line 167 | - ignore 168 | - code 169 | - ctx 170 | 171 | ### Description 172 | 173 | A dox description is comprised of three parts, the "full" description, 174 | the "summary", and the "body". The following example has only a "summary", 175 | as it consists of a single paragraph only, therefore the "full" property has 176 | only this value as well. 177 | 178 | [write1.js](./doc_examples/write1.js): 179 | 180 | ```js 181 | /** 182 | * Output the given `str` to _stdout_. 183 | */ 184 | 185 | exports.write = function(str) { 186 | process.stdout.write(str); 187 | }; 188 | ``` 189 | 190 | yields: 191 | 192 | ```json 193 | [ 194 | { 195 | "description": { 196 | "full": "

Output the given str to stdout.

", 197 | "summary": "

Output the given str to stdout.

", 198 | "body": "" 199 | }, 200 | // ... other tags 201 | } 202 | ] 203 | ``` 204 | 205 | Large descriptions might look something like the following, where the "summary" is still the first paragraph, the remaining description becomes the "body". Keep in mind this _is_ markdown, so you can indent code, use lists, links, etc. Dox also augments markdown, allowing "Some Title:\n" to form a header. 206 | 207 | [write2.js](./doc_examples/write2.js): 208 | 209 | ```js 210 | /** 211 | * Output the given `str` to _stdout_ 212 | * or the stream specified by `options`. 213 | * 214 | * Options: 215 | * 216 | * - `stream` defaulting to _stdout_ 217 | * 218 | * Examples: 219 | * 220 | * mymodule.write('foo') 221 | * mymodule.write('foo', { stream: process.stderr }) 222 | * 223 | */ 224 | 225 | exports.write = function(str, options) { 226 | options = options || {}; 227 | (options.stream || process.stdout).write(str); 228 | }; 229 | ``` 230 | 231 | yields: 232 | 233 | ```json 234 | [ 235 | { 236 | "description": { 237 | "full": "

Output the given str to stdout
\nor the stream specified by options.

\n

Options:

\n\n

Examples:

\n
mymodule.write('foo')\nmymodule.write('foo', { stream: process.stderr })\n
", 238 | "summary": "

Output the given str to stdout
\nor the stream specified by options.

", 239 | "body": "

Options:

\n\n

Examples:

\n
mymodule.write('foo')\nmymodule.write('foo', { stream: process.stderr })\n
" 240 | }, 241 | // ... other tags 242 | } 243 | ] 244 | ``` 245 | 246 | ### Tags 247 | 248 | Dox also supports JSdoc-style tags. Currently only __@api__ is special-cased, providing the `comment.isPrivate` boolean so you may omit "private" utilities etc. 249 | 250 | [write_tags.js](./doc_examples/write_tags.js): 251 | 252 | ```js 253 | /** 254 | * Output the given `str` to _stdout_ 255 | * or the stream specified by `options`. 256 | * 257 | * @param {String} str 258 | * @param {{stream: Writable}} options 259 | * @return {Object} exports for chaining 260 | */ 261 | 262 | exports.write = function(str, options) { 263 | options = options || {}; 264 | (options.stream || process.stdout).write(str); 265 | return this; 266 | }; 267 | ``` 268 | 269 | yields: 270 | 271 | ```json 272 | [ 273 | { 274 | "tags": [ 275 | { 276 | "type": "param", 277 | "string": "{String} str", 278 | "name": "str", 279 | "description": "", 280 | "types": [ 281 | "String" 282 | ], 283 | "typesDescription": "String", 284 | "optional": false, 285 | "nullable": false, 286 | "nonNullable": false, 287 | "variable": false, 288 | "html": "

{String} str

" 289 | }, 290 | { 291 | "type": "param", 292 | "string": "{{stream: Writable}} options", 293 | "name": "options", 294 | "description": "", 295 | "types": [ 296 | { 297 | "stream": [ 298 | "Writable" 299 | ] 300 | } 301 | ], 302 | "typesDescription": "{stream: Writable}", 303 | "optional": false, 304 | "nullable": false, 305 | "nonNullable": false, 306 | "variable": false, 307 | "html": "

{{stream: Writable}} options

" 308 | }, 309 | { 310 | "type": "return", 311 | "string": "{Object} exports for chaining", 312 | "types": [ 313 | "Object" 314 | ], 315 | "typesDescription": "Object", 316 | "optional": false, 317 | "nullable": false, 318 | "nonNullable": false, 319 | "variable": false, 320 | "description": "

exports for chaining

" 321 | } 322 | ], 323 | // ... other tags 324 | } 325 | ] 326 | ``` 327 | 328 | #### Complex jsdoc tags 329 | 330 | dox supports all jsdoc type strings specified in the [jsdoc documentation](http://usejsdoc.org/tags-type.html). You can 331 | specify complex object types including optional flag `=`, nullable `?`, non-nullable `!` and variable arguments `...`. 332 | 333 | Additionally you can use `typesDescription` which contains formatted HTML for displaying complex types. 334 | 335 | [generatePersonInfo.js](./doc_examples/generatePersonInfo.js): 336 | 337 | ```js 338 | /** 339 | * Generates a person information string based on input. 340 | * 341 | * @param {string | {name: string, age: number | date}} name Name or person object 342 | * @param {{separator: string} =} options An options object 343 | * @return {string} The constructed information string 344 | */ 345 | 346 | exports.generatePersonInfo = function(name, options) { 347 | var str = ''; 348 | var separator = options && options.separator ? options.separator : ' '; 349 | 350 | if(typeof name === 'object') { 351 | str = [name.name, '(', name.age, ')'].join(separator); 352 | } else { 353 | str = name; 354 | } 355 | }; 356 | ``` 357 | 358 | yields: 359 | 360 | ```json 361 | [ 362 | { 363 | "tags": [ 364 | { 365 | "type": "param", 366 | "string": "{string | {name: string, age: number | date}} name Name or person object", 367 | "name": "name", 368 | "description": "

Name or person object

", 369 | "types": [ 370 | "string", 371 | { 372 | "name": [ 373 | "string" 374 | ], 375 | "age": [ 376 | "number", 377 | "date" 378 | ] 379 | } 380 | ], 381 | "typesDescription": "string | {name: string, age: number | date}", 382 | "optional": false, 383 | "nullable": false, 384 | "nonNullable": false, 385 | "variable": false 386 | }, 387 | { 388 | "type": "param", 389 | "string": "{{separator: string} =} options An options object", 390 | "name": "options", 391 | "description": "

An options object

", 392 | "types": [ 393 | { 394 | "separator": [ 395 | "string" 396 | ] 397 | } 398 | ], 399 | "typesDescription": "{separator: string|undefined}", 400 | "optional": true, 401 | "nullable": false, 402 | "nonNullable": false, 403 | "variable": false 404 | }, 405 | { 406 | "type": "return", 407 | "string": "{string} The constructed information string", 408 | "types": [ 409 | "string" 410 | ], 411 | "typesDescription": "string", 412 | "optional": false, 413 | "nullable": false, 414 | "nonNullable": false, 415 | "variable": false, 416 | "description": "

The constructed information string

" 417 | } 418 | ], 419 | // ... other tags 420 | } 421 | ] 422 | ``` 423 | 424 | ### Code 425 | 426 | The `.code` property is the code following the comment block, in our previous examples: 427 | 428 | ```js 429 | exports.write = function(str, options) { 430 | options = options || {}; 431 | (options.stream || process.stdout).write(str); 432 | return this; 433 | }; 434 | ``` 435 | 436 | ### Ctx 437 | 438 | The `.ctx` object indicates the context of the code block, is it a method, a function, a variable etc. Below are some examples: 439 | 440 | [ctx.js](./doc_examples/ctx.js): 441 | 442 | ```js 443 | /** */ 444 | exports.generate = function(str, options) {}; 445 | ``` 446 | 447 | yields: 448 | 449 | ```json 450 | [ 451 | { 452 | // ... other tags 453 | "ctx": { 454 | "type": "method", 455 | "receiver": "exports", 456 | "name": "generate", 457 | "string": "exports.generate()" 458 | } 459 | } 460 | ] 461 | ``` 462 | 463 | ```js 464 | /** */ 465 | var foo = 'bar'; 466 | ``` 467 | 468 | yields: 469 | 470 | ```json 471 | [ 472 | { 473 | // ... other tags 474 | "ctx": { 475 | "type": "declaration", 476 | "name": "foo", 477 | "value": "'bar'", 478 | "string": "foo" 479 | } 480 | } 481 | ] 482 | ``` 483 | 484 | ```js 485 | /** */ 486 | function User() {} 487 | ``` 488 | 489 | yields: 490 | 491 | ```json 492 | [ 493 | { 494 | // ... other tags 495 | "ctx": { 496 | "type": "function", 497 | "name": "User", 498 | "string": "User()" 499 | } 500 | } 501 | ] 502 | ``` 503 | 504 | ### Extending Context Matching 505 | 506 | Context matching in dox is done by performing pattern matching against the code following a 507 | comment block. `dox.contextPatternMatchers` is an array of all pattern matching functions, 508 | which dox will iterate over until one of them returns a result. If none return a result, 509 | then the comment block does not receive a `ctx` value. 510 | 511 | This array is exposed to allow for extension of unsupported context patterns by adding more 512 | functions. Each function is passed the code following the comment block and (if detected) 513 | the parent context if the block. 514 | 515 | ```js 516 | dox.contextPatternMatchers.push(function (str, parentContext) { 517 | // return a context object if found 518 | // return false otherwise 519 | }); 520 | ``` 521 | 522 | ### Ignore 523 | 524 | Comments and their associated bodies of code may be flagged with "!" to be considered worth ignoring, these are typically things like file comments containing copyright etc, however you of course can output them in your templates if you want. 525 | 526 | ```js 527 | /** 528 | * Not ignored. 529 | */ 530 | ``` 531 | 532 | vs 533 | 534 | ```js 535 | /*! 536 | * Ignored. 537 | */ 538 | ``` 539 | 540 | You may use `-S`, `--skipSingleStar` or `{skipSingleStar: true}` to ignore `/* ... */` comments. 541 | 542 | ### Running tests 543 | 544 | Install dev dependencies and execute `make test`: 545 | 546 | ```sh 547 | npm install -d 548 | make test 549 | ``` 550 | 551 | ## License 552 | 553 | (The MIT License) 554 | 555 | Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> 556 | 557 | Permission is hereby granted, free of charge, to any person obtaining 558 | a copy of this software and associated documentation files (the 559 | 'Software'), to deal in the Software without restriction, including 560 | without limitation the rights to use, copy, modify, merge, publish, 561 | distribute, sublicense, and/or sell copies of the Software, and to 562 | permit persons to whom the Software is furnished to do so, subject to 563 | the following conditions: 564 | 565 | The above copyright notice and this permission notice shall be 566 | included in all copies or substantial portions of the Software. 567 | 568 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 569 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 570 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 571 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 572 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 573 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 574 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 575 | -------------------------------------------------------------------------------- /bin/dox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var program = require('commander') 8 | , pkg = require('../package') 9 | , util = require('util') 10 | , dox = require('..'); 11 | 12 | // options 13 | 14 | program 15 | .version(pkg.version) 16 | .option('-r, --raw', 'output "raw" comments, leaving the markdown intact') 17 | .option('-a, --api', 'output markdown readme documentation') 18 | .option('-s, --skipPrefixes [prefixes]', 'skip comments prefixed with these prefixes, separated by commas') 19 | .option('-d, --debug', 'output parsed comments for debugging') 20 | .option('-S, --skipSingleStar', 'set to false to ignore `/* ... */` comments'); 21 | 22 | // examples 23 | 24 | program.on('--help', function(){ 25 | console.log(' Examples:'); 26 | console.log(''); 27 | console.log(' # stdin'); 28 | console.log(' $ dox > myfile.json'); 29 | console.log(''); 30 | console.log(' # operates over stdio'); 31 | console.log(' $ dox < myfile.js > myfile.json'); 32 | console.log(''); 33 | }); 34 | 35 | // parse argv 36 | 37 | program.parse(process.argv); 38 | 39 | // process stdin 40 | 41 | var buf = ''; 42 | process.stdin.setEncoding('utf8'); 43 | process.stdin.on('data', function(chunk){ buf += chunk; }); 44 | process.stdin.on('end', function(){ 45 | var obj = dox.parseComments(buf, { raw: program.raw || program.api, skipPrefixes: program.skipPrefix && program.skipPrefix.split(','), skipSingleStar: !!program.skipSingleStar }); 46 | if (program.debug) { 47 | process.stdout.write(util.inspect(obj, false, Infinity, true) + '\n'); 48 | } else if (program.api) { 49 | process.stdout.write(dox.api(obj)); 50 | } else { 51 | process.stdout.write(JSON.stringify(obj, null, 2)); 52 | } 53 | }).resume(); 54 | -------------------------------------------------------------------------------- /doc_examples/ctx.js: -------------------------------------------------------------------------------- 1 | /** */ 2 | exports.generate = function(str, options) {}; 3 | 4 | /** */ 5 | var foo = 'bar'; 6 | 7 | /** */ 8 | function User() {} 9 | -------------------------------------------------------------------------------- /doc_examples/generatePersonInfo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generates a person information string based on input. 3 | * 4 | * @param {string | {name: string, age: number | date}} name Name or person object 5 | * @param {{separator: string} =} options An options object 6 | * @return {string} The constructed information string 7 | */ 8 | 9 | exports.generatePersonInfo = function(name, options) { 10 | var str = ''; 11 | var separator = options && options.separator ? options.separator : ' '; 12 | 13 | if(typeof name === 'object') { 14 | str = [name.name, '(', name.age, ')'].join(separator); 15 | } else { 16 | str = name; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /doc_examples/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Escape the given `html`. 4 | * 5 | * @param {String} html 6 | * @return {String} 7 | * @api private 8 | */ 9 | 10 | exports.escape = function(html){ 11 | return String(html) 12 | .replace(/&(?!\w+;)/g, '&') 13 | .replace(//g, '>'); 15 | }; -------------------------------------------------------------------------------- /doc_examples/write1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Output the given `str` to _stdout_. 3 | */ 4 | 5 | exports.write = function(str) { 6 | process.stdout.write(str); 7 | }; 8 | -------------------------------------------------------------------------------- /doc_examples/write2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Output the given `str` to _stdout_ 3 | * or the stream specified by `options`. 4 | * 5 | * Options: 6 | * 7 | * - `stream` defaulting to _stdout_ 8 | * 9 | * Examples: 10 | * 11 | * mymodule.write('foo') 12 | * mymodule.write('foo', { stream: process.stderr }) 13 | * 14 | */ 15 | 16 | exports.write = function(str, options) { 17 | options = options || {}; 18 | (options.stream || process.stdout).write(str); 19 | }; 20 | -------------------------------------------------------------------------------- /doc_examples/write_tags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Output the given `str` to _stdout_ 3 | * or the stream specified by `options`. 4 | * 5 | * @param {String} str 6 | * @param {{stream: Writable}} options 7 | * @return {Object} exports for chaining 8 | */ 9 | 10 | exports.write = function(str, options) { 11 | options = options || {}; 12 | (options.stream || process.stdout).write(str); 13 | return this; 14 | }; 15 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "dox" { 2 | export interface ParseCommentOptions { 3 | skipSingleStar?: boolean; 4 | skipPrefixes?: string[]; 5 | raw?: boolean; 6 | } 7 | 8 | export interface CommentDescription { 9 | full: string; 10 | summary: string; 11 | body: string; 12 | } 13 | 14 | export interface Comment { 15 | /** array of tag objects */ 16 | tags: Tag[]; 17 | /** the first line of the comment */ 18 | description: CommentDescription; 19 | /** true when "@api private" is used */ 20 | isPrivate: boolean; 21 | isConstructor: boolean; 22 | line: number; 23 | /** lines following the description */ 24 | body: any; 25 | /** both the description and the body */ 26 | content: any; 27 | codeStart: number; 28 | code: string; 29 | ctx: ContextPatternMatchersCtx; 30 | ignore: boolean; 31 | } 32 | 33 | export interface Tag { 34 | string: string; 35 | /** from "@param"/"@property"/"@template", name of parameter/property */ 36 | name?: string; 37 | /** text after various tags */ 38 | description?: string; 39 | /** from "@see", everything before "http" */ 40 | title?: string; 41 | /** from "@see" */ 42 | url?: string; 43 | /** from "@see" */ 44 | local?: string; 45 | /** when "@api private" this field will be "private" (what is after "@api") and in case of "@protected"/"@public"/"@private" will be the tag itself */ 46 | visibility?: string; 47 | /** the string after "@memberOf"/"@lends" */ 48 | parent?: string; 49 | /** the string after "@extends"/"@implements"/"@augments" */ 50 | otherClass?: string; 51 | /** from "@borrows" */ 52 | otherMemberName?: string; 53 | /** from "@borrows" */ 54 | thisMemberName?: string; 55 | /** all parsed types from tags that support it, like "@param" */ 56 | types?: string[]; 57 | /** from "@description" */ 58 | full?: string; 59 | /** from "@description" */ 60 | summary?: string; 61 | /** from "@description" */ 62 | body?: string; 63 | typesDescription?: string; 64 | /** 65 | * "true" when the property is marked optional 66 | * @example 67 | * @param {string} [optionalProperty] 68 | * @param {string} nonOptionalProperty 69 | */ 70 | optional?: boolean; 71 | /** 72 | * "true" when the property is marked nullable 73 | * @example 74 | * @param {?string} nullable 75 | * @param {string} normal 76 | * @param {!string} nonNullable 77 | */ 78 | nullable?: boolean; 79 | /** 80 | * "true" when the property is marked non-nullable 81 | * @example 82 | * @param {?string} nullable 83 | * @param {string} normal 84 | * @param {!string} nonNullable 85 | */ 86 | nonNullable?: boolean; 87 | /** 88 | * "true" when using the spread type 89 | * @example 90 | * @param {...string} spread 91 | * @param {string} nonSpread 92 | */ 93 | variable?: boolean; 94 | } 95 | 96 | export function api(comments: Comment[]): string; 97 | /** Parse comments in the given string of `js`. */ 98 | export function parseComments(js: string, options: ParseCommentOptions): Comment[]; 99 | /** Removes excess indentation from string of code. */ 100 | export function trimIndentation(str: string): string; 101 | /** 102 | * Parse the given comment `str`. 103 | * 104 | * The comment object returned contains the following 105 | */ 106 | export function parseComment(str: string, options: ParseCommentOptions): any; 107 | /** 108 | * Extracts different parts of a tag by splitting string into pieces separated by whitespace. If the white spaces are 109 | * somewhere between curly braces (which is used to indicate param/return type in JSDoc) they will not be used to split 110 | * the string. This allows to specify jsdoc tags without the need to eliminate all white spaces i.e. {number | string} 111 | */ 112 | export function extractTagParts(str: any): string[]; 113 | /** Parse tag string "@param {Array} name description" etc. */ 114 | export function parseTag(str: any): Tag; 115 | /** 116 | * Parse tag type string "{Array|Object}" etc. 117 | * This function also supports complex type descriptors like in jsDoc or even the enhanced syntax used by the 118 | * [google closure compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types) 119 | * 120 | * The resulting array from the type descriptor `{number|string|{name:string,age:number|date}}` would look like this: 121 | */ 122 | export function parseTagTypes(str: string, tag: Tag): string[]; 123 | /** 124 | * Determine if a parameter is optional. 125 | * 126 | * Examples: 127 | * JSDoc: {Type} [name] 128 | * Google: {Type=} name 129 | * TypeScript: {Type?} name 130 | */ 131 | export function parseParamOptional(tag: Tag): boolean; 132 | /** 133 | * Parse the context from the given `str` of js. 134 | * 135 | * This method attempts to discover the context 136 | * for the comment based on it's code. Currently 137 | * supports: 138 | * 139 | * - classes 140 | * - class constructors 141 | * - class methods 142 | * - function statements 143 | * - function expressions 144 | * - prototype methods 145 | * - prototype properties 146 | * - methods 147 | * - properties 148 | * - declarations 149 | */ 150 | export function parseCodeContext(str: string, parentContext?: any | undefined): any; 151 | 152 | export interface ContextPatternMatchersCtx { 153 | type: 'class' | 'constructor' | 'method' | 'function' | 'property' | 'prototype' | 'declaration'; 154 | constructor?: string; 155 | cons?: string; 156 | name: string; 157 | string: string; 158 | extends?: string; 159 | value?: string; 160 | } 161 | 162 | export type ContextPatternMatchersFn = (str: string, parentContext?: ContextPatternMatchersCtx) => ContextPatternMatchersCtx; 163 | 164 | export let contextPatternMatchers: ContextPatternMatchersFn[]; 165 | } 166 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./lib/dox'); -------------------------------------------------------------------------------- /lib/api.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(comments){ 3 | var buf = []; 4 | 5 | comments.forEach(function(comment){ 6 | if (comment.isPrivate) return; 7 | if (comment.ignore) return; 8 | var ctx = comment.ctx; 9 | var desc = comment.description; 10 | if (!ctx) return; 11 | if (~desc.full.indexOf('Module dependencies')) return; 12 | if (!ctx.string.indexOf('module.exports')) return; 13 | buf.push('### ' + context(comment)); 14 | buf.push(''); 15 | buf.push(desc.full.trim().replace(/^/gm, ' ')); 16 | buf.push(''); 17 | }); 18 | 19 | buf = buf 20 | .join('\n') 21 | .replace(/^ *#/gm, '') 22 | 23 | var code = buf.match(/^( {4}[^\n]+\n*)+/gm) || []; 24 | 25 | code.forEach(function(block){ 26 | var code = block.replace(/^ {4}/gm, ''); 27 | buf = buf.replace(block, '```js\n' + code.trimRight() + '\n```\n\n'); 28 | }); 29 | 30 | return toc(buf) + '\n\n' + buf; 31 | }; 32 | 33 | function toc(str) { 34 | return headings(str).map(function(h){ 35 | var clean = h.title.replace(/\(.*?\)/, '()'); 36 | return ' - [' + clean + '](#' + slug(h.title) + ')'; 37 | }).join('\n'); 38 | } 39 | 40 | function slug(str) { 41 | return str.replace(/\W+/g, '').toLowerCase(); 42 | } 43 | 44 | function headings(str) { 45 | return (str.match(/^#+ *([^\n]+)/gm) || []).map(function(str){ 46 | str = str.replace(/^(#+) */, ''); 47 | return { 48 | title: str, 49 | level: RegExp.$1.length 50 | } 51 | }); 52 | } 53 | 54 | function context(comment) { 55 | var ctx = comment.ctx; 56 | var tags = comment.tags; 57 | 58 | var alias = tags.map(function(tag) { 59 | return tag.type === 'alias' && tag.string 60 | }).filter(Boolean); 61 | 62 | switch (ctx.type) { 63 | case 'function': 64 | var name = alias.pop() || ctx.name; 65 | return name + '(' + params(tags) + ')' 66 | case 'method': 67 | var name = alias.pop() || (ctx.cons || ctx.receiver) + '.' + ctx.name; 68 | return name + '(' + params(tags) + ')'; 69 | default: 70 | return alias.pop() || ctx.string; 71 | } 72 | } 73 | 74 | function params(tags) { 75 | return tags.filter(function(tag){ 76 | return tag.type == 'param'; 77 | }).map(function(param){ 78 | return param.name + ':' + param.types.join('|'); 79 | }).join(', '); 80 | } 81 | -------------------------------------------------------------------------------- /lib/dox.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Module dependencies. 3 | */ 4 | 5 | var markdown = require('markdown-it')({ 6 | html: true, 7 | xhtmlOut: true, 8 | breaks: true, 9 | langPrefix: 'lang-' 10 | }); 11 | 12 | /** 13 | * Expose api. 14 | */ 15 | 16 | exports.api = require('./api'); 17 | 18 | /** 19 | * Parse comments in the given string of `js`. 20 | * 21 | * @param {String} js 22 | * @param {Object} options 23 | * @return {Array} 24 | * @see exports.parseComment 25 | * @api public 26 | */ 27 | 28 | exports.parseComments = function(js, options){ 29 | options = options || {}; 30 | js = js.replace(/\r\n/gm, '\n'); 31 | 32 | var comments = [] 33 | , skipSingleStar = options.skipSingleStar 34 | , comment 35 | , buf = '' 36 | , ignore 37 | , withinMultiline = false 38 | , withinSingle = false 39 | , withinString = false 40 | , code 41 | , linterPrefixes = options.skipPrefixes || ['jslint', 'jshint', 'eshint'] 42 | , skipPattern = new RegExp('^' + (options.raw ? '' : '

') + '('+ linterPrefixes.join('|') + ')') 43 | , lineNum = 1 44 | , lineNumStarting = 1 45 | , parentContext 46 | , withinEscapeChar 47 | , currentStringQuoteChar; 48 | 49 | 50 | for (var i = 0, len = js.length; i < len; ++i) { 51 | withinEscapeChar = withinString && !withinEscapeChar && js[i - 1] == '\\'; 52 | 53 | // start comment 54 | if (!withinMultiline && !withinSingle && !withinString && 55 | '/' == js[i] && '*' == js[i+1] && (!skipSingleStar || js[i+2] == '*')) { 56 | lineNumStarting = lineNum; 57 | // code following the last comment 58 | if (buf.trim().length) { 59 | comment = comments[comments.length - 1]; 60 | if(comment) { 61 | // Adjust codeStart for any vertical space between comment and code 62 | comment.codeStart += buf.match(/^(\s*)/)[0].split('\n').length - 1; 63 | comment.code = code = exports.trimIndentation(buf).trim(); 64 | comment.ctx = exports.parseCodeContext(code, parentContext); 65 | 66 | if (comment.isConstructor && comment.ctx){ 67 | comment.ctx.type = "constructor"; 68 | } 69 | 70 | // starting a new namespace 71 | if (comment.ctx && (comment.ctx.type === 'prototype' || comment.ctx.type === 'class')){ 72 | parentContext = comment.ctx; 73 | } 74 | // reasons to clear the namespace 75 | // new property/method in a different constructor 76 | else if (!parentContext || !comment.ctx || !comment.ctx.constructor || !parentContext.constructor || parentContext.constructor !== comment.ctx.constructor){ 77 | parentContext = null; 78 | } 79 | } 80 | buf = ''; 81 | } 82 | i += 2; 83 | withinMultiline = true; 84 | ignore = '!' == js[i]; 85 | 86 | // if the current character isn't whitespace and isn't an ignored comment, 87 | // back up one character so we don't clip the contents 88 | if (' ' !== js[i] && '\n' !== js[i] && '\t' !== js[i] && '!' !== js[i]) i--; 89 | 90 | // end comment 91 | } else if (withinMultiline && !withinSingle && '*' == js[i] && '/' == js[i+1]) { 92 | i += 2; 93 | buf = buf.replace(/^[ \t]*\* ?/gm, ''); 94 | comment = exports.parseComment(buf, options); 95 | comment.ignore = ignore; 96 | comment.line = lineNumStarting; 97 | comment.codeStart = lineNum + 1; 98 | if (!comment.description.full.match(skipPattern)) { 99 | comments.push(comment); 100 | } 101 | withinMultiline = ignore = false; 102 | buf = ''; 103 | } else if (!withinSingle && !withinMultiline && !withinString && '/' == js[i] && '/' == js[i+1]) { 104 | withinSingle = true; 105 | buf += js[i]; 106 | } else if (withinSingle && !withinMultiline && '\n' == js[i]) { 107 | withinSingle = false; 108 | buf += js[i]; 109 | } else if (!withinSingle && !withinMultiline && !withinEscapeChar && ('\'' == js[i] || '"' == js[i] || '`' == js[i])) { 110 | if(withinString) { 111 | if(js[i] == currentStringQuoteChar) { 112 | withinString = false; 113 | } 114 | } else { 115 | withinString = true; 116 | currentStringQuoteChar = js[i]; 117 | } 118 | 119 | buf += js[i]; 120 | } else { 121 | buf += js[i]; 122 | } 123 | 124 | if('\n' == js[i]) { 125 | lineNum++; 126 | } 127 | 128 | } 129 | 130 | if (comments.length === 0) { 131 | comments.push({ 132 | tags: [], 133 | description: {full: '', summary: '', body: ''}, 134 | isPrivate: false, 135 | isConstructor: false, 136 | line: lineNumStarting 137 | }); 138 | } 139 | 140 | // trailing code 141 | if (buf.trim().length) { 142 | comment = comments[comments.length - 1]; 143 | // Adjust codeStart for any vertical space between comment and code 144 | comment.codeStart += buf.match(/^(\s*)/)[0].split('\n').length - 1; 145 | comment.code = code = exports.trimIndentation(buf).trim(); 146 | comment.ctx = exports.parseCodeContext(code, parentContext); 147 | } 148 | 149 | return comments; 150 | }; 151 | 152 | /** 153 | * Removes excess indentation from string of code. 154 | * 155 | * @param {String} str 156 | * @return {String} 157 | * @api public 158 | */ 159 | 160 | exports.trimIndentation = function (str) { 161 | // Find indentation from first line of code. 162 | var indent = str.match(/(?:^|\n)([ \t]*)[^\s]/); 163 | if (indent) { 164 | // Replace indentation on all lines. 165 | str = str.replace(new RegExp('(^|\n)' + indent[1], 'g'), '$1'); 166 | } 167 | return str; 168 | }; 169 | 170 | /** 171 | * Parse the given comment `str`. 172 | * 173 | * The comment object returned contains the following 174 | * 175 | * - `tags` array of tag objects 176 | * - `description` the first line of the comment 177 | * - `body` lines following the description 178 | * - `content` both the description and the body 179 | * - `isPrivate` true when "@api private" is used 180 | * 181 | * @param {String} str 182 | * @param {Object} options 183 | * @return {Object} 184 | * @see exports.parseTag 185 | * @api public 186 | */ 187 | 188 | exports.parseComment = function(str, options) { 189 | str = str.trim(); 190 | options = options || {}; 191 | 192 | var comment = { tags: [] } 193 | , raw = options.raw 194 | , description = {} 195 | , tags = str.split(/\n\s*@/); 196 | 197 | // A comment has no description 198 | if (tags[0].charAt(0) === '@') { 199 | tags.unshift(''); 200 | } 201 | 202 | // parse comment body 203 | description.full = tags[0]; 204 | description.summary = description.full.split('\n\n')[0]; 205 | description.body = description.full.split('\n\n').slice(1).join('\n\n'); 206 | comment.description = description; 207 | 208 | // parse tags 209 | if (tags.length) { 210 | comment.tags = tags.slice(1).map(exports.parseTag); 211 | comment.isPrivate = comment.tags.some(function(tag){ 212 | return 'private' == tag.visibility; 213 | }); 214 | comment.isConstructor = comment.tags.some(function(tag){ 215 | return 'constructor' == tag.type || 'augments' == tag.type; 216 | }); 217 | comment.isClass = comment.tags.some(function(tag){ 218 | return 'class' == tag.type; 219 | }); 220 | comment.isEvent = comment.tags.some(function(tag){ 221 | return 'event' == tag.type; 222 | }); 223 | 224 | if (!description.full || !description.full.trim()) { 225 | comment.tags.some(function(tag){ 226 | if ('description' == tag.type) { 227 | description.full = tag.full; 228 | description.summary = tag.summary; 229 | description.body = tag.body; 230 | return true; 231 | } 232 | }); 233 | } 234 | } 235 | 236 | // markdown 237 | if (!raw) { 238 | description.full = markdown.render(description.full).trim(); 239 | description.summary = markdown.render(description.summary).trim(); 240 | description.body = markdown.render(description.body).trim(); 241 | comment.tags.forEach(function (tag) { 242 | if (tag.description) tag.description = markdown.render(tag.description).trim(); 243 | else tag.html = markdown.render(tag.string).trim(); 244 | }); 245 | } 246 | 247 | return comment; 248 | }; 249 | 250 | //TODO: Find a smarter way to do this 251 | /** 252 | * Extracts different parts of a tag by splitting string into pieces separated by whitespace. If the white spaces are 253 | * somewhere between curly braces (which is used to indicate param/return type in JSDoc) they will not be used to split 254 | * the string. This allows to specify jsdoc tags without the need to eliminate all white spaces i.e. {number | string} 255 | * 256 | * @param str The tag line as a string that needs to be split into parts 257 | * @returns {Array.} An array of strings containing the parts 258 | */ 259 | 260 | exports.extractTagParts = function(str) { 261 | var level = 0, 262 | extract = '', 263 | split = []; 264 | 265 | str.split('').forEach(function(c) { 266 | if(c.match(/\s/) && level === 0) { 267 | split.push(extract); 268 | extract = ''; 269 | } else { 270 | if(c === '{') { 271 | level++; 272 | } else if (c === '}') { 273 | level--; 274 | } 275 | 276 | extract += c; 277 | } 278 | }); 279 | 280 | split.push(extract); 281 | return split.filter(function(str) { 282 | return str.length > 0; 283 | }); 284 | }; 285 | 286 | 287 | /** 288 | * Parse tag string "@param {Array} name description" etc. 289 | * 290 | * @param {String} 291 | * @return {Object} 292 | * @api public 293 | */ 294 | 295 | exports.parseTag = function(str) { 296 | var tag = {} 297 | , lines = str.split('\n') 298 | , parts = exports.extractTagParts(lines[0]) 299 | , type = tag.type = parts.shift().replace('@', '') 300 | , matchType = new RegExp('^@?' + type + ' *') 301 | , matchTypeStr = /^\{.+\}$/; 302 | 303 | tag.string = str.replace(matchType, ''); 304 | 305 | if (lines.length > 1) { 306 | parts.push(lines.slice(1).join('\n')); 307 | } 308 | 309 | switch (type) { 310 | case 'property': 311 | case 'template': 312 | case 'param': 313 | var typeString = matchTypeStr.test(parts[0]) ? parts.shift() : ""; 314 | tag.name = parts.shift() || ''; 315 | tag.description = parts.join(' '); 316 | exports.parseTagTypes(typeString, tag); 317 | break; 318 | case 'define': 319 | case 'return': 320 | case 'returns': 321 | var typeString = matchTypeStr.test(parts[0]) ? parts.shift() : ""; 322 | exports.parseTagTypes(typeString, tag); 323 | tag.description = parts.join(' '); 324 | break; 325 | case 'see': 326 | if (~str.indexOf('http')) { 327 | tag.title = parts.length > 1 328 | ? parts.shift() 329 | : ''; 330 | tag.url = parts.join(' '); 331 | } else { 332 | tag.local = parts.join(' '); 333 | } 334 | break; 335 | case 'api': 336 | tag.visibility = parts.shift(); 337 | break; 338 | case 'public': 339 | case 'private': 340 | case 'protected': 341 | tag.visibility = type; 342 | break; 343 | case 'enum': 344 | case 'typedef': 345 | case 'type': 346 | exports.parseTagTypes(parts.shift(), tag); 347 | break; 348 | case 'lends': 349 | case 'memberOf': 350 | tag.parent = parts.shift(); 351 | break; 352 | case 'extends': 353 | case 'implements': 354 | case 'augments': 355 | tag.otherClass = parts.shift(); 356 | break; 357 | case 'borrows': 358 | tag.otherMemberName = parts.join(' ').split(' as ')[0]; 359 | tag.thisMemberName = parts.join(' ').split(' as ')[1]; 360 | break; 361 | case 'throws': 362 | var typeString = matchTypeStr.test(parts[0]) ? parts.shift() : ""; 363 | tag.types = exports.parseTagTypes(typeString); 364 | tag.description = parts.join(' '); 365 | break; 366 | case 'description': 367 | tag.full = parts.join(' ').trim(); 368 | tag.summary = tag.full.split('\n\n')[0]; 369 | tag.body = tag.full.split('\n\n').slice(1).join('\n\n'); 370 | break; 371 | default: 372 | tag.string = parts.join(' ').replace(/\s+$/, ''); 373 | break; 374 | } 375 | 376 | return tag; 377 | }; 378 | 379 | /** 380 | * Parse tag type string "{Array|Object}" etc. 381 | * This function also supports complex type descriptors like in jsDoc or even the enhanced syntax used by the 382 | * [google closure compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler#types) 383 | * 384 | * The resulting array from the type descriptor `{number|string|{name:string,age:number|date}}` would look like this: 385 | * 386 | * [ 387 | * 'number', 388 | * 'string', 389 | * { 390 | * age: ['number', 'date'], 391 | * name: ['string'] 392 | * } 393 | * ] 394 | * 395 | * @param {String} str 396 | * @return {Array} 397 | * @api public 398 | */ 399 | 400 | exports.parseTagTypes = function(str, tag) { 401 | if (!str) { 402 | if(tag) { 403 | tag.types = []; 404 | tag.typesDescription = ""; 405 | tag.optional = tag.nullable = tag.nonNullable = tag.variable = false; 406 | } 407 | return []; 408 | } 409 | var {parse, publish, createDefaultPublisher, NodeType, SyntaxType} = require('jsdoctypeparser'); 410 | var result = parse(str.substring(1, str.length - 1)); 411 | 412 | var customPublisher = Object.assign({}, createDefaultPublisher(), { 413 | NAME(nameNode) { 414 | var output = '' + nameNode.name + ''; 415 | 416 | if (result.type === NodeType.OPTIONAL) { 417 | output += '|undefined'; 418 | } else if (result.type === NodeType.NULLABLE) { 419 | output += '|null'; 420 | } 421 | 422 | return output; 423 | } 424 | }); 425 | 426 | var types = (function transform(type) { 427 | if (type && type.type === NodeType.UNION) { 428 | return [transform(type.left), transform(type.right)].flat(); 429 | } else if (type && type.type === NodeType.NAME) { 430 | return [type.name]; 431 | } else if (type && type.type === NodeType.RECORD) { 432 | return [type.entries.reduce(function (obj, entry) { 433 | obj[entry.key] = transform(entry.value); 434 | return obj; 435 | }, {})]; 436 | } else if (type && type.type === NodeType.GENERIC) { 437 | if (type.meta.syntax === SyntaxType.GenericTypeSyntax.ANGLE_BRACKET) { 438 | return [type.subject.name + '<' + transform(type.objects[0]).join('|') + '>']; 439 | } else if (type.meta.syntax === SyntaxType.GenericTypeSyntax.ANGLE_BRACKET_WITH_DOT) { 440 | return [type.subject.name + '.<' + transform(type.objects[0]).join('|') + '>']; 441 | } else if (type.meta.syntax === SyntaxType.GenericTypeSyntax.SQUARE_BRACKET) { 442 | return [type.subject.name + '[' + transform(type.objects[0]).join('|') + ']']; 443 | } else if (type.meta.syntax === SyntaxType.VariadicTypeSyntax.PREFIX_DOTS) { 444 | return [`...${type.subject.name}`]; 445 | } else if (type.meta.syntax === SyntaxType.VariadicTypeSyntax.SUFFIX_DOTS) { 446 | return [`${type.subject.name}...`]; 447 | } else if (type.meta.syntax === SyntaxType.VariadicTypeSyntax.ONLY_DOTS) { 448 | return ['...']; 449 | } 450 | return [type.subject.name] 451 | } else if (type && type.value) { 452 | return transform(type.value); 453 | } else { 454 | return type.toString(); 455 | } 456 | }(result)); 457 | 458 | if(tag) { 459 | tag.types = types; 460 | tag.typesDescription = publish(result, customPublisher).replace(/^\?|=$/, ''); 461 | tag.optional = (tag.name && tag.name.slice(0,1) === '[') || result.type === NodeType.OPTIONAL; 462 | tag.nullable = result.type === NodeType.NULLABLE; 463 | tag.nonNullable = result.meta ? result.meta.syntax === 'SUFFIX_QUESTION_MARK' || result.meta.syntax === 'PREFIX_BANG': false; 464 | tag.variable = result.type === NodeType.VARIADIC; 465 | } 466 | 467 | return types; 468 | }; 469 | 470 | /** 471 | * Determine if a parameter is optional. 472 | * 473 | * Examples: 474 | * JSDoc: {Type} [name] 475 | * Google: {Type=} name 476 | * TypeScript: {Type?} name 477 | * 478 | * @param {Object} tag 479 | * @return {Boolean} 480 | * @api public 481 | */ 482 | 483 | exports.parseParamOptional = function(tag) { 484 | var lastTypeChar = tag.types.slice(-1)[0].slice(-1); 485 | return tag.name.slice(0,1) === '[' || lastTypeChar === '=' || lastTypeChar === '?'; 486 | }; 487 | 488 | /** 489 | * Parse the context from the given `str` of js. 490 | * 491 | * This method attempts to discover the context 492 | * for the comment based on it's code. Currently 493 | * supports: 494 | * 495 | * - classes 496 | * - class constructors 497 | * - class methods 498 | * - function statements 499 | * - function expressions 500 | * - prototype methods 501 | * - prototype properties 502 | * - methods 503 | * - properties 504 | * - declarations 505 | * 506 | * @param {String} str 507 | * @param {Object=} parentContext An indication if we are already in something. Like a namespace or an inline declaration. 508 | * @return {Object} 509 | * @api public 510 | */ 511 | 512 | exports.parseCodeContext = function(str, parentContext) { 513 | parentContext = parentContext || {}; 514 | 515 | var ctx; 516 | 517 | // loop through all context matchers, returning the first successful match 518 | return exports.contextPatternMatchers.some(function (matcher) { 519 | return ctx = matcher(str, parentContext); 520 | }) && ctx; 521 | }; 522 | 523 | exports.contextPatternMatchers = [ 524 | 525 | function (str) { 526 | // class, possibly exported by name or as a default 527 | if (/^\s*(export(\s+default)?\s+)?class\s+([\w$]+)(\s+extends\s+([\w$.]+(?:\(.*\))?))?\s*{/.exec(str)) { 528 | return { 529 | type: 'class' 530 | , constructor: RegExp.$3 531 | , cons: RegExp.$3 532 | , name: RegExp.$3 533 | , extends: RegExp.$5 534 | , string: 'new ' + RegExp.$3 + '()' 535 | }; 536 | } 537 | }, 538 | 539 | function (str, parentContext) { 540 | // class constructor 541 | if (/^\s*constructor\s*\(/.exec(str)) { 542 | return { 543 | type: 'constructor' 544 | , constructor: parentContext.name 545 | , cons: parentContext.name 546 | , name: 'constructor' 547 | , string: (parentContext && parentContext.name && parentContext.name + '.prototype.' || '') + 'constructor()' 548 | }; 549 | // class method 550 | } 551 | }, 552 | 553 | function (str, parentContext) { 554 | if (/^\s*(static)?\s*(\*)?\s*([\w$]+|\[.*\])\s*\(/.exec(str)) { 555 | return { 556 | type: 'method' 557 | , constructor: parentContext.name 558 | , cons: parentContext.name 559 | , name: RegExp.$2 + RegExp.$3 560 | , string: (parentContext && parentContext.name && parentContext.name + (RegExp.$1 ? '.' : '.prototype.') || '') + RegExp.$2 + RegExp.$3 + '()' 561 | }; 562 | // named function statement, possibly exported by name or as a default 563 | } 564 | }, 565 | 566 | function (str) { 567 | if (/^\s*(export(\s+default)?\s+)?function\s+([\w$]+)\s*\(/.exec(str)) { 568 | return { 569 | type: 'function' 570 | , name: RegExp.$3 571 | , string: RegExp.$3 + '()' 572 | }; 573 | } 574 | }, 575 | 576 | function (str) { 577 | // anonymous function expression exported as a default 578 | if (/^\s*export\s+default\s+function\s*\(/.exec(str)) { 579 | return { 580 | type: 'function' 581 | , name: RegExp.$1 // undefined 582 | , string: RegExp.$1 + '()' 583 | }; 584 | } 585 | }, 586 | 587 | function (str) { 588 | // function expression 589 | if (/^return\s+function(?:\s+([\w$]+))?\s*\(/.exec(str)) { 590 | return { 591 | type: 'function' 592 | , name: RegExp.$1 593 | , string: RegExp.$1 + '()' 594 | }; 595 | } 596 | }, 597 | 598 | function (str) { 599 | // function expression 600 | if (/^\s*(?:const|let|var)\s+([\w$]+)\s*=\s*function/.exec(str)) { 601 | return { 602 | type: 'function' 603 | , name: RegExp.$1 604 | , string: RegExp.$1 + '()' 605 | }; 606 | } 607 | }, 608 | 609 | function (str, parentContext) { 610 | // prototype method 611 | if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\.\s*([\w$]+)\s*=\s*function/.exec(str)) { 612 | return { 613 | type: 'method' 614 | , constructor: RegExp.$1 615 | , cons: RegExp.$1 616 | , name: RegExp.$2 617 | , string: RegExp.$1 + '.prototype.' + RegExp.$2 + '()' 618 | }; 619 | } 620 | }, 621 | 622 | function (str) { 623 | // prototype property 624 | if (/^\s*([\w$.]+)\s*\.\s*prototype\s*\.\s*([\w$]+)\s*=\s*([^\n;]+)/.exec(str)) { 625 | return { 626 | type: 'property' 627 | , constructor: RegExp.$1 628 | , cons: RegExp.$1 629 | , name: RegExp.$2 630 | , value: RegExp.$3.trim() 631 | , string: RegExp.$1 + '.prototype.' + RegExp.$2 632 | }; 633 | } 634 | }, 635 | 636 | function (str) { 637 | // prototype property without assignment 638 | if (/^\s*([\w$]+)\s*\.\s*prototype\s*\.\s*([\w$]+)\s*/.exec(str)) { 639 | return { 640 | type: 'property' 641 | , constructor: RegExp.$1 642 | , cons: RegExp.$1 643 | , name: RegExp.$2 644 | , string: RegExp.$1 + '.prototype.' + RegExp.$2 645 | }; 646 | } 647 | }, 648 | 649 | function (str) { 650 | // inline prototype 651 | if (/^\s*([\w$.]+)\s*\.\s*prototype\s*=\s*{/.exec(str)) { 652 | return { 653 | type: 'prototype' 654 | , constructor: RegExp.$1 655 | , cons: RegExp.$1 656 | , name: RegExp.$1 657 | , string: RegExp.$1 + '.prototype' 658 | }; 659 | } 660 | }, 661 | 662 | function (str, parentContext) { 663 | // inline method 664 | if (/^\s*([\w$.]+)\s*:\s*function/.exec(str)) { 665 | return { 666 | type: 'method' 667 | , constructor: parentContext.name 668 | , cons: parentContext.name 669 | , name: RegExp.$1 670 | , string: (parentContext && parentContext.name && parentContext.name + '.prototype.' || '') + RegExp.$1 + '()' 671 | }; 672 | } 673 | }, 674 | 675 | function (str, parentContext) { 676 | // inline property 677 | if (/^\s*([\w$.]+)\s*:\s*([^\n;]+)/.exec(str)) { 678 | return { 679 | type: 'property' 680 | , constructor: parentContext.name 681 | , cons: parentContext.name 682 | , name: RegExp.$1 683 | , value: RegExp.$2.trim() 684 | , string: (parentContext && parentContext.name && parentContext.name + '.' || '') + RegExp.$1 685 | }; 686 | } 687 | }, 688 | 689 | function (str, parentContext) { 690 | // inline getter/setter 691 | if (/^\s*(get|set)\s*([\w$.]+)\s*\(/.exec(str)) { 692 | return { 693 | type: 'property' 694 | , constructor: parentContext.name 695 | , cons: parentContext.name 696 | , name: RegExp.$2 697 | , string: (parentContext && parentContext.name && parentContext.name + '.prototype.' || '') + RegExp.$2 698 | }; 699 | } 700 | }, 701 | 702 | function (str) { 703 | // method 704 | if (/^\s*([\w$.]+)\s*\.\s*([\w$]+)\s*=\s*function/.exec(str)) { 705 | return { 706 | type: 'method' 707 | , receiver: RegExp.$1 708 | , name: RegExp.$2 709 | , string: RegExp.$1 + '.' + RegExp.$2 + '()' 710 | }; 711 | } 712 | }, 713 | 714 | function (str) { 715 | // property 716 | if (/^\s*([\w$.]+)\s*\.\s*([\w$]+)\s*=\s*([^\n;]+)/.exec(str)) { 717 | return { 718 | type: 'property' 719 | , receiver: RegExp.$1 720 | , name: RegExp.$2 721 | , value: RegExp.$3.trim() 722 | , string: RegExp.$1 + '.' + RegExp.$2 723 | }; 724 | } 725 | }, 726 | 727 | function (str) { 728 | // declaration 729 | if (/^\s*(?:const|let|var)\s+([\w$]+)\s*=\s*([^\n;]+)/.exec(str)) { 730 | return { 731 | type: 'declaration' 732 | , name: RegExp.$1 733 | , value: RegExp.$2.trim() 734 | , string: RegExp.$1 735 | }; 736 | } 737 | } 738 | ]; 739 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dox", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "dox", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "commander": "9.4.0", 13 | "jsdoctypeparser": "^9.0.0", 14 | "markdown-it": "13.0.1" 15 | }, 16 | "bin": { 17 | "dox": "bin/dox" 18 | }, 19 | "devDependencies": { 20 | "mocha": "10.0.0", 21 | "should": "13.2.3" 22 | } 23 | }, 24 | "node_modules/@ungap/promise-all-settled": { 25 | "version": "1.1.2", 26 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 27 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 28 | "dev": true 29 | }, 30 | "node_modules/ansi-colors": { 31 | "version": "4.1.1", 32 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 33 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 34 | "dev": true, 35 | "engines": { 36 | "node": ">=6" 37 | } 38 | }, 39 | "node_modules/ansi-regex": { 40 | "version": "5.0.1", 41 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 42 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 43 | "dev": true, 44 | "engines": { 45 | "node": ">=8" 46 | } 47 | }, 48 | "node_modules/ansi-styles": { 49 | "version": "4.3.0", 50 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 51 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 52 | "dev": true, 53 | "dependencies": { 54 | "color-convert": "^2.0.1" 55 | }, 56 | "engines": { 57 | "node": ">=8" 58 | }, 59 | "funding": { 60 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 61 | } 62 | }, 63 | "node_modules/anymatch": { 64 | "version": "3.1.2", 65 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 66 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 67 | "dev": true, 68 | "dependencies": { 69 | "normalize-path": "^3.0.0", 70 | "picomatch": "^2.0.4" 71 | }, 72 | "engines": { 73 | "node": ">= 8" 74 | } 75 | }, 76 | "node_modules/argparse": { 77 | "version": "2.0.1", 78 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 79 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 80 | }, 81 | "node_modules/balanced-match": { 82 | "version": "1.0.2", 83 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 84 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 85 | "dev": true 86 | }, 87 | "node_modules/binary-extensions": { 88 | "version": "2.2.0", 89 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 90 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 91 | "dev": true, 92 | "engines": { 93 | "node": ">=8" 94 | } 95 | }, 96 | "node_modules/brace-expansion": { 97 | "version": "2.0.1", 98 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 99 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 100 | "dev": true, 101 | "dependencies": { 102 | "balanced-match": "^1.0.0" 103 | } 104 | }, 105 | "node_modules/braces": { 106 | "version": "3.0.2", 107 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 108 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 109 | "dev": true, 110 | "dependencies": { 111 | "fill-range": "^7.0.1" 112 | }, 113 | "engines": { 114 | "node": ">=8" 115 | } 116 | }, 117 | "node_modules/browser-stdout": { 118 | "version": "1.3.1", 119 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 120 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 121 | "dev": true 122 | }, 123 | "node_modules/camelcase": { 124 | "version": "6.3.0", 125 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 126 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 127 | "dev": true, 128 | "engines": { 129 | "node": ">=10" 130 | }, 131 | "funding": { 132 | "url": "https://github.com/sponsors/sindresorhus" 133 | } 134 | }, 135 | "node_modules/chalk": { 136 | "version": "4.1.2", 137 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 138 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 139 | "dev": true, 140 | "dependencies": { 141 | "ansi-styles": "^4.1.0", 142 | "supports-color": "^7.1.0" 143 | }, 144 | "engines": { 145 | "node": ">=10" 146 | }, 147 | "funding": { 148 | "url": "https://github.com/chalk/chalk?sponsor=1" 149 | } 150 | }, 151 | "node_modules/chalk/node_modules/supports-color": { 152 | "version": "7.2.0", 153 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 154 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 155 | "dev": true, 156 | "dependencies": { 157 | "has-flag": "^4.0.0" 158 | }, 159 | "engines": { 160 | "node": ">=8" 161 | } 162 | }, 163 | "node_modules/chokidar": { 164 | "version": "3.5.3", 165 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 166 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 167 | "dev": true, 168 | "funding": [ 169 | { 170 | "type": "individual", 171 | "url": "https://paulmillr.com/funding/" 172 | } 173 | ], 174 | "dependencies": { 175 | "anymatch": "~3.1.2", 176 | "braces": "~3.0.2", 177 | "glob-parent": "~5.1.2", 178 | "is-binary-path": "~2.1.0", 179 | "is-glob": "~4.0.1", 180 | "normalize-path": "~3.0.0", 181 | "readdirp": "~3.6.0" 182 | }, 183 | "engines": { 184 | "node": ">= 8.10.0" 185 | }, 186 | "optionalDependencies": { 187 | "fsevents": "~2.3.2" 188 | } 189 | }, 190 | "node_modules/cliui": { 191 | "version": "7.0.4", 192 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 193 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 194 | "dev": true, 195 | "dependencies": { 196 | "string-width": "^4.2.0", 197 | "strip-ansi": "^6.0.0", 198 | "wrap-ansi": "^7.0.0" 199 | } 200 | }, 201 | "node_modules/color-convert": { 202 | "version": "2.0.1", 203 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 204 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 205 | "dev": true, 206 | "dependencies": { 207 | "color-name": "~1.1.4" 208 | }, 209 | "engines": { 210 | "node": ">=7.0.0" 211 | } 212 | }, 213 | "node_modules/color-name": { 214 | "version": "1.1.4", 215 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 216 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 217 | "dev": true 218 | }, 219 | "node_modules/commander": { 220 | "version": "9.4.0", 221 | "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", 222 | "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", 223 | "engines": { 224 | "node": "^12.20.0 || >=14" 225 | } 226 | }, 227 | "node_modules/concat-map": { 228 | "version": "0.0.1", 229 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 230 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 231 | "dev": true 232 | }, 233 | "node_modules/debug": { 234 | "version": "4.3.4", 235 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 236 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 237 | "dev": true, 238 | "dependencies": { 239 | "ms": "2.1.2" 240 | }, 241 | "engines": { 242 | "node": ">=6.0" 243 | }, 244 | "peerDependenciesMeta": { 245 | "supports-color": { 246 | "optional": true 247 | } 248 | } 249 | }, 250 | "node_modules/debug/node_modules/ms": { 251 | "version": "2.1.2", 252 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 253 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 254 | "dev": true 255 | }, 256 | "node_modules/decamelize": { 257 | "version": "4.0.0", 258 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 259 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 260 | "dev": true, 261 | "engines": { 262 | "node": ">=10" 263 | }, 264 | "funding": { 265 | "url": "https://github.com/sponsors/sindresorhus" 266 | } 267 | }, 268 | "node_modules/diff": { 269 | "version": "5.0.0", 270 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 271 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 272 | "dev": true, 273 | "engines": { 274 | "node": ">=0.3.1" 275 | } 276 | }, 277 | "node_modules/emoji-regex": { 278 | "version": "8.0.0", 279 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 280 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 281 | "dev": true 282 | }, 283 | "node_modules/entities": { 284 | "version": "3.0.1", 285 | "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", 286 | "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", 287 | "engines": { 288 | "node": ">=0.12" 289 | }, 290 | "funding": { 291 | "url": "https://github.com/fb55/entities?sponsor=1" 292 | } 293 | }, 294 | "node_modules/escalade": { 295 | "version": "3.1.1", 296 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 297 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 298 | "dev": true, 299 | "engines": { 300 | "node": ">=6" 301 | } 302 | }, 303 | "node_modules/escape-string-regexp": { 304 | "version": "4.0.0", 305 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 306 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 307 | "dev": true, 308 | "engines": { 309 | "node": ">=10" 310 | }, 311 | "funding": { 312 | "url": "https://github.com/sponsors/sindresorhus" 313 | } 314 | }, 315 | "node_modules/fill-range": { 316 | "version": "7.0.1", 317 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 318 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 319 | "dev": true, 320 | "dependencies": { 321 | "to-regex-range": "^5.0.1" 322 | }, 323 | "engines": { 324 | "node": ">=8" 325 | } 326 | }, 327 | "node_modules/find-up": { 328 | "version": "5.0.0", 329 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 330 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 331 | "dev": true, 332 | "dependencies": { 333 | "locate-path": "^6.0.0", 334 | "path-exists": "^4.0.0" 335 | }, 336 | "engines": { 337 | "node": ">=10" 338 | }, 339 | "funding": { 340 | "url": "https://github.com/sponsors/sindresorhus" 341 | } 342 | }, 343 | "node_modules/flat": { 344 | "version": "5.0.2", 345 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 346 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 347 | "dev": true, 348 | "bin": { 349 | "flat": "cli.js" 350 | } 351 | }, 352 | "node_modules/fs.realpath": { 353 | "version": "1.0.0", 354 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 355 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 356 | "dev": true 357 | }, 358 | "node_modules/fsevents": { 359 | "version": "2.3.2", 360 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 361 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 362 | "dev": true, 363 | "hasInstallScript": true, 364 | "optional": true, 365 | "os": [ 366 | "darwin" 367 | ], 368 | "engines": { 369 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 370 | } 371 | }, 372 | "node_modules/get-caller-file": { 373 | "version": "2.0.5", 374 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 375 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 376 | "dev": true, 377 | "engines": { 378 | "node": "6.* || 8.* || >= 10.*" 379 | } 380 | }, 381 | "node_modules/glob": { 382 | "version": "7.2.0", 383 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 384 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 385 | "dev": true, 386 | "dependencies": { 387 | "fs.realpath": "^1.0.0", 388 | "inflight": "^1.0.4", 389 | "inherits": "2", 390 | "minimatch": "^3.0.4", 391 | "once": "^1.3.0", 392 | "path-is-absolute": "^1.0.0" 393 | }, 394 | "engines": { 395 | "node": "*" 396 | }, 397 | "funding": { 398 | "url": "https://github.com/sponsors/isaacs" 399 | } 400 | }, 401 | "node_modules/glob-parent": { 402 | "version": "5.1.2", 403 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 404 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 405 | "dev": true, 406 | "dependencies": { 407 | "is-glob": "^4.0.1" 408 | }, 409 | "engines": { 410 | "node": ">= 6" 411 | } 412 | }, 413 | "node_modules/glob/node_modules/brace-expansion": { 414 | "version": "1.1.11", 415 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 416 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 417 | "dev": true, 418 | "dependencies": { 419 | "balanced-match": "^1.0.0", 420 | "concat-map": "0.0.1" 421 | } 422 | }, 423 | "node_modules/glob/node_modules/minimatch": { 424 | "version": "3.1.2", 425 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 426 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 427 | "dev": true, 428 | "dependencies": { 429 | "brace-expansion": "^1.1.7" 430 | }, 431 | "engines": { 432 | "node": "*" 433 | } 434 | }, 435 | "node_modules/has-flag": { 436 | "version": "4.0.0", 437 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 438 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 439 | "dev": true, 440 | "engines": { 441 | "node": ">=8" 442 | } 443 | }, 444 | "node_modules/he": { 445 | "version": "1.2.0", 446 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 447 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 448 | "dev": true, 449 | "bin": { 450 | "he": "bin/he" 451 | } 452 | }, 453 | "node_modules/inflight": { 454 | "version": "1.0.6", 455 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 456 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 457 | "dev": true, 458 | "dependencies": { 459 | "once": "^1.3.0", 460 | "wrappy": "1" 461 | } 462 | }, 463 | "node_modules/inherits": { 464 | "version": "2.0.4", 465 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 466 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 467 | "dev": true 468 | }, 469 | "node_modules/is-binary-path": { 470 | "version": "2.1.0", 471 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 472 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 473 | "dev": true, 474 | "dependencies": { 475 | "binary-extensions": "^2.0.0" 476 | }, 477 | "engines": { 478 | "node": ">=8" 479 | } 480 | }, 481 | "node_modules/is-extglob": { 482 | "version": "2.1.1", 483 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 484 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 485 | "dev": true, 486 | "engines": { 487 | "node": ">=0.10.0" 488 | } 489 | }, 490 | "node_modules/is-fullwidth-code-point": { 491 | "version": "3.0.0", 492 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 493 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 494 | "dev": true, 495 | "engines": { 496 | "node": ">=8" 497 | } 498 | }, 499 | "node_modules/is-glob": { 500 | "version": "4.0.3", 501 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 502 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 503 | "dev": true, 504 | "dependencies": { 505 | "is-extglob": "^2.1.1" 506 | }, 507 | "engines": { 508 | "node": ">=0.10.0" 509 | } 510 | }, 511 | "node_modules/is-number": { 512 | "version": "7.0.0", 513 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 514 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 515 | "dev": true, 516 | "engines": { 517 | "node": ">=0.12.0" 518 | } 519 | }, 520 | "node_modules/is-plain-obj": { 521 | "version": "2.1.0", 522 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 523 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 524 | "dev": true, 525 | "engines": { 526 | "node": ">=8" 527 | } 528 | }, 529 | "node_modules/is-unicode-supported": { 530 | "version": "0.1.0", 531 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 532 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 533 | "dev": true, 534 | "engines": { 535 | "node": ">=10" 536 | }, 537 | "funding": { 538 | "url": "https://github.com/sponsors/sindresorhus" 539 | } 540 | }, 541 | "node_modules/js-yaml": { 542 | "version": "4.1.0", 543 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 544 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 545 | "dev": true, 546 | "dependencies": { 547 | "argparse": "^2.0.1" 548 | }, 549 | "bin": { 550 | "js-yaml": "bin/js-yaml.js" 551 | } 552 | }, 553 | "node_modules/jsdoctypeparser": { 554 | "version": "9.0.0", 555 | "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", 556 | "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==", 557 | "bin": { 558 | "jsdoctypeparser": "bin/jsdoctypeparser" 559 | }, 560 | "engines": { 561 | "node": ">=10" 562 | } 563 | }, 564 | "node_modules/linkify-it": { 565 | "version": "4.0.1", 566 | "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", 567 | "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", 568 | "dependencies": { 569 | "uc.micro": "^1.0.1" 570 | } 571 | }, 572 | "node_modules/locate-path": { 573 | "version": "6.0.0", 574 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 575 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 576 | "dev": true, 577 | "dependencies": { 578 | "p-locate": "^5.0.0" 579 | }, 580 | "engines": { 581 | "node": ">=10" 582 | }, 583 | "funding": { 584 | "url": "https://github.com/sponsors/sindresorhus" 585 | } 586 | }, 587 | "node_modules/log-symbols": { 588 | "version": "4.1.0", 589 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 590 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 591 | "dev": true, 592 | "dependencies": { 593 | "chalk": "^4.1.0", 594 | "is-unicode-supported": "^0.1.0" 595 | }, 596 | "engines": { 597 | "node": ">=10" 598 | }, 599 | "funding": { 600 | "url": "https://github.com/sponsors/sindresorhus" 601 | } 602 | }, 603 | "node_modules/markdown-it": { 604 | "version": "13.0.1", 605 | "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", 606 | "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", 607 | "dependencies": { 608 | "argparse": "^2.0.1", 609 | "entities": "~3.0.1", 610 | "linkify-it": "^4.0.1", 611 | "mdurl": "^1.0.1", 612 | "uc.micro": "^1.0.5" 613 | }, 614 | "bin": { 615 | "markdown-it": "bin/markdown-it.js" 616 | } 617 | }, 618 | "node_modules/mdurl": { 619 | "version": "1.0.1", 620 | "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", 621 | "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" 622 | }, 623 | "node_modules/minimatch": { 624 | "version": "5.0.1", 625 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 626 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 627 | "dev": true, 628 | "dependencies": { 629 | "brace-expansion": "^2.0.1" 630 | }, 631 | "engines": { 632 | "node": ">=10" 633 | } 634 | }, 635 | "node_modules/mocha": { 636 | "version": "10.0.0", 637 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", 638 | "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", 639 | "dev": true, 640 | "dependencies": { 641 | "@ungap/promise-all-settled": "1.1.2", 642 | "ansi-colors": "4.1.1", 643 | "browser-stdout": "1.3.1", 644 | "chokidar": "3.5.3", 645 | "debug": "4.3.4", 646 | "diff": "5.0.0", 647 | "escape-string-regexp": "4.0.0", 648 | "find-up": "5.0.0", 649 | "glob": "7.2.0", 650 | "he": "1.2.0", 651 | "js-yaml": "4.1.0", 652 | "log-symbols": "4.1.0", 653 | "minimatch": "5.0.1", 654 | "ms": "2.1.3", 655 | "nanoid": "3.3.3", 656 | "serialize-javascript": "6.0.0", 657 | "strip-json-comments": "3.1.1", 658 | "supports-color": "8.1.1", 659 | "workerpool": "6.2.1", 660 | "yargs": "16.2.0", 661 | "yargs-parser": "20.2.4", 662 | "yargs-unparser": "2.0.0" 663 | }, 664 | "bin": { 665 | "_mocha": "bin/_mocha", 666 | "mocha": "bin/mocha.js" 667 | }, 668 | "engines": { 669 | "node": ">= 14.0.0" 670 | }, 671 | "funding": { 672 | "type": "opencollective", 673 | "url": "https://opencollective.com/mochajs" 674 | } 675 | }, 676 | "node_modules/ms": { 677 | "version": "2.1.3", 678 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 679 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 680 | "dev": true 681 | }, 682 | "node_modules/nanoid": { 683 | "version": "3.3.3", 684 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 685 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 686 | "dev": true, 687 | "bin": { 688 | "nanoid": "bin/nanoid.cjs" 689 | }, 690 | "engines": { 691 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 692 | } 693 | }, 694 | "node_modules/normalize-path": { 695 | "version": "3.0.0", 696 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 697 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 698 | "dev": true, 699 | "engines": { 700 | "node": ">=0.10.0" 701 | } 702 | }, 703 | "node_modules/once": { 704 | "version": "1.4.0", 705 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 706 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 707 | "dev": true, 708 | "dependencies": { 709 | "wrappy": "1" 710 | } 711 | }, 712 | "node_modules/p-limit": { 713 | "version": "3.1.0", 714 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 715 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 716 | "dev": true, 717 | "dependencies": { 718 | "yocto-queue": "^0.1.0" 719 | }, 720 | "engines": { 721 | "node": ">=10" 722 | }, 723 | "funding": { 724 | "url": "https://github.com/sponsors/sindresorhus" 725 | } 726 | }, 727 | "node_modules/p-locate": { 728 | "version": "5.0.0", 729 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 730 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 731 | "dev": true, 732 | "dependencies": { 733 | "p-limit": "^3.0.2" 734 | }, 735 | "engines": { 736 | "node": ">=10" 737 | }, 738 | "funding": { 739 | "url": "https://github.com/sponsors/sindresorhus" 740 | } 741 | }, 742 | "node_modules/path-exists": { 743 | "version": "4.0.0", 744 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 745 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 746 | "dev": true, 747 | "engines": { 748 | "node": ">=8" 749 | } 750 | }, 751 | "node_modules/path-is-absolute": { 752 | "version": "1.0.1", 753 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 754 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 755 | "dev": true, 756 | "engines": { 757 | "node": ">=0.10.0" 758 | } 759 | }, 760 | "node_modules/picomatch": { 761 | "version": "2.3.1", 762 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 763 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 764 | "dev": true, 765 | "engines": { 766 | "node": ">=8.6" 767 | }, 768 | "funding": { 769 | "url": "https://github.com/sponsors/jonschlinkert" 770 | } 771 | }, 772 | "node_modules/randombytes": { 773 | "version": "2.1.0", 774 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 775 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 776 | "dev": true, 777 | "dependencies": { 778 | "safe-buffer": "^5.1.0" 779 | } 780 | }, 781 | "node_modules/readdirp": { 782 | "version": "3.6.0", 783 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 784 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 785 | "dev": true, 786 | "dependencies": { 787 | "picomatch": "^2.2.1" 788 | }, 789 | "engines": { 790 | "node": ">=8.10.0" 791 | } 792 | }, 793 | "node_modules/require-directory": { 794 | "version": "2.1.1", 795 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 796 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 797 | "dev": true, 798 | "engines": { 799 | "node": ">=0.10.0" 800 | } 801 | }, 802 | "node_modules/safe-buffer": { 803 | "version": "5.2.1", 804 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 805 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 806 | "dev": true, 807 | "funding": [ 808 | { 809 | "type": "github", 810 | "url": "https://github.com/sponsors/feross" 811 | }, 812 | { 813 | "type": "patreon", 814 | "url": "https://www.patreon.com/feross" 815 | }, 816 | { 817 | "type": "consulting", 818 | "url": "https://feross.org/support" 819 | } 820 | ] 821 | }, 822 | "node_modules/serialize-javascript": { 823 | "version": "6.0.0", 824 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 825 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 826 | "dev": true, 827 | "dependencies": { 828 | "randombytes": "^2.1.0" 829 | } 830 | }, 831 | "node_modules/should": { 832 | "version": "13.2.3", 833 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 834 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 835 | "dev": true, 836 | "dependencies": { 837 | "should-equal": "^2.0.0", 838 | "should-format": "^3.0.3", 839 | "should-type": "^1.4.0", 840 | "should-type-adaptors": "^1.0.1", 841 | "should-util": "^1.0.0" 842 | } 843 | }, 844 | "node_modules/should-equal": { 845 | "version": "2.0.0", 846 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 847 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 848 | "dev": true, 849 | "dependencies": { 850 | "should-type": "^1.4.0" 851 | } 852 | }, 853 | "node_modules/should-format": { 854 | "version": "3.0.3", 855 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 856 | "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", 857 | "dev": true, 858 | "dependencies": { 859 | "should-type": "^1.3.0", 860 | "should-type-adaptors": "^1.0.1" 861 | } 862 | }, 863 | "node_modules/should-type": { 864 | "version": "1.4.0", 865 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 866 | "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", 867 | "dev": true 868 | }, 869 | "node_modules/should-type-adaptors": { 870 | "version": "1.1.0", 871 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 872 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 873 | "dev": true, 874 | "dependencies": { 875 | "should-type": "^1.3.0", 876 | "should-util": "^1.0.0" 877 | } 878 | }, 879 | "node_modules/should-util": { 880 | "version": "1.0.1", 881 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 882 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 883 | "dev": true 884 | }, 885 | "node_modules/string-width": { 886 | "version": "4.2.3", 887 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 888 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 889 | "dev": true, 890 | "dependencies": { 891 | "emoji-regex": "^8.0.0", 892 | "is-fullwidth-code-point": "^3.0.0", 893 | "strip-ansi": "^6.0.1" 894 | }, 895 | "engines": { 896 | "node": ">=8" 897 | } 898 | }, 899 | "node_modules/strip-ansi": { 900 | "version": "6.0.1", 901 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 902 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 903 | "dev": true, 904 | "dependencies": { 905 | "ansi-regex": "^5.0.1" 906 | }, 907 | "engines": { 908 | "node": ">=8" 909 | } 910 | }, 911 | "node_modules/strip-json-comments": { 912 | "version": "3.1.1", 913 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 914 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 915 | "dev": true, 916 | "engines": { 917 | "node": ">=8" 918 | }, 919 | "funding": { 920 | "url": "https://github.com/sponsors/sindresorhus" 921 | } 922 | }, 923 | "node_modules/supports-color": { 924 | "version": "8.1.1", 925 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 926 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 927 | "dev": true, 928 | "dependencies": { 929 | "has-flag": "^4.0.0" 930 | }, 931 | "engines": { 932 | "node": ">=10" 933 | }, 934 | "funding": { 935 | "url": "https://github.com/chalk/supports-color?sponsor=1" 936 | } 937 | }, 938 | "node_modules/to-regex-range": { 939 | "version": "5.0.1", 940 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 941 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 942 | "dev": true, 943 | "dependencies": { 944 | "is-number": "^7.0.0" 945 | }, 946 | "engines": { 947 | "node": ">=8.0" 948 | } 949 | }, 950 | "node_modules/uc.micro": { 951 | "version": "1.0.6", 952 | "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", 953 | "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" 954 | }, 955 | "node_modules/workerpool": { 956 | "version": "6.2.1", 957 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 958 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 959 | "dev": true 960 | }, 961 | "node_modules/wrap-ansi": { 962 | "version": "7.0.0", 963 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 964 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 965 | "dev": true, 966 | "dependencies": { 967 | "ansi-styles": "^4.0.0", 968 | "string-width": "^4.1.0", 969 | "strip-ansi": "^6.0.0" 970 | }, 971 | "engines": { 972 | "node": ">=10" 973 | }, 974 | "funding": { 975 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 976 | } 977 | }, 978 | "node_modules/wrappy": { 979 | "version": "1.0.2", 980 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 981 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 982 | "dev": true 983 | }, 984 | "node_modules/y18n": { 985 | "version": "5.0.8", 986 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 987 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 988 | "dev": true, 989 | "engines": { 990 | "node": ">=10" 991 | } 992 | }, 993 | "node_modules/yargs": { 994 | "version": "16.2.0", 995 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 996 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 997 | "dev": true, 998 | "dependencies": { 999 | "cliui": "^7.0.2", 1000 | "escalade": "^3.1.1", 1001 | "get-caller-file": "^2.0.5", 1002 | "require-directory": "^2.1.1", 1003 | "string-width": "^4.2.0", 1004 | "y18n": "^5.0.5", 1005 | "yargs-parser": "^20.2.2" 1006 | }, 1007 | "engines": { 1008 | "node": ">=10" 1009 | } 1010 | }, 1011 | "node_modules/yargs-parser": { 1012 | "version": "20.2.4", 1013 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1014 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1015 | "dev": true, 1016 | "engines": { 1017 | "node": ">=10" 1018 | } 1019 | }, 1020 | "node_modules/yargs-unparser": { 1021 | "version": "2.0.0", 1022 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1023 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1024 | "dev": true, 1025 | "dependencies": { 1026 | "camelcase": "^6.0.0", 1027 | "decamelize": "^4.0.0", 1028 | "flat": "^5.0.2", 1029 | "is-plain-obj": "^2.1.0" 1030 | }, 1031 | "engines": { 1032 | "node": ">=10" 1033 | } 1034 | }, 1035 | "node_modules/yocto-queue": { 1036 | "version": "0.1.0", 1037 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1038 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1039 | "dev": true, 1040 | "engines": { 1041 | "node": ">=10" 1042 | }, 1043 | "funding": { 1044 | "url": "https://github.com/sponsors/sindresorhus" 1045 | } 1046 | } 1047 | }, 1048 | "dependencies": { 1049 | "@ungap/promise-all-settled": { 1050 | "version": "1.1.2", 1051 | "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", 1052 | "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", 1053 | "dev": true 1054 | }, 1055 | "ansi-colors": { 1056 | "version": "4.1.1", 1057 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 1058 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 1059 | "dev": true 1060 | }, 1061 | "ansi-regex": { 1062 | "version": "5.0.1", 1063 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1064 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1065 | "dev": true 1066 | }, 1067 | "ansi-styles": { 1068 | "version": "4.3.0", 1069 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1070 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1071 | "dev": true, 1072 | "requires": { 1073 | "color-convert": "^2.0.1" 1074 | } 1075 | }, 1076 | "anymatch": { 1077 | "version": "3.1.2", 1078 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", 1079 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", 1080 | "dev": true, 1081 | "requires": { 1082 | "normalize-path": "^3.0.0", 1083 | "picomatch": "^2.0.4" 1084 | } 1085 | }, 1086 | "argparse": { 1087 | "version": "2.0.1", 1088 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1089 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 1090 | }, 1091 | "balanced-match": { 1092 | "version": "1.0.2", 1093 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1094 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1095 | "dev": true 1096 | }, 1097 | "binary-extensions": { 1098 | "version": "2.2.0", 1099 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1100 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1101 | "dev": true 1102 | }, 1103 | "brace-expansion": { 1104 | "version": "2.0.1", 1105 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 1106 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 1107 | "dev": true, 1108 | "requires": { 1109 | "balanced-match": "^1.0.0" 1110 | } 1111 | }, 1112 | "braces": { 1113 | "version": "3.0.2", 1114 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1115 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1116 | "dev": true, 1117 | "requires": { 1118 | "fill-range": "^7.0.1" 1119 | } 1120 | }, 1121 | "browser-stdout": { 1122 | "version": "1.3.1", 1123 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 1124 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 1125 | "dev": true 1126 | }, 1127 | "camelcase": { 1128 | "version": "6.3.0", 1129 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", 1130 | "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", 1131 | "dev": true 1132 | }, 1133 | "chalk": { 1134 | "version": "4.1.2", 1135 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1136 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1137 | "dev": true, 1138 | "requires": { 1139 | "ansi-styles": "^4.1.0", 1140 | "supports-color": "^7.1.0" 1141 | }, 1142 | "dependencies": { 1143 | "supports-color": { 1144 | "version": "7.2.0", 1145 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1146 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1147 | "dev": true, 1148 | "requires": { 1149 | "has-flag": "^4.0.0" 1150 | } 1151 | } 1152 | } 1153 | }, 1154 | "chokidar": { 1155 | "version": "3.5.3", 1156 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1157 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1158 | "dev": true, 1159 | "requires": { 1160 | "anymatch": "~3.1.2", 1161 | "braces": "~3.0.2", 1162 | "fsevents": "~2.3.2", 1163 | "glob-parent": "~5.1.2", 1164 | "is-binary-path": "~2.1.0", 1165 | "is-glob": "~4.0.1", 1166 | "normalize-path": "~3.0.0", 1167 | "readdirp": "~3.6.0" 1168 | } 1169 | }, 1170 | "cliui": { 1171 | "version": "7.0.4", 1172 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 1173 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 1174 | "dev": true, 1175 | "requires": { 1176 | "string-width": "^4.2.0", 1177 | "strip-ansi": "^6.0.0", 1178 | "wrap-ansi": "^7.0.0" 1179 | } 1180 | }, 1181 | "color-convert": { 1182 | "version": "2.0.1", 1183 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1184 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1185 | "dev": true, 1186 | "requires": { 1187 | "color-name": "~1.1.4" 1188 | } 1189 | }, 1190 | "color-name": { 1191 | "version": "1.1.4", 1192 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1193 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1194 | "dev": true 1195 | }, 1196 | "commander": { 1197 | "version": "9.4.0", 1198 | "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", 1199 | "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==" 1200 | }, 1201 | "concat-map": { 1202 | "version": "0.0.1", 1203 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1204 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1205 | "dev": true 1206 | }, 1207 | "debug": { 1208 | "version": "4.3.4", 1209 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 1210 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 1211 | "dev": true, 1212 | "requires": { 1213 | "ms": "2.1.2" 1214 | }, 1215 | "dependencies": { 1216 | "ms": { 1217 | "version": "2.1.2", 1218 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1219 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1220 | "dev": true 1221 | } 1222 | } 1223 | }, 1224 | "decamelize": { 1225 | "version": "4.0.0", 1226 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", 1227 | "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", 1228 | "dev": true 1229 | }, 1230 | "diff": { 1231 | "version": "5.0.0", 1232 | "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", 1233 | "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", 1234 | "dev": true 1235 | }, 1236 | "emoji-regex": { 1237 | "version": "8.0.0", 1238 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1239 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1240 | "dev": true 1241 | }, 1242 | "entities": { 1243 | "version": "3.0.1", 1244 | "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", 1245 | "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" 1246 | }, 1247 | "escalade": { 1248 | "version": "3.1.1", 1249 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 1250 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 1251 | "dev": true 1252 | }, 1253 | "escape-string-regexp": { 1254 | "version": "4.0.0", 1255 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1256 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1257 | "dev": true 1258 | }, 1259 | "fill-range": { 1260 | "version": "7.0.1", 1261 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1262 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1263 | "dev": true, 1264 | "requires": { 1265 | "to-regex-range": "^5.0.1" 1266 | } 1267 | }, 1268 | "find-up": { 1269 | "version": "5.0.0", 1270 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1271 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1272 | "dev": true, 1273 | "requires": { 1274 | "locate-path": "^6.0.0", 1275 | "path-exists": "^4.0.0" 1276 | } 1277 | }, 1278 | "flat": { 1279 | "version": "5.0.2", 1280 | "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", 1281 | "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", 1282 | "dev": true 1283 | }, 1284 | "fs.realpath": { 1285 | "version": "1.0.0", 1286 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1287 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1288 | "dev": true 1289 | }, 1290 | "fsevents": { 1291 | "version": "2.3.2", 1292 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1293 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1294 | "dev": true, 1295 | "optional": true 1296 | }, 1297 | "get-caller-file": { 1298 | "version": "2.0.5", 1299 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1300 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1301 | "dev": true 1302 | }, 1303 | "glob": { 1304 | "version": "7.2.0", 1305 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 1306 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 1307 | "dev": true, 1308 | "requires": { 1309 | "fs.realpath": "^1.0.0", 1310 | "inflight": "^1.0.4", 1311 | "inherits": "2", 1312 | "minimatch": "^3.0.4", 1313 | "once": "^1.3.0", 1314 | "path-is-absolute": "^1.0.0" 1315 | }, 1316 | "dependencies": { 1317 | "brace-expansion": { 1318 | "version": "1.1.11", 1319 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1320 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1321 | "dev": true, 1322 | "requires": { 1323 | "balanced-match": "^1.0.0", 1324 | "concat-map": "0.0.1" 1325 | } 1326 | }, 1327 | "minimatch": { 1328 | "version": "3.1.2", 1329 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1330 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1331 | "dev": true, 1332 | "requires": { 1333 | "brace-expansion": "^1.1.7" 1334 | } 1335 | } 1336 | } 1337 | }, 1338 | "glob-parent": { 1339 | "version": "5.1.2", 1340 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1341 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1342 | "dev": true, 1343 | "requires": { 1344 | "is-glob": "^4.0.1" 1345 | } 1346 | }, 1347 | "has-flag": { 1348 | "version": "4.0.0", 1349 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1350 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1351 | "dev": true 1352 | }, 1353 | "he": { 1354 | "version": "1.2.0", 1355 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1356 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1357 | "dev": true 1358 | }, 1359 | "inflight": { 1360 | "version": "1.0.6", 1361 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1362 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1363 | "dev": true, 1364 | "requires": { 1365 | "once": "^1.3.0", 1366 | "wrappy": "1" 1367 | } 1368 | }, 1369 | "inherits": { 1370 | "version": "2.0.4", 1371 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1372 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1373 | "dev": true 1374 | }, 1375 | "is-binary-path": { 1376 | "version": "2.1.0", 1377 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1378 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1379 | "dev": true, 1380 | "requires": { 1381 | "binary-extensions": "^2.0.0" 1382 | } 1383 | }, 1384 | "is-extglob": { 1385 | "version": "2.1.1", 1386 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1387 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1388 | "dev": true 1389 | }, 1390 | "is-fullwidth-code-point": { 1391 | "version": "3.0.0", 1392 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1393 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1394 | "dev": true 1395 | }, 1396 | "is-glob": { 1397 | "version": "4.0.3", 1398 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1399 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1400 | "dev": true, 1401 | "requires": { 1402 | "is-extglob": "^2.1.1" 1403 | } 1404 | }, 1405 | "is-number": { 1406 | "version": "7.0.0", 1407 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1408 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1409 | "dev": true 1410 | }, 1411 | "is-plain-obj": { 1412 | "version": "2.1.0", 1413 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", 1414 | "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", 1415 | "dev": true 1416 | }, 1417 | "is-unicode-supported": { 1418 | "version": "0.1.0", 1419 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", 1420 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", 1421 | "dev": true 1422 | }, 1423 | "js-yaml": { 1424 | "version": "4.1.0", 1425 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1426 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1427 | "dev": true, 1428 | "requires": { 1429 | "argparse": "^2.0.1" 1430 | } 1431 | }, 1432 | "jsdoctypeparser": { 1433 | "version": "9.0.0", 1434 | "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-9.0.0.tgz", 1435 | "integrity": "sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw==" 1436 | }, 1437 | "linkify-it": { 1438 | "version": "4.0.1", 1439 | "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", 1440 | "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", 1441 | "requires": { 1442 | "uc.micro": "^1.0.1" 1443 | } 1444 | }, 1445 | "locate-path": { 1446 | "version": "6.0.0", 1447 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1448 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1449 | "dev": true, 1450 | "requires": { 1451 | "p-locate": "^5.0.0" 1452 | } 1453 | }, 1454 | "log-symbols": { 1455 | "version": "4.1.0", 1456 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", 1457 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", 1458 | "dev": true, 1459 | "requires": { 1460 | "chalk": "^4.1.0", 1461 | "is-unicode-supported": "^0.1.0" 1462 | } 1463 | }, 1464 | "markdown-it": { 1465 | "version": "13.0.1", 1466 | "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", 1467 | "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", 1468 | "requires": { 1469 | "argparse": "^2.0.1", 1470 | "entities": "~3.0.1", 1471 | "linkify-it": "^4.0.1", 1472 | "mdurl": "^1.0.1", 1473 | "uc.micro": "^1.0.5" 1474 | } 1475 | }, 1476 | "mdurl": { 1477 | "version": "1.0.1", 1478 | "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", 1479 | "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" 1480 | }, 1481 | "minimatch": { 1482 | "version": "5.0.1", 1483 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", 1484 | "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", 1485 | "dev": true, 1486 | "requires": { 1487 | "brace-expansion": "^2.0.1" 1488 | } 1489 | }, 1490 | "mocha": { 1491 | "version": "10.0.0", 1492 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", 1493 | "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==", 1494 | "dev": true, 1495 | "requires": { 1496 | "@ungap/promise-all-settled": "1.1.2", 1497 | "ansi-colors": "4.1.1", 1498 | "browser-stdout": "1.3.1", 1499 | "chokidar": "3.5.3", 1500 | "debug": "4.3.4", 1501 | "diff": "5.0.0", 1502 | "escape-string-regexp": "4.0.0", 1503 | "find-up": "5.0.0", 1504 | "glob": "7.2.0", 1505 | "he": "1.2.0", 1506 | "js-yaml": "4.1.0", 1507 | "log-symbols": "4.1.0", 1508 | "minimatch": "5.0.1", 1509 | "ms": "2.1.3", 1510 | "nanoid": "3.3.3", 1511 | "serialize-javascript": "6.0.0", 1512 | "strip-json-comments": "3.1.1", 1513 | "supports-color": "8.1.1", 1514 | "workerpool": "6.2.1", 1515 | "yargs": "16.2.0", 1516 | "yargs-parser": "20.2.4", 1517 | "yargs-unparser": "2.0.0" 1518 | } 1519 | }, 1520 | "ms": { 1521 | "version": "2.1.3", 1522 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1523 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1524 | "dev": true 1525 | }, 1526 | "nanoid": { 1527 | "version": "3.3.3", 1528 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", 1529 | "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", 1530 | "dev": true 1531 | }, 1532 | "normalize-path": { 1533 | "version": "3.0.0", 1534 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1535 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1536 | "dev": true 1537 | }, 1538 | "once": { 1539 | "version": "1.4.0", 1540 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1541 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1542 | "dev": true, 1543 | "requires": { 1544 | "wrappy": "1" 1545 | } 1546 | }, 1547 | "p-limit": { 1548 | "version": "3.1.0", 1549 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1550 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1551 | "dev": true, 1552 | "requires": { 1553 | "yocto-queue": "^0.1.0" 1554 | } 1555 | }, 1556 | "p-locate": { 1557 | "version": "5.0.0", 1558 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1559 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1560 | "dev": true, 1561 | "requires": { 1562 | "p-limit": "^3.0.2" 1563 | } 1564 | }, 1565 | "path-exists": { 1566 | "version": "4.0.0", 1567 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1568 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1569 | "dev": true 1570 | }, 1571 | "path-is-absolute": { 1572 | "version": "1.0.1", 1573 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1574 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1575 | "dev": true 1576 | }, 1577 | "picomatch": { 1578 | "version": "2.3.1", 1579 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1580 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1581 | "dev": true 1582 | }, 1583 | "randombytes": { 1584 | "version": "2.1.0", 1585 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1586 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1587 | "dev": true, 1588 | "requires": { 1589 | "safe-buffer": "^5.1.0" 1590 | } 1591 | }, 1592 | "readdirp": { 1593 | "version": "3.6.0", 1594 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1595 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1596 | "dev": true, 1597 | "requires": { 1598 | "picomatch": "^2.2.1" 1599 | } 1600 | }, 1601 | "require-directory": { 1602 | "version": "2.1.1", 1603 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1604 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1605 | "dev": true 1606 | }, 1607 | "safe-buffer": { 1608 | "version": "5.2.1", 1609 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1610 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1611 | "dev": true 1612 | }, 1613 | "serialize-javascript": { 1614 | "version": "6.0.0", 1615 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", 1616 | "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", 1617 | "dev": true, 1618 | "requires": { 1619 | "randombytes": "^2.1.0" 1620 | } 1621 | }, 1622 | "should": { 1623 | "version": "13.2.3", 1624 | "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", 1625 | "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", 1626 | "dev": true, 1627 | "requires": { 1628 | "should-equal": "^2.0.0", 1629 | "should-format": "^3.0.3", 1630 | "should-type": "^1.4.0", 1631 | "should-type-adaptors": "^1.0.1", 1632 | "should-util": "^1.0.0" 1633 | } 1634 | }, 1635 | "should-equal": { 1636 | "version": "2.0.0", 1637 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", 1638 | "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", 1639 | "dev": true, 1640 | "requires": { 1641 | "should-type": "^1.4.0" 1642 | } 1643 | }, 1644 | "should-format": { 1645 | "version": "3.0.3", 1646 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 1647 | "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", 1648 | "dev": true, 1649 | "requires": { 1650 | "should-type": "^1.3.0", 1651 | "should-type-adaptors": "^1.0.1" 1652 | } 1653 | }, 1654 | "should-type": { 1655 | "version": "1.4.0", 1656 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 1657 | "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", 1658 | "dev": true 1659 | }, 1660 | "should-type-adaptors": { 1661 | "version": "1.1.0", 1662 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", 1663 | "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", 1664 | "dev": true, 1665 | "requires": { 1666 | "should-type": "^1.3.0", 1667 | "should-util": "^1.0.0" 1668 | } 1669 | }, 1670 | "should-util": { 1671 | "version": "1.0.1", 1672 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", 1673 | "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", 1674 | "dev": true 1675 | }, 1676 | "string-width": { 1677 | "version": "4.2.3", 1678 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1679 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1680 | "dev": true, 1681 | "requires": { 1682 | "emoji-regex": "^8.0.0", 1683 | "is-fullwidth-code-point": "^3.0.0", 1684 | "strip-ansi": "^6.0.1" 1685 | } 1686 | }, 1687 | "strip-ansi": { 1688 | "version": "6.0.1", 1689 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1690 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1691 | "dev": true, 1692 | "requires": { 1693 | "ansi-regex": "^5.0.1" 1694 | } 1695 | }, 1696 | "strip-json-comments": { 1697 | "version": "3.1.1", 1698 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1699 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1700 | "dev": true 1701 | }, 1702 | "supports-color": { 1703 | "version": "8.1.1", 1704 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 1705 | "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 1706 | "dev": true, 1707 | "requires": { 1708 | "has-flag": "^4.0.0" 1709 | } 1710 | }, 1711 | "to-regex-range": { 1712 | "version": "5.0.1", 1713 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1714 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1715 | "dev": true, 1716 | "requires": { 1717 | "is-number": "^7.0.0" 1718 | } 1719 | }, 1720 | "uc.micro": { 1721 | "version": "1.0.6", 1722 | "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", 1723 | "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" 1724 | }, 1725 | "workerpool": { 1726 | "version": "6.2.1", 1727 | "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", 1728 | "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", 1729 | "dev": true 1730 | }, 1731 | "wrap-ansi": { 1732 | "version": "7.0.0", 1733 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1734 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1735 | "dev": true, 1736 | "requires": { 1737 | "ansi-styles": "^4.0.0", 1738 | "string-width": "^4.1.0", 1739 | "strip-ansi": "^6.0.0" 1740 | } 1741 | }, 1742 | "wrappy": { 1743 | "version": "1.0.2", 1744 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1745 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1746 | "dev": true 1747 | }, 1748 | "y18n": { 1749 | "version": "5.0.8", 1750 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1751 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1752 | "dev": true 1753 | }, 1754 | "yargs": { 1755 | "version": "16.2.0", 1756 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 1757 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 1758 | "dev": true, 1759 | "requires": { 1760 | "cliui": "^7.0.2", 1761 | "escalade": "^3.1.1", 1762 | "get-caller-file": "^2.0.5", 1763 | "require-directory": "^2.1.1", 1764 | "string-width": "^4.2.0", 1765 | "y18n": "^5.0.5", 1766 | "yargs-parser": "^20.2.2" 1767 | } 1768 | }, 1769 | "yargs-parser": { 1770 | "version": "20.2.4", 1771 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", 1772 | "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", 1773 | "dev": true 1774 | }, 1775 | "yargs-unparser": { 1776 | "version": "2.0.0", 1777 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", 1778 | "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", 1779 | "dev": true, 1780 | "requires": { 1781 | "camelcase": "^6.0.0", 1782 | "decamelize": "^4.0.0", 1783 | "flat": "^5.0.2", 1784 | "is-plain-obj": "^2.1.0" 1785 | } 1786 | }, 1787 | "yocto-queue": { 1788 | "version": "0.1.0", 1789 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 1790 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 1791 | "dev": true 1792 | } 1793 | } 1794 | } 1795 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dox", 3 | "description": "Markdown / JSdoc documentation generator", 4 | "version": "1.0.0", 5 | "author": "TJ Holowaychuk ", 6 | "contributors": [ 7 | "Jocelyn Badgley ", 8 | "Arseny Zarechnev ", 9 | "Thomas Parisot ", 10 | "Stephen Mathieson ", 11 | "Vladimir Tsvang ", 12 | "Nathan Rajlich ", 13 | "Gion Kunz ", 14 | "ForbesLindesay" 15 | ], 16 | "homepage": "https://github.com/tj/dox", 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/visionmedia/dox.git" 20 | }, 21 | "keywords": [ 22 | "documentation", 23 | "docs", 24 | "markdown", 25 | "jsdoc" 26 | ], 27 | "bin": { 28 | "dox": "./bin/dox" 29 | }, 30 | "dependencies": { 31 | "commander": "9.4.0", 32 | "jsdoctypeparser": "^9.0.0", 33 | "markdown-it": "13.0.1" 34 | }, 35 | "devDependencies": { 36 | "mocha": "10.0.0", 37 | "should": "13.2.3" 38 | }, 39 | "license": "MIT", 40 | "scripts": { 41 | "test": "NODE_ENV=test ./node_modules/.bin/mocha test/*.test.js" 42 | }, 43 | "types": "./index.d.ts" 44 | } 45 | -------------------------------------------------------------------------------- /test/dox.jsdoccomplex.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var dox = require('../') 6 | , should = require('should') 7 | , fs = require('fs'); 8 | 9 | function fixture(name, fn) { 10 | fs.readFile(__dirname + '/fixtures/' + name, 'utf8', fn); 11 | } 12 | 13 | module.exports = { 14 | 'test .parseComments() jsdoc complex types': function(done){ 15 | fixture('jsdoc-complex-types.js', function(err, str){ 16 | var comments = dox.parseComments(str) 17 | , complexTypeParamAndReturn = comments.shift() 18 | , nestedComplexTypeParam = comments.shift() 19 | , optionalParam = comments.shift() 20 | , nullableParam = comments.shift() 21 | , nonNullableParam = comments.shift() 22 | , variableParam = comments.shift() 23 | , optionalVariableNullableParam = comments.shift(); 24 | 25 | ///////////////////////////////////// 26 | // complexTypeParamAndReturn 27 | ///////////////////////////////////// 28 | complexTypeParamAndReturn.tags.should.with.lengthOf(3); 29 | complexTypeParamAndReturn.tags[0].types.should.be.eql([ 30 | 'number', 31 | 'string', 32 | { 33 | name: ['string'], 34 | age: ['number'] 35 | } 36 | ]); 37 | complexTypeParamAndReturn.tags[0].typesDescription.should 38 | .equal('number | string | {name: string, age: number}'); 39 | complexTypeParamAndReturn.tags[0].string.should 40 | .equal('{number|string|{name:string,age:number}} a'); 41 | 42 | complexTypeParamAndReturn.tags[1].types.should.be.eql([ 43 | 'number', 44 | { 45 | name: ['string'], 46 | age: ['number'] 47 | }, 48 | 'Array' 49 | ]); 50 | complexTypeParamAndReturn.tags[1].typesDescription.should 51 | .equal('number | {name: string, age: number} | Array'); 52 | complexTypeParamAndReturn.tags[1].string.should 53 | .equal('{number|{name:string,age:number}|Array} a'); 54 | 55 | complexTypeParamAndReturn.tags[2].types.should.be.eql([ 56 | { 57 | name: ['string'], 58 | age: ['number'] 59 | } 60 | ]); 61 | complexTypeParamAndReturn.tags[2].typesDescription.should 62 | .equal('{name: string, age: number}'); 63 | complexTypeParamAndReturn.tags[2].string.should 64 | .equal('{{name:string,age:number}}'); 65 | 66 | ///////////////////////////////////// 67 | // nestedComplexTypeParam 68 | ///////////////////////////////////// 69 | nestedComplexTypeParam.tags.should.with.lengthOf(1); 70 | nestedComplexTypeParam.tags[0].types.should.be.eql([ 71 | 'number', 72 | 'string', 73 | { 74 | length: ['number'], 75 | type: [{ 76 | name: [{ 77 | first: ['string'], 78 | last: ['string'] 79 | }], 80 | id: ['number', 'string'] 81 | }] 82 | } 83 | ]); 84 | nestedComplexTypeParam.tags[0].string.should 85 | .equal('{number | string | {length: number, type: {name: {first: string, last: string}, id: number | string}}} a Description of param'); 86 | 87 | ///////////////////////////////////// 88 | // optionalParam 89 | ///////////////////////////////////// 90 | optionalParam.tags.should.with.lengthOf(1); 91 | optionalParam.tags[0].optional.should.be.true; 92 | optionalParam.tags[0].string.should.equal('{number=} a'); 93 | 94 | ///////////////////////////////////// 95 | // nullableParam 96 | ///////////////////////////////////// 97 | nullableParam.tags.should.with.lengthOf(1); 98 | nullableParam.tags[0].nullable.should.be.true; 99 | nullableParam.tags[0].string.should.equal('{?number} a'); 100 | 101 | ///////////////////////////////////// 102 | // nonNullableParam 103 | ///////////////////////////////////// 104 | nonNullableParam.tags.should.with.lengthOf(1); 105 | nonNullableParam.tags[0].nonNullable.should.be.true; 106 | nonNullableParam.tags[0].string.should.equal('{!number} a'); 107 | 108 | ///////////////////////////////////// 109 | // variableParam 110 | ///////////////////////////////////// 111 | variableParam.tags.should.with.lengthOf(1); 112 | variableParam.tags[0].variable.should.be.true; 113 | variableParam.tags[0].string.should.equal('{...number} a'); 114 | 115 | done(); 116 | }); 117 | } 118 | }; 119 | 120 | -------------------------------------------------------------------------------- /test/dox.multilinetags.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var dox = require('../') 6 | , should = require('should') 7 | , fs = require('fs'); 8 | 9 | function fixture(name, fn) { 10 | fs.readFile(__dirname + '/fixtures/' + name, 'utf8', fn); 11 | } 12 | 13 | module.exports = { 14 | 'test .parseComments() multiline tags': function(done){ 15 | fixture('multilinetags.js', function(err, str){ 16 | var comments = dox.parseComments(str); 17 | 18 | comments.length.should.equal(15); 19 | 20 | var only = comments.shift() 21 | , first = comments.shift() 22 | , last = comments.shift() 23 | , mid = comments.shift() 24 | , onlyParam = comments.shift() 25 | , firstParam = comments.shift() 26 | , lastParam = comments.shift() 27 | , midParam = comments.shift() 28 | , onlyReturn = comments.shift() 29 | , firstReturn = comments.shift() 30 | , lastReturn = comments.shift() 31 | , midReturn = comments.shift() 32 | , example = comments.shift(); 33 | 34 | only.tags.should.with.lengthOf(1); 35 | only.tags[0].string.should.equal('one\ntwo\nthree'); 36 | only.tags[0].html.should.equal('

one
\ntwo
\nthree

'); 37 | first.tags.should.with.lengthOf(2); 38 | first.tags[0].string.should.equal('one\ntwo\nthree'); 39 | first.tags[0].html.should.equal('

one
\ntwo
\nthree

'); 40 | first.tags[1].string.should.equal('last'); 41 | first.tags[1].html.should.equal('

last

'); 42 | last.tags.should.with.lengthOf(2); 43 | last.tags[0].string.should.equal('first'); 44 | last.tags[0].html.should.equal('

first

'); 45 | last.tags[1].string.should.equal('one\ntwo\nthree'); 46 | last.tags[1].html.should.equal('

one
\ntwo
\nthree

'); 47 | mid.tags.should.with.lengthOf(3); 48 | mid.tags[0].string.should.equal('first'); 49 | mid.tags[0].html.should.equal('

first

'); 50 | mid.tags[1].string.should.equal('one\ntwo\nthree'); 51 | mid.tags[1].html.should.equal('

one
\ntwo
\nthree

'); 52 | mid.tags[2].string.should.equal('last'); 53 | mid.tags[2].html.should.equal('

last

'); 54 | 55 | onlyParam.tags.should.with.lengthOf(1); 56 | onlyParam.tags[0].type.should.equal('param'); 57 | onlyParam.tags[0].name.should.equal('foo'); 58 | onlyParam.tags[0].types.should.eql(['String']); 59 | onlyParam.tags[0].description.should.equal('

one
\ntwo
\nthree

'); 60 | onlyParam.tags[0].string.should.equal('{String} foo\none\ntwo\nthree'); 61 | firstParam.tags.should.with.lengthOf(2); 62 | firstParam.tags[0].type.should.equal('param'); 63 | firstParam.tags[0].name.should.equal('foo'); 64 | firstParam.tags[0].types.should.eql(['String']); 65 | firstParam.tags[0].description.should.equal('

one
\ntwo
\nthree

'); 66 | firstParam.tags[0].string.should.equal('{String} foo\none\ntwo\nthree'); 67 | firstParam.tags[1].string.should.equal('last'); 68 | firstParam.tags[1].html.should.equal('

last

'); 69 | lastParam.tags.should.with.lengthOf(2); 70 | lastParam.tags[0].string.should.equal('first'); 71 | lastParam.tags[0].html.should.equal('

first

'); 72 | lastParam.tags[1].type.should.equal('param'); 73 | lastParam.tags[1].name.should.equal('foo'); 74 | lastParam.tags[1].types.should.eql(['String']); 75 | lastParam.tags[1].description.should.equal('

one
\ntwo
\nthree

'); 76 | lastParam.tags[1].string.should.equal('{String} foo\none\ntwo\nthree'); 77 | midParam.tags.should.with.lengthOf(3); 78 | midParam.tags[0].type.should.equal('foo'); 79 | midParam.tags[0].string.should.equal('first'); 80 | midParam.tags[0].html.should.equal('

first

'); 81 | midParam.tags[1].type.should.equal('param'); 82 | midParam.tags[1].name.should.equal('foo'); 83 | midParam.tags[1].types.should.eql(['String']); 84 | midParam.tags[1].description.should.equal('

one
\ntwo
\nthree

'); 85 | midParam.tags[1].string.should.equal('{String} foo\none\ntwo\nthree'); 86 | midParam.tags[2].string.should.equal('last'); 87 | midParam.tags[2].html.should.equal('

last

'); 88 | 89 | onlyReturn.tags.should.with.lengthOf(1); 90 | onlyReturn.tags[0].type.should.equal('return'); 91 | onlyReturn.tags[0].types.should.eql(['String']); 92 | onlyReturn.tags[0].description.should.equal('

one
\ntwo
\nthree

'); 93 | onlyReturn.tags[0].string.should.equal('{String}\none\ntwo\nthree'); 94 | firstReturn.tags.should.with.lengthOf(2); 95 | firstReturn.tags[0].type.should.equal('return'); 96 | firstReturn.tags[0].types.should.eql(['String']); 97 | firstReturn.tags[0].description.should.equal('

one
\ntwo
\nthree

'); 98 | firstReturn.tags[0].string.should.equal('{String}\none\ntwo\nthree'); 99 | firstReturn.tags[1].string.should.equal('last'); 100 | firstReturn.tags[1].html.should.equal('

last

'); 101 | lastReturn.tags.should.with.lengthOf(2); 102 | lastReturn.tags[0].string.should.equal('first'); 103 | lastReturn.tags[0].html.should.equal('

first

'); 104 | lastReturn.tags[1].type.should.equal('return'); 105 | lastReturn.tags[1].types.should.eql(['String']); 106 | lastReturn.tags[1].description.should.equal('

one
\ntwo
\nthree

'); 107 | lastReturn.tags[1].string.should.equal('{String}\none\ntwo\nthree'); 108 | midReturn.tags.should.with.lengthOf(3); 109 | midReturn.tags[0].string.should.equal('first'); 110 | midReturn.tags[0].html.should.equal('

first

'); 111 | midReturn.tags[1].type.should.equal('return'); 112 | midReturn.tags[1].types.should.eql(['String']); 113 | midReturn.tags[1].description.should.equal('

one
\ntwo
\nthree

'); 114 | midReturn.tags[1].string.should.equal('{String}\none\ntwo\nthree'); 115 | midReturn.tags[2].string.should.equal('last'); 116 | midReturn.tags[2].html.should.equal('

last

'); 117 | 118 | example.tags.should.with.lengthOf(1); 119 | example.tags[0].string.should.equal(' test(one);\n test(two);'); 120 | example.tags[0].html.should.equal('
test(one);\ntest(two);\n
'); 121 | 122 | var i167 = comments.shift(); 123 | i167.tags.should.with.lengthOf(3); 124 | i167.tags[0].type.should.equal('tag-1'); 125 | i167.tags[0].string.should.equal('foo'); 126 | i167.tags[0].html.should.equal('

foo

'); 127 | i167.tags[1].type.should.equal('tag-2'); 128 | i167.tags[1].string.should.equal('bar'); 129 | i167.tags[1].html.should.equal('

bar

'); 130 | i167.tags[2].type.should.equal('tag-3'); 131 | i167.tags[2].string.should.equal('baz'); 132 | i167.tags[2].html.should.equal('

baz

'); 133 | 134 | var i167 = comments.shift(); 135 | i167.tags.should.with.lengthOf(3); 136 | i167.tags[0].type.should.equal('tag-1'); 137 | i167.tags[0].string.should.equal('foo'); 138 | i167.tags[0].html.should.equal('

foo

'); 139 | i167.tags[1].type.should.equal('tag-2'); 140 | i167.tags[1].string.should.equal('bar'); 141 | i167.tags[1].html.should.equal('

bar

'); 142 | i167.tags[2].type.should.equal('tag-3'); 143 | i167.tags[2].string.should.equal('baz'); 144 | i167.tags[2].html.should.equal('

baz

'); 145 | 146 | done(); 147 | }); 148 | } 149 | }; 150 | -------------------------------------------------------------------------------- /test/dox.test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var dox = require('../') 7 | , should = require('should') 8 | , fs = require('fs'); 9 | 10 | function fixture(name, fn) { 11 | fs.readFile(__dirname + '/fixtures/' + name, 'utf8', fn); 12 | } 13 | 14 | module.exports = { 15 | 'test .parseComments() blocks': function(done){ 16 | fixture('a.js', function(err, str){ 17 | var comments = dox.parseComments(str) 18 | , file = comments.shift() 19 | , version = comments.shift(); 20 | file.should.have.property('ignore', true); 21 | file.description.full.should.equal('

A
\nCopyright (c) 2010 Author Name
\nMIT Licensed

'); 22 | file.description.summary.should.equal('

A
\nCopyright (c) 2010 Author Name
\nMIT Licensed

'); 23 | file.description.body.should.equal(''); 24 | file.tags.should.be.empty; 25 | file.line.should.equal(2); 26 | file.codeStart.should.equal(7); 27 | 28 | version.should.have.property('ignore', false); 29 | version.description.full.should.equal('

Library version.

'); 30 | version.description.summary.should.equal('

Library version.

'); 31 | version.description.body.should.equal(''); 32 | version.tags.should.be.empty; 33 | version.line.should.equal(8); 34 | version.codeStart.should.equal(12); 35 | done(); 36 | }); 37 | }, 38 | 39 | 'test .parseComments() tags': function(done){ 40 | fixture('b.js', function(err, str){ 41 | var comments = dox.parseComments(str); 42 | 43 | var version = comments.shift(); 44 | version.description.summary.should.equal('

Library version.

'); 45 | version.description.full.should.equal('

Library version.

'); 46 | version.tags.should.have.length(2); 47 | version.tags[0].type.should.equal('type'); 48 | version.tags[0].types.should.eql(['String']); 49 | version.tags[0].string.should.equal('{String}'); 50 | version.tags[1].type.should.equal('api'); 51 | version.tags[1].visibility.should.equal('public'); 52 | version.tags[1].string.should.equal('public'); 53 | version.ctx.type.should.equal('property'); 54 | version.ctx.receiver.should.equal('exports'); 55 | version.ctx.name.should.equal('version'); 56 | version.ctx.value.should.equal("'0.0.1'"); 57 | version.line.should.equal(2); 58 | version.codeStart.should.equal(9); 59 | 60 | var parse = comments.shift(); 61 | parse.description.summary.should.equal(''); 62 | parse.tags[0].type.should.equal('param'); 63 | parse.tags[0].name.should.equal('config'); 64 | parse.tags[0].description.should.equal('

An object that must provide a requestExecutor field.

'); 65 | parse.tags[0].types.should.eql(['Object']); 66 | parse.line.should.equal(12); 67 | parse.codeStart.should.equal(15); 68 | done(); 69 | }); 70 | }, 71 | 72 | 'test .parseComments() complex': function(done){ 73 | fixture('c.js', function(err, str){ 74 | var comments = dox.parseComments(str); 75 | 76 | var file = comments.shift(); 77 | 78 | file.tags.should.be.empty; 79 | // the following doesn't work as gh-md now obfuscates emails different on every pass 80 | //file.description.full.should.equal('

Dox
\nCopyright (c) 2010 TJ Holowaychuk tj@vision-media.ca
\nMIT Licensed

'); 81 | file.description.full.should.be.type('string'); 82 | file.ignore.should.be.true; 83 | file.line.should.equal(2); 84 | file.codeStart.should.equal(7); 85 | 86 | var mods = comments.shift(); 87 | mods.tags.should.be.empty; 88 | mods.description.full.should.equal('

Module dependencies.

'); 89 | mods.description.summary.should.equal('

Module dependencies.

'); 90 | mods.description.body.should.equal(''); 91 | mods.ignore.should.be.false; 92 | mods.code.should.equal('var markdown = require(\'github-flavored-markdown\').parse;'); 93 | mods.ctx.type.should.equal('declaration'); 94 | mods.ctx.name.should.equal('markdown'); 95 | mods.ctx.value.should.equal('require(\'github-flavored-markdown\').parse'); 96 | mods.line.should.equal(8); 97 | mods.codeStart.should.equal(12); 98 | 99 | var version = comments.shift(); 100 | version.tags.should.be.empty; 101 | version.description.full.should.equal('

Library version.

'); 102 | version.line.should.equal(14); 103 | version.codeStart.should.equal(18); 104 | 105 | var parseComments = comments.shift(); 106 | parseComments.tags.should.have.length(4); 107 | parseComments.ctx.type.should.equal('method'); 108 | parseComments.ctx.receiver.should.equal('exports'); 109 | parseComments.ctx.name.should.equal('parseComments'); 110 | parseComments.description.full.should.equal('

Parse comments in the given string of js.

'); 111 | parseComments.description.summary.should.equal('

Parse comments in the given string of js.

'); 112 | parseComments.description.body.should.equal(''); 113 | parseComments.line.should.equal(20); 114 | parseComments.codeStart.should.equal(29); 115 | 116 | var parseComment = comments.shift(); 117 | parseComment.tags.should.have.length(4); 118 | parseComment.description.summary.should.equal('

Parse the given comment str.

'); 119 | parseComment.description.full.should.equal('

Parse the given comment str.

\n

The comment object returned contains the following

\n'); 120 | parseComment.description.body.should.equal('

The comment object returned contains the following

\n'); 121 | parseComment.line.should.equal(75); 122 | parseComment.codeStart.should.equal(92); 123 | 124 | var parseTag = comments.shift(); 125 | parseTag.line.should.equal(120); 126 | parseTag.codeStart.should.equal(128); 127 | 128 | // Should be the comment be parsed ? 129 | var shouldNotFail = comments.shift(); 130 | shouldNotFail.line.should.equal(133); 131 | shouldNotFail.codeStart.should.equal(134); 132 | 133 | var parseTagTypes = comments.shift(); 134 | parseTagTypes.tags.should.have.length(3); 135 | parseTagTypes.description.full.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 136 | parseTagTypes.line.should.equal(164); 137 | parseTagTypes.codeStart.should.equal(172); 138 | 139 | var escape = comments.pop(); 140 | escape.tags.should.have.length(4); 141 | escape.tags[3].string.should.equal('With `Markdown` syntax'); 142 | escape.tags[3].html.should.equal('

With Markdown syntax

'); 143 | escape.description.full.should.equal('

Escape the given html.

'); 144 | escape.ctx.type.should.equal('function'); 145 | escape.ctx.name.should.equal('escape'); 146 | escape.line.should.equal(253); 147 | escape.codeStart.should.equal(262); 148 | done(); 149 | }); 150 | }, 151 | 152 | 'test .parseComments() tags with tabs': function (done) { 153 | fixture('d-tabs.js', function (err, str) { 154 | var comments = dox.parseComments(str) 155 | , first = comments.shift(); 156 | 157 | first.tags.should.have.length(4); 158 | first.description.full.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 159 | first.description.summary.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 160 | first.description.body.should.equal(''); 161 | first.ctx.type.should.equal('method'); 162 | first.ctx.receiver.should.equal('exports'); 163 | first.ctx.name.should.equal('parseTagTypes'); 164 | first.code.should.equal('exports.parseTagTypes = function(str) {\n\treturn str\n\t\t.replace(/[{}]/g, \'\')\n\t\t.split(/ *[|,\\/] */);\n};'); 165 | first.line.should.equal(2); 166 | first.codeStart.should.equal(11); 167 | done(); 168 | }); 169 | }, 170 | 171 | 'test .parseComments() tags with spaces': function (done) { 172 | fixture('d-spaces.js', function (err, str) { 173 | var comments = dox.parseComments(str) 174 | , first = comments.shift(); 175 | 176 | first.tags.should.have.length(4); 177 | first.description.full.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 178 | first.description.summary.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 179 | first.description.body.should.equal(''); 180 | first.ctx.type.should.equal('method'); 181 | first.ctx.receiver.should.equal('exports'); 182 | first.ctx.name.should.equal('parseTagTypes'); 183 | first.code.should.equal('exports.parseTagTypes = function(str) {\n return str\n .replace(/[{}]/g, \'\')\n .split(/ *[|,\\/] */);\n};'); 184 | first.line.should.equal(2); 185 | first.codeStart.should.equal(11); 186 | done(); 187 | }); 188 | }, 189 | 190 | 'test .parseComments() tags with mixed whitespace': function (done) { 191 | fixture('d-mixed.js', function (err, str) { 192 | var comments = dox.parseComments(str) 193 | , first = comments.shift(); 194 | 195 | first.tags.should.have.length(4); 196 | first.description.full.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 197 | first.description.summary.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 198 | first.description.body.should.equal(''); 199 | first.ctx.type.should.equal('method'); 200 | first.ctx.receiver.should.equal('exports'); 201 | first.ctx.name.should.equal('parseTagTypes'); 202 | first.code.should.equal('exports.parseTagTypes = function(str) {\n\treturn str\n\t\t.replace(/[{}]/g, \'\')\n\t\t.split(/ *[|,\\/] */);\n};'); 203 | first.line.should.equal(2); 204 | first.codeStart.should.equal(11); 205 | done(); 206 | }); 207 | }, 208 | 209 | 'test .parseComments() prototypes': function (done){ 210 | fixture('prototypes.js', function(err, str){ 211 | var comments = dox.parseComments(str) 212 | 213 | comments.should.be.an.instanceOf(Array); 214 | comments.should.have.lengthOf(3); 215 | 216 | // constructor 217 | comments[0].description.full.should.equal('

Does a lot of foo

'); 218 | comments[0].ctx.type.should.be.equal('constructor'); 219 | comments[0].ctx.name.should.be.equal('Foo'); 220 | comments[0].ctx.string.should.be.equal('Foo()'); 221 | comments[0].line.should.equal(2); 222 | comments[0].codeStart.should.equal(8); 223 | 224 | comments[1].description.full.should.equal('

A property of an instance of Foo

'); 225 | comments[1].ctx.type.should.be.equal('property'); 226 | comments[1].ctx.name.should.be.equal('property'); 227 | comments[1].ctx.string.should.be.equal('Foo.prototype.property'); 228 | comments[1].line.should.equal(12); 229 | comments[1].codeStart.should.equal(16); 230 | 231 | comments[2].description.full.should.equal('

A method of an instance of Foo

'); 232 | comments[2].ctx.type.should.be.equal('method'); 233 | comments[2].ctx.name.should.be.equal('method'); 234 | comments[2].ctx.string.should.be.equal('Foo.prototype.method()'); 235 | comments[2].line.should.equal(18); 236 | comments[2].codeStart.should.equal(22); 237 | 238 | done(); 239 | }); 240 | }, 241 | 242 | 'test .parseComments() classes': function (done){ 243 | fixture('classes.js', function(err, str){ 244 | var comments = dox.parseComments(str) 245 | 246 | comments.should.be.an.instanceOf(Array); 247 | comments.should.have.lengthOf(12); 248 | 249 | // class, extends and is exported as default 250 | comments[0].description.full.should.equal('

A Foo.

'); 251 | comments[0].ctx.type.should.be.equal('class'); 252 | comments[0].ctx.name.should.be.equal('FooBar'); 253 | comments[0].ctx.constructor.should.be.equal('FooBar'); 254 | comments[0].ctx.extends.should.be.equal('Foo.Baz'); 255 | comments[0].ctx.string.should.be.equal('new FooBar()'); 256 | comments[0].line.should.equal(2); 257 | comments[0].codeStart.should.equal(7); 258 | 259 | // class constructor 260 | comments[1].description.full.should.equal('

construct a Foo

'); 261 | comments[1].ctx.type.should.be.equal('constructor'); 262 | comments[1].ctx.name.should.be.equal('constructor'); 263 | comments[1].ctx.constructor.should.be.equal('FooBar'); 264 | comments[1].ctx.string.should.be.equal('FooBar.prototype.constructor()'); 265 | comments[1].line.should.equal(9); 266 | comments[1].codeStart.should.equal(14); 267 | 268 | // class method 269 | comments[2].description.full.should.equal('

Method of the Foo class.

'); 270 | comments[2].ctx.type.should.be.equal('method'); 271 | comments[2].ctx.name.should.be.equal('bar'); 272 | comments[2].ctx.constructor.should.be.equal('FooBar'); 273 | comments[2].ctx.string.should.be.equal('FooBar.prototype.bar()'); 274 | comments[2].line.should.equal(18); 275 | comments[2].codeStart.should.equal(22); 276 | 277 | // class static method 278 | comments[3].description.full.should.equal('

Static method of the Foo class.

'); 279 | comments[3].ctx.type.should.be.equal('method'); 280 | comments[3].ctx.name.should.be.equal('staticMethod'); 281 | comments[3].ctx.constructor.should.be.equal('FooBar'); 282 | comments[3].ctx.string.should.be.equal('FooBar.staticMethod()'); 283 | comments[3].line.should.equal(26); 284 | comments[3].codeStart.should.equal(30); 285 | 286 | // class static generator method 287 | comments[4].description.full.should.equal('

Static generator method of the Foo class.

'); 288 | comments[4].ctx.type.should.be.equal('method'); 289 | comments[4].ctx.name.should.be.equal('*staticGeneratorMethod'); 290 | comments[4].ctx.constructor.should.be.equal('FooBar'); 291 | comments[4].ctx.string.should.be.equal('FooBar.*staticGeneratorMethod()'); 292 | comments[4].line.should.equal(34); 293 | comments[4].codeStart.should.equal(38); 294 | 295 | // class generator method with computed name 296 | comments[5].description.full.should.equal('

Generator method with computed name.

'); 297 | comments[5].ctx.type.should.be.equal('method'); 298 | comments[5].ctx.name.should.be.equal('*[Symbol.iterator]'); 299 | comments[5].ctx.constructor.should.be.equal('FooBar'); 300 | comments[5].ctx.string.should.be.equal('FooBar.prototype.*[Symbol.iterator]()'); 301 | comments[5].line.should.equal(42); 302 | comments[5].codeStart.should.equal(46); 303 | 304 | // class setter 305 | comments[6].description.full.should.equal('

Setter for the blah property.

'); 306 | comments[6].ctx.type.should.be.equal('property'); 307 | comments[6].ctx.name.should.be.equal('blah'); 308 | comments[6].ctx.constructor.should.be.equal('FooBar'); 309 | comments[6].ctx.string.should.be.equal('FooBar.prototype.blah'); 310 | comments[6].line.should.equal(50); 311 | comments[6].codeStart.should.equal(53); 312 | 313 | // class getter 314 | comments[7].description.full.should.equal('

Getter for the blah property.

'); 315 | comments[7].ctx.type.should.be.equal('property'); 316 | comments[7].ctx.name.should.be.equal('blah'); 317 | comments[7].ctx.constructor.should.be.equal('FooBar'); 318 | comments[7].ctx.string.should.be.equal('FooBar.prototype.blah'); 319 | comments[7].line.should.equal(57); 320 | comments[7].codeStart.should.equal(61); 321 | 322 | // class, extends and is exported by name 323 | comments[8].description.full.should.equal(''); 324 | comments[8].ctx.type.should.be.equal('class'); 325 | comments[8].ctx.name.should.be.equal('Baz'); 326 | comments[8].ctx.constructor.should.be.equal('Baz'); 327 | comments[8].ctx.extends.should.be.equal('FooBar'); 328 | comments[8].ctx.string.should.be.equal('new Baz()'); 329 | comments[8].line.should.equal(67); 330 | comments[8].codeStart.should.equal(70); 331 | 332 | // class constructor 333 | comments[9].description.full.should.equal(''); 334 | comments[9].ctx.type.should.be.equal('constructor'); 335 | comments[9].ctx.name.should.be.equal('constructor'); 336 | comments[9].ctx.constructor.should.be.equal('Baz'); 337 | comments[9].ctx.string.should.be.equal('Baz.prototype.constructor()'); 338 | comments[9].line.should.equal(72); 339 | comments[9].codeStart.should.equal(75); 340 | 341 | // class 342 | comments[10].description.full.should.equal(''); 343 | comments[10].ctx.type.should.be.equal('class'); 344 | comments[10].ctx.name.should.be.equal('Lorem'); 345 | comments[10].ctx.constructor.should.be.equal('Lorem'); 346 | comments[10].ctx.extends.should.be.equal(''); 347 | comments[10].ctx.string.should.be.equal('new Lorem()'); 348 | comments[10].line.should.equal(80); 349 | comments[10].codeStart.should.equal(83); 350 | 351 | // class extended by assigment expression 352 | comments[11].description.full.should.equal(''); 353 | comments[11].ctx.type.should.be.equal('class'); 354 | comments[11].ctx.name.should.be.equal('Ipsum'); 355 | comments[11].ctx.constructor.should.be.equal('Ipsum'); 356 | comments[11].ctx.extends.should.be.equal('mixin(Foo.Bar, Baz)'); 357 | comments[11].ctx.string.should.be.equal('new Ipsum()'); 358 | comments[11].line.should.equal(89); 359 | comments[11].codeStart.should.equal(92); 360 | 361 | done(); 362 | }); 363 | }, 364 | 365 | 'test .parseComments() inline prototypes': function (done){ 366 | fixture('prototypes-inline.js', function(err, str){ 367 | var comments = dox.parseComments(str) 368 | 369 | comments.should.be.an.instanceOf(Array); 370 | comments.should.have.lengthOf(8); 371 | 372 | // constructor 373 | comments[0].description.full.should.equal('

Luke, I am your constructor.

'); 374 | comments[0].ctx.type.should.be.equal('constructor'); 375 | comments[0].ctx.name.should.be.equal('Foo'); 376 | comments[0].ctx.string.should.be.equal('Foo()'); 377 | 378 | // prototype object 379 | comments[1].description.full.should.equal('

To be relevant or not to be. This is the question.

'); 380 | comments[1].ctx.type.should.be.equal('prototype'); 381 | comments[1].ctx.name.should.be.equal('Foo'); 382 | comments[1].ctx.string.should.be.equal('Foo.prototype'); 383 | 384 | // property as a named method function 385 | comments[2].description.full.should.equal('

Returns the first item.

'); 386 | comments[2].ctx.type.should.be.equal('method'); 387 | comments[2].ctx.name.should.be.equal('getFirst'); 388 | comments[2].ctx.string.should.be.equal('Foo.prototype.getFirst()'); 389 | 390 | // getter function 391 | comments[3].description.full.should.equal('

Returns the first item.
\nActs as an ES5 alias of Foo.prototype.getFirst for feature sake.

'); 392 | comments[3].ctx.type.should.be.equal('property'); 393 | comments[3].ctx.name.should.be.equal('first'); 394 | comments[3].ctx.string.should.be.equal('Foo.prototype.first'); 395 | 396 | // setter function 397 | comments[4].description.full.should.equal('

Sets an internal property.

'); 398 | comments[4].ctx.type.should.be.equal('property'); 399 | comments[4].ctx.name.should.be.equal('seed'); 400 | comments[4].ctx.string.should.be.equal('Foo.prototype.seed'); 401 | 402 | // property as an anonymous method function 403 | comments[5].description.full.should.equal('

Anonymous function on property.

'); 404 | comments[5].ctx.type.should.be.equal('method'); 405 | comments[5].ctx.name.should.be.equal('random'); 406 | comments[5].ctx.string.should.be.equal('Foo.prototype.random()'); 407 | 408 | // this should be a separated function 409 | comments[6].description.full.should.equal('

My only purpose is to check we do not inherit from any parent context.

'); 410 | comments[6].ctx.type.should.be.equal('function'); 411 | comments[6].ctx.name.should.be.equal('breakingBad'); 412 | 413 | // classical prototype function property 414 | comments[7].description.full.should.equal('

Returns the last item.

\n
var f = new Foo([1, 5, 10]);\n\nf.getLast() === 10;\n
'); 415 | comments[7].ctx.type.should.be.equal('method'); 416 | comments[7].ctx.name.should.be.equal('getLast'); 417 | comments[7].ctx.string.should.be.equal('Foo.prototype.getLast()'); 418 | 419 | done(); 420 | }); 421 | }, 422 | 423 | 'test .parseComments() literal inline': function (done){ 424 | fixture('literal-inline.js', function(err, str){ 425 | var comments = dox.parseComments(str) 426 | 427 | comments.should.be.an.instanceOf(Array); 428 | comments.should.have.lengthOf(5); 429 | 430 | // parent target 431 | comments[0].description.full.should.equal('

Targeted literal

'); 432 | comments[0].ctx.type.should.be.equal('declaration'); 433 | comments[0].ctx.name.should.be.equal('Target'); 434 | comments[0].ctx.string.should.be.equal('Target'); 435 | 436 | // literal property 437 | comments[1].description.full.should.equal('

Sub object

'); 438 | comments[1].ctx.type.should.be.equal('property'); 439 | comments[1].ctx.name.should.be.equal('options'); 440 | comments[1].ctx.string.should.be.equal('options'); 441 | 442 | // property as an anonymous method function 443 | comments[2].description.full.should.equal('

This function surely does something

'); 444 | comments[2].ctx.type.should.be.equal('method'); 445 | comments[2].ctx.name.should.be.equal('doSomething'); 446 | comments[2].ctx.string.should.be.equal('doSomething()'); 447 | 448 | // property as a named method function 449 | comments[3].description.full.should.equal('

And them something else

'); 450 | comments[3].ctx.type.should.be.equal('method'); 451 | comments[3].ctx.name.should.be.equal('doSomethingElse'); 452 | comments[3].ctx.string.should.be.equal('doSomethingElse()'); 453 | 454 | // getter function 455 | comments[4].description.full.should.equal('

This is the result of doing anything

'); 456 | comments[4].ctx.type.should.be.equal('property'); 457 | comments[4].ctx.name.should.be.equal('result'); 458 | comments[4].ctx.string.should.be.equal('result'); 459 | comments[4].ctx.value.should.be.equal('doAnything()'); 460 | 461 | done(); 462 | }); 463 | }, 464 | 465 | 'test .parseComments() tag types': function (done){ 466 | fixture('d.js', function(err, str){ 467 | var comments = dox.parseComments(str); 468 | var first = comments.shift(); 469 | first.tags.should.have.length(4); 470 | first.description.full.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 471 | first.description.summary.should.equal('

Parse tag type string "{Array|Object}" etc.

'); 472 | first.description.body.should.equal(''); 473 | first.ctx.type.should.equal('method'); 474 | first.ctx.receiver.should.equal('exports'); 475 | first.ctx.name.should.equal('parseTagTypes'); 476 | first.code.should.equal('exports.parseTagTypes = function(str) {\n return str\n .replace(/[{}]/g, \'\')\n .split(/ *[|,\\/] */);\n};'); 477 | first.line.should.equal(2); 478 | first.codeStart.should.equal(11); 479 | done(); 480 | }); 481 | }, 482 | 483 | 'test .parseComments() code': function(done){ 484 | fixture('b.js', function(err, str){ 485 | var comments = dox.parseComments(str) 486 | , version = comments.shift() 487 | , parse = comments.shift(); 488 | 489 | version.code.should.equal("exports.version = '0.0.1';"); 490 | parse.code.should.equal('exports.parse = function(str) {\n return "wahoo";\n}'); 491 | done(); 492 | }); 493 | }, 494 | 495 | 'test .parseComments() titles': function(done){ 496 | fixture('titles.js', function(err, str){ 497 | var comments = dox.parseComments(str); 498 | comments[0].description.body.should.containEql('

Some examples

'); 499 | comments[0].description.body.should.not.containEql('

for example

'); 500 | comments[0].description.body.should.containEql('

Some longer thing for example

'); 501 | comments[0].line.should.equal(2); 502 | comments[0].codeStart.should.equal(14); 503 | 504 | comments[1].description.full.should.equal('

Description 1

'); 505 | comments[1].tags.should.have.length(2); 506 | comments[1].tags[0].type.should.equal('description'); 507 | comments[1].tags[0].full.should.equal('Description 2'); 508 | comments[1].tags[1].type.should.equal('description'); 509 | comments[1].tags[1].full.should.equal('Description 3'); 510 | 511 | comments[2].description.full.should.equal('

Something Else

'); 512 | done(); 513 | }); 514 | }, 515 | 516 | 'test .parseComments() code with a multi-line comment on a single line': function(done){ 517 | fixture('single-multiline.js', function(err, str){ 518 | var comments = dox.parseComments(str); 519 | 520 | comments[0].description.full.should.equal('

Normal multiline doc block

'); 521 | comments[1].description.full.should.equal('

Single-line multiline block

'); 522 | comments[2].description.full.should.equal('

Unspaced-line multiline block

'); 523 | comments[3].description.full.should.equal('

argument A

'); 524 | comments[4].description.full.should.equal('

argument B

'); 525 | 526 | done(); 527 | }); 528 | }, 529 | 530 | 'test .parseCodeContext() function statement': function(){ 531 | var ctx = dox.parseCodeContext('function $foo(){\n\n}'); 532 | ctx.type.should.equal('function'); 533 | ctx.name.should.equal('$foo'); 534 | }, 535 | 536 | 'test .parseCodeContext() returned unnamed function statement': function(){ 537 | var ctx = dox.parseCodeContext('return function (){\n\n}'); 538 | ctx.type.should.equal('function'); 539 | ctx.name.should.equal(''); 540 | }, 541 | 542 | 'test .parseCodeContext() returned named function statement': function(){ 543 | var ctx = dox.parseCodeContext('return function $foo (){\n\n}'); 544 | ctx.type.should.equal('function'); 545 | ctx.name.should.equal('$foo'); 546 | }, 547 | 548 | 'test .parseCodeContext() function expression': function(){ 549 | var ctx = dox.parseCodeContext('var $foo = function(){\n\n}'); 550 | ctx.type.should.equal('function'); 551 | ctx.name.should.equal('$foo'); 552 | }, 553 | 554 | 'test .parseCodeContext() prototype method': function(){ 555 | var ctx = dox.parseCodeContext('$User.prototype.$save = function(){}'); 556 | ctx.type.should.equal('method'); 557 | ctx.constructor.should.equal('$User'); 558 | ctx.name.should.equal('$save'); 559 | }, 560 | 561 | 'test .parseCodeContext() prototype property': function(){ 562 | var ctx = dox.parseCodeContext('$Database.prototype.$enabled = true;\nasdf'); 563 | ctx.type.should.equal('property'); 564 | ctx.constructor.should.equal('$Database'); 565 | ctx.name.should.equal('$enabled'); 566 | ctx.value.should.equal('true'); 567 | }, 568 | 569 | 'test .parseCodeContext() prototype property with value==null': function(){ 570 | var ctx = dox.parseCodeContext('Database.prototype.$enabled = null;\nasdf'); 571 | ctx.type.should.equal('property'); 572 | ctx.constructor.should.equal('Database'); 573 | ctx.name.should.equal('$enabled'); 574 | ctx.string.should.equal('Database.prototype.$enabled'); 575 | ctx.value.should.equal('null'); 576 | }, 577 | 578 | 'test .parseCodeContext() prototype property without value': function(){ 579 | var ctx = dox.parseCodeContext('Database.prototype.$enabled;\nasdf'); 580 | ctx.type.should.equal('property'); 581 | ctx.constructor.should.equal('Database'); 582 | ctx.name.should.equal('$enabled'); 583 | ctx.string.should.equal('Database.prototype.$enabled'); 584 | ctx.should.not.have.property('value'); 585 | }, 586 | 587 | 'test .parseCodeContext() method': function(){ 588 | var ctx = dox.parseCodeContext('$user.$save = function(){}'); 589 | ctx.type.should.equal('method'); 590 | ctx.receiver.should.equal('$user'); 591 | ctx.name.should.equal('$save'); 592 | }, 593 | 594 | 'test .parseCodeContext() property': function(){ 595 | var ctx = dox.parseCodeContext('$user.$name = "tj";\nasdf'); 596 | ctx.type.should.equal('property'); 597 | ctx.receiver.should.equal('$user'); 598 | ctx.name.should.equal('$name'); 599 | ctx.value.should.equal('"tj"'); 600 | }, 601 | 602 | 'test .parseCodeContext() declaration': function(){ 603 | var ctx = dox.parseCodeContext('var $name = "tj";\nasdf'); 604 | ctx.type.should.equal('declaration'); 605 | ctx.name.should.equal('$name'); 606 | ctx.value.should.equal('"tj"'); 607 | }, 608 | 609 | 'test .parseTag() @constructor': function(){ 610 | var tag = dox.parseTag('@constructor'); 611 | tag.type.should.equal('constructor'); 612 | }, 613 | 614 | 'test .parseTag() @see': function(){ 615 | var tag = dox.parseTag('@see http://google.com'); 616 | tag.type.should.equal('see'); 617 | tag.title.should.equal(''); 618 | tag.url.should.equal('http://google.com'); 619 | 620 | var tag = dox.parseTag('@see Google http://google.com'); 621 | tag.type.should.equal('see'); 622 | tag.title.should.equal('Google'); 623 | tag.url.should.equal('http://google.com'); 624 | 625 | var tag = dox.parseTag('@see exports.parseComment'); 626 | tag.type.should.equal('see'); 627 | tag.local.should.equal('exports.parseComment'); 628 | }, 629 | 630 | 'test .parseTag() @api': function(){ 631 | var tag = dox.parseTag('@api private'); 632 | tag.type.should.equal('api'); 633 | tag.visibility.should.equal('private'); 634 | }, 635 | 636 | 'test .parseTag() @type': function(){ 637 | var tag = dox.parseTag('@type {String}'); 638 | tag.type.should.equal('type'); 639 | tag.types.should.eql(['String']); 640 | }, 641 | 642 | 'test .parseTag() @param': function(){ 643 | var tag = dox.parseTag('@param {String|Buffer}'); 644 | tag.type.should.equal('param'); 645 | tag.types.should.eql(['String', 'Buffer']); 646 | tag.name.should.equal(''); 647 | tag.description.should.equal(''); 648 | tag.string.should.equal('{String|Buffer}'); 649 | tag.optional.should.be.false; 650 | }, 651 | 652 | 'test .parseTag() @param optional': function(){ 653 | var tag = dox.parseTag('@param {string} [foo]') 654 | tag.type.should.equal('param'); 655 | tag.types.should.eql(['string']); 656 | tag.name.should.equal('[foo]'); 657 | tag.description.should.equal(''); 658 | tag.string.should.equal('{string} [foo]'); 659 | tag.optional.should.be.true; 660 | 661 | var tag = dox.parseTag('@param {string=} foo') 662 | tag.type.should.equal('param'); 663 | tag.types.should.eql(['string']); 664 | tag.name.should.equal('foo'); 665 | tag.description.should.equal(''); 666 | tag.string.should.equal('{string=} foo'); 667 | tag.optional.should.be.true; 668 | 669 | var tag = dox.parseTag('@param {string?} foo') 670 | tag.type.should.equal('param'); 671 | tag.types.should.eql(['string']); 672 | tag.name.should.equal('foo'); 673 | tag.description.should.equal(''); 674 | tag.string.should.equal('{string?} foo'); 675 | tag.nullable.should.be.true; 676 | 677 | var tag = dox.parseTag('@param {string|Buffer=} foo') 678 | tag.type.should.equal('param'); 679 | tag.types.should.eql(['string', 'Buffer']); 680 | tag.name.should.equal('foo'); 681 | tag.description.should.equal(''); 682 | tag.string.should.equal('{string|Buffer=} foo'); 683 | tag.optional.should.be.true; 684 | }, 685 | 686 | 'test .parseTag() @return': function(){ 687 | var tag = dox.parseTag('@return {String} a normal string'); 688 | tag.type.should.equal('return'); 689 | tag.types.should.eql(['String']); 690 | tag.description.should.equal('a normal string'); 691 | tag.string.should.equal('{String} a normal string'); 692 | }, 693 | 694 | 'test .parseTag() @augments': function(){ 695 | var tag = dox.parseTag('@augments otherClass'); 696 | tag.type.should.equal('augments'); 697 | tag.otherClass.should.equal('otherClass') 698 | tag.string.should.equal('otherClass'); 699 | }, 700 | 701 | 'test .parseTag() @author': function(){ 702 | var tag = dox.parseTag('@author Bob Bobson'); 703 | tag.type.should.equal('author'); 704 | tag.string.should.equal('Bob Bobson'); 705 | }, 706 | 707 | 'test .parseTag() @borrows': function(){ 708 | var tag = dox.parseTag('@borrows foo as bar'); 709 | tag.type.should.equal('borrows'); 710 | tag.otherMemberName.should.equal('foo'); 711 | tag.thisMemberName.should.equal('bar'); 712 | tag.string.should.equal('foo as bar'); 713 | }, 714 | 715 | 'test .parseTag() @memberOf': function(){ 716 | var tag = dox.parseTag('@memberOf Foo.bar') 717 | tag.type.should.equal('memberOf') 718 | tag.parent.should.equal('Foo.bar') 719 | tag.string.should.equal('Foo.bar') 720 | }, 721 | 722 | 'test .parseTag() @example': function(){ 723 | tag = dox.parseTag('@example\n Foo.bar();', true); 724 | tag.type.should.equal('example') 725 | tag.string.should.equal(' Foo.bar();'); 726 | }, 727 | 728 | 'test .parseTag() default': function(){ 729 | var tag = dox.parseTag('@hello universe is better than world'); 730 | tag.type.should.equal('hello'); 731 | tag.string.should.equal('universe is better than world'); 732 | }, 733 | 734 | 'test .parseComments() code with no comments': function(done){ 735 | fixture('uncommented.js', function(err, str){ 736 | var comments = dox.parseComments(str) 737 | , all = comments.shift(); 738 | all.code.should.equal("function foo() {\n doSomething();\n}"); 739 | done(); 740 | }); 741 | }, 742 | 743 | 'test .parseComments() with a simple single line comment in code': function(done){ 744 | fixture('singleline.js', function(err, str){ 745 | var comments = dox.parseComments(str) 746 | , all = comments.shift(); 747 | all.code.should.equal("function foo() {\n // Maybe useful\n doSomething();\n}"); 748 | done(); 749 | }); 750 | }, 751 | 752 | 'test .parseComments() code with a comment without description': function(done){ 753 | fixture('nodescription.js', function(err, str){ 754 | var comments = dox.parseComments(str) 755 | , all = comments.shift(); 756 | all.tags.should.have.length(1); 757 | all.tags[0].type.should.equal('return'); 758 | all.tags[0].description.should.equal('

Digit

'); 759 | all.tags[0].string.should.equal('{number} Digit'); 760 | all.description.full.should.equal(''); 761 | all.description.summary.should.equal(''); 762 | all.description.summary.should.equal(''); 763 | all.code.should.equal("function foo() {\n return 1;\n}"); 764 | all.line.should.equal(1); 765 | all.codeStart.should.equal(4); 766 | done(); 767 | }); 768 | }, 769 | 770 | 'test .api() without inline code in comments': function(done) { 771 | fixture('a.js', function(err, str){ 772 | var comments = dox.parseComments(str); 773 | var apiDocs = dox.api(comments); 774 | apiDocs.should.equal(" - [exports.version](#exportsversion)\n\n## exports.version\n\n

Library version.

\n"); 775 | done(); 776 | }); 777 | }, 778 | 779 | 'test .parseComments() on single star comments with skipSingleStar=true': function (done) { 780 | fixture('single.js', function(err, str){ 781 | var comments = dox.parseComments(str, { skipSingleStar: true }); 782 | 783 | comments.should.have.lengthOf(1); 784 | comments[0].tags.should.be.empty; 785 | comments[0].code.should.be.equal(str.trim()); 786 | comments[0].description.full.should.be.empty; 787 | done(); 788 | }); 789 | }, 790 | 791 | 'test .parseComments() on single star comments no options': function (done) { 792 | fixture('single.js', function(err, str){ 793 | var comments = dox.parseComments(str); 794 | 795 | comments.should.have.lengthOf(1); 796 | comments[0].tags[0].should.be.eql({ 797 | type: 'return' 798 | , types: [ 'Object' ] 799 | , typesDescription: 'Object' 800 | , description: '

description

' 801 | , string: '{Object} description' 802 | , nullable: false 803 | , nonNullable: false 804 | , variable: false 805 | , optional: false 806 | }); 807 | comments[0].description.full.should.be.equal('

foo description

'); 808 | done(); 809 | }); 810 | }, 811 | 812 | 'test .api() with @alias flag': function(done){ 813 | fixture('alias.js', function(err, str){ 814 | var comments = dox.parseComments(str); 815 | var apiDocs = dox.api(comments); 816 | apiDocs.should.startWith(" - [hello()](#hello)\n - [window.hello()](#windowhello)\n\n"); 817 | done(); 818 | }); 819 | }, 820 | 821 | 'test .api() still includes parameters using functions': function(done){ 822 | fixture('functions.js', function(err, str){ 823 | var comments = dox.parseComments(str); 824 | var apiDocs = dox.api(comments); 825 | 826 | apiDocs.should.containEql("## fnName(a:String, b:Number)"); 827 | done(); 828 | }); 829 | }, 830 | 831 | 'test .parseComments() does not interpret jshint directives as jsdoc': function (done) { 832 | fixture('jshint.js', function (err, str){ 833 | var comments = dox.parseComments(str); 834 | comments.length.should.equal(1); 835 | comments[0].description.full.should.equal("

something else

"); 836 | done(); 837 | }); 838 | }, 839 | 840 | 'test skipPrefix': function (done) { 841 | fixture('skip_prefix.js', function (err, str){ 842 | var comments = dox.parseComments(str, {skipPrefixes: ["myspecialawesomelinter"]}); 843 | comments.length.should.equal(1); 844 | comments[0].description.full.should.equal("

something else

"); 845 | done(); 846 | }); 847 | }, 848 | 849 | 'test that */* combinations in code do not cause failures': function (done) { 850 | fixture('asterik.js', function (err, str){ 851 | var comments = dox.parseComments(str); 852 | comments.length.should.equal(4); 853 | comments[0].description.full.should.equal("

One

"); 854 | comments[0].ctx.type.should.equal('function'); 855 | comments[0].ctx.name.should.equal('one'); 856 | comments[1].description.full.should.equal("

Two

"); 857 | comments[1].ctx.type.should.equal('function'); 858 | comments[1].ctx.name.should.equal('two'); 859 | 860 | comments[2].description.full.should.equal("

Three

"); 861 | comments[2].ctx.type.should.equal('function'); 862 | comments[2].ctx.name.should.equal('three'); 863 | comments[3].description.full.should.equal("

Four

"); 864 | comments[3].ctx.type.should.equal('function'); 865 | comments[3].ctx.name.should.equal('four'); 866 | done(); 867 | }); 868 | }, 869 | 870 | 'test that // in string literals does not cause failures': function (done) { 871 | fixture('slash.js', function (err, str){ 872 | var comments = dox.parseComments(str); 873 | comments.length.should.equal(2); 874 | comments[0].description.full.should.equal("

One

"); 875 | comments[1].description.full.should.equal("

Two

"); 876 | done(); 877 | }); 878 | }, 879 | 880 | 'test event tags': function (done) { 881 | fixture('event.js', function (err, str){ 882 | var comments = dox.parseComments(str); 883 | //console.log(comments); 884 | comments.length.should.equal(2); 885 | comments[0].description.full.should.equal("

Throw a snowball.

"); 886 | comments[1].description.full.should.equal("

Snowball event.

"); 887 | comments[0].isEvent.should.be.false; 888 | comments[1].isEvent.should.be.true; 889 | done(); 890 | }); 891 | }, 892 | 893 | 'test optional types for @enum': function (done) { 894 | fixture('enums.js', function (err, str){ 895 | var comments = dox.parseComments(str); 896 | comments.length.should.equal(2); 897 | comments[0].description.full.should.equal("

FSM states.

"); 898 | comments[0].tags[0].type.should.equal("enum"); 899 | comments[0].tags[0].types.length.should.equal(0); 900 | comments[0].tags[0].string.should.equal(""); 901 | 902 | comments[1].description.full.should.equal("

Colors.

"); 903 | comments[1].tags[0].type.should.equal("enum"); 904 | comments[0].tags[0].types.length.should.equal(0); 905 | comments[1].tags[0].string.should.equal(""); 906 | done(); 907 | }); 908 | }, 909 | 910 | 'test optional types for @throws': function (done) { 911 | fixture('throws.js', function (err, str){ 912 | var comments = dox.parseComments(str); 913 | comments.length.should.equal(2); 914 | comments[0].description.full.should.equal("

Raise an exception for fun.

"); 915 | comments[0].tags[0].type.should.equal("throws"); 916 | comments[0].tags[0].types.length.should.equal(0); 917 | comments[0].tags[0].string.should.equal("An error message."); 918 | comments[0].tags[0].description.should.equal("

An error message.

"); 919 | 920 | comments[1].description.full.should.equal("

Validate user input.

"); 921 | comments[1].tags[0].type.should.equal("throws"); 922 | comments[1].tags[0].types[0].should.equal("TypeError"); 923 | comments[1].tags[0].string.should.equal("{TypeError} Invalid argument."); 924 | comments[1].tags[0].description.should.equal("

Invalid argument.

"); 925 | done(); 926 | }); 927 | }, 928 | 929 | 'Issue 169': function (done) { 930 | fixture('issue169.js', function (err, str){ 931 | var comments = dox.parseComments(str); 932 | 933 | comments.length.should.equal(1); 934 | comments[0].description.full.should.equal(""); 935 | comments[0].tags.length.should.equal(3); 936 | 937 | comments[0].tags[0].type.should.equal("fileoverview"); 938 | comments[0].tags[0].string.should.equal("Takes two options objects and merges them"); 939 | comments[0].tags[0].html.should.equal("

Takes two options objects and merges them

"); 940 | 941 | comments[0].tags[1].type.should.equal("author"); 942 | comments[0].tags[1].string.should.equal("Scott Nath"); 943 | comments[0].tags[1].html.should.equal("

Scott Nath

"); 944 | 945 | comments[0].tags[2].type.should.equal("requires"); 946 | comments[0].tags[2].string.should.equal("NPM:lodash.merge"); 947 | comments[0].tags[2].html.should.equal("

NPM:lodash.merge

"); 948 | done(); 949 | }); 950 | }, 951 | 952 | 'test that quotes within strings are correctly handled': function (done) { 953 | fixture('string-quotes.js', function (err, str){ 954 | var comments = dox.parseComments(str); 955 | comments.length.should.equal(8); 956 | comments[0].description.full.should.equal("

One

"); 957 | comments[1].description.full.should.equal("

Two

"); 958 | comments[2].description.full.should.equal("

Three

"); 959 | comments[3].description.full.should.equal("

Four

"); 960 | comments[4].description.full.should.equal("

Five

"); 961 | comments[5].description.full.should.equal("

Six

"); 962 | comments[6].description.full.should.equal("

Seven

"); 963 | comments[7].description.full.should.equal("

Eight

"); 964 | done(); 965 | }); 966 | }, 967 | 968 | 'test generic types': function(done){ 969 | fixture('types.js', function(err, str){ 970 | var comments = dox.parseComments(str); 971 | comments[0].tags[0].types[0].should.equal("Promise"); 972 | comments[1].tags[0].types[0].should.equal("Promise."); 973 | done(); 974 | }); 975 | }, 976 | }; 977 | -------------------------------------------------------------------------------- /test/fixtures/a.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * A 4 | * Copyright (c) 2010 Author Name 5 | * MIT Licensed 6 | */ 7 | 8 | /** 9 | * Library version. 10 | */ 11 | 12 | exports.version = '0.0.1'; -------------------------------------------------------------------------------- /test/fixtures/alias.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Alias Tag Used 3 | * 4 | * @alias hello 5 | */ 6 | window.hello = function() { 7 | 8 | } 9 | 10 | /** 11 | * Alias Tag Unused 12 | */ 13 | window.hello = function() { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/asterik.js: -------------------------------------------------------------------------------- 1 | /** 2 | * One 3 | */ 4 | var one = function() { 5 | return ['**/**', '*/*', '*/**', '**/*'].join(''); 6 | } 7 | 8 | /** 9 | * Two 10 | * @param {number} num 11 | */ 12 | var two = function(num) { 13 | return num * 2; 14 | } 15 | 16 | /** 17 | * Three 18 | */ 19 | let three = function() { 20 | return ['**/**', '*/*', '*/**', '**/*'].join(''); 21 | } 22 | 23 | /** 24 | * Four 25 | * @param {number} num 26 | */ 27 | const four = function(num) { 28 | return num * 2; 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/b.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Library version. 4 | * 5 | * @type {String} 6 | * @api public 7 | */ 8 | 9 | exports.version = '0.0.1'; 10 | 11 | 12 | /** 13 | * @param {Object} config An object that must provide a `requestExecutor` field. 14 | */ 15 | exports.parse = function(str) { 16 | return "wahoo"; 17 | } 18 | -------------------------------------------------------------------------------- /test/fixtures/c.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * Dox 4 | * Copyright (c) 2010 TJ Holowaychuk 5 | * MIT Licensed 6 | */ 7 | 8 | /** 9 | * Module dependencies. 10 | */ 11 | 12 | var markdown = require('github-flavored-markdown').parse; 13 | 14 | /** 15 | * Library version. 16 | */ 17 | 18 | exports.version = '0.0.5'; 19 | 20 | /** 21 | * Parse comments in the given string of `js`. 22 | * 23 | * @param {String} js 24 | * @return {Array} 25 | * @see exports.parseComment 26 | * @api public 27 | */ 28 | 29 | exports.parseComments = function(js){ 30 | var comments = [] 31 | , comment 32 | , buf = '' 33 | , ignore 34 | , within 35 | , code; 36 | 37 | for (var i = 0, len = js.length; i < len; ++i) { 38 | // start comment 39 | if ('/' == js[i] && '*' == js[i+1]) { 40 | // code following previous comment 41 | if (buf.trim().length) { 42 | comment = comments[comments.length - 1]; 43 | comment.code = code = buf.trim(); 44 | comment.ctx = exports.parseCodeContext(code); 45 | buf = ''; 46 | } 47 | i += 2; 48 | within = true; 49 | ignore = '!' == js[i]; 50 | // end comment 51 | } else if ('*' == js[i] && '/' == js[i+1]) { 52 | i += 2; 53 | buf = buf.replace(/^ *\* ?/gm, ''); 54 | var comment = exports.parseComment(buf); 55 | comment.ignore = ignore; 56 | comments.push(comment); 57 | within = ignore = false; 58 | buf = ''; 59 | // buffer comment or code 60 | } else { 61 | buf += js[i]; 62 | } 63 | } 64 | 65 | // trailing code 66 | if (buf.trim().length) { 67 | comment = comments[comments.length - 1]; 68 | code = buf.trim(); 69 | comment.code = code; 70 | } 71 | 72 | return comments; 73 | }; 74 | 75 | /** 76 | * Parse the given comment `str`. 77 | * 78 | * ## The comment object returned contains the following 79 | * 80 | * - `tags` array of tag objects 81 | * - `description` the first line of the comment 82 | * - `body` lines following the description 83 | * - `content` both the description and the body 84 | * - `isPrivate` true when "@api private" is used 85 | * 86 | * @param {String} str 87 | * @return {Object} 88 | * @see exports.parseTag 89 | * @api public 90 | */ 91 | 92 | exports.parseComment = function(str) { 93 | str = str.trim(); 94 | var comment = { tags: [] } 95 | , description = {}; 96 | 97 | // parse comment body 98 | description.full = str.split('@')[0].replace(/^([\w ]+):/gm, '## $1'); 99 | description.summary = description.full.split('\n\n')[0]; 100 | description.body = description.full.split('\n\n').slice(1).join('\n\n'); 101 | comment.description = description; 102 | 103 | // parse tags 104 | if (~str.indexOf('@')) { 105 | var tags = '@' + str.split('@').slice(1).join('@'); 106 | comment.tags = tags.split('\n').map(exports.parseTag); 107 | comment.isPrivate = comment.tags.some(function(tag){ 108 | return 'api' == tag.type && 'private' == tag.visibility; 109 | }) 110 | } 111 | 112 | // markdown 113 | description.full = markdown(escape(description.full)); 114 | description.summary = markdown(escape(description.summary)); 115 | description.body = markdown(escape(description.body)); 116 | 117 | return comment; 118 | } 119 | 120 | /** 121 | * Parse tag string "@param {Array} name description" etc. 122 | * 123 | * @param {String} 124 | * @return {Object} 125 | * @api public 126 | */ 127 | 128 | exports.parseTag = function(str) { 129 | var tag = {} 130 | , parts = str.split(/ +/) 131 | , type = tag.type = parts.shift().replace('@', ''); 132 | 133 | /* shouldn't fail */ 134 | switch (type) { // asfasdfasdf /** ///// 135 | case 'param': 136 | tag.types = exports.parseTagTypes(parts.shift()); 137 | tag.name = parts.shift() || ''; 138 | tag.description = parts.join(' '); 139 | break; 140 | case 'return': 141 | tag.types = exports.parseTagTypes(parts.shift()); 142 | tag.description = parts.join(' '); 143 | break; 144 | case 'see': 145 | if (~str.indexOf('http')) { 146 | tag.title = parts.length > 1 147 | ? parts.shift() 148 | : ''; 149 | tag.url = parts.join(' '); 150 | } else { 151 | tag.local = parts.join(' '); 152 | } 153 | case 'api': 154 | tag.visibility = parts.shift(); 155 | break; 156 | case 'type': 157 | tag.types = exports.parseTagTypes(parts.shift()); 158 | break; 159 | } 160 | 161 | return tag; 162 | } 163 | 164 | /** 165 | * Parse tag type string "{Array|Object}" etc. 166 | * 167 | * @param {String} str 168 | * @return {Array} 169 | * @api public 170 | */ 171 | 172 | exports.parseTagTypes = function(str) { 173 | return str 174 | .replace(/[{}]/g, '') 175 | .split(/ *[|,\/] */); 176 | }; 177 | 178 | /** 179 | * Parse the context from the given `str` of js. 180 | * 181 | * This method attempts to discover the context 182 | * for the comment based on it's code. Currently 183 | * supports: 184 | * 185 | * - function statements 186 | * - function expressions 187 | * - prototype methods 188 | * - prototype properties 189 | * - methods 190 | * - properties 191 | * - declarations 192 | * 193 | * @param {String} str 194 | * @return {Object} 195 | * @api public 196 | */ 197 | 198 | exports.parseCodeContext = function(str){ 199 | var str = str.split('\n')[0]; 200 | 201 | // function statement 202 | if (/^function (\w+)\(/.exec(str)) { 203 | return { 204 | type: 'function' 205 | , name: RegExp.$1 206 | }; 207 | // function expression 208 | } else if (/^var *(\w+) *= *function/.exec(str)) { 209 | return { 210 | type: 'function' 211 | , name: RegExp.$1 212 | }; 213 | // prototype method 214 | } else if (/^(\w+)\.prototype\.(\w+) *= *function/.exec(str)) { 215 | return { 216 | type: 'method' 217 | , constructor: RegExp.$1 218 | , name: RegExp.$2 219 | }; 220 | // prototype property 221 | } else if (/^(\w+)\.prototype\.(\w+) *= *([^\n;]+)/.exec(str)) { 222 | return { 223 | type: 'property' 224 | , constructor: RegExp.$1 225 | , name: RegExp.$2 226 | , value: RegExp.$3 227 | }; 228 | // method 229 | } else if (/^(\w+)\.(\w+) *= *function/.exec(str)) { 230 | return { 231 | type: 'method' 232 | , receiver: RegExp.$1 233 | , name: RegExp.$2 234 | }; 235 | // property 236 | } else if (/^(\w+)\.(\w+) *= *([^\n;]+)/.exec(str)) { 237 | return { 238 | type: 'property' 239 | , receiver: RegExp.$1 240 | , name: RegExp.$2 241 | , value: RegExp.$3 242 | }; 243 | // declaration 244 | } else if (/^var +(\w+) *= *([^\n;]+)/.exec(str)) { 245 | return { 246 | type: 'declaration' 247 | , name: RegExp.$1 248 | , value: RegExp.$2 249 | }; 250 | } 251 | }; 252 | 253 | /** 254 | * Escape the given `html`. 255 | * 256 | * @param {String} html 257 | * @return {String} 258 | * @api private 259 | * @custom With `Markdown` syntax 260 | */ 261 | 262 | function escape(html){ 263 | return String(html) 264 | .replace(/&(?!\w+;)/g, '&') 265 | .replace(//g, '>'); 267 | } 268 | -------------------------------------------------------------------------------- /test/fixtures/classes.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * A Foo. 4 | * @class FooBar 5 | * @extends Foo.Baz 6 | */ 7 | export default class FooBar extends Foo.Baz { 8 | 9 | /* 10 | * construct a Foo 11 | * @constructor 12 | * @param {Object} options constructor options 13 | */ 14 | constructor(options) { 15 | this.options = options 16 | } 17 | 18 | /* 19 | * Method of the Foo class. 20 | * @return {Overflow} 21 | */ 22 | bar() { 23 | return 99999999999999999999999999999999999999999999999999999999999999999 24 | } 25 | 26 | /** 27 | * Static method of the Foo class. 28 | * @return {String} 29 | */ 30 | static staticMethod() { 31 | return 'static method' 32 | } 33 | 34 | /** 35 | * Static generator method of the Foo class. 36 | * @return {String} 37 | */ 38 | static *staticGeneratorMethod() { 39 | return 'static method' 40 | } 41 | 42 | /** 43 | * Generator method with computed name. 44 | * @return {String} 45 | */ 46 | * [Symbol.iterator]() { 47 | for (let arg of this.args) yield arg 48 | } 49 | 50 | /* 51 | * Setter for the blah property. 52 | */ 53 | set blah() { 54 | this.blah = "blah" 55 | } 56 | 57 | /* 58 | * Getter for the blah property. 59 | * @return {String} 60 | */ 61 | get blah() { 62 | return this.blah 63 | } 64 | 65 | } 66 | 67 | /* 68 | * @class Baz 69 | */ 70 | export class Baz extends FooBar { 71 | 72 | /* 73 | * @param {Object} options constructor options 74 | */ 75 | constructor(options) { 76 | this.options = options 77 | } 78 | } 79 | 80 | /* 81 | * @class Lorem 82 | */ 83 | class Lorem { 84 | constructor(options) { 85 | this.options = options 86 | } 87 | } 88 | 89 | /* 90 | * @class Lorem 91 | */ 92 | class Ipsum extends mixin(Foo.Bar, Baz) { 93 | constructor(options) { 94 | this.options = options 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /test/fixtures/d-mixed.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Parse tag type string "{Array|Object}" etc. 4 | * 5 | * @name is arbitrary 6 | * @param {String} str 7 | * @return {Array} 8 | * @api public 9 | */ 10 | 11 | exports.parseTagTypes = function(str) { 12 | return str 13 | .replace(/[{}]/g, '') 14 | .split(/ *[|,\/] */); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/d-spaces.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Parse tag type string "{Array|Object}" etc. 4 | * 5 | * @name is arbitrary 6 | * @param {String} str 7 | * @return {Array} 8 | * @api public 9 | */ 10 | 11 | exports.parseTagTypes = function(str) { 12 | return str 13 | .replace(/[{}]/g, '') 14 | .split(/ *[|,\/] */); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/d-tabs.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Parse tag type string "{Array|Object}" etc. 4 | * 5 | * @name is arbitrary 6 | * @param {String} str 7 | * @return {Array} 8 | * @api public 9 | */ 10 | 11 | exports.parseTagTypes = function(str) { 12 | return str 13 | .replace(/[{}]/g, '') 14 | .split(/ *[|,\/] */); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/d.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Parse tag type string "{Array|Object}" etc. 4 | * 5 | * @name is arbitrary 6 | * @param {String} str 7 | * @return {Array} 8 | * @api public 9 | */ 10 | 11 | exports.parseTagTypes = function(str) { 12 | return str 13 | .replace(/[{}]/g, '') 14 | .split(/ *[|,\/] */); 15 | }; 16 | -------------------------------------------------------------------------------- /test/fixtures/enums.js: -------------------------------------------------------------------------------- 1 | /** 2 | * FSM states. 3 | * 4 | * @enum 5 | */ 6 | FSM.prototype.states = { 7 | STOPPED : 0, 8 | STARTING : 1, 9 | WORKING : 2, 10 | STOPPING : 3 11 | }; 12 | 13 | 14 | /** 15 | * Colors. 16 | * 17 | * @enum 18 | */ 19 | exports.RED = "#f00"; 20 | exports.GREEN = "#0f0"; 21 | exports.BLUE = "#00f"; 22 | exports.BLACK = "#000"; 23 | exports.WHITE = "#fff"; 24 | -------------------------------------------------------------------------------- /test/fixtures/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Throw a snowball. 3 | * 4 | * @fires Hurl#snowball 5 | */ 6 | Hurl.prototype.snowball = function() { 7 | /** 8 | * Snowball event. 9 | * 10 | * @event Hurl#snowball 11 | * @property {boolean} isPacked - Indicates whether the snowball is tightly packed. 12 | * @api public 13 | */ 14 | this.emit('snowball', { 15 | isPacked: this._snowball.isPacked 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /test/fixtures/functions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function Name 3 | * 4 | * @param {String} a 5 | * @param {Number} b 6 | */ 7 | function fnName(a, b) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/issue169.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Takes two options objects and merges them 3 | * 4 | * @author Scott Nath 5 | * 6 | * @requires NPM:lodash.merge 7 | */ 8 | 'use strict'; 9 | -------------------------------------------------------------------------------- /test/fixtures/jsdoc-complex-types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {number|string|{name:string,age:number}} a 4 | * @param {number|{name:string,age:number}|Array} a 5 | * @returns {{name:string,age:number}} 6 | */ 7 | function complexTypeParamAndReturn(a, b) { 8 | return { 9 | name: 'Test', 10 | age: 30 11 | } 12 | } 13 | 14 | /** 15 | * 16 | * @param {number | string | {length: number, type: {name: {first: string, last: string}, id: number | string}}} a Description of param 17 | */ 18 | function nestedComplexTypeParam(a) { 19 | 20 | } 21 | 22 | /** 23 | * 24 | * @param {number=} a 25 | */ 26 | function optionalParam(a) { 27 | 28 | } 29 | 30 | /** 31 | * 32 | * @param {?number} a 33 | */ 34 | function nullableParam(a) { 35 | 36 | } 37 | 38 | /** 39 | * 40 | * @param {!number} a 41 | */ 42 | function nonNullableParam(a) { 43 | 44 | } 45 | 46 | /** 47 | * 48 | * @param {...number} a 49 | */ 50 | function variableParam(a) { 51 | 52 | } 53 | -------------------------------------------------------------------------------- /test/fixtures/jshint.js: -------------------------------------------------------------------------------- 1 | /*jshint asi:true */ 2 | 3 | var foo; 4 | 5 | /* jshint asi:true */ 6 | 7 | var test; 8 | 9 | /* something else */ 10 | -------------------------------------------------------------------------------- /test/fixtures/literal-inline.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Targeted literal 4 | * @type {Object} 5 | */ 6 | var Target = { 7 | 8 | /** 9 | * Sub object 10 | * @type {Object} 11 | */ 12 | options: { 13 | 14 | }, 15 | 16 | /** 17 | * This function surely does something 18 | * @return {boolean} 19 | */ 20 | doSomething: function () { 21 | return true; 22 | }, 23 | 24 | /** 25 | * And them something else 26 | */ 27 | doSomethingElse: function doSomethingElse () { 28 | 29 | }, 30 | 31 | /** 32 | * This is the result of doing anything 33 | * @type {function} 34 | */ 35 | result: doAnything() 36 | 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /test/fixtures/multilinetags.js: -------------------------------------------------------------------------------- 1 | /** 2 | * only 3 | * 4 | * @sample 5 | * one 6 | * two 7 | * three 8 | */ 9 | function only() { 10 | } 11 | 12 | /** 13 | * first 14 | * 15 | * @sample 16 | * one 17 | * two 18 | * three 19 | * @bar last 20 | */ 21 | function first() { 22 | } 23 | 24 | /** 25 | * last 26 | * 27 | * @foo first 28 | * @sample 29 | * one 30 | * two 31 | * three 32 | */ 33 | function last() { 34 | } 35 | 36 | /** 37 | * mid 38 | * 39 | * @foo first 40 | * @sample 41 | * one 42 | * two 43 | * three 44 | * @bar last 45 | */ 46 | function mid() { 47 | } 48 | 49 | /** 50 | * only 51 | * 52 | * @param {String} foo 53 | * one 54 | * two 55 | * three 56 | */ 57 | function onlyParam() { 58 | } 59 | 60 | /** 61 | * first 62 | * 63 | * @param {String} foo 64 | * one 65 | * two 66 | * three 67 | * @bar last 68 | */ 69 | function firstParam() { 70 | } 71 | 72 | /** 73 | * last 74 | * 75 | * @foo first 76 | * @param {String} foo 77 | * one 78 | * two 79 | * three 80 | */ 81 | function lastParam() { 82 | } 83 | 84 | /** 85 | * mid 86 | * 87 | * @foo first 88 | * @param {String} foo 89 | * one 90 | * two 91 | * three 92 | * @bar last 93 | */ 94 | function midParam() { 95 | } 96 | 97 | /** 98 | * only 99 | * 100 | * @return {String} 101 | * one 102 | * two 103 | * three 104 | */ 105 | function onlyReturn() { 106 | } 107 | 108 | /** 109 | * first 110 | * 111 | * @return {String} 112 | * one 113 | * two 114 | * three 115 | * @bar last 116 | */ 117 | function firstReturn() { 118 | } 119 | 120 | /** 121 | * last 122 | * 123 | * @foo first 124 | * @return {String} 125 | * one 126 | * two 127 | * three 128 | */ 129 | function lastReturn() { 130 | } 131 | 132 | /** 133 | * mid 134 | * 135 | * @foo first 136 | * @return {String} 137 | * one 138 | * two 139 | * three 140 | * @bar last 141 | */ 142 | function midReturn() { 143 | } 144 | 145 | /** 146 | * example 147 | * 148 | * @example 149 | * test(one); 150 | * test(two); 151 | */ 152 | function example() { 153 | } 154 | 155 | /** 156 | * @tag-1 foo 157 | * @tag-2 bar 158 | * 159 | * @tag-3 baz 160 | */ 161 | 162 | /** 163 | * @tag-1 164 | * foo 165 | * @tag-2 166 | * bar 167 | * 168 | * @tag-3 169 | * baz 170 | */ -------------------------------------------------------------------------------- /test/fixtures/nodescription.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @return {number} Digit 3 | */ 4 | function foo() { 5 | return 1; 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/prototypes-inline.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Luke, I am your constructor. 5 | * 6 | * @param {Array} bar 7 | * @constructor 8 | */ 9 | function Foo(bar){ 10 | this.bar = bar; 11 | this._seed = Math.random(); 12 | } 13 | 14 | /** 15 | * To be relevant or not to be. This is the question. 16 | * 17 | * @namespace Foo 18 | */ 19 | Foo.prototype = { 20 | /** 21 | * Returns the first item. 22 | * 23 | * @returns {Number} 24 | */ 25 | getFirst: function getFirstBarItem(){ 26 | return this.bar[0]; 27 | }, 28 | /** 29 | * Returns the first item. 30 | * Acts as an ES5 alias of `Foo.prototype.getFirst` for feature sake. 31 | * 32 | * @see Foo.prototype.getFirst 33 | * @returns {Number} 34 | */ 35 | get first(){ 36 | return this.bar[0]; 37 | }, 38 | /** 39 | * Sets an internal property. 40 | * 41 | * @param {Number} s 42 | */ 43 | set seed(s){ 44 | this._seed = s; 45 | }, 46 | /** 47 | * Anonymous function on property. 48 | * 49 | * @returns {number} 50 | */ 51 | random: function(){ 52 | return this._seed * 1337; 53 | } 54 | }; 55 | 56 | /** 57 | * My only purpose is to check we do not inherit from any parent context. 58 | * 59 | * @returns {string} 60 | */ 61 | function breakingBad() { 62 | return "Meth"; 63 | } 64 | 65 | /** 66 | * Returns the last item. 67 | * 68 | * ```javascript 69 | * var f = new Foo([1, 5, 10]); 70 | * 71 | * f.getLast() === 10; 72 | * ``` 73 | * 74 | * @returns {Number} 75 | */ 76 | Foo.prototype.getLast = function getLast(){ 77 | return this.bar[this.bar.length]; 78 | }; -------------------------------------------------------------------------------- /test/fixtures/prototypes.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Does a lot of foo 4 | * 5 | * @constructor 6 | * @param {String} bar 7 | */ 8 | function Foo(bar) { 9 | this.bar = bar 10 | } 11 | 12 | /** 13 | * A property of an instance of Foo 14 | * @type {String} 15 | */ 16 | Foo.prototype.property = 'this is a property' 17 | 18 | /** 19 | * A method of an instance of Foo 20 | * @return {Boolean} 21 | */ 22 | Foo.prototype.method = function () { 23 | return false 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/single-multiline.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Normal multiline doc block 4 | */ 5 | 6 | var a; 7 | 8 | /* Single-line multiline block */ 9 | 10 | var b; 11 | 12 | /*Unspaced-line multiline block*/ 13 | 14 | var c; 15 | 16 | function (a /* argument A */, b /* argument B */) { 17 | 18 | } -------------------------------------------------------------------------------- /test/fixtures/single.js: -------------------------------------------------------------------------------- 1 | /* 2 | * foo description 3 | * 4 | * @return {Object} description 5 | */ 6 | function foo() { 7 | return bar 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/singleline.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Code below must contain the useful comment 3 | */ 4 | function foo() { 5 | // Maybe useful 6 | doSomething(); 7 | } -------------------------------------------------------------------------------- /test/fixtures/skip_prefix.js: -------------------------------------------------------------------------------- 1 | /*myspecialawesomelinter asi:true */ 2 | 3 | var test; 4 | 5 | /* something else */ 6 | -------------------------------------------------------------------------------- /test/fixtures/slash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * One 3 | */ 4 | var one = '//'; 5 | 6 | /** 7 | * Two 8 | */ 9 | var two = 2; 10 | -------------------------------------------------------------------------------- /test/fixtures/string-quotes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * One 3 | */ 4 | function one() { 5 | var a = "String with double quotes containing a single quote: '"; 6 | } 7 | 8 | /** 9 | * Two 10 | */ 11 | function two() { 12 | var a = "String with double quotes containing an escaped single quote: \'"; 13 | } 14 | 15 | /** 16 | * Three 17 | */ 18 | function three() { 19 | var a = "String with double quotes containing an escaped double quote: \""; 20 | } 21 | 22 | /** 23 | * Four 24 | */ 25 | function four() { 26 | var a = 'String with single quotes containing a double quote: "'; 27 | } 28 | 29 | /** 30 | * Five 31 | */ 32 | function five() { 33 | var a = 'String with single quotes containing an escaped double quote: \"'; 34 | } 35 | 36 | /** 37 | * Six 38 | */ 39 | function six() { 40 | var a = 'String with single quotes containing an escaped single quote: \''; 41 | } 42 | 43 | /** 44 | * Seven 45 | */ 46 | function seven() { 47 | var contents = 'contents'; 48 | var a = `Template 'string' with "quoted" ${contents}.`; 49 | } 50 | 51 | /** 52 | * Eight 53 | */ 54 | function eight() { 55 | var a = "Last function that should also get included"; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /test/fixtures/throws.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Raise an exception for fun. 3 | * 4 | * @throws An error message. 5 | */ 6 | function crashMe() { 7 | throw "Bang!"; 8 | } 9 | 10 | /** 11 | * Validate user input. 12 | * 13 | * @throws {TypeError} Invalid argument. 14 | */ 15 | function validateUserInput(input) { 16 | if (typeof input !== "string")) { 17 | throw new TypeError("Input is not a string"); 18 | } 19 | return true; 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/titles.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Description. 4 | * 5 | * ## Some examples 6 | * 7 | * foo 8 | * 9 | * ## Some longer thing for example 10 | * 11 | * bar 12 | * 13 | */ 14 | 15 | /** 16 | * Description 1 17 | * 18 | * @description Description 2 19 | * 20 | * @description 21 | * Description 3 22 | */ 23 | 24 | /** 25 | * @description 26 | * Something Else 27 | */ -------------------------------------------------------------------------------- /test/fixtures/types.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @return {Promise} Promise that returns a string if available. 3 | */ 4 | 5 | /** 6 | * @return {Promise.} Promise that returns a string if available. 7 | */ 8 | -------------------------------------------------------------------------------- /test/fixtures/uncommented.js: -------------------------------------------------------------------------------- 1 | function foo() { 2 | doSomething(); 3 | } --------------------------------------------------------------------------------