feat: add support for packages entry point strategy

This commit is contained in:
HiDeoo
2024-01-03 16:25:06 +01:00
parent 037b2190fe
commit 89552ec687
27 changed files with 312 additions and 31 deletions

View File

@@ -0,0 +1,29 @@
import starlight from '@astrojs/starlight'
import { defineConfig } from 'astro/config'
import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc'
export default defineConfig({
base: '/packages-entrypoints/',
integrations: [
starlight({
plugins: [
starlightTypeDoc({
entryPoints: ['../fixtures/packages/packages/*'],
output: 'api-packages-entrypoints',
tsconfig: '../fixtures/packages/tsconfig.json',
typeDoc: {
entryPointStrategy: 'packages',
},
}),
],
sidebar: [
{
label: 'Guides',
items: [{ label: 'Example Guide', link: '/guides/example/' }],
},
typeDocSidebarGroup,
],
title: 'Starlight TypeDoc Packages Entry Points Example',
}),
],
})

View File

@@ -9,6 +9,7 @@
"dev": "astro dev",
"dev:single-entrypoints": "astro dev --config astro.config.ts",
"dev:multiple-entrypoints": "astro dev --config astro.multiple-entrypoints.config.ts",
"dev:packages-entrypoints": "pnpm -C ../fixtures/packages run build && astro dev --config astro.packages-entrypoints.config.ts",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",

View File

@@ -1,3 +1,4 @@
# Autogenerated by https://github.com/HiDeoo/starlight-typedoc
api/
api-multiple-entrypoints/
api-packages-entrypoints/

View File

@@ -0,0 +1,34 @@
{
"name": "@starlight-typedoc/fixtures-packages",
"version": "0.0.1",
"license": "MIT",
"description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
"author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
"type": "module",
"scripts": {
"build": "tsc --build"
},
"dependencies": {
"typescript": "5.1.6"
},
"engines": {
"node": ">=18.14.1"
},
"packageManager": "pnpm@8.6.1",
"private": true,
"sideEffects": false,
"keywords": [
"starlight",
"plugin",
"typedoc",
"typescript",
"documentation",
"astro"
],
"homepage": "https://github.com/HiDeoo/starlight-typedoc",
"repository": {
"type": "git",
"url": "https://github.com/HiDeoo/starlight-typedoc.git"
},
"bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
}

View File

@@ -0,0 +1,25 @@
{
"name": "bar",
"version": "0.0.1",
"license": "MIT",
"description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
"author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
"type": "module",
"packageManager": "pnpm@8.6.1",
"private": true,
"sideEffects": false,
"keywords": [
"starlight",
"plugin",
"typedoc",
"typescript",
"documentation",
"astro"
],
"homepage": "https://github.com/HiDeoo/starlight-typedoc",
"repository": {
"type": "git",
"url": "https://github.com/HiDeoo/starlight-typedoc.git"
},
"bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
}

View File

@@ -0,0 +1,6 @@
/**
* A function that does a bar thing.
*/
export function doBar() {
return 'doBar'
}

View File

@@ -0,0 +1 @@
export * from './functions.js'

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}

View File

@@ -0,0 +1,3 @@
{
"entryPoints": ["src/index.ts"]
}

View File

@@ -0,0 +1,25 @@
{
"name": "foo",
"version": "0.0.1",
"license": "MIT",
"description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.",
"author": "HiDeoo <github@hideoo.dev> (https://hideoo.dev)",
"type": "module",
"packageManager": "pnpm@8.6.1",
"private": true,
"sideEffects": false,
"keywords": [
"starlight",
"plugin",
"typedoc",
"typescript",
"documentation",
"astro"
],
"homepage": "https://github.com/HiDeoo/starlight-typedoc",
"repository": {
"type": "git",
"url": "https://github.com/HiDeoo/starlight-typedoc.git"
},
"bugs": "https://github.com/HiDeoo/starlight-typedoc/issues"
}

View File

@@ -0,0 +1,16 @@
/**
* A function that does a foo thing.
* @deprecated Use the new {@link doFooFaster} function instead.
*/
export function doFoo() {
return 'doFoo'
}
/**
* A function that does another foo thing but faster.
*
* This is a faster alternative to {@link doFoo}.
*/
export function doFooFaster() {
return 'doFoo'
}

View File

@@ -0,0 +1 @@
export * from './functions.js'

View File

@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}

View File

