02 November 2014

ApiDoc

The output of the ApiDoc documentates the JavaScript code of the AngularJS Controllers and Services. This post shows you the workflow used to create the documentation automatically from the comments in the JavaScript source files using JSdoc and smartercomments. You can see the results on the ApiDoc page.

Flow

In the JavaScript source files you need to annotate your functions with comments. You can see examples at the commenting section at the bottom of this page. Before running JSdoc, smartcomments takes care of completing the comments in the source files. It will add e.g. @params annotations to the functions where they are missing. After smartcomment completed, the ApiDoc is generated by running the grunt task grunt doc which outputs the documentation in markdown language. The markdown is injected into a template file (documentation.hbs) which will be copied over to the _posts directory and commited back to the repository. GitHub will now render the documentation file in my page…

The dependencies in package.json are:

1
2
3
"grunt-jsdoc-to-markdown": "0.4.2",
"grunt-jsdoc": "0.5.7",
"smartcomments": "0.3.2"

In the Gruntfile.js it looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jsdoc : {
     dist : {
         src: ['app/js/*.js'], 
         options: {
             destination: 'assets/docs',
             configure : "./tests/config/jsdoc_conf.json",
             verbose: true,
             debug: true
         }
     }
 },
 
 // jsdoc to markdown language, compatible with templates
 jsdoc2md: {
     withTemplate: {
         options: {
             helper: 'tests/jsdoc2md/handlebars/helpers/*.js',
             template: 'tests/jsdoc2md/handlebars/templates/documentation.hbs',
             partial: 'tests/jsdoc2md/handlebars/partials/*.hbs'
         },
         src: "app/js/*.js",
         dest: "_posts/apidoc/"+grunt.template.today('yyyy-mm-dd')+"-ApiDoc.md"
     }
 }
 grunt.registerTask('doc', ['jsdoc2md'])

If you run grunt doc it will output the file _posts/apidoc/2014-11-02-ApiDoc.md (date is added. This is without smartcomments - the JS files are only preprocessed on Travis).

The documentation.hbs template is important for injecting the documentation in a markdown file with correct yaml frontmatter (the text between — and —) for this page:

---
layout: documentation
categories:
- apidoc
tagline:
tags:
- controller
published: true
---

 {{> documentation}}

grunt-to-markdown acutally uses the DMD library to transform JSdoc output to markdown. If you wanted to customize the input to the documentation.hbs template your starting point would be here. E.g. if you wanted to inject the documentation with a table of content you would replace > documentation with > main in above example. You could also override partials by adding them to the tests/jsdoc2md/handlebars/partials/ directory as configured in the Gruntfiles.js as shown above.

JSdoc

JSdoc is used to parse the JavaScript source files for comments. This could be done manually before committing by running the grunt task jsdoc2md. By the name of the task you can see that the plugin grunt-jsdoc-to-markdown is used to get the documentation in markdown.

Smartcomments

I use smartcomments to prevent cluttering my code comments with @param annotations, but also to be sure that the documentation of atttributes passed into functions are always up to date. The way it works is going through the JavaScript codes and where it doesn’t find a comment it adds it. This is an optional task run by Travis during assembly. Only the resulting ApiDoc file is commited back, not the modified JS files). It runs before running the JSdoc task.

So, during Travis build I run the smartcomments generation command defined in .travis.yml:

before_script:
  # automatically generate comments
  - smartcomments -g --config ./tests/config/smartcomments.json

First you need to have a ./tests/config/smartcomments.jsonconfiguration file which is passed to smartcomments in abouve statement via the --config parameter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "target_dir": ["app/js/"],
    "match_files": ["^((?!~).)*.(js)$"],
    "backup" : false,
    "private" : true,
    "favor_generated" : true,
    "tags": {
        "function":{
            "name":{},
            "desc":{"value":"Description"},
            "params":{}
        }
    }
}

Please note the private: true parameter. It will skip adding comments to functions annotated with /** @private */ as shown in the following section.

Commenting

Preparation of a “Controllers” namespace:

1
2
3
4
/**
 * @namespace Controllers
 * @description  The controller must be responsible for binding model data to views using $scope, and control information flow. It does not contain logic to fetch the data or manipulating it.
 */

Annotating a controller:

1
2
3
4
5
6
7
8
/**
 * @namespace Controllers.ExampleCtrl
 * @memborOf Controllers
 * @description ExampleCtrl is responsible for...
 */
 myApp.controller("ExampleCtrl",function ($scope) {
 ...
 }

Annotating a function inside the controller:

1
2
3
4
5
6
7
8
/**
 * @function getExample
 * @memberOf Controllers.ExampleCtrl
 * @description Function inside ExampleCtrl
 */
$scope.getComments = function() {
   ...
}

Prevent smartcomments from generating comments for functions you don’t want to be visible in the documentation. Annotate with /** @private */ comment.

1
2
3
4
/** @private  */
var privateFunction = function() {
...
}

I hope this can be useful for somebody. Please leave a comment if something is not clear…

Drop me a line!