How to JSDoc

How do you document your JavaScript? I use JSDoc. It's a tool which goes through your source code, looks at comments, and generates a set of HTML files. It's human readable even without generating the documentation. And because the documentation is in comments, it is less likely to become outdated as your code evolves.

This is a short how-to on installing, configuring, and using JSDoc.

All the examples in this blog post are available on github in my JSDoc samples repo.

How to install

JSDoc is available as an npm package. Assuming you have node and npm installed, simply execute the following to install JSDoc.

$ npm i --save-dev jsdoc

You can supply parameters through the command line. But it is much simpler to use a config file instead (more on this later).

$ touch jsdoc.conf.json

We run the JSDoc command line tool to generate the HTML output.

$ ./node_modules/.bin/jsdoc -c jsdoc.conf.json

Or, we can have an npm script defined in our package.json file.

{
  "scripts": {
    "doc": "jsdoc -c jsdoc.conf.json"
  }
}

Which can then be executed through npm.

$ npm run doc

JSDoc places its output in ./out by default. But we can easily change the output destination using the configuration file or command line arguments.

How to configure

You can read about configuring jsdoc in detail on the JSDoc website. But, here is a starter config that will process .js, .jsdoc, and .jsx files. You will notice this is pretty much the same starter configuration found on the official JSDoc website. It also enables the markdown plugin which lets you use markdown within your JSDoc comments to format your documentation.

{
    "tags": {
        "allowUnknownTags": true,
        "dictionaries": ["jsdoc","closure"]
    },
    "source": {
        "include": ["src"],
        "includePattern": ".+\\.js(doc|x)?$",
        "excludePattern": "(^|\\/|\\\\)_"
    },
    "plugins": [
        "plugins/markdown"
    ],
    "templates": {
        "cleverLinks": true,
        "monospaceLinks": true
    },
    "opts": {
        "destination": "docs",
        "recurse": true,
        "readme": "README.md"
    }
}

How to write jsdoc

JSDoc looks for comment blocks that start with a forward slash and two asterisks (as opposed to regular comment blocks that start with a forward slash and a single asterisk).

The comment block is associated with whatever code element immediately follows it. The block can contain text, and various tags which are used to convey information like return types, parameters, namespaces etc. This is what a simple JSDoc comment looks like

/**
 * Multiply two numbers.
 * @param {number} a - the first number
 * @param {number} b - the second number
 * @returns {number} - the product of a and b
 */
function multiply (a, b) {
  return a * b
}

And this is the JSDoc output

Screenshot of JSDoc output for sample multiply function

This blog post focuses on just a few tags, enough to get you a taste without overwhelming you. Once you are comfortable, you should head over to usejsdoc.org to see the complete list. Note that some tags are synonymous. For instance, @param and @arg both mean the same thing, a function parameter.

How to document...

A function

A function does not need a particular tag. But you can include a description, parameters, return values, exceptions, events and even related hyperlinks which might be relevant to the function. For example,

/**
 * Returns the area of a triangle calculated using Heron's formula.
 *
 * @param {number} a - length of one of the sides of the triangle
 * @param {number} b - length of another side of the triangle
 * @param {number} c - length of the last side of the triangle
 * @returns {number} - the area
 * @throws Will throw an error if any of the side-lengths is negative
 * @see https://www.mathsisfun.com/geometry/herons-formula.html
 */
function areaOfTriangle(a, b, c) {
  if (a < 0 || b < 0 || c < 0) {
    throw new Error('Side length can not be negative');
  }
  const s = (a + b + c) / 2;
  return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}

From this code, JSDoc produces the following output.

Screenshot of JSDoc output for sample function

A module

You can tell JSDoc that a given file is a module by using the @module tag.

/**
 * @module lib/triangle-geometry
 * @desc Contains utility functions for triangles.
 */

/**
 * Returns the area of a triangle calculated using Heron's formula.
 *
 * @param {number} a - length of one of the sides of the triangle
 * @param {number} b - length of another side of the triangle
 * @param {number} c - length of the last side of the triangle
 * @returns {number} - the area
 * @throws Will throw an error if any of the side-lengths is negative
 * @see https://www.mathsisfun.com/geometry/herons-formula.html
 */
