Skip to content

Commit

Permalink
feat: Added support for TypDoc watch option
Browse files Browse the repository at this point in the history
  • Loading branch information
tgreyuk committed Feb 21, 2021
1 parent 5a5e70d commit d3a2ab6
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 94 deletions.
12 changes: 6 additions & 6 deletions packages/vuepress-plugin-typedoc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"homepage": "https://github.com/tgreyuk/typedoc-plugin-markdown/tree/master/packages/vuepress-plugin-typedoc",
"peerDependencies": {
"typedoc": ">=0.20.0",
"typedoc": ">=0.20.19",
"typedoc-plugin-markdown": ">=3.4.0"
},
"devDependencies": {
Expand All @@ -25,11 +25,11 @@
"scripts": {
"lint": "eslint ./src --ext .ts",
"prepublishOnly": "npm run lint && npm run build && npm run test",
"build": "rm -rf ./dist && tsc && copyfiles --up 1 ./src/**/*.hbs ./dist/",
"demo:start": "rm -rf ./demo/docs/api && npm run build && cd demo && npm run dev",
"demo:build-and-serve": "npm run build && cd demo && npm run build && npx http-server docs/.vuepress/dist -o",
"demo:init": "rm -rf demo && npx create-vuepress demo",
"test": "jest --colors",
"build": "rm -rf ./dist && tsc",
"test:init": "rm -rf test/site && yarn create vuepress-site test/site",
"test:demo:start": "rm -rf ./test/site/docs/api && yarn run build && cd test/site/docs && npm run dev",
"test:demo:build": "yarn run build && cd demo && yarn run build && npx http-server docs/.vuepress/dist -o",
"test": "jest --colors --updateSnapshot",
"build-and-test": "npm run build && npm run test"
},
"author": "Thomas Grey",
Expand Down
52 changes: 52 additions & 0 deletions packages/vuepress-plugin-typedoc/src/front-matter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { reflectionTitle } from 'typedoc-plugin-markdown/dist/resources/helpers/reflection-title';
import { Component } from 'typedoc/dist/lib/converter/components';
import { RendererComponent } from 'typedoc/dist/lib/output/components';
import { PageEvent } from 'typedoc/dist/lib/output/events';

@Component({ name: 'front-matter' })
export class FrontMatterComponent extends RendererComponent {
initialize() {
super.initialize();
this.listenTo(this.application.renderer, {
[PageEvent.END]: this.onPageEnd,
});
}
onPageEnd(page: PageEvent) {
if (page.contents) {
page.contents = page.contents
.replace(/^/, this.getYamlString(this.getYamlItems(page)) + '\n\n')
.replace(/[\r\n]{3,}/g, '\n\n');
}
}

getYamlString(yamlItems: { [key: string]: string | number | boolean }) {
const yaml = `---
${Object.entries(yamlItems)
.map(
([key, value]) =>
`${key}: ${
typeof value === 'string' ? `"${this.escapeYAMLString(value)}"` : value
}`,
)
.join('\n')}
---`;
return yaml;
}

getYamlItems(page: PageEvent): any {
const pageTitle = this.getTitle(page);

return {
title: pageTitle,
};
}

getTitle(page: PageEvent) {
return reflectionTitle.call(page, false);
}

// prettier-ignore
escapeYAMLString(str: string) {
return str.replace(/([^\\])'/g, '$1\\\'').replace(/\"/g, '');
}
}
80 changes: 80 additions & 0 deletions packages/vuepress-plugin-typedoc/src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as path from 'path';

import {
Application,
MixedDeclarationOption,
ParameterType,
TSConfigReader,
TypeDocReader,
} from 'typedoc';

import { PluginOptions, SidebarOptions } from './types';

/**
* Default plugin options
*/
const DEFAULT_PLUGIN_OPTIONS: PluginOptions = {
out: 'api',
hideBreadcrumbs: true,
plugin: ['typedoc-plugin-markdown'],
sidebar: {
parentCategory: 'none',
fullNames: false,
sidebarFile: 'typedoc-sidebar.js',
sidebarPath: '',
},
watch: false,
};

/**
* Merge default with user options
* @param opts
*/
export const getOptions = (opts: Partial<PluginOptions>): PluginOptions => {
// base options
let options = {
...DEFAULT_PLUGIN_OPTIONS,
...opts,
};
// sidebar
if (opts.sidebar === null) {
options = { ...options, sidebar: null };
} else {
const sidebar = {
...DEFAULT_PLUGIN_OPTIONS.sidebar,
...opts.sidebar,
} as SidebarOptions;
options = {
...options,
sidebar: {
...sidebar,
sidebarPath: path.resolve(sidebar.sidebarFile),
},
};
}
// plugin
if (opts.plugin) {
options = {
...options,
plugin: [...DEFAULT_PLUGIN_OPTIONS.plugin, ...opts.plugin],
};
}
// additional
options = {
...options,
// siteDir,
// outputDirectory: path.resolve(siteDir, options.docsRoot, options.out),
};
return options;
};

export const addOptions = (app: Application) => {
// configure deault typedoc options
app.options.addReader(new TypeDocReader());
app.options.addReader(new TSConfigReader());

app.options.addDeclaration({
name: 'sidebar',
type: ParameterType.Mixed,
} as MixedDeclarationOption);
};
126 changes: 47 additions & 79 deletions packages/vuepress-plugin-typedoc/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,115 +1,83 @@
import {
Application,
ProjectReflection,
TSConfigReader,
TypeDocOptions,
TypeDocReader,
} from 'typedoc';
import { FrontMatterComponent } from 'typedoc-plugin-markdown/dist/components/front-matter';
import { Application, ProjectReflection } from 'typedoc';

import { FrontMatterComponent } from './front-matter';
import { addOptions, getOptions } from './options';
import { getSidebarJson } from './sidebar';
import { PluginOptions } from './types';

const DEFAULT_PLUGIN_OPTIONS: PluginOptions = {
out: 'api',
sidebar: {
parentCategory: 'none',
fullNames: false,
},
};

const app = new Application();

let done = false;
let app: Application;
let project: ProjectReflection | undefined;
const done = false;

export const typedocPlugin = (opts: PluginOptions, ctx: any) => {
const sourceDir = ctx.sourceDir;

const options = { ...DEFAULT_PLUGIN_OPTIONS, ...opts };

const outFolder = options.out ? options.out : 'api';
const out = sourceDir + '/' + outFolder;
const sidebar = options.sidebar;

let project: ProjectReflection | undefined;

// don't re-compile on dev server
// merge default plugin options with user options
const options = getOptions(opts);

return {
name: 'vuepress-plugin-typedoc',
async ready() {
// TypeDoc options
const vuepressOptions = Object.keys(options).reduce((option, key) => {
if (
![...['id'], ...Object.keys(DEFAULT_PLUGIN_OPTIONS)].includes(key)
) {
option[key] = options[key];
}
return option;
}, {});
if (!done) {
app = new Application();

app.options.addReader(new TypeDocReader());
app.options.addReader(new TSConfigReader());
const sourceDir = ctx.sourceDir;
const outputDirectory = sourceDir + '/' + options.out;

// bootstrap
app.bootstrap({
// filtered vuepress options
...vuepressOptions,
hideInPageTOC: true,
hideBreadcrumbs: true,
// TypeDoc plugins
plugin: [
...['typedoc-plugin-markdown'],
...(opts.plugin
? opts.plugin.filter((name) => name !== 'typedoc-plugin-markdown')
: []),
],
} as Partial<TypeDocOptions>);
// add plugin options
addOptions(app);

app.renderer.addComponent(
'frontmatter',
new FrontMatterComponent(app.renderer),
);
// bootstrap typedoc app
app.bootstrap(options);

project = app.convert();
// add frontmatter component to typedoc renderer
app.renderer.addComponent('fm', new FrontMatterComponent(app.renderer));

// if project is undefined typedoc has a problem - error logging will be supplied by typedoc.
if (!project) {
done = true;
return;
}
// add sidebar component to typedoc renderer
/*if (options.sidebar) {
app.renderer.addComponent(
'sidebar',
new SidebarComponent(app.renderer),
);
}*/

await app.generateDocs(project, out);
project = app.convert();

return;
// if project is undefined typedoc has a problem - error logging will be supplied by typedoc.
if (!project) {
return;
}

// generate docs
await app.generateDocs(project, outputDirectory);

// watch pp
if (options.watch) {
app.convertAndWatch(async (project) => {
await app.generateDocs(project, outputDirectory);
});
}
}
},
async enhanceAppFiles() {
if (done || !sidebar) {
if (done || !options.sidebar) {
return;
}

const theme = app.renderer.theme as any;

const navigation = theme.getNavigation(project);

return {
name: 'typedoc-sidebar',
content: `export default ({ siteData, options }) => {
siteData.themeConfig.sidebarDepth = 2;
siteData.themeConfig.sidebarDepth = 0;
siteData.themeConfig.sidebar = Object.assign({},siteData.themeConfig.sidebar,${JSON.stringify(
{
[`/${outFolder}/`]: getSidebarJson(
[`/${options.out}/`]: getSidebarJson(
navigation,
outFolder,
sidebar,
options.out,
options.sidebar,
),
},
)});
}`,
};
},

afterDevServer() {
done = true;
},
};
};
6 changes: 3 additions & 3 deletions packages/vuepress-plugin-typedoc/src/sidebar.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { NavigationItem } from 'typedoc';

import { SidebarOptions } from './types';

export const getSidebarJson = (
navigation: NavigationItem,
outFolder: string,
out: string,
sidebar: SidebarOptions,
) => {
const navJson: any = [];
Expand All @@ -17,7 +18,7 @@ export const getSidebarJson = (
if (navigationItem.url && navigationItem.children?.length === 0) {
const urlKey = navigationItem.url.replace('.md', '');
navJson.push([
urlKey === 'README' ? `/${outFolder}/` : 'modules',
urlKey === 'README' ? `/${out}/` : 'modules',
navigationItem.title,
]);
} else {
Expand All @@ -37,7 +38,6 @@ export const getSidebarJson = (
}
return navJson;
};

const getUrlKey = (url: string) => {
return url.replace('.md', '');
};
10 changes: 6 additions & 4 deletions packages/vuepress-plugin-typedoc/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { Application, ProjectReflection } from 'typedoc';

export interface PluginOptions {
id?: string;
logger?: string;
out?: string;
hideBreadcrumbs: boolean;
out: string;
sidebar?: SidebarOptions | null;
plugin?: string[];
plugin: string[];
watch: boolean;
}

export interface SidebarOptions {
fullNames: boolean;
parentCategory: string;
sidebarFile: string;
sidebarPath: string;
}

export interface LoadedContent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Array [
exports[`(sidebars) should generate default 1`] = `
Object {
"content": "export default ({ siteData, options }) => {
siteData.themeConfig.sidebarDepth = 2;
siteData.themeConfig.sidebarDepth = 0;
siteData.themeConfig.sidebar = Object.assign({},siteData.themeConfig.sidebar,{\\"/api/\\":[[\\"/api/\\",\\"Readme\\"],[\\"modules\\",\\"Exports\\"],{\\"title\\":\\"Namespaces\\",\\"children\\":[[\\"modules/namespacea\\",\\"namespaceA\\"],[\\"modules/namespaceb\\",\\"namespaceB\\"]]},{\\"title\\":\\"Enumerations\\",\\"children\\":[[\\"enums/enumitema\\",\\"enumItemA\\"],[\\"enums/enumitemb\\",\\"enumItemB\\"]]},{\\"title\\":\\"Classes\\",\\"children\\":[[\\"classes/namespacea.somenestedclass\\",\\"SomeNestedClass\\"],[\\"classes/classitema\\",\\"ClassItemA\\"],[\\"classes/classitemb\\",\\"ClassItemB\\"]]},{\\"title\\":\\"Interfaces\\",\\"children\\":[[\\"interfaces/interfaceitema\\",\\"InterfaceItemA\\"],[\\"interfaces/interfaceitemb\\",\\"InterfaceItemB\\"]]}]});
}",
"name": "typedoc-sidebar",
Expand All @@ -24,7 +24,7 @@ Object {
exports[`(sidebars) should generate with parent category and fullNames 1`] = `
Object {
"content": "export default ({ siteData, options }) => {
siteData.themeConfig.sidebarDepth = 2;
siteData.themeConfig.sidebarDepth = 0;
siteData.themeConfig.sidebar = Object.assign({},siteData.themeConfig.sidebar,{\\"/api/\\":[{\\"title\\":\\"Parent Category\\",\\"children\\":[[\\"/api/\\",\\"Readme\\"],[\\"modules\\",\\"Exports\\"],{\\"title\\":\\"Namespaces\\",\\"children\\":[[\\"modules/namespacea\\",\\"namespaceA\\"],[\\"modules/namespaceb\\",\\"namespaceB\\"]]},{\\"title\\":\\"Enumerations\\",\\"children\\":[[\\"enums/enumitema\\",\\"enumItemA\\"],[\\"enums/enumitemb\\",\\"enumItemB\\"]]},{\\"title\\":\\"Classes\\",\\"children\\":[[\\"classes/namespacea.somenestedclass\\",\\"namespaceA.SomeNestedClass\\"],[\\"classes/classitema\\",\\"ClassItemA\\"],[\\"classes/classitemb\\",\\"ClassItemB\\"]]},{\\"title\\":\\"Interfaces\\",\\"children\\":[[\\"interfaces/interfaceitema\\",\\"InterfaceItemA\\"],[\\"interfaces/interfaceitemb\\",\\"InterfaceItemB\\"]]}]}]});
}",
"name": "typedoc-sidebar",
Expand Down

0 comments on commit d3a2ab6

Please sign in to comment.