forked from jsdoc/jsdoc.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
about-plugins.html
381 lines (374 loc) · 21.2 KB
/
about-plugins.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
<!DOCTYPE html>
<!-- THIS IS A GENERATED FILE. DO NOT EDIT. -->
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="description" content="How to create and use JSDoc plugins.">
<title>Use JSDoc: About JSDoc plugins</title>
<link rel="stylesheet" href="styles/usejsdoc.css">
<link rel="stylesheet" href="styles/prettify.css">
<link rel="stylesheet" href="styles/css3-github-ribbon.css">
<script src="scripts/prettify.js"></script>
<!--[if lt IE 9]>
<script src="scripts/html5shiv.min.js"></script>
<script src="scripts/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header>
<a href="./index.html">@use JSDoc</a>
</header>
<article>
<h1>About JSDoc plugins</h1>
<h2>Table of Contents</h2>
<ul>
<li>
<a href="#creating-and-enabling-a-plugin">Creating and Enabling a Plugin</a>
</li>
<li>
<a href="#authoring-jsdoc-3-plugins">Authoring JSDoc 3 Plugins</a>
<ul>
<li>
<a href="#event-handlers">Event Handlers</a>
</li>
<li>
<a href="#tag-definitions">Tag Definitions</a>
</li>
<li>
<a href="#node-visitors">Node Visitors</a>
</li>
<li>
<a href="#throwing-errors">Throwing Errors</a>
</li>
</ul>
</li>
</ul>
<h2 id="creating-and-enabling-a-plugin">Creating and Enabling a Plugin</h2>
<p>There are two steps required to create and enable a new JSDoc plugin:</p>
<ol>
<li>Create a JavaScript module to contain your plugin code.</li>
<li>Include that module in the "plugins" array of <code>conf.json</code>. You can specify an absolute or relative path. If you use a relative path, JSDoc
searches for the plugin in the current working directory and the JSDoc directory, in that order.</li>
</ol>
<p>For example, if your plugin source code was saved in the "plugins/shout.js" file in the current working directory, you would include it by adding a
reference to it in conf.json like so:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint"><code>...
"plugins": [
"plugins/shout"
]
...
</code></pre>
</figure>
<h2 id="authoring-jsdoc-3-plugins">Authoring JSDoc 3 Plugins</h2>
<p>JSDoc 3's plugin system offers extensive control over the parsing process. A plugin can affect the parse results by doing any of the following:</p>
<ul>
<li>Defining event handlers</li>
<li>Defining tags</li>
<li>Defining a parse tree node processor</li>
</ul>
<h3 id="event-handlers">Event Handlers</h3>
<p>At the highest level, a plugin may register handlers for specific named-events that occur in the documentation generation process. JSDoc will pass the handler
an event object containing pertinent information. Your plugin module should export a <em>handlers</em> object that contains your handler, like so:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.handlers = {
newDoclet: function(e) {
//Do something when we see a new doclet
}
}
</code></pre>
</figure>
<h4 id="event-parsebegin">Event: parseBegin</h4>
<p>The <code>parseBegin</code> event is fired before JSDoc starts loading and parsing the source files. Your plugin can control which files JSDoc will parse by
modifying the event's contents.</p>
<p><strong>Note</strong>: This event is fired in JSDoc 3.2 and later.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>sourcefiles: An array of paths to source files that will be parsed.</li>
</ul>
<h4 id="event-filebegin">Event: fileBegin</h4>
<p>This is triggered when the parser has started on a new file. You might use this to do any per-file initialization your plugin needs to do.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>filename: the name of the file</li>
</ul>
<h4 id="event-beforeparse">Event: beforeParse</h4>
<p>This is triggered before parsing has begun. You can use this method to modify the source code that will be parsed. For instance, you might add some virtual doclets
so they get added to the documentation.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>filename: the name of the file</li>
<li>source: the contents of the file</li>
</ul>
<p>Below is an example that adds a virtual doclet for a function to the source so that it will get parsed and added to the documentation. This might be done to
document methods that will be present for end-user use, but might not be in the source code being documented, like methods provided by a third-party superclass:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.handlers = {
beforeParse: function(e) {
var extraDoc = ["",
"/**",
"Here's a description of this function",
"@name superFunc",
"@memberof ui.mywidget",
"@function",
"*/", ""];
e.source += extraDoc.join("\n");
}
}
</code></pre>
</figure>
<h4 id="event-jsdoccommentfound">Event: jsdocCommentFound</h4>
<p>This is fired whenever a JSDoc comment is found. It may or may not be associated with any code. You might use this to modify the contents of a comment before
it is processed.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>filename: the name of the file</li>
<li>comment: the text of the comment</li>
<li>lineno: the line number the comment was found on</li>
</ul>
<h4 id="event-symbolfound">Event: symbolFound</h4>
<p>This is fired when the parser comes across a symbol in the code it thinks is important. This usually means things that one might want to document -- variables,
functions, object literals, object property definitions, assignments, etc., but the symbols the parser finds can be modified by a plugin (see <a href="#node-visitors">Node Visitors</a> below).</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>filename: the name of the file</li>
<li>comment: the comment associated with the symbol, if any</li>
<li>id: the unique id of the symbol</li>
<li>lineno: the line number the symbols was found on</li>
<li>range: an array containing the first and last characters of the code associated with the symbol</li>
<li>astnode: the node of the parse tree</li>
<li>code: information about the code. This usually contains "name", "type", and "node" properties and might also have "value",
"paramnames", or "funcscope" properties depending on the symbol.</li>
</ul>
<h4 id="event-newdoclet">Event: newDoclet</h4>
<p>This is the highest level event and is fired when a new doclet has been created. This means that a JSDoc comment or a symbol has been processed, and the actual
doclet that will be passed to the template has been created.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>doclet: the new doclet that was created</li>
</ul>
<p>The properties of the doclet can vary depending on the comment or symbol used to create it. Additionally, tag definitions (See "Tag Definitions" below)
can modify the doclet. Some common properties you're likely to see include:</p>
<ul>
<li>comment: the text of the comment (may be empty if symbol is undocumented)</li>
<li>meta: some information about the doclet, like filename, line number, etc.</li>
<li>description</li>
<li>kind</li>
<li>name</li>
<li>longname: the fully qualified name, including memberof info</li>
<li>memberof: the function/class/namespace that this is a member of</li>
<li>scope: (global|static|instance|inner)</li>
<li>undocumented: true if the symbol didn't have a JSDoc comment</li>
<li>defaultvalue: the specified default value for a property/variable</li>
<li>type: the specified type of parameter/property/function return (e.g. Boolean)</li>
<li>params: an object containing the list of parameters to a function</li>
<li>tags: an object containing the set of tags not handled by the parser (note: this is only available if <code>allowUnknownTags</code> is set to true in the conf.json
file for JSDoc 3)</li>
</ul>
<p>Below is an example of a newDoclet handler that shouts the descriptions:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.handlers = {
newDoclet: function(e) {
// e.doclet will refer to the newly created doclet
// you can read and modify properties of that doclet if you wish
if (typeof e.doclet.description === 'string') {
e.doclet.description = e.doclet.description.toUpperCase();
}
}
};
</code></pre>
</figure>
<h4 id="event-filecomplete">Event: fileComplete</h4>
<p>This is fired when the parser is done with a file. You might use this to perform some cleanup for your plugin.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>filename: the name of the file</li>
<li>source: the contents of the file</li>
</ul>
<h4 id="event-parsecomplete">Event: parseComplete</h4>
<p>The <code>parseComplete</code> event is fired after JSDoc has parsed all of the specified source files.</p>
<p><strong>Note</strong>: This event is fired in JSDoc 3.2 and later.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>sourcefiles: An array of paths to source files that were parsed.</li>
<li>doclets: An array of doclet objects. See the <a href="#event-newdoclet">newDoclet event</a> for details about the properties that each doclet can contain.
<strong>Note</strong>: This property is available in JSDoc 3.2.1 and later.</li>
</ul>
<h4 id="event-processingcomplete">Event: processingComplete</h4>
<p>The <code>processingComplete</code> event is fired after JSDoc updates the parse results to reflect inherited and borrowed symbols.</p>
<p><strong>Note</strong>: This event is fired in JSDoc 3.2.1 and later.</p>
<p>The event object will contain the following properties:</p>
<ul>
<li>doclets: An array of doclet objects. See the <a href="#event-newdoclet">newDoclet event</a> for details about the properties that each doclet can contain.</li>
</ul>
<h3 id="tag-definitions">Tag Definitions</h3>
<p>Adding tags to the tag dictionary is a mid-level way to affect documentation generation. Before a newDoclet event is triggered, JSDoc comment blocks are parsed
to determine the description and any JSDoc tags that may be present. When a tag is found, if it has been defined in the tag dictionary, it is given a chance
to modify the doclet.</p>
<p>Plugins can define tags by exporting a <em>defineTags</em> function. That function will be passed a dictionary that can be used to define tags, like so:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.defineTags = function(dictionary) {
//define tags here
}
</code></pre>
</figure>
<h4 id="the-dictionary">The Dictionary</h4>
<p>The dictionary provides the following methods:</p>
<ul>
<li>defineTag(title, opts) Used to define tags. The first parameter is the name of the tag (e.g. "param" or "overview"). the second is an object
containing options for the tag. The options can be the following:
<ul>
<li>mustHaveValue (Boolean): whether or not the tag must have a value (e.g "@name TheName")</li>
<li>mustNotHaveValue (Boolean): whether or not the tag must not have a value</li>
<li>canHaveType (Boolean): Whether or not the tag can have a type (e.g. "@param <strong>{String}</strong> name the description of name")</li>
<li>canHaveName (Boolean): Whether or not the tag can have a name (e.g. "@param {String} <strong>name</strong> the description of name")</li>
<li>isNamespace (Boolean): Whether or not the tag marks a doclet as representing a namespace. The "@module" tag, for instance, sets this to true.</li>
<li>onTagged (Function): A callback function executed when the tag is found. The function is passed two parameters: the doclet and the tag.</li>
</ul>
</li>
<li>lookUp(title) Used to lookup a tag. Returns either the tag or false if it's not defined</li>
<li>isNamespace(kind) Used to determine if a particular doclet type represents a namespace</li>
<li>normalise(title) Used to find the canonical name of a tag. The name passed in might be that name or a synonym</li>
</ul>
<p>A tag's onTagged callback can modify the contents of the doclet or tag.</p>
<figure>
<figcaption>Defining an onTagged callback</figcaption><pre class="prettyprint lang-js"><code>dictionary.defineTag('instance', {
onTagged: function(doclet, tag) {
doclet.scope = "instance";
}
});
</code></pre>
</figure>
<p>The defineTag method returns a Tag object, which has a "synonym" method that can be used to declare a synonym for the tag.</p>
<figure>
<figcaption>Defining a tag synonym</figcaption><pre class="prettyprint lang-js"><code>dictionary.defineTag('exception', { <options for exception tag> })
.synonym('throws');
</code></pre>
</figure>
<h3 id="node-visitors">Node Visitors</h3>
<p>At the lowest level, plugin authors can process each node in the parse tree by defining a node visitor that will visit each node, creating an opportunity to
do things like modify comments and trigger parser events for any arbitrary piece of code.</p>
<p>Plugins can define a node visitor by exporting a <code>nodeVisitor</code> object that contains a <code>visitNode</code> function, like so:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.nodeVisitor = {
visitNode: function(node, e, parser, currentSourceName) {
//do all sorts of crazy things here
}
}
</code></pre>
</figure>
<p>The function is called on each node with the following parameters:</p>
<ul>
<li>node: the node of the parse tree</li>
<li>e: the event. If the node is one that the parser handles, this will already be populated with the same things described in the <em>symbolFound</em> event above.
Otherwise, it will be an empty object on which to set various properties.</li>
<li>parser: the parser</li>
<li>currentSourceName: the name of the file being parsed</li>
</ul>
<h4 id="making-things-happen">Making things happen</h4>
<p>The primary reasons to implement a node visitor are to be able to document things that aren't normally documented (like function calls that create classes)
or to auto generate documentation for code that isn't documented. For instance, a plugin might look for calls to a "_trigger" method since it
knows that means an event is fired and then generate documentation for the event.</p>
<p>To make things happen, the <code>visitNode</code> function should modify properties of the event parameter. In general the goal is to construct a comment and
then get an event to fire. After the parser lets all of the node visitors have a look at the node, it looks to see if the event object has a <code>comment</code> property and an <code>event</code> property. If it has both, the event named in the event property is fired. The event is usually "symbolFound" or
"jsdocCommentFound", but theoretically, a plugin could define its own events and handle them.</p>
<h4 id="example-of-a-node-visitor">Example of a node visitor</h4>
<p>Below is an example of what a plugin for documenting jQuery UI widgets might do. jQuery UI uses a factory function call to create widget classes. The plugin
looks for that function call and creates a symbol with documentation. It also looks for any "this._trigger" function calls and automatically creates
documentation for the events that are triggered:</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>exports.nodeVisitor = {
visitNode: function(node, e, parser, currentSourceName) {
if (node.type === Token.OBJECTLIT && node.parent && node.parent.type === Token.CALL && isInWidgetFactory(node, 1)) {
var widgetName = node.parent.arguments.get(0).toSource();
e.id = 'astnode' + node.hashCode(); // the id of the object literal node
e.comment = String(node.parent.jsDoc||'');
e.lineno = node.parent.getLineno();
e.filename = currentSourceName;
e.astnode = node;
e.code = {
name: "" + widgetName.substring(1, widgetName.length() - 1),
type: "class",
node: node
};
e.event = "symbolFound";
e.finishers = [parser.addDocletRef];
addCommentTag(e, "param", "{Object=} options A set of configuration options");
}
else if(isTriggerCall(node)) {
var nameNode = node.arguments.get(0);
eventName = String((nameNode.type == Token.STRING) ? nameNode.value : nameNode.toSource()),
func = {},
comment = "@event\n",
eventKey = "";
if (node.enclosingFunction) {
func.id = 'astnode'+node.enclosingFunction.hashCode();
func.doclet = parser.refs[func.id];
}
if(func.doclet) {
func.doclet.addTag("fires", eventName);
if (func.doclet.memberof) {
eventKey = func.doclet.memberof + "#event:" + eventName;
comment += "@name " + func.doclet.memberof + "#" + eventName;
}
}
e.comment = comment;
e.lineno = node.getLineno();
e.filename = currentSourceName;
e.event = "jsdocCommentFound";
}
}
};
function isTriggerCall(node) {
if(node.type != Token.CALL) { return false; }
var target = node.getTarget(),
left = target && target.left && String(target.left.toSource()),
right = target && target.right && String(target.right.toSource());
return (left === "this" && right === "_trigger");
}
function isInWidgetFactory(node, depth) {
var parent = node.parent,
d = 0;
while(parent && (!depth || d < depth)) {
if (parent.type === Token.CALL) {
var target = parent.getTarget(),
left = target && target.left && String(target.left.toSource()),
right = target && target.right && String(target.right.toSource());
return ((left === "$" || left === "jQuery") && right === "widget");
} else {
parent = parent.parent;
d++;
}
}
return false;
}
</code></pre>
</figure>
<p>You'll notice a "finishers" property set. The finishers property should contain an array of functions to be called after the event is fired and
all the handlers have processed it. The parser provides an <code>addDocletRef</code> function that adds the doclet to the map (keyed off of the id property)
of doclets it knows about.</p>
<p>Lastly, the visitors are executed in the order the plugins are listed in the conf.json file. A plugin can stop later plugins from visiting a node by setting
a <code>stopPropagation</code> property on the event object (e.stopPropagation = true). A plugin can stop the event from firing setting a <code>preventDefault</code> property.</p>
<h3 id="throwing-errors">Throwing Errors</h3>
<p>If you wish your plugin to throw an error, do it using the <code>handle</code> function in the <code>jsdoc/util/error</code> module.</p>
<figure>
<figcaption>Example</figcaption><pre class="prettyprint lang-js"><code>require('jsdoc/util/error').handle( new Error('I do not like green eggs and ham!') );
</code></pre>
</figure>
<p>By default, this will throw the error, halting the execution of JSDoc. However, if the user enabled JSDoc's <code>--lenient</code> switch, JSDoc will simply
log the error to the console and continue.</p>
</article>
<footer>
<a class="license-badge" href="http://creativecommons.org/licenses/by-sa/3.0/">
<img alt="Creative Commons License" class="license-badge" src="images/cc-by-sa.svg" width="80" height="15" />
</a>
<br> Copyright © 2011-2014 the
<a href="https://github.com/jsdoc3/jsdoc3.github.com/contributors">contributors</a> to the JSDoc 3 documentation project.
<br> This website is <a href="https://github.com/jsdoc3/jsdoc3.github.com">open source</a> and is licensed under the <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">
Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.
</footer>
<script type="text/javascript">
prettyPrint();
</script>
</body>
</html>