Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom partials and templates #361

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/typedoc-plugin-markdown/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ export function load(app: Application) {
type: ParameterType.Boolean,
defaultValue: false,
});

app.options.addDeclaration({
help: "[Markdown Plugin] Custom Handlebars partial files to register.",
name: 'customPartials',
type: ParameterType.Object,
defaultValue: {},
});

app.options.addDeclaration({
help: "[Markdown Plugin] Custom Handlebars template files to override defaults.",
name: 'customTemplates',
type: ParameterType.Object,
defaultValue: {},
});
}

export { MarkdownTheme };
78 changes: 66 additions & 12 deletions packages/typedoc-plugin-markdown/src/render-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,81 @@ import typeAndParentHelper from './resources/helpers/type-and-parent';
import typeParameterTableHelper from './resources/helpers/type-parameter-table';
import { MarkdownTheme } from './theme';

const TEMPLATE_PATH = path.join(__dirname, 'resources', 'templates');
export enum TemplateName {
Index = 'index',
Reflection = 'reflection',
ReflectionMember = 'reflection.member'
}

export const indexTemplate = Handlebars.compile(
fs.readFileSync(path.join(TEMPLATE_PATH, 'index.hbs')).toString(),
);
let compiledTemplates: Record<TemplateName, HandlebarsTemplateDelegate>;

export const reflectionTemplate = Handlebars.compile(
fs.readFileSync(path.join(TEMPLATE_PATH, 'reflection.hbs')).toString(),
);
export function compileTemplates(theme: MarkdownTheme) {
// Default template files
const templatesFolder = path.join(__dirname, 'resources', 'templates');
const templateFiles: Record<TemplateName, string> = {
[TemplateName.Index]: path.join(templatesFolder, 'index.hbs'),
[TemplateName.Reflection]: path.join(templatesFolder, 'reflection.hbs'),
[TemplateName.ReflectionMember]: path.join(templatesFolder, 'reflection.member.hbs')
}
// Merge in user-defined templates
if (theme.customTemplates) {
// Merge the custom templates into the defaults
Object.entries(theme.customTemplates).forEach(([name, path]) => {
if (path) {
console.debug(`[Markdown Plugin] Using custom template "${name}" = ${path}`)
templateFiles[name] = path;
} else {
console.warn(`Custom template "${name}" was defined with an empty file path, so it will be ignored.`);
}
})
}
// Read file contents and compile
const compiled: Partial<typeof compiledTemplates> = {}
Object.entries(templateFiles).forEach(([templateName, filePath]) => {
if (!fs.existsSync(filePath)) {
throw new Error(`Template "${templateName}" file does not exist at path: ${filePath}`);
}
compiled[templateName] = Handlebars.compile(fs.readFileSync(filePath).toString());
});
// Set global variable
compiledTemplates = compiled as typeof compiledTemplates;
}

export const reflectionMemberTemplate = Handlebars.compile(
fs.readFileSync(path.join(TEMPLATE_PATH, 'reflection.member.hbs')).toString(),
);
export function getCompiledTemplate<T = any>(templateName: TemplateName): HandlebarsTemplateDelegate<T> {
if (!compiledTemplates) {
throw new Error(`Failed to get template "${templateName}" because templates have not yet been compiled.`)
}
if (!compiledTemplates[templateName]) {
throw new Error(`Template "${templateName}" has no compiled template.`)
}
return compiledTemplates[templateName] as HandlebarsTemplateDelegate<T>;
}

export function registerPartials() {
export function registerPartials(theme: MarkdownTheme) {
// Default partials files
const partialFilePaths: Record<string, string> = {};
const partialsFolder = path.join(__dirname, 'resources', 'partials');
const partialFiles = fs.readdirSync(partialsFolder);
partialFiles.forEach((partialFile) => {
const partialName = path.basename(partialFile, '.hbs');
partialFilePaths[partialName] = partialsFolder + '/' + partialFile;
});
// Merge in user-defined partials
Object.entries(theme.customPartials).forEach(([name, path]) => {
if (path) {
console.debug(`[Markdown Plugin] Using custom partial "${name}" = ${path}`)
partialFilePaths[name] = path;
} else {
console.warn(`Custom partial "${name}" was defined with an empty file path, so it will be ignored.`);
}
})
// Read file contents and register
Object.entries(partialFilePaths).forEach(([partialName, filePath]) => {
if (!fs.existsSync(filePath)) {
throw new Error(`Partial "${partialName}" file does not exist at path: ${filePath}`);
}
const partialContent = fs
.readFileSync(partialsFolder + '/' + partialFile)
.readFileSync(filePath)
.toString();
Handlebars.registerPartial(partialName, partialContent);
});
Expand Down
21 changes: 14 additions & 7 deletions packages/typedoc-plugin-markdown/src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ import {
import { getKindPlural } from './groups';
import { NavigationItem } from './navigation-item';
import {
indexTemplate,
reflectionMemberTemplate,
reflectionTemplate,
compileTemplates,
getCompiledTemplate,
registerHelpers,
registerPartials,
TemplateName,
} from './render-utils';
import { formatContents } from './utils';

export { TemplateName };

export class MarkdownTheme extends Theme {
allReflectionsHaveOwnDocument!: boolean;
customPartials: Record<string, string>;
customTemplates: Partial<Record<TemplateName, string>>;
entryDocument: string;
entryPoints!: string[];
filenameSeparator!: string;
Expand Down Expand Up @@ -52,6 +56,8 @@ export class MarkdownTheme extends Theme {

// prettier-ignore
this.allReflectionsHaveOwnDocument = this.getOption('allReflectionsHaveOwnDocument',) as boolean;
this.customPartials = this.getOption('customPartials') as MarkdownTheme['customPartials'];
this.customTemplates = this.getOption('customTemplates') as MarkdownTheme['customTemplates'];
this.entryDocument = this.getOption('entryDocument') as string;
this.entryPoints = this.getOption('entryPoints') as string[];
this.filenameSeparator = this.getOption('filenameSeparator') as string;
Expand All @@ -75,8 +81,9 @@ export class MarkdownTheme extends Theme {
[PageEvent.BEGIN]: this.onBeginPage,
});

registerPartials();
registerPartials(this);
registerHelpers(this);
compileTemplates(this);
}

render(page: PageEvent<Reflection>): string {
Expand Down Expand Up @@ -219,7 +226,7 @@ export class MarkdownTheme extends Theme {

getReflectionTemplate() {
return (pageEvent: PageEvent<ContainerReflection>) => {
return reflectionTemplate(pageEvent, {
return getCompiledTemplate(TemplateName.Reflection)(pageEvent, {
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: true,
data: { theme: this },
Expand All @@ -229,7 +236,7 @@ export class MarkdownTheme extends Theme {

getReflectionMemberTemplate() {
return (pageEvent: PageEvent<ContainerReflection>) => {
return reflectionMemberTemplate(pageEvent, {
return getCompiledTemplate(TemplateName.ReflectionMember)(pageEvent, {
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: true,
data: { theme: this },
Expand All @@ -239,7 +246,7 @@ export class MarkdownTheme extends Theme {

getIndexTemplate() {
return (pageEvent: PageEvent<ContainerReflection>) => {
return indexTemplate(pageEvent, {
return getCompiledTemplate(TemplateName.Index)(pageEvent, {
allowProtoMethodsByDefault: true,
allowProtoPropertiesByDefault: true,
data: { theme: this },
Expand Down