src/Publisher/Builder/ClassDocBuilder.js
import IceCap from 'ice-cap';
import DocBuilder from './DocBuilder.js';
import {parseExample} from './util.js';
/**
* Class Output Builder class.
*/
export default class ClassDocBuilder extends DocBuilder {
/**
* execute building output.
* @param {function(html: string, filePath: string)} callback - is called each class.
*/
exec(callback) {
let ice = this._buildLayoutDoc();
ice.autoDrop = false;
let docs = this._find({kind: ['class']});
for (let doc of docs) {
let fileName = this._getOutputFileName(doc);
let baseUrl = this._getBaseUrl(fileName);
let title = this._getTitle(doc);
ice.load('content', this._buildClassDoc(doc), IceCap.MODE_WRITE);
ice.attr('baseUrl', 'href', baseUrl, IceCap.MODE_WRITE);
ice.text('title', title, IceCap.MODE_WRITE);
callback(ice.html, fileName);
}
}
/**
* build class output.
* @param {DocObject} doc - class doc object.
* @returns {IceCap} built output.
* @private
*/
_buildClassDoc(doc) {
let expressionExtends = this._buildExpressionExtendsHTML(doc);
let mixinClasses = this._buildMixinClassesHTML(doc);
let extendsChain = this._buildExtendsChainHTML(doc);
let directSubclass = this._buildDirectSubclassHTML(doc);
let indirectSubclass = this._buildIndirectSubclassHTML(doc);
let instanceDocs = this._find({kind: 'variable'}).filter((v)=> {
return v.type && v.type.types.includes(doc.longname);
});
let ice = new IceCap(this._readTemplate('class.html'));
// header
if (doc.export && doc.importPath && doc.importStyle) {
let link = this._buildFileDocLinkHTML(doc, doc.importPath);
ice.into('importPath', `import ${doc.importStyle} from '${link}'`, (code, ice)=>{
ice.load('importPathCode', code);
});
}
ice.text('access', doc.access);
ice.text('kind', doc.interface ? 'interface' : 'class');
ice.load('source', this._buildFileDocLinkHTML(doc, 'source'), 'append');
ice.text('since', doc.since, 'append');
ice.text('version', doc.version, 'append');
ice.load('variation', this._buildVariationHTML(doc), 'append');
ice.into('expressionExtends', expressionExtends, (expressionExtends, ice)=> ice.load('expressionExtendsCode', expressionExtends));
ice.load('mixinExtends', mixinClasses, 'append');
ice.load('extendsChain', extendsChain, 'append');
ice.load('directSubclass', directSubclass, 'append');
ice.load('indirectSubclass', indirectSubclass, 'append');
ice.load('implements', this._buildDocsLinkHTML(doc.implements, null, false, ', '), 'append');
ice.load('indirectImplements', this._buildDocsLinkHTML(doc._custom_indirect_implements, null, false, ', '), 'append');
ice.load('directImplemented', this._buildDocsLinkHTML(doc._custom_direct_implemented, null, false, ', '), 'append');
ice.load('indirectImplemented', this._buildDocsLinkHTML(doc._custom_indirect_implemented, null, false, ', '), 'append');
// self
ice.text('name', doc.name);
ice.load('description', doc.description);
ice.load('deprecated', this._buildDeprecatedHTML(doc));
ice.load('experimental', this._buildExperimentalHTML(doc));
ice.load('see', this._buildDocsLinkHTML(doc.see), 'append');
ice.load('todo', this._buildDocsLinkHTML(doc.todo), 'append');
ice.into('instanceDocs', instanceDocs, (instanceDocs, ice)=>{
ice.loop('instanceDoc', instanceDocs, (i, instanceDoc, ice)=>{
ice.load('instanceDoc', this._buildDocLinkHTML(instanceDoc.longname));
});
});
ice.into('exampleDocs', doc.examples, (examples, ice)=>{
ice.loop('exampleDoc', examples, (i, example, ice)=>{
let parsed = parseExample(example);
ice.text('exampleCode', parsed.body);
ice.text('exampleCaption', parsed.caption);
});
});
ice.into('tests', doc._custom_tests, (tests, ice)=>{
ice.loop('test', tests, (i, test, ice)=>{
let testDoc = this._find({longname: test})[0];
ice.load('test', this._buildFileDocLinkHTML(testDoc, testDoc.testFullDescription));
});
});
// summary
ice.load('staticMemberSummary', this._buildSummaryHTML(doc, 'member', 'Members', true));
ice.load('staticMethodSummary', this._buildSummaryHTML(doc, 'method', 'Methods', true));
ice.load('constructorSummary', this._buildSummaryHTML(doc, 'constructor', 'Constructor', false));
ice.load('memberSummary', this._buildSummaryHTML(doc, 'member', 'Members', false));
ice.load('methodSummary', this._buildSummaryHTML(doc, 'method', 'Methods', false));
ice.load('inheritedSummary', this._buildInheritedSummaryHTML(doc), 'append');
// detail
ice.load('staticMemberDetails', this._buildDetailHTML(doc, 'member', 'Members', true));
ice.load('staticMethodDetails', this._buildDetailHTML(doc, 'method', 'Methods', true));
ice.load('constructorDetails', this._buildDetailHTML(doc, 'constructor', 'Constructors', false));
ice.load('memberDetails', this._buildDetailHTML(doc, 'member', 'Members', false));
ice.load('methodDetails', this._buildDetailHTML(doc, 'method', 'Methods', false));
return ice;
}
/**
* build variation of doc.
* @param {DocObject} doc - target doc object.
* @returns {string} variation links html.
* @private
* @experimental
*/
_buildVariationHTML(doc) {
var variationDocs = this._find({memberof: doc.memberof, name: doc.name});
var html = [];
for (var variationDoc of variationDocs) {
if (variationDoc.variation === doc.variation) continue;
html.push(this._buildDocLinkHTML(variationDoc.longname, `(${variationDoc.variation || 1})`));
}
return html.join(', ');
}
/**
* build mixin extends html.
* @param {DocObject} doc - target class doc.
* @return {string} mixin extends html.
*/
_buildMixinClassesHTML(doc) {
if (!doc.extends) return '';
if (doc.extends.length <= 1) return '';
let links = [];
for (var longname of doc.extends) {
links.push(this._buildDocLinkHTML(longname));
}
return `<div>${links.join(', ')}</div>`;
}
/**
* build expression extends html.
* @param {DocObject} doc - target class doc.
* @return {string} expression extends html.
*/
_buildExpressionExtendsHTML(doc) {
if (!doc.expressionExtends) return '';
let html = doc.expressionExtends.replace(/[A-Z_$][a-zA-Z0-9_$]*/g, (v)=>{
return this._buildDocLinkHTML(v);
});
return `class ${doc.name} extends ${html}`;
}
/**
* build class ancestor extends chain.
* @param {DocObject} doc - target class doc.
* @returns {string} extends chain links html.
* @private
*/
_buildExtendsChainHTML(doc) {
if (!doc._custom_extends_chains) return '';
if (doc.extends.length > 1) return '';
var links = [];
for (var longname of doc._custom_extends_chains) {
links.push(this._buildDocLinkHTML(longname));
}
links.push(doc.name);
return `<div>${links.join(' → ')}</div>`;
}
/**
* build in-direct subclass list.
* @param {DocObject} doc - target class doc.
* @returns {string} html of in-direct subclass links.
* @private
*/
_buildIndirectSubclassHTML(doc) {
if (!doc._custom_indirect_subclasses) return '';
var links = [];
for (var longname of doc._custom_indirect_subclasses) {
links.push(this._buildDocLinkHTML(longname));
}
return `<div>${links.join(', ')}</div>`;
}
/**
* build direct subclass list.
* @param {DocObject} doc - target class doc.
* @returns {string} html of direct subclass links.
* @private
*/
_buildDirectSubclassHTML(doc) {
if (!doc._custom_direct_subclasses) return '';
var links = [];
for (var longname of doc._custom_direct_subclasses) {
links.push(this._buildDocLinkHTML(longname));
}
return `<div>${links.join(', ')}</div>`;
}
/**
* build inherited method/member summary.
* @param {DocObject} doc - target class doc.
* @returns {string} html of inherited method/member from ancestor classes.
* @private
*/
_buildInheritedSummaryHTML(doc) {
if (['class', 'interface'].indexOf(doc.kind) === -1) return '';
let longnames = [
...doc._custom_extends_chains || []
//...doc.implements || [],
//...doc._custom_indirect_implements || [],
];
let html = [];
for (let longname of longnames) {
let superDoc = this._find({longname})[0];
if (!superDoc) continue;
let targetDocs = this._find({memberof: longname, kind: ['member', 'method', 'get', 'set']});
targetDocs.sort((a, b)=>{
if (a.static !== b.static) return -(a.static - b.static);
let order = {get: 0, set: 0, member: 1, method: 2};
if (order[a.kind] !== order[b.kind]) {
return order[a.kind] - order[b.kind];
}
order = {public: 0, protected: 1, private: 2};
if (a.access != b.access) return order[a.access] - order[b.access];
if (a.name !== b.name) return a.name < b.name ? -1 : 1;
order = {get: 0, set: 1, member: 2};
return order[a.kind] - order[b.kind];
});
let title = `<span class="toggle closed"></span> From ${superDoc.kind} ${this._buildDocLinkHTML(longname, superDoc.name)}`;
let result = this._buildSummaryDoc(targetDocs, '----------', false, superDoc.kind);
if (result) {
result.load('title', title, IceCap.MODE_WRITE);
html.push(result.html);
}
}
return html.join('\n');
}
}