UNPKG

@webdoc/template-library

Version:

Goodies for @webdoc template packages! See @webdoc/legacy-template for an example!

262 lines (239 loc) 6.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SymbolLinks = void 0; var _model = require("@webdoc/model"); var _Logger = require("./Logger"); const catharsis = require("catharsis"); const pathToUrl = new Map(); const pathToID = new Map(); const urlToPath = new Map(); const usedIDs = {}; const usedFileNames = {}; const SCOPE_TO_PUNC = {}; const STANDALONE_DOCS = ["ClassDoc", "NSDoc", "InterfaceDoc", "MixinDoc", "PackageDoc", "TutorialDoc"]; const hasOwnProp = Object.prototype.hasOwnProperty; function isComplexTypeExpression(expr) { return /^{.+}$/.test(expr) || /^.+\|.+$/.test(expr) || /^.+<.+>$/.test(expr); } function hasUrlPrefix(text) { return /^(http|ftp)s?:\/\//.test(text); } function getShortName(docPath) { const parts = docPath.split(/(.|#|~)/); return parts[parts.length - 1]; } function stringifyType(parsedType, cssClass, stringifyLinkMap) { return require("catharsis").stringify(parsedType, { cssClass: cssClass, htmlSafe: true, links: stringifyLinkMap }); } function parseType(longname) { let err; try { return catharsis.parse(longname, { jsdoc: true }); } catch (e) { err = new Error(`unable to parse ${longname}: ${e.message}`); _Logger.templateLogger.error(err); return longname; } } function fragmentHash(fragmentId) { if (!fragmentId) { return ""; } return `#${fragmentId}`; } function registerID(docPath, fragment) { pathToID.set(docPath, fragment); } function generateID(fileName, id) { let key; let nonUnique = true; key = id.toLowerCase(); id = id.replace(/\s/g, ""); while (nonUnique) { if (hasOwnProp.call(usedIDs, fileName) && hasOwnProp.call(usedIDs[fileName], key)) { id += "_"; key = id.toLowerCase(); } else { nonUnique = false; } } usedIDs[fileName] = usedIDs[fileName] || {}; usedIDs[fileName][key] = id; return id; } function getID(docPath, id) { if (pathToID.has(docPath)) { id = pathToID.get(docPath); } else if (!id) { return ""; } else { id = generateID(docPath, id); registerID(docPath, id); } return id || ""; } function formatNameForLink(doclet) { let newName = (doclet.name || "") + (doclet.variation || ""); const scopePunc = SCOPE_TO_PUNC[doclet.scope] || ""; if (scopePunc !== "#") { newName = scopePunc + newName; } return newName; } function generateFileName(fileName, str) { let key = fileName.toLowerCase(); let nonUnique = true; if (!fileName.length || fileName[0] === "_") { fileName = `-${fileName}`; key = fileName.toLowerCase(); } while (nonUnique) { if (hasOwnProp.call(usedFileNames, key)) { fileName += "_"; key += "_"; } else { nonUnique = false; } } usedFileNames[key] = str; return fileName; } function getFileName(str) { if (pathToUrl.has(str)) { return pathToUrl.get(str); } let basename = (str || "" ).replace(/[\\/?*:|'"<>]/g, "_") .replace(/~/g, "-") .replace(/#/g, "_") .replace(/\//g, "_") .replace(/\([\s\S]*\)$/, "") .replace(/^[.-]/, ""); basename = basename.length ? basename : "_"; const fileName = generateFileName(basename, str) + ".html"; pathToUrl.set(str, fileName); urlToPath.set(fileName, str); return fileName; } const registerLink = (docPath, fileUrl) => { pathToUrl.set(docPath, fileUrl); urlToPath.set(fileUrl, docPath); return fileUrl; }; const createLink = (doc) => { let fakeContainer; let filename; let fragment = ""; const docPath = doc.path; let match; if (!STANDALONE_DOCS.includes(doc.type)) { match = /(\S+):/.exec(docPath); if (match && STANDALONE_DOCS.includes(match[1])) { fakeContainer = match[1]; } } if (STANDALONE_DOCS.includes(doc.type)) { filename = getFileName(docPath); } else if (!STANDALONE_DOCS.includes(doc.type) && fakeContainer) { filename = getFileName(doc.memberof || docPath); if (doc.name !== doc.path) { fragment = formatNameForLink(doc); fragment = getID(docPath, fragment); } } else { filename = getFileName(doc.parent.path || exports.globalName); if (doc.name !== doc.path) { fragment = formatNameForLink(doc); fragment = getID(docPath, fragment); } } return (filename || "") + fragmentHash(fragment); }; function buildLink(docPath, linkText = docPath, options) { if (!docPath) { return ""; } if ((0, _model.isDataType)(docPath)) { let link = docPath.template; for (let i = 1; i < docPath.length; i++) { link = link.replace(`%${i}`, buildLink(docPath[i], docPath[i], options)); } return link; } else if (typeof docPath !== "string") { docPath = docPath.path; } if (linkText && typeof linkText !== "string") { linkText = linkText.path; } options.linkMap = options.linkMap || pathToUrl; const classString = options.cssClass ? ` class="${options.cssClass}"` : ""; let fileUrl; const fragmentString = fragmentHash(options.fragmentId || ""); let text; let parsedType; const stripped = docPath ? docPath.replace(/^<|>$/g, "") : ""; if (hasUrlPrefix(stripped)) { fileUrl = stripped; text = linkText || stripped; } else if (docPath && isComplexTypeExpression(docPath) && /\{@.+\}/.test(docPath) === false && /^<[\s\S]+>/.test(docPath) === false) { parsedType = parseType(docPath); return stringifyType(parsedType, options.cssClass, Object.fromEntries(options.linkMap)); } else { fileUrl = options.linkMap.has(docPath) ? options.linkMap.get(docPath) : ""; text = linkText || (options.shortenName ? getShortName(docPath) : docPath); } text = options.monospace ? `<code>${text}</code>` : text; if (!fileUrl) { return text; } else { return `<a href="${encodeURI(fileUrl + fragmentString)}"${classString}>${text}</a>`; } } const linkTo = (docPath, linkText, cssClass, fragmentId) => buildLink(docPath, linkText, { cssClass: cssClass || "", fragmentId: fragmentId || "", linkMap: pathToUrl }); const getAncestorLinks = (doc, cssClass) => { const ancestors = []; let searchDoc = doc.parent; while (searchDoc) { ancestors.push(searchDoc); searchDoc = searchDoc.parent; } const links = []; ancestors.forEach(ancestor => { if (ancestor.type === "RootDoc" || !ancestor.parent) { return; } links.unshift(linkTo(ancestor.path, ancestor.name, cssClass)); }); if (links.length) { } return links; }; const SymbolLinks = { STANDALONE_DOCS, pathToUrl, urlToPath, registerID, generateID, getID, generateFileName, getFileName, registerLink, createLink, buildLink, linkTo, getAncestorLinks }; exports.SymbolLinks = SymbolLinks;