Understanding ESDoc plugins
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.