Automatic Documentation For JavaScript Projects — README, JSDoc & Mermaid

Esbjörn Blomquist
11 min readFeb 22, 2021
Misty Autumn Forest

This article is a result of some research I did around generating documentation for NodeJS/JavaScript based projects.

When using JSDoc to document JavaScript code with doc-strings, the next step is to get this information extracted and presented in a nice, friendly way. In any project, we also always have a README.md file with introductory descriptions and information. This main markdown file should serve as the front page of such a documentation UI.

Inside our README.md we could have made informative drawings to explain some control flow or architecture, written in the great mermaid language. We certainly want these drawings to become rendered graphically in our documentation webpage. Our README.md, needs to otherwise be rendered nicely with bullet lists, tables, and other expected output from markdown syntax.

JSDoc has many tags to describe a JavaScript construct. It would be nice to have support for a great set of these in our generated rendering. Basic stuff like optional arguments and default values are not always supported by JSDoc parsers. Ability to use some markdown in JSDoc descriptions can also be really nice.

Personally nowadays I really like dark theme pretty much everywhere on a screen. It would thus be nice to get this possibility for our documentation. Another feature that would be highly desired is a search field with ability to search or filter among all our documented constructs.

A documentation website needs to be set up once, and then generated again with new updates from the code and README with just one npm run ... command.

There are many frameworks and libraries out there that promises to do the job. What they produce does however vary a lot. Which ones are easy to install, easy to configure, support enough JSDoc tags, possible to style or change theme, and have enough features?

A summary of my set of requirements goes something like this:

  • Get the README.md document as landing page.
  • Get a well structured and easily navigated site of extracted documentation from the JSDoc doc strings found around the code.
  • Good support for different JSDoc tags and clear default styling of each documented construct from the code (@param , @returns , @static , @async , etc).
  • A global search field.
  • Some markdown support for the JSDoc description fields (at least support for multi-line text in the main description as well as for the @returns).
  • Automatic rendering of possible mermaid diagrams in the README.

A note to those not running Linux is that some scripts herein will contain Linux commands and will not work in other operating systems. It is left as an exercise to translate these into platform independent (nodejs based) equivalents.

Documentation.js

Docs: https://github.com/documentationjs/documentation
Repository: https://github.com/documentationjs/documentation
npm package: https://www.npmjs.com/package/documentation
GitHub stars: 5k
npm downloads: 50k

This library is in a way closely related to jsdoc-to-markdown (used in the setup of many other strategies below) in that it can just be a markdown generator with JS files as input, parsing doc strings. But it can also instead build HTML from the JSDoc, and glue the output together with markdown files.

That makes up the minimum of what we want. But let’s see how we set it up and how the result behaves.

npm i -D documentation

Add to package.json scripts:

"docs-show": "firefox -new-tab ./docs/index.html -foreground",
"docs-gen": "npx documentation build {​​​​​​​src/utils/*.js,src/logic/*.js}​​​​​​​ --config documentation.yml -f html -o docs",
"docs": "npm run docs-gen && npm run docs-show"

Create a new file documentation.yml:

toc:
- name: README
file: README.md

Add to .gitignore:

/docs

Now run:
npm run docs

Pros:

  • Minimalistic & clean output.
  • Very simple setup.

Cons:

  • No easy file or @module scoping.
    - This makes every function and construct end up in just a big pool.
  • No simple way to get dark theme, except starting to build one ourselves.

The ability to generate markdown from JSDoc was tested as a way to get module scoping. However, it made a much worse job, with missing information and formatting, compared to its HTML output.

Docusaurus v2

Docs: https://v2.docusaurus.io/
Repository: https://github.com/facebook/docusaurus
npm package: https://www.npmjs.com/package/@docusaurus/core
GitHub stars: 21k
npm downloads: 30k

This is version 2 of facebook’s popular static site generator. In combination with jsdoc-to-markdown it is able to provide the solution we're looking for.

npx @docusaurus/init@latest init docs classic

npm i -D jsdoc-to-markdown mermaid

Now we have a docs app inside our app (in /docs). We need to go there and add some things.
cd docs
npm i -D docusaurus-lunr-search remark-mermaid

Edit the file docusaurus.config.js:

