├── .gitignore ├── .jshintrc ├── README.md ├── examples ├── api_data.json ├── api_project.json └── example.md ├── index.js ├── package.json └── templates └── default.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser" : true, 3 | "devel" : true, 4 | "jquery" : true, 5 | "couch" : false, 6 | "es5" : false, 7 | "node" : true, 8 | "rhino" : false, 9 | "prototypejs" : false, 10 | "mootools" : false, 11 | 12 | "asi" : false, 13 | "bitwise" : false, 14 | "boss" : false, 15 | "curly" : true, 16 | "debug" : false, 17 | "eqeqeq" : true, 18 | "eqnull" : true, 19 | "evil" : false, 20 | "forin" : false, 21 | "immed" : true, 22 | "laxbreak" : false, 23 | "maxerr" : 100, 24 | "maxlen" : 0, 25 | "newcap" : true, 26 | "noarg" : true, 27 | "noempty" : false, 28 | "nonew" : false, 29 | "nomen" : false, 30 | "onevar" : false, 31 | "passfail" : false, 32 | "plusplus" : false, 33 | "regexp" : false, 34 | "undef" : false, 35 | "unused" : "vars", 36 | "sub" : true, 37 | "strict" : true, 38 | "globalstrict": true, 39 | "white" : true, 40 | "expr" : true, 41 | "smarttabs": true, 42 | "predef" : [ 43 | "describe", 44 | "it", 45 | "beforeEach", 46 | "afterEach" 47 | ] 48 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is no longer maintained. 2 | 3 | Head over to [https://github.com/rigwild/apidoc-markdown](https://github.com/rigwild/apidoc-markdown) for a an up to date version. 4 | -------------------------------------------------------------------------------- /examples/api_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "get", 4 | "url": "/user/:id", 5 | "title": "Read data of a User", 6 | "version": "0.3.0", 7 | "name": "GetUser", 8 | "group": "User", 9 | "permission": { 10 | "name": "admin", 11 | "title": "Admin access rights needed.", 12 | "description": "Optionally you can write here further Informations about the permission.\n\nAn \"apiDefinePermission\"-block can have an \"apiVersion\", so you can attach the block to a specific version.\n" 13 | }, 14 | "description": "Compare Verison 0.3.0 with 0.2.0 and you will see the green markers with new items in version 0.3.0 and red markers with removed items since 0.2.0.", 15 | "parameter": { 16 | "fields": { 17 | "Parameter": [ 18 | { 19 | "group": "Parameter", 20 | "type": "String", 21 | "field": "id", 22 | "optional": false, 23 | "description": "The Users-ID." 24 | } 25 | ] 26 | } 27 | }, 28 | "examples": [ 29 | { 30 | "title": "CURL example:", 31 | "content": " curl -i -X POST http://localhost:3001/example\n -H 'Content-Type: application/json' \\\n -d '{ \"id\": \"4711\" }'\n" 32 | } 33 | ], 34 | "success": { 35 | "fields": { 36 | "Success 200": [ 37 | { 38 | "group": "Success 200", 39 | "type": "String", 40 | "field": "id", 41 | "optional": false, 42 | "description": "The Users-ID." 43 | }, 44 | { 45 | "group": "Success 200", 46 | "type": "Date", 47 | "field": "registered", 48 | "optional": false, 49 | "description": "Registration Date." 50 | }, 51 | { 52 | "group": "Success 200", 53 | "type": "Date", 54 | "field": "name", 55 | "optional": false, 56 | "description": "Fullname of the User." 57 | } 58 | ] 59 | }, 60 | "examples": [ 61 | { 62 | "title": "Success-Response (example):", 63 | "content": " HTTP/1.1 200 OK\n {\n \"id\": \"4711\"\n \"registered\": \"31.01.2013\"\n \"name\": \"John Doe\"\n }\n" 64 | } 65 | ] 66 | }, 67 | "error": { 68 | "fields": { 69 | "Error 4xx": [ 70 | { 71 | "group": "Error 4xx", 72 | "field": "NoAccessRight", 73 | "optional": false, 74 | "description": "Only authenticated Admins can access the data." 75 | }, 76 | { 77 | "group": "Error 4xx", 78 | "field": "UserNotFound", 79 | "optional": false, 80 | "description": "The id of the User was not found." 81 | } 82 | ] 83 | }, 84 | "examples": [ 85 | { 86 | "title": "Error-Response (example):", 87 | "content": " HTTP/1.1 401 Not Authenticated\n {\n \"error\": \"NoAccessRight\"\n }\n" 88 | } 89 | ] 90 | }, 91 | "filename": "example/example.js" 92 | }, 93 | { 94 | "type": "get", 95 | "url": "/user/:id", 96 | "title": "Read data of a User", 97 | "version": "0.2.0", 98 | "name": "GetUser", 99 | "group": "User", 100 | "permission": { 101 | "name": "admin", 102 | "title": "This title is visible in version 0.1.0 and 0.2.0", 103 | "description": "" 104 | }, 105 | "description": "Here you can describe the function.\nMultilines are possible.", 106 | "parameter": { 107 | "fields": { 108 | "Parameter": [ 109 | { 110 | "group": "Parameter", 111 | "type": "String", 112 | "field": "id", 113 | "optional": false, 114 | "description": "The Users-ID." 115 | } 116 | ] 117 | } 118 | }, 119 | "success": { 120 | "fields": { 121 | "Success 200": [ 122 | { 123 | "group": "Success 200", 124 | "type": "String", 125 | "field": "id", 126 | "optional": false, 127 | "description": "The Users-ID." 128 | }, 129 | { 130 | "group": "Success 200", 131 | "type": "Date", 132 | "field": "name", 133 | "optional": false, 134 | "description": "Fullname of the User." 135 | } 136 | ] 137 | } 138 | }, 139 | "error": { 140 | "fields": { 141 | "Error 4xx": [ 142 | { 143 | "group": "Error 4xx", 144 | "field": "UserNotFound", 145 | "optional": false, 146 | "description": "The id of the User was not found." 147 | } 148 | ] 149 | } 150 | }, 151 | "filename": "example/example.js" 152 | }, 153 | { 154 | "type": "get", 155 | "url": "/user/:id", 156 | "title": "Read data of a User", 157 | "version": "0.1.0", 158 | "name": "GetUser", 159 | "group": "User", 160 | "permission": { 161 | "name": "admin", 162 | "title": "This title is visible in version 0.1.0 and 0.2.0", 163 | "description": "" 164 | }, 165 | "description": "Here you can describe the function.\nMultilines are possible.", 166 | "parameter": { 167 | "fields": { 168 | "Parameter": [ 169 | { 170 | "group": "Parameter", 171 | "type": "String", 172 | "field": "id", 173 | "optional": false, 174 | "description": "The Users-ID." 175 | } 176 | ] 177 | } 178 | }, 179 | "success": { 180 | "fields": { 181 | "Success 200": [ 182 | { 183 | "group": "Success 200", 184 | "type": "String", 185 | "field": "id", 186 | "optional": false, 187 | "description": "The Users-ID." 188 | }, 189 | { 190 | "group": "Success 200", 191 | "type": "Date", 192 | "field": "name", 193 | "optional": false, 194 | "description": "Fullname of the User." 195 | } 196 | ] 197 | } 198 | }, 199 | "error": { 200 | "fields": { 201 | "Error 4xx": [ 202 | { 203 | "group": "Error 4xx", 204 | "field": "UserNotFound", 205 | "optional": false, 206 | "description": "The error description text in version 0.1.0." 207 | } 208 | ] 209 | } 210 | }, 211 | "filename": "example/example.js" 212 | }, 213 | { 214 | "type": "post", 215 | "url": "/user", 216 | "title": "Create a new User", 217 | "version": "0.3.0", 218 | "name": "PostUser", 219 | "group": "User", 220 | "permission": "none", 221 | "description": "In this case \"apiErrorStructure\" is defined and used.\nDefine blocks with params that will be used in several functions, so you dont have to rewrite them.", 222 | "parameter": { 223 | "fields": { 224 | "Parameter": [ 225 | { 226 | "group": "Parameter", 227 | "type": "String", 228 | "field": "name", 229 | "optional": false, 230 | "description": "Name of the User." 231 | } 232 | ] 233 | } 234 | }, 235 | "success": { 236 | "fields": { 237 | "Success 200": [ 238 | { 239 | "group": "Success 200", 240 | "type": "String", 241 | "field": "id", 242 | "optional": false, 243 | "description": "The new Users-ID." 244 | } 245 | ] 246 | } 247 | }, 248 | "error": { 249 | "fields": { 250 | "Error 4xx": [ 251 | { 252 | "group": "Error 4xx", 253 | "field": "NoAccessRight", 254 | "optional": false, 255 | "description": "Only authenticated Admins can access the data." 256 | }, 257 | { 258 | "group": "Error 4xx", 259 | "field": "UserNameTooShort", 260 | "optional": false, 261 | "description": "Minimum of 5 characters required." 262 | } 263 | ] 264 | }, 265 | "examples": [ 266 | { 267 | "title": " Response (example):", 268 | "content": " HTTP/1.1 400 Bad Request\n {\n \"error\": \"UserNameTooShort\"\n }\n" 269 | } 270 | ] 271 | }, 272 | "filename": "example/example.js" 273 | }, 274 | { 275 | "type": "post", 276 | "url": "/user", 277 | "title": "Create a User", 278 | "version": "0.2.0", 279 | "name": "PostUser", 280 | "group": "User", 281 | "permission": "none", 282 | "description": "In this case \"apiErrorStructure\" is defined and used.\nDefine blocks with params that will be used in several functions, so you dont have to rewrite them.", 283 | "parameter": { 284 | "fields": { 285 | "Parameter": [ 286 | { 287 | "group": "Parameter", 288 | "type": "String", 289 | "field": "name", 290 | "optional": false, 291 | "description": "Name of the User." 292 | } 293 | ] 294 | } 295 | }, 296 | "success": { 297 | "fields": { 298 | "Success 200": [ 299 | { 300 | "group": "Success 200", 301 | "type": "String", 302 | "field": "id", 303 | "optional": false, 304 | "description": "The Users-ID." 305 | } 306 | ] 307 | } 308 | }, 309 | "error": { 310 | "fields": { 311 | "Error 4xx": [ 312 | { 313 | "group": "Error 4xx", 314 | "field": "NoAccessRight", 315 | "optional": false, 316 | "description": "Only authenticated Admins can access the data." 317 | }, 318 | { 319 | "group": "Error 4xx", 320 | "field": "UserNameTooShort", 321 | "optional": false, 322 | "description": "Minimum of 5 characters required." 323 | } 324 | ] 325 | }, 326 | "examples": [ 327 | { 328 | "title": " Response (example):", 329 | "content": " HTTP/1.1 400 Bad Request\n {\n \"error\": \"UserNameTooShort\"\n }\n" 330 | } 331 | ] 332 | }, 333 | "filename": "example/example.js" 334 | }, 335 | { 336 | "type": "put", 337 | "url": "/user/:id", 338 | "title": "Change a new User", 339 | "version": "0.3.0", 340 | "name": "PutUser", 341 | "group": "User", 342 | "permission": "none", 343 | "description": "This function has same errors like POST /user, but errors not defined again, they were included with \"apiErrorStructure\"", 344 | "parameter": { 345 | "fields": { 346 | "Parameter": [ 347 | { 348 | "group": "Parameter", 349 | "type": "String", 350 | "field": "name", 351 | "optional": false, 352 | "description": "Name of the User." 353 | } 354 | ] 355 | } 356 | }, 357 | "error": { 358 | "fields": { 359 | "Error 4xx": [ 360 | { 361 | "group": "Error 4xx", 362 | "field": "NoAccessRight", 363 | "optional": false, 364 | "description": "Only authenticated Admins can access the data." 365 | }, 366 | { 367 | "group": "Error 4xx", 368 | "field": "UserNameTooShort", 369 | "optional": false, 370 | "description": "Minimum of 5 characters required." 371 | } 372 | ] 373 | }, 374 | "examples": [ 375 | { 376 | "title": " Response (example):", 377 | "content": " HTTP/1.1 400 Bad Request\n {\n \"error\": \"UserNameTooShort\"\n }\n" 378 | } 379 | ] 380 | }, 381 | "filename": "example/example.js" 382 | }, 383 | { 384 | "version": "0.3.0", 385 | "group": "example.js", 386 | "type": "", 387 | "url": "", 388 | "filename": "example/example.js" 389 | }, 390 | { 391 | "version": "0.2.0", 392 | "error": { 393 | "fields": { 394 | "Error 4xx": [ 395 | { 396 | "group": "Error 4xx", 397 | "field": "NoAccessRight", 398 | "optional": false, 399 | "description": "Only authenticated Admins can access the data." 400 | }, 401 | { 402 | "group": "Error 4xx", 403 | "field": "UserNameTooShort", 404 | "optional": false, 405 | "description": "Minimum of 5 characters required." 406 | } 407 | ] 408 | }, 409 | "examples": [ 410 | { 411 | "title": " Response (example):", 412 | "content": " HTTP/1.1 400 Bad Request\n {\n \"error\": \"UserNameTooShort\"\n }\n" 413 | } 414 | ] 415 | }, 416 | "group": "example.js", 417 | "type": "", 418 | "url": "", 419 | "filename": "example/example.js" 420 | }, 421 | { 422 | "version": "0.1.0", 423 | "group": "example.js", 424 | "type": "", 425 | "url": "", 426 | "filename": "example/example.js" 427 | } 428 | ] -------------------------------------------------------------------------------- /examples/api_project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apidoc-example", 3 | "version": "0.3.0", 4 | "description": "apidoc example project", 5 | "apidoc": "

