UNPKG

typedoc

Version:

Create api documentation for TypeScript projects.

368 lines (367 loc) 20.1 kB
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; import { Theme } from "../../theme.js"; import { ReferenceReflection, ReflectionCategory, ReflectionGroup, ReflectionKind, } from "../../../models/index.js"; import { DefaultThemeRenderContext } from "./DefaultThemeRenderContext.js"; import { getIcons } from "./partials/icon.js"; import { filterMap, JSX } from "#utils"; import { classNames, getDisplayName, toStyleClass } from "../lib.js"; import { PageKind } from "../../router.js"; import { loadHighlighter, Option } from "#node-utils"; let DefaultTheme = (() => { let _classSuper = Theme; let _lightTheme_decorators; let _lightTheme_initializers = []; let _lightTheme_extraInitializers = []; let _darkTheme_decorators; let _darkTheme_initializers = []; let _darkTheme_extraInitializers = []; let _highlightLanguages_decorators; let _highlightLanguages_initializers = []; let _highlightLanguages_extraInitializers = []; let _ignoredHighlightLanguages_decorators; let _ignoredHighlightLanguages_initializers = []; let _ignoredHighlightLanguages_extraInitializers = []; return class DefaultTheme extends _classSuper { static { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0; _lightTheme_decorators = [Option("lightHighlightTheme")]; _darkTheme_decorators = [Option("darkHighlightTheme")]; _highlightLanguages_decorators = [Option("highlightLanguages")]; _ignoredHighlightLanguages_decorators = [Option("ignoredHighlightLanguages")]; __esDecorate(this, null, _lightTheme_decorators, { kind: "accessor", name: "lightTheme", static: false, private: false, access: { has: obj => "lightTheme" in obj, get: obj => obj.lightTheme, set: (obj, value) => { obj.lightTheme = value; } }, metadata: _metadata }, _lightTheme_initializers, _lightTheme_extraInitializers); __esDecorate(this, null, _darkTheme_decorators, { kind: "accessor", name: "darkTheme", static: false, private: false, access: { has: obj => "darkTheme" in obj, get: obj => obj.darkTheme, set: (obj, value) => { obj.darkTheme = value; } }, metadata: _metadata }, _darkTheme_initializers, _darkTheme_extraInitializers); __esDecorate(this, null, _highlightLanguages_decorators, { kind: "accessor", name: "highlightLanguages", static: false, private: false, access: { has: obj => "highlightLanguages" in obj, get: obj => obj.highlightLanguages, set: (obj, value) => { obj.highlightLanguages = value; } }, metadata: _metadata }, _highlightLanguages_initializers, _highlightLanguages_extraInitializers); __esDecorate(this, null, _ignoredHighlightLanguages_decorators, { kind: "accessor", name: "ignoredHighlightLanguages", static: false, private: false, access: { has: obj => "ignoredHighlightLanguages" in obj, get: obj => obj.ignoredHighlightLanguages, set: (obj, value) => { obj.ignoredHighlightLanguages = value; } }, metadata: _metadata }, _ignoredHighlightLanguages_initializers, _ignoredHighlightLanguages_extraInitializers); if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); } // Note: This will always contain lowercased names to avoid issues with // case-insensitive file systems. usedFileNames = new Set(); /** @internal */ markedPlugin; router; /** * The icons which will actually be rendered. The source of truth lives on the theme, and * the {@link DefaultThemeRenderContext.icons} member will produce references to these. * * These icons will be written twice. Once to an `icons.svg` file in the assets directory * which will be referenced by icons on the context, and once to an `icons.js` file so that * references to the icons can be dynamically embedded within the page for use by the search * dropdown and when loading the page on `file://` urls. * * Custom themes may overwrite this entire object or individual properties on it to customize * the icons used within the page, however TypeDoc currently assumes that all icons are svg * elements, so custom themes must also use svg elements. */ icons; ContextClass = DefaultThemeRenderContext; #lightTheme_accessor_storage = __runInitializers(this, _lightTheme_initializers, void 0); get lightTheme() { return this.#lightTheme_accessor_storage; } set lightTheme(value) { this.#lightTheme_accessor_storage = value; } #darkTheme_accessor_storage = (__runInitializers(this, _lightTheme_extraInitializers), __runInitializers(this, _darkTheme_initializers, void 0)); get darkTheme() { return this.#darkTheme_accessor_storage; } set darkTheme(value) { this.#darkTheme_accessor_storage = value; } #highlightLanguages_accessor_storage = (__runInitializers(this, _darkTheme_extraInitializers), __runInitializers(this, _highlightLanguages_initializers, void 0)); get highlightLanguages() { return this.#highlightLanguages_accessor_storage; } set highlightLanguages(value) { this.#highlightLanguages_accessor_storage = value; } #ignoredHighlightLanguages_accessor_storage = (__runInitializers(this, _highlightLanguages_extraInitializers), __runInitializers(this, _ignoredHighlightLanguages_initializers, void 0)); get ignoredHighlightLanguages() { return this.#ignoredHighlightLanguages_accessor_storage; } set ignoredHighlightLanguages(value) { this.#ignoredHighlightLanguages_accessor_storage = value; } getRenderContext(pageEvent) { return new this.ContextClass(this.router, this, pageEvent, this.application.options); } documentTemplate = (__runInitializers(this, _ignoredHighlightLanguages_extraInitializers), (pageEvent) => { return this.getRenderContext(pageEvent).documentTemplate(pageEvent); }); reflectionTemplate = (pageEvent) => { return this.getRenderContext(pageEvent).reflectionTemplate(pageEvent); }; indexTemplate = (pageEvent) => { return this.getRenderContext(pageEvent).indexTemplate(pageEvent); }; hierarchyTemplate = (pageEvent) => { return this.getRenderContext(pageEvent).hierarchyTemplate(pageEvent); }; defaultLayoutTemplate = (pageEvent, template) => { return this.getRenderContext(pageEvent).defaultLayout(template, pageEvent); }; getReflectionClasses(reflection) { const filters = this.application.options.getValue("visibilityFilters"); return getReflectionClasses(reflection, filters); } /** * This is used so that themes may define multiple icons for modified icons (e.g. method, and inherited method) */ getReflectionIcon(reflection) { return reflection.kind; } /** * Create a new DefaultTheme instance. * * @param renderer The renderer this theme is attached to. */ constructor(renderer) { super(renderer); this.icons = getIcons(); this.markedPlugin = renderer.markedPlugin; this.router = renderer.router; } render(page) { const templateMapping = { [PageKind.Index]: this.indexTemplate, [PageKind.Document]: this.documentTemplate, [PageKind.Hierarchy]: this.hierarchyTemplate, [PageKind.Reflection]: this.reflectionTemplate, }; const template = templateMapping[page.pageKind]; if (!template) { throw new Error(`TypeDoc's DefaultTheme does not support the page kind ${page.pageKind}`); } if (!page.isReflectionEvent()) { throw new Error(`TypeDoc's DefaultTheme requires that a page model be a reflection when rendering ${page.pageKind}`); } const templateOutput = this.defaultLayoutTemplate(page, template); return "<!DOCTYPE html>" + JSX.renderElement(templateOutput) + "\n"; } async preRender(_event) { await loadHighlighter(this.lightTheme, this.darkTheme, // Checked in option validation this.highlightLanguages, this.ignoredHighlightLanguages); } _navigationCache; /** * If implementing a custom theme, it is recommended to override {@link buildNavigation} instead. */ getNavigation(project) { // This is ok because currently TypeDoc wipes out the theme after each render. // Might need to change in the future, but it's fine for now. if (this._navigationCache) { return this._navigationCache; } return (this._navigationCache = this.buildNavigation(project)); } buildNavigation(project) { // eslint-disable-next-line @typescript-eslint/no-this-alias const theme = this; const router = this.router; const opts = this.application.options.getValue("navigation"); const leaves = this.application.options.getValue("navigationLeaves"); return getNavigationElements(project) || []; function toNavigation(element) { if (opts.excludeReferences && element instanceof ReferenceReflection) { return; } const children = getNavigationElements(element); if (element instanceof ReflectionCategory || element instanceof ReflectionGroup) { if (!children?.length) { return; } return { text: element.title, children, }; } const icon = theme.getReflectionIcon(element) === element.kind ? undefined : theme.getReflectionIcon(element); return { text: getDisplayName(element), path: router.getFullUrl(element), kind: element.kind & ReflectionKind.Project ? undefined : element.kind, class: classNames({ deprecated: element.isDeprecated() }, theme.getReflectionClasses(element)), children: children?.length ? children : undefined, icon, }; } function getNavigationElements(parent) { if (parent instanceof ReflectionCategory) { return filterMap(parent.children, toNavigation); } if (parent instanceof ReflectionGroup) { if (shouldShowCategories(parent.owningReflection, opts) && parent.categories) { return filterMap(parent.categories, toNavigation); } return filterMap(parent.children, toNavigation); } if (leaves.includes(parent.getFullName())) { return; } if (!parent.kindOf(ReflectionKind.MayContainDocuments)) { return; } if (parent.isDocument()) { return filterMap(parent.children, toNavigation); } if (!parent.kindOf(ReflectionKind.SomeModule | ReflectionKind.Project)) { // Tricky: Non-module children don't show up in the navigation pane, // but any documents added by them should. return filterMap(parent.documents, toNavigation); } if (parent.categories && shouldShowCategories(parent, opts)) { return filterMapWithNoneCollection(parent.categories); } if (parent.groups && shouldShowGroups(parent, opts)) { return filterMapWithNoneCollection(parent.groups); } if (opts.includeFolders && parent.childrenIncludingDocuments?.some((child) => child.name.includes("/"))) { return deriveModuleFolders(parent.childrenIncludingDocuments); } return filterMap(parent.childrenIncludingDocuments, toNavigation); } function filterMapWithNoneCollection(reflection) { const none = reflection.find((x) => x.title.toLocaleLowerCase() === "none"); const others = reflection.filter((x) => x.title.toLocaleLowerCase() !== "none"); const mappedOthers = filterMap(others, toNavigation); if (none) { const noneMappedChildren = filterMap(none.children, toNavigation); return [...noneMappedChildren, ...mappedOthers]; } return mappedOthers; } function deriveModuleFolders(children) { const result = []; const resolveOrCreateParents = (path, root = result) => { if (path.length > 1) { const inner = root.find((el) => el.text === path[0]); if (inner) { inner.children ||= []; return resolveOrCreateParents(path.slice(1), inner.children); } else { root.push({ text: path[0], children: [], }); return resolveOrCreateParents(path.slice(1), root[root.length - 1].children); } } return root; }; // Note: This might end up putting a module within another module if we document // both foo/index.ts and foo/bar.ts. for (const child of children.filter((c) => router.hasOwnDocument(c))) { const nav = toNavigation(child); if (nav) { const parts = child.name.split("/"); const collection = resolveOrCreateParents(parts); nav.text = parts[parts.length - 1]; collection.push(nav); } } // Now merge single-possible-paths together so we don't have folders in our navigation // which contain only another single folder. if (opts.compactFolders) { const queue = [...result]; while (queue.length) { const review = queue.shift(); queue.push(...(review.children || [])); if (review.kind || review.path) continue; if (review.children?.length === 1) { const copyFrom = review.children[0]; const fullName = `${review.text}/${copyFrom.text}`; delete review.children; Object.assign(review, copyFrom); review.text = fullName; queue.push(review); } } } return result; } } }; })(); export { DefaultTheme }; function getReflectionClasses(reflection, filters) { const classes = new Set(); // Filter classes should match up with the settings function in // partials/navigation.tsx. for (const key of Object.keys(filters)) { if (key === "inherited") { if (reflection.flags.isInherited) { classes.add("tsd-is-inherited"); } } else if (key === "protected") { if (reflection.flags.isProtected) { classes.add("tsd-is-protected"); } } else if (key === "private") { if (reflection.flags.isPrivate) { classes.add("tsd-is-private"); } } else if (key === "external") { if (reflection.flags.isExternal) { classes.add("tsd-is-external"); } } else if (key.startsWith("@")) { if (key === "@deprecated") { if (reflection.isDeprecated()) { classes.add(toStyleClass(`tsd-is-${key.substring(1)}`)); } } else if (reflection.comment?.hasModifier(key) || reflection.comment?.getTag(key)) { classes.add(toStyleClass(`tsd-is-${key.substring(1)}`)); } else if (reflection.isDeclaration()) { const ownSignatures = reflection.getNonIndexSignatures(); // Check methods and accessors, find common tags, elevate if (ownSignatures.length && ownSignatures.every((refl) => refl.comment?.hasModifier(key) || refl.comment?.getTag(key))) { classes.add(toStyleClass(`tsd-is-${key.substring(1)}`)); } } } } return Array.from(classes).join(" "); } function shouldShowCategories(reflection, opts) { if (opts.includeCategories) { return !reflection.comment?.hasModifier("@hideCategories"); } return reflection.comment?.hasModifier("@showCategories") === true; } function shouldShowGroups(reflection, opts) { if (opts.includeGroups) { return !reflection.comment?.hasModifier("@hideGroups"); } return reflection.comment?.hasModifier("@showGroups") === true; }