/* eslint-disable */
const remarkMermaid = require("remark-mermaid");
const lunrSearch = require.resolve("docusaurus-lunr-search");
module.exports = {
title: "Comparator UI",
tagline: "OneToolBox TestCase Comparator UI",
url: "https://your-docusaurus-test-site.com",
baseUrl: "/",
onBrokenLinks: "throw",
onBrokenMarkdownLinks: "warn",
favicon: "img/favicon.ico",
organizationName: "ericsson", // Usually your GitHub org/user name.
projectName: "comparator-ui", // Usually your repo name.
themeConfig: {
navbar: {
title: "Comparator UI",
logo: {
alt: "Comparator UI Logo",
src: "img/logo.svg"
},
items: []
},
footer: {
style: "dark",
links: []
}
},
scripts: [
"https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.4/mermaid.min.js",
"/init.js"
],
presets: [
[
"@docusaurus/preset-classic",
{
docs: {
sidebarPath: require.resolve("./sidebars.json"),
routeBasePath: "/",
remarkPlugins: [[remarkMermaid, { simple: true }]]
},
theme: {
customCss: require.resolve("./src/css/custom.css")
}
}
]
],
plugins: [lunrSearch]
};

To complete the mermaid support, add a file init.js here in /docs containing the following.

/* eslint-disable */
window.addEventListener("load", () =>
mermaid.initialize({ startOnLoad: true })
);

We also need to configure the sidebar. In sidebars.js (still in /docs):

{
"someSidebar": {
"Site Docs": ["README", "api"]
}
}

We don’t want the special landing page that Docusaurus provides, but instead just our README. We need to remove the file src/pages/index.js.

Thus, still in our /docs folder:
rm src/pages/index.js

cd ..

Add to .gitignore:

/docs/docs

We will be checking in the /docs folder into version control (git), but not the target for the generated files, /docs/docs.

The "scripts" we need to add in our package.json (not the one in /docs!) are:

"postinstall": "cd docs && npm i && mkdir docs && cd ..",
"docs-readme": "echo -e \"---\nid: README\nslug: /\n---\n$(cat README.md)\" > docs/docs/README.md",
"docs-api": "npx jsdoc2md {src/**/*.js} > docs/docs/api.md",
"docs": "npm run docs-readme && npm run docs-api && cd docs && npm run build && npm run serve"
  • The docs-readme script adds necessary initial meta data to our README and copies it to /docs/docs.
  • docs-api runs the cli tool in jsdoc-to-markdown on a glob and outputs to /docs/docs. You can modify the glob here to fit your code-base.

Now, by running

npm run docs

we can browse to localhost:3000 to see our result.

Pros:

  • Very good structure of the site, with page ToC & file/module separation.
  • Great default theme with both light & dark mode.
    - Great markdown rendering.
  • Well supported & used framework.

Cons:

  • Complex setup.
    - Lots of configuration.
  • Not the best search feature.
    - Added through plugin, might exist better ones.

Docsify

Docs: https://docsify.js.org/
Repository: https://github.com/docsifyjs/docsify/
npm package: https://www.npmjs.com/package/docsify
GitHub stars: 16k
npm downloads: 25k

This versatile and popular documentation framework is an interesting candidate. It can not however parse JSDoc so we need to combine this solution with jsdoc-to-markdown.

npm i -D docsify-cli jsdoc-to-markdown docsify-fulltextsearch

docsify init ./docs

Now that you got a /docs folder, we will modify index.html and add a file _navbar.md inside it.

First open index.html and replace its content with the code below (In case this article has gotten a bit dated, you might want to check the standard generated index.html and just add chosen bits from below).

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Documentation</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Description" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css"
/>
<style>
/* style the nav-bar to be much more visible */
.app-nav {
text-align: center;
font-size: 1.5rem;
position: relative;
background-color: rgba(0, 0, 0, 0.3);
margin-left: 0 !important;
right: 0 !important;
left: 0;
top: 0;
z-index: 1;
}
.app-nav > ul > li {
padding: 16px;
border-bottom: 4px solid var(--mono-tint2);
}
</style>
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: "Documentation",
loadNavbar: true,
mergeNavbar: true, // Is this needed ?????????????
auto2top: true,
subMaxLevel: 3,
formatUpdated: "{MM}/{DD} {HH}:{mm}",
// logo: 'logo.png', // Add a logo if you wish
search: ["/", "/api"],
executeScript: true
};
</script>
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<!-- plugins -->
<script src="https://cdn.jsdelivr.net/npm/docsify-themeable@0/dist/js/docsify-themeable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
<!-- mermaid -->
<script src="//unpkg.com/mermaid/dist/mermaid.js"></script>
<script src="//unpkg.com/docsify-mermaid@latest/dist/docsify-mermaid.js">
<script>
mermaid.initialize({ startOnLoad: true });
</script>
<style>
/* activate full width for content, for mermaid graphs benefit */
.markdown-section {
max-width: none;
}
</style>
</body>
</html>

