Greatly simplify and document the usage of JSDoc

This commit simplifies the exports.js plugin so it only relies
on the stability notes to generate the documentation, which
completely decouples it from the exportable API.

As a rule of thumb, whenever something has an 'api' annotation,
it should also have a 'stability' annotation. A more verbose
documentation of ol3 specific annotation usage is available in
the new 'apidoc/readme.md' file.

This commit also modifies all source files to implement these
usage suggestions.
This commit is contained in:
Andreas Hocevar
2014-04-13 10:43:05 +02:00
committed by Tim Schaub
parent aaf6101d0f
commit c17ac0cae3
84 changed files with 403 additions and 195 deletions

51
apidoc/plugins/api.js Normal file
View File

@@ -0,0 +1,51 @@
/*
* Based on @stability annotations, and assuming that items with no @stability
* annotation should not be documented, this plugin removes undocumented symbols
* from the documentation. Undocumented classes with documented members get a
* 'hideConstructur' property, which is read by the template so it can hide the
* constructor.
*/
function hasApiMembers(doclet) {
return doclet.longname.split('#')[0] == this.longname;
}
var api = [];
exports.handlers = {
newDoclet: function(e) {
var doclet = e.doclet;
// Keep track of api items - needed in parseComplete to determine classes
// with api members.
if (doclet.stability) {
api.push(doclet);
}
// Mark explicity defined namespaces - needed in parseComplete to keep
// namespaces that we need as containers for api items.
if (/.*\.jsdoc$/.test(doclet.meta.filename) && doclet.kind == 'namespace') {
doclet.namespace_ = true;
}
},
parseComplete: function(e) {
var doclets = e.doclets;
for (var i = doclets.length - 1; i >= 0; --i) {
var doclet = doclets[i];
// Always document namespaces and items with stability annotation
if (doclet.stability || doclet.namespace_) {
continue;
}
if (doclet.kind == 'class' && api.some(hasApiMembers, doclet)) {
// Mark undocumented classes with documented members as unexported.
// This is used in ../template/tmpl/container.tmpl to hide the
// constructor from the docs.
doclet.hideConstructor = true;
} else {
// Remove all other undocumented symbols
doclets.splice(i, 1);
}
}
}
};

View File

@@ -1,109 +0,0 @@
/*
* This plugin removes unexported symbols from the documentation.
* Unexported modules linked from @param or @fires will be marked unexported,
* and the documentation will not contain the constructor. Everything else is
* marked undocumented, which will remove it from the docs.
*/
var api = [];
var unexported = [];
var observablesByClass = {};
function collectExports(source) {
var symbols = JSON.parse(source).symbols;
for (var i = 0, ii = symbols.length; i < ii; ++i) {
api.push(symbols[i].name);
}
}
var encoding = env.conf.encoding || 'utf8';
var fs = require('jsdoc/fs');
collectExports(fs.readFileSync('build/symbols.json', encoding));
exports.handlers = {
newDoclet: function(e) {
var i, ii, j, jj;
if (e.doclet.meta.filename == "olx.js" && e.doclet.longname != 'olx') {
api.push(e.doclet.longname);
}
if (e.doclet.longname.indexOf('oli.') === 0) {
unexported.push(e.doclet.longname.replace(/^oli\./, 'ol.'));
}
if (api.indexOf(e.doclet.longname) > -1) {
var names, name;
var params = e.doclet.params;
if (params) {
for (i = 0, ii = params.length; i < ii; ++i) {
names = params[i].type.names;
if (names) {
for (j = 0, jj=names.length; j < jj; ++j) {
name = names[j];
if (unexported.indexOf(name) === -1) {
unexported.push(name);
}
}
}
}
}
var links = e.doclet.comment.match(/\{@link ([^\}]*)\}/g);
if (links) {
for (i=0, ii=links.length; i < ii; ++i) {
var link = links[i].match(/\{@link (.*)\}/)[1];
if (unexported.indexOf(link) === -1) {
unexported.push(link);
}
}
}
}
if (e.doclet.observables) {
var observables = observablesByClass[e.doclet.longname] = [];
for (i = e.doclet.observables.length - 1; i >= 0; --i) {
observables.push(e.doclet.observables[i].name);
}
}
},
parseComplete: function(e) {
for (var j = e.doclets.length - 1; j >= 0; --j) {
var doclet = e.doclets[j];
if (doclet.meta.filename == 'olx.js' && doclet.kind == 'typedef') {
for (var i = e.doclets.length - 1; i >= 0; --i) {
var propertyDoclet = e.doclets[i];
if (propertyDoclet.memberof == doclet.longname) {
if (!doclet.properties) {
doclet.properties = [];
}
doclet.properties.unshift(propertyDoclet);
e.doclets.splice(i, 1);
}
}
}
if (doclet.kind == 'namespace' || doclet.kind == 'event' || doclet.fires) {
continue;
}
var fqn = doclet.longname;
if (fqn) {
var getterOrSetter = fqn.match(/([^#]*)#[gs]et(.*)/);
if (getterOrSetter) {
var observables = observablesByClass[getterOrSetter[1]];
if (observables && observables.indexOf(getterOrSetter[2].toLowerCase()) > -1) {
// Always document getters/setters of observables
continue;
}
}
if (doclet.memberof && doclet.memberof.indexOf('oli.') === 0 &&
unexported.indexOf(doclet.memberof) > -1) {
// Always document members of referenced oli interfaces
continue;
}
doclet.unexported = (api.indexOf(fqn) === -1 && unexported.indexOf(fqn) !== -1);
if (api.indexOf(fqn) === -1 && unexported.indexOf(fqn) === -1) {
e.doclets.splice(j, 1);
}
}
}
}
};

View File

@@ -10,13 +10,16 @@ exports.defineTags = function(dictionary) {
}
});
var augmentsTag = dictionary.lookUp('augments');
dictionary.defineTag('implements', {
mustHaveValue: true,
onTagged: function(doclet, tag) {
tag.value = tag.value.match(/^\{?([^\}]*)\}?$/)[1];
augmentsTag.onTagged.apply(this, arguments);
if (!doclet.implements) {
doclet.implements = [];
}
doclet.implements.push(tag.value.match(/^{(.*)}$/)[1]);
doclet.implements.push(tag.value);
}
});

View File

@@ -0,0 +1,23 @@
/*
* Converts olx.js @type annotations into properties of the previous @typedef.
*/
var olxTypedef = null;
exports.handlers = {
newDoclet: function(e) {
var doclet = e.doclet;
if (doclet.meta.filename == 'olx.js') {
if (doclet.kind == 'typedef') {
olxTypedef = doclet;
doclet.properties = [];
} else if (olxTypedef && doclet.memberof == olxTypedef.longname) {
olxTypedef.properties.push(doclet);
} else {
olxTypedef = null;
}
}
}
};