Example text from API.md

\n

General

\n

This Text is optionally and not needed to create the documentation.

\n

HowTo include

\n

This text is from file "API.md".

\n

In your projects "package.json" you can set "apidoc" with a description text or "apidocFilename" with the filename to include into your documentation.

\n

This example attempts to integrate "API.md". If not available, then the "apidoc" string is used.

\n
{\n  "name": "example",\n  "version": "0.3.0",\n  "description": "apidoc example project.",\n  "apidoc": "This is a description, it will be ignored if parameter apidocFilename exist.",\n  "apidocFilename": "API.md"\n}
\n", 6 | "generator": { 7 | "version": "0.3.0", 8 | "time": "2014-01-14T15:09:45.694Z" 9 | } 10 | } -------------------------------------------------------------------------------- /examples/example.md: -------------------------------------------------------------------------------- 1 | # apidoc-example v0.3.0 2 | 3 | apidoc example project 4 | 5 | - [User](#user) 6 | - [Read data of a User](#read-data-of-a-user) 7 | - [Create a new User](#create-a-new-user) 8 | - [Change a new User](#change-a-new-user) 9 | 10 | 11 | 12 | # User 13 | 14 | ## Read data of a User 15 | 16 | Compare Verison 0.3.0 with 0.2.0 and you will see the green markers with new items in version 0.3.0 and red markers with removed items since 0.2.0. 17 | 18 | GET /user/:id 19 | 20 | ### Parameters 21 | 22 | | Name | Type | Description | 23 | |---------|-----------|--------------------------------------| 24 | | id | String | The Users-ID. | 25 | 26 | ### Examples 27 | 28 | CURL example: 29 | 30 | ``` 31 | curl -i -X POST http://localhost:3001/example 32 | -H 'Content-Type: application/json' \ 33 | -d '{ "id": "4711" }' 34 | 35 | ``` 36 | 37 | ### Success Response 38 | 39 | Success-Response (example): 40 | 41 | ``` 42 | HTTP/1.1 200 OK 43 | { 44 | "id": "4711" 45 | "registered": "31.01.2013" 46 | "name": "John Doe" 47 | } 48 | 49 | ``` 50 | ### Error Response 51 | 52 | Error-Response (example): 53 | 54 | ``` 55 | HTTP/1.1 401 Not Authenticated 56 | { 57 | "error": "NoAccessRight" 58 | } 59 | 60 | ``` 61 | ## Create a new User 62 | 63 | In this case "apiErrorStructure" is defined and used. 64 | Define blocks with params that will be used in several functions, so you dont have to rewrite them. 65 | 66 | POST /user 67 | 68 | ### Parameters 69 | 70 | | Name | Type | Description | 71 | |---------|-----------|--------------------------------------| 72 | | name | String | Name of the User. | 73 | 74 | ### Error Response 75 | 76 | Response (example): 77 | 78 | ``` 79 | HTTP/1.1 400 Bad Request 80 | { 81 | "error": "UserNameTooShort" 82 | } 83 | 84 | ``` 85 | ## Change a new User 86 | 87 | This function has same errors like POST /user, but errors not defined again, they were included with "apiErrorStructure" 88 | 89 | PUT /user/:id 90 | 91 | ### Parameters 92 | 93 | | Name | Type | Description | 94 | |---------|-----------|--------------------------------------| 95 | | name | String | Name of the User. | 96 | 97 | ### Error Response 98 | 99 | Response (example): 100 | 101 | ``` 102 | HTTP/1.1 400 Bad Request 103 | { 104 | "error": "UserNameTooShort" 105 | } 106 | 107 | ``` 108 | 109 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var fs = require('fs'), 6 | ejs = require('ejs'), 7 | _ = require('underscore'); 8 | 9 | var argv = require('optimist') 10 | .usage('Generate documentation from apidoc data.\nUsage: apidoc-markdown -p [path] -o [output file]') 11 | .demand(['path', 'output']) 12 | .alias({ 13 | 'path': 'p', 14 | 'output': 'o', 15 | 'template': 't' 16 | }) 17 | .describe({ 18 | 'path': 'Path to generated apidoc output. Where api_data.json & api_project.json resides.', 19 | 'output': 'Output file to write.', 20 | 'template': 'Path to EJS template file, if not specified default template will be used.', 21 | 'prepend': 'Prepend file after TOC.' 22 | }).argv; 23 | 24 | ejs.filters.undef = function (obj) { 25 | return obj ? obj : ''; 26 | }; 27 | 28 | ejs.filters.mlink = function (obj) { 29 | return (obj || '').toLowerCase().replace(/\s/g, '-'); 30 | }; 31 | 32 | var tmplFile = argv.template ? argv.template : __dirname + '/templates/default.md', 33 | apiData = JSON.parse(fs.readFileSync(argv.path + '/api_data.json')), 34 | projData = JSON.parse(fs.readFileSync(argv.path + '/api_project.json')), 35 | template = ejs.compile(fs.readFileSync(tmplFile).toString()); 36 | 37 | apiData = _.filter(apiData, function (entry) { 38 | return entry.type; 39 | }); 40 | 41 | var apiByGroup = _.groupBy(apiData, function (entry) { 42 | return entry.group; 43 | }); 44 | 45 | var apiByGroupAndName = {}; 46 | Object.keys(apiByGroup).forEach(function (key) { 47 | apiByGroupAndName[key] = _.groupBy(apiByGroup[key], function (entry) { 48 | return entry.name; 49 | }); 50 | }); 51 | 52 | var data = { 53 | project: projData, 54 | data: apiByGroupAndName 55 | }; 56 | 57 | data.prepend = argv.prepend ? fs.readFileSync(argv.prepend).toString() : null; 58 | fs.writeFileSync(argv.output, template(data)); 59 | console.log('Wrote apidoc-markdown template output to: ' + argv.output); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apidoc-markdown", 3 | "version": "0.2.1", 4 | "description": "Generate API documentation in markdown format from apidoc data.", 5 | "main": "index.js", 6 | "scripts": { 7 | "release": "release-it -n -i patch", 8 | "release:minor": "release-it -n -i minor", 9 | "release:major": "release-it -n -i major" 10 | }, 11 | "directories": { 12 | "example": "examples" 13 | }, 14 | "bin": { 15 | "apidoc-markdown": "./index.js" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/martinj/node-apidoc-markdown" 20 | }, 21 | "keywords": [ 22 | "apidoc", 23 | "markdown" 24 | ], 25 | "author": "Martin Jonsson ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/martinj/node-apidoc-markdown/issues" 29 | }, 30 | "dependencies": { 31 | "optimist": "~0.6.0", 32 | "underscore": "~1.5.2", 33 | "ejs": "~0.8.5" 34 | }, 35 | "devDependencies": { 36 | "release-it": "^2.8.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /templates/default.md: -------------------------------------------------------------------------------- 1 | # <%= project.name %> v<%= project.version %> 2 | 3 | <%= project.description %> 4 | 5 | <% Object.keys(data).forEach(function (group) { -%> 6 | - [<%= group %>](#<%=: group | mlink %>) 7 | <% Object.keys(data[group]).forEach(function (sub) { -%> 8 | - [<%= data[group][sub][0].title %>](#<%=: data[group][sub][0].title | mlink %>) 9 | <% }); -%> 10 | 11 | <% }); %> 12 | 13 | <% if (prepend) { -%> 14 | <%- prepend %> 15 | <% } -%> 16 | <% Object.keys(data).forEach(function (group) { -%> 17 | # <%= group %> 18 | 19 | <% Object.keys(data[group]).forEach(function (sub) { -%> 20 | ## <%= data[group][sub][0].title %> 21 | 22 | <%-: data[group][sub][0].description | undef %> 23 | 24 | <%-: data[group][sub][0].type | upcase %> <%= data[group][sub][0].url %> 25 | 26 | <% if (data[group][sub][0].header && data[group][sub][0].header.fields.Header.length) { -%> 27 | ### Headers 28 | 29 | | Name | Type | Description | 30 | |---------|-----------|--------------------------------------| 31 | <% data[group][sub][0].header.fields.Header.forEach(function (header) { -%> 32 | | <%- header.field %> | <%- header.type %> | <%- header.optional ? '**optional**' : '' %> <%- header.description %> | 33 | <% }); //forech parameter -%> 34 | <% } //if parameters -%> 35 | <% if (data[group][sub][0].parameter && data[group][sub][0].parameter.fields && data[group][sub][0].parameter.fields.Parameter.length) { -%> 36 | 37 | ### Parameters 38 | 39 | | Name | Type | Description | 40 | |---------|-----------|--------------------------------------| 41 | <% data[group][sub][0].parameter.fields.Parameter.forEach(function (param) { -%> 42 | | <%- param.field %> | <%- param.type %> | <%- param.optional ? '**optional**' : '' %> <%- param.description %> | 43 | <% }); //forech parameter -%> 44 | <% } //if parameters -%> 45 | <% if (data[group][sub][0].examples && data[group][sub][0].examples.length) { -%> 46 | 47 | ### Examples 48 | 49 | <% data[group][sub][0].examples.forEach(function (example) { -%> 50 | <%= example.title %> 51 | 52 | ``` 53 | <%- example.content %> 54 | ``` 55 | <% }); //foreach example -%> 56 | <% } //if example -%> 57 | 58 | <% if (data[group][sub][0].success && data[group][sub][0].success.examples && data[group][sub][0].success.examples.length) { -%> 59 | ### Success Response 60 | 61 | <% data[group][sub][0].success.examples.forEach(function (example) { -%> 62 | <%= example.title %> 63 | 64 | ``` 65 | <%- example.content %> 66 | ``` 67 | <% }); //foreach success example -%> 68 | <% } //if examples -%> 69 | <% if (data[group][sub][0].error && data[group][sub][0].error.examples && data[group][sub][0].error.examples.length) { -%> 70 | ### Error Response 71 | 72 | <% data[group][sub][0].error.examples.forEach(function (example) { -%> 73 | <%= example.title %> 74 | 75 | ``` 76 | <%- example.content %> 77 | ``` 78 | <% }); //foreach error example -%> 79 | <% } //if examples -%> 80 | <% }); //foreach sub -%> 81 | <% }); //foreach group -%> 82 | 83 | --------------------------------------------------------------------------------