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
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.
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 < 0 || b < 0 || c < 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.