Skip to content

Commit

Permalink
feat(remark): refactor remark-toc implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
tgreyuk committed Dec 22, 2024
1 parent fe0ef4e commit 31d39ee
Show file tree
Hide file tree
Showing 18 changed files with 155 additions and 97 deletions.
26 changes: 14 additions & 12 deletions devtools/packages/prebuild-options/tasks/generate-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@ import * as path from 'path';
import * as prettier from 'prettier';
import { DeclarationOption, ParameterType } from 'typedoc';

const ignoreTypes = [
'textContentMappings',
'remarkPlugins',
'remarkStringifyOptions',
];
const ignoreTypes = ['textContentMappings', 'remarkStringifyOptions'];

export async function generateOptionsModels(docsConfig: DocsConfig) {
const optionsConfig = await import(docsConfig.declarationsPath as string);
Expand All @@ -34,7 +30,7 @@ async function writeTypeDocDeclarations(
(option as any).type === ParameterType.Mixed &&
(option as any).defaultValue,
)
.map(([name, option]) => capitalize(name));
.map(([name, option]) => capitalize(name, false));

const out: string[] = [];

Expand Down Expand Up @@ -126,17 +122,20 @@ ${name}: ${getType(name, option, true)};`,
?.filter(([name]) => !ignoreTypes.includes(name))
.map(([name, option]) => {
return `
/**
* ${getComments(name)}
*/
export interface ${capitalize(name)} {
${
// this is a hack need to fix properly
name === 'remarkPlugins'
? 'export type RemarkPlugin = string | [string, Record<string, any>];'
: `export interface ${capitalize(name)} {
${Object.entries(option.defaultValue as any)
.map(
([key, value]) =>
`'${key}'${value === undefined ? '?' : ''}: ${getValueType(key, value)}`,
)
.join(';')}
}
}`
}
`;
})
.join('\n')}
Expand Down Expand Up @@ -255,6 +254,9 @@ function getObjectType(name: string) {
return 'string';
}

function capitalize(str: string) {
function capitalize(str: string, includeArray = true) {
if (str.endsWith('s')) {
str = `${str.slice(0, -1)}${includeArray ? '[]' : ''}`;
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion packages/typedoc-plugin-remark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"prepublishOnly": "npm run lint && npm run build",
"prebuild": "rm -rf dist && prebuild-options",
"build": "tsc",
"build-and-run": "npm run build && npm run pretest",
"pretest": "tsx ./test/__scripts__/prepare.ts",
"test": "jest",
"test:update": "npm run build && npm run test -- -u"
Expand All @@ -31,7 +32,8 @@
"remark-frontmatter": "^5.0.0",
"remark-gfm": "^4.0.0",
"remark-mdx": "^3.1.0",
"to-vfile": "^8.0.0"
"to-vfile": "^8.0.0",
"unist-util-visit": "^5.0.0"
},
"peerDependencies": {
"typedoc-plugin-markdown": ">=4.3.0"
Expand Down
3 changes: 2 additions & 1 deletion packages/typedoc-plugin-remark/src/_typedoc.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// THIS FILE IS AUTO GENERATED FROM THE OPTIONS CONFIG. DO NOT EDIT DIRECTLY.
import { ManuallyValidatedOption } from 'typedoc';
import { RemarkPlugin } from './types/options.js';
declare module 'typedoc' {
export interface TypeDocOptionMap {
defaultRemarkPlugins: { gfm: boolean; frontmatter: boolean; mdx: boolean };
remarkPlugins: ManuallyValidatedOption<Partial<any>>;
remarkPlugins: ManuallyValidatedOption<RemarkPlugin[]>;
remarkStringifyOptions: ManuallyValidatedOption<Record<string, any>>;
}
}
59 changes: 0 additions & 59 deletions packages/typedoc-plugin-remark/src/helpers/add-toc.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RemarkPlugin } from '../types/remark-plugin.js';
import { RemarkPlugin } from '../types/options.js';

export function getDefaultPlugins(defaultPluginsFlag: {
gfm: boolean;
Expand Down
38 changes: 25 additions & 13 deletions packages/typedoc-plugin-remark/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
* @module
*/

import * as path from 'path';
import { Application, DeclarationOption, RendererEvent } from 'typedoc';
import { MarkdownPageEvent } from 'typedoc-plugin-markdown';
import { addTableOfContents } from './helpers/add-toc.js';
import { fileURLToPath } from 'url';
import { getDefaultPlugins } from './helpers/get-default-plugins.js';
import * as options from './options/declarations.js';
import { parse } from './parse.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export function load(app: Application) {
Object.entries(options).forEach(([name, option]) => {
app.options.addDeclaration({
Expand All @@ -19,28 +22,37 @@ export function load(app: Application) {
} as DeclarationOption);
});

app.renderer.on(MarkdownPageEvent.END, (event: MarkdownPageEvent) => {
const remarkPlugins = app.options.getValue('remarkPlugins') as [];
const remarkPluginsNames = remarkPlugins.map((plugin) =>
Array.isArray(plugin) ? plugin[0] : plugin,
) as string[];

if (remarkPluginsNames.includes('remark-toc')) {
addTableOfContents(event, remarkPlugins, remarkPluginsNames, app);
}
});

app.renderer.postRenderAsyncJobs.push(async (output: RendererEvent) => {
const remarkPlugins = app.options.getValue('remarkPlugins') as [];
const defaultPlugins = getDefaultPlugins(
app.options.getValue('defaultRemarkPlugins'),
);
const userPlugins = app.options.getValue('remarkPlugins') as string[];
const remarkStringifyOptions = app.options.getValue(
'remarkStringifyOptions',
);
const remarkPluginsNames = remarkPlugins.map((plugin) =>
Array.isArray(plugin) ? plugin[0] : plugin,
) as string[];

if (output.urls?.length) {
await Promise.all(
output.urls?.map(async (urlMapping) => {
if (remarkPluginsNames.includes('remark-toc')) {
const tocPluginIndex = remarkPluginsNames.findIndex(
(name) => name === 'remark-toc',
);
const tocPlugin = remarkPlugins[tocPluginIndex];
const tocOptions = Array.isArray(tocPlugin) ? tocPlugin[1] : {};
defaultPlugins.push([
path.join(__dirname, 'plugins', 'add-toc.js'),
{
reflection: urlMapping.model,
typedocOptions: app.options,
tocOptions,
},
]);
}
const filePath = `${output.outputDirectory}/${urlMapping.url}`;
return await parse(
filePath,
Expand Down
1 change: 0 additions & 1 deletion packages/typedoc-plugin-remark/src/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@
* @module
*/

export * as helpers from '../helpers/add-toc.js';
export * as declarations from './declarations.js';
2 changes: 1 addition & 1 deletion packages/typedoc-plugin-remark/src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as path from 'path';
import { remark } from 'remark';
import { read, writeSync } from 'to-vfile';
import { RemarkPlugin } from './types/remark-plugin.js';
import { RemarkPlugin } from './types/options.js';

export async function parse(
filePath: string,
Expand Down
62 changes: 62 additions & 0 deletions packages/typedoc-plugin-remark/src/plugins/add-toc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Heading } from 'mdast';
import { DeclarationReflection, Options, ReflectionKind } from 'typedoc';
import { Node } from 'unist';
import { visit } from 'unist-util-visit';

/**
* This plugin is used internally to add a toc heading as required to be present in the page when using the remark-toc plugin.
*/
export default function (options: any) {
const { reflection, typedocOptions, tocOptions } = options;

return (tree: Node) => {
// If the current page is already an index page, do nothing.
if (isIndexPage(reflection, typedocOptions)) {
return tree;
}
visit(
tree,
'heading',
(node: Heading, index, parent: { children: Node[] }) => {
if (node.depth === 2) {
const newHeading: Heading = {
type: 'heading',
depth: 2,
children: [
{
type: 'text',
value: tocOptions?.heading || 'Contents',
},
],
};
parent?.children.splice(index, 0, newHeading);
return false;
}
},
);
};
}

/**
* Determine if the current page is already an index page.
* - Reflection is a project and all children are modules.
* - Reflection is a module and outputFileStrategy is equal to "members".
*/
function isIndexPage(
reflection: DeclarationReflection,
typedocOptions: Options,
) {
if (
reflection.kind === ReflectionKind.Project &&
reflection.children?.every((child) => child.kind === ReflectionKind.Module)
) {
return true;
}
if (
[ReflectionKind.Project, ReflectionKind.Module].includes(reflection.kind) &&
typedocOptions?.getValue('outputFileStrategy') === 'members'
) {
return true;
}
return false;
}
4 changes: 3 additions & 1 deletion packages/typedoc-plugin-remark/src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export interface PluginOptions {
/**
* An array of remark plugin names to be executed.
*/
remarkPlugins: any;
remarkPlugins: RemarkPlugin[];

/**
* Custom options for the remark-stringify plugin.
*/
remarkStringifyOptions: Record<string, any>;
}

export type RemarkPlugin = string | [string, Record<string, any>];
1 change: 0 additions & 1 deletion packages/typedoc-plugin-remark/src/types/remark-plugin.ts

This file was deleted.

3 changes: 2 additions & 1 deletion packages/typedoc-plugin-remark/test/__scripts__/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ fs.removeSync(`./test/out`);
const fixtures = [
{ options: 'typedoc.modules.json', outDir: 'modules' },
{ options: 'typedoc.members.json', outDir: 'members' },
{ options: 'typedoc.toc.json', outDir: 'toc' },
{ options: 'typedoc.globals.json', outDir: 'globals' },
{ options: 'typedoc.globals-notoc.json', outDir: 'globals-notoc' },
{ options: 'typedoc.globals-mdx.json', outDir: 'globals-mdx' },
{ options: 'typedoc.globals-notoc.json', outDir: 'globals-notoc' },
];

// write fixtures
Expand Down
31 changes: 31 additions & 0 deletions packages/typedoc-plugin-remark/test/stubs/module-toc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @module
*/

export const someVariable = true;

export const someFunction = (a: string, b: number) => {
return true;
};

export class Class {
a: string;
b: number;
}

export type SimpleTypeAlias = string | boolean;

export type TypeAliasWithTypeDeclaration = {
a: string;
b: number;
};

export interface Interface {
a: string;
b: number;
}

export enum Enum {
A,
B,
}
2 changes: 1 addition & 1 deletion packages/typedoc-plugin-remark/test/typedoc.base.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"entryPoints": ["./stubs/*.ts"],
"entryPoints": ["./stubs/module-1.ts", "./stubs/module-2.ts"],
"tsconfig": "./stubs/tsconfig.json",
"out": "./out",
"plugin": [
Expand Down
1 change: 0 additions & 1 deletion packages/typedoc-plugin-remark/test/typedoc.members.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"extends": "./typedoc.base.json",
"entryPoints": ["./stubs/*.ts"],
"remarkPlugins": ["remark-github", ["remark-toc", { "maxDepth": 3 }]],
"defaultRemarkPlugins": {
"mdx": false
Expand Down
Loading

0 comments on commit 31d39ee

Please sign in to comment.