├── .changeset
├── README.md
└── config.json
├── .github
└── workflows
│ ├── autofix.yml
│ ├── integration.yml
│ └── release.yml
├── .gitignore
├── .node-version
├── .prettierignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── docs
├── README.md
├── astro.config.ts
├── package.json
├── public
│ └── favicon.svg
├── src
│ ├── assets
│ │ └── showcase
│ │ │ ├── bunshi.org.png
│ │ │ ├── chord.vercel.app.png
│ │ │ ├── dgmjs.dev.png
│ │ │ ├── discord-components.js.org.png
│ │ │ ├── docs.seyfert.dev.png
│ │ │ ├── docs.shepherdpro.com.png
│ │ │ ├── echo-d.net.png
│ │ │ ├── eddienubes.github.io-sagetest.png
│ │ │ ├── fevol.github.io-obsidian-typings.png
│ │ │ ├── lilybird.didas.dev.png
│ │ │ └── vinoth.info-react-sketch-canvas.png
│ ├── content.config.ts
│ ├── content
│ │ └── docs
│ │ │ ├── configuration.md
│ │ │ ├── getting-started.mdx
│ │ │ ├── guides
│ │ │ ├── frontmatter.mdx
│ │ │ └── multiple-instances.md
│ │ │ ├── index.mdx
│ │ │ └── resources
│ │ │ ├── showcase.mdx
│ │ │ └── starlight.mdx
│ ├── env.d.ts
│ └── styles
│ │ └── custom.css
└── tsconfig.json
├── eslint.config.mjs
├── example
├── README.md
├── astro.config.ts
├── astro.multiple-entrypoints.config.ts
├── astro.multiple-plugins.config.ts
├── astro.packages-entrypoints.config.ts
├── package.json
├── public
│ └── favicon.svg
├── src
│ ├── assets
│ │ └── houston.webp
│ ├── content.config.ts
│ ├── content
│ │ └── docs
│ │ │ ├── .gitignore
│ │ │ ├── guides
│ │ │ └── example.md
│ │ │ └── index.mdx
│ ├── env.d.ts
│ ├── plugins
│ │ └── frontmatter.js
│ └── styles
│ │ └── custom.css
└── tsconfig.json
├── fixtures
├── basics
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── Bar.ts
│ │ ├── Baz.ts
│ │ ├── Foo.ts
│ │ ├── functions.ts
│ │ ├── index.ts
│ │ ├── module.ts
│ │ ├── noDocs.ts
│ │ ├── noExports.ts
│ │ ├── shared.ts
│ │ └── types.ts
│ └── tsconfig.json
└── packages
│ ├── package.json
│ ├── packages
│ ├── bar
│ │ ├── package.json
│ │ ├── src
│ │ │ ├── functions.ts
│ │ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── typedoc.json
│ └── foo
│ │ ├── package.json
│ │ ├── src
│ │ ├── functions.ts
│ │ └── index.ts
│ │ ├── tsconfig.json
│ │ └── typedoc.json
│ ├── tsconfig.base.json
│ └── tsconfig.json
├── package.json
├── packages
└── starlight-typedoc
│ ├── .npmignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── eslint.config.mjs
│ ├── index.ts
│ ├── libs
│ ├── logger.ts
│ ├── markdown.ts
│ ├── starlight.ts
│ ├── theme.ts
│ └── typedoc.ts
│ ├── package.json
│ ├── playwright.config.ts
│ ├── tests
│ ├── e2e
│ │ ├── basics
│ │ │ ├── asides.test.ts
│ │ │ ├── content.test.ts
│ │ │ ├── pagination.test.ts
│ │ │ ├── sidebar.test.ts
│ │ │ └── slug.test.ts
│ │ ├── fixtures
│ │ │ └── DocPage.ts
│ │ ├── packages
│ │ │ ├── content.test.ts
│ │ │ └── sidebar.test.ts
│ │ ├── plugins
│ │ │ └── sidebar.test.ts
│ │ └── test.ts
│ └── unit
│ │ ├── sidebar.test.ts
│ │ └── typedoc.test.ts
│ └── vitest.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
3 | "changelog": [
4 | "@changesets/changelog-github",
5 | { "repo": "HiDeoo/starlight-typedoc" }
6 | ],
7 | "commit": false,
8 | "access": "public",
9 | "baseBranch": "main",
10 | "updateInternalDependencies": "patch",
11 | "ignore": ["starlight-typedoc-docs", "starlight-typedoc-example", "@starlight-typedoc/fixtures-*"]
12 | }
13 |
--------------------------------------------------------------------------------
/.github/workflows/autofix.yml:
--------------------------------------------------------------------------------
1 | name: autofix.ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 | workflow_call:
11 |
12 | permissions:
13 | contents: read
14 |
15 | concurrency:
16 | cancel-in-progress: true
17 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
18 |
19 | jobs:
20 | autofix:
21 | name: Format code
22 | runs-on: ubuntu-latest
23 | steps:
24 | - name: Checkout
25 | uses: actions/checkout@v4
26 |
27 | - name: Install pnpm
28 | uses: pnpm/action-setup@v4
29 | with:
30 | version: 8.6.1
31 |
32 | - name: Install Node.js
33 | uses: actions/setup-node@v4
34 | with:
35 | cache: pnpm
36 | node-version: 18
37 |
38 | - name: Install dependencies
39 | run: pnpm install
40 |
41 | - name: Format code
42 | run: pnpm format
43 |
44 | - name: Run autofix
45 | uses: autofix-ci/action@ff86a557419858bb967097bfc916833f5647fa8c
46 | with:
47 | fail-fast: false
48 |
--------------------------------------------------------------------------------
/.github/workflows/integration.yml:
--------------------------------------------------------------------------------
1 | name: integration
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 | workflow_call:
11 |
12 | concurrency:
13 | cancel-in-progress: true
14 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
15 |
16 | jobs:
17 | lint_test:
18 | name: Lint & Test
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v4
23 |
24 | - name: Install pnpm
25 | uses: pnpm/action-setup@v4
26 | with:
27 | version: 8.6.1
28 |
29 | - name: Install Node.js
30 | uses: actions/setup-node@v4
31 | with:
32 | cache: pnpm
33 | node-version: 18
34 |
35 | - name: Install dependencies
36 | run: pnpm install
37 |
38 | - name: Generates docs TypeScript types
39 | run: pnpm astro sync
40 | working-directory: docs
41 |
42 | - name: Generates example TypeScript types
43 | run: pnpm astro sync
44 | working-directory: example
45 |
46 | - name: Lint
47 | run: pnpm lint
48 |
49 | - name: Test
50 | run: pnpm test
51 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | changeset:
10 | name: Changeset
11 | if: ${{ github.repository_owner == 'hideoo' }}
12 | runs-on: ubuntu-latest
13 | permissions:
14 | contents: write
15 | id-token: write
16 | pull-requests: write
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v4
20 | with:
21 | fetch-depth: 0
22 |
23 | - name: Install pnpm
24 | uses: pnpm/action-setup@v4
25 | with:
26 | version: 8.6.1
27 |
28 | - name: Install Node.js
29 | uses: actions/setup-node@v4
30 | with:
31 | cache: pnpm
32 | node-version: 18
33 |
34 | - name: Install dependencies
35 | run: pnpm install
36 |
37 | - name: Create Release Pull Request or Publish
38 | uses: changesets/action@v1
39 | with:
40 | version: pnpm run version
41 | publish: pnpm changeset publish
42 | commit: 'ci: release'
43 | title: 'ci: release'
44 | env:
45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
46 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .astro
2 | .DS_Store
3 | .eslintcache
4 | .idea
5 | .next
6 | .turbo
7 | .vercel
8 | .vscode/*
9 | !.vscode/extensions.json
10 | !.vscode/launch.json
11 | !.vscode/settings.json
12 | !.vscode/tasks.json
13 | .vscode-test
14 | .vscode-test-web
15 | *.local
16 | *.log
17 | *.pem
18 | *.tsbuildinfo
19 | build
20 | coverage
21 | dist
22 | dist-multiple-entrypoints
23 | dist-ssr
24 | lerna-debug.log*
25 | logs
26 | next-env.d.ts
27 | node_modules
28 | npm-debug.log*
29 | out
30 | pnpm-debug.log*
31 | releases
32 | test-results
33 | yarn-debug.log*
34 | yarn-error.log*
35 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | v18.17.1
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .astro
2 | .changeset
3 | .github/blocks
4 | .next
5 | .vercel
6 | .vscode-test
7 | .vscode-test-web
8 | build
9 | coverage
10 | dist
11 | dist-ssr
12 | out
13 | pnpm-lock.yaml
14 | example/src/content/docs/api*
15 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.useFlatConfig": true,
3 | "eslint.validate": [
4 | "javascript",
5 | "javascriptreact",
6 | "typescript",
7 | "typescriptreact",
8 | "html",
9 | "vue",
10 | "markdown",
11 | "astro"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023-present, HiDeoo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | packages/starlight-typedoc/README.md
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 |
2 |
starlight-typedoc 📚
3 |
Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 |
5 |
6 |
15 |
16 | ## Docs
17 |
18 | Run the docs locally using [pnpm](https://pnpm.io):
19 |
20 | ```shell
21 | pnpm run dev
22 | ```
23 |
24 | ## License
25 |
26 | Licensed under the MIT License, Copyright © HiDeoo.
27 |
28 | See [LICENSE](https://github.com/HiDeoo/starlight-typedoc/blob/main/LICENSE) for more information.
29 |
--------------------------------------------------------------------------------
/docs/astro.config.ts:
--------------------------------------------------------------------------------
1 | import starlight from '@astrojs/starlight'
2 | import { defineConfig } from 'astro/config'
3 |
4 | export default defineConfig({
5 | integrations: [
6 | starlight({
7 | customCss: ['./src/styles/custom.css'],
8 | editLink: {
9 | baseUrl: 'https://github.com/HiDeoo/starlight-typedoc/edit/main/docs/',
10 | },
11 | sidebar: [
12 | {
13 | label: 'Start Here',
14 | items: [
15 | { label: 'Getting Started', link: '/getting-started/' },
16 | { label: 'Configuration', link: '/configuration/' },
17 | ],
18 | },
19 | {
20 | label: 'Guides',
21 | items: [
22 | { label: 'Multiple Instances', link: '/guides/multiple-instances/' },
23 | { label: 'Frontmatter', link: '/guides/frontmatter/' },
24 | ],
25 | },
26 | {
27 | label: 'Resources',
28 | items: [
29 | { label: 'Showcase', link: '/resources/showcase/' },
30 | { label: 'Plugins and Tools', link: '/resources/starlight/' },
31 | ],
32 | },
33 | { label: 'Demo', link: 'https://starlight-typedoc-example.vercel.app/api/functions/dothingc/' },
34 | ],
35 | social: {
36 | blueSky: 'https://bsky.app/profile/hideoo.dev',
37 | github: 'https://github.com/HiDeoo/starlight-typedoc',
38 | },
39 | title: 'Starlight TypeDoc',
40 | }),
41 | ],
42 | })
43 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starlight-typedoc-docs",
3 | "version": "0.17.0",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "scripts": {
9 | "dev": "astro dev",
10 | "build": "astro build",
11 | "preview": "astro preview",
12 | "astro": "astro",
13 | "lint": "eslint . --cache --max-warnings=0"
14 | },
15 | "dependencies": {
16 | "@astrojs/starlight": "^0.32.0",
17 | "@hideoo/starlight-plugins-docs-components": "^0.4.0",
18 | "astro": "^5.3.0",
19 | "sharp": "^0.33.5"
20 | },
21 | "engines": {
22 | "node": ">=18.17.1"
23 | },
24 | "packageManager": "pnpm@8.6.3",
25 | "private": true,
26 | "sideEffects": false,
27 | "keywords": [
28 | "starlight",
29 | "plugin",
30 | "typedoc",
31 | "typescript",
32 | "documentation",
33 | "astro"
34 | ],
35 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
36 | "repository": {
37 | "type": "git",
38 | "url": "https://github.com/HiDeoo/starlight-typedoc.git",
39 | "directory": "docs"
40 | },
41 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
42 | }
43 |
--------------------------------------------------------------------------------
/docs/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/src/assets/showcase/bunshi.org.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/bunshi.org.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/chord.vercel.app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/chord.vercel.app.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/dgmjs.dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/dgmjs.dev.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/discord-components.js.org.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/discord-components.js.org.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/docs.seyfert.dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/docs.seyfert.dev.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/docs.shepherdpro.com.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/docs.shepherdpro.com.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/echo-d.net.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/echo-d.net.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/eddienubes.github.io-sagetest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/eddienubes.github.io-sagetest.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/fevol.github.io-obsidian-typings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/fevol.github.io-obsidian-typings.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/lilybird.didas.dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/lilybird.didas.dev.png
--------------------------------------------------------------------------------
/docs/src/assets/showcase/vinoth.info-react-sketch-canvas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/docs/src/assets/showcase/vinoth.info-react-sketch-canvas.png
--------------------------------------------------------------------------------
/docs/src/content.config.ts:
--------------------------------------------------------------------------------
1 | import { docsLoader } from '@astrojs/starlight/loaders'
2 | import { docsSchema } from '@astrojs/starlight/schema'
3 | import { defineCollection } from 'astro:content'
4 |
5 | export const collections = {
6 | docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
7 | }
8 |
--------------------------------------------------------------------------------
/docs/src/content/docs/configuration.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Configuration
3 | description: An overview of all the configuration options supported by the Starlight TypeDoc plugin.
4 | ---
5 |
6 | The Starlight TypeDoc plugin can be configured inside the `astro.config.mjs` configuration file of your project:
7 |
8 | ```js {11}
9 | // astro.config.mjs
10 | import starlight from '@astrojs/starlight'
11 | import { defineConfig } from 'astro/config'
12 | import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
13 |
14 | export default defineConfig({
15 | integrations: [
16 | starlight({
17 | plugins: [
18 | starlightTypeDoc({
19 | // Configuration options go here.
20 | }),
21 | ],
22 | title: 'My Docs',
23 | }),
24 | ],
25 | })
26 | ```
27 |
28 | ## Configuration options
29 |
30 | You can pass the following options to the Starlight TypeDoc plugin.
31 |
32 | ### `entryPoints` (required)
33 |
34 | **Type:** `string[]`
35 |
36 | The path(s) to the entry point(s) to document.
37 |
38 | ### `tsconfig` (required)
39 |
40 | **Type:** `string`
41 |
42 | The path to the `tsconfig.json` file to use for the documentation generation.
43 |
44 | ### `output`
45 |
46 | **Type:** `string`
47 | **Default:** `'api'`
48 |
49 | The output directory containing the generated documentation markdown files relative to the `src/content/docs/` directory.
50 |
51 | ### `pagination`
52 |
53 | **Type:** `boolean`
54 | **Default:** `false`
55 |
56 | Whether the footer should include previous and next page links for the generated documentation.
57 |
58 | ### `sidebar`
59 |
60 | **Type:** [`StarlightTypeDocSidebarOptions`](#sidebar-configuration)
61 |
62 | The generated documentation [sidebar configuration](#sidebar-configuration).
63 |
64 | ### `typeDoc`
65 |
66 | **Type:** `TypeDocConfig`
67 |
68 | Additional [TypeDoc](https://typedoc.org/options) or [typedoc-plugin-markdown](https://github.com/tgreyuk/typedoc-plugin-markdown/blob/next/packages/typedoc-plugin-markdown/docs/usage/options.md) configuration to override the [default settings](https://github.com/HiDeoo/starlight-typedoc/blob/main/packages/starlight-typedoc/libs/typedoc.ts#L21-L28) used by the plugin.
69 |
70 | :::note
71 | When using TypeDoc [`packages`](https://typedoc.org/options/input/#packages) entry point strategy, all entry points should be directories that may contain their own TypeDoc configuration.
72 | As documented in the [TypeDoc documentation](https://typedoc.org/options/input/#packages), the root configuration provided by this plugin will not be copied or merged with the entry point configuration.
73 | :::
74 |
75 | ### `watch`
76 |
77 | **Type:** `boolean`
78 | **Default:** `false`
79 |
80 | Whether to watch the entry point(s) for changes and regenerate the documentation when needed.
81 |
82 | ### `errorOnEmptyDocumentation`
83 |
84 | **Type:** `boolean`
85 | **Default:** `true`
86 |
87 | Whether the plugin should error when no TypeDoc documentation is generated.
88 |
89 | By default, the plugin will error when no TypeDoc documentation is generated.
90 | Setting this option to `false` will prevent the plugin from erroring in this case.
91 | This can be useful when generating documentation for multiple entry points and only some of them contain documented code at a given time.
92 |
93 | ## Sidebar configuration
94 |
95 | The sidebar configuration is an object with the following properties:
96 |
97 | ### `collapsed`
98 |
99 | **Type:** `boolean`
100 | **Default:** `false`
101 |
102 | Wheter the generated documentation sidebar group should be collapsed by default.
103 | Note that nested sidebar groups are always collapsed.
104 |
105 | ### `label`
106 |
107 | **Type:** `string`
108 | **Default:** `'API'`
109 |
110 | The generated documentation sidebar group label.
111 |
--------------------------------------------------------------------------------
/docs/src/content/docs/getting-started.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Getting Started
3 | description: Learn how to generate documentation from TypeScript code using the Starlight TypeDoc plugin.
4 | ---
5 |
6 | A [Starlight](https://starlight.astro.build) plugin using [TypeDoc](https://typedoc.org) and [typedoc-plugin-markdown](https://github.com/tgreyuk/typedoc-plugin-markdown) to generate documentation from TypeScript code.
7 |
8 | Check out the [demo](https://starlight-typedoc-example.vercel.app) for a preview of the generated documentation.
9 |
10 | ## Installation
11 |
12 | import { Steps } from '@astrojs/starlight/components'
13 | import { PackageManagers } from '@hideoo/starlight-plugins-docs-components'
14 |
15 |
16 |
17 | 1. Starlight TypeDoc is a Starlight [plugin](https://starlight.astro.build/reference/plugins/). Install it and its peer dependencies using your favorite package manager:
18 |
19 |
20 |
21 | 2. Configure the plugin in your Starlight [configuration](https://starlight.astro.build/reference/configuration/#plugins) in the `astro.config.mjs` file.
22 |
23 | ```diff lang="js"
24 | // astro.config.mjs
25 | import starlight from '@astrojs/starlight'
26 | import { defineConfig } from 'astro/config'
27 | +import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
28 |
29 | export default defineConfig({
30 | integrations: [
31 | starlight({
32 | + plugins: [
33 | + // Generate the documentation.
34 | + starlightTypeDoc({
35 | + entryPoints: ['../path/to/entry-point.ts'],
36 | + tsconfig: '../path/to/tsconfig.json',
37 | + }),
38 | + ],
39 | sidebar: [
40 | {
41 | label: 'Guides',
42 | items: [{ label: 'Example Guide', link: '/guides/example/' }],
43 | },
44 | + // Add the generated sidebar group to the sidebar.
45 | + typeDocSidebarGroup,
46 | ],
47 | title: 'My Docs',
48 | }),
49 | ],
50 | })
51 | ```
52 |
53 | 3. [Start the development server](https://starlight.astro.build/getting-started/#start-the-development-server) to preview the generated documentation.
54 |
55 |
56 |
57 | The Starlight TypeDoc plugin behavior can be tweaked using various [configuration options](/configuration).
58 |
--------------------------------------------------------------------------------
/docs/src/content/docs/guides/frontmatter.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Frontmatter
3 | description: Learn how to customize the frontmatter of pages generated by the Starlight TypeDoc plugin.
4 | ---
5 |
6 | By default, the Starlight TypeDoc plugin generates documentation pages which include a [frontmatter](https://starlight.astro.build/reference/frontmatter/) with various fields already set like the [title](https://starlight.astro.build/reference/frontmatter/#title-required) of the page.
7 |
8 | ## Customize the frontmatter
9 |
10 | Using the [`typedoc-plugin-frontmatter`](https://typedoc-plugin-markdown.org/plugins/frontmatter) plugin, you can customize the frontmatter of the generated documentation pages using a custom TypeDoc plugin.
11 |
12 | import { Steps } from '@astrojs/starlight/components'
13 | import { PackageManagers } from '@hideoo/starlight-plugins-docs-components'
14 |
15 |
16 |
17 | 1. Install the `typedoc-plugin-frontmatter` plugin using your favorite package manager:
18 |
19 |
20 |
21 | 2. Create a custom TypeDoc plugin to [customize](https://typedoc-plugin-markdown.org/plugins/frontmatter/customizing) the frontmatter of the generated documentation pages.
22 |
23 | The following example customizes the frontmatter of the `variables/something.md` page to include a sidebar [`badge`](https://starlight.astro.build/reference/frontmatter/#badge) with the text "New":
24 |
25 | ```diff lang="js"
26 | // src/plugins/frontmatter.js
27 | import { MarkdownPageEvent } from 'typedoc-plugin-markdown'
28 |
29 | /** @param {import('typedoc-plugin-markdown').MarkdownApplication} app */
30 | export function load(app) {
31 | app.renderer.on(
32 | MarkdownPageEvent.BEGIN,
33 | /** @param {MarkdownPageEvent} page */
34 | (page) => {
35 | // Customize the frontmatter for a specific page.
36 | if (page.url === 'variables/something.md') {
37 | // Update the frontmatter of the generated page.
38 | page.frontmatter = {
39 | // Include a sidebar badge with the text 'New'.
40 | sidebar: {
41 | badge: {
42 | text: 'New',
43 | },
44 | },
45 | ...page.frontmatter,
46 | }
47 | }
48 | },
49 | )
50 | }
51 |
52 | ```
53 |
54 | 3. Edit the Starlight TypeDoc plugin configuration in your `astro.config.mjs` file to include the `typedoc-plugin-frontmatter` plugin and the custom one you created in the previous step using the [`typeDoc`](/configuration/#typedoc) option:
55 |
56 | ```diff lang="js"
57 | // astro.config.mjs
58 | import starlight from '@astrojs/starlight'
59 | import { defineConfig } from 'astro/config'
60 | import starlightTypeDoc from 'starlight-typedoc'
61 |
62 | export default defineConfig({
63 | integrations: [
64 | starlight({
65 | plugins: [
66 | starlightTypeDoc({
67 | entryPoints: ['../path/to/entry-point.ts'],
68 | tsconfig: '../path/to/tsconfig.json',
69 | + // Add plugins to customize the frontmatter.
70 | + typeDoc: {
71 | + plugin: [
72 | + 'typedoc-plugin-frontmatter',
73 | + './src/plugins/frontmatter.js'
74 | + ],
75 | + },
76 | }),
77 | ],
78 | title: 'My Docs',
79 | }),
80 | ],
81 | })
82 | ```
83 |
84 |
85 |
86 | To learn more about the `typedoc-plugin-frontmatter` plugin, check out the [documentation](https://typedoc-plugin-markdown.org/plugins/frontmatter/customizing).
87 |
--------------------------------------------------------------------------------
/docs/src/content/docs/guides/multiple-instances.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Multiple Instances
3 | description: Learn how to use multiple instances of the Starlight TypeDoc plugin.
4 | ---
5 |
6 | By default, the Starlight TypeDoc plugin generates documentation for one or multiple [entry points](/configuration/#entrypoints-required) using the same plugin or TypeDoc configuration.
7 |
8 | Sometimes, you may want to generate documentation for different entry points using different configurations.
9 | To achieve this, the Starlight TypeDoc plugin exposes a `createStarlightTypeDocPlugin()` function that allows you to create multiple instances of the plugin.
10 |
11 | ## `createStarlightTypeDocPlugin()`
12 |
13 | Calling the `createStarlightTypeDocPlugin()` function returns an array with exactly two values:
14 |
15 | 1. A new Starlight TypeDoc plugin instance that you can add to your Starlight configuration.
16 | 1. A reference to the generated sidebar group for that instance that you can add to your sidebar.
17 |
18 | The following example creates two Starlight TypeDoc plugin instances for two different entry points: one for a public API and another for an admin API.
19 | The associated sidebar groups are then added to the sidebar:
20 |
21 | ```js {6-7}
22 | // astro.config.mjs
23 | import starlight from '@astrojs/starlight'
24 | import { defineConfig } from 'astro/config'
25 | import { createStarlightTypeDocPlugin } from 'starlight-typedoc'
26 |
27 | const [publicStarlightTypeDoc, publicTypeDocSidebarGroup] = createStarlightTypeDocPlugin()
28 | const [adminStarlightTypeDoc, adminTypeDocSidebarGroup] = createStarlightTypeDocPlugin()
29 |
30 | export default defineConfig({
31 | integrations: [
32 | starlight({
33 | plugins: [
34 | // Generate the documentation for the public API.
35 | publicStarlightTypeDoc({
36 | entryPoints: ['../path/to/public/entry-point.ts'],
37 | output: 'api-public',
38 | tsconfig: '../path/to/public/tsconfig.json',
39 | }),
40 | // Generate the documentation for the admin API.
41 | adminStarlightTypeDoc({
42 | entryPoints: ['../path/to/admin/entry-point.ts'],
43 | output: 'api-admin',
44 | tsconfig: '../path/to/admin/tsconfig.json',
45 | }),
46 | ],
47 | sidebar: [
48 | {
49 | label: 'User Guide',
50 | // Add the generated public sidebar group to the sidebar.
51 | items: [publicTypeDocSidebarGroup],
52 | },
53 | {
54 | label: 'Admin Guide',
55 | // Add the generated admin sidebar group to the sidebar.
56 | items: [adminTypeDocSidebarGroup],
57 | },
58 | ],
59 | title: 'My Docs',
60 | }),
61 | ],
62 | })
63 | ```
64 |
--------------------------------------------------------------------------------
/docs/src/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Starlight TypeDoc
3 | description: Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 | head:
5 | - tag: title
6 | content: Starlight TypeDoc
7 | template: splash
8 | editUrl: false
9 | lastUpdated: false
10 | hero:
11 | tagline: Starlight plugin to generate documentation from TypeScript using TypeDoc.
12 | image:
13 | html: '📚'
14 | actions:
15 | - text: Getting Started
16 | link: /getting-started/
17 | icon: rocket
18 | - text: Demo
19 | link: https://starlight-typedoc-example.vercel.app/api/functions/dothingc/
20 | icon: external
21 | variant: minimal
22 | ---
23 |
24 | import { Card, CardGrid } from '@astrojs/starlight/components'
25 |
26 | ## Next steps
27 |
28 |
29 |
30 | Check the [getting started guide](/getting-started/) for installation instructions.
31 |
32 |
33 | Edit your config in `astro.config.mjs`.
34 |
35 |
36 | Generate documentation from your TypeScript code.
37 |
38 |
39 | Learn more in [the Starlight TypeDoc Docs](/getting-started/).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/docs/src/content/docs/resources/showcase.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Showcase
3 | description: Discover Starlight project using the Starlight TypeDoc plugin.
4 | ---
5 |
6 | import { ShowcaseIntro, Showcase } from '@hideoo/starlight-plugins-docs-components'
7 |
8 |
12 |
13 | ## Sites
14 |
15 | Starlight TypeDoc is already being used in production. These are some of the sites around the web:
16 |
17 |
76 |
77 | See all the [public project repos using Starlight TypeDoc on GitHub](https://github.com/hideoo/starlight-typedoc/network/dependents).
78 |
--------------------------------------------------------------------------------
/docs/src/content/docs/resources/starlight.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Starlight Plugins and Tools
3 | description: Discover other Starlight plugins, components and tools developed by HiDeoo.
4 | ---
5 |
6 | import { ResourcesIntro, Resources } from '@hideoo/starlight-plugins-docs-components'
7 |
8 |
9 |
10 | ## Plugins
11 |
12 |
13 |
14 | ## Components
15 |
16 |
17 |
18 | ## Tools
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/docs/src/styles/custom.css:
--------------------------------------------------------------------------------
1 | .hero-html {
2 | --size: 10rem;
3 |
4 | font-size: var(--size);
5 | justify-content: center;
6 | line-height: var(--size);
7 | }
8 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import hideoo from '@hideoo/eslint-config'
2 |
3 | export default hideoo()
4 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 |
2 |
starlight-typedoc 📚
3 |
Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 |
5 |
6 |
15 |
16 | ## Example
17 |
18 | Run the example locally using [pnpm](https://pnpm.io):
19 |
20 | ```shell
21 | pnpm run dev
22 | ```
23 |
24 | ## License
25 |
26 | Licensed under the MIT License, Copyright © HiDeoo.
27 |
28 | See [LICENSE](https://github.com/HiDeoo/starlight-typedoc/blob/main/LICENSE) for more information.
29 |
--------------------------------------------------------------------------------
/example/astro.config.ts:
--------------------------------------------------------------------------------
1 | import starlight from '@astrojs/starlight'
2 | import { defineConfig } from 'astro/config'
3 | import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
4 |
5 | export default defineConfig({
6 | integrations: [
7 | starlight({
8 | customCss: ['./src/styles/custom.css'],
9 | editLink: {
10 | baseUrl: 'https://github.com/HiDeoo/starlight-typedoc/edit/main/example/',
11 | },
12 | plugins: [
13 | starlightTypeDoc({
14 | entryPoints: ['../fixtures/basics/src/index.ts'],
15 | tsconfig: '../fixtures/basics/tsconfig.json',
16 | sidebar: {
17 | label: 'API (auto-generated)',
18 | },
19 | typeDoc: {
20 | plugin: ['typedoc-plugin-mdn-links', 'typedoc-plugin-frontmatter', './src/plugins/frontmatter.js'],
21 | },
22 | }),
23 | ],
24 | sidebar: [
25 | {
26 | label: 'Guides',
27 | items: ['guides/example'],
28 | },
29 | typeDocSidebarGroup,
30 | ],
31 | social: {
32 | blueSky: 'https://bsky.app/profile/hideoo.dev',
33 | github: 'https://github.com/HiDeoo/starlight-typedoc',
34 | },
35 | title: 'Starlight TypeDoc Example',
36 | }),
37 | ],
38 | })
39 |
--------------------------------------------------------------------------------
/example/astro.multiple-entrypoints.config.ts:
--------------------------------------------------------------------------------
1 | import starlight from '@astrojs/starlight'
2 | import { defineConfig } from 'astro/config'
3 | import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
4 |
5 | export default defineConfig({
6 | base: '/multiple-entrypoints/',
7 | integrations: [
8 | starlight({
9 | plugins: [
10 | starlightTypeDoc({
11 | entryPoints: ['../fixtures/basics/src/Bar.ts', '../fixtures/basics/src/Foo.ts'],
12 | output: 'api-multiple-entrypoints',
13 | pagination: true,
14 | sidebar: {
15 | collapsed: true,
16 | },
17 | tsconfig: '../fixtures/basics/tsconfig.json',
18 | // @ts-expect-error - Fake the `readme` option not being set to ensure that frontmatter titles are escaped properly.
19 | // @see https://github.com/HiDeoo/starlight-typedoc/pull/7
20 | typeDoc: {
21 | readme: undefined,
22 | },
23 | }),
24 | ],
25 | sidebar: [
26 | {
27 | label: 'Guides',
28 | items: [{ label: 'Example Guide', link: '/guides/example/' }],
29 | },
30 | typeDocSidebarGroup,
31 | ],
32 | title: 'Starlight TypeDoc Multiple Entry Points Example',
33 | }),
34 | ],
35 | outDir: './dist-multiple-entrypoints',
36 | server: {
37 | port: 4322,
38 | },
39 | })
40 |
--------------------------------------------------------------------------------
/example/astro.multiple-plugins.config.ts:
--------------------------------------------------------------------------------
1 | import starlight from '@astrojs/starlight'
2 | import { defineConfig } from 'astro/config'
3 | import { createStarlightTypeDocPlugin } from 'starlight-typedoc'
4 |
5 | const [barStarlightTypeDoc, barTypeDocSidebarGroup] = createStarlightTypeDocPlugin()
6 | const [fooStarlightTypeDoc, fooTypeDocSidebarGroup] = createStarlightTypeDocPlugin()
7 |
8 | export default defineConfig({
9 | base: '/multiple-plugins/',
10 | integrations: [
11 | starlight({
12 | plugins: [
13 | barStarlightTypeDoc({
14 | entryPoints: ['../fixtures/basics/src/Bar.ts'],
15 | output: 'api-multiple-plugins-bar',
16 | tsconfig: '../fixtures/basics/tsconfig.json',
17 | sidebar: {
18 | label: 'Bar API',
19 | },
20 | }),
21 | fooStarlightTypeDoc({
22 | entryPoints: ['../fixtures/basics/src/Foo.ts'],
23 | output: 'api-multiple-plugins-foo',
24 | tsconfig: '../fixtures/basics/tsconfig.json',
25 | sidebar: {
26 | label: 'Foo API',
27 | },
28 | }),
29 | ],
30 | sidebar: [
31 | {
32 | label: 'Bar Content',
33 | items: [{ ...barTypeDocSidebarGroup, badge: 'generated' }],
34 | },
35 | {
36 | label: 'Foo Content',
37 | items: [fooTypeDocSidebarGroup],
38 | },
39 | ],
40 | title: 'Starlight TypeDoc Multiple Plugins Example',
41 | }),
42 | ],
43 | })
44 |
--------------------------------------------------------------------------------
/example/astro.packages-entrypoints.config.ts:
--------------------------------------------------------------------------------
1 | import starlight from '@astrojs/starlight'
2 | import { defineConfig } from 'astro/config'
3 | import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
4 |
5 | export default defineConfig({
6 | base: '/packages-entrypoints/',
7 | integrations: [
8 | starlight({
9 | plugins: [
10 | starlightTypeDoc({
11 | entryPoints: ['../fixtures/packages/packages/*'],
12 | output: 'api-packages-entrypoints',
13 | tsconfig: '../fixtures/packages/tsconfig.json',
14 | typeDoc: {
15 | entryPointStrategy: 'packages',
16 | },
17 | }),
18 | ],
19 | sidebar: [
20 | {
21 | label: 'Guides',
22 | items: [{ label: 'Example Guide', link: '/guides/example/' }],
23 | },
24 | typeDocSidebarGroup,
25 | ],
26 | title: 'Starlight TypeDoc Packages Entry Points Example',
27 | }),
28 | ],
29 | })
30 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starlight-typedoc-example",
3 | "version": "0.17.0",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "scripts": {
9 | "dev": "astro dev",
10 | "dev:single-entrypoints": "astro dev --config astro.config.ts",
11 | "dev:multiple-entrypoints": "astro dev --config astro.multiple-entrypoints.config.ts",
12 | "dev:packages-entrypoints": "pnpm -C ../fixtures/packages run build && astro dev --config astro.packages-entrypoints.config.ts",
13 | "dev:multiple-plugins": "astro dev --config astro.multiple-plugins.config.ts",
14 | "build": "astro build",
15 | "build:single-entrypoints": "astro build --config astro.config.ts",
16 | "build:multiple-entrypoints": "astro build --config astro.multiple-entrypoints.config.ts",
17 | "build:packages-entrypoints": "pnpm -C ../fixtures/packages run build && astro build --config astro.packages-entrypoints.config.ts",
18 | "build:multiple-plugins": "astro build --config astro.multiple-plugins.config.ts",
19 | "preview": "astro preview",
20 | "preview:single-entrypoints": "astro preview --config astro.config.ts",
21 | "preview:multiple-entrypoints": "astro preview --config astro.multiple-entrypoints.config.ts",
22 | "preview:packages-entrypoints": "astro preview --config astro.packages-entrypoints.config.ts",
23 | "preview:multiple-plugins": "astro preview --config astro.multiple-plugins.config.ts",
24 | "astro": "astro",
25 | "lint": "eslint . --cache --max-warnings=0"
26 | },
27 | "dependencies": {
28 | "@astrojs/starlight": "^0.32.0",
29 | "astro": "^5.3.0",
30 | "sharp": "^0.33.5",
31 | "starlight-typedoc": "workspace:*",
32 | "typedoc": "^0.28.1",
33 | "typedoc-plugin-frontmatter": "^1.3.0",
34 | "typedoc-plugin-markdown": "^4.6.0",
35 | "typedoc-plugin-mdn-links": "^5.0.1"
36 | },
37 | "engines": {
38 | "node": ">=18.17.1"
39 | },
40 | "packageManager": "pnpm@8.6.1",
41 | "private": true,
42 | "sideEffects": false,
43 | "keywords": [
44 | "starlight",
45 | "plugin",
46 | "typedoc",
47 | "typescript",
48 | "documentation",
49 | "astro"
50 | ],
51 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
52 | "repository": {
53 | "type": "git",
54 | "url": "https://github.com/HiDeoo/starlight-typedoc.git",
55 | "directory": "example"
56 | },
57 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
58 | }
59 |
--------------------------------------------------------------------------------
/example/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example/src/assets/houston.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HiDeoo/starlight-typedoc/e990682b951cecd249a051afc332a503d9dd2b8e/example/src/assets/houston.webp
--------------------------------------------------------------------------------
/example/src/content.config.ts:
--------------------------------------------------------------------------------
1 | import { docsLoader } from '@astrojs/starlight/loaders'
2 | import { docsSchema } from '@astrojs/starlight/schema'
3 | import { defineCollection, z } from 'astro:content'
4 |
5 | export const collections = {
6 | docs: defineCollection({
7 | loader: docsLoader(),
8 | schema: docsSchema({
9 | extend: z.object({
10 | banner: z
11 | .object({
12 | content: z.string(),
13 | })
14 | .default({
15 | content:
16 | 'This is a demo of the Starlight TypeDoc plugin — Back to the documentation.',
17 | }),
18 | }),
19 | }),
20 | }),
21 | }
22 |
--------------------------------------------------------------------------------
/example/src/content/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Autogenerated by https://github.com/HiDeoo/starlight-typedoc
2 | api/
3 | api-multiple-entrypoints/
4 | api-packages-entrypoints/
5 | api-multiple-plugins-bar/
6 | api-multiple-plugins-foo/
7 |
--------------------------------------------------------------------------------
/example/src/content/docs/guides/example.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Example Guide
3 | description: A guide in my new Starlight docs site.
4 | ---
5 |
6 | Guides lead a user through a specific task they want to accomplish, often with a sequence of steps.
7 | Writing a good guide requires thinking about what your users are trying to do.
8 |
9 | ## Further reading
10 |
11 | - Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework
12 |
--------------------------------------------------------------------------------
/example/src/content/docs/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Starlight TypeDoc Example
3 | description: Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 | head:
5 | - tag: title
6 | content: Starlight TypeDoc Example
7 | template: splash
8 | editUrl: false
9 | lastUpdated: false
10 | hero:
11 | tagline: Starlight plugin to generate documentation from TypeScript using TypeDoc.
12 | image:
13 | html: '📚'
14 | actions:
15 | - text: Check Example
16 | link: /api/functions/dothingc/
17 | icon: rocket
18 | - text: Documentation
19 | link: https://starlight-typedoc.vercel.app/
20 | icon: external
21 | variant: minimal
22 | ---
23 |
24 | import { Card, CardGrid } from '@astrojs/starlight/components'
25 |
26 | ## Next steps
27 |
28 |
29 |
30 | Browse through the example pages to see how the plugin works.
31 |
32 |
33 | Check the [getting started](https://starlight-typedoc.vercel.app/getting-started/) guide for installation
34 | instructions.
35 |
36 |
37 |
--------------------------------------------------------------------------------
/example/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/example/src/plugins/frontmatter.js:
--------------------------------------------------------------------------------
1 | import { MarkdownPageEvent } from 'typedoc-plugin-markdown'
2 |
3 | /** @param {import('typedoc-plugin-markdown').MarkdownApplication} app */
4 | export function load(app) {
5 | app.renderer.on(
6 | MarkdownPageEvent.BEGIN,
7 | /** @param {MarkdownPageEvent} page */
8 | (page) => {
9 | if (page.url === 'variables/anObject.md') {
10 | page.frontmatter = {
11 | sidebar: {
12 | badge: {
13 | text: 'New',
14 | },
15 | },
16 | ...page.frontmatter,
17 | }
18 | }
19 | },
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/example/src/styles/custom.css:
--------------------------------------------------------------------------------
1 | .hero-html {
2 | --size: 10rem;
3 |
4 | font-size: var(--size);
5 | justify-content: center;
6 | line-height: var(--size);
7 | }
8 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/fixtures/basics/README.md:
--------------------------------------------------------------------------------
1 |
2 |
starlight-typedoc 📚
3 |
Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 |
5 |
6 |
15 |
16 | ## Fixtures
17 |
18 | This folder contains fixtures used for the example and tests.
19 |
20 | ## License
21 |
22 | Licensed under the MIT License, Copyright © HiDeoo.
23 |
24 | See [LICENSE](https://github.com/HiDeoo/starlight-typedoc/blob/main/LICENSE) for more information.
25 |
--------------------------------------------------------------------------------
/fixtures/basics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@starlight-typedoc/fixtures-basics",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "dependencies": {
9 | "typescript": "^5.7.2"
10 | },
11 | "engines": {
12 | "node": ">=18.17.1"
13 | },
14 | "packageManager": "pnpm@8.6.1",
15 | "private": true,
16 | "sideEffects": false,
17 | "keywords": [
18 | "starlight",
19 | "plugin",
20 | "typedoc",
21 | "typescript",
22 | "documentation",
23 | "astro"
24 | ],
25 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/HiDeoo/starlight-typedoc.git",
29 | "directory": "fixtures/basics"
30 | },
31 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
32 | }
33 |
--------------------------------------------------------------------------------
/fixtures/basics/src/Bar.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a Bar class.
3 | */
4 | export class Bar {
5 | /**
6 | * The bar property.
7 | * @default 'bar'
8 | */
9 | readonly bar = 'bar'
10 |
11 | /**
12 | * This does something.
13 | * @alpha
14 | */
15 | doSomething(element: HTMLElement) {
16 | element
17 |
18 | this.#doSomethingElse()
19 |
20 | return 'something'
21 | }
22 |
23 | #doSomethingElse() {
24 | return 'something else'
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/fixtures/basics/src/Baz.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a Baz class.
3 | *
4 | * @group MyCustomGroup
5 | */
6 | export class Baz {
7 | /**
8 | * The baz property.
9 | * @default 'baz'
10 | */
11 | readonly baz = 'baz'
12 | }
13 |
--------------------------------------------------------------------------------
/fixtures/basics/src/Foo.ts:
--------------------------------------------------------------------------------
1 | import { Bar } from './Bar'
2 | import type { Thing } from './types'
3 |
4 | /**
5 | * This is a Foo class.
6 | */
7 | export class Foo extends Bar {
8 | /**
9 | * The foo property.
10 | * @default 'foo'
11 | */
12 | readonly foo = 'foo'
13 |
14 | /**
15 | * Parse a thing.
16 | * @see {@link Thing}
17 | */
18 | parseThing(thing: Thing) {
19 | return thing
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/fixtures/basics/src/functions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A function that does a thing.
3 | */
4 | export function doThingA() {
5 | return 'thingA'
6 | }
7 |
8 | /**
9 | * A function that does another thing.
10 | * @deprecated
11 | */
12 | export function doThingB() {
13 | return 'thingB'
14 | }
15 |
16 | /**
17 | * A function that does another thing.
18 | * @deprecated Use the new {@link doThingFaster} function instead.
19 | */
20 | export function doThingC() {
21 | return 'thingC'
22 | }
23 |
24 | /**
25 | * A function that does another thing but faster.
26 | *
27 | * This is a faster alternative to {@link doThingB}.
28 | */
29 | export function doThingFaster() {
30 | return 'thingB'
31 | }
32 |
33 | /**
34 | * A function that print dollars.
35 | */
36 | export function $() {
37 | return '$'
38 | }
39 |
--------------------------------------------------------------------------------
/fixtures/basics/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './Bar'
2 | export * from './Foo'
3 | export * from './Baz'
4 | export * from './functions'
5 | export * from './shared'
6 | export * from './types'
7 |
--------------------------------------------------------------------------------
/fixtures/basics/src/module.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a module
3 | *
4 | * @module
5 | */
6 |
7 | export * as bar from './Bar'
8 | export * as foo from './Foo'
9 | export * as functions from './functions'
10 | export * as shared from './shared'
11 | export * as types from './types'
12 |
--------------------------------------------------------------------------------
/fixtures/basics/src/noDocs.ts:
--------------------------------------------------------------------------------
1 | export const UNDOCUMENTED = 'UNDOCUMENTED'
2 |
--------------------------------------------------------------------------------
/fixtures/basics/src/noExports.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/fixtures/basics/src/shared.ts:
--------------------------------------------------------------------------------
1 | export { doThingA as doThingARef } from './functions'
2 |
3 | /**
4 | * This is a string variable.
5 | */
6 | export const aString = 'the_string_value'
7 |
8 | // eslint-disable-next-line import/no-mutable-exports
9 | export let anUndefinedString: string
10 |
11 | /**
12 | * This is an object variable.
13 | */
14 | export const anObject = {
15 | /**
16 | * This is foo.
17 | */
18 | foo: 'Foo',
19 | bar: {
20 | /**
21 | * This is baz.
22 | * @beta
23 | */
24 | baz: ['Baz'],
25 | },
26 | }
27 |
28 | export const anObjectAsConst = {
29 | foo: 'Foo',
30 | bar: {
31 | baz: ['Baz'],
32 | },
33 | } as const
34 |
35 | export enum ANumericEnum {
36 | Foo,
37 | Bar,
38 | }
39 |
40 | export enum AStringEnum {
41 | Foo = 'foo',
42 | Bar = 'bar',
43 | }
44 |
--------------------------------------------------------------------------------
/fixtures/basics/src/types.ts:
--------------------------------------------------------------------------------
1 | export interface Thing {
2 | /**
3 | * This is foo.
4 | */
5 | foo: string
6 | /**
7 | * This is bar.
8 | * @experimental
9 | */
10 | bar: number
11 | }
12 |
13 | export type Things = Thing[]
14 |
--------------------------------------------------------------------------------
/fixtures/basics/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@hideoo/tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/fixtures/packages/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@starlight-typedoc/fixtures-packages",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "scripts": {
9 | "build": "tsc --build"
10 | },
11 | "dependencies": {
12 | "typescript": "^5.7.2"
13 | },
14 | "engines": {
15 | "node": ">=18.17.1"
16 | },
17 | "packageManager": "pnpm@8.6.1",
18 | "private": true,
19 | "sideEffects": false,
20 | "keywords": [
21 | "starlight",
22 | "plugin",
23 | "typedoc",
24 | "typescript",
25 | "documentation",
26 | "astro"
27 | ],
28 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
29 | "repository": {
30 | "type": "git",
31 | "url": "https://github.com/HiDeoo/starlight-typedoc.git",
32 | "directory": "fixtures/packages"
33 | },
34 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
35 | }
36 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/bar/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bar",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "packageManager": "pnpm@8.6.1",
9 | "private": true,
10 | "sideEffects": false,
11 | "keywords": [
12 | "starlight",
13 | "plugin",
14 | "typedoc",
15 | "typescript",
16 | "documentation",
17 | "astro"
18 | ],
19 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/HiDeoo/starlight-typedoc.git"
23 | },
24 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
25 | }
26 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/bar/src/functions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A function that does a bar thing.
3 | */
4 | export function doBar() {
5 | return 'doBar'
6 | }
7 |
8 | /**
9 | * A function that does another bar thing but better.
10 | *
11 | * This is a better alternative to {@link doBar}.
12 | */
13 | export function doBarBetter(options: DoBarBetterOptions) {
14 | return options.name
15 | }
16 |
17 | /**
18 | * Options for {@link doBarBetter}
19 | */
20 | export interface DoBarBetterOptions {
21 | name: string
22 | number: number
23 | }
24 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/bar/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './functions.js'
2 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/bar/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src"]
8 | }
9 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/bar/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "entryPoints": ["src/index.ts"]
3 | }
4 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/foo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "foo",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "packageManager": "pnpm@8.6.1",
9 | "private": true,
10 | "sideEffects": false,
11 | "keywords": [
12 | "starlight",
13 | "plugin",
14 | "typedoc",
15 | "typescript",
16 | "documentation",
17 | "astro"
18 | ],
19 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
20 | "repository": {
21 | "type": "git",
22 | "url": "https://github.com/HiDeoo/starlight-typedoc.git"
23 | },
24 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
25 | }
26 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/foo/src/functions.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A function that does a foo thing.
3 | * @deprecated Use the new {@link doFooFaster} function instead.
4 | */
5 | export function doFoo() {
6 | return 'doFoo'
7 | }
8 |
9 | /**
10 | * A function that does another foo thing but faster.
11 | *
12 | * This is a faster alternative to {@link doFoo}.
13 | */
14 | export function doFooFaster() {
15 | return 'doFoo'
16 | }
17 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/foo/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './functions.js'
2 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/foo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "rootDir": "src",
5 | "outDir": "dist"
6 | },
7 | "include": ["src"]
8 | }
9 |
--------------------------------------------------------------------------------
/fixtures/packages/packages/foo/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "entryPoints": ["src/index.ts"]
3 | }
4 |
--------------------------------------------------------------------------------
/fixtures/packages/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "declaration": true,
5 | "declarationMap": true,
6 | "module": "NodeNext",
7 | "strict": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/fixtures/packages/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./packages/bar"
6 | },
7 | {
8 | "path": "./packages/foo"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starlight-typedoc-monorepo",
3 | "version": "0.17.0",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "scripts": {
9 | "test": "pnpm --stream -r test",
10 | "lint": "astro check --noSync && pnpm -r lint",
11 | "format": "prettier -w --cache --ignore-unknown .",
12 | "version": "pnpm changeset version && pnpm i --no-frozen-lockfile"
13 | },
14 | "devDependencies": {
15 | "@astrojs/check": "^0.9.4",
16 | "@changesets/changelog-github": "^0.5.0",
17 | "@changesets/cli": "^2.27.10",
18 | "@hideoo/eslint-config": "^4.0.0",
19 | "@hideoo/prettier-config": "^2.0.0",
20 | "@hideoo/tsconfig": "^2.0.1",
21 | "astro": "^5.3.0",
22 | "eslint": "^9.17.0",
23 | "prettier": "^3.4.2",
24 | "typescript": "^5.7.2"
25 | },
26 | "engines": {
27 | "node": ">=18.17.1"
28 | },
29 | "packageManager": "pnpm@8.6.1",
30 | "pnpm": {
31 | "peerDependencyRules": {
32 | "allowedVersions": {
33 | "typescript": "5.7.2"
34 | }
35 | }
36 | },
37 | "private": true,
38 | "sideEffects": false,
39 | "keywords": [
40 | "starlight",
41 | "plugin",
42 | "typedoc",
43 | "typescript",
44 | "documentation",
45 | "astro"
46 | ],
47 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
48 | "repository": {
49 | "type": "git",
50 | "url": "https://github.com/HiDeoo/starlight-typedoc.git"
51 | },
52 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues",
53 | "prettier": "@hideoo/prettier-config"
54 | }
55 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/.npmignore:
--------------------------------------------------------------------------------
1 | .eslintcache
2 | eslint.config.mjs
3 | playwright.config.ts
4 | tests
5 | vitest.config.ts
6 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # starlight-typedoc
2 |
3 | ## 0.21.3
4 |
5 | ### Patch Changes
6 |
7 | - [#85](https://github.com/HiDeoo/starlight-typedoc/pull/85) [`51d3e37`](https://github.com/HiDeoo/starlight-typedoc/commit/51d3e370a5fff417a2c104175c0ee17d4048eb66) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes a sidebar generation issue when using the [plugin `watch` configuration option](https://starlight-typedoc.vercel.app/configuration/#watch).
8 |
9 | ## 0.21.2
10 |
11 | ### Patch Changes
12 |
13 | - [#83](https://github.com/HiDeoo/starlight-typedoc/pull/83) [`2fc1bf2`](https://github.com/HiDeoo/starlight-typedoc/commit/2fc1bf2499d226ca624e18ae235134f314b18d47) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes a regression introduced in [version `0.21.0`](https://github.com/HiDeoo/starlight-typedoc/releases/tag/starlight-typedoc%400.21.0) that prevented the development server from starting when using the [plugin `watch` configuration option](https://starlight-typedoc.vercel.app/configuration/#watch).
14 |
15 | ## 0.21.1
16 |
17 | ### Patch Changes
18 |
19 | - [#80](https://github.com/HiDeoo/starlight-typedoc/pull/80) [`e447787`](https://github.com/HiDeoo/starlight-typedoc/commit/e4477874721b8c8482375c35587aebabb3fa8d17) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Fixes a potential page and link generation issue with some declaration reference names such as a function named `$`.
20 |
21 | ## 0.21.0
22 |
23 | ### Minor Changes
24 |
25 | - [#77](https://github.com/HiDeoo/starlight-typedoc/pull/77) [`751021f`](https://github.com/HiDeoo/starlight-typedoc/commit/751021f9e1029600266cc8c3bea8232e385bbbc3) Thanks [@HiDeoo](https://github.com/HiDeoo)! - ⚠️ **BREAKING CHANGE:** The minimum supported version of `typedoc` is now `0.28.0`.
26 |
27 | ⚠️ **BREAKING CHANGE:** The minimum supported version of `typedoc-plugin-markdown` is now `4.6.0`.
28 |
29 | ## 0.20.0
30 |
31 | ### Minor Changes
32 |
33 | - [#74](https://github.com/HiDeoo/starlight-typedoc/pull/74) [`2765549`](https://github.com/HiDeoo/starlight-typedoc/commit/276554979760b992d204ce25106c51611f289749) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds a new [`errorOnEmptyDocumentation`](https://starlight-typedoc.vercel.app/configuration/#erroronemptydocumentation) option, defaulting to `true`, to control whether the plugin should error when no TypeDoc documentation is generated.
34 |
35 | Setting this option to `false` will prevent the plugin from erroring in this case which can be useful when generating documentation for multiple entry points and only some of them contain documented code at a given time.
36 |
37 | The current behavior remains unchanged, and the plugin will error when no TypeDoc documentation is generated if the option is not explicitly set to `false`.
38 |
39 | ## 0.19.0
40 |
41 | ### Minor Changes
42 |
43 | - [#70](https://github.com/HiDeoo/starlight-typedoc/pull/70) [`8ffcff1`](https://github.com/HiDeoo/starlight-typedoc/commit/8ffcff196052e58913135db766a102d7c3a4fb94) Thanks [@HiDeoo](https://github.com/HiDeoo)! - ⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now version `0.32.0`.
44 |
45 | Please use the `@astrojs/upgrade` command to upgrade your project:
46 |
47 | ```sh
48 | npx @astrojs/upgrade
49 | ```
50 |
51 | ## 0.18.0
52 |
53 | ### Minor Changes
54 |
55 | - [#66](https://github.com/HiDeoo/starlight-typedoc/pull/66) [`c4014bc`](https://github.com/HiDeoo/starlight-typedoc/commit/c4014bc2669e2072c2a452367641f11cc621214b) Thanks [@HiDeoo](https://github.com/HiDeoo)! - Adds support for Astro v5, drops support for Astro v4.
56 |
57 | ⚠️ **BREAKING CHANGE:** The minimum supported version of Starlight is now `0.30.0`.
58 |
59 | Please follow the [upgrade guide](https://github.com/withastro/starlight/releases/tag/%40astrojs/starlight%400.30.0) to update your project.
60 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/README.md:
--------------------------------------------------------------------------------
1 |
2 |
starlight-typedoc 📚
3 |
Starlight plugin to generate documentation from TypeScript using TypeDoc.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 | ## Getting Started
22 |
23 | Want to get started immediately? Check out the [getting started guide](https://starlight-typedoc.vercel.app/getting-started/) or check out the [demo](https://starlight-typedoc-example.vercel.app) to see the plugin in action.
24 |
25 | ## Features
26 |
27 | A [Starlight](https://starlight.astro.build) plugin using [TypeDoc](https://typedoc.org) and [typedoc-plugin-markdown](https://github.com/tgreyuk/typedoc-plugin-markdown) to generate documentation from TypeScript code.
28 |
29 | ## License
30 |
31 | Licensed under the MIT License, Copyright © HiDeoo.
32 |
33 | See [LICENSE](https://github.com/HiDeoo/starlight-typedoc/blob/main/LICENSE) for more information.
34 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import hideoo from '@hideoo/eslint-config'
2 |
3 | export default hideoo({
4 | ignores: ['eslint.config.mjs'],
5 | languageOptions: {
6 | parserOptions: {
7 | project: ['../../tsconfig.json'],
8 | },
9 | },
10 | })
11 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/index.ts:
--------------------------------------------------------------------------------
1 | import { randomBytes } from 'node:crypto'
2 |
3 | import type { StarlightPlugin } from '@astrojs/starlight/types'
4 | import type { TypeDocOptions } from 'typedoc'
5 |
6 | import {
7 | getSidebarFromReflections,
8 | getSidebarGroupPlaceholder,
9 | getSidebarWithoutReflections,
10 | type SidebarGroup,
11 | } from './libs/starlight'
12 | import { generateTypeDoc, NoReflectionsError, type TypeDocConfig } from './libs/typedoc'
13 |
14 | export const typeDocSidebarGroup = getSidebarGroupPlaceholder()
15 |
16 | export default function starlightTypeDocPlugin(options: StarlightTypeDocOptions): StarlightPlugin {
17 | return makeStarlightTypeDocPlugin(typeDocSidebarGroup)(options)
18 | }
19 |
20 | export function createStarlightTypeDocPlugin(): [plugin: typeof starlightTypeDocPlugin, sidebarGroup: SidebarGroup] {
21 | const sidebarGroup = getSidebarGroupPlaceholder(Symbol(randomBytes(24).toString('base64url')))
22 |
23 | return [makeStarlightTypeDocPlugin(sidebarGroup), sidebarGroup]
24 | }
25 |
26 | function makeStarlightTypeDocPlugin(sidebarGroup: SidebarGroup): (options: StarlightTypeDocOptions) => StarlightPlugin {
27 | return function starlightTypeDocPlugin(options: StarlightTypeDocOptions) {
28 | return {
29 | name: 'starlight-typedoc-plugin',
30 | hooks: {
31 | async 'config:setup'({ astroConfig, command, config, logger, updateConfig }) {
32 | if (command === 'preview') return
33 |
34 | try {
35 | const { definitions, outputDirectory, reflections } = await generateTypeDoc(options, astroConfig, logger)
36 |
37 | updateConfig({
38 | sidebar: getSidebarFromReflections(
39 | config.sidebar,
40 | sidebarGroup,
41 | options.sidebar,
42 | reflections,
43 | definitions,
44 | outputDirectory,
45 | ),
46 | })
47 | } catch (error) {
48 | if (options.errorOnEmptyDocumentation === false && error instanceof NoReflectionsError) {
49 | logger.warn('No documentation generated but ignoring as `errorOnEmptyDocumentation` is disabled.')
50 | updateConfig({ sidebar: getSidebarWithoutReflections(config.sidebar, sidebarGroup) })
51 | return
52 | }
53 |
54 | throw error
55 | }
56 | },
57 | },
58 | }
59 | }
60 | }
61 |
62 | export interface StarlightTypeDocOptions {
63 | /**
64 | * The path(s) to the entry point(s) to document.
65 | */
66 | entryPoints: NonNullable
67 | /**
68 | * Whether the plugin should error when no TypeDoc documentation is generated.
69 | * @default true
70 | */
71 | errorOnEmptyDocumentation?: boolean
72 | /**
73 | * The output directory containing the generated documentation markdown files relative to the `src/content/docs/`
74 | * directory.
75 | * @default 'api'
76 | */
77 | output?: string
78 | /**
79 | * The sidebar configuration for the generated documentation.
80 | */
81 | sidebar?: StarlightTypeDocSidebarOptions
82 | /**
83 | * Whether the footer should include previous and next page links for the generated documentation.
84 | * @default false
85 | */
86 | pagination?: boolean
87 | /**
88 | * The path to the `tsconfig.json` file to use for the documentation generation.
89 | */
90 | tsconfig: NonNullable
91 | /**
92 | * Additional TypeDoc configuration.
93 | * @see https://typedoc.org/options
94 | */
95 | typeDoc?: TypeDocConfig
96 | /**
97 | * Whether to watch the entry point(s) for changes and regenerate the documentation when needed.
98 | * @default false
99 | */
100 | watch?: boolean
101 | }
102 |
103 | export interface StarlightTypeDocSidebarOptions {
104 | /**
105 | * Wheter the generated documentation sidebar group should be collapsed by default.
106 | * Note that nested sidebar groups are always collapsed.
107 | * @default false
108 | */
109 | collapsed?: boolean
110 | /**
111 | * The generated documentation sidebar group label.
112 | * @default 'API'
113 | */
114 | label?: string
115 | }
116 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/libs/logger.ts:
--------------------------------------------------------------------------------
1 | import type { AstroIntegrationLogger } from 'astro'
2 | import { LogLevel, Logger } from 'typedoc'
3 |
4 | export class StarlightTypeDocLogger extends Logger {
5 | #logger: AstroIntegrationLogger
6 |
7 | constructor(logger: AstroIntegrationLogger) {
8 | super()
9 |
10 | this.#logger = logger
11 | }
12 |
13 | override log(message: string, level: LogLevel): void {
14 | super.log(message, level)
15 |
16 | if (level < this.level) {
17 | return
18 | }
19 |
20 | switch (level) {
21 | case LogLevel.Error: {
22 | this.#logger.error(message)
23 | break
24 | }
25 | case LogLevel.Warn: {
26 | this.#logger.warn(message)
27 | break
28 | }
29 | case LogLevel.Verbose: {
30 | this.#logger.debug(message)
31 | break
32 | }
33 | default: {
34 | this.#logger.info(message)
35 | break
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/libs/markdown.ts:
--------------------------------------------------------------------------------
1 | export function addFrontmatter(content: string, frontmatter: Record) {
2 | const entries = Object.entries(frontmatter).map(([key, value]) => `${key}: ${value}`)
3 |
4 | if (entries.length === 0) {
5 | return content
6 | }
7 |
8 | return `---\n${entries.join('\n')}\n---\n\n${content}`
9 | }
10 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/libs/starlight.ts:
--------------------------------------------------------------------------------
1 | import path from 'node:path'
2 |
3 | import type { HookParameters } from '@astrojs/starlight/types'
4 | import { slug } from 'github-slugger'
5 | import {
6 | type DeclarationReflection,
7 | type ProjectReflection,
8 | ReflectionKind,
9 | ReferenceReflection,
10 | type ReflectionGroup,
11 | } from 'typedoc'
12 |
13 | import type { StarlightTypeDocSidebarOptions } from '..'
14 |
15 | import type { TypeDocDefinitions } from './typedoc'
16 |
17 | const externalLinkRegex = /^(http|ftp)s?:\/\//
18 |
19 | const sidebarDefaultOptions = {
20 | collapsed: false,
21 | label: 'API',
22 | } satisfies StarlightTypeDocSidebarOptions
23 |
24 | const starlightTypeDocSidebarGroupLabel = Symbol('StarlightTypeDocSidebarGroupLabel')
25 |
26 | export function getSidebarGroupPlaceholder(label = starlightTypeDocSidebarGroupLabel): SidebarGroup {
27 | return {
28 | items: [],
29 | label: label.toString(),
30 | }
31 | }
32 |
33 | export function getSidebarFromReflections(
34 | sidebar: StarlightUserConfigSidebar,
35 | sidebarGroupPlaceholder: SidebarGroup,
36 | options: StarlightTypeDocSidebarOptions = {},
37 | reflections: ProjectReflection | DeclarationReflection,
38 | definitions: TypeDocDefinitions,
39 | baseOutputDirectory: string,
40 | ): StarlightUserConfigSidebar {
41 | if (!sidebar || sidebar.length === 0) {
42 | return sidebar
43 | }
44 |
45 | const sidebarGroup = getSidebarGroupFromReflections(
46 | options,
47 | reflections,
48 | definitions,
49 | baseOutputDirectory,
50 | baseOutputDirectory,
51 | )
52 |
53 | function replaceSidebarGroupPlaceholder(group: SidebarManualGroup): SidebarGroup {
54 | if (group.label === sidebarGroupPlaceholder.label) {
55 | return group.badge ? { ...sidebarGroup, badge: group.badge } : sidebarGroup
56 | }
57 |
58 | if (isSidebarManualGroup(group)) {
59 | return {
60 | ...group,
61 | items: group.items.map((item) => {
62 | return isSidebarManualGroup(item) ? replaceSidebarGroupPlaceholder(item) : item
63 | }),
64 | }
65 | }
66 |
67 | return group
68 | }
69 |
70 | return sidebar.map((item) => {
71 | return isSidebarManualGroup(item) ? replaceSidebarGroupPlaceholder(item) : item
72 | })
73 | }
74 |
75 | export function getSidebarWithoutReflections(
76 | sidebar: StarlightUserConfigSidebar,
77 | sidebarGroupPlaceholder: SidebarGroup,
78 | ): StarlightUserConfigSidebar {
79 | if (!sidebar || sidebar.length === 0) {
80 | return sidebar
81 | }
82 |
83 | function removeSidebarGroupPlaceholder(
84 | entries: NonNullable,
85 | ): NonNullable {
86 | const sidebarWithoutPlaceholder: StarlightUserConfigSidebar = []
87 |
88 | for (const item of entries) {
89 | if (isSidebarManualGroup(item)) {
90 | if (item.label === sidebarGroupPlaceholder.label) continue
91 |
92 | sidebarWithoutPlaceholder.push({
93 | ...item,
94 | items: removeSidebarGroupPlaceholder(item.items),
95 | })
96 | continue
97 | }
98 |
99 | sidebarWithoutPlaceholder.push(item)
100 | }
101 |
102 | return sidebarWithoutPlaceholder
103 | }
104 |
105 | return removeSidebarGroupPlaceholder(sidebar)
106 | }
107 |
108 | function getSidebarGroupFromPackageReflections(
109 | options: StarlightTypeDocSidebarOptions,
110 | reflections: ProjectReflection | DeclarationReflection,
111 | definitions: TypeDocDefinitions,
112 | baseOutputDirectory: string,
113 | ): SidebarGroup {
114 | const groups = (reflections.children ?? []).map((child) => {
115 | const url = definitions[child.id]
116 |
117 | if (!url) {
118 | return undefined
119 | }
120 |
121 | const parsedPath = path.parse(url)
122 |
123 | return getSidebarGroupFromReflections(
124 | options,
125 | child,
126 | definitions,
127 | baseOutputDirectory,
128 | `${baseOutputDirectory}/${parsedPath.dir}`,
129 | child.name,
130 | )
131 | })
132 |
133 | return {
134 | label: options.label ?? sidebarDefaultOptions.label,
135 | collapsed: options.collapsed ?? sidebarDefaultOptions.collapsed,
136 | items: groups.filter((item): item is SidebarGroup => item !== undefined),
137 | }
138 | }
139 |
140 | function getSidebarGroupFromReflections(
141 | options: StarlightTypeDocSidebarOptions,
142 | reflections: ProjectReflection | DeclarationReflection,
143 | definitions: TypeDocDefinitions,
144 | baseOutputDirectory: string,
145 | outputDirectory: string,
146 | label?: string,
147 | ): SidebarGroup {
148 | if ((!reflections.groups || reflections.groups.length === 0) && reflections.children) {
149 | return getSidebarGroupFromPackageReflections(options, reflections, definitions, outputDirectory)
150 | }
151 |
152 | const groups = reflections.groups ?? []
153 |
154 | return {
155 | label: label ?? options.label ?? sidebarDefaultOptions.label,
156 | collapsed: options.collapsed ?? sidebarDefaultOptions.collapsed,
157 | items: groups
158 | .flatMap((group) => {
159 | if (group.title === 'Modules') {
160 | return group.children.map((child) => {
161 | const url = definitions[child.id]
162 |
163 | if (!url || child.variant === 'document') {
164 | return undefined
165 | }
166 |
167 | const parsedPath = path.parse(url)
168 | const isParentKindModule = child.parent?.kind === ReflectionKind.Module
169 |
170 | return getSidebarGroupFromReflections(
171 | { collapsed: true, label: child.name },
172 | child,
173 | definitions,
174 | baseOutputDirectory,
175 | `${outputDirectory}/${isParentKindModule ? parsedPath.dir.split('/').slice(1).join('/') : parsedPath.dir}`,
176 | )
177 | })
178 | }
179 |
180 | if (isReferenceReflectionGroup(group)) {
181 | return getReferencesSidebarGroup(group, definitions, baseOutputDirectory)
182 | }
183 |
184 | const directory = `${outputDirectory}/${slug(group.title.toLowerCase())}`
185 |
186 | // The groups generated using the `@group` tag do not have an associated directory on disk.
187 | const isGroupWithDirectory = group.children.some((child) => {
188 | return path.posix
189 | .join(baseOutputDirectory, definitions[child.id]?.replace('\\', '/') ?? '')
190 | .startsWith(directory)
191 | })
192 |
193 | if (!isGroupWithDirectory) {
194 | return undefined
195 | }
196 |
197 | return {
198 | collapsed: true,
199 | label: group.title,
200 | autogenerate: {
201 | collapsed: true,
202 | directory,
203 | },
204 | }
205 | })
206 | .filter((item): item is SidebarGroup => item !== undefined),
207 | }
208 | }
209 |
210 | function getReferencesSidebarGroup(
211 | group: ReflectionGroup,
212 | definitions: TypeDocDefinitions,
213 | baseOutputDirectory: string,
214 | ): SidebarManualGroup | undefined {
215 | const referenceItems: LinkItem[] = group.children
216 | .map((child) => {
217 | const reference = child as ReferenceReflection
218 | let target = reference.tryGetTargetReflectionDeep()
219 |
220 | if (!target) {
221 | return undefined
222 | }
223 |
224 | if (target.kindOf(ReflectionKind.TypeLiteral) && target.parent) {
225 | target = target.parent
226 | }
227 |
228 | const url = definitions[target.id]
229 |
230 | if (!url) {
231 | return undefined
232 | }
233 |
234 | return {
235 | label: reference.name,
236 | link: getRelativeURL(url, getStarlightTypeDocOutputDirectory(baseOutputDirectory)),
237 | }
238 | })
239 | .filter((item): item is LinkItem => item !== undefined)
240 |
241 | if (referenceItems.length === 0) {
242 | return undefined
243 | }
244 |
245 | return {
246 | label: group.title,
247 | items: referenceItems,
248 | }
249 | }
250 |
251 | export function getAsideMarkdown(type: AsideType, title: string, content: string) {
252 | return `:::${type}[${title}]
253 | ${content}
254 | :::`
255 | }
256 |
257 | export function getRelativeURL(url: string, baseUrl: string, pageUrl?: string): string {
258 | if (externalLinkRegex.test(url)) {
259 | return url
260 | }
261 |
262 | const currentDirname = path.dirname(pageUrl ?? '')
263 | const urlDirname = path.dirname(url)
264 | const relativeUrl =
265 | currentDirname === urlDirname ? url : path.posix.join(currentDirname, path.posix.relative(currentDirname, url))
266 |
267 | const filePath = path.parse(relativeUrl)
268 | const [, anchor] = filePath.base.split('#')
269 | const segments = filePath.dir
270 | .split(/[/\\]/)
271 | .map((segment) => slug(segment))
272 | .filter((segment) => segment !== '')
273 |
274 | let constructedUrl = typeof baseUrl === 'string' ? baseUrl : ''
275 | constructedUrl += segments.length > 0 ? `${segments.join('/')}/` : ''
276 | const fileNameSlug = slug(filePath.name)
277 | constructedUrl += fileNameSlug || filePath.name
278 | constructedUrl += '/'
279 | constructedUrl += anchor && anchor.length > 0 ? `#${anchor}` : ''
280 |
281 | return constructedUrl
282 | }
283 |
284 | export function getStarlightTypeDocOutputDirectory(outputDirectory: string, base = '') {
285 | return path.posix.join(base, `/${outputDirectory}${outputDirectory.endsWith('/') ? '' : '/'}`)
286 | }
287 |
288 | function isSidebarManualGroup(item: NonNullable[number]): item is SidebarManualGroup {
289 | return typeof item === 'object' && 'items' in item
290 | }
291 |
292 | function isReferenceReflectionGroup(group: ReflectionGroup) {
293 | return group.children.every((child) => child instanceof ReferenceReflection)
294 | }
295 |
296 | export type SidebarGroup =
297 | | SidebarManualGroup
298 | | {
299 | autogenerate: {
300 | collapsed?: boolean
301 | directory: string
302 | }
303 | collapsed?: boolean
304 | label: string
305 | }
306 |
307 | interface SidebarManualGroup {
308 | collapsed?: boolean
309 | items: (LinkItem | SidebarGroup)[]
310 | label: string
311 | badge?:
312 | | string
313 | | {
314 | text: string
315 | variant: 'note' | 'danger' | 'success' | 'caution' | 'tip' | 'default'
316 | }
317 | | undefined
318 | }
319 |
320 | interface LinkItem {
321 | label: string
322 | link: string
323 | }
324 |
325 | type AsideType = 'caution' | 'danger' | 'note' | 'tip'
326 |
327 | type StarlightUserConfigSidebar = HookParameters<'config:setup'>['config']['sidebar']
328 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/libs/theme.ts:
--------------------------------------------------------------------------------
1 | import { Reflection, type Comment, type CommentTag, type Options, type CommentDisplayPart } from 'typedoc'
2 | import { MarkdownTheme, MarkdownThemeContext, type MarkdownPageEvent } from 'typedoc-plugin-markdown'
3 |
4 | import { getAsideMarkdown, getRelativeURL } from './starlight'
5 |
6 | const customBlockTagTypes = ['@deprecated'] as const
7 | const customModifiersTagTypes = ['@alpha', '@beta', '@experimental'] as const
8 |
9 | export class StarlightTypeDocTheme extends MarkdownTheme {
10 | override getRenderContext(event: MarkdownPageEvent): StarlightTypeDocThemeRenderContext {
11 | return new StarlightTypeDocThemeRenderContext(this, event, this.application.options)
12 | }
13 | }
14 |
15 | class StarlightTypeDocThemeRenderContext extends MarkdownThemeContext {
16 | constructor(theme: MarkdownTheme, event: MarkdownPageEvent, options: Options) {
17 | super(theme, event, options)
18 |
19 | const superPartials = this.partials
20 |
21 | this.partials = {
22 | ...superPartials,
23 | comment: (comment, options) => {
24 | const filteredComment = { ...comment } as Comment
25 | filteredComment.blockTags = []
26 | filteredComment.modifierTags = new Set<`@${string}`>()
27 |
28 | const customTags: CustomTag[] = []
29 |
30 | for (const blockTag of comment.blockTags) {
31 | if (this.#isCustomBlockCommentTagType(blockTag.tag)) {
32 | customTags.push({ blockTag, type: blockTag.tag })
33 | } else {
34 | blockTag.content = blockTag.content.map((part) => this.#parseCommentDisplayPart(part))
35 | filteredComment.blockTags.push(blockTag)
36 | }
37 | }
38 |
39 | for (const modifierTag of comment.modifierTags) {
40 | if (this.#isCustomModifierCommentTagType(modifierTag)) {
41 | customTags.push({ type: modifierTag })
42 | } else {
43 | filteredComment.modifierTags.add(modifierTag)
44 | }
45 | }
46 |
47 | filteredComment.summary = comment.summary.map((part) => this.#parseCommentDisplayPart(part))
48 |
49 | let markdown = superPartials.comment(filteredComment, options)
50 |
51 | if (options?.showSummary === false) {
52 | return markdown
53 | }
54 |
55 | for (const customCommentTag of customTags) {
56 | switch (customCommentTag.type) {
57 | case '@alpha': {
58 | markdown = this.#addReleaseStageAside(markdown, 'Alpha')
59 | break
60 | }
61 | case '@beta': {
62 | markdown = this.#addReleaseStageAside(markdown, 'Beta')
63 | break
64 | }
65 | case '@deprecated': {
66 | markdown = this.#addDeprecatedAside(markdown, customCommentTag.blockTag)
67 | break
68 | }
69 | case '@experimental': {
70 | markdown = this.#addReleaseStageAside(markdown, 'Experimental')
71 | break
72 | }
73 | }
74 | }
75 |
76 | return markdown
77 | },
78 | }
79 | }
80 |
81 | override urlTo(reflection: Reflection): string {
82 | const outputDirectory = this.options.getValue('starlight-typedoc-output')
83 | const baseUrl = typeof outputDirectory === 'string' ? outputDirectory : ''
84 |
85 | return getRelativeURL(this.router.getFullUrl(reflection), baseUrl, this.page.url)
86 | }
87 |
88 | #parseCommentDisplayPart = (part: CommentDisplayPart): CommentDisplayPart => {
89 | if (
90 | part.kind === 'inline-tag' &&
91 | (part.tag === '@link' || part.tag === '@linkcode' || part.tag === '@linkplain') &&
92 | part.target instanceof Reflection
93 | ) {
94 | return {
95 | ...part,
96 | target: this.urlTo(part.target),
97 | }
98 | }
99 |
100 | return part
101 | }
102 |
103 | #isCustomBlockCommentTagType = (tag: string): tag is CustomBlockTagType => {
104 | return customBlockTagTypes.includes(tag as CustomBlockTagType)
105 | }
106 |
107 | #isCustomModifierCommentTagType = (tag: string): tag is CustomModifierTagType => {
108 | return customModifiersTagTypes.includes(tag as CustomModifierTagType)
109 | }
110 |
111 | #addAside(markdown: string, ...args: Parameters) {
112 | return `${markdown}\n\n${getAsideMarkdown(...args)}`
113 | }
114 |
115 | #addDeprecatedAside(markdown: string, blockTag: CommentTag) {
116 | const content =
117 | blockTag.content.length > 0
118 | ? this.helpers.getCommentParts(blockTag.content)
119 | : 'This API is no longer supported and may be removed in a future release.'
120 |
121 | return this.#addAside(markdown, 'caution', 'Deprecated', content)
122 | }
123 |
124 | #addReleaseStageAside(markdown: string, title: string) {
125 | return this.#addAside(
126 | markdown,
127 | 'caution',
128 | title,
129 | 'This API should not be used in production and may be trimmed from a public release.',
130 | )
131 | }
132 | }
133 |
134 | type CustomBlockTagType = (typeof customBlockTagTypes)[number]
135 | type CustomModifierTagType = (typeof customModifiersTagTypes)[number]
136 |
137 | type CustomTag =
138 | | { type: CustomModifierTagType }
139 | | {
140 | blockTag: CommentTag
141 | type: CustomBlockTagType
142 | }
143 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/libs/typedoc.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'node:fs'
2 | import path from 'node:path'
3 | import url from 'node:url'
4 |
5 | import type { AstroConfig, AstroIntegrationLogger } from 'astro'
6 | import { slug } from 'github-slugger'
7 | import {
8 | Application,
9 | PageEvent,
10 | TSConfigReader,
11 | type TypeDocOptions,
12 | ParameterType,
13 | RendererEvent,
14 | type PageDefinition,
15 | type ProjectReflection,
16 | } from 'typedoc'
17 | import type { MarkdownPageEvent, PluginOptions } from 'typedoc-plugin-markdown'
18 |
19 | import type { StarlightTypeDocOptions } from '..'
20 |
21 | import { StarlightTypeDocLogger } from './logger'
22 | import { addFrontmatter } from './markdown'
23 | import { getRelativeURL, getStarlightTypeDocOutputDirectory } from './starlight'
24 | import { StarlightTypeDocTheme } from './theme'
25 |
26 | const defaultTypeDocConfig: TypeDocConfig = {
27 | excludeInternal: true,
28 | excludePrivate: true,
29 | excludeProtected: true,
30 | githubPages: false,
31 | readme: 'none',
32 | theme: 'starlight-typedoc',
33 | }
34 |
35 | const markdownPluginConfig: TypeDocConfig = {
36 | hideBreadcrumbs: true,
37 | hidePageHeader: true,
38 | hidePageTitle: true,
39 | }
40 |
41 | export async function generateTypeDoc(
42 | options: StarlightTypeDocOptions,
43 | config: AstroConfig,
44 | logger: AstroIntegrationLogger,
45 | ) {
46 | const outputDirectory = options.output ?? 'api'
47 |
48 | const app = await bootstrapApp(
49 | options.entryPoints,
50 | options.tsconfig,
51 | options.typeDoc,
52 | {
53 | base: config.base,
54 | directory: outputDirectory,
55 | path: path.join(url.fileURLToPath(config.srcDir), 'content/docs', outputDirectory),
56 | },
57 | options.pagination ?? false,
58 | logger,
59 | )
60 |
61 | const definitions: TypeDocDefinitions = {}
62 | app.renderer.on(RendererEvent.END, (event) => {
63 | for (const page of event.pages) {
64 | if (!('id' in page.model)) continue
65 | definitions[page.model.id] = page.url
66 | }
67 | })
68 |
69 | let reflections: ProjectReflection | undefined
70 |
71 | if (options.watch) {
72 | reflections = await new Promise((resolve) => {
73 | void app.convertAndWatch(async (reflections) => {
74 | await app.generateOutputs(reflections)
75 | resolve(reflections)
76 | })
77 | })
78 | } else {
79 | reflections = await app.convert()
80 |
81 | if (
82 | (!reflections?.groups || reflections.groups.length === 0) &&
83 | !reflections?.children?.some((child) => (child.groups ?? []).length > 0)
84 | ) {
85 | throw new NoReflectionsError()
86 | }
87 |
88 | await app.generateOutputs(reflections)
89 | }
90 |
91 | return { definitions, outputDirectory, reflections }
92 | }
93 |
94 | async function bootstrapApp(
95 | entryPoints: NonNullable,
96 | tsconfig: NonNullable,
97 | config: TypeDocConfig = {},
98 | output: TypeDocOutput,
99 | pagination: boolean,
100 | logger: AstroIntegrationLogger,
101 | ) {
102 | const pagesToRemove: string[] = []
103 | const outputDirectory = getStarlightTypeDocOutputDirectory(output.directory, output.base)
104 |
105 | const app = await Application.bootstrapWithPlugins({
106 | ...defaultTypeDocConfig,
107 | ...markdownPluginConfig,
108 | ...config,
109 | // typedoc-plugin-markdown must be applied here so that it isn't overwritten by any additional applied plugins
110 | plugin: [...(config.plugin ?? []), 'typedoc-plugin-markdown'],
111 | entryPoints,
112 | tsconfig,
113 | outputs: [{ name: 'markdown', path: output.path }],
114 | })
115 | app.logger = new StarlightTypeDocLogger(logger)
116 | app.options.addReader(new TSConfigReader())
117 | app.renderer.defineTheme('starlight-typedoc', StarlightTypeDocTheme)
118 | app.renderer.on(PageEvent.BEGIN, (event) => {
119 | onRendererPageBegin(event as MarkdownPageEvent, outputDirectory, pagination)
120 | })
121 | app.renderer.on(PageEvent.END, (event) => {
122 | const shouldRemovePage = onRendererPageEnd(event as MarkdownPageEvent, outputDirectory, pagination)
123 | if (shouldRemovePage) {
124 | pagesToRemove.push(event.filename)
125 | }
126 | })
127 | app.renderer.on(RendererEvent.END, () => {
128 | onRendererEnd(pagesToRemove)
129 | })
130 | app.options.addDeclaration({
131 | defaultValue: outputDirectory,
132 | help: 'The starlight-typedoc output directory containing the generated documentation markdown files relative to the `src/content/docs/` directory.',
133 | name: 'starlight-typedoc-output',
134 | type: ParameterType.String,
135 | })
136 |
137 | return app
138 | }
139 |
140 | function onRendererPageBegin(event: MarkdownPageEvent, outputDirectory: string, pagination: boolean) {
141 | if (event.frontmatter) {
142 | event.frontmatter = getModelFrontmatter(event, outputDirectory, {
143 | ...event.frontmatter,
144 | editUrl: false,
145 | next: pagination,
146 | prev: pagination,
147 | title: event.model.name,
148 | })
149 | }
150 | }
151 |
152 | // Returning `true` will delete the page from the filesystem.
153 | function onRendererPageEnd(event: MarkdownPageEvent, outputDirectory: string, pagination: boolean) {
154 | if (!event.contents) {
155 | return false
156 | } else if (/^.+[/\\]README\.md$/.test(event.url)) {
157 | // Do not save `README.md` files for multiple entry points.
158 | // It is no longer supported in TypeDoc 0.26.0 to call `event.preventDefault()` to prevent the file from being saved.
159 | // https://github.com/TypeStrong/typedoc/commit/6e6b3b662c92b3d4bc24b6c6c0c6e227e063c759
160 | // event.preventDefault()
161 | return true
162 | }
163 |
164 | if (!event.frontmatter) {
165 | event.contents = addFrontmatter(
166 | event.contents,
167 | getModelFrontmatter(event, outputDirectory, {
168 | editUrl: false,
169 | next: pagination,
170 | prev: pagination,
171 | // Wrap in quotes to prevent issue with special characters in frontmatter
172 | title: `"${event.model.name}"`,
173 | }),
174 | )
175 | }
176 |
177 | return false
178 | }
179 |
180 | function onRendererEnd(pagesToRemove: string[]) {
181 | for (const page of pagesToRemove) {
182 | fs.rmSync(page, { force: true })
183 | }
184 | }
185 |
186 | function getModelFrontmatter(
187 | event: MarkdownPageEvent,
188 | outputDirectory: string,
189 | frontmatter: NonNullable,
190 | ) {
191 | const defaultSlug = slug(event.model.name)
192 |
193 | if (defaultSlug.length === 0) {
194 | frontmatter['slug'] = getRelativeURL(event.url, outputDirectory, event.url).replaceAll(/^\/|\/$/g, '')
195 | }
196 |
197 | return frontmatter
198 | }
199 |
200 | export class NoReflectionsError extends Error {
201 | constructor() {
202 | super('Failed to generate TypeDoc documentation.')
203 | }
204 | }
205 |
206 | export type TypeDocConfig = Partial & PluginOptions>
207 | export type TypeDocDefinitions = Record
208 |
209 | interface TypeDocOutput {
210 | base: string
211 | directory: string
212 | path: string
213 | }
214 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starlight-typedoc",
3 | "version": "0.21.3",
4 | "license": "MIT",
5 | "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
6 | "author": "HiDeoo (https://hideoo.dev)",
7 | "type": "module",
8 | "main": "dist/index.cjs",
9 | "module": "dist/index.js",
10 | "types": "dist/index.d.ts",
11 | "exports": {
12 | ".": "./index.ts",
13 | "./package.json": "./package.json"
14 | },
15 | "scripts": {
16 | "test": "pnpm test:unit && pnpm test:e2e",
17 | "test:unit": "vitest",
18 | "test:e2e": "pnpm run test:e2e:basics && pnpm run test:e2e:packages && pnpm run test:e2e:plugins",
19 | "test:e2e:basics": "cross-env TEST_TYPE=basics pnpm run playwright",
20 | "test:e2e:packages": "cross-env TEST_TYPE=packages pnpm run playwright",
21 | "test:e2e:plugins": "cross-env TEST_TYPE=plugins pnpm run playwright",
22 | "playwright": "playwright install --with-deps chromium && playwright test",
23 | "lint": "eslint . --cache --max-warnings=0"
24 | },
25 | "dependencies": {
26 | "github-slugger": "^2.0.0"
27 | },
28 | "devDependencies": {
29 | "@playwright/test": "^1.49.1",
30 | "@types/node": "^18.19.68",
31 | "cross-env": "^7.0.3",
32 | "typescript": "^5.7.2",
33 | "vitest": "2.1.6"
34 | },
35 | "peerDependencies": {
36 | "@astrojs/starlight": ">=0.32.0",
37 | "typedoc": ">=0.28.0",
38 | "typedoc-plugin-markdown": ">=4.6.0"
39 | },
40 | "engines": {
41 | "node": ">=18.17.1"
42 | },
43 | "packageManager": "pnpm@8.6.1",
44 | "publishConfig": {
45 | "access": "public",
46 | "provenance": true
47 | },
48 | "sideEffects": false,
49 | "keywords": [
50 | "starlight",
51 | "plugin",
52 | "typedoc",
53 | "typescript",
54 | "documentation",
55 | "astro"
56 | ],
57 | "homepage": "https://github.com/HiDeoo/starlight-typedoc",
58 | "repository": {
59 | "type": "git",
60 | "url": "https://github.com/HiDeoo/starlight-typedoc.git",
61 | "directory": "packages/starlight-typedoc"
62 | },
63 | "bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
64 | }
65 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, devices } from '@playwright/test'
2 |
3 | export default defineConfig({
4 | forbidOnly: !!process.env['CI'],
5 | projects: [
6 | {
7 | name: 'chromium',
8 | use: { ...devices['Desktop Chrome'], headless: true },
9 | },
10 | ],
11 | testDir: `tests/e2e/${process.env['TEST_TYPE']}`,
12 | webServer:
13 | process.env['TEST_TYPE'] === 'basics'
14 | ? [
15 | {
16 | command: 'pnpm build:single-entrypoints && pnpm preview:single-entrypoints',
17 | cwd: '../../example',
18 | reuseExistingServer: !process.env['CI'],
19 | url: 'http://localhost:4321',
20 | },
21 | {
22 | command: 'pnpm build:multiple-entrypoints && pnpm preview:multiple-entrypoints',
23 | cwd: '../../example',
24 | reuseExistingServer: !process.env['CI'],
25 | url: 'http://localhost:4322/multiple-entrypoints/',
26 | },
27 | ]
28 | : process.env['TEST_TYPE'] === 'plugins'
29 | ? [
30 | {
31 | command: 'pnpm build:multiple-plugins && pnpm preview:multiple-plugins',
32 | cwd: '../../example',
33 | reuseExistingServer: !process.env['CI'],
34 | url: 'http://localhost:4321/multiple-plugins/',
35 | },
36 | ]
37 | : [
38 | {
39 | command: 'pnpm build:packages-entrypoints && pnpm preview:packages-entrypoints',
40 | cwd: '../../example',
41 | reuseExistingServer: !process.env['CI'],
42 | url: 'http://localhost:4321/packages-entrypoints/',
43 | },
44 | ],
45 | })
46 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/basics/asides.test.ts:
--------------------------------------------------------------------------------
1 | import type { DocPage } from '../fixtures/DocPage'
2 | import { expect, test } from '../test'
3 |
4 | test('should use an aside for the deprecated tag with no content', async ({ docPage }) => {
5 | await docPage.goto('functions/dothingb')
6 |
7 | const name = 'Deprecated'
8 | const { aside, title, content } = await getAside(docPage, name)
9 |
10 | await expect(aside).toBeVisible()
11 | expect(title).toBe(name)
12 | expect(content).toBe('This API is no longer supported and may be removed in a future release.')
13 | })
14 |
15 | test('should use an aside for the deprecated tag with custom content', async ({ docPage }) => {
16 | await docPage.goto('functions/dothingc')
17 |
18 | const name = 'Deprecated'
19 | const { aside, title, content } = await getAside(docPage, name)
20 |
21 | await expect(aside).toBeVisible()
22 | expect(title).toBe(name)
23 | expect(content).toBe('Use the new doThingFaster function instead.')
24 | })
25 |
26 | const releaseStageCases: [releaseStage: string, url: string][] = [
27 | ['Alpha', 'classes/bar'],
28 | ['Beta', 'variables/anobject'],
29 | ['Experimental', 'interfaces/thing'],
30 | ]
31 |
32 | for (const [releaseStage, url] of releaseStageCases) {
33 | test(`should use an aside for the ${releaseStage.toLowerCase()} tag`, async ({ docPage }) => {
34 | await docPage.goto(url)
35 |
36 | const { aside, title, content } = await getAside(docPage, releaseStage)
37 |
38 | await expect(aside).toBeVisible()
39 | expect(title).toBe(releaseStage)
40 | expect(content).toBe('This API should not be used in production and may be trimmed from a public release.')
41 | })
42 | }
43 |
44 | async function getAside(docPage: DocPage, name: string) {
45 | const aside = docPage.content.getByRole('complementary', { exact: true, name })
46 |
47 | const paragraphs = aside.getByRole('paragraph', { includeHidden: true })
48 | const title = await paragraphs.nth(0).textContent()
49 | const content = await paragraphs.nth(1).textContent()
50 |
51 | return { aside, title, content }
52 | }
53 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/basics/content.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '../test'
2 |
3 | test('should add titles to the frontmatter', async ({ docPage }) => {
4 | await docPage.goto('classes/foo')
5 |
6 | expect(docPage.title).toBe('Foo')
7 |
8 | await docPage.goto('functions/dothinga')
9 |
10 | expect(docPage.title).toBe('doThingA')
11 | })
12 |
13 | test('should properly format links for a single entry point', async ({ docPage }) => {
14 | await docPage.goto('classes/classfoo')
15 |
16 | const barLinkLocators = await docPage.content.getByRole('link', { exact: true, name: 'Bar' }).all()
17 | const barLinkHrefs = await Promise.all(barLinkLocators.map((link) => link.getAttribute('href')))
18 |
19 | expect(barLinkHrefs.every((href) => href === '/api/classes/classbar/')).toBe(true)
20 | })
21 |
22 | test('should properly format links for multiple entry points', async ({ docPage }) => {
23 | docPage.useMultipleEntryPoints()
24 |
25 | await docPage.goto('modulefoo/classes/classfoo')
26 |
27 | const barLinkLocators = await docPage.content.getByRole('link', { exact: true, name: 'Bar' }).all()
28 | const barLinkHrefs = await Promise.all(barLinkLocators.map((link) => link.getAttribute('href')))
29 |
30 | expect(barLinkHrefs.every((href) => href === '/api-multiple-entrypoints/modulebar/classes/classbar/')).toBe(true)
31 | })
32 |
33 | test('should properly format links with anchors for a single entry point', async ({ docPage }) => {
34 | await docPage.goto('classes/foo')
35 |
36 | const barConstructorLinkHref = await docPage.content
37 | .getByRole('link', { exact: true, name: 'constructor' })
38 | .getAttribute('href')
39 |
40 | expect(barConstructorLinkHref).toEqual('/api/classes/bar/#constructor')
41 | })
42 |
43 | test('should properly format links with anchors for multiple entry points', async ({ docPage }) => {
44 | docPage.useMultipleEntryPoints()
45 |
46 | await docPage.goto('foo/classes/foo')
47 |
48 | const barConstructorLinkHref = await docPage.content
49 | .getByRole('link', { exact: true, name: 'constructor' })
50 | .getAttribute('href')
51 |
52 | expect(barConstructorLinkHref).toEqual('/multiple-entrypoints/api-multiple-entrypoints/bar/classes/bar/#constructor')
53 | })
54 |
55 | test('should disable edit links', async ({ docPage }) => {
56 | await docPage.goto('../guides/example')
57 |
58 | await expect(docPage.page.getByRole('link', { exact: true, name: 'Edit page' })).toBeVisible()
59 |
60 | await docPage.goto('classes/classbar')
61 |
62 | await expect(docPage.page.getByRole('link', { exact: true, name: 'Edit page' })).not.toBeVisible()
63 | })
64 |
65 | test('should support TypeDoc plugins', async ({ docPage }) => {
66 | await docPage.goto('classes/foo')
67 |
68 | const mdnLink = docPage.page.getByRole('link', { exact: true, name: 'HTMLElement' })
69 |
70 | await expect(mdnLink).toBeVisible()
71 |
72 | const mdnLinkHref = await mdnLink.getAttribute('href')
73 |
74 | expect(mdnLinkHref?.startsWith('https://developer.mozilla.org')).toBe(true)
75 | })
76 |
77 | test('should support plugins customizing the frontmatter', async ({ docPage }) => {
78 | await docPage.goto('variables/anobject')
79 |
80 | const sidebarBadge = docPage.page.locator('a[aria-current="page"] .sl-badge')
81 |
82 | await expect(sidebarBadge).toBeVisible()
83 |
84 | expect(await sidebarBadge.textContent()).toBe('New')
85 | })
86 |
87 | test('should properly format links in summary', async ({ docPage }) => {
88 | await docPage.goto('functions/dothingfaster')
89 |
90 | await docPage.content.getByRole('link', { exact: true, name: 'doThingB' }).click()
91 | await docPage.page.waitForURL('**/api/functions/dothingb/')
92 | })
93 |
94 | test('should properly format links in block tag comments', async ({ docPage }) => {
95 | await docPage.goto('classes/foo')
96 |
97 | await docPage.content.locator('h4:has-text("See") + p a').click()
98 | await docPage.page.waitForURL('**/api/interfaces/thing/')
99 | })
100 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/basics/pagination.test.ts:
--------------------------------------------------------------------------------
1 | import type { DocPage } from '../fixtures/DocPage'
2 | import { expect, test } from '../test'
3 |
4 | test('should not include pagination links by default', async ({ docPage }) => {
5 | await docPage.goto('classes/foo')
6 |
7 | const { next, prev } = getPrevNext(docPage)
8 |
9 | await expect(next).not.toBeVisible()
10 | await expect(prev).not.toBeVisible()
11 | })
12 |
13 | test('should include pagination links if configured to do so', async ({ docPage }) => {
14 | docPage.useMultipleEntryPoints()
15 |
16 | await docPage.goto('bar/classes/bar')
17 |
18 | const { next, prev } = getPrevNext(docPage)
19 |
20 | await expect(next).toBeVisible()
21 | await expect(prev).toBeVisible()
22 | })
23 |
24 | function getPrevNext(docPage: DocPage) {
25 | return {
26 | next: docPage.page.locator('a[rel="next"]'),
27 | prev: docPage.page.locator('a[rel="prev"]'),
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/basics/sidebar.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '../test'
2 |
3 | const singleEntrypointUrl = 'classes/foo'
4 | const multipleEntrypointsUrl = 'bar/classes/bar'
5 |
6 | test('should include the TypeDoc sidebar group for a single entry point', async ({ docPage }) => {
7 | await docPage.goto(singleEntrypointUrl)
8 |
9 | await expect(docPage.typeDocSidebarLabel).toBeVisible()
10 | })
11 |
12 | test('should include the TypeDoc sidebar group for multiple entry points', async ({ docPage }) => {
13 | docPage.useMultipleEntryPoints()
14 |
15 | await docPage.goto(multipleEntrypointsUrl)
16 |
17 | await expect(docPage.typeDocSidebarLabel).toBeVisible()
18 | })
19 |
20 | test('should not collapse the TypeDoc sidebar group by default', async ({ docPage }) => {
21 | await docPage.goto(singleEntrypointUrl)
22 |
23 | await docPage.page.getByRole('link', { name: 'Example Guide' }).click()
24 |
25 | expect(await docPage.isTypeDocSidebarCollapsed()).toBe(false)
26 | })
27 |
28 | test('should collapse the TypeDoc sidebar group if specified', async ({ docPage }) => {
29 | docPage.useMultipleEntryPoints()
30 |
31 | await docPage.goto(multipleEntrypointsUrl)
32 |
33 | await docPage.page.getByRole('link', { name: 'Example Guide' }).first().click()
34 |
35 | expect(await docPage.isTypeDocSidebarCollapsed()).toBe(true)
36 | })
37 |
38 | test('should generate the proper items for for a single entry point', async ({ docPage }) => {
39 | await docPage.goto(singleEntrypointUrl)
40 |
41 | const items = await docPage.getTypeDocSidebarItems()
42 |
43 | expect(items).toMatchObject([
44 | {
45 | label: 'Enumerations',
46 | items: [{ name: 'ANumericEnum' }, { name: 'AStringEnum' }],
47 | collapsed: true,
48 | },
49 | {
50 | label: 'Classes',
51 | items: [{ name: 'Bar' }, { name: 'Baz' }, { name: 'Foo' }],
52 | collapsed: true,
53 | },
54 | {
55 | label: 'Interfaces',
56 | items: [{ name: 'Thing' }],
57 | collapsed: true,
58 | },
59 | {
60 | label: 'Type Aliases',
61 | items: [{ name: 'Things' }],
62 | collapsed: true,
63 | },
64 | {
65 | label: 'Variables',
66 | items: [{ name: 'anObject' }, { name: 'anObjectAsConst' }, { name: 'anUndefinedString' }, { name: 'aString' }],
67 | collapsed: true,
68 | },
69 | {
70 | label: 'Functions',
71 | items: [
72 | { name: '$' },
73 | { name: 'doThingA' },
74 | { name: 'doThingB' },
75 | { name: 'doThingC' },
76 | { name: 'doThingFaster' },
77 | ],
78 | collapsed: true,
79 | },
80 | {
81 | label: 'References',
82 | items: [{ name: 'doThingARef' }],
83 | collapsed: true,
84 | },
85 | // `MyCustomGroup` defined in `fixtures/basics/src/Baz.ts` does not have a directory on disk which means it should
86 | // not be included in the sidebar.
87 | ])
88 | })
89 |
90 | test('should generate the proper items for for multiple entry points', async ({ docPage }) => {
91 | docPage.useMultipleEntryPoints()
92 |
93 | await docPage.goto(multipleEntrypointsUrl)
94 |
95 | const items = await docPage.getTypeDocSidebarItems()
96 |
97 | expect(items).toMatchObject([
98 | {
99 | label: 'Bar',
100 | items: [
101 | {
102 | label: 'Classes',
103 | items: [{ name: 'Bar' }],
104 | },
105 | ],
106 | collapsed: true,
107 | },
108 | {
109 | label: 'Foo',
110 | items: [
111 | {
112 | label: 'Classes',
113 | items: [{ name: 'Foo' }],
114 | },
115 | ],
116 | collapsed: true,
117 | },
118 | ])
119 | })
120 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/basics/slug.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '../test'
2 |
3 | test('handles pages with a name that would lead to an empty slug', async ({ docPage }) => {
4 | await docPage.goto('functions/$')
5 |
6 | await expect(docPage.content.getByText('A function that print dollars.')).toBeVisible()
7 | expect(await docPage.sidebar.locator('a[aria-current="page"]').getAttribute('href')).toBe('/api/functions/$/')
8 | })
9 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/fixtures/DocPage.ts:
--------------------------------------------------------------------------------
1 | import type { Locator, Page } from '@playwright/test'
2 |
3 | export class DocPage {
4 | title: string | null = null
5 |
6 | #useMultipleEntryPoints = false
7 | #usePackagesEntryPoints = false
8 | #useMultiplePlugins = false
9 |
10 | constructor(public readonly page: Page) {}
11 |
12 | async goto(url: string) {
13 | const baseDir = this.#useMultipleEntryPoints
14 | ? 'multiple-entrypoints/api-multiple-entrypoints'
15 | : this.#usePackagesEntryPoints
16 | ? 'packages-entrypoints/api-packages-entrypoints'
17 | : this.#useMultiplePlugins
18 | ? 'multiple-plugins'
19 | : 'api'
20 | const baseUrl = `http://localhost:${this.#useMultipleEntryPoints ? 4322 : 4321}/${baseDir}`
21 |
22 | await this.page.goto(`${baseUrl}${url.startsWith('/') ? url : `/${url}`}${url.endsWith('/') ? '' : '/'}`)
23 |
24 | const title = await this.content.getByRole('heading', { level: 1 }).textContent()
25 | this.title = title ? title.trim() : null
26 | }
27 |
28 | useMultipleEntryPoints() {
29 | this.#useMultipleEntryPoints = true
30 | }
31 |
32 | usePackagesEntryPoints() {
33 | this.#usePackagesEntryPoints = true
34 | }
35 |
36 | useMultiplePlugins() {
37 | this.#useMultiplePlugins = true
38 | }
39 |
40 | get content() {
41 | return this.page.getByRole('main')
42 | }
43 |
44 | get sidebar() {
45 | return this.page.getByRole('navigation', { name: 'Main' })
46 | }
47 |
48 | get typeDocSidebarLabel() {
49 | return this.#typeDocSidebarRootDetails.getByText(this.#expectedTypeDocSidebarLabel, {
50 | exact: true,
51 | })
52 | }
53 |
54 | get #expectedTypeDocSidebarLabel() {
55 | return this.#useMultipleEntryPoints || this.#usePackagesEntryPoints ? 'API' : 'API (auto-generated)'
56 | }
57 |
58 | get #typeDocSidebarRootDetails() {
59 | return this.sidebar
60 | .getByRole('listitem')
61 | .locator(`details:has(summary > div > span:has-text("${this.#expectedTypeDocSidebarLabel}"))`)
62 | }
63 |
64 | getTypeDocSidebarItems() {
65 | return this.#getTypeDocSidebarChildrenItems(this.#typeDocSidebarRootDetails.locator('> ul'))
66 | }
67 |
68 | async getSidebarItems() {
69 | return this.#getTypeDocSidebarChildrenItems(this.sidebar.locator('ul.top-level'))
70 | }
71 |
72 | async #getTypeDocSidebarChildrenItems(list: Locator): Promise {
73 | const items: TypeDocSidebarItem[] = []
74 |
75 | for (const category of await list.locator('> li > details').all()) {
76 | items.push({
77 | collapsed: !(await category.getAttribute('open')),
78 | label: await category.locator(`> summary > div > span:not(.sl-badge)`).textContent(),
79 | items: await this.#getTypeDocSidebarChildrenItems(category.locator('> ul')),
80 | })
81 | }
82 |
83 | for (const link of await list.locator('> li > a > span:not(.sl-badge)').all()) {
84 | const name = await link.textContent()
85 |
86 | items.push({ name: name ? name.trim() : null })
87 | }
88 |
89 | return items
90 | }
91 |
92 | async isTypeDocSidebarCollapsed() {
93 | return (await this.#typeDocSidebarRootDetails.getAttribute('open')) === null
94 | }
95 | }
96 |
97 | type TypeDocSidebarItem = TypeDocSidebarItemGroup | TypeDocSidebarItemLink
98 |
99 | interface TypeDocSidebarItemLink {
100 | name: string | null
101 | }
102 |
103 | interface TypeDocSidebarItemGroup {
104 | collapsed: boolean
105 | items: (TypeDocSidebarItemGroup | TypeDocSidebarItemLink)[]
106 | label: string | null
107 | }
108 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/packages/content.test.ts:
--------------------------------------------------------------------------------
1 | import { test } from '../test'
2 |
3 | test('should properly format links', async ({ docPage }) => {
4 | docPage.usePackagesEntryPoints()
5 |
6 | await docPage.goto('bar/functions/dobarbetter')
7 |
8 | await docPage.content.getByRole('link', { exact: true, name: 'DoBarBetterOptions' }).click()
9 | await docPage.page.waitForURL('**/api-packages-entrypoints/bar/interfaces/dobarbetteroptions/**')
10 | })
11 |
12 | test('should properly format links in block tag comments', async ({ docPage }) => {
13 | docPage.usePackagesEntryPoints()
14 |
15 | await docPage.goto('foo/functions/dofoofaster')
16 |
17 | await docPage.content.getByRole('link', { exact: true, name: 'doFoo' }).click()
18 | await docPage.page.waitForURL('**/api-packages-entrypoints/foo/functions/dofoo/')
19 | })
20 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/packages/sidebar.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '../test'
2 |
3 | const url = 'foo/functions/dofoo'
4 |
5 | test('should include the TypeDoc sidebar group', async ({ docPage }) => {
6 | docPage.usePackagesEntryPoints()
7 |
8 | await docPage.goto(url)
9 |
10 | await expect(docPage.typeDocSidebarLabel).toBeVisible()
11 | })
12 |
13 | test('should generate the proper items for for multiple entry points', async ({ docPage }) => {
14 | docPage.usePackagesEntryPoints()
15 |
16 | await docPage.goto(url)
17 |
18 | const items = await docPage.getTypeDocSidebarItems()
19 |
20 | expect(items).toMatchObject([
21 | {
22 | label: 'bar',
23 | items: [
24 | {
25 | label: 'Interfaces',
26 | items: [{ name: 'DoBarBetterOptions' }],
27 | },
28 | {
29 | label: 'Functions',
30 | items: [{ name: 'doBar' }, { name: 'doBarBetter' }],
31 | },
32 | ],
33 | collapsed: true,
34 | },
35 | {
36 | label: 'foo',
37 | items: [
38 | {
39 | label: 'Functions',
40 | items: [{ name: 'doFoo' }, { name: 'doFooFaster' }],
41 | },
42 | ],
43 | collapsed: true,
44 | },
45 | ])
46 | })
47 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/plugins/sidebar.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, test } from '../test'
2 |
3 | test('should generate the proper items for for multiple plugins with different configurations', async ({ docPage }) => {
4 | docPage.useMultiplePlugins()
5 |
6 | await docPage.goto('api-multiple-plugins-bar/classes/bar')
7 |
8 | const items = await docPage.getSidebarItems()
9 |
10 | expect(items).toMatchObject([
11 | {
12 | label: 'Bar Content',
13 | items: [
14 | {
15 | collapsed: true,
16 | label: 'Bar API',
17 | items: [
18 | {
19 | collapsed: true,
20 | label: 'Classes',
21 | items: [{ name: 'Bar' }],
22 | },
23 | ],
24 | },
25 | ],
26 | },
27 | {
28 | label: 'Foo Content',
29 | items: [
30 | {
31 | collapsed: true,
32 | label: 'Foo API',
33 | items: [
34 | {
35 | collapsed: true,
36 | label: 'Classes',
37 | items: [{ name: 'Foo' }],
38 | },
39 | ],
40 | },
41 | ],
42 | },
43 | ])
44 | })
45 |
46 | test('should support having a badge', async ({ docPage }) => {
47 | docPage.useMultiplePlugins()
48 |
49 | await docPage.goto('api-multiple-plugins-bar/classes/bar')
50 |
51 | await expect(docPage.page.locator('.sl-badge:has-text("generated")')).toBeVisible()
52 | })
53 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/e2e/test.ts:
--------------------------------------------------------------------------------
1 | import { test as base } from '@playwright/test'
2 |
3 | import { DocPage } from './fixtures/DocPage'
4 |
5 | export { expect } from '@playwright/test'
6 |
7 | export const test = base.extend({
8 | docPage: async ({ page }, use) => {
9 | const docPage = new DocPage(page)
10 |
11 | await use(docPage)
12 | },
13 | })
14 |
15 | interface Fixtures {
16 | docPage: DocPage
17 | }
18 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/unit/sidebar.test.ts:
--------------------------------------------------------------------------------
1 | import type { ProjectReflection } from 'typedoc'
2 | import { describe, expect, test } from 'vitest'
3 |
4 | import { typeDocSidebarGroup } from '../../index'
5 | import { getSidebarFromReflections, getSidebarWithoutReflections } from '../../libs/starlight'
6 |
7 | const gettingStartedLink = {
8 | label: 'Getting Started',
9 | link: '/guides/getting-started/',
10 | }
11 |
12 | describe('getSidebarFromReflections', () => {
13 | test('should not do anything for an undefined sidebar', () => {
14 | expect(getTestSidebar([])).toEqual([])
15 | })
16 |
17 | test('should not do anything for an empty sidebar', () => {
18 | expect(getTestSidebar([])).toEqual([])
19 | })
20 |
21 | test('should not do anything for a sidebar without a placeholder', () => {
22 | expect(getTestSidebar([gettingStartedLink])).toEqual([gettingStartedLink])
23 | })
24 |
25 | test('should replace a placeholder at the rool level', () => {
26 | expect(getTestSidebar([gettingStartedLink, typeDocSidebarGroup])).toMatchInlineSnapshot(`
27 | [
28 | {
29 | "label": "Getting Started",
30 | "link": "/guides/getting-started/",
31 | },
32 | {
33 | "collapsed": false,
34 | "items": [],
35 | "label": "API",
36 | },
37 | ]
38 | `)
39 | })
40 |
41 | test('should replace a nested placeholder', () => {
42 | expect(
43 | getTestSidebar([
44 | {
45 | label: 'Guides',
46 | items: [typeDocSidebarGroup, gettingStartedLink],
47 | },
48 | ]),
49 | ).toMatchInlineSnapshot(`
50 | [
51 | {
52 | "items": [
53 | {
54 | "collapsed": false,
55 | "items": [],
56 | "label": "API",
57 | },
58 | {
59 | "label": "Getting Started",
60 | "link": "/guides/getting-started/",
61 | },
62 | ],
63 | "label": "Guides",
64 | },
65 | ]
66 | `)
67 | })
68 |
69 | test('should replace multiple placeholders', () => {
70 | expect(
71 | getTestSidebar([
72 | gettingStartedLink,
73 | {
74 | label: 'Guides',
75 | items: [gettingStartedLink, typeDocSidebarGroup],
76 | },
77 | typeDocSidebarGroup,
78 | ]),
79 | ).toMatchInlineSnapshot(`
80 | [
81 | {
82 | "label": "Getting Started",
83 | "link": "/guides/getting-started/",
84 | },
85 | {
86 | "items": [
87 | {
88 | "label": "Getting Started",
89 | "link": "/guides/getting-started/",
90 | },
91 | {
92 | "collapsed": false,
93 | "items": [],
94 | "label": "API",
95 | },
96 | ],
97 | "label": "Guides",
98 | },
99 | {
100 | "collapsed": false,
101 | "items": [],
102 | "label": "API",
103 | },
104 | ]
105 | `)
106 | })
107 | })
108 |
109 | describe('getSidebarWithoutReflections', () => {
110 | test('should not do anything for an undefined sidebar', () => {
111 | expect(getTestSidebarWithoutReflections([])).toEqual([])
112 | })
113 |
114 | test('should not do anything for an empty sidebar', () => {
115 | expect(getTestSidebarWithoutReflections([])).toEqual([])
116 | })
117 |
118 | test('should not do anything for a sidebar without a placeholder', () => {
119 | expect(getTestSidebarWithoutReflections([gettingStartedLink])).toEqual([gettingStartedLink])
120 | })
121 |
122 | test('should remove a placeholder at the rool level', () => {
123 | expect(getTestSidebarWithoutReflections([gettingStartedLink, typeDocSidebarGroup])).toMatchInlineSnapshot(`
124 | [
125 | {
126 | "label": "Getting Started",
127 | "link": "/guides/getting-started/",
128 | },
129 | ]
130 | `)
131 | })
132 |
133 | test('should remove a nested placeholder', () => {
134 | expect(
135 | getTestSidebarWithoutReflections([
136 | {
137 | label: 'Guides',
138 | items: [typeDocSidebarGroup, gettingStartedLink],
139 | },
140 | ]),
141 | ).toMatchInlineSnapshot(`
142 | [
143 | {
144 | "items": [
145 | {
146 | "label": "Getting Started",
147 | "link": "/guides/getting-started/",
148 | },
149 | ],
150 | "label": "Guides",
151 | },
152 | ]
153 | `)
154 | })
155 |
156 | test('should remove multiple placeholders', () => {
157 | expect(
158 | getTestSidebarWithoutReflections([
159 | gettingStartedLink,
160 | {
161 | label: 'Guides',
162 | items: [gettingStartedLink, typeDocSidebarGroup],
163 | },
164 | typeDocSidebarGroup,
165 | ]),
166 | ).toMatchInlineSnapshot(`
167 | [
168 | {
169 | "label": "Getting Started",
170 | "link": "/guides/getting-started/",
171 | },
172 | {
173 | "items": [
174 | {
175 | "label": "Getting Started",
176 | "link": "/guides/getting-started/",
177 | },
178 | ],
179 | "label": "Guides",
180 | },
181 | ]
182 | `)
183 | })
184 | })
185 |
186 | function getTestSidebar(sidebar: Parameters[0]) {
187 | return getSidebarFromReflections(sidebar, typeDocSidebarGroup, {}, {} as ProjectReflection, {}, 'api')
188 | }
189 |
190 | function getTestSidebarWithoutReflections(sidebar: Parameters[0]) {
191 | return getSidebarWithoutReflections(sidebar, typeDocSidebarGroup)
192 | }
193 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/tests/unit/typedoc.test.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'node:fs'
2 |
3 | import type { AstroIntegrationLogger, AstroConfig } from 'astro'
4 | import { afterAll, afterEach, beforeAll, expect, test, vi } from 'vitest'
5 |
6 | import type { StarlightTypeDocOptions } from '../..'
7 | import { generateTypeDoc } from '../../libs/typedoc'
8 |
9 | vi.mock(import('node:fs'), async (importOriginal) => {
10 | const mod = await importOriginal()
11 | return {
12 | ...mod,
13 | mkdirSync: vi.fn(),
14 | rmSync: vi.fn(),
15 | writeFileSync: vi.fn(),
16 | }
17 | })
18 |
19 | const starlightTypeDocOptions = {
20 | tsconfig: '../../fixtures/basics/tsconfig.json',
21 | typeDoc: {
22 | logLevel: 4,
23 | },
24 | } satisfies Partial
25 |
26 | const starlightTypeDocAstroConfig: Partial = {
27 | // './src' is the default value supplied by Astro — https://docs.astro.build/en/reference/configuration-reference/#srcdir
28 | srcDir: new URL('src', import.meta.url),
29 | }
30 |
31 | beforeAll(() => {
32 | vi.spyOn(fs, 'mkdirSync').mockReturnValue(undefined)
33 | vi.spyOn(fs, 'rmSync').mockReturnValue(undefined)
34 | vi.spyOn(fs, 'writeFileSync').mockReturnValue(undefined)
35 | })
36 |
37 | afterEach(() => {
38 | vi.clearAllMocks()
39 | })
40 |
41 | afterAll(() => {
42 | vi.restoreAllMocks()
43 | })
44 |
45 | test('should throw an error with no exports', async () => {
46 | await expect(
47 | generateTestTypeDoc({
48 | ...starlightTypeDocOptions,
49 | entryPoints: ['../../fixtures/basics/src/noExports.ts'],
50 | }),
51 | ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to generate TypeDoc documentation.]`)
52 | })
53 |
54 | test('should support providing custom TypeDoc options', async () => {
55 | const options = {
56 | ...starlightTypeDocOptions,
57 | entryPoints: ['../../fixtures/basics/src/noDocs.ts'],
58 | }
59 |
60 | await expect(generateTestTypeDoc(options)).resolves.not.toThrow()
61 |
62 | await expect(
63 | generateTestTypeDoc({
64 | ...options,
65 | typeDoc: {
66 | ...starlightTypeDocOptions.typeDoc,
67 | excludeNotDocumented: true,
68 | },
69 | }),
70 | ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to generate TypeDoc documentation.]`)
71 | })
72 |
73 | test('should generate the doc in `src/content/docs/api` by default', async () => {
74 | await generateTestTypeDoc({
75 | ...starlightTypeDocOptions,
76 | entryPoints: ['../../fixtures/basics/src/functions.ts'],
77 | })
78 |
79 | const mkdirSyncSpy = vi.mocked(fs.mkdirSync)
80 |
81 | expect(mkdirSyncSpy).toHaveBeenCalled()
82 | expect(mkdirSyncSpy.mock.calls[0]?.[0].toString()).toMatch(/src[/\\]content[/\\]docs[/\\]api$/)
83 | })
84 |
85 | test('should generate the doc in `/content/docs/api` of the srcDir via the AstroConfig', async () => {
86 | await generateTestTypeDoc(
87 | {
88 | ...starlightTypeDocOptions,
89 | entryPoints: ['../../fixtures/basics/src/functions.ts'],
90 | },
91 | { srcDir: new URL('www/src', import.meta.url) },
92 | )
93 |
94 | const mkdirSyncSpy = vi.mocked(fs.mkdirSync)
95 |
96 | expect(mkdirSyncSpy).toHaveBeenCalled()
97 | expect(mkdirSyncSpy.mock.calls[0]?.[0].toString()).toMatch(/www[/\\]src[/\\]content[/\\]docs[/\\]api$/)
98 | })
99 |
100 | test('should generate the doc in a custom output directory relative to `src/content/docs/`', async () => {
101 | const output = 'dist-api'
102 |
103 | await generateTestTypeDoc({
104 | ...starlightTypeDocOptions,
105 | entryPoints: ['../../fixtures/basics/src/functions.ts'],
106 | output,
107 | })
108 |
109 | const mkdirSyncSpy = vi.mocked(fs.mkdirSync)
110 |
111 | expect(mkdirSyncSpy).toHaveBeenCalled()
112 | expect(mkdirSyncSpy.mock.calls[0]?.[0].toString()).toMatch(
113 | new RegExp(`src[/\\\\]content[/\\\\]docs[/\\\\]${output}$`),
114 | )
115 | })
116 |
117 | test('should not add `README.md` module files for multiple entry points', async () => {
118 | await generateTestTypeDoc({
119 | ...starlightTypeDocOptions,
120 | entryPoints: ['../../fixtures/basics/src/Bar.ts', '../../fixtures/basics/src/Foo.ts'],
121 | })
122 |
123 | const rmSyncSpy = vi.mocked(fs.rmSync)
124 | const filePaths = rmSyncSpy.mock.calls.map((call) => call[0].toString())
125 |
126 | expect(rmSyncSpy).toHaveBeenCalled()
127 | expect(filePaths.filter((filePath) => /\/(?:Bar|Foo)\/README\.md$/.test(filePath)).length).toBe(2)
128 | })
129 |
130 | test('should support overriding typedoc-plugin-markdown readme page generation', async () => {
131 | await generateTestTypeDoc({
132 | ...starlightTypeDocOptions,
133 | typeDoc: {
134 | ...starlightTypeDocOptions.typeDoc,
135 | readme: 'README.md',
136 | },
137 | entryPoints: ['../../fixtures/basics/src/Bar.ts', '../../fixtures/basics/src/Foo.ts'],
138 | })
139 |
140 | const writeFileSyncSpy = vi.mocked(fs.writeFileSync)
141 | const filePaths = writeFileSyncSpy.mock.calls.map((call) => call[0].toString())
142 |
143 | expect(filePaths.some((filePath) => filePath.endsWith('modules.md'))).toBe(true)
144 | expect(filePaths.some((filePath) => filePath.endsWith('README.md'))).toBe(true)
145 | })
146 |
147 | test('should output modules with index', async () => {
148 | await generateTestTypeDoc({
149 | ...starlightTypeDocOptions,
150 | typeDoc: {
151 | ...starlightTypeDocOptions.typeDoc,
152 | outputFileStrategy: 'modules',
153 | entryFileName: 'index.md',
154 | },
155 | entryPoints: ['../../fixtures/basics/src/module.ts'],
156 | })
157 |
158 | const writeFileSyncSpy = vi.mocked(fs.writeFileSync)
159 | const filePaths = writeFileSyncSpy.mock.calls.map((call) => call[0].toString())
160 |
161 | expect(filePaths).toEqual([
162 | expect.stringMatching(/index\.md$/),
163 | expect.stringMatching(/namespaces\/bar\.md$/),
164 | expect.stringMatching(/namespaces\/foo\.md$/),
165 | expect.stringMatching(/namespaces\/functions\.md$/),
166 | expect.stringMatching(/namespaces\/shared\.md$/),
167 | expect.stringMatching(/namespaces\/types\.md$/),
168 | ])
169 | })
170 |
171 | test('should output index with correct module path', async () => {
172 | await generateTestTypeDoc({
173 | ...starlightTypeDocOptions,
174 | typeDoc: {
175 | ...starlightTypeDocOptions.typeDoc,
176 | outputFileStrategy: 'modules',
177 | entryFileName: 'index.md',
178 | },
179 | entryPoints: ['../../fixtures/basics/src/module.ts'],
180 | })
181 |
182 | const writeFileSyncSpy = vi.mocked(fs.writeFileSync)
183 | const [, content] = writeFileSyncSpy.mock.calls.find((call) => call[0].toString().endsWith('index.md')) as [
184 | fs.PathOrFileDescriptor,
185 | string,
186 | ]
187 |
188 | expect(
189 | content.includes(`
190 | - [bar](/api/starlight-typedoc/namespaces/bar/)
191 | - [foo](/api/starlight-typedoc/namespaces/foo/)
192 | - [functions](/api/starlight-typedoc/namespaces/functions/)
193 | - [shared](/api/starlight-typedoc/namespaces/shared/)
194 | - [types](/api/starlight-typedoc/namespaces/types/)`),
195 | ).toBe(true)
196 | })
197 |
198 | function generateTestTypeDoc(
199 | options: Parameters[0],
200 | config: Partial = starlightTypeDocAstroConfig,
201 | ) {
202 | return generateTypeDoc(
203 | {
204 | ...starlightTypeDocOptions,
205 | ...options,
206 | },
207 | config as AstroConfig,
208 | {
209 | info() {
210 | // noop
211 | },
212 | warn() {
213 | // noop
214 | },
215 | } as unknown as AstroIntegrationLogger,
216 | )
217 | }
218 |
--------------------------------------------------------------------------------
/packages/starlight-typedoc/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitest/config'
2 |
3 | export default defineConfig({
4 | test: {
5 | include: ['tests/unit/**/*.test.ts'],
6 | server: {
7 | deps: {
8 | inline: ['typedoc'],
9 | },
10 | },
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - 'docs'
3 | - 'example'
4 | - 'fixtures/*'
5 | - 'packages/*'
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@hideoo/tsconfig",
3 | "include": ["docs/.astro/types.d.ts", "**/*"]
4 | }
5 |
--------------------------------------------------------------------------------