@webdoc/template-library
Version:
Goodies for @webdoc template packages! See @webdoc/legacy-template for an example!
262 lines (239 loc) • 6.72 kB
JavaScript
;
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;