JSDoc
We are currently moving from JSDuck, as it is unmaintained. For more details on this work see JSDoc WMF theme. |
MediaWiki and related codebases use JSDoc annotations to document JavaScript code. This page contains information about setting up JSDoc, documenting code, and publishing a docs site.
Documenting your code
[edit]JSDoc reads descriptions and annotations from code comments in JavaScript files. In general, annotations begin with @
and must be separated by new lines. Annotation blocks that precede a function or symbol assume that the annotation belongs to the following code and use the name of that function or symbol.
For a complete list of tags supported by JSDoc, see the JSDoc documentation.
ES6 example
[edit]/**
* Description for MyClass.
*
* @extends MyParentClass
*/
class MyClass extends MyParentClass {
/**
* @param {Object} [config] Configuration options
* @param {string} [config.optionA] Description for optionA
* @param {string} [config.optionB] Description for optionB
*/
constructor( config ) {
// …
}
// …
}
export default MyClass;
When using ES6 classes, you do not need to use tags such as @class, @member, @constructor, and @module since the structure of your code already expresses this to JSDoc. Adding these tags might cause duplicate definitions.
If you use ECMAScript Modules and want to export the class, this must be a separate statement due to a bug in JSDoc.[1] In the above example, using export default MyClass { … }
will break the documentation. Instead, move the export default MyClass;
to a separate statement.
ES5 examples
[edit]If a class declaration has many tags for both the class and the constructor, group the class tags and the constructor tags together, and separate them with a line break.
/**
* ContentEditable MediaWiki alien block extension node.
*
* @class
* @abstract
* @extends ve.ce.MWBlockExtensionNode
* @mixes ve.ce.MWAlienExtensionNode
*
* @constructor
* @param {ve.dm.MWAlienBlockExtensionNode} model Model to observe
* @param {Object} [config] Configuration options
*/
ve.ce.MWAlienBlockExtensionNode = function VeCeMWAlienBlockExtensionNode() {
To document a class that uses ES5 syntax, with the class and constructor defined together as function MyClass(…) {…}
, use:
- a
@classdesc
tag to document the class - a
@description
tag to document the constructor
/**
* @classdesc Class description.
*
* @description Constructor description.
*
* @param {string} myParam
* @return {string} Description
*/
ResourceLoader module example
[edit]/**
* Fetch and display a preview of the current editing area.
*
* @memberof module:mediawiki.page.preview
* @param {Object} config Configuration options
* @param {string} [config.summary=null] The edit summary
* @return {jQuery.Promise}
* @fires Hooks~'wikipage.content'
* @stable
*/
function doPreview( config ) {
// ...
}
/**
* Fetch and display a preview of the current editing area.
*
* @example
* var preview = require( 'mediawiki.page.preview' );
* preview.doPreview();
*
* @exports mediawiki.page.preview
*/
module.exports = {
doPreview: doPreview,
};
Links
[edit]Learn how to refer to other elements in your code by reading up on namepaths and the @link tag.
You can also use Markdown link syntax or a link enclosed in angle brackets. To link to a Phabricator task, use the task number (for example: T55555).
Types
[edit]In addition to the types supported by JSDoc, the JSDoc WMF theme supports string literals in type definitions using the syntax used by TypeScript (and recognized by TypeScript in JSDoc comments). For example: @param {'a'|'b'} param
.
Chainable
[edit]The JSDoc WMF theme supports the @chainable
tag to indicate chainable methods.
Configuration parameters
[edit]
When supplying an options
object (or config
or similar) as a parameter, document it using @param {Type} options.key
/**
* Creates an ve.ui.MWCategoryItemWidget object.
*
* @param {Object} config
* @param {Object} config.item Category item
* @param {string} config.item.name Category name
* @param {string} config.item.value
* @param {string} [config.item.sortKey='']
* @param {ve.dm.MWCategoryMetaItem} config.item.metaItem
* @param {boolean} [config.hidden] Whether the category is hidden or not
* @param {boolean} [config.missing] Whether the category's description page is missing
* @param {string} [config.redirectTo] The name of the category this category's page redirects to.
*/
ve.ui.MWCategoryItemWidget = function VeUiMWCategoryItemWidget( config ) {
Code examples
[edit]You can include examples in JSDoc by using the @example
tag (with an optional <caption></caption>
) or by using Markdown syntax (a line with ```
before and after the example). Markdown syntax is useful when you want to include multiple examples with text in between.
Previewing your changes
[edit]For MediaWiki core, you can generate a preview of the JSDoc site for a given patch using the patch demo tool. To view the JSDoc preview, check the box for "Build core documentation" on the patch demo form, and select the JSDoc documentation link on the main page of the demo wiki.
To test the docs locally, run npm install
and npm run doc
.
To run the JSDoc WMF theme locally, see the local development instructions in the theme's code repository.
Setting up JSDoc
[edit]Once you've documented your code using JSDoc tags, follow these steps to build and publish the docs.
1. Configure JSDoc
[edit]Create a jsdoc.json
file in your project's repository to configure JSDoc.
Configuration example
[edit]The below configuration example needs at least jsdoc-wmf-theme version 1.1.0 installed:
{
"opts": {
"destination": "docs/js",
"pedantic": true,
"readme": "README.md",
"recurse": true,
"template": "node_modules/jsdoc-wmf-theme"
},
"plugins": [
"node_modules/jsdoc-wmf-theme/plugins/default"
],
"source": {
"include": [ "modules", "resources" ],
"exclude": [ ]
},
"templates": {
"cleverLinks": true,
"default": {
"useLongnameInNav": true
},
"wmf": {
"maintitle": "My Project",
"repository": "https://gerrit.wikimedia.org/g/MyProject",
"siteMap": {
"sections": true
},
"linkMap": {}
}
}
}
Reusable links
[edit]Certain objects outside your repo will be automatically hyperlinked by the JSDoc WMF theme:
- OO and OO.ui
- jQuery
- mozilla.org standard built-in objects, such as Object, Error, Number, String, Array, Map, Iterator, Intl, etc.
- mozilla.org web APIs, such as console, DOM*, HTML*, XML*
Any objects outside your repo not defined by the theme will need to be defined manually, either using a linkMap or a prefixMap. See code examples below. Failing to define these will trigger "unknown link" warnings when running npm run doc
.
{
...
"templates": {
"wmf": {
"linkMap": {
"mw": "https://doc.wikimedia.org/mediawiki-core/master/js/mw.html",
"jQuery": "https://api.jquery.com/jQuery",
"Array": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array"
}
}
}
}
{
...
"templates": {
"wmf": {
"prefixMap": {
"mw.": "https://doc.wikimedia.org/mediawiki-core/master/js/{type}.html"
}
}
}
}
To unregister a default prefix, set the prefix to false
in your JSDoc config. To override a default prefix with local links, set the prefix to true
in your JSDoc config; for example, the OOUI documentation uses the below configuration to override the default prefix with links to the OOUI docs:
{
...
"templates": {
"wmf": {
"prefixMap": {
"OO.ui.": true
}
}
}
}
Configuration options
[edit]Option | Description |
---|---|
opts.destination |
Location of the autogenerated JavaScript documentation output. This should be ignored in a .gitignore . If you're using the regular CI jobs for generating and publishing the documentation, this must be docs .
|
opts.pedantic |
Treat errors as fatal errors, and treat warnings as errors |
opts.readme |
Homepage content for the JSDoc site |
opts.recurse |
Recurse into subdirectories when scanning for source files and tutorials |
opts.template |
JSDoc theme. For Wikimedia codebases, use node_modules/jsdoc-wmf-theme
|
plugins |
Array of plugin names to execute after building the docs, executed in the order they are specified. Theme plugins should be prefixed with node_modules/jsdoc-wmf-theme/plugins/ ; JSDoc plugins should be prefixed with plugins/ . At a minimum, we recommend adding the plugins shown in the example above. For more options, see #Plugins.
|
source.include |
Array of paths that JSDoc should parse. You can also use includePattern and a regular expression.
|
source.exclude |
Array of paths that JSDoc should ignore. You can also use excludePattern and a regular expression.
|
templates.cleverLinks |
Sets link text formatting based on the type of link. Set to true .
|
templates.default.useLongnameInNav |
If set to true , the navigation sidebar displays the full name of each item, including ancestors (for example: mw.user.clientPrefs).
If set to |
templates.wmf.maintitle |
Title of the site, overrides name from package.json
|
templates.wmf.repository |
Link to the project's code repository, overrides repo from package.json
|
templates.wmf.linkMap |
Map of #Reusable links |
templates.wmf.hideSections |
Array of sections that will not appear in the sidebar, can include Modules, Externals, Namespaces, Classes, Interfaces, Events, Mixins, and Tutorials |
templates.wmf.siteMap |
Object that adds a sitemap page to the sidebar |
templates.wmf.siteMap.sections |
Set to true to split the sitemap into sections by category (modules, classes, namespaces, etc.), defaults to false
|
templates.wmf.siteMap.include |
Array of categories to include in the sitemap, can include modules, externals, namespaces, classes, interfaces, events, mixins, and tutorials. Must be lowercase. Defaults to all categories |
Plugins
[edit]By adding plugins: [ "node_modules/jsdoc-wmf-theme/plugins/default" ]
to your config file as suggested above, you will enable these plugins:
Plugin | Description |
---|---|
allow-dots-in-modules | Supports module names that include periods |
betterlinks | Supports external links without a @link tag and Phabricator short links
|
externals | Map links to URLs using a pattern instead of hard-coding each URL |
jsdoc-class-hierarchy | Adds a floating class hierarchy box when your repo contains @extends statements. To enable this plugin, add "class-hierarchy": {"showList": true} to the opts section of your JSDoc config.
|
markdown | Supports Markdown formatting in descriptions |
summarize | Creates a summary field for each element based on its description |
Plugins are a great way to extend the functionality of JSDoc. You can contribute your own plugins to the theme by adding a file to the plugins directory. For more information about writing plugins, see the JSDoc documentation.
Allowing globals
[edit]Although globals are not recommended, you can configure the JSDoc WMF theme to allow specific globals using the opts.allowedGlobals
option.
For example, to allow the global initReferencePreviewsInstrumentation
method:
"opts": {
"allowedGlobals": [
"initReferencePreviewsInstrumentation"
]
}
Custom navigation
[edit]The JSDoc WMF theme allows you to customize the navigation sidebar. This feature can help provide additional context for the docs and help larger projects reduce information overload.
In this example, this project has over 70 classes, so the Classes section in the sidebar is too long to navigate easily. Instead, the project uses the custom navigation feature to:
- Hide the Classes section from the sidebar, and rely on the list of classes on each namespace page to guide users.
- Rename the Namespaces section.
- Add a landing page to the Namespaces section to provide context about each namespace.
- Limit the Namespaces section to only the top-level namespaces.
{
"opts": {
"pages": {
"namespaces": {
"longname": "Frontend API",
"readme": "resources/src/frontend-api.md",
"depth": 1
}
}
},
"templates": {
"wmf": {
"hideSections": [ "Classes" ]
}
}
}
Option | Description |
---|---|
opts.pages |
Object that configures custom navigation. Can include configuration objects for modules, externals, namespaces, classes, interfaces, events, mixins, and tutorials |
opts.pages.CATEGORY_NAME.longname |
Name to use in the sidebar in place of the default name |
opts.pages.CATEGORY_NAME.readme |
Markdown file to use as a category's landing page |
opts.pages.CATEGORY_NAME.depth |
Limits the navigation items shown under a category by the number of elements in each item. For example, mw has a depth of 1; mw.Api has a depth of 2
|
2. Configure NPM
[edit]- Create a script named
doc
in your package.json file that runsjsdoc -c jsdoc.json
. If you want to include private symbols in the docs, usejsdoc -c jsdoc.json -p
. The script should also be invoked as part oftest
, such as... && npm run doc && ...
. - Add the latest JSDoc release and jsdoc-wmf-theme release to
devDependencies
.npm install --save-dev jsdoc
npm install --save-dev jsdoc-wmf-theme
Configuration example
[edit]{
...,
"scripts": {
...,
"test": "grunt lint && npm run doc",
"doc": "jsdoc -c jsdoc.json",
...,
},
"devDependencies": {
...,
"jsdoc": "^x.x.x",
"jsdoc-wmf-theme": "^x.x.x",
...,
}
}
3. Preview your changes
[edit]4. Publish on doc.wikimedia.org
[edit]To add the documentation to https://doc.wikimedia.org:
- Get consensus from the repo's maintainers (for example, via a Phabricator ticket)
- Make sure
npm run doc
is configured and working in package.json - Create a patch for the repo integration/config that enables doc generation and upload for your repository in zuul/layout.yaml. (example)
- Create a patch for the repo integration/docroot that adds a section to wikimedia/doc/opensource.yaml (example)
To unpublish from doc.wikimedia.org, do the reverse. More details can be found in Continuous integration/Documentation generation and wikitech:doc.wikimedia.org.
Converting from JSDuck to JSDoc
[edit]Wikimedia used to use JSDuck to generate /docs/ pages, but is now transitioning to JSDoc. There are certain block tags that occur in JSDuck but never occur in JSDoc. These should be refactored, and avoided in new code.
Troubleshooting
[edit]Globals
[edit]Error: Unexpected global detected
The JSDoc WMF theme does not allow global methods, events, or properties outside of the window
namespace. For recommended alternatives, see Manual:Coding conventions/JavaScript#Exporting. If the theme detects a global, JSDoc will fail with the error "Unexpected global detected" and list the relevant name and path.
To fix this issue, make sure the global is part of a module, namespace, or class. You can also configure the theme to allow specific globals.
Common causes:
- In an ES5-style class, you may need to add a
@memberof
tag with the class, namespace, or module name. Modules names should be formatted asmodule:name
. - In an ES6-style class, an unnecessary
@class
annotation can cause this error. - If the error relates to a method in an ECMAScript module, see the note under #ES6 example.