diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index 2cae26753d..a629167ad9 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -146,6 +146,10 @@ export type DocusaurusConfig = { * @default false */ noIndex: boolean; + orphanPages?: { + onOrphanPage: ReportingSeverity; + entryPoints: string[]; + }; /** * The behavior of Docusaurus when it detects any broken link. * diff --git a/packages/docusaurus/src/commands/build.ts b/packages/docusaurus/src/commands/build.ts index d13d7039fe..fe3dde5a2a 100644 --- a/packages/docusaurus/src/commands/build.ts +++ b/packages/docusaurus/src/commands/build.ts @@ -132,12 +132,7 @@ async function buildLocale({ outDir, generatedFilesDir, plugins, - siteConfig: { - baseUrl, - onBrokenLinks, - staticDirectories: staticDirectoriesOption, - }, - routes, + siteConfig: {staticDirectories: staticDirectoriesOption}, } = props; const clientManifestPath = path.join( @@ -264,13 +259,7 @@ async function buildLocale({ }), ); - await handleBrokenLinks({ - allCollectedLinks, - routes, - onBrokenLinks, - outDir, - baseUrl, - }); + await handleBrokenLinks({allCollectedLinks, props}); logger.success`Generated static files in path=${path.relative( process.cwd(), diff --git a/packages/docusaurus/src/server/brokenLinks.ts b/packages/docusaurus/src/server/brokenLinks.ts index 22f466772d..3a0ce7eb2b 100644 --- a/packages/docusaurus/src/server/brokenLinks.ts +++ b/packages/docusaurus/src/server/brokenLinks.ts @@ -18,7 +18,7 @@ import { resolvePathname, } from '@docusaurus/utils'; import {getAllFinalRoutes} from './utils'; -import type {RouteConfig, ReportingSeverity} from '@docusaurus/types'; +import type {RouteConfig, Props, DocusaurusConfig} from '@docusaurus/types'; type BrokenLink = { link: string; @@ -214,19 +214,47 @@ async function filterExistingFileLinks({ ); } -export async function handleBrokenLinks({ +function findOrphanLinks({ allCollectedLinks, - onBrokenLinks, - routes, - baseUrl, - outDir, + orphanPages, }: { allCollectedLinks: {[location: string]: string[]}; - onBrokenLinks: ReportingSeverity; - routes: RouteConfig[]; - baseUrl: string; - outDir: string; + orphanPages: DocusaurusConfig['orphanPages']; +}) { + if (!orphanPages || orphanPages.onOrphanPage === 'ignore') { + return; + } + const visited = new Set(); + function dfs(link: string) { + if (visited.has(link)) { + return; + } + visited.add(link); + allCollectedLinks[link]?.forEach((l) => dfs(resolvePathname(l, link))); + } + orphanPages.entryPoints.forEach(dfs); + const orphaned = new Set(Object.keys(allCollectedLinks)); + visited.forEach((l) => orphaned.delete(l)); + reportMessage( + logger.interpolate`Orphan pages found: url=${Array.from(orphaned)}`, + orphanPages.onOrphanPage, + ); +} + +export async function handleBrokenLinks({ + allCollectedLinks, + props: { + routes, + baseUrl, + outDir, + siteConfig: {onBrokenLinks, orphanPages}, + }, +}: { + allCollectedLinks: {[location: string]: string[]}; + props: Props; }): Promise { + findOrphanLinks({allCollectedLinks, orphanPages}); + if (onBrokenLinks === 'ignore') { return; } diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index 4027f84ca4..d83d873da8 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -227,6 +227,12 @@ export const ConfigSchema = Joi.object({ clientModules: Joi.array() .items(Joi.string()) .default(DEFAULT_CONFIG.clientModules), + orphanPages: Joi.object({ + onOrphanPage: Joi.string() + .equal('ignore', 'log', 'warn', 'error', 'throw') + .default('warn'), + entryPoints: Joi.array().items(Joi.string()).default([]), + }), tagline: Joi.string().allow('').default(DEFAULT_CONFIG.tagline), titleDelimiter: Joi.string().default(DEFAULT_CONFIG.titleDelimiter), noIndex: Joi.bool().default(DEFAULT_CONFIG.noIndex), diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 6885a87f86..c8392eef67 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -117,6 +117,10 @@ const config = { description: 'An optimized site generator in React. Docusaurus helps you to move fast and write content. Build documentation websites, blogs, marketing pages, and more.', }, + orphanPages: { + onOrphanPage: 'warn', + entryPoints: ['/', '/tests'], + }, staticDirectories: [ 'static', path.join(__dirname, '_dogfooding/_asset-tests'),