@@ -0,0 +1,3 @@
{
"entryPoints": ["src/index.ts"]
}

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"module": "NodeNext",
"strict": true
}
}

View File

@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./packages/bar"
},
{
"path": "./packages/foo"
}
]
}

View File

@@ -2,7 +2,7 @@ import path from 'node:path'
import type { StarlightPlugin } from '@astrojs/starlight/types'
import { slug } from 'github-slugger'
import type { DeclarationReflection, ProjectReflection } from 'typedoc'
import { type DeclarationReflection, type ProjectReflection, ReflectionKind } from 'typedoc'
import type { StarlightTypeDocSidebarOptions } from '..'
@@ -54,16 +54,43 @@ export function getSidebarFromReflections(
})
}
function getSidebarGroupFromReflections(
function getSidebarGroupFromPackageReflections(
options: StarlightTypeDocSidebarOptions,
reflections: ProjectReflection | DeclarationReflection,
outputDirectory: string,
): SidebarGroup {
const groups = reflections.groups ?? []
const groups = (reflections.children ?? []).map((child) => {
if (!child.url) {
return undefined
}
const url = path.parse(child.url)
return getSidebarGroupFromReflections(options, child, `${outputDirectory}/${url.dir}`, child.name)
})
return {
label: options.label ?? sidebarDefaultOptions.label,
collapsed: options.collapsed ?? sidebarDefaultOptions.collapsed,
items: groups.filter((item): item is SidebarGroup => item !== undefined),
}
}
function getSidebarGroupFromReflections(
options: StarlightTypeDocSidebarOptions,
reflections: ProjectReflection | DeclarationReflection,
outputDirectory: string,
label?: string,
): SidebarGroup {
if ((!reflections.groups || reflections.groups.length === 0) && reflections.children) {
return getSidebarGroupFromPackageReflections(options, reflections, outputDirectory)
}
const groups = reflections.groups ?? []
return {
label: label ?? options.label ?? sidebarDefaultOptions.label,
collapsed: options.collapsed ?? sidebarDefaultOptions.collapsed,
items: groups
.flatMap((group) => {
if (group.title === 'Modules') {
@@ -73,11 +100,12 @@ function getSidebarGroupFromReflections(
}
const url = path.parse(child.url)
const isParentKindModule = child.parent?.kind === ReflectionKind.Module
return getSidebarGroupFromReflections(
{ collapsed: true, label: child.name },
child,
`${outputDirectory}/${url.dir}`,
`${outputDirectory}/${isParentKindModule ? url.dir.split('/').slice(1).join('/') : url.dir}`,
)
})
}

View File

@@ -47,7 +47,10 @@ export async function generateTypeDoc(options: StarlightTypeDocOptions, base: st
)
const reflections = await app.convert()
if (!reflections?.groups || reflections.groups.length === 0) {
if (
(!reflections?.groups || reflections.groups.length === 0) &&
!reflections?.children?.some((child) => (child.groups ?? []).length > 0)
) {
throw new Error('Failed to generate TypeDoc documentation.')
}

View File

@@ -15,7 +15,10 @@
"scripts": {
"test": "pnpm test:unit && pnpm test:e2e",
"test:unit": "vitest",
"test:e2e": "playwright install --with-deps chromium && playwright test",
"test:e2e": "pnpm run test:e2e:basics && pnpm run test:e2e:packages",
"test:e2e:basics": "TEST_TYPE=basics pnpm run playwright",
"test:e2e:packages": "TEST_TYPE=packages pnpm run playwright",
"playwright": "playwright install --with-deps chromium && playwright test",
"lint": "prettier -c --cache . && eslint . --cache --max-warnings=0"
},
"dependencies": {

View File

@@ -8,19 +8,29 @@ export default defineConfig({
use: { ...devices['Desktop Chrome'] },
},
],
testDir: 'tests/e2e',
webServer: [
{
command: 'pnpm run dev:single-entrypoints',
cwd: '../../example',
reuseExistingServer: !process.env['CI'],
url: 'http://localhost:4321',
},
{
command: 'pnpm run dev:multiple-entrypoints',
cwd: '../../example',
reuseExistingServer: !process.env['CI'],
url: 'http://localhost:4322/multiple-entrypoints/',
},
],
testDir: `tests/e2e/${process.env['TEST_TYPE']}`,
webServer:
process.env['TEST_TYPE'] === 'basics'
? [
{
command: 'pnpm run dev:single-entrypoints',
cwd: '../../example',
reuseExistingServer: !process.env['CI'],
url: 'http://localhost:4321',
},
{
command: 'pnpm run dev:multiple-entrypoints',
cwd: '../../example',
reuseExistingServer: !process.env['CI'],
url: 'http://localhost:4322/multiple-entrypoints/',
},
]
: [
{
command: 'pnpm run dev:packages-entrypoints',
cwd: '../../example',
reuseExistingServer: !process.env['CI'],
url: 'http://localhost:4321/packages-entrypoints/',
},
],
})

View File

@@ -1,5 +1,5 @@
import type { DocPage } from './fixtures/DocPage'
import { expect, test } from './test'
import type { DocPage } from '../fixtures/DocPage'
import { expect, test } from '../test'
test('should use an aside for the deprecated tag with no content', async ({ docPage }) => {
await docPage.goto('functions/dothingb')

View File

@@ -1,4 +1,4 @@
import { expect, test } from './test'
import { expect, test } from '../test'
test('should add titles to the frontmatter', async ({ docPage }) => {
await docPage.goto('classes/foo')

View File

@@ -1,5 +1,5 @@
import type { DocPage } from './fixtures/DocPage'
import { expect, test } from './test'
import type { DocPage } from '../fixtures/DocPage'
import { expect, test } from '../test'
test('should not include pagination links by default', async ({ docPage }) => {
await docPage.goto('classes/foo')

View File

@@ -1,4 +1,4 @@
import { expect, test } from './test'
import { expect, test } from '../test'
const singleEntrypointUrl = 'classes/foo'
const multipleEntrypointsUrl = 'bar/classes/bar'

View File

@@ -4,13 +4,17 @@ export class DocPage {
title: string | null = null
#useMultipleEntryPoints = false
#usePackagesEntryPoints = false
constructor(public readonly page: Page) {}
async goto(url: string) {
const baseUrl = `http://localhost:${this.#useMultipleEntryPoints ? 4322 : 4321}/${
this.#useMultipleEntryPoints ? 'multiple-entrypoints/api-multiple-entrypoints' : 'api'
}`
const baseDir = this.#useMultipleEntryPoints
? 'multiple-entrypoints/api-multiple-entrypoints'
: this.#usePackagesEntryPoints
? 'packages-entrypoints/api-packages-entrypoints'
: 'api'
const baseUrl = `http://localhost:${this.#useMultipleEntryPoints ? 4322 : 4321}/${baseDir}`
await this.page.goto(`${baseUrl}${url.startsWith('/') ? url : `/${url}`}${url.endsWith('/') ? '' : '/'}`)
@@ -22,6 +26,10 @@ export class DocPage {
this.#useMultipleEntryPoints = true
}
usePackagesEntryPoints() {
this.#usePackagesEntryPoints = true
}
get content() {
return this.page.getByRole('main')
}
@@ -37,7 +45,7 @@ export class DocPage {
}
get #expectedTypeDocSidebarLabel() {
return this.#useMultipleEntryPoints ? 'API' : 'API (auto-generated)'
return this.#useMultipleEntryPoints || this.#usePackagesEntryPoints ? 'API' : 'API (auto-generated)'
}
get #typeDocSidebarRootDetails() {

View File

@@ -0,0 +1,42 @@
import { expect, test } from '../test'
const url = 'foo/functions/dofoo'
test('should include the TypeDoc sidebar group', async ({ docPage }) => {
docPage.usePackagesEntryPoints()
await docPage.goto(url)
await expect(docPage.typeDocSidebarLabel).toBeVisible()
})
test('should generate the proper items for for multiple entry points', async ({ docPage }) => {
docPage.usePackagesEntryPoints()
await docPage.goto(url)
const items = await docPage.getTypeDocSidebarItems()
expect(items).toMatchObject([
{
label: 'bar',
items: [
{
label: 'Functions',
items: [{ name: 'doBar' }],
},
],
collapsed: true,
},
{
label: 'foo',
items: [
{
label: 'Functions',
items: [{ name: 'doFoo' }, { name: 'doFooFaster' }],
},
],
collapsed: true,
},
])
})

6
pnpm-lock.yaml generated
View File

@@ -75,6 +75,12 @@ importers:
specifier: 5.1.6
version: 5.1.6
fixtures/packages:
dependencies:
typescript:
specifier: 5.1.6
version: 5.1.6
packages/starlight-typedoc:
dependencies:
github-slugger: