typedoc-theme-yaf
Version:
A fresh, opinionated and standalone front-end documentation application consuming Typedoc generated data.
268 lines • 11.7 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 (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