Then add the file _navbar.md in the docs folder with this content:

* [README](/)
* [API](/api)

Add our re-generated doc files to .gitignore:

/docs/README.md
/docs/api.md

Lastly we need some "scripts" in package.json:

"docs-show": "npx docsify serve ./docs",
"docs-gen": "cp -u README.md docs/ && npx jsdoc2md {src/**/*.js} > docs/api.md",
"docs": "npm run docs-gen && npm run docs-show"

Now we are ready to run:

npm run docs

Pros:

  • Easy adding of mermaid support.
    - Simple possibility to get full width for mermaid graphs.
  • Dark theme.

Cons:

  • Section nav-bar is a bit too hidden and needs custom styling.
  • Search results could be prettier.
  • Somewhat complex setup/configuration.
    - But at least most conf is in one file.

JSDoc + docdash

JSDoc3

Docs: https://jsdoc.app/
Repository: https://github.com/jsdoc/jsdoc
npm package: https://www.npmjs.com/package/jsdoc
GitHub stars: 11k
npm downloads: 500k

docdash

Docs: http://clenemt.github.io/docdash/
Repository: https://github.com/clenemt/docdash
npm package: https://www.npmjs.com/package/docdash
GitHub stars: 600
npm downloads: 90k

This is what anyone would first encounter searching for a JSDoc syntax documentation site generator. It is very old in the game, but still highly used (also behind the scenes in other documentation frameworks). It is based on so called templates that are modules of both theming/styling and features & settings for the site generation. There are many available templates. We add perhaps the most popular one that is great looking and has a well built set of features and settings.

npm i -D jsdoc docdash replace

The following is a somewhat standard JSON config file .jsdoc.json with template docdash activated and tweaked for our needs.

{
"plugins": ["plugins/markdown"],
"source": {
"include": ["src", "README.md"],
"includePattern": ".+\\.js$",
"excludePattern": "(node_modules/|docs)"
},
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"]
},
"opts": {
"destination": "./docs/",
"readme": "./README.md",
"encoding": "utf8",
"private": true,
"recurse": true,
"template": "./node_modules/docdash"
},
"docdash": {
"search": true,
"scripts": ["https://unpkg.com/mermaid/dist/mermaid.js", "docs-init.js"]
}
}

For enabling mermaid graphs we added a couple of scripts above in the "docdash" section, where the second one is a local custom docs-init.js that needs the following lines.

/* eslint-disable */
window.addEventListener("load", () =>
mermaid.initialize({ startOnLoad: true })
);

These are suggested "scripts" to add in package.json:

"docs-show": "firefox -new-tab ./docs/index.html -foreground",
"docs-mermaid": "npx replace '<pre class=\"prettyprint source lang-mermaid\"><code>(((?!(<\\/code>))(\\s|\\S))+)<\\/code><\\/pre>' '<div class=\"mermaid\">$1</div>' -r --include=\"index.html\" ./docs/",
"docs-gen": "npx jsdoc -c .jsdoc.json && cp -u docs-init.js docs/",
"docs": "npm run docs-gen && npm run docs-mermaid && npm run docs-show"

The "docs-mermaid" script became a beast so feel free to make some node script file instead that replaces mermaid code tags. (Even better if you make some module on npm for this as a JSDoc plugin! Yes, there is a jsdoc-mermaid plugin already, but that one only converts mermaid code inside doc-strings). The reason I made this as an inline script is to keep everything together a bit more, for simplicity.

We update our .gitignore to not include our generated bundle in /docs:

/docs

An alternative: If we include "package.json" in our "source"."include" list in .jsdoc.json above, JSDoc will produce versioned bundles in /docs instead of putting the docs site bundle directly in /docs. It can in that case be nice to push these to source control.

(The mermaid parts can of course be removed if that support is really not needed: The line "docdash"."scripts" in the config, the docs-init.js file, and && cp -u docs-init.js docs/ in "docs-gen" npm script.)

Pros:

  • Most popular solution with long time in the game.
  • High versatility through templates.
  • Selecting a good template can give many settings & features.
    - Great markdown & doc strings rendering can be achieved.
  • Multiple well built and actively maintained templates available.
  • Straightforward setup & configuration.
    - At least without mermaid additions, but even with the mermaid hack it’s not that complex.

Cons:

  • No plugin for adding mermaid support.
    - Template settings, a minor script, and a substitution/replace command can however enable it.

Docma

Docs: https://onury.io/docma/
Repository: https://github.com/onury/docma
npm package: https://www.npmjs.com/package/docma
GitHub stars: 280
npm downloads: 2k

