@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
661 lines • 29.9 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mermaidHide = void 0;
exports.getTypeScriptSourceFiles = getTypeScriptSourceFiles;
exports.dropGenericsFromTypeName = dropGenericsFromTypeName;
exports.removeCommentSymbolsFromTypeScriptComment = removeCommentSymbolsFromTypeScriptComment;
exports.getStartLineOfTypeScriptNode = getStartLineOfTypeScriptNode;
exports.getTypePathLink = getTypePathLink;
exports.visualizeMermaidClassDiagram = visualizeMermaidClassDiagram;
exports.getTypesFromFolder = getTypesFromFolder;
exports.printHierarchy = printHierarchy;
exports.printCodeOfElement = printCodeOfElement;
exports.shortLink = shortLink;
exports.shortLinkFile = shortLinkFile;
exports.getDocumentationForType = getDocumentationForType;
const typescript_1 = __importStar(require("typescript"));
const assert_1 = require("../../util/assert");
const doc_files_1 = require("./doc-files");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const doc_code_1 = require("./doc-code");
const doc_structure_1 = require("./doc-structure");
const html_hover_over_1 = require("../../util/html-hover-over");
const doc_general_1 = require("./doc-general");
const mermaid_1 = require("../../util/mermaid/mermaid");
const options = {
target: typescript_1.default.ScriptTarget.ESNext,
skipLibCheck: true,
skipDefaultLibCheck: true,
allowJs: true,
strict: false,
checkJs: false,
strictNullChecks: false,
noUncheckedIndexedAccess: false,
noUncheckedSideEffectImports: false,
noCheck: true,
noEmit: true,
noResolve: true,
noUnusedLocals: false,
alwaysStrict: true,
incremental: false,
types: [],
lib: [],
noLib: true,
moduleResolution: typescript_1.default.ModuleResolutionKind.Classic,
allowUnreachableCode: true,
allowUnusedLabels: true,
disableSolutionSearching: true,
};
/**
* Retrieve TypeScript source files from the given file names.
*/
function getTypeScriptSourceFiles(fileNames) {
try {
const program = typescript_1.default.createProgram(fileNames, options);
return { program, files: fileNames.map(fileName => program.getSourceFile(fileName)).filter(file => !!file) };
}
catch (err) {
console.error('Failed to get source files', err);
return { files: [], program: undefined };
}
}
const DropGenericsPattern = /<.*>/g;
/**
* Drop generics from a TypeScript type name.
* @example
* ```ts
* const typeName = 'MyType<T, U>';
* const cleanName = dropGenericsFromTypeName(typeName);
* console.log(cleanName); // 'MyType'
* ```
*/
function dropGenericsFromTypeName(type) {
let previous;
do {
previous = type;
type = type.replace(DropGenericsPattern, '');
} while (type !== previous);
return type;
}
const PruneDocCommentPattern = /^\/\*\*?|\*\/$|^\s*\*\s?|\s*\*$/gm;
const PrunedocLinkPattern = /\{@[a-zA-Z]+ ([^}]+\|)?(?<name>[^}]+)}/gm;
/**
* Remove comment symbols from a TypeScript comment string.
* This also takes care of special JSDoc tags like `{@link ...}` and `{@see ...}`.
* @example
* ```ts
* const comment = '/**\n* This is a comment.\n* It has multiple lines.\n *\/'; // closing comment sadly escaped for ts doc
* const cleaned = removeCommentSymbolsFromTypeScriptComment(comment);
* console.log(cleaned);
* ```
* This will output:
* ```md
* This is a comment.
* It has multiple lines.
* ```
*/
function removeCommentSymbolsFromTypeScriptComment(comment) {
return comment
// remove '/** \n * \n */...
.replace(PruneDocCommentPattern, '')
// replace {@key foo|bar} with `bar` and {@key foo} with `foo`
.replace(PrunedocLinkPattern, '<code>$<name></code>')
.trim();
}
function getTextualCommentsFromTypeScript(node) {
const comments = typescript_1.default.getJSDocCommentsAndTags(node);
const out = [];
for (const { comment } of comments) {
if (typeof comment === 'string') {
out.push(removeCommentSymbolsFromTypeScriptComment(comment));
}
else if (comment !== undefined) {
for (const c of comment) {
out.push(removeCommentSymbolsFromTypeScriptComment(c.getText(c.getSourceFile())));
}
}
}
return out;
}
/**
*
*/
function getStartLineOfTypeScriptNode(node, sourceFile) {
const lineStart = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line;
return lineStart + 1;
}
function hasModifier(modifiers, modifier) {
return modifiers.some(mod => typeof mod === 'object' && mod !== null && 'kind' in mod && mod.kind === modifier);
}
function formatNode(node, sourceFile, typeChecker) {
const name = node.name?.getText(sourceFile);
let prefix = '', suffix = '';
if ('modifiers' in node && Array.isArray(node.modifiers)) {
if (hasModifier(node.modifiers, typescript_1.SyntaxKind.PrivateKeyword)) {
prefix = '-';
}
else if (hasModifier(node.modifiers, typescript_1.SyntaxKind.ProtectedKeyword)) {
prefix = '#';
}
else if (hasModifier(node.modifiers, typescript_1.SyntaxKind.PublicKeyword)) {
prefix = '+';
}
if (hasModifier(node.modifiers, typescript_1.SyntaxKind.AbstractKeyword)) {
suffix = '*';
}
else if (hasModifier(node.modifiers, typescript_1.SyntaxKind.StaticKeyword)) {
suffix = '$';
}
}
const type = getType(node, typeChecker);
const typeAnnotation = type.includes('=>') ? type.replaceAll(/\s+=>\s+/g, ' ') : ': ' + type;
return `${prefix}${mermaid_1.Mermaid.escape(name + typeAnnotation)}${suffix}`;
}
function getType(node, typeChecker) {
const tryDirect = typeChecker.getTypeAtLocation(node);
return tryDirect ? typeChecker.typeToString(tryDirect) : 'unknown';
}
const defaultSkip = ['Pick', 'Partial', 'Required', 'Readonly', 'Omit', 'DeepPartial', 'DeepReadonly', 'DeepWritable', 'StrictOmit'];
function followTypeReference(type, sourceFile) {
const node = type.typeName;
if (typescript_1.default.isQualifiedName(node)) {
return [node.right.getText(sourceFile) ?? ''];
}
const args = type.typeArguments?.map(arg => arg.getText(sourceFile)) ?? [];
const nodeLexeme = node.getText(sourceFile) ?? '';
const baseLexeme = type.getText(sourceFile) ?? '';
if (defaultSkip.map(s => nodeLexeme.startsWith(s))) {
return [baseLexeme, ...args];
}
return [nodeLexeme, baseLexeme, ...args];
}
function collectHierarchyInformation(sourceFiles, options) {
const hierarchyList = [];
const typeChecker = options.program.getTypeChecker();
const visit = (node, sourceFile) => {
if (!node) {
return;
}
if (typescript_1.default.isInterfaceDeclaration(node)) {
const interfaceName = node.name?.getText(sourceFile) ?? '';
const baseTypes = node.heritageClauses?.flatMap(clause => clause.types
.map(type => type.getText(sourceFile) ?? '')
.map(dropGenericsFromTypeName)) ?? [];
const generics = node.typeParameters?.map(param => param.getText(sourceFile) ?? '') ?? [];
hierarchyList.push({
name: dropGenericsFromTypeName(interfaceName),
node,
kind: 'interface',
extends: baseTypes,
generics,
comments: getTextualCommentsFromTypeScript(node),
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
properties: node.members.map(member => formatNode(member, sourceFile, typeChecker)),
});
}
else if (typescript_1.default.isTypeAliasDeclaration(node)) {
const typeName = node.name?.getText(sourceFile) ?? '';
let baseTypes = [];
if (typescript_1.default.isIntersectionTypeNode(node.type) || typescript_1.default.isUnionTypeNode(node.type)) {
baseTypes = node.type.types
.filter(typeNode => typescript_1.default.isTypeReferenceNode(typeNode))
.flatMap(typeName => followTypeReference(typeName, sourceFile))
.map(dropGenericsFromTypeName);
}
else if (typescript_1.default.isTypeReferenceNode(node.type)) {
baseTypes = followTypeReference(node.type, sourceFile).map(dropGenericsFromTypeName);
}
const generics = node.typeParameters?.map(param => param.getText(sourceFile) ?? '') ?? [];
hierarchyList.push({
name: dropGenericsFromTypeName(typeName),
node,
kind: 'type',
extends: baseTypes,
comments: getTextualCommentsFromTypeScript(node),
generics,
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
});
}
else if (typescript_1.default.isEnumDeclaration(node)) {
const enumName = node.name?.getText(sourceFile) ?? '';
hierarchyList.push({
name: dropGenericsFromTypeName(enumName),
node,
kind: 'enum',
extends: [],
comments: getTextualCommentsFromTypeScript(node),
generics: [],
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
properties: node.members.map(member => formatNode(member, sourceFile, typeChecker))
});
}
else if (typescript_1.default.isEnumMember(node)) {
const typeName = node.parent.name?.getText(sourceFile) ?? '';
const enumName = dropGenericsFromTypeName(typeName);
hierarchyList.push({
name: dropGenericsFromTypeName(node.name.getText(sourceFile)),
node,
kind: 'enum',
extends: [enumName],
comments: getTextualCommentsFromTypeScript(node),
generics: [],
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
});
}
else if (typescript_1.default.isClassDeclaration(node)) {
const className = node.name?.getText(sourceFile) ?? '';
const baseTypes = node.heritageClauses?.flatMap(clause => clause.types
.map(type => type.getText(sourceFile) ?? '')
.map(dropGenericsFromTypeName)) ?? [];
const generics = node.typeParameters?.map(param => param.getText(sourceFile) ?? '') ?? [];
hierarchyList.push({
name: dropGenericsFromTypeName(className),
node,
kind: 'class',
extends: baseTypes,
comments: getTextualCommentsFromTypeScript(node),
generics,
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
properties: node.members
.filter(member => member.name !== undefined)
.map(member => formatNode(member, sourceFile, typeChecker)),
});
}
else if (typescript_1.default.isVariableDeclaration(node) || typescript_1.default.isExportDeclaration(node) || typescript_1.default.isExportAssignment(node) || typescript_1.default.isDeclarationStatement(node)) {
const name = node.name?.getText(sourceFile) ?? '';
const comments = getTextualCommentsFromTypeScript(node);
hierarchyList.push({
name: dropGenericsFromTypeName(name),
node,
kind: 'variable',
extends: [],
comments,
generics: [],
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
});
}
else if (typescript_1.default.isPropertyAssignment(node) || typescript_1.default.isPropertyDeclaration(node) || typescript_1.default.isPropertySignature(node)
|| typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isMethodSignature(node) || typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isGetAccessorDeclaration(node) || typescript_1.default.isSetAccessorDeclaration(node)) {
const name = node.name?.getText(sourceFile) ?? '';
// get the name of the object/enclosing type
let parent = node.parent;
while (typeof parent === 'object' && parent !== undefined && !('name' in parent)) {
parent = parent.parent;
}
if (typeof parent === 'object' && 'name' in parent) {
const comments = getTextualCommentsFromTypeScript(node);
hierarchyList.push({
name: dropGenericsFromTypeName(name),
node,
kind: 'variable',
extends: [parent.name?.getText(sourceFile) ?? ''],
comments,
generics: [],
filePath: sourceFile.fileName,
lineNumber: getStartLineOfTypeScriptNode(node, sourceFile),
});
}
}
typescript_1.default.forEachChild(node, child => visit(child, sourceFile));
};
for (const sf of sourceFiles) {
visit(sf, sf);
}
return hierarchyList;
}
function getTypePathForTypeScript({ filePath }) {
return filePath.replace(/^.*\/src\//, 'src/').replace(/^.*\/test\//, 'test/');
}
/**
* Return the link to the type in the source code.
* If you create a wiki, please refer to the functions provided by the {@link GeneralWikiContext}.
*/
function getTypePathLink(elem, prefix = doc_files_1.RemoteFlowrFilePathBaseRef) {
const fromSource = getTypePathForTypeScript(elem);
return `${prefix}/${fromSource}#L${elem.lineNumber}`;
}
function generateMermaidClassDiagram(hierarchyList, rootName, options, visited = new Set()) {
const collect = { nodeLines: [], edgeLines: [] };
if (visited.has(rootName)) {
return collect;
} // Prevent circular references
visited.add(rootName);
const node = hierarchyList.find(h => h.name === rootName);
if (!node) {
return collect;
}
const genericPart = node.generics.length > 0 ? `~${node.generics.join(', ')}~` : '';
collect.nodeLines.push(`class ${node.name}${genericPart}{`);
collect.nodeLines.push(` <<${node.kind}>>`);
const writtenProperties = new Set();
if (!options.simplify && node.properties) {
for (const property of node.properties) {
collect.nodeLines.push(` ${property}`);
writtenProperties.add(property);
}
}
collect.nodeLines.push('}');
if (node.kind === 'type') {
collect.nodeLines.push(`style ${node.name} opacity:.35,fill:#FAFAFA`);
}
collect.nodeLines.push(`click ${node.name} href "${getTypePathLink(node)}" "${mermaid_1.Mermaid.escape(node.comments?.join('; ').replace(/\n/g, ' ') ?? '')}"`);
const inline = [...options.inlineTypes ?? [], ...defaultSkip];
let baseTypes = node.extends;
if (options.reverse) {
baseTypes = hierarchyList
.filter(e => e.kind === 'class' || e.kind === 'interface' || e.kind === 'type' || e.kind === 'enum')
.filter(e => e.extends.includes(rootName))
.map(e => e.name);
}
if (baseTypes.length > 0) {
for (const baseType of baseTypes) {
if (inline.includes(baseType)) {
const info = hierarchyList.find(h => h.name === baseType);
for (const property of info?.properties ?? []) {
if (!writtenProperties.has(property)) {
collect.nodeLines.push(` ${node.name} : ${property} [from ${baseType}]`);
writtenProperties.add(property);
}
}
}
else {
if (node.kind === 'type' || hierarchyList.find(h => h.name === baseType)?.kind === 'type') {
collect.edgeLines.push(`${dropGenericsFromTypeName(baseType)} .. ${node.name}`);
}
else {
collect.edgeLines.push(`${dropGenericsFromTypeName(baseType)} ${options.reverse ? '--|>' : '<|--'} ${node.name}`);
}
const { nodeLines, edgeLines } = generateMermaidClassDiagram(hierarchyList, baseType, options, visited);
collect.nodeLines.push(...nodeLines);
collect.edgeLines.push(...edgeLines);
}
}
}
return collect;
}
/**
* Visualize the type hierarchy as a mermaid class diagram.
*/
function visualizeMermaidClassDiagram(hierarchyList, options) {
if (!options.typeNameForMermaid) {
return undefined;
}
const { nodeLines, edgeLines } = generateMermaidClassDiagram(hierarchyList, options.typeNameForMermaid, options);
return nodeLines.length === 0 && edgeLines.length === 0 ? '' : `
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
direction ${options.reverse ? 'LR' : 'RL'}
${nodeLines.join('\n')}
${edgeLines.join('\n')}
`;
}
function getTypesFromFileAsMermaid(fileNames, options) {
const { files, program } = getTypeScriptSourceFiles(fileNames);
(0, assert_1.guard)(files.length > 0, () => `No source files found for ${JSON.stringify(fileNames)}`);
const withProgram = { ...options, program };
const hierarchyList = collectHierarchyInformation(files, withProgram);
return {
mermaid: visualizeMermaidClassDiagram(hierarchyList, withProgram),
info: hierarchyList,
program
};
}
/**
* Inspect typescript source code for types and return a report.
*/
function getTypesFromFolder(options) {
(0, assert_1.guard)(options.rootFolder !== undefined || options.files !== undefined, 'Either rootFolder or files must be provided');
let files = [...options.files ?? []];
if (options.rootFolder) {
const folders = Array.isArray(options.rootFolder) ? options.rootFolder : [options.rootFolder];
for (const folder of folders) {
files = files.concat(fs_1.default.readdirSync(folder, { recursive: true })
.filter(f => {
const p = f.toString();
return p.endsWith('.ts')
&& !p.endsWith('.test.ts')
&& !p.endsWith('-app.ts')
&& !p.endsWith('.d.ts');
})
.map(f => path_1.default.join(folder, f.toString())));
}
}
return getTypesFromFileAsMermaid(files, options);
}
function implSnippet(node, program, showName = true, nesting = 0, open = false, showImplSnippet = true) {
(0, assert_1.guard)(node !== undefined, 'Node must be defined => invalid change of type name?');
const indent = ' '.repeat(nesting * 2);
const bold = node.kind === 'interface' || node.kind === 'enum' ? '**' : '';
const sep = node.comments ? ' \n' : '\n';
let text = node.comments?.join('\n') ?? '';
if (text.trim() !== '') {
text = ' ' + text;
}
if (showImplSnippet) {
const code = node.node.getFullText(program.getSourceFile(node.node.getSourceFile().fileName));
text += `\n<details${open ? ' open' : ''}><summary style="color:gray">Defined at <a href="${getTypePathLink(node)}">${getTypePathLink(node, '.')}</a></summary>\n\n${(0, doc_code_1.codeBlock)('ts', code)}\n\n</details>\n`;
}
else {
text += `\n<br/><i>(Defined at <a href="${getTypePathLink(node)}">${getTypePathLink(node, '.')}</a>)</i>\n`;
}
const init = showName ? `* ${bold}[${node.name}](${getTypePathLink(node)})${bold} ${sep}${indent}` : '';
return ` ${indent}${showName ? init : ''} ${text.replaceAll('\t', ' ').split(/\n/g).join(`\n${indent} `)}`;
}
exports.mermaidHide = ['MergeableRecord', 'Leaf', 'Location', 'Namespace', 'Base', 'WithChildren', 'Partial', 'RAccessBase'];
/**
* Print the hierarchy of types starting from the given root.
* If you create a wiki, please refer to the functions provided by the {@link GeneralWikiContext}.
*/
function printHierarchy({ program, info, root, ignoredTypes, collapseFromNesting = 1, initialNesting = 0, maxDepth = 20, skipNesting = 0, openTop, showImplSnippet = true, reverse = false }) {
if (initialNesting > maxDepth) {
return '';
}
const node = info.find(e => e.name === root);
if (!node) {
return '';
}
let thisLine = '';
if (initialNesting >= skipNesting) {
thisLine = implSnippet(node, program, true, initialNesting, initialNesting === 0 && openTop, showImplSnippet);
}
let baseTypes = node.extends;
if (reverse) {
baseTypes = info
.filter(e => e.kind === 'class' || e.kind === 'interface')
.filter(e => e.extends.includes(root))
.map(e => e.name);
}
const result = [];
for (const baseType of baseTypes) {
if (exports.mermaidHide.includes(baseType) || ignoredTypes?.includes(baseType)) {
continue;
}
const res = printHierarchy({ program, info, root: baseType, ignoredTypes, collapseFromNesting, initialNesting: initialNesting + 1, maxDepth, skipNesting, showImplSnippet, reverse });
result.push(res);
}
const out = result.join('\n');
if (initialNesting >= collapseFromNesting - 1) {
const more = baseTypes.length > 4 ? baseTypes.slice(0, 4).join(', ') + ', ...' : baseTypes.join(', ');
return thisLine + (out ? (0, doc_structure_1.details)(`View more (${more})`, out, { prefixInit: ' '.repeat(2 * (initialNesting + 2)) }) : '');
}
else {
return thisLine + (out ? '\n' + out : '');
}
}
/**
* Print an element from the info as code block.
* If you create a wiki, please refer to the functions provided by the {@link GeneralWikiContext}.
*
* This is great to show examples that are directly taken from the source code.
*/
function printCodeOfElement({ program, info, dropLinesEnd = 0, dropLinesStart = 0, doNotAutoGobble, hideDefinedAt }, name) {
const node = info.find(e => e.name === name);
if (!node) {
console.error(`Could not find node ${name} when resolving function!`);
return '';
}
let code = node.node.getFullText(program.getSourceFile(node.node.getSourceFile().fileName)).trim();
if (dropLinesStart > 0 || dropLinesEnd > 0) {
const lines = code.split(/\n/g);
if (dropLinesStart + dropLinesEnd >= lines.length) {
return '';
}
code = lines.slice(dropLinesStart, lines.length - dropLinesEnd).join('\n');
}
if (!doNotAutoGobble) {
// gobble leading spaces
const lines = code.replaceAll('\t', ' ').split(/\n/g);
let gobble = Number.POSITIVE_INFINITY;
for (const line of lines) {
const match = line.match(/^(\s*)\S+/);
if (match) {
gobble = Math.min(gobble, match[1].length);
}
}
if (gobble !== Number.POSITIVE_INFINITY && gobble > 0) {
code = lines.map(line => line.startsWith(' '.repeat(gobble)) ? line.slice(gobble) : line).join('\n');
}
}
if (hideDefinedAt) {
return (0, doc_code_1.codeBlock)('ts', code);
}
else {
return `${(0, doc_code_1.codeBlock)('ts', code)}\n<i>Defined at <a href="${getTypePathLink(node)}">${getTypePathLink(node, '.')}</a></i>\n`;
}
}
function fuzzyCompare(a, b) {
const aStr = a.toLowerCase().replace(/[^a-z0-9]/g, '-').trim();
const bStr = b.toLowerCase().replace(/[^a-z0-9]/g, '-').trim();
return aStr === bStr || aStr.includes(bStr) || bStr.includes(aStr);
}
function retrieveNode(name, hierarchy, fuzzy = false, type = undefined) {
let container = undefined;
if (name.includes('::')) {
[container, name] = name.split(/:::?/);
}
let node = hierarchy.filter(e => fuzzy ? fuzzyCompare(e.name, name) : e.name === name);
if (node.length === 0) {
return undefined;
}
else if (container) {
node = node.filter(n => fuzzy ? n.extends.some(n => fuzzyCompare(n, container)) : n.extends.includes(container));
if (node.length === 0) {
return undefined;
}
}
if (type) {
node = node.filter(n => n.kind === type);
if (node.length === 0) {
return undefined;
}
}
return [container, name, node[0]];
}
/**
* Create a short link to a type in the documentation.
* If you create a wiki, please refer to the functions provided by the {@link GeneralDocContext}.
* @param name - The name of the type, e.g. `MyType`, may include a container, e.g.,`MyContainer::MyType` (this works with function nestings too)
* Use `:::` if you want to access a scoped function, but the name should be displayed without the scope
* @param hierarchy - The hierarchy of types to search in
* @param codeStyle - Whether to use code style for the link
* @param realNameWrapper - How to highlight the function in name in the `x::y` format?
* @param fuzzy - Whether to use fuzzy matching when searching for the type
* @param type - Optionally restrict to a certain type of element
*/
function shortLink(name, hierarchy, codeStyle = true, realNameWrapper = 'b', fuzzy, type) {
const res = retrieveNode(name, hierarchy, fuzzy, type);
if (!res) {
console.error(`Could not find node ${name} when resolving short link!`);
return '';
}
const [, mainName, node] = res;
let pkg = res[0];
if (name.includes(':::')) {
pkg = undefined;
}
const comments = node.comments?.join('\n').replace(/\\?\n|```[a-zA-Z]*|\s\s*/g, ' ').replace(/<\/?code>|`/g, '').replace(/<\/?p\/?>/g, ' ').replace(/"/g, '\'') ?? '';
return `<a href="${getTypePathLink(node)}">${codeStyle ? '<code>' : ''}${(node.comments?.length ?? 0) > 0 ?
(0, html_hover_over_1.textWithTooltip)(pkg ? `${pkg}::<${realNameWrapper}>${mainName}</${realNameWrapper}>` : mainName, comments.length > 400 ? comments.slice(0, 400) + '...' : comments) :
pkg ? `${pkg}::<${realNameWrapper}>${mainName}</${realNameWrapper}>` : mainName}${codeStyle ? '</code>' : ''}</a>`;
}
/**
* Create a short link to a type in the documentation.
* If you create a wiki, please refer to the functions provided by the {@link GeneralWikiContext}.
* @param name - The name of the type, e.g. `MyType`, may include a container, e.g.,`MyContainer::MyType` (this works with function nestings too)
* Use `:::` if you want to access a scoped function, but the name should be displayed without the scope
* @param hierarchy - The hierarchy of types to search in
*/
function shortLinkFile(name, hierarchy) {
const res = retrieveNode(name, hierarchy);
if (!res) {
console.error(`Could not find node ${name} when resolving short link!`);
return '';
}
const [, , node] = res;
return `<a href="${getTypePathLink(node)}">${getTypePathForTypeScript(node)}</a>`;
}
/**
* Retrieve documentation comments for a type.
* If you create a wiki, please refer to the functions provided by the {@link GeneralWikiContext}.
* @param name - The name of the type, e.g. `MyType`, may include a container, e.g.,`MyContainer::MyType` (this works with function nestings too)
* Use `:::` if you want to access a scoped function, but the name should be displayed without the scope
* @param hierarchy - The hierarchy of types to search in
* @param prefix - A prefix to add to each line of the documentation
* @param filter - Optional filters for retrieving the documentation
*/
function getDocumentationForType(name, hierarchy, prefix = '', filter) {
const res = retrieveNode(name, hierarchy, filter?.fuzzy, filter?.type);
if (!res) {
return '';
}
const [, , node] = res;
return (0, doc_general_1.prefixLines)(node.comments?.join('\n') ?? '', prefix);
}
//# sourceMappingURL=doc-types.js.map