diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index c3c8ba8..6d7bad3 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -33,7 +33,7 @@ jobs: - name: Store Playwright Version run: | - PLAYWRIGHT_VERSION=$(pnpm playwright --version | sed 's/Version //') + PLAYWRIGHT_VERSION=$(pnpm exec playwright --version | sed 's/Version //') echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV working-directory: packages/starlight-typedoc diff --git a/example/astro.config.ts b/example/astro.config.ts index 134c5ff..67d233c 100644 --- a/example/astro.config.ts +++ b/example/astro.config.ts @@ -10,8 +10,8 @@ export default defineConfig({ }, plugins: [ starlightTypeDoc({ - entryPoints: ['../fixtures/src/index.ts'], - tsconfig: '../fixtures/tsconfig.json', + entryPoints: ['../fixtures/basics/src/index.ts'], + tsconfig: '../fixtures/basics/tsconfig.json', sidebar: { label: 'API (auto-generated)', }, diff --git a/example/astro.multiple-entrypoints.config.ts b/example/astro.multiple-entrypoints.config.ts index 7a0b509..bd1095c 100644 --- a/example/astro.multiple-entrypoints.config.ts +++ b/example/astro.multiple-entrypoints.config.ts @@ -8,13 +8,13 @@ export default defineConfig({ starlight({ plugins: [ starlightTypeDoc({ - entryPoints: ['../fixtures/src/Bar.ts', '../fixtures/src/Foo.ts'], + entryPoints: ['../fixtures/basics/src/Bar.ts', '../fixtures/basics/src/Foo.ts'], output: 'api-multiple-entrypoints', pagination: true, sidebar: { collapsed: true, }, - tsconfig: '../fixtures/tsconfig.json', + tsconfig: '../fixtures/basics/tsconfig.json', // @ts-expect-error - Fake the `readme` option not being set to ensure that frontmatter titles are escaped properly. // @see https://github.com/HiDeoo/starlight-typedoc/pull/7 typeDoc: { diff --git a/example/astro.packages-entrypoints.config.ts b/example/astro.packages-entrypoints.config.ts new file mode 100644 index 0000000..8337c46 --- /dev/null +++ b/example/astro.packages-entrypoints.config.ts @@ -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', + }), + ], +}) diff --git a/example/package.json b/example/package.json index 4b51c26..b1e7002 100644 --- a/example/package.json +++ b/example/package.json @@ -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", diff --git a/example/src/content/docs/.gitignore b/example/src/content/docs/.gitignore index cc914cc..3577df1 100644 --- a/example/src/content/docs/.gitignore +++ b/example/src/content/docs/.gitignore @@ -1,3 +1,4 @@ # Autogenerated by https://github.com/HiDeoo/starlight-typedoc api/ api-multiple-entrypoints/ +api-packages-entrypoints/ diff --git a/fixtures/README.md b/fixtures/basics/README.md similarity index 100% rename from fixtures/README.md rename to fixtures/basics/README.md diff --git a/fixtures/package.json b/fixtures/basics/package.json similarity index 93% rename from fixtures/package.json rename to fixtures/basics/package.json index 629b561..d97c1da 100644 --- a/fixtures/package.json +++ b/fixtures/basics/package.json @@ -1,5 +1,5 @@ { - "name": "@starlight-typedoc/fixtures", + "name": "@starlight-typedoc/fixtures-basics", "version": "0.0.1", "license": "MIT", "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.", diff --git a/fixtures/src/Bar.ts b/fixtures/basics/src/Bar.ts similarity index 100% rename from fixtures/src/Bar.ts rename to fixtures/basics/src/Bar.ts diff --git a/fixtures/src/Foo.ts b/fixtures/basics/src/Foo.ts similarity index 100% rename from fixtures/src/Foo.ts rename to fixtures/basics/src/Foo.ts diff --git a/fixtures/src/functions.ts b/fixtures/basics/src/functions.ts similarity index 100% rename from fixtures/src/functions.ts rename to fixtures/basics/src/functions.ts diff --git a/fixtures/src/index.ts b/fixtures/basics/src/index.ts similarity index 100% rename from fixtures/src/index.ts rename to fixtures/basics/src/index.ts diff --git a/fixtures/src/module.ts b/fixtures/basics/src/module.ts similarity index 100% rename from fixtures/src/module.ts rename to fixtures/basics/src/module.ts diff --git a/fixtures/src/noDocs.ts b/fixtures/basics/src/noDocs.ts similarity index 100% rename from fixtures/src/noDocs.ts rename to fixtures/basics/src/noDocs.ts diff --git a/fixtures/src/noExports.ts b/fixtures/basics/src/noExports.ts similarity index 100% rename from fixtures/src/noExports.ts rename to fixtures/basics/src/noExports.ts diff --git a/fixtures/src/shared.ts b/fixtures/basics/src/shared.ts similarity index 91% rename from fixtures/src/shared.ts rename to fixtures/basics/src/shared.ts index e6e8234..8fc7b61 100644 --- a/fixtures/src/shared.ts +++ b/fixtures/basics/src/shared.ts @@ -1,3 +1,5 @@ +export { doThingA as doThingARef } from './functions' + /** * This is a string variable. */ diff --git a/fixtures/src/types.ts b/fixtures/basics/src/types.ts similarity index 100% rename from fixtures/src/types.ts rename to fixtures/basics/src/types.ts diff --git a/fixtures/basics/tsconfig.json b/fixtures/basics/tsconfig.json new file mode 100644 index 0000000..4082f16 --- /dev/null +++ b/fixtures/basics/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tsconfig.json" +} diff --git a/fixtures/packages/package.json b/fixtures/packages/package.json new file mode 100644 index 0000000..7fe6839 --- /dev/null +++ b/fixtures/packages/package.json @@ -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 (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" +} diff --git a/fixtures/packages/packages/bar/package.json b/fixtures/packages/packages/bar/package.json new file mode 100644 index 0000000..17a9027 --- /dev/null +++ b/fixtures/packages/packages/bar/package.json @@ -0,0 +1,25 @@ +{ + "name": "bar", + "version": "0.0.1", + "license": "MIT", + "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.", + "author": "HiDeoo (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" +} diff --git a/fixtures/packages/packages/bar/src/functions.ts b/fixtures/packages/packages/bar/src/functions.ts new file mode 100644 index 0000000..b47332e --- /dev/null +++ b/fixtures/packages/packages/bar/src/functions.ts @@ -0,0 +1,6 @@ +/** + * A function that does a bar thing. + */ +export function doBar() { + return 'doBar' +} diff --git a/fixtures/packages/packages/bar/src/index.ts b/fixtures/packages/packages/bar/src/index.ts new file mode 100644 index 0000000..c560706 --- /dev/null +++ b/fixtures/packages/packages/bar/src/index.ts @@ -0,0 +1 @@ +export * from './functions.js' diff --git a/fixtures/packages/packages/bar/tsconfig.json b/fixtures/packages/packages/bar/tsconfig.json new file mode 100644 index 0000000..5285d28 --- /dev/null +++ b/fixtures/packages/packages/bar/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/fixtures/packages/packages/bar/typedoc.json b/fixtures/packages/packages/bar/typedoc.json new file mode 100644 index 0000000..35fed2c --- /dev/null +++ b/fixtures/packages/packages/bar/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["src/index.ts"] +} diff --git a/fixtures/packages/packages/foo/package.json b/fixtures/packages/packages/foo/package.json new file mode 100644 index 0000000..9a77a03 --- /dev/null +++ b/fixtures/packages/packages/foo/package.json @@ -0,0 +1,25 @@ +{ + "name": "foo", + "version": "0.0.1", + "license": "MIT", + "description": "Starlight plugin to generate documentation from TypeScript using TypeDoc.", + "author": "HiDeoo (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" +} diff --git a/fixtures/packages/packages/foo/src/functions.ts b/fixtures/packages/packages/foo/src/functions.ts new file mode 100644 index 0000000..0167155 --- /dev/null +++ b/fixtures/packages/packages/foo/src/functions.ts @@ -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' +} diff --git a/fixtures/packages/packages/foo/src/index.ts b/fixtures/packages/packages/foo/src/index.ts new file mode 100644 index 0000000..c560706 --- /dev/null +++ b/fixtures/packages/packages/foo/src/index.ts @@ -0,0 +1 @@ +export * from './functions.js' diff --git a/fixtures/packages/packages/foo/tsconfig.json b/fixtures/packages/packages/foo/tsconfig.json new file mode 100644 index 0000000..5285d28 --- /dev/null +++ b/fixtures/packages/packages/foo/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"] +} diff --git a/fixtures/packages/packages/foo/typedoc.json b/fixtures/packages/packages/foo/typedoc.json new file mode 100644 index 0000000..35fed2c --- /dev/null +++ b/fixtures/packages/packages/foo/typedoc.json @@ -0,0 +1,3 @@ +{ + "entryPoints": ["src/index.ts"] +} diff --git a/fixtures/packages/tsconfig.base.json b/fixtures/packages/tsconfig.base.json new file mode 100644 index 0000000..652f8f0 --- /dev/null +++ b/fixtures/packages/tsconfig.base.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "declaration": true, + "declarationMap": true, + "module": "NodeNext", + "strict": true + } +} diff --git a/fixtures/packages/tsconfig.json b/fixtures/packages/tsconfig.json new file mode 100644 index 0000000..0e4c80f --- /dev/null +++ b/fixtures/packages/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./packages/bar" + }, + { + "path": "./packages/foo" + } + ] +} diff --git a/fixtures/tsconfig.json b/fixtures/tsconfig.json deleted file mode 100644 index 3c43903..0000000 --- a/fixtures/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../tsconfig.json" -} diff --git a/packages/starlight-typedoc/libs/starlight.ts b/packages/starlight-typedoc/libs/starlight.ts index a2a8e41..4042839 100644 --- a/packages/starlight-typedoc/libs/starlight.ts +++ b/packages/starlight-typedoc/libs/starlight.ts @@ -2,10 +2,18 @@ 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, + ReferenceReflection, + type ReflectionGroup, +} from 'typedoc' import type { StarlightTypeDocSidebarOptions } from '..' +const externalLinkRegex = /^(http|ftp)s?:\/\// + const sidebarDefaultOptions = { collapsed: false, label: 'API', @@ -24,13 +32,13 @@ export function getSidebarFromReflections( sidebar: StarlightUserConfigSidebar, options: StarlightTypeDocSidebarOptions = {}, reflections: ProjectReflection | DeclarationReflection, - outputDirectory: string, + baseOutputDirectory: string, ): StarlightUserConfigSidebar { if (!sidebar || sidebar.length === 0) { return sidebar } - const sidebarGroup = getSidebarGroupFromReflections(options, reflections, outputDirectory) + const sidebarGroup = getSidebarGroupFromReflections(options, reflections, baseOutputDirectory, baseOutputDirectory) function replaceSidebarGroupPlaceholder(group: SidebarManualGroup): SidebarGroup { if (group.label === starlightTypeDocSidebarGroupLabel.toString()) { @@ -54,15 +62,49 @@ export function getSidebarFromReflections( }) } +function getSidebarGroupFromPackageReflections( + options: StarlightTypeDocSidebarOptions, + reflections: ProjectReflection | DeclarationReflection, + baseOutputDirectory: string, +): SidebarGroup { + const groups = (reflections.children ?? []).map((child) => { + if (!child.url) { + return undefined + } + + const url = path.parse(child.url) + + return getSidebarGroupFromReflections( + options, + child, + baseOutputDirectory, + `${baseOutputDirectory}/${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, + baseOutputDirectory: string, 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: options.label ?? sidebarDefaultOptions.label, + label: label ?? options.label ?? sidebarDefaultOptions.label, collapsed: options.collapsed ?? sidebarDefaultOptions.collapsed, items: groups .flatMap((group) => { @@ -73,15 +115,21 @@ 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}`, + baseOutputDirectory, + `${outputDirectory}/${isParentKindModule ? url.dir.split('/').slice(1).join('/') : url.dir}`, ) }) } + if (isReferenceReflectionGroup(group)) { + return getReferencesSidebarGroup(group, baseOutputDirectory) + } + return { collapsed: true, label: group.title, @@ -95,16 +143,85 @@ function getSidebarGroupFromReflections( } } +function getReferencesSidebarGroup( + group: ReflectionGroup, + baseOutputDirectory: string, +): SidebarManualGroup | undefined { + const referenceItems: LinkItem[] = group.children + .map((child) => { + const reference = child as ReferenceReflection + let target = reference.tryGetTargetReflectionDeep() + + if (!target) { + return undefined + } + + if (target.kindOf(ReflectionKind.TypeLiteral) && target.parent) { + target = target.parent + } + + if (!target.url) { + return undefined + } + + return { + label: reference.name, + link: getRelativeURL(target.url, getStarlightTypeDocOutputDirectory(baseOutputDirectory)), + } + }) + .filter((item): item is LinkItem => item !== undefined) + + if (referenceItems.length === 0) { + return undefined + } + + return { + label: group.title, + items: referenceItems, + } +} + export function getAsideMarkdown(type: AsideType, title: string, content: string) { return `:::${type}[${title}] ${content} :::` } +export function getRelativeURL(url: string | undefined, baseUrl: string): string | null { + if (!url) { + return null + } else if (externalLinkRegex.test(url)) { + return url + } + + const filePath = path.parse(url) + const [, anchor] = filePath.base.split('#') + const segments = filePath.dir + .split('/') + .map((segment) => slug(segment)) + .filter((segment) => segment !== '') + + let constructedUrl = typeof baseUrl === 'string' ? baseUrl : '' + constructedUrl += segments.length > 0 ? `${segments.join('/')}/` : '' + constructedUrl += slug(filePath.name) + constructedUrl += '/' + constructedUrl += anchor && anchor.length > 0 ? `#${anchor}` : '' + + return constructedUrl +} + +export function getStarlightTypeDocOutputDirectory(outputDirectory: string, base = '') { + return path.posix.join(base, `/${outputDirectory}${outputDirectory.endsWith('/') ? '' : '/'}`) +} + function isSidebarManualGroup(item: NonNullable[number]): item is SidebarManualGroup { return 'items' in item } +function isReferenceReflectionGroup(group: ReflectionGroup) { + return group.children.every((child) => child instanceof ReferenceReflection) +} + type SidebarGroup = | SidebarManualGroup | { diff --git a/packages/starlight-typedoc/libs/theme.ts b/packages/starlight-typedoc/libs/theme.ts index 2a37317..6d611d9 100644 --- a/packages/starlight-typedoc/libs/theme.ts +++ b/packages/starlight-typedoc/libs/theme.ts @@ -1,16 +1,11 @@ -import path from 'node:path' - -import { slug } from 'github-slugger' import { Reflection, type Comment, type CommentTag, type Options, type PageEvent } from 'typedoc' import { MarkdownTheme, MarkdownThemeRenderContext } from 'typedoc-plugin-markdown' -import { getAsideMarkdown } from './starlight' +import { getAsideMarkdown, getRelativeURL } from './starlight' const customBlockTagTypes = ['@deprecated'] as const const customModifiersTagTypes = ['@alpha', '@beta', '@experimental'] as const -const externalLinkRegex = /^(http|ftp)s?:\/\// - export class StarlightTypeDocTheme extends MarkdownTheme { override getRenderContext(event: PageEvent) { return new StarlightTypeDocThemeRenderContext(event, this.application.options) @@ -26,27 +21,10 @@ class StarlightTypeDocThemeRenderContext extends MarkdownThemeRenderContext { } override relativeURL: (url: string | undefined) => string | null = (url) => { - if (!url) { - return null - } else if (externalLinkRegex.test(url)) { - return url - } + const outputDirectory = this.options.getValue('starlight-typedoc-output') + const baseUrl = typeof outputDirectory === 'string' ? outputDirectory : '' - const filePath = path.parse(url) - const [, anchor] = filePath.base.split('#') - const segments = filePath.dir - .split('/') - .map((segment) => slug(segment)) - .filter((segment) => segment !== '') - const baseUrl = this.options.getValue('starlight-typedoc-output') - - let constructedUrl = typeof baseUrl === 'string' ? baseUrl : '' - constructedUrl += segments.length > 0 ? `${segments.join('/')}/` : '' - constructedUrl += slug(filePath.name) - constructedUrl += '/' - constructedUrl += anchor && anchor.length > 0 ? `#${anchor}` : '' - - return constructedUrl + return getRelativeURL(url, baseUrl) } override comment: ( diff --git a/packages/starlight-typedoc/libs/typedoc.ts b/packages/starlight-typedoc/libs/typedoc.ts index 6dfae47..94e0a0c 100644 --- a/packages/starlight-typedoc/libs/typedoc.ts +++ b/packages/starlight-typedoc/libs/typedoc.ts @@ -15,6 +15,7 @@ import type { StarlightTypeDocOptions } from '..' import { StarlightTypeDocLogger } from './logger' import { addFrontmatter } from './markdown' +import { getStarlightTypeDocOutputDirectory } from './starlight' import { StarlightTypeDocTheme } from './theme' const defaultTypeDocConfig: TypeDocConfig = { @@ -47,7 +48,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.') } @@ -89,7 +93,7 @@ async function bootstrapApp( onRendererPageEnd(event, pagination) }) app.options.addDeclaration({ - defaultValue: path.posix.join(base, `/${outputDirectory}${outputDirectory.endsWith('/') ? '' : '/'}`), + defaultValue: getStarlightTypeDocOutputDirectory(outputDirectory, base), help: 'The starlight-typedoc output directory containing the generated documentation markdown files relative to the `src/content/docs/` directory.', name: 'starlight-typedoc-output', type: ParameterType.String, diff --git a/packages/starlight-typedoc/package.json b/packages/starlight-typedoc/package.json index c100c10..9a83a6b 100644 --- a/packages/starlight-typedoc/package.json +++ b/packages/starlight-typedoc/package.json @@ -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": { diff --git a/packages/starlight-typedoc/playwright.config.ts b/packages/starlight-typedoc/playwright.config.ts index 3a3cb25..3426cc3 100644 --- a/packages/starlight-typedoc/playwright.config.ts +++ b/packages/starlight-typedoc/playwright.config.ts @@ -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/', + }, + ], }) diff --git a/packages/starlight-typedoc/tests/e2e/asides.test.ts b/packages/starlight-typedoc/tests/e2e/basics/asides.test.ts similarity index 95% rename from packages/starlight-typedoc/tests/e2e/asides.test.ts rename to packages/starlight-typedoc/tests/e2e/basics/asides.test.ts index 85ea0e5..4467fa1 100644 --- a/packages/starlight-typedoc/tests/e2e/asides.test.ts +++ b/packages/starlight-typedoc/tests/e2e/basics/asides.test.ts @@ -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') diff --git a/packages/starlight-typedoc/tests/e2e/content.test.ts b/packages/starlight-typedoc/tests/e2e/basics/content.test.ts similarity index 98% rename from packages/starlight-typedoc/tests/e2e/content.test.ts rename to packages/starlight-typedoc/tests/e2e/basics/content.test.ts index 5afd845..f070639 100644 --- a/packages/starlight-typedoc/tests/e2e/content.test.ts +++ b/packages/starlight-typedoc/tests/e2e/basics/content.test.ts @@ -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') diff --git a/packages/starlight-typedoc/tests/e2e/pagination.test.ts b/packages/starlight-typedoc/tests/e2e/basics/pagination.test.ts similarity index 88% rename from packages/starlight-typedoc/tests/e2e/pagination.test.ts rename to packages/starlight-typedoc/tests/e2e/basics/pagination.test.ts index 3839d2f..ffbce10 100644 --- a/packages/starlight-typedoc/tests/e2e/pagination.test.ts +++ b/packages/starlight-typedoc/tests/e2e/basics/pagination.test.ts @@ -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') diff --git a/packages/starlight-typedoc/tests/e2e/sidebar.test.ts b/packages/starlight-typedoc/tests/e2e/basics/sidebar.test.ts similarity index 95% rename from packages/starlight-typedoc/tests/e2e/sidebar.test.ts rename to packages/starlight-typedoc/tests/e2e/basics/sidebar.test.ts index b9a481a..103cb04 100644 --- a/packages/starlight-typedoc/tests/e2e/sidebar.test.ts +++ b/packages/starlight-typedoc/tests/e2e/basics/sidebar.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from './test' +import { expect, test } from '../test' const singleEntrypointUrl = 'classes/foo' const multipleEntrypointsUrl = 'bar/classes/bar' @@ -41,6 +41,11 @@ test('should generate the proper items for for a single entry point', async ({ d const items = await docPage.getTypeDocSidebarItems() expect(items).toMatchObject([ + { + label: 'References', + items: [{ name: 'doThingARef' }], + collapsed: true, + }, { label: 'Enumerations', items: [{ name: 'ANumericEnum' }, { name: 'AStringEnum' }], diff --git a/packages/starlight-typedoc/tests/e2e/fixtures/DocPage.ts b/packages/starlight-typedoc/tests/e2e/fixtures/DocPage.ts index af697ce..9efe733 100644 --- a/packages/starlight-typedoc/tests/e2e/fixtures/DocPage.ts +++ b/packages/starlight-typedoc/tests/e2e/fixtures/DocPage.ts @@ -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() { diff --git a/packages/starlight-typedoc/tests/e2e/packages/sidebar.test.ts b/packages/starlight-typedoc/tests/e2e/packages/sidebar.test.ts new file mode 100644 index 0000000..65cb7e9 --- /dev/null +++ b/packages/starlight-typedoc/tests/e2e/packages/sidebar.test.ts @@ -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, + }, + ]) +}) diff --git a/packages/starlight-typedoc/tests/unit/typedoc.test.ts b/packages/starlight-typedoc/tests/unit/typedoc.test.ts index 8104043..81e4457 100644 --- a/packages/starlight-typedoc/tests/unit/typedoc.test.ts +++ b/packages/starlight-typedoc/tests/unit/typedoc.test.ts @@ -7,7 +7,7 @@ import type { StarlightTypeDocOptions } from '../..' import { generateTypeDoc } from '../../libs/typedoc' const starlightTypeDocOptions = { - tsconfig: '../../fixtures/tsconfig.json', + tsconfig: '../../fixtures/basics/tsconfig.json', typeDoc: { logLevel: 4, }, @@ -30,7 +30,7 @@ test('should throw an error with no exports', async () => { await expect( generateTestTypeDoc({ ...starlightTypeDocOptions, - entryPoints: ['../../fixtures/src/noExports.ts'], + entryPoints: ['../../fixtures/basics/src/noExports.ts'], }), ).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to generate TypeDoc documentation.]`) }) @@ -38,7 +38,7 @@ test('should throw an error with no exports', async () => { test('should support providing custom TypeDoc options', async () => { const options = { ...starlightTypeDocOptions, - entryPoints: ['../../fixtures/src/noDocs.ts'], + entryPoints: ['../../fixtures/basics/src/noDocs.ts'], } await expect(generateTestTypeDoc(options)).resolves.not.toThrow() @@ -57,7 +57,7 @@ test('should support providing custom TypeDoc options', async () => { test('should generate the doc in `src/content/docs/api` by default', async () => { await generateTestTypeDoc({ ...starlightTypeDocOptions, - entryPoints: ['../../fixtures/src/functions.ts'], + entryPoints: ['../../fixtures/basics/src/functions.ts'], }) const mkdirSyncSpy = vi.mocked(fs.mkdirSync) @@ -71,7 +71,7 @@ test('should generate the doc in a custom output directory relative to `src/cont await generateTestTypeDoc({ ...starlightTypeDocOptions, - entryPoints: ['../../fixtures/src/functions.ts'], + entryPoints: ['../../fixtures/basics/src/functions.ts'], output, }) @@ -84,7 +84,7 @@ test('should generate the doc in a custom output directory relative to `src/cont test('should not add `README.md` module files for multiple entry points', async () => { await generateTestTypeDoc({ ...starlightTypeDocOptions, - entryPoints: ['../../fixtures/src/Bar.ts', '../../fixtures/src/Foo.ts'], + entryPoints: ['../../fixtures/basics/src/Bar.ts', '../../fixtures/basics/src/Foo.ts'], }) const writeFileSyncSpy = vi.mocked(fs.writeFileSync) @@ -101,7 +101,7 @@ test('should support overriding typedoc-plugin-markdown readme and index page ge ...starlightTypeDocOptions.typeDoc, readme: 'README.md', }, - entryPoints: ['../../fixtures/src/Bar.ts', '../../fixtures/src/Foo.ts'], + entryPoints: ['../../fixtures/basics/src/Bar.ts', '../../fixtures/basics/src/Foo.ts'], }) const writeFileSyncSpy = vi.mocked(fs.writeFileSync) @@ -119,7 +119,7 @@ test('should output modules with index', async () => { outputFileStrategy: 'modules', entryFileName: 'index.md', }, - entryPoints: ['../../fixtures/src/module.ts'], + entryPoints: ['../../fixtures/basics/src/module.ts'], }) const writeFileSyncSpy = vi.mocked(fs.writeFileSync) @@ -143,7 +143,7 @@ test('should output index with correct module path', async () => { outputFileStrategy: 'modules', entryFileName: 'index.md', }, - entryPoints: ['../../fixtures/src/module.ts'], + entryPoints: ['../../fixtures/basics/src/module.ts'], }) const writeFileSyncSpy = vi.mocked(fs.writeFileSync) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36c3a1e..862095a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,7 +69,13 @@ importers: specifier: 3.0.3 version: 3.0.3(typedoc@0.25.0) - fixtures: + fixtures/basics: + dependencies: + typescript: + specifier: 5.1.6 + version: 5.1.6 + + fixtures/packages: dependencies: typescript: specifier: 5.1.6 @@ -2601,6 +2607,7 @@ packages: /eslint-config-prettier@9.0.0(eslint@8.48.0): resolution: {integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==} + hasBin: true peerDependencies: eslint: '>=7.0.0' dependencies: @@ -6089,6 +6096,7 @@ packages: /tsconfck@3.0.0(typescript@5.1.6): resolution: {integrity: sha512-w3wnsIrJNi7avf4Zb0VjOoodoO0woEqGgZGQm+LHH9przdUI+XDKsWAXwxHA1DaRTjeuZNcregSzr7RaA8zG9A==} engines: {node: ^18 || >=20} + hasBin: true peerDependencies: typescript: ^5.0.0 || 5.1.6 peerDependenciesMeta: @@ -6208,6 +6216,7 @@ packages: /typedoc@0.25.0(typescript@5.1.6): resolution: {integrity: sha512-FvCYWhO1n5jACE0C32qg6b3dSfQ8f2VzExnnRboowHtqUD6ARzM2r8YJeZFYXhcm2hI4C2oCRDgNPk/yaQUN9g==} engines: {node: '>= 16'} + hasBin: true peerDependencies: typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.1.6 dependencies: @@ -6221,6 +6230,7 @@ packages: /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} + hasBin: true /ufo@1.3.2: resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} @@ -6352,6 +6362,7 @@ packages: /update-browserslist-db@1.0.13(browserslist@4.22.2): resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + hasBin: true peerDependencies: browserslist: '>= 4.21.0' dependencies: @@ -6437,6 +6448,7 @@ packages: /vite@5.0.10(@types/node@18.16.16): resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==} engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: '@types/node': ^18.0.0 || >=20.0.0 less: '*' @@ -6481,6 +6493,7 @@ packages: /vitest@1.0.4(@types/node@18.16.16): resolution: {integrity: sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg==} engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5cb3c12..7c8fd9c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ packages: - 'docs/' - 'example/' - - 'fixtures/' + - 'fixtures/*' - 'packages/*'