UNPKG

leafdoc

Version:

A lightweight NaturalDocs-like LeafletJS-style documentation generator

992 lines (773 loc) 30.3 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var fs = _interopDefault(require('fs')); var path = _interopDefault(require('path')); var Handlebars = _interopDefault(require('handlebars')); var marked = _interopDefault(require('marked')); require('xregexp'); var unicodeRegExpIDStart = _interopDefault(require('unicode-7.0.0/properties/ID_Start/regex')); var unicodeRegExpIDContinue = _interopDefault(require('unicode-7.0.0/properties/ID_Continue/regex')); var __trivial_mjs = require('./trivial.mjs'); var __multilang_mjs = require('./multilang.mjs'); // Minor wrapper over Handlebars var templateDir = __dirname + '/../templates/basic'; var templates = Object.create(null); function setTemplateDir(newDir) { templates = Object.create(null); templateDir = newDir; } function getTemplate(templateName) { if (!templates[templateName]) { templates[templateName] = Handlebars.compile(fs.readFileSync(path.join(templateDir, templateName + '.hbs')).toString()); } return templates[templateName]; } var _AKAs = {}; function setAKAs(akas) { // console.log('Template thing updating AKAs'); _AKAs = akas; } // export Handlebars as engine; /// TODO: Catch all code blocks and check if the contents is a known class, namespace or AKA // marked.setOptions({ // highlight: function (code) { // return require('highlight').highlight(code).value; // } // }); /// TODO: Make the big markdown functions also automatically link to stuff. // Helper to replace AKAs in markdown links. function replaceAKAs(str) { str = str.trim(); for (var i in _AKAs) { // [...](#a) → [...](#b) str = str.replace('(#' + i + ')', '(#' + _AKAs[i] + ')'); // `a` → [`a`](#b) str = str.replace('`' + i + '`', '[`' + i + '`](#' + _AKAs[i] + ')'); } return str; } Handlebars.registerHelper('markdown', function (str) { if (!str) { return; } if (str instanceof Array) { str = str.join('\n').trim(); // str = str.join(' '); } return marked(replaceAKAs(str)) .trim() .replace('<p>', '') .replace('</p>', ''); }); Handlebars.registerHelper('rawmarkdown', function (str) { if (!str) { return; } if (str instanceof Array) { str = str.join('\n'); } return marked(replaceAKAs(str)); }); // Automatically link to AKAs, mostly used on method/function/param/option data types. Handlebars.registerHelper('type', function (str) { if (!str) { return; } if (str in _AKAs) { var id = _AKAs[str]; return '<a href=\'#' + id + '\'>' + str + '</a>'; } else { // Should be a built-in type return str; } }); // JSON stringify the stuff. Handlebars.registerHelper('json', function (obj) { console.log(obj); return JSON.stringify(obj, undefined, 1); }); // Regexps (maybe) shared between files. var xRegExp = XRegExp; // One or more lines starting with whitespace and two or more forward slashes, // or // whitespace-slash-asterisk whatever asterisk-slash. var commentBlock = xRegExp('^(?<multiline> ( (?!\\n) \\s*\\/{2,}\\s*.*\\n )+ ) \ | \ (\\s*\\/\\* (?<block> [\\s\\S]*?) \\*\\/) \ ', 'gmnx'); var leafdocFile = xRegExp('^(?<block> [\\s\\S]+ )$', 'gmnx'); // Inside each line of a comment /* */ block, skips the leading spaces / asterisk (if any) var leadingBlock = xRegExp('^ ( \\s* \\* \\s? )? (?<line> .* ) $', 'nx'); // Inside each line of a comment // block, skips the leading // var leadingLine = xRegExp('^ \\s*/{0,4}\\s{0,1} (?<line> .* ) $', 'nx'); // Inside .leafdoc files, match any line without skipping anything var anyLine = xRegExp('^ (?<line> .* ) $', 'nx'); // Parses a 🍂 directive, init'd at redoLeafDirective() var leafDirective = redoLeafDirective('🍂'); // Re-builds the 🍂 directive based on a different leading character function redoLeafDirective(char) { leafDirective = xRegExp(' \\s* ' + char + ' (?<directive> \\S+ ) (\\s+ (?<content> [^;\\n]+ )){0,1} ', 'gnx'); return leafDirective; } // Parses an identifier, allowing only unicode ID_Start and ID_Continue characters // An identifier allows dots in it, to allow for namespacing identifiers. // TODO: An identifier shall allow an underscore at the beginning, as JS does. var identifier = xRegExp.build('^({{ID_Start}} ( {{ID_Continue}} | \\. | : )*)$', { ID_Start: unicodeRegExpIDStart, // eslint-disable-line camelcase ID_Continue: unicodeRegExpIDContinue // eslint-disable-line camelcase }, 'nx'); // Parses a function name, its return type, and its parameters // Funny thing about functions is that not all printable characters are allowed. Thus, // use unicode ID_Start and ID_Continue character sets via 'identifier' sub-regexp. // eslint-disable-next-line no-useless-escape var functionDefinition = xRegExp.build('^ (?<name> {{identifier}} ) (?<required> (\\?{0,1}) ) \\s* (?<params> \\( .* \\) ){0,1} \\s* ( \\: \\s* (?<type> .+? ) )? ( = \\s* (?<default> .+ ) \\s* ){0,1} \$', { identifier: identifier }, 'nx'); // var functionParam = xRegExp.build('^ \\s* (?<name> {{identifier}}) \\s* ( \\: \\s* (?<type> .+ ) \\s* ) $', {identifier: identifier}, 'nx'); // var functionParam = xRegExp.build('\\s* (?<name> ( {{identifier}} | … ) \\?{0,1} ) \\s* ( \\: \\s* (?<type> [^,]+ ) \\s* ) (, | \\)) ', {identifier: identifier}, 'gnx'); var functionParam = xRegExp.build('\\s* (?<name> ( {{identifier}} | … ) \\?{0,1} ) \\s* ( \\: \\s* (?<type> [^,]+ ) \\s* )? (, | \\)) ', {identifier: identifier}, 'gnx'); // Parses a miniclass name and its real class between parentheses. var miniclassDefinition = xRegExp('^ (?<miniclass> .+ ) \\s* \\( (?<realclass> .+ ) \\) $', 'nx'); // 🍂class Leafdoc; Represents the Leafdoc parser var Leafdoc = function Leafdoc(options) { var this$1 = this; this._namespaces = {}; this._knownDocumentables = [ 'example', 'constructor', 'destructor', 'factory', 'option', 'event', 'method', 'function', 'property' ]; this._documentableLabels = { 'example': 'Usage example', 'factory': 'Creation', 'constructor': 'Constructor', 'destructor': 'Destructor', 'option': 'Options', 'event': 'Events', 'method': 'Methods', 'function': 'Functions', 'property': 'Properties' }; this._inheritableDocumentables = [ 'method', 'function', 'event', 'option', 'property' ]; // Holds a list of miniclasses, with the miniclass as key and the real // class as value. // Maybe a better name would be "subnamespaces" or whatever. this._miniclasses = {}; this._AKAs = {}; // 🍂section // 🍂aka Leafdoc options if (options) { // 🍂option templateDir: String = 'templates/basic' // Defines which subdirectory (relative to the directory the curent JS // script is running) holds the handlebars template files for building up the HTML. if (options.templateDir) { setTemplateDir(options.templateDir); } // 🍂option showInheritancesWhenEmpty: Boolean = false // When `true`, child classes/namespaces will display documentables from ancestors, even if the child class doesn't have any of such documentables. // e.g. display inherited events even if the child doesn't define any new events. this.showInheritancesWhenEmpty = options.showInheritancesWhenEmpty || false; // 🍂option leadingCharacter: String = '🍂' // Overrides the Leaf symbol as the leading character for documentation lines. // See also [`setLeadingCharacter`](#leafdoc-setleadingcharacter). if (options.leadingCharacter) { this.setLeadingCharacter(options.leadingCharacter); } // 🍂option customDocumentables: Map = {} // A key-value map. Each pair will be passed to [`registerDocumentable`](#leafdoc-registerdocumentable). if (options.customDocumentables) { for (var i in options.customDocumentables) { this$1.registerDocumentable(i, options.customDocumentables[i]); } } // 🍂option verbose: Boolean = false // Set to `true` to display more information as files are being read. if (options.verbose) { this._verbose = options.verbose; } } }; // 🍂method registerDocumentable (name: String, label?: String, inheritable?: Boolean): this // Registers a new documentable type, beyond the preset ones (function, // property, etc). New documentable should also not be an already used // keyword (class, namespace, inherits, etc). // When registering new documentables, make sure that there is an appropriate // template file for it. // Set `label` to the text for the sections in the generated docs. // `inheritable` parameter determines documentable can be inherited via inherits keyword in a subclass. Leafdoc.prototype.registerDocumentable = function (name, label, inheritable) { this._knownDocumentables.push(name, label); if (label) { this._documentableLabels[name] = label; } if (inheritable) { this._inheritableDocumentables.push(name); } return this; }; // 🍂method getTemplateEngine(): Handlebars // Returns handlebars template engine used to render templates. // You can use it for override helpers or register new. Leafdoc.prototype.getTemplateEngine = function () { return template.engine; }; // 🍂method setLeadingCharacter(char: String): this // In the rare case you don't want to use &#x1f342; as the leading character for // leaf directives, run this function with the desired character, e.g. // `setLeadingCharacter('@');` // The new leading character will apply only to files/dirs/strings parsed from // that moment on, so it's a good idea to call this before anything else. Leafdoc.prototype.setLeadingCharacter = function (char) { // console.log('Setting leading character to', char); redoLeafDirective(char); }; // 🍂method addDir (dirname: String, extensions?: String[]): this // Recursively scans a directory, and parses any files that match the // given `extensions` (by default `.js` and `.leafdoc`, mind the dots). // Files with a `.leafdoc` extension will be treated as leafdoc-only // instead of source. Leafdoc.prototype.addDir = function (dirname, extensions) { var this$1 = this; if (!extensions) { extensions = ['.js', '.leafdoc']; } var filenames = fs.readdirSync(dirname); for (var i in filenames) { var filename = path.join(dirname, filenames[i]); // Check if dir, recurse if so var stats = fs.lstatSync(filename); if (stats.isDirectory()) { this$1.addDir(filename, extensions); } else if (extensions.indexOf(path.extname(filename)) !== -1) { if (this$1._verbose) { console.log('Leafdoc processing file: ', filename); } this$1.addFile(filename, path.extname(filename) !== '.leafdoc'); } } return this; }; // 🍂method addFile(filename: String, isSource?: Boolean): this // Parses the given file using [`addBuffer`](#leafdoc-addbuffer). Leafdoc.prototype.addFile = function (filename, isSource) { return this.addBuffer(fs.readFileSync(filename), isSource, filename); }; // 🍂method addBuffer(buf: Buffer, isSource?: Boolean, filename?: String): this // Parses the given buffer using [`addStr`](#leafdoc-addstr) underneath. Set `isSource` to `true` to parse Leafdoc directives inside comment blocks. Otherwise, the whole file is interpreted as Leafdoc directives. Leafdoc.prototype.addBuffer = function (buf, isSource, filename) { return this.addStr(buf.toString(), isSource, filename); }; // 🍂method addStr(str: String, isSource?: Boolean, filename?: String): this // Parses the given string for Leafdoc comments. The string is assumed to // be source code with comments (with the comment separators defiend by the filename extension), // unless `isSource` is explicitly set to `false`. Leafdoc.prototype.addStr = function (str, isSource, filename) { var this$1 = this; // Leaflet files use DOS line feeds, which screw up things. str = str.replace(/\r\n?/g, '\n'); var ns = '__default'; // namespace (or class) var sec = '__default'; // section var dt = ''; // Type of documentable var dc = ''; // Name of documentable var alt = 0; // Will auto-increment for documentables with 🍂alternative var altAppliesTo = null; // Ensures 'alt' resets when documentable changes // Scope of the current line (parser state): ns, sec or dc. // (namespace, section, documentable) var scope = ''; var namespaces = this._namespaces; var currentNamespace, currentSection, currentDocumentable; // Temporal placeholder - section comments and AKAs are dangling until // the documentable type is known var sectionComments = []; var sectionAKA = []; var sectionIsUninheritable = false; var parser = isSource ? multilangParser : trivialParser; var parsedBlocks = parser(str, filename); // 1: Fetch comment blocks (from the parser). For each block... for (var i = 0, l = parsedBlocks.length; i < l; i++) { var commentBlock$$1 = parsedBlocks[i]; var blockIsEmpty = true; // Edge case: some comment blocks in markdown might choke up if (!commentBlock$$1) { break; } // 2: Split into lines var lines = commentBlock$$1.split('\n'); // 3: Split lines into directives (separated by ";") var directives = []; for (var i$1 in lines) { var line = lines[i$1]; // var match = regex.exec(line); // Skips extra comment characters // var lineStr = match[1]; // Might happen in some binary files // console.log(line); // break; // } var lineIsValid = false; var parsedCharacters = 0; // console.log('Line: ', i, line); // var match = regex.exec(line); var match = (void 0); // In "param foo, bar", directive is "param" and content is "foo, bar" while (match = leafDirective.exec(line)) { if (match[2]) { match[2] = match[2].trim(); } directives.push([match[1], match[2]]); // [directive, content] // console.log('directive match: ', match); blockIsEmpty = false; lineIsValid = true; parsedCharacters = match.index + match[0].length; } if (lineIsValid) { // console.log('After having matched a line:', match); var trailing = line.substr(parsedCharacters + 1).trim(); // console.log('After having matched a line:', trailing); if (trailing) { directives.push(['comment', trailing]); } } if (!lineIsValid && !blockIsEmpty) { // implicit 🍂comment directive. directives.push(['comment', line]); } } // console.log('directives', directives); for (var i$2 in directives) { var directive = directives[i$2][0], content = directives[i$2][1]; // 4: Parse 🍂 directives // console.log(directive, '-', content); if (directive === 'class' || directive === 'namespace') { ns = content.trim(); sec = '__default'; scope = 'ns'; } else if (directive === 'miniclass') { var split = miniclassDefinition.exec(content); if (!split) { console.error('Invalid miniclass definition: ', content); console.log(split); } else { ns = split[1].trim(); var miniparent = split[2]; sec = '__default'; scope = 'ns'; this$1._miniclasses[ns] = miniparent; } } else if (directive === 'section') { sec = content || '__default'; scope = 'sec'; } else if (this$1._knownDocumentables.indexOf(directive) !== -1) { scope = 'dc'; dt = directive; dc = ''; // The name of the documentable will be set later } // console.log(scope, '-', directive, '-', content); if (scope === 'ns') { if (!namespaces.hasOwnProperty(ns)) { // console.log('Defining class/namespace ', ns); namespaces[ns] = { name: ns, aka: [], comments: [], supersections: {}, inherits: [] }; } if (directive === 'aka') { namespaces[ns].aka.push(content); } if (directive === 'comment') { namespaces[ns].comments.push(content); } if (directive === 'inherits') { namespaces[ns].inherits.push(content); } currentNamespace = namespaces[ns]; } if (scope === 'sec') { if (directive === 'comment') { sectionComments.push(content); } if (directive === 'aka') { sectionAKA.push(content); } if (directive === 'uninheritable') { sectionIsUninheritable = true; } } if (scope === 'dc') { if (!currentNamespace) { console.error('Error: No class/namespace set when parsing through:'); console.error(commentBlock$$1); } if (!currentNamespace.supersections.hasOwnProperty(dt)) { currentNamespace.supersections[dt] = { name: dt, aka: [], comments: [], sections: {} }; } if (!currentNamespace.supersections[dt].sections.hasOwnProperty(sec)) { currentNamespace.supersections[dt].sections[sec] = { name: sec, aka: sectionAKA, comments: sectionComments, uninheritable: sectionIsUninheritable, documentables: {}, type: dt }; sectionAKA = []; sectionComments = []; sectionIsUninheritable = false; } currentSection = currentNamespace.supersections[dt].sections[sec]; // console.log(currentSection); // console.log(directive); if (this$1._knownDocumentables.indexOf(directive) !== -1) { // Documentables might have more than their name as content. // All documentables will follow the syntax for functions, // with optional parameters, optional required flag, optional type, and optional default value. // console.log(content, ', ', alt); var name = (void 0), paramString = (void 0), params = {}, type = null, defaultValue = null, optional = false; if (content) { var split$1 = functionDefinition.exec(content); if (!split$1) { console.error('Invalid ' + directive + ' definition: ', content); } else { optional = split$1[2] == '?'; var assign; (assign = split$1, name = assign[1], paramString = assign[3], type = assign[4], defaultValue = assign[5]); // name = split[1]; // paramString = split[3]; // type = split[4]; // defaultValue = split[5]; if (paramString) { var match$1 = (void 0); while (match$1 = functionParam.exec(paramString)) { params[ match$1[1] ] = {name: match$1[1], type: match$1[2]}; } // console.log("\"" + paramString + "\"\n\t", params); } } } else { name = '__default'; } // Handle alternatives - just modify the name if 'alt' and 'altAppliesTo' match if (altAppliesTo === name) { dc = name + '-alternative-' + alt; } else { dc = name; alt = 0; altAppliesTo = null; } if (!currentSection.documentables.hasOwnProperty(dc)) { currentSection.documentables[dc] = { name: name, aka: [], comments: [], params: params, // Only for functions/methods/factories type: type ? type.trim() : null, optional: optional, defaultValue: defaultValue || null // Only for options, properties }; } currentDocumentable = currentSection.documentables[dc]; } else if (directive === 'alternative') { alt++; // Alternative applies to current documentable name; if name // doesn't match, alternative has no effect. altAppliesTo = currentDocumentable.name; } else if (directive === 'param') { // Params are param name, type. /// TODO: Think about default values, or param explanation. var split = content.split(':'); var paramName = split[0].trim(); var paramType = split[1] ? split[1].trim() : ''; currentDocumentable.params[paramName] = {name: paramName, type: paramType}; } else if (directive === 'aka') { currentDocumentable.aka.push(content); } else if (directive === 'comment') { // console.log('Doing stuff with a method comments: ', content); currentDocumentable.comments.push(content); } } } } // console.log(this._namespaces.Leafdoc.__default[0]); // console.log(this._namespaces.Marker.__default[0]); // console.log(this._namespaces); // console.log(this._namespaces.Marker.supersections.method.sections.__default); // console.log('namespaces after addStr', this._namespaces); return this; }; /* * 🍂method outputStr: String * Outputs the documentation to a string. * Use only after all the needed files have been parsed. */ Leafdoc.prototype.outputStr = function () { var this$1 = this; this._resolveAKAs(); var out = ''; for (var ns in this$1._namespaces) { out += this$1._stringifyNamespace(this$1._namespaces[ns]); } // console.log('miniclasses: ', this._miniclasses); return (getTemplate('html'))({body: out}); }; /* * 🍂method outputJSON: String * Outputs the internal documentation tree to a JSON blob, without any formatting. * Use only after all the needed files have been parsed. */ Leafdoc.prototype.outputJSON = function () { this._resolveAKAs(); return JSON.stringify(this._namespaces, undefined, 1); }; Leafdoc.prototype._stringifyNamespace = function (namespace, isMini) { var this$1 = this; if (!isMini && this._miniclasses.hasOwnProperty(namespace.name)) { return ''; } var out = ''; var ancestors = this._flattenInheritances(namespace.name); /// Ensure explicit order of the supersections (known types of documentable: /// example, factory, options, events, methods, properties for (var i in this$1._knownDocumentables) { var s = this$1._knownDocumentables[i]; var supersectionHasSomething = namespace.supersections.hasOwnProperty(s); if (s !== 'example' && this$1.showInheritancesWhenEmpty && !supersectionHasSomething) { // console.log('checking for empty section with inherited stuff, ', namespace.name, s, ancestors); for (var i in ancestors) { var ancestor = ancestors[i]; // console.log(ancestor, this._namespaces[ancestor].supersections.hasOwnProperty(s)); if (this$1._namespaces[ancestor].supersections.hasOwnProperty(s)) { for (var sec in this$1._namespaces[ancestor].supersections[s].sections) { if (!this$1._namespaces[ancestor].supersections[s].sections[sec].uninheritable) { supersectionHasSomething = true; } } // console.log(this._namespaces[ancestor].supersections[s]); if (supersectionHasSomething) { namespace.supersections[s] = { name: this$1._namespaces[ancestor].supersections[s].name, sections: {} }; } } } } if (supersectionHasSomething) { out += this$1._stringifySupersection(namespace.supersections[s], ancestors, namespace.name, isMini); } } if (!isMini) { for (var i in this$1._miniclasses) { if (this$1._miniclasses[i] === namespace.name) { out += this$1._stringifyNamespace(this$1._namespaces[i], true); // console.log('out is now', out); } } } // console.log(namespace); return (getTemplate('namespace'))({ name: isMini ? undefined : namespace.name, id: namespace.id, comments: namespace.comments, supersections: out }); }; Leafdoc.prototype._stringifySupersection = function (supersection, ancestors, namespacename, isMini) { var this$1 = this; var sections = ''; var inheritances = ''; // The "__default" section should show above any named sections if ('__default' in supersection.sections) { var oldSections = supersection.sections; supersection.sections = {__default: oldSections.__default}; for (var s in oldSections) { if (s !== '__default') { supersection.sections[s] = oldSections[s]; } } } for (var s in supersection.sections) { sections += this$1._stringifySection(supersection.sections[s], supersection.name, false); } var name = supersection.name; var label = this._documentableLabels[name] || name; // Calculate inherited documentables. // In the order of the ancestors, check if each documentable has already been // selected for output, skip it if so. Group rest into inherited sections. if (this._inheritableDocumentables.indexOf(name) !== -1) { if (ancestors.length) { // inheritances += 'Inherits stuff from: ' + inheritances.join(','); var inheritedSections = []; // Build a list of the documentables which have been already outputted var skip = []; for (var s in supersection.sections) { var section = supersection.sections[s]; for (var d in section.documentables) { skip.push(d); } } // console.log('Will skip: ', skip); for (var i in ancestors) { var parent = ancestors[i]; // console.log('Processing ancestor ', parent); if (this$1._namespaces[parent].supersections.hasOwnProperty(name)) { var parentSupersection = this$1._namespaces[parent].supersections[name]; for (var s in parentSupersection.sections) { var parentSection = parentSupersection.sections[s]; if (parentSection && !parentSection.uninheritable) { var inheritedSection = { name: parentSection.name === '__default' ? label : parentSection.name, parent: parent, documentables: [], id: parentSection.id }; for (var d in parentSection.documentables) { // console.log('Checking if should show inherited ', d); if (skip.indexOf(d) === -1) { skip.push(d); inheritedSection.documentables.push(parentSection.documentables[d]); } } // console.log(inheritedSection.documentables); if (inheritedSection.documentables.length) { inheritedSections.push(inheritedSection); } else { // console.log('Everything from inherited section has been overwritten', parent, name); } } } } } // Inherited sections have been calculated, template them away. for (var i in inheritedSections) { var inheritedSection = inheritedSections[i]; inheritances += (getTemplate('inherited'))({ name: inheritedSection.name, ancestor: inheritedSection.parent, inherited: this$1._stringifySection(inheritedSection, supersection.name, namespacename), id: inheritedSection.id }); } } } return (getTemplate('supersection'))({ name: isMini ? namespacename : label, id: supersection.id, comments: supersection.comments, sections: sections, inheritances: inheritances }); }; Leafdoc.prototype._stringifySection = function (section, documentableType, inheritingNamespace) { var this$1 = this; var name = (section.name === '__default' || inheritingNamespace) ? '' : section.name; // if (name) console.log('Named section:', section); // console.log('Section:', section); // If inheriting, recreate the documentable changing the ID. var docs = section.documentables; if (inheritingNamespace) { docs = []; for (var i in section.documentables) { var oldDoc = section.documentables[i]; docs.push({ name: oldDoc.name, comments: oldDoc.comments, params: oldDoc.params, type: oldDoc.type, defaultValue: oldDoc.defaultValue, id: this$1._normalizeName(inheritingNamespace, oldDoc.name) }); } } // console.log(documentableType, section.name === '__default'); return (getTemplate('section'))({ name: name, id: section.id, comments: section.comments, documentables: (getTemplate(documentableType))({ documentables: docs }), isSecondarySection: (section.name !== '__default' && documentableType !== 'example' && !inheritingNamespace), isInherited: !!inheritingNamespace }); }; // Loop through all the documentables, create an _anchor property, and // return a plain object containing a map of all valid link names to their anchors. Leafdoc.prototype._resolveAKAs = function () { var this$1 = this; for (var ns in this$1._namespaces) { var namespace = this$1._namespaces[ns]; namespace.id = this$1._normalizeName(namespace.name); this$1._assignAKAs(namespace.id, namespace.aka); this$1._assignAKAs(namespace.id, [namespace.name]); // console.log('Resolve namespace AKAs: ', namespace.id, namespace.name, namespace.aka); for (var ss in namespace.supersections) { // console.log(namespace.supersections[ss]); var supersection = namespace.supersections[ss]; var documentableType = supersection.name; supersection.id = this$1._normalizeName(namespace.name, supersection.name); this$1._assignAKAs(supersection.id, [supersection.id + 's']); for (var s in supersection.sections) { var section = supersection.sections[s]; section.id = this$1._normalizeName(namespace.name, section.name === '__default' ? documentableType : section.name); this$1._assignAKAs(section.id, section.aka); // console.log('Resolve section AKAs: ', section.id, section.name, section.aka); for (var d in section.documentables) { var doc = section.documentables[d]; if (doc.name !== '__default') { // Skip comments and examples doc.id = this$1._normalizeName(namespace.name, doc.name); this$1._assignAKAs(doc.id, doc.aka); // console.log('Resolve doc AKAs: ', doc.id, doc.name, doc.aka); } } } } } setAKAs(this._AKAs); // console.log(this._AKAs); }; Leafdoc.prototype._normalizeName = function (namespace, name) { var id = namespace + (name ? '-' + name : ''); id = id.trim().replace(/[\s\.]/g, '-'); return id.toLowerCase(); }; Leafdoc.prototype._assignAKAs = function (id, akas) { var this$1 = this; this._AKAs[id] = id; for (var i in akas) { this$1._AKAs[akas[i].trim()] = id; } }; // Given a class/namespace, recurse through inherited classes to build // up an ordered list of clases/namespaces this class inherits from. Leafdoc.prototype._flattenInheritances = function (classname, inheritancesSoFar) { var this$1 = this; if (!inheritancesSoFar) { // console.log('Resolving inheritances for ', classname); inheritancesSoFar = []; } if (this._namespaces.hasOwnProperty(classname)) { for (var i in this$1._namespaces[classname].inherits) { var parent = this$1._namespaces[classname].inherits[i]; if (inheritancesSoFar.indexOf(parent) === -1) { inheritancesSoFar.push(parent); inheritancesSoFar = this$1._flattenInheritances(parent, inheritancesSoFar); } } } else { console.warn('Warning: Ancestor class/namespace «', classname, '» not found!'); return []; } // console.log(classname, '→', inheritancesSoFar); // console.log(this._namespaces[classname].inherits); return inheritancesSoFar; }; module.exports = Leafdoc;