This commit is contained in:
Joshua Chen 2022-06-11 17:43:17 +08:00
parent 2da34337a6
commit ef937e402c
No known key found for this signature in database
GPG key ID: C37145B818BDB68F
2 changed files with 48 additions and 29 deletions

View file

@ -9,7 +9,7 @@ import {jest} from '@jest/globals';
import path from 'path';
import _ from 'lodash';
import {handleBrokenLinks} from '../brokenLinks';
import type {RouteConfig} from '@docusaurus/types';
import type {DocusaurusConfig, Props, RouteConfig} from '@docusaurus/types';
describe('handleBrokenLinks', () => {
const routes: RouteConfig[] = [
@ -136,10 +136,14 @@ describe('handleBrokenLinks', () => {
};
await handleBrokenLinks({
allCollectedLinks: allCollectedCorrectLinks,
onBrokenLinks: 'error',
props: {
routes,
baseUrl: '/',
outDir,
siteConfig: {
onBrokenLinks: 'error',
} as DocusaurusConfig,
} as Props,
});
expect(consoleMock).toBeCalledTimes(0);
});
@ -148,10 +152,14 @@ describe('handleBrokenLinks', () => {
await expect(() =>
handleBrokenLinks({
allCollectedLinks,
onBrokenLinks: 'throw',
props: {
routes,
baseUrl: '/',
outDir,
siteConfig: {
onBrokenLinks: 'throw',
} as DocusaurusConfig,
} as Props,
}),
).rejects.toThrowErrorMatchingSnapshot();
});
@ -162,10 +170,14 @@ describe('handleBrokenLinks', () => {
const lodashMock = jest.spyOn(_, 'mapValues');
await handleBrokenLinks({
allCollectedLinks,
onBrokenLinks: 'ignore',
props: {
routes,
baseUrl: '/',
outDir,
siteConfig: {
onBrokenLinks: 'ignore',
} as DocusaurusConfig,
} as Props,
});
expect(lodashMock).toBeCalledTimes(0);
lodashMock.mockRestore();
@ -185,10 +197,14 @@ describe('handleBrokenLinks', () => {
await expect(() =>
handleBrokenLinks({
allCollectedLinks,
onBrokenLinks: 'throw',
props: {
routes,
baseUrl: '/',
outDir,
siteConfig: {
onBrokenLinks: 'throw',
} as DocusaurusConfig,
} as Props,
}),
).rejects.toThrowErrorMatchingSnapshot();
});

View file

@ -52,8 +52,7 @@ function getPageBrokenLinks({
// @ts-expect-error: React router types RouteConfig with an actual React
// component, but we load route components with string paths.
// We don't actually access component here, so it's fine.
.map((l) => matchRoutes(routes, l))
.flat();
.flatMap((l) => matchRoutes(routes, l));
return matchedRoutes.length === 0;
}
@ -78,10 +77,8 @@ function getAllBrokenLinks({
allCollectedLinks: {[location: string]: string[]};
routes: RouteConfig[];
}): {[location: string]: BrokenLink[]} {
const filteredRoutes = filterIntermediateRoutes(routes);
const allBrokenLinks = _.mapValues(allCollectedLinks, (pageLinks, pagePath) =>
getPageBrokenLinks({pageLinks, pagePath, routes: filteredRoutes}),
getPageBrokenLinks({pageLinks, pagePath, routes}),
);
return _.pickBy(allBrokenLinks, (brokenLinks) => brokenLinks.length > 0);
@ -217,24 +214,29 @@ async function filterExistingFileLinks({
function findOrphanLinks({
allCollectedLinks,
orphanPages,
routes,
}: {
allCollectedLinks: {[location: string]: string[]};
orphanPages: DocusaurusConfig['orphanPages'];
routes: RouteConfig[];
}) {
if (!orphanPages || orphanPages.onOrphanPage === 'ignore') {
return;
}
const visited = new Set<string>();
function dfs(link: string) {
if (visited.has(link)) {
// @ts-expect-error: see comment above
const normalLink = matchRoutes(routes, link)[0]?.match.path;
if (!normalLink || visited.has(normalLink)) {
return;
}
visited.add(link);
allCollectedLinks[link]?.forEach((l) => dfs(resolvePathname(l, link)));
visited.add(normalLink);
allCollectedLinks[normalLink]?.forEach((l) =>
dfs(resolvePathname(l, link)),
);
}
orphanPages.entryPoints.forEach(dfs);
const orphaned = new Set(Object.keys(allCollectedLinks));
visited.forEach((l) => orphaned.delete(l));
const orphaned = routes.map((r) => r.path).filter((l) => !visited.has(l));
reportMessage(
logger.interpolate`Orphan pages found: url=${Array.from(orphaned)}`,
orphanPages.onOrphanPage,
@ -244,7 +246,7 @@ function findOrphanLinks({
export async function handleBrokenLinks({
allCollectedLinks,
props: {
routes,
routes: allRoutes,
baseUrl,
outDir,
siteConfig: {onBrokenLinks, orphanPages},
@ -253,7 +255,8 @@ export async function handleBrokenLinks({
allCollectedLinks: {[location: string]: string[]};
props: Props;
}): Promise<void> {
findOrphanLinks({allCollectedLinks, orphanPages});
const routes = filterIntermediateRoutes(allRoutes);
findOrphanLinks({allCollectedLinks, orphanPages, routes});
if (onBrokenLinks === 'ignore') {
return;