Skip to content

Commit

Permalink
feat(plugin-pages): add plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
GerkinDev committed Feb 26, 2022
1 parent 37521ed commit 2434d33
Show file tree
Hide file tree
Showing 40 changed files with 18,460 additions and 0 deletions.
8 changes: 8 additions & 0 deletions packages/typedoc-plugin-pages/.eslintrc.js
@@ -0,0 +1,8 @@
module.exports = {
root: true,
extends: [ '@knodes/eslint-config/ts' ],
env: { node: true },
parserOptions: {
project: [ `${__dirname}/tsconfig.build.json`, `${__dirname}/tsconfig.spec.json` ],
},
};
4 changes: 4 additions & 0 deletions packages/typedoc-plugin-pages/.gitignore
@@ -0,0 +1,4 @@
# Integration test artifacts
test/integration/project/pagesconfig.json
test/integration/project/typedoc.json
test/integration/project/build
31 changes: 31 additions & 0 deletions packages/typedoc-plugin-pages/README.md
@@ -0,0 +1,31 @@
# typedoc-plugin-pages

> A TypeDoc plugin that lets you integrate your own pages into the documentation output
[![npm](https://img.shields.io/npm/v/typedoc-plugin-pages?color=brightgreen)](https://www.npmjs.com/package/typedoc-plugin-pages)

This plugin is based on [typedoc-plugin-loopingz-pages](https://github.com/loopingz/typedoc-plugin-loopingz-pages), which is in turn a fork of [typedoc-plugin-pages](https://github.com/mipatterson/typedoc-plugin-pages). Integrating it in this monorepo should (I hope) make easier maintainance.

## Compatibility

This plugin version should match TypeDoc `major.minor.x` for compatibility.

## Features

- 🔍 Search integration
- 🔗 Interpage hyperlinks
- 🎨 Integrated Theme
- 📑 Reflection Navigation Title Customization
- ✏️ Integrated spell-checking (coming soon...)

## Demo

You can view a live demo of the original `typedoc-plugin-pages` [here](https://mipatterson.github.io/typedoc-plugin-pages/).

## Usage

See the [Quick Start](https://mipatterson.github.io/typedoc-plugin-pages/pages/Getting%20Started/quick-start.html) guide to get started.

## Supported Versions of TypeDoc

This plugin is designed to work with as many versions of TypeDoc as possible. It has been tested with the following versions `0.16.5` through `0.17.6`. If you are reporting an issue, please include the version of TypeDoc you are using the plugin with.
@@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Real behavior should render correctly 1`] = `
"<!DOCTYPE html><html class=\\"default\\"><head><meta charSet=\\"utf-8\\"/><meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"/><title>Test | @knodes/typedoc-plugin-pages</title><meta name=\\"description\\" content=\\"Documentation for @knodes/typedoc-plugin-pages\\"/><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"/><link rel=\\"stylesheet\\" href=\\"../assets/style.css\\"/><link rel=\\"stylesheet\\" href=\\"../assets/highlight.css\\"/><script async src=\\"../assets/search.js\\" id=\\"search-script\\"></script></head><body><script>document.body.classList.add(localStorage.getItem(\\"tsd-theme\\") || \\"os\\")</script><header><div class=\\"tsd-page-toolbar\\"><div class=\\"container\\"><div class=\\"table-wrap\\"><div class=\\"table-cell\\" id=\\"tsd-search\\" data-base=\\"..\\"><div class=\\"field\\"><label for=\\"tsd-search-field\\" class=\\"tsd-widget search no-caption\\">Search</label><input type=\\"text\\" id=\\"tsd-search-field\\"/></div><ul class=\\"results\\"><li class=\\"state loading\\">Preparing search index...</li><li class=\\"state failure\\">The search index is not available</li></ul><a href=\\"../index.html\\" class=\\"title\\">@knodes/typedoc-plugin-pages</a></div><div class=\\"table-cell\\" id=\\"tsd-widgets\\"><div id=\\"tsd-filter\\"><a href=\\"#\\" class=\\"tsd-widget options no-caption\\" data-toggle=\\"options\\">Options</a><div class=\\"tsd-filter-group\\"><div class=\\"tsd-select\\" id=\\"tsd-filter-visibility\\"><span class=\\"tsd-select-label\\">All</span><ul class=\\"tsd-select-list\\"><li data-value=\\"public\\">Public</li><li data-value=\\"protected\\">Public/Protected</li><li data-value=\\"private\\" class=\\"selected\\">All</li></ul></div> <input type=\\"checkbox\\" id=\\"tsd-filter-inherited\\" checked/><label class=\\"tsd-widget\\" for=\\"tsd-filter-inherited\\">Inherited</label><input type=\\"checkbox\\" id=\\"tsd-filter-externals\\" checked/><label class=\\"tsd-widget\\" for=\\"tsd-filter-externals\\">Externals</label></div></div><a href=\\"#\\" class=\\"tsd-widget menu no-caption\\" data-toggle=\\"menu\\">Menu</a></div></div></div></div><div class=\\"tsd-page-title\\"><div class=\\"container\\"><ul class=\\"tsd-breadcrumb\\"><li><a href=\\"../modules.html\\">@knodes/typedoc-plugin-pages</a></li><li><a href=\\"Test.html\\">Test</a></li></ul><h1>Class Test</h1></div></div></header><div class=\\"container container-main\\"><div class=\\"row\\"><div class=\\"col-8 col-content\\"><section class=\\"tsd-panel tsd-comment\\"><div class=\\"tsd-comment tsd-typography\\"><div class=\\"lead\\">
<p>A test class</p>
</div><div><p><a href=\\"../pages/foo/bar.html\\">⇒ Bar</a></p>
</div></div></section><section class=\\"tsd-panel tsd-hierarchy\\"><h3>Hierarchy</h3><ul class=\\"tsd-hierarchy\\"><li><span class=\\"target\\">Test</span></li></ul></section><section class=\\"tsd-panel-group tsd-index-group\\"><h2>Index</h2><section class=\\"tsd-panel tsd-index-panel\\"><div class=\\"tsd-index-content\\"><section class=\\"tsd-index-section \\"><h3>Constructors</h3><ul class=\\"tsd-index-list\\"><li class=\\"tsd-kind-constructor tsd-parent-kind-class\\"><a href=\\"Test.html#constructor\\" class=\\"tsd-kind-icon\\">constructor</a></li></ul></section></div></section></section><section class=\\"tsd-panel-group tsd-member-group \\"><h2>Constructors</h2><section class=\\"tsd-panel tsd-member tsd-kind-constructor tsd-parent-kind-class\\"><a id=\\"constructor\\" class=\\"tsd-anchor\\"></a><h3 class=\\"tsd-anchor-link\\">constructor<a href=\\"#constructor\\" aria-label=\\"Permalink\\" class=\\"tsd-anchor-icon\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" class=\\"icon icon-tabler icon-tabler-link\\" viewBox=\\"0 0 24 24\\" stroke-width=\\"2\\" stroke=\\"currentColor\\" fill=\\"none\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\"><path stroke=\\"none\\" d=\\"M0 0h24v24H0z\\" fill=\\"none\\"></path><path d=\\"M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5\\"></path><path d=\\"M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5\\"></path></svg></a></h3><ul class=\\"tsd-signatures tsd-kind-constructor tsd-parent-kind-class\\"><li class=\\"tsd-signature tsd-kind-icon\\">new <wbr/>Test<span class=\\"tsd-signature-symbol\\">(</span><span class=\\"tsd-signature-symbol\\">)</span><span class=\\"tsd-signature-symbol\\">: </span><a href=\\"Test.html\\" class=\\"tsd-signature-type\\" data-tsd-kind=\\"Class\\">Test</a></li></ul><ul class=\\"tsd-descriptions\\"><li class=\\"tsd-description\\"><h4 class=\\"tsd-returns-title\\">Returns <a href=\\"Test.html\\" class=\\"tsd-signature-type\\" data-tsd-kind=\\"Class\\">Test</a></h4></li></ul></section></section></div><div class=\\"col-4 col-menu menu-sticky-wrap menu-highlight\\"><nav class=\\"tsd-navigation primary\\"><ul><li class=\\"\\"><a href=\\"../modules.html\\">Exports</a></li><li class=\\" undefined\\"><a> <wbr/>Qux</a></li><li class=\\" undefined\\"><a href=\\"../pages/qux/baaz.html\\">⇒ <wbr/>Baaz</a></li><li class=\\" undefined\\"><a href=\\"../pages/foo/index.html\\"> <wbr/>Foo</a></li><li class=\\" undefined\\"><a href=\\"../pages/foo/bar.html\\">⇒ <wbr/>Bar</a></li></ul></nav><nav class=\\"tsd-navigation secondary menu-sticky\\"><ul><li class=\\"current tsd-kind-class\\"><a href=\\"Test.html\\" class=\\"tsd-kind-icon\\">Test</a><ul><li class=\\"tsd-kind-constructor tsd-parent-kind-class\\"><a href=\\"Test.html#constructor\\" class=\\"tsd-kind-icon\\">constructor</a></li></ul></li></ul></nav></div></div></div><footer class=\\"with-border-bottom\\"><div class=\\"container\\"><h2>Legend</h2><div class=\\"tsd-legend-group\\"><ul class=\\"tsd-legend\\"><li class=\\"tsd-kind-constructor tsd-parent-kind-class\\"><span class=\\"tsd-kind-icon\\">Constructor</span></li></ul></div><h2>Settings</h2><p>Theme <select id=\\"theme\\"><option value=\\"os\\">OS</option><option value=\\"light\\">Light</option><option value=\\"dark\\">Dark</option></select></p></div></footer><div class=\\"container tsd-generator\\"><p>Generated using <a href=\\"https://typedoc.org/\\" target=\\"_blank\\">TypeDoc</a></p></div><div class=\\"overlay\\"></div><script src=\\"../assets/main.js\\"></script></body></html>"
`;
113 changes: 113 additions & 0 deletions packages/typedoc-plugin-pages/__tests__/integration/simple.spec.ts
@@ -0,0 +1,113 @@
import { readFile } from 'fs/promises';
import { resolve } from 'path';

import { JSDOM } from 'jsdom';
import { escapeRegExp } from 'lodash';
import { Application, ArgumentsReader, TSConfigReader, TypeDocOptions, TypeDocReader } from 'typedoc';

const rootDir = resolve( __dirname, '../mock-fs/simple' );
jest.setTimeout( 30000 );
beforeEach( () => {
process.chdir( rootDir );
} );
const checkFile = async ( ...args: [...paths: string[], withContent: ( text: string ) => Promise<void> | void] ) => {
const fullPath = resolve( ...args.slice( 0, -1 ) as string[] );
const content = await readFile( fullPath, 'utf-8' );
const cb = args[args.length - 1] as ( text: string ) => Promise<void> | void;
await cb( content );
};
const setName = ( fn: any, name: string ) => {
Object.defineProperty( fn, 'toString', { value: () => name, writable: true } );
return fn;
};
const passes = <T extends unknown[]>( assert: ( ...args: T ) => void ) => setName( ( ...args: T ) => {
try{
assert( ...args );
return true;
} catch( _e ){
return false;
}
}, `passes(${assert.toString()})` );
const menuItemMatcher = ( text: string, current: boolean, link: string | null ) => {
const reg = new RegExp( `^(\\s|<wbr\\s*\\/>|⇒)*${escapeRegExp( text )}$` );
return expect.toSatisfy( setName(
passes( ( x: HTMLLIElement ) => {
expect( x ).toHaveTextContent( reg, { normalizeWhitespace: true } );
if( current ){
expect( x ).toHaveClass( 'current' );
} else {
expect( x ).not.toHaveClass( 'current' );
}
const a = x.querySelector( 'a' );
expect( a ).toBeTruthy();
if( link === null ){
expect( a ).not.toHaveAttribute( 'href' );
} else {
expect( a ).toHaveAttribute( 'href', link );
}
} ),
`menuItemMatcher(text: ${JSON.stringify( text )}, current: ${JSON.stringify( current )}, link: ${JSON.stringify( link )})` ) );
};
describe( 'Real behavior', () => {
it( 'should render correctly', async () => {
const app = new Application();
app.options.addReader( new ArgumentsReader( 0, [] ) );
app.options.addReader( new TypeDocReader() );
app.options.addReader( new TSConfigReader() );
const baseOptions: Partial<TypeDocOptions> = {
entryPoints: [ resolve( rootDir, './src/test.ts' ) ],
tsconfig: resolve( rootDir, './tsconfig.json' ),
treatWarningsAsErrors: true,
plugin: [ resolve( __dirname, '../../src/index' ) ],
};
app.bootstrap( { ...baseOptions } as any );
const project = app.convert()!;
app.validate( project );
const docsDir = resolve( rootDir, './docs' );
await app.generateDocs( project, docsDir );
await checkFile( docsDir, 'pages/foo/index.html', c => {
expect( c ).toContain( '<h2>Some foo</h2>' );
const dom = new JSDOM( c );
const menu = dom.window.document.querySelector( '.tsd-navigation.primary' )!;
const items = Array.from( menu.querySelectorAll( 'li' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Foo', true, 'index.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Bar', false, 'bar.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Qux', false, null ) );
expect( items ).toContainEqual( menuItemMatcher( 'Baaz', false, '../qux/baaz.html' ) );
} );
await checkFile( docsDir, 'pages/foo/bar.html', c => {
expect( c ).toContain( '<h2>Some bar</h2>' );
const dom = new JSDOM( c );
const menu = dom.window.document.querySelector( '.tsd-navigation.primary' )!;
const items = Array.from( menu.querySelectorAll( 'li' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Foo', true, 'index.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Bar', true, 'bar.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Qux', false, null ) );
expect( items ).toContainEqual( menuItemMatcher( 'Baaz', false, '../qux/baaz.html' ) );
} );
await checkFile( docsDir, 'pages/qux/baaz.html', c => {
expect( c ).toContain( '<h2>Some baaz</h2>' );
const dom = new JSDOM( c );
const menu = dom.window.document.querySelector( '.tsd-navigation.primary' )!;
const items = Array.from( menu.querySelectorAll( 'li' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Foo', false, '../foo/index.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Bar', false, '../foo/bar.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Qux', true, null ) );
expect( items ).toContainEqual( menuItemMatcher( 'Baaz', true, 'baaz.html' ) );
} );
await checkFile( rootDir, 'docs/classes/Test.html', c => {
const dom = new JSDOM( c );
const menu = dom.window.document.querySelector( '.tsd-navigation.primary' )!;
const items = Array.from( menu.querySelectorAll( 'li' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Foo', false, '../pages/foo/index.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Bar', false, '../pages/foo/bar.html' ) );
expect( items ).toContainEqual( menuItemMatcher( 'Qux', false, null ) );
expect( items ).toContainEqual( menuItemMatcher( 'Baaz', false, '../pages/qux/baaz.html' ) );
const link = dom.window.document.querySelector( '.tsd-comment a' );
expect( link ).toBeTruthy();
expect( link ).toHaveTextContent( /^(\s|<wbr\s*\/>|⇒)*Bar$/ );
expect( link ).toHaveAttribute( 'href', '../pages/foo/bar.html' );
expect( c ).toMatchSnapshot();
} );
} );
} );
Empty file.
@@ -0,0 +1 @@
## Some foo
@@ -0,0 +1 @@
## Some bar
@@ -0,0 +1 @@
## Some baaz
@@ -0,0 +1,11 @@
/** @type {import('../../../src').IPluginOptions} */
module.exports = {
pages: [
{ title: 'Foo', source: 'foo.md', children: [
{ title: 'Bar', source: 'bar.md' },
] },
{ title: 'Qux', childrenDir: 'qux', children: [
{ title: 'Baaz', source: 'baaz.md' },
] },
],
};
@@ -0,0 +1,8 @@
/**
* A test class
*
* {@page foo/bar.md}
*/
export class Test {

}
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS"
},
"include": ["**/*.ts"]
}
12 changes: 12 additions & 0 deletions packages/typedoc-plugin-pages/jest.config.js
@@ -0,0 +1,12 @@
const base = require( '../../jest.config.base' );
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
...base,
projects: [
base.projects[0],
{
...base.projects[1],
setupFilesAfterEnv: [ ...base.projects[1].setupFilesAfterEnv, '@testing-library/jest-dom' ],
},
],
};

0 comments on commit 2434d33

Please sign in to comment.