UNPKG

typedoc-theme-yaf

Version:

A fresh, opinionated and standalone front-end documentation application consuming Typedoc generated data.

268 lines 11.7 kB
"use strict"; 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __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.YafTheme = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const typedoc_1 = require("typedoc"); const typeClasses = __importStar(require("typedoc")); const highlighter_1 = require("./highlighter"); const YafSerialiser_1 = require("./YafSerialiser"); let oldRenderer; /** * This extends the TypeDoc default theme and provides a collection of overrides and methods to serialise and save data fragments * for consumption by the theme single page application (SPA) frontend. * */ class YafTheme extends typedoc_1.DefaultTheme { /** * A placeholder for the replacement MD highlighter which is injected at {@link backend.load}. */ static highlighter; constructor(renderer) { super(oldRenderer || renderer); this.markedPlugin.getHighlighted = (text, lang) => (0, highlighter_1.getHighlighted)(YafTheme.highlighter, text, lang); this.markedPlugin.getRelativeUrl = (absolute) => { console.log(absolute); return 'foo'; }; if (!oldRenderer) { this.application.renderer.on(typedoc_1.RendererEvent.END, this.prepareYafTheme); oldRenderer = renderer; } } /** * We do not have, or need the `PageEvent` function parameter of the default typedoc call, * so override the default function. * @returns the theme render `context` */ getRenderContext() { return new typedoc_1.DefaultThemeRenderContext(this, this.application.options); } /** * Prevents the default typedoc `render` call from saving pages to html. * @param page * @returns */ render(page) { page.preventDefault(); return ''; } /** * Triggered at the typedoc `RendererEvent.BEGIN` event. * * It prepares various items of data that are to be consumed by the theme front-end; * * @param output */ prepareYafTheme = (output) => { const context = this.getRenderContext(); const { saveYafThemeAssets } = YafTheme.fileFactory; const docDir = this.application.options.getValue('out') || 'docs'; let distDir = path_1.default.join(__dirname, '..', '../'); const hackyDistDir = path_1.default.join(distDir, 'src'); /** to make npm published paths work... */ if (fs_extra_1.default.existsSync(hackyDistDir)) distDir = hackyDistDir; const project = output.project; const projectObject = this.application.serializer.toObject(project); const yafSerialiser = new YafSerialiser_1.YafSerialiser(projectObject, project, context); saveYafThemeAssets(distDir, docDir, yafSerialiser.dataObjectArray, project, context); }; /** * A collection of methods to work with data and files. * * @group Factories */ static fileFactory = { /** * Copies / saves various theme assets and data files for consumption by the front-end. */ saveYafThemeAssets: (distDir, docDir, dataObjectArray, project, context) => { const { copyThemeFiles, saveDataFile } = this.fileFactory; const { makeNavTree, makeYafReflectionMap, makeYafKindSymbols, makeNeedsParenthesis, } = this.factory; copyThemeFiles(distDir, docDir); saveDataFile('yafNavigationMenu', docDir, makeNavTree(project)); saveDataFile('yafReflectionMap', docDir, makeYafReflectionMap(dataObjectArray)); saveDataFile('yafKindSymbols', docDir, makeYafKindSymbols(context.icons)); saveDataFile('yafReflectionKind', docDir, typedoc_1.ReflectionKind); saveDataFile('yafNeedsParenthesis', docDir, makeNeedsParenthesis()); dataObjectArray.forEach((object) => { const fileName = object.kind === typedoc_1.ReflectionKind.Project ? 'index' : object.location.query; saveDataFile(fileName, docDir, object); }); }, /** * Copies various theme resource files into the documentation target directory. * @param distDir The absolute path to the project root * @param outDir The absolute path to the root documentation out directory */ copyThemeFiles: (distDir, outDir) => { const mediaSrc = path_1.default.join(distDir, 'media'); const mediaDest = path_1.default.join(outDir, 'media'); const frontendSrc = path_1.default.join(distDir, 'frontend'); const frontendDest = path_1.default.join(outDir, 'frontend'); const indexSrc = path_1.default.join(distDir, 'index.html'); const indexDest = path_1.default.join(outDir, 'index.html'); const assetsPath = path_1.default.join(outDir, 'assets'); fs_extra_1.default.copySync(frontendSrc, frontendDest); fs_extra_1.default.copySync(mediaSrc, mediaDest); fs_extra_1.default.copySync(indexSrc, indexDest); fs_extra_1.default.copySync(path_1.default.join(assetsPath, 'versionsMenu.js'), path_1.default.join(mediaDest, 'versionsMenu.js')); setTimeout(() => { console.log(`This theme no longer needs the default theme assets, so deleting: ${assetsPath}`); fs_extra_1.default.removeSync(assetsPath); }); }, /** * saves a data object to a data .json file for consumption by the front-end. * @param fileName the file name with or without .json extension * @param docDir the absolute path to the document directory * @param data * @param dataDir the data subdirectory path under the document directory */ saveDataFile: (fileName, docDir, data, dataDir = 'data') => { fileName = fileName.replace(/.JSON$/i, '.json'); if (!fileName.endsWith('.json')) fileName = `${fileName}.json`; const dirPath = dataDir ? path_1.default.join(docDir, dataDir) : docDir; const filePath = path_1.default.join(dirPath, fileName); fs_extra_1.default.ensureDirSync(path_1.default.dirname(filePath)); fs_extra_1.default.writeJsonSync(filePath, data); }, }; /** * A collection of methods to create data for saving as frontend assets. * * @group Factories */ static factory = { makeYafKindSymbols: (icons) => { const symbols = {}; Object.keys(icons) .filter((key) => !!typedoc_1.ReflectionKind[key]) .forEach((key) => { const functionString = String(icons[key]) .split('()')[1] .split('ReflectionKind.')[1] .split(/[^A-Z]/i)[0]; symbols[key] = { className: functionString.toLocaleLowerCase(), symbol: functionString[0], }; }); return symbols; }, makeYafReflectionMap: (data, map = {}) => { if (!data) return; const { defaultReflectionLink, makeYafReflectionMap } = this.factory; data.forEach((objectReflection) => { const { id, name, location, kind, flags, parentId } = objectReflection; const mapId = kind === typedoc_1.ReflectionKind.Project ? 'project' : objectReflection.id; map[mapId] = defaultReflectionLink(id, parentId, name, location, kind, flags); const hasChildren = objectReflection.children; const hasSignatures = objectReflection.signatures; const hasDeclarations = objectReflection.type && 'declaration' in objectReflection.type; if (hasChildren) { makeYafReflectionMap(objectReflection.children, map); } if (hasSignatures) { makeYafReflectionMap(objectReflection.signatures, map); } if (hasDeclarations) { makeYafReflectionMap(objectReflection.type .declaration.children, map); } }); return map; }, defaultReflectionLink: (id, parentId, name, location, kind, flags) => { return { id, parentId, name, query: location.query, hash: location.hash, kind, flags, }; }, makeNeedsParenthesis: () => { const map = {}; Object.values(typeClasses) .filter((f) => typeof f === 'function' && String(f).indexOf('extends Type') > -1) .forEach((f) => { const newClass = new f([]); map[newClass.type] = {}; Object.values(typedoc_1.TypeContext).forEach((context) => { map[newClass.type][context] = newClass.needsParenthesis(context); }); }); return map; }, /** * Builds a data tree for the main navigation menu * @param menuNode * @param reflection * @returns a hierarchical tree representation of the main navigation menu. */ makeNavTree: (reflection, menuNode = {}) => { const { makeNavTree } = this.factory; if (reflection.isProject()) { menuNode['project'] = { children: {}, }; reflection.children?.forEach((child) => makeNavTree(child, menuNode)); return menuNode; } const { id, children, type } = reflection; menuNode[id] = { children: {}, }; children?.forEach((child) => { makeNavTree(child, menuNode[id].children); }); if (type instanceof typedoc_1.ReflectionType) { type.declaration.children?.forEach((child) => { makeNavTree(child, menuNode[reflection.id].children); }); } return menuNode; }, }; } exports.YafTheme = YafTheme; //# sourceMappingURL=YafTheme.js.map