Skip to content

Commit

Permalink
Introduce --router option
Browse files Browse the repository at this point in the history
Resolves #2111
  • Loading branch information
Gerrit0 committed Dec 15, 2024
1 parent c1ebf14 commit 096df5e
Show file tree
Hide file tree
Showing 12 changed files with 613 additions and 76 deletions.
6 changes: 2 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ title: Changelog

## Beta

- Added a `--router` option which can be used to modify TypeDoc's output folder
structure. This can be extended with plugins.
- TypeDoc will now only create references for symbols re-exported from modules.
- API: Introduced a `Router` which is used for URL creation. `Reflection.url`,
`Reflection.anchor`, and `Reflection.hasOwnDocument` have been removed.

TODO:

- Add option for choosing router

## Unreleased

## v0.27.5 (2024-12-14)
Expand Down
70 changes: 70 additions & 0 deletions site/options/output.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,76 @@ $ typedoc --theme default

Specify the theme name that should be used.

## router

```bash
$ typedoc --router default
```

Specify the router that should be used to determine what files to create for the
HTML output and how to link between pages. Additional routers may be added by
plugins/themes. TypeDoc ships with the following builtin routers:

- **kind** (default) - Creates folders according to their the documented member's kind.
- **kind-dir** - Like **kind**, but renders each page as `index.html` within a directory for the page name. This can be used to make "clean" urls.
- **structure** - Creates folders according to the module structure.
- **structure-dir** - Like **structure**, but renders each page as `index.html` within a directory for the page name. This can be used to make "clean" urls.
- **group** - Creates folders according to the reflection's [`@group`](../tags/group.md).
- **category** - Creates folders according to the reflection's [`@category`](../tags/category.md).

This is easiest to understand with an example. Given the following API:

```ts
export function initialize(): void;
/** @group Opts */
export class Options {}
export namespace TypeDoc {
export const VERSION: string;
}
```

TypeDoc will create a folder structure resembling the following, the common
`assets` folder and `index.html` / `modules.html` files have been omitted for
brevity.

**kind**

```text
docs
├── classes
│ └── Options.html
├── functions
│ └── initialize.html
├── modules
│ └── TypeDoc.html
└── variables
└── TypeDoc.VERSION.html
```

**structure**

```text
├── initialize.html
├── Options.html
├── TypeDoc
│ └── VERSION.html
└── TypeDoc.html
```

**groups**

```text
docs
├── Opts
│ └── Options.html
├── Functions
│ └── initialize.html
├── Namespaces
│ └── TypeDoc.html
└── Variables
└── TypeDoc.VERSION.html
```

## lightHighlightTheme

```bash
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ export {
RendererEvent,
MarkdownEvent,
IndexEvent,
DefaultRouter,
BaseRouter,
KindRouter,
KindDirRouter,
StructureRouter,
StructureDirRouter,
GroupRouter,
CategoryRouter,
PageKind,
} from "./lib/output/index.js";
export type {
Expand Down
3 changes: 3 additions & 0 deletions src/lib/internationalization/locales/en.cts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export = {
could_not_empty_output_directory_0: `Could not empty the output directory {0}`,
could_not_create_output_directory_0: `Could not create the output directory {0}`,
theme_0_is_not_defined_available_are_1: `The theme '{0}' is not defined. The available themes are: {1}`,
router_0_is_not_defined_available_are_1: `The router '{0}' is not defined. The available routers are: {1}`,
reflection_0_links_to_1_but_anchor_does_not_exist_try_2: `{0} links to {1}, but the anchor does not exist. You may have meant:\n\t{2}`,

// entry points
Expand Down Expand Up @@ -232,6 +233,8 @@ export = {
"Specify whether the output JSON should be formatted with tabs",
help_emit: "Specify what TypeDoc should emit, 'docs', 'both', or 'none'",
help_theme: "Specify the theme name to render the documentation with",
help_router:
"Specify the router name to use to determine file names in the documentation",
help_lightHighlightTheme:
"Specify the code highlighting theme in light mode",
help_darkHighlightTheme: "Specify the code highlighting theme in dark mode",
Expand Down
8 changes: 7 additions & 1 deletion src/lib/output/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ export { DefaultThemeRenderContext } from "./themes/default/DefaultThemeRenderCo
export { Slugger } from "./themes/default/Slugger.js";

export {
DefaultRouter,
BaseRouter,
KindRouter,
KindDirRouter,
StructureRouter,
StructureDirRouter,
GroupRouter,
CategoryRouter,
PageKind,
type PageDefinition,
type Router,
Expand Down
55 changes: 41 additions & 14 deletions src/lib/output/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ import {
NavigationPlugin,
SitemapPlugin,
} from "./plugins/index.js";
import { DefaultRouter, type PageDefinition, type Router } from "./router.js";
import {
CategoryRouter,
GroupRouter,
KindDirRouter,
KindRouter,
StructureDirRouter,
StructureRouter,
type PageDefinition,
type Router,
} from "./router.js";

/**
* Describes the hooks available to inject output in the default theme.
Expand Down Expand Up @@ -169,7 +178,12 @@ export interface RendererEvents {
*/
export class Renderer extends AbstractComponent<Application, RendererEvents> {
private routers = new Map<string, new (app: Application) => Router>([
["default", DefaultRouter],
["kind", KindRouter],
["structure", StructureRouter],
["kind-dir", KindDirRouter],
["structure-dir", StructureDirRouter],
["group", GroupRouter],
["category", CategoryRouter],
]);

private themes = new Map<string, new (renderer: Renderer) => Theme>([
Expand Down Expand Up @@ -235,6 +249,10 @@ export class Renderer extends AbstractComponent<Application, RendererEvents> {
@Option("theme")
private accessor themeName!: string;

/** @internal */
@Option("router")
private accessor routerName!: string;

@Option("cleanOutputDir")
private accessor cleanOutputDir!: boolean;

Expand Down Expand Up @@ -317,17 +335,15 @@ export class Renderer extends AbstractComponent<Application, RendererEvents> {
const momento = this.hooks.saveMomento();
this.renderStartTime = Date.now();

// GERRIT: Support user input
this.router = new (this.routers.get("default")!)(this.application);

if (
!this.prepareRouter() ||
!this.prepareTheme() ||
!(await this.prepareOutputDirectory(outputDirectory))
) {
return;
}

const pages = this.router.buildPages(project);
const pages = this.router!.buildPages(project);

const output = new RendererEvent(outputDirectory, project, pages);
this.trigger(RendererEvent.BEGIN, output);
Expand Down Expand Up @@ -402,14 +418,25 @@ export class Renderer extends AbstractComponent<Application, RendererEvents> {
}
}

/**
* Ensure that a theme has been setup.
*
* If a the user has set a theme we try to find and load it. If no theme has
* been specified we load the default theme.
*
* @returns TRUE if a theme has been setup, otherwise FALSE.
*/
private prepareRouter(): boolean {
if (!this.theme) {
const ctor = this.routers.get(this.routerName);
if (!ctor) {
this.application.logger.error(
this.application.i18n.router_0_is_not_defined_available_are_1(
this.routerName,
[...this.routers.keys()].join(", "),
),
);
return false;
} else {
this.router = new ctor(this.application);
}
}

return true;
}

private prepareTheme(): boolean {
if (!this.theme) {
const ctor = this.themes.get(this.themeName);
Expand Down
Loading

0 comments on commit 096df5e

Please sign in to comment.