This framework tryout did not get as much time and effort as others because of it being unpupular, and also because of too many thumbs down for the initial result.

npm i -D docma

One short line needed in package.json -> "scripts":

"docs": "docma && docma serve ./docs"

Pros:

  • Good markdown styling.
  • Interesting feature of automatic categorization of documented constructs.
    - Colors & filters

Cons:

  • Markdown pages & code docs are split into completely separate pages.
    - This should be easy to solve given that the Docma docs themselves combine them.
  • Bad JSDoc styling.
    - No markdown support in doc strings.
  • JSDoc tag support is not great.
  • The automatic categorization lowers control & adds lots of unwanted entities.
  • It would need a hack to enable mermaid.

DocumentJS

Docs: https://documentjs.com/
Repository: https://github.com/bitovi/documentjs
npm package: https://www.npmjs.com/package/documentjs
GitHub stars: 590
npm downloads: 180

This framework tryout did not get as much time and effort as others because of it being unpupular, and also because of too many thumbs down for the initial result.

npm i -D documentjs

We need a small configuration file documentjs.json:

{
"sites": {
"docs": {
"glob": "{src/**/*.js,README.md}"
}
}
}

Lines to add in package.json -> "scripts":

"docs-show": "firefox -new-tab ./docs/index.html -foreground",
"docs": "npx documentjs && rm -rf docs && mv ../docs . && npm run docs-show"

It is not nice that the default behavior is for it to put the docs outside our project folder. It might be that the idea is that documentjs should be run from inside /src, but my effort to try it that way was not easily successful.

As with the others, we add to our .gitignore:

/docs

Pros:

  • Simple setup & configuration.

Cons:

  • Bad JSDoc support.
  • Theme customization seems possible, but no ready ready made alternative themes to be found.
  • Not a very generic default design.
  • Site structure is very split up.
  • No search.
  • No mermaid support.
  • Very low usage/popularity.

EsDoc

Docs: https://esdoc.org/
Repository: https://github.com/esdoc/esdoc
npm package: https://www.npmjs.com/package/esdoc
GitHub stars: 2.6k
npm downloads: 65k

EsDoc has an extended feature set compared to the others, in that it parses all code and not just the doc strings. It will try to produce some documentation for all your constructs, regardless of doc strings (but of course much better documentation for identifiers with good doc strings). It is then also able to produce documentation coverage data.

The minimum set of dependencies are these 2:

npm i -D esdoc esdoc-standard-plugin

Some additional plugins can be nice to add:

npm i -D esdoc-ecmascript-proposal-plugin esdoc-brand-plugin esdoc-undocumented-identifier-plugin

Then we need a configuration file .esdoc.json:

{
"source": "./src",
"destination": "./docs",
"plugins": [
{ "name": "esdoc-standard-plugin" },
{
"name": "esdoc-ecmascript-proposal-plugin",
"option": { "all": true }
},
{
"name": "esdoc-brand-plugin",
"option": {
"title": "Comparator UI",
"description": "The app for the testcase comparator feature",
"repository": "https://github.com/foo/bar",
"site": "http://my-library.org",
"image": "http://my-library.org/logo.png"
}
},
{
"name": "esdoc-undocumented-identifier-plugin",
"option": { "enable": true }
}
]
}

If you want the minimal configuration, just remove all plugins except esdoc-standard-plugin.

Lines to add in package.json -> "scripts":

"docs-show": "firefox -new-tab ./docs/index.html -foreground",
"docs": "npx esdoc && npm docs-show"

Pros:

  • Great way for configuration, and nice plugin system.
  • Might be the solution you want if you rather go for automatic parsing for everything and skip manual doc strings.

Cons:

  • Bad default scoping / No module scopes.
  • Last commit: Spring of 2018.
  • Even with esdoc-ecmascript-proposal-plugin it errored a lot parsing my code.

EsDoc2

There’s also a separate library that is a sort of community version of EsDoc. It is very similar to EsDoc, perhaps a bit more modern and maintained, but my experience was not any better than EsDoc.

Docs: http://esdoc2.org/
Repository: https://github.com/esdoc2/esdoc2
npm package: https://www.npmjs.com/package/esdoc2
GitHub stars: 30
npm downloads: 14k

Conclusion

JSDoc with docdash is the winner.

JSDoc with docdash is the winner. But given a slightly changed set of requirements, or depending on personal taste, one could favor docusaurus v2 or docsify, because they are not far behind and you can customize a great lot. I also liked documentation.js because of it being clean & minimalistic.

--

--

Esbjörn Blomquist

Software architect and developer with web and functional JavaScript close to his heart. Worked many years with web solutions mainly within the IoT field.