Understanding ESDoc plugins

David Barral
Trabe
Published in
4 min readJun 18, 2018
Photo by Janko Ferlič on Unsplash

My team uses ESDoc to generate the documentation of JS projects. We use custom plugins to fit the generated docs to our needs. I wrote a post about those custom plugins, but it is a bit overwhelming without prior knowledge about how ESDoc plugins work. Hence this post.

Very brief ESDoc 101

ESDOC is a good documentation generator for JavaScript.

ESDoc supports out of the box EcmaScript 2015 code and uses JSDoc tags. The process is simple, you only have to add comments and some special tags to the code.

To generate the documentation using ESDoc you need to install some dependencies.

npm install --save-dev esdoc esdoc-standard-plugin

Write a basic .esdoc.js config file.

And finally run the esdoc command and browse to the generated docs/index.html.

Everything is a plugin

ESDoc is all about plugins. Almost all ESDoc features are provided as plugins. As you can see in the example above, even the most basic ESDoc configuration relies on the esdoc-standard-plugin.

As an ESDoc user you can add more ESDoc supported plugins or implement your own ones. Unfortunately the documentation is very basic and the only way to learn how they work is by experimenting or by reading the official plugin’s code in the esdoc-plugins repo. Let’s try filling that void!

Anatomy of an ESDoc plugin

A plugin is any object that implements at least one lifecycle handler (more on the lifecycle later).

ESDoc invokes the configured plugins and their handlers in order, injecting a PluginEvent. This event contains the information relevant to the lifecycle step besides the plugin options in event.data.option. The options object is the way we have to pass info to our plugin. It will contain the data defined in the option key from the config file.

The 10 lifecycle steps (in execution order)

onHandlePlugins

Runs once.

Use it to change the plugins configuration in runtime. event.data.plugins contains all the plugins with their config, in execution order.

onStart

Runs once.

The place to initialize the plugin using the config received in event.data.option.

onHandleConfig

Runs once.

It’s possible to change the whole ESDoc configuration stored in event.data.config. You can, for example, update it to force a particular execution order of the plugins.

onHandleCode

Runs for each source file.

You can change the raw code before being parsed. You can test against event.data.filePath which stores the full path of the source file, and update event.data.code which contains the raw code as a string.

onHandleCodeParser

Runs for each source file.

You can define a custom parser for each piece of code being parsed. By default ESDOC uses Babylon to parse the code. If a custom parser is configured, it must return a valid and compatible AST.

The PluginEvent contains the same info received in onHandleCode plus a parser function in event.data.parse, and the parser options in event.data.parserOption. You can change event.data.parse to be your custom parsing function.

Do not play with these unless you know what you are doing.

onHandleAST

Runs for each source file.

Manual code AST modifications go here. Handle with care! The event contains the same onHandleCode info plus the AST in event.data.ast. In this step you can add, remove or modify the AST nodes at will.

onHandleDocs

Runs once.

ESDOC generates its own metadata based on the AST of the code. The metadata is later used to generate the different parts of the documentation. In this step you can tinker with the metadata stored in the event.data.docs array and update it.

For example, the sum function will be represented by a structure like this one, that you can update to change the importPath for example.

Take into account that ESDOC will inject some entries to enrich the documentation in event.data.docs, like references to Standard ECMAScript stuff (data types, APIs, etc). This docs have an"external" type.

onPublish

Runs once.

You are given access to a file handling API at event.data to generate extra content. The API has four methods:

  • readFile(path): to read a file.
  • writeFile(path, content): to write a file.
  • copyFile(src, dest): to copy one file.
  • copyDir(src, dest): to copy a whole folder.

onHandleContent.

Runs for each emitted file.

Use this to fine tune the HTML output. event.data.fileName contains the full path of the emitted file and event.data.content the updatable HTML content as a string.

onComplete

Runs once.

Executed after all the output has been emitted and written to disk. Here you can do here whatever post processing you may need to do.

The plugin lifecycle in action

You can view how the plugin lifecycle works by adding a custom plugin that outputs some logging on each step. Use the following config and code to see it in action.

Notice that there is only one instance of the plugin. You need to be careful with stateful plugins. Although it isn’t “normal” to use many times the same plugin, ESDoc won’t protect you against it.

To infinity… and beyond!

Once you know and understand how ESDoc plugins work, writing your own plugin is quite straightforward. You can now check my other post where I show how to use ESDoc plugins to improve documentation, using some examples extracted from real projects.

--

--

David Barral
Trabe
Editor for

Co-founder @Trabe. Developer drowning in a sea of pointless code.