/* eslint-disable import/no-commonjs */
/*global env: true */
const hasOwnProp = Object.prototype.hasOwnProperty;
// Work around an issue with hasOwnProperty in JSDoc's templateHelper.js.
//TODO Fix in JSDoc.
Object.prototype.hasOwnProperty = function (property) {
return property in this;
};
const template = require('jsdoc/lib/jsdoc/template');
const fs = require('jsdoc/lib/jsdoc/fs');
const path = require('jsdoc/lib/jsdoc/path');
const taffy = require('taffydb').taffy;
const handle = require('jsdoc/lib/jsdoc/util/error').handle;
const helper = require('jsdoc/lib/jsdoc/util/templateHelper');
const htmlsafe = helper.htmlsafe;
const linkto = helper.linkto;
const resolveAuthorLinks = helper.resolveAuthorLinks;
const outdir = env.opts.destination;
// Work around an issue with hasOwnProperty in JSDoc's templateHelper.js.
//TODO Fix in JSDoc.
Object.prototype.hasOwnProperty = hasOwnProp;
let view;
let data;
function find(spec) {
return helper.find(data, spec);
}
function tutoriallink(tutorial) {
return helper.toTutorial(tutorial, null, {
tag: 'em',
classname: 'disabled',
prefix: 'Tutorial: ',
});
}
function getAncestorLinks(doclet) {
return helper.getAncestorLinks(data, doclet);
}
function hashToLink(doclet, hash) {
if (!/^(#.+)/.test(hash)) {
return hash;
}
let url = helper.createLink(doclet);
url = url.replace(/(#.+|$)/, hash);
return '' + hash + '';
}
function needsSignature(doclet) {
let needsSig = false;
// function and class definitions always get a signature
if (doclet.kind === 'function' || doclet.kind === 'class') {
needsSig = true;
} else if (
doclet.kind === 'typedef' &&
doclet.type &&
doclet.type.names &&
doclet.type.names.length
) {
// typedefs that contain functions get a signature, too
for (let i = 0, l = doclet.type.names.length; i < l; i++) {
if (doclet.type.names[i].toLowerCase() === 'function') {
needsSig = true;
break;
}
}
}
return needsSig;
}
function addSignatureParams(f) {
const params = helper.getSignatureParams(f, 'optional');
f.signature = (f.signature || '') + '(' + params.join(', ') + ')';
}
function addSignatureReturns(f) {
const returnTypes = helper.getSignatureReturns(f);
f.signature = '' + (f.signature || '') + '';
if (returnTypes.length) {
f.signature +=
'' +
(returnTypes.length ? '{' + returnTypes.join('|') + '}' : '') +
'';
}
}
function addSignatureTypes(f) {
const types = helper.getSignatureTypes(f);
f.signature =
(f.signature || '') +
'' +
(types.length ? ' :' + types.join('|') : '') +
' ';
}
function shortenPaths(files, commonPrefix) {
// always use forward slashes
const regexp = new RegExp('\\\\', 'g');
Object.keys(files).forEach(function (file) {
files[file].shortened = files[file].resolved
.replace(commonPrefix, '')
.replace(regexp, '/');
});
return files;
}
function resolveSourcePath(filepath) {
return path.resolve(process.cwd(), filepath);
}
function getPathFromDoclet(doclet) {
if (!doclet.meta) {
return;
}
const filepath =
doclet.meta.path && doclet.meta.path !== 'null'
? doclet.meta.path + '/' + doclet.meta.filename.split(/[\/\\]/).pop()
: doclet.meta.filename;
return filepath;
}
function generate(title, docs, filename, resolveLinks) {
resolveLinks = resolveLinks === false ? false : true;
const docData = {
filename: filename,
title: title,
docs: docs,
packageInfo: (find({kind: 'package'}) || [])[0],
};
const outpath = path.join(outdir, filename);
let html = view.render('container.tmpl', docData);
if (resolveLinks) {
html = helper.resolveLinks(html); // turn {@link foo} into foo
}
fs.writeFileSync(outpath, html, 'utf8');
}
function generateSourceFiles(sourceFiles) {
Object.keys(sourceFiles).forEach(function (file) {
let source;
// links are keyed to the shortened path in each doclet's `meta.filename` property
const sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened);
helper.registerLink(sourceFiles[file].shortened, sourceOutfile);
try {
source = {
kind: 'source',
code: helper.htmlsafe(
fs.readFileSync(sourceFiles[file].resolved, 'utf8')
),
};
} catch (e) {
handle(e);
}
generate(
'Source: ' + sourceFiles[file].shortened,
[source],
sourceOutfile,
false
);
});
}
/**
* Look for classes or functions with the same name as modules (which indicates that the module
* exports only that class or function), then attach the classes or functions to the `module`
* property of the appropriate module doclets. The name of each class or function is also updated
* for display purposes. This function mutates the original arrays.
*
* @private
* @param {Array} doclets The array of classes and functions to
* check.
* @param {Array} modules The array of module doclets to search.
*/
function attachModuleSymbols(doclets, modules) {
const symbols = {};
// build a lookup table
doclets.forEach(function (symbol) {
symbols[symbol.longname] = symbol;
});
modules.forEach(function (module) {
if (symbols[module.longname]) {
module.module = symbols[module.longname];
module.module.name =
module.module.name.replace('module:', 'require("') + '")';
}
});
}
function getPrettyName(doclet) {
const fullname = doclet.longname.replace('module:', '');
if (doclet.isDefaultExport) {
return fullname.split('~')[0];
}
return fullname;
}
/**
* Create the navigation sidebar.
* @param {object} members The members that will be used to create the sidebar.
* @param {Array