├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── examples ├── README.md ├── index.js └── jsdoc.conf ├── index.js ├── lib ├── authentication.js ├── bodyparam.js ├── chain.js ├── headerparam.js ├── parameterTableBuilder.js ├── queryparam.js ├── responsecode.js ├── responseparam.js ├── route.js ├── routeparam.js └── serviceparam.js ├── package.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | .sass-cache 3 | /node_modules 4 | *.pdf 5 | pm2-deploy.log 6 | npm-debug.log 7 | *[#]*[#] 8 | *[#]* 9 | *.DS_Store 10 | .vscode/ 11 | volumes/ 12 | *.tgz 13 | scripts/node_modules 14 | /out 15 | .idea 16 | .DS_Store 17 | package-lock.json 18 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | examples/ 2 | out/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 https://github.com/bvanderlaan 4 | Copyright (c) 2017 https://github.com/vmarchaud 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is a fork 2 | 3 | This project is a fork of https://github.com/bvanderlaan/jsdoc-route-plugin 4 | Currently, bvanderlaan doesn't seems to be available to maintain the project so i'll continue here. 5 | 6 | # JsDoc HTTP Plugin 7 | 8 | This is a plugin for [JsDoc](http://usejsdoc.org/) which is a tool to generate HTML documentation from comment blocks. 9 | JsDoc will scan your code files looking for comment blocks then generate a nicely formated HTML document. 10 | 11 | JsDoc supports a number of tags to help document a number of things such as each parameter in a function or what the function will return. 12 | These tags are picked up by JsDoc and used when generating the HTML documentation; for example function parameters are shown in a table. 13 | 14 | This plugin adds custom tags to JsDoc that work with the default document template. The custom tags are meant to help document HTTP endpoints. 15 | 16 | 17 | ## How to install 18 | 19 | First you need to install JsDoc 20 | ```console 21 | npm install jsdoc --save-dev 22 | ``` 23 | 24 | Then you need to install the JsDoc Route Plugin 25 | 26 | ```console 27 | npm install jsdoc-http-plugin --save-dev 28 | ``` 29 | 30 | Next you need to tell [JsDoc](http://usejsdoc.org/) to enable the plugin. 31 | 32 | You can do this by adding a `jsdoc.conf` file and telling [JsDoc](http://usejsdoc.org/) to use it when you run it. 33 | 34 | **Example jsdoc.conf** 35 | ```js 36 | { 37 | "tags": { 38 | "allowUnknownTags": true, 39 | "dictionaries": ["jsdoc","closure"] 40 | }, 41 | "source": { 42 | "include": [ "." ], 43 | "exclude": [ "node_modules" ], 44 | "includePattern": ".+\\.js(doc|x)?$", 45 | "excludePattern": "(^|\\/|\\\\)_" 46 | }, 47 | "plugins": ["jsdoc-http-plugin"], 48 | "templates": { 49 | "cleverLinks": false, 50 | "monospaceLinks": false 51 | }, 52 | "opts": { 53 | "recurse": true 54 | } 55 | } 56 | ``` 57 | 58 | Now run [JsDoc](http://usejsdoc.org/) with the `--config` flag. 59 | ```console 60 | jsdoc --config jsdoc.conf 61 | ``` 62 | 63 | ## Caveats 64 | 65 | - when used with markdown plugin, it should be put before `jsdoc-http-plugin` 66 | 67 | > "plugins": ["plugins/markdown", "jsdoc-http-plugin"], 68 | 69 | ## Example 70 | 71 | If you want to see an example of this plugin in action run the `npm run example1` command. 72 | That will run [JsDoc](http://usejsdoc.org/) against a sample Express app located in `examples` and produce HTML documentation in the `out` folder. 73 | To view the documentation open `out/index.html` in a browser. 74 | 75 | ## What are the new Tags 76 | 77 | The new tags are all about documenting Express routes. 78 | Find a list of them and how they are to be used below. 79 | 80 | ## @path 81 | 82 | Because JsDoc does not know about routes we need to decorate the route documentation with the `@name` tag to make JsDoc think you are documenting a member of the given module. 83 | This will add an entry under the **Members** section in the HTML document; however, if we used only the `@name` tag to describe the route verb and path it might look a bit odd as it would show up like this: 84 | > *(inner)* POST /v1/files 85 | 86 | To make documenting a route a bit nicer I suggest using the `@name` tag to define a common name for the route, such as File Upload, and the `@path` tag to define the verb and route path. 87 | Using the `@path` tag will also change the method attribute from *(inner)* to *(path)*. 88 | 89 | ```js 90 | /** 91 | * Upload a file. 92 | * 93 | * @name File Upload 94 | * @path {POST} /v1/file 95 | */ 96 | server.post({ 97 | url: '/v1/file', 98 | }, (req, res, next) => {...} 99 | ``` 100 | 101 | The `@path` tag will add a table showing the HTTP verb (i.e. POST, PUT, DEL, GET), and the route path (i.e. /v1/files) for the route you are documenting just under the friendly name of the route above the details section. 102 | 103 | Only one `@path` tag is expected per endpoint document. 104 | 105 | ## @auth 106 | 107 | The `@auth` tag allows you to state what authentication a route requires. 108 | 109 | ```js 110 | /** 111 | * Upload a file. 112 | * 113 | * @name File Upload 114 | * @path {POST} /v1/file 115 | * @auth This route requires HTTP Basic Authentication. If authentication fails it will return a 401 error. 116 | */ 117 | server.post({ 118 | url: '/v1/file', 119 | }, (req, res, next) => {...} 120 | ``` 121 | 122 | It will result in a new sub-heading called **Authentication** with whatever text you provided to the tag beneath it. 123 | 124 | Only one `@auth` tag is expected per endpoint document. 125 | 126 | ## @header 127 | 128 | The `@header` allows you to document any parameters which are passed via the header of the HTTP request. 129 | 130 | With this tag you need to provide the name and a description. The name is the first word of the text following the tag. 131 | * `@header MyName And this part is the description` 132 | 133 | You can also optionally provide a type for the parameter. 134 | * `@header {String} MyName And this part is the description` 135 | 136 | ```js 137 | /** 138 | * Upload a file. 139 | * 140 | * @name File Upload 141 | * @path {POST} /v1/file 142 | * @header authorization is the identification information for the request 143 | * @header {String} user-id is the unique User Id to assign to the file 144 | */ 145 | server.post({ 146 | url: '/v1/file', 147 | }, (req, res, next) => {...} 148 | ``` 149 | 150 | The above would add a table under the route description that lists all the header parameters. 151 | You can use the `@header` tag as many times as you have parameters in your request header you wish to document. 152 | 153 | 154 | ## @body 155 | 156 | The `@body` allows you to document any parameters which are passed via the body of the HTTP request. 157 | 158 | With this tag you need to provide the name and a description. The name is the first word of the text following the tag. 159 | * `@body MyName And this part is the description` 160 | 161 | You can also optionally provide a type for the parameter. 162 | * `@body {String} MyName And this part is the description` 163 | 164 | You can also specify that the parameter is optional by placing the name within square brackets. 165 | * `@body {String} [MyName] And this part is the description` 166 | 167 | Lastly you can define a default value for the parameter. The idea is to document the value which will be used if the parameter is not provided. 168 | * `@body {String} [MyName=Phillip] And this part is the description` 169 | 170 | 171 | ```js 172 | /** 173 | * Upload a file. 174 | * 175 | * @name File Upload 176 | * @path {POST} /v1/file 177 | * @body {String} userId is the unique identifier for the user we are uploading the file to. 178 | * @body {Boolean} [sync=false] when true the route will be synchronous otherwise the route 179 | * is asynchronous. 180 | */ 181 | server.post({ 182 | url: '/v1/file', 183 | }, (req, res, next) => {...} 184 | ``` 185 | 186 | The above would add a table under the route description that lists all the body parameters. 187 | 188 | You can use the `@bodyparam` tag as many times as you have parameters in your request body you wish to document. 189 | 190 | ## @params 191 | 192 | The `@params` allows you to document any parameters which make up part of the route path. 193 | 194 | With this tag you need to provide the name and a description. The name is the first word of the text following the tag. 195 | * `@params MyName And this part is the description` 196 | 197 | You can also optionally provide a type for the parameter. 198 | * `@params {String} MyName And this part is the description` 199 | 200 | ```js 201 | /** 202 | * Download a file. 203 | * 204 | * @name Download File 205 | * @path {GET} /v1/files/:fileId 206 | * @params {String} :fileId is the unique identifier for the file to download. 207 | */ 208 | server.get({ 209 | url: '/v1/files/:fileId', 210 | }, (req, res, next) => {...} 211 | ``` 212 | 213 | The above would add a table under the route description that lists all the route parameters. 214 | 215 | You can use the `@params` tag as many times as you have parameters in your route path. 216 | 217 | ## @query 218 | 219 | The `@query` allows you to document any parameters which are passed via HTTP request url. 220 | 221 | With this tag you need to provide the name and a description. The name is the first word of the text following the tag. 222 | * `@query MyName And this part is the description` 223 | 224 | You can also optionally provide a type for the parameter. 225 | * `@query {String} MyName And this part is the description` 226 | 227 | You can also specify that the parameter is optional by placing the name within square brackets. 228 | * `@query {String} [MyName] And this part is the description` 229 | 230 | Lastly you can define a default value for the parameter. The idea is to document the value which will be used if the parameter is not provided. 231 | * `@query {String} [MyName=Phillip] And this part is the description` 232 | 233 | 234 | ```js 235 | /** 236 | * Download files. 237 | * 238 | * @name Download Files 239 | * @path {GET} /v1/files 240 | * @query {String} [fileType] will limit the download to just these file types. 241 | */ 242 | server.get({ 243 | url: '/v1/files', 244 | }, (req, res, next) => {...} 245 | ``` 246 | 247 | The above would add a table under the route description that lists all the query parameters. 248 | 249 | You can use the `@query` tag as many times as you have parameters in your request url you wish to document. 250 | 251 | ## @response 252 | 253 | The `@response` allows you to document the response that your route will make 254 | 255 | With this tag you need to provide the name and a description. The name is the first word of the text following the tag. 256 | * `@response MyName And this part is the description` 257 | 258 | You can also optionally provide a type for the parameter. 259 | * `@response {String} MyName And this part is the description` 260 | 261 | You can also specify that the response is optional by placing the name within square brackets. 262 | * `@response {String} [MyName] And this part is the description` 263 | 264 | Lastly you can define a default value for the parameter. 265 | * `@response {String} [MyName=Phillip] And this part is the description` 266 | 267 | 268 | ```js 269 | /** 270 | * Download files. 271 | * 272 | * @name Download Files 273 | * @path {GET} /v1/files 274 | * @response {Object} metadata 275 | * @response {String} metadata.name 276 | * @response {String} metadata.link 277 | */ 278 | server.get({ 279 | url: '/v1/files', 280 | }, (req, res, next) => {...} 281 | ``` 282 | 283 | The above would add a table under the route description that lists that the route answer with a json document containing the `name` and `link` key. 284 | 285 | You can use the `@response` tag as many times as you have parameters in your response you wish to document. 286 | 287 | 288 | ## @code 289 | 290 | The `@code` allows you to document the http response code that your route will make 291 | 292 | With this tag you need to provide the number like this 293 | * `@code {200} and then you document why this code is happening` 294 | 295 | ```js 296 | /** 297 | * Download files. 298 | * 299 | * @name Download Files 300 | * @path {GET} /v1/files 301 | * @code {200} if the request is successful 302 | * @code {500} if the request fail because the database isn't accesible 303 | * @response {Object} metadata 304 | * @response {String} metadata.name 305 | * @response {String} metadata.link 306 | */ 307 | server.get({ 308 | url: '/v1/files', 309 | }, (req, res, next) => {...} 310 | ``` 311 | 312 | The above would add a table under the route description that lists that the route answer with a json document containing the `name` and `link` key. 313 | 314 | You can use the `@response` tag as many times as you have parameters in your response you wish to document. 315 | 316 | ## @service 317 | 318 | The `@service` allows you to document the host used to make a the request, in the time of microservice, you may have to hit a specific host to reach a specific microservice, you may say that they must be behind a load balancer, but in localhost thats not always the case. 319 | 320 | With this tag you need to provide the number like this 321 | * `@service API` 322 | 323 | ```js 324 | /** 325 | * Download files. 326 | * 327 | * @name Download Files 328 | * @service DOWNLOAD 329 | * @path {GET} /v1/files 330 | * @code {200} if the request is successful 331 | * @code {500} if the request fail because the database isn't accesible 332 | * @response {Object} metadata 333 | * @response {String} metadata.name 334 | * @response {String} metadata.link 335 | */ 336 | server.get({ 337 | url: '/v1/files', 338 | }, (req, res, next) => {...} 339 | ``` 340 | 341 | ## @chain 342 | 343 | The `@chain` allows you to document the sequence of handlers which will handle the request before current endpoint or middleware, for e.g it may be the express.js middlewares. 344 | 345 | With this tag you may provide @link or human-friendly name of chain element. It is important to keep order of @chain declarations same as real request handling order. 346 | * `@chain {@link module:} || @chain ` 347 | 348 | ```js 349 | /** 350 | * Handlers chain - example middleware 351 | * 352 | * @name SomeMiddleware 353 | * @path {POST} /v1/chain 354 | * @version v1 355 | * @since v1 356 | */ 357 | app.get('/v1/chain', function (request, response, next) { 358 | ... 359 | next() 360 | }) 361 | 362 | /** 363 | * Handlers chain - endpoint after the middleware 364 | * 365 | * @name SomeEndpoint 366 | * @path {POST} /v1/chain 367 | * @version v1 368 | * @since v1 369 | * @code 200 370 | * @chain {@link module:MyRoutes.SomeMiddleware} 371 | * @chain This handler 372 | */ 373 | app.get('/v1/chain', function (request, response) { 374 | ... 375 | }) 376 | ``` 377 | 378 | The above would add a table under the route description that lists that the route answer with a json document containing the `name` and `link` key. 379 | 380 | ## Contributing 381 | 382 | Bug reports and pull requests are welcome on GitHub at https://github.com/vmarchaud/jsdoc-http-plugin. This project is intended to be a safe, welcoming space for 383 | collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 384 | 385 | ## License 386 | 387 | The library is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 388 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Example 1 2 | 3 | This is just an example Express app which does not really do anything but shows how you can document its routes with JsDoc Route Plugin. -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an example of how to document routes. 3 | * @module MyRoutes 4 | */ 5 | 6 | 'use strict' 7 | 8 | var express = require('express') 9 | var app = express() 10 | 11 | /** 12 | * Just says hello. 13 | * @name Hello 14 | * @path {GET} / 15 | * @response {String} hello 16 | * @response {Array} entries 17 | */ 18 | app.get('/', function (request, response) { 19 | response.send({ hello: 'world', entries: [] }) 20 | }) 21 | 22 | /** 23 | * Upload a file. 24 | * 25 | * @name File Upload 26 | * @path {POST} /v1/file 27 | * @auth This route requires HTTP Basic Authentication. If authentication fails it will return a 401 error. 28 | * @code {200} Success everyting is cool 29 | * @version v1 30 | * @since v1 31 | */ 32 | app.post('/v1/file', function (request, response) { 33 | response.send(200) 34 | }) 35 | 36 | /** 37 | * Upload a file. 38 | * 39 | * @name File Upload 40 | * @path {POST} /v2/file 41 | * @version v2 42 | * @since v1 43 | * @auth This route requires HTTP Basic Authentication. If authentication fails it will return a 401 error. 44 | * @header authorization is the identification information for the request 45 | * @header {String} user-id is the unique User Id to assign to the file 46 | * @code {200} everyting is cool 47 | */ 48 | app.post('/v2/file', function (request, response) { 49 | response.send(200) 50 | }) 51 | 52 | /** 53 | * Upload a file. 54 | * 55 | * @name File Upload 56 | * @path {POST} /v3/file 57 | * @version v3 58 | * @since v1 59 | * @auth This route requires HTTP Basic Authentication. If authentication fails it will return a 401 error. 60 | * @body {String} userId is the unique identifier for the user we are uploading the file to. 61 | * @body {Boolean} [sync=false] when true the route will be synchronous otherwise the route 62 | * is asynchronous. 63 | */ 64 | app.post('/v3/file', function (request, response) { 65 | response.send(200) 66 | }) 67 | 68 | /** 69 | * Upload a file. 70 | * 71 | * @name File Upload 72 | * @path {POST} /v4/file 73 | * @version v4 74 | * @since v1 75 | * @auth This route requires HTTP Basic Authentication. If authentication fails it will return a 401 error. 76 | * @body {String} userId is the unique identifier for the user we are uploading the file to. 77 | * @body {Boolean} [sync=false] when true the route will be synchronous otherwise the route 78 | * is asynchronous. 79 | * @query {String} folder is the folder to upload the file to. 80 | */ 81 | app.post('/v4/file', function (request, response) { 82 | response.send(200) 83 | }) 84 | 85 | /** 86 | * Download a file. 87 | * 88 | * @name Download a File 89 | * @path {GET} /v1/files/:fileId 90 | * @version v1 91 | * @since v1 92 | * @params {String} :fileId is the unique identifier for the file to download. 93 | * @response {String} msg Your file 94 | */ 95 | app.get('/v1/files/:fileId', function (request, response) { 96 | request.send({ msg: 'Your File' }) 97 | }) 98 | 99 | /** 100 | * Download files. 101 | * 102 | * @name Download Files 103 | * @path {GET} /v1/files 104 | * @version v1 105 | * @since v1 106 | * @query {String} [fileType] will limit the download to just these file types. 107 | * @response {Object} data 108 | * @response {String} data.msg 109 | */ 110 | app.get('/v1/files', function (request, response) { 111 | request.send({ data: { msg: 'Your Files' } }) 112 | }) 113 | 114 | 115 | /** 116 | * Handlers chain - example middleware 117 | * @name SomeMiddleware 118 | * @path {POST} /v1/chain 119 | * @version v1 120 | * @since v1 121 | */ 122 | app.get('/v1/chain', function (request, response, next) { 123 | next() 124 | }) 125 | 126 | /** 127 | * 128 | */ 129 | function all () { 130 | 131 | } 132 | 133 | /** 134 | * Handlers chain - endpoint after the middleware 135 | * 136 | * @name SomeEndpoint 137 | * @path {POST} /v1/chain 138 | * @version v1 139 | * @since v1 140 | * @code 200 141 | * @chain {@link module:MyRoutes.SomeMiddleware} 142 | * @chain This handler 143 | */ 144 | app.get('/v1/chain', function (request, response) { 145 | request.send(200) 146 | }) 147 | 148 | app.listen(3000, function () { 149 | console.log('Example app is listening on port 3000, have fun!') 150 | }) 151 | -------------------------------------------------------------------------------- /examples/jsdoc.conf: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc","closure"] 5 | }, 6 | "source": { 7 | "include": [ "." ], 8 | "exclude": [ "node_modules", "lib", "index.js" ], 9 | "includePattern": ".+\\.js(doc|x)?$", 10 | "excludePattern": "(^|\\/|\\\\)_" 11 | }, 12 | "plugins": ["../index.js"], 13 | "templates": { 14 | "cleverLinks": false, 15 | "monospaceLinks": false 16 | }, 17 | "opts": { 18 | "recurse": true 19 | } 20 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines custom JDOC tags for documenting Express routes. 3 | * @module jsdoc-route-plugin 4 | */ 5 | 6 | 'use strict' 7 | 8 | const authenticationTag = require('./lib/authentication') 9 | const routeTag = require('./lib/route') 10 | const bodyParameterTag = require('./lib/bodyparam') 11 | const headerParameterTag = require('./lib/headerparam') 12 | const queryParameterTag = require('./lib/queryparam') 13 | const routeParameterTag = require('./lib/routeparam') 14 | const responseParameterTag = require('./lib/responseparam') 15 | const responseCodeTag = require('./lib/responsecode') 16 | const serviceTag = require('./lib/serviceparam') 17 | const chainTag = require('./lib/chain') 18 | 19 | exports.defineTags = function (dictionary) { 20 | dictionary.defineTag(serviceTag.name, serviceTag.options) 21 | dictionary.defineTag(authenticationTag.name, authenticationTag.options) 22 | dictionary.defineTag(routeTag.name, routeTag.options) 23 | dictionary.defineTag(bodyParameterTag.name, bodyParameterTag.options) 24 | dictionary.defineTag(headerParameterTag.name, headerParameterTag.options) 25 | dictionary.defineTag(queryParameterTag.name, queryParameterTag.options) 26 | dictionary.defineTag(routeParameterTag.name, routeParameterTag.options) 27 | dictionary.defineTag(responseParameterTag.name, responseParameterTag.options) 28 | dictionary.defineTag(responseCodeTag.name, responseCodeTag.options) 29 | dictionary.defineTag(chainTag.name, chainTag.options) 30 | } 31 | 32 | exports.handlers = { 33 | newDoclet: function (e) { 34 | serviceTag.newDocletHandler(e) 35 | authenticationTag.newDocletHandler(e) 36 | bodyParameterTag.newDocletHandler(e) 37 | headerParameterTag.newDocletHandler(e) 38 | queryParameterTag.newDocletHandler(e) 39 | routeParameterTag.newDocletHandler(e) 40 | routeTag.newDocletHandler(e) 41 | responseParameterTag.newDocletHandler(e) 42 | responseCodeTag.newDocletHandler(e) 43 | chainTag.newDocletHandler(e) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/authentication.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document the authentication of a route. 4 | * @module lib/authentication 5 | */ 6 | 7 | 'use strict' 8 | 9 | exports.name = 'auth' 10 | exports.options = { 11 | mustHaveValue: false, 12 | mustNotHaveDescription: false, 13 | canHaveType: false, 14 | canHaveName: false, 15 | onTagged: function (doclet, tag) { 16 | doclet.authentication = tag.value 17 | doclet.description = `${doclet.description || ''} 18 |
Authentication
19 |

${doclet.authentication || 'A authentication is needed to access this endpoint'}

` 20 | } 21 | } 22 | exports.newDocletHandler = function (e) { /* Do Nothing */ } 23 | -------------------------------------------------------------------------------- /lib/bodyparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document body parameters of a route. 4 | * @module lib/bodyparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'body' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: true, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.body) { 19 | doclet.body = [] 20 | } 21 | 22 | doclet.body.push({ 23 | 'name': tag.value.name, 24 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 25 | 'description': tag.value.description || '', 26 | 'optional': tag.value.optional === undefined ? '' : 'optional', 27 | 'defaultvalue': tag.value.defaultvalue === undefined ? undefined : tag.value.defaultvalue 28 | }) 29 | } 30 | } 31 | exports.newDocletHandler = function (e) { 32 | const parameters = e.doclet.body 33 | if (parameters) { 34 | const table = tableBuilder.build('Body Parameters', parameters) 35 | 36 | e.doclet.description = `${e.doclet.description || ''} 37 | ${table}` 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/chain.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document middlewares / endpoints chain for route 4 | * @module lib/chain 5 | */ 6 | 7 | 'use strict' 8 | 9 | exports.name = 'chain' 10 | exports.options = { 11 | mustHaveValue: true, 12 | mustNotHaveDescription: false, 13 | canHaveType: false, 14 | canHaveName: true, 15 | onTagged: function (doclet, tag) { 16 | if (!doclet.chain) { 17 | doclet.chain = [] 18 | } 19 | 20 | doclet.chain.push({ 21 | 'name': tag.text, 22 | }) 23 | } 24 | } 25 | 26 | exports.newDocletHandler = function (e) { 27 | 28 | 29 | 30 | const chain = e.doclet.chain 31 | 32 | 33 | if (chain && chain.length) { 34 | 35 | 36 | e.doclet.description = `
Handlers Chain:
37 | 38 | 39 | ${chain.reduce((html, handler) => { 40 | 41 | html += `` 42 | 43 | return html 44 | }, '')} 45 |
Order
${handler.name}
46 | ${e.doclet.description || ''}` 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/headerparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document header parameters of a route. 4 | * @module lib/headerparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'header' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: true, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.header) { 19 | doclet.header = [] 20 | } 21 | 22 | doclet.header.push({ 23 | 'name': tag.value.name, 24 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 25 | 'description': tag.value.description || '', 26 | 'optional': tag.value.optional === undefined ? '' : 'optional', 27 | 'defaultvalue': tag.value.defaultvalue === undefined ? undefined : tag.value.defaultvalue 28 | }) 29 | } 30 | } 31 | exports.newDocletHandler = function (e) { 32 | const parameters = e.doclet.header 33 | if (parameters) { 34 | const table = tableBuilder.build('Header Parameters', parameters) 35 | 36 | e.doclet.description = `${e.doclet.description || ''} 37 | ${table}` 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/parameterTableBuilder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module converts an array of parameter objects into an HTML table. 3 | * @module parameterTableBuilder 4 | */ 5 | 6 | 'use strict' 7 | 8 | function extraColumnInfo (params) { 9 | const result = { 10 | showAttributes: false, 11 | showDefaultValue: false 12 | } 13 | 14 | params.forEach((param) => { 15 | result.showAttributes = result.showAttributes || param.optional 16 | result.showDefaultValue = result.showDefaultValue || param.defaultvalue !== undefined 17 | }) 18 | 19 | return result 20 | } 21 | 22 | function buildTableEntry (param, columnInfo, opts) { 23 | const attributeCell = columnInfo.showAttributes ? `${param.optional}` : '' 24 | const defaultCell = columnInfo.showDefaultValue ? `${param.defaultvalue === undefined ? '' : param.defaultvalue}` : '' 25 | 26 | return ` 27 | ${opts.name === false ? '' : `${param.name}`} 28 | ${opts.type === false ? '' : `${escapeHtml(param.type)}`} 29 | ${attributeCell} 30 | ${defaultCell} 31 | ${param.description} 32 | ` 33 | } 34 | 35 | function buildTableHeader (columnInfo, opts) { 36 | const attributeHeader = columnInfo.showAttributes ? 'Attributes' : '' 37 | const defaultHeader = columnInfo.showDefaultValue ? 'Default' : '' 38 | return ` 39 | 40 | ${opts.name === false ? '' : 'Name'} 41 | ${opts.type === false ? '' : 'Type'} 42 | ${attributeHeader} 43 | ${defaultHeader} 44 | Description 45 | 46 | ` 47 | } 48 | 49 | function escapeHtml(unsafe) { 50 | if (unsafe && typeof unsafe === 'string') { 51 | return unsafe 52 | .replace(/&/g, "&") 53 | .replace(//g, ">") 55 | .replace(/"/g, """) 56 | .replace(/'/g, "'"); 57 | } else { 58 | return unsafe; 59 | } 60 | } 61 | 62 | exports.build = function (title, params, opts) { 63 | opts = Object.assign({ 64 | name: true, 65 | type: true, 66 | description: true 67 | }, opts) 68 | const columnInfo = extraColumnInfo(params) 69 | const paramTableEntries = [] 70 | 71 | params.forEach((param) => { 72 | paramTableEntries.push(buildTableEntry(param, columnInfo, opts)) 73 | }) 74 | 75 | return `
${title}:
76 | 77 | ${buildTableHeader(columnInfo, opts)} 78 | ${paramTableEntries.join('')} 79 |
` 80 | } 81 | -------------------------------------------------------------------------------- /lib/queryparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document header parameters of a route. 4 | * @module lib/queryparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'query' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: true, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.query) { 19 | doclet.query = [] 20 | } 21 | 22 | doclet.query.push({ 23 | 'name': tag.value.name, 24 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 25 | 'description': tag.value.description || '', 26 | 'optional': tag.value.optional === undefined ? '' : 'optional', 27 | 'defaultvalue': tag.value.defaultvalue === undefined ? undefined : tag.value.defaultvalue 28 | }) 29 | } 30 | } 31 | exports.newDocletHandler = function (e) { 32 | const parameters = e.doclet.query 33 | if (parameters) { 34 | const table = tableBuilder.build('Query Parameters', parameters) 35 | 36 | e.doclet.description = `${e.doclet.description || ''} 37 | ${table}` 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/responsecode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document responce code of a route. 4 | * @module lib/responsecode 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'code' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: false, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.code) { 19 | doclet.code = [] 20 | } 21 | 22 | doclet.code.push({ 23 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 24 | 'description': tag.value.description || '' 25 | }) 26 | } 27 | } 28 | exports.newDocletHandler = function (e) { 29 | const parameters = e.doclet.code 30 | if (parameters) { 31 | const table = tableBuilder.build('Response Code', parameters, { name: false }) 32 | 33 | e.doclet.description = `${e.doclet.description || ''} 34 | ${table}` 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/responseparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document return parameters of a route. 4 | * @module lib/responseparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'response' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: true, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.response) { 19 | doclet.response = [] 20 | } 21 | 22 | doclet.response.push({ 23 | 'name': tag.value.name, 24 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 25 | 'description': tag.value.description || '', 26 | 'optional': tag.value.optional === undefined ? '' : 'optional', 27 | 'defaultvalue': tag.value.defaultvalue === undefined ? undefined : tag.value.defaultvalue 28 | }) 29 | } 30 | } 31 | exports.newDocletHandler = function (e) { 32 | const parameters = e.doclet.response 33 | if (parameters) { 34 | const table = tableBuilder.build('Response', parameters) 35 | 36 | e.doclet.description = `${e.doclet.description || ''} 37 | ${table}` 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/route.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document header parameters of a route. 4 | * @module lib/headerparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | exports.name = 'path' 10 | exports.options = { 11 | mustHaveValue: true, 12 | mustNotHaveDescription: true, 13 | canHaveType: true, 14 | canHaveName: true, 15 | onTagged: function (doclet, tag) { 16 | doclet.route = { 17 | 'name': tag.value.name, 18 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '' 19 | } 20 | } 21 | } 22 | exports.newDocletHandler = function (e) { 23 | var route = e.doclet.route 24 | if (route) { 25 | e.doclet.scope = 'route' 26 | e.doclet.description = `
Route:
27 | 28 | 29 | 30 | 31 | 32 | 33 |
MethodPath
${route.type}${route.name}
34 | ${e.doclet.description || ''}` 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/routeparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document route parameters of a route. 4 | * @module lib/routeparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | const tableBuilder = require('./parameterTableBuilder') 10 | 11 | exports.name = 'params' 12 | exports.options = { 13 | mustHaveValue: true, 14 | mustNotHaveDescription: false, 15 | canHaveType: true, 16 | canHaveName: true, 17 | onTagged: function (doclet, tag) { 18 | if (!doclet.params) { 19 | doclet.params = [] 20 | } 21 | 22 | doclet.params.push({ 23 | 'name': tag.value.name, 24 | 'type': tag.value.type ? (tag.value.type.names.length === 1 ? tag.value.type.names[0] : tag.value.type.names) : '', 25 | 'description': tag.value.description || '' 26 | }) 27 | } 28 | } 29 | exports.newDocletHandler = function (e) { 30 | if (e.doclet.route === undefined) 31 | return; 32 | 33 | const parameters = e.doclet.params 34 | if (parameters) { 35 | const table = tableBuilder.build('Route Parameters', parameters) 36 | 37 | e.doclet.description = `${e.doclet.description || ''} 38 | ${table}` 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/serviceparam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module defines a custom jsDoc tag. 3 | * It allows you to document header parameters of a route. 4 | * @module lib/headerparam 5 | */ 6 | 7 | 'use strict' 8 | 9 | exports.name = 'service' 10 | exports.options = { 11 | mustHaveValue: false, 12 | mustNotHaveDescription: true, 13 | canHaveType: false, 14 | canHaveName: true, 15 | onTagged: function (doclet, tag) { 16 | doclet.service = { 17 | 'name': tag.value.name 18 | } 19 | } 20 | } 21 | exports.newDocletHandler = function (e) { 22 | var service = e.doclet.service 23 | if (service) { 24 | e.doclet.scope = 'service' 25 | e.doclet.description = `${e.doclet.description || ''} 26 |
Service:
27 |

Any call to this endpoint need to be done on service : ${service.name}

` 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsdoc-http-plugin", 3 | "version": "0.3.2", 4 | "description": "Plugin to add HTTP Route tags for documenting HTTP endpoints", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo 'boo on me I have no tests.'", 8 | "clean": "rm -rf ./out/*;", 9 | "example1": "rm -rf ./out/*; ./node_modules/.bin/jsdoc -c ./examples/jsdoc.conf --readme ./examples/README.md; true" 10 | }, 11 | "keywords": [ 12 | "jsdoc", 13 | "documentation", 14 | "express", 15 | "restful", 16 | "rest", 17 | "route", 18 | "plugin" 19 | ], 20 | "engines": { 21 | "node": ">=4.6.2" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/vmarchaud/jsdoc-http-plugin.git" 26 | }, 27 | "publishConfig": { 28 | "registry": "https://registry.npmjs.org/" 29 | }, 30 | "author": "Brad van der Laan", 31 | "license": "MIT", 32 | "dependencies": { 33 | "jsdoc": "^3.4.3" 34 | }, 35 | "peerDependencies": { 36 | "jsdoc": "^3.4.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | babylon@7.0.0-beta.19: 6 | version "7.0.0-beta.19" 7 | resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503" 8 | 9 | bluebird@~3.5.0: 10 | version "3.5.1" 11 | resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" 12 | 13 | catharsis@~0.8.9: 14 | version "0.8.9" 15 | resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.9.tgz#98cc890ca652dd2ef0e70b37925310ff9e90fc8b" 16 | dependencies: 17 | underscore-contrib "~0.3.0" 18 | 19 | escape-string-regexp@~1.0.5: 20 | version "1.0.5" 21 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 22 | 23 | graceful-fs@^4.1.9: 24 | version "4.1.11" 25 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 26 | 27 | js2xmlparser@~3.0.0: 28 | version "3.0.0" 29 | resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733" 30 | dependencies: 31 | xmlcreate "^1.0.1" 32 | 33 | jsdoc@^3.4.3: 34 | version "3.5.5" 35 | resolved "https://registry.yarnpkg.com/jsdoc/-/jsdoc-3.5.5.tgz#484521b126e81904d632ff83ec9aaa096708fa4d" 36 | dependencies: 37 | babylon "7.0.0-beta.19" 38 | bluebird "~3.5.0" 39 | catharsis "~0.8.9" 40 | escape-string-regexp "~1.0.5" 41 | js2xmlparser "~3.0.0" 42 | klaw "~2.0.0" 43 | marked "~0.3.6" 44 | mkdirp "~0.5.1" 45 | requizzle "~0.2.1" 46 | strip-json-comments "~2.0.1" 47 | taffydb "2.6.2" 48 | underscore "~1.8.3" 49 | 50 | klaw@~2.0.0: 51 | version "2.0.0" 52 | resolved "https://registry.yarnpkg.com/klaw/-/klaw-2.0.0.tgz#59c128e0dc5ce410201151194eeb9cbf858650f6" 53 | dependencies: 54 | graceful-fs "^4.1.9" 55 | 56 | marked@~0.3.6: 57 | version "0.3.19" 58 | resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790" 59 | 60 | minimist@0.0.8: 61 | version "0.0.8" 62 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 63 | 64 | mkdirp@~0.5.1: 65 | version "0.5.1" 66 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 67 | dependencies: 68 | minimist "0.0.8" 69 | 70 | requizzle@~0.2.1: 71 | version "0.2.1" 72 | resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.1.tgz#6943c3530c4d9a7e46f1cddd51c158fc670cdbde" 73 | dependencies: 74 | underscore "~1.6.0" 75 | 76 | strip-json-comments@~2.0.1: 77 | version "2.0.1" 78 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 79 | 80 | taffydb@2.6.2: 81 | version "2.6.2" 82 | resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" 83 | 84 | underscore-contrib@~0.3.0: 85 | version "0.3.0" 86 | resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" 87 | dependencies: 88 | underscore "1.6.0" 89 | 90 | underscore@1.6.0, underscore@~1.6.0: 91 | version "1.6.0" 92 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" 93 | 94 | underscore@~1.8.3: 95 | version "1.8.3" 96 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" 97 | 98 | xmlcreate@^1.0.1: 99 | version "1.0.2" 100 | resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f" 101 | --------------------------------------------------------------------------------