function areaOfTriangle(a, b, c) {
  if (a < 0 || b < 0 || c < 0) {
    throw new Error('Side length can not be negative');
  }
  const s = (a + b + c) / 2;
  return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}

JSDoc will automatically associate all code-items in this file with the module when generating documentation.

You can omit the module name from your @module tag and let JSDoc figure it out from the file-path. But I have found that to be less than satisfactory for modules with a directory and index.js. That's because JSDoc will consider the module-name to be <directory-name>/index.js while you probably want it to be just <module-name>.

For items in other files (for a multi-file module), you must make the association yourself by using the @memberof tag. For example…

/**
 * The perimeter; the sum of all sides.
 *
 * @memberof module:lib/triangle-geometry
 * @param {number} a - length of one of the sides of the triangle
 * @param {number} b - length of another side of the triangle
 * @param {number} c - length of the last side of the triangle
 * @returns {number} - the perimeter
 * @throws Will throw an error if any of the side-lengths is negative
 * @see https://www.mathsisfun.com/triangle.html
 */
function perimeterOfTriangle(a, b, c) {
  if (a &lt; 0 || b &lt; 0 || c &lt; 0) {
    throw new Error('Side length can not be negative');
  }
  return a + b + c;
}

A class

When using a constructor function

Use the @class tag to identity ES5 constructor functions. JSDoc will automatically recognize instance functions (as long as the functions have a JSDoc block).

/**
 * A triangle.
 * @param {number} a - length of a side
 * @param {number} b - length of the second side
 * @param {number} c - length of the third side
 * @class
 */
function Triangle (a, b, c) {
  this.a = a;
  this.b = b;
  this.c = c;
}

/**
 * @return {number} - the area of this triangle
 */
Triangle.prototype.getArea = function() {
  return areaOfTriangle(this.a, this.b, this.c);
};

When using ES6 class syntax

JSDoc is able to identify ES6 classes without a @class or @constructor tag.

/**
 * A triangle.
 */
class Triangle {
  /**
   * @param {number} a - length of a side
   * @param {number} b - length of the second side
   * @param {number} c - length of the third side
   */
  constructor(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }

  /**
   * @return {number} - the area of this triangle
   */
  getArea() {
    return areaOfTriangle(this.a, this.b, this.c);
  }
}

An object

When it appears as a parameter

Each property of the object must be mentioned as a separate @param. The name needs to be a qualified dot-notation name. For example, consider the following code where properties a, b, and c are mentioned as individual parameters with the names triangle.a, triangle.b, and triangle.c.

/**
 * Returns the perimeter of a triangle.
 * @param {Object} triangle - the triangle whose parameter is desired
 * @param {number} triangle.a - length of a side
 * @param {number} triangle.b - length of the second side
 * @param {number} triangle.c - length of the third side
 */
function perimeter(triangle) {
  return triangle.a + triangle.b + triangle.c;
}

When it appears as a constant

We use the tags @type and @property to describe the object.

/**
 * A sample object being documented.
 * @type {Object}
 * @property {string} propertyOne='foo' - the first property
 * @property {number} propertyTwo=2 - a numeric property
 * @property {string} propertyThree='baz' - the last property
 */
const AnObject = {
  propertyOne: 'foo',
  propertyTwo: 2,
  propertyThree: 'baz'
};

Note that this is how JSDoc can be used to document propTypes in React components.

An enum

Enums are documented using the @enum tag. JSDoc will present the enumeration as an object, and tabulate the various enum values as properties. You can document individual values with JSDoc and it will appear in the JSDoc table as description.

/**
 * An enumaration of various triangle types.
 * @enum {Object}
 */
const TriangleType = {
  equilateral: 'equilateral',
  isosceles: 'isosceles',
  scalene: 'scalene'
};

Customising look and feel

You can customise the default template JSDoc ships with, or try one of the many community ones. My current favorite is toast-jsdoc.

Conclusion

I hope this very short introduction has got you curious about JSDoc. Check out the jsdoc-sample repository and run jsdoc on it. You will love having detailed API docs for your JavaScript, and so will anyone else who has to use your code.

References