mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-03 20:27:20 +02:00
Merge f927499c42
into 67924ca979
This commit is contained in:
commit
1f5e42c89b
8 changed files with 97 additions and 12 deletions
9
packages/docusaurus-types/src/bronkenLinks.d.ts
vendored
Normal file
9
packages/docusaurus-types/src/bronkenLinks.d.ts
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export type BrokenLink = {
|
||||||
|
link: string;
|
||||||
|
resolvedLink: string;
|
||||||
|
anchor: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BrokenLinksMap = {
|
||||||
|
[pathname: string]: BrokenLink[]
|
||||||
|
};
|
10
packages/docusaurus-types/src/config.d.ts
vendored
10
packages/docusaurus-types/src/config.d.ts
vendored
|
@ -13,6 +13,8 @@ import type {PluginConfig, PresetConfig, HtmlTagObject} from './plugin';
|
||||||
|
|
||||||
import type {ProcessorOptions} from '@mdx-js/mdx';
|
import type {ProcessorOptions} from '@mdx-js/mdx';
|
||||||
|
|
||||||
|
import type {BrokenLinksMap} from './bronkenLinks';
|
||||||
|
|
||||||
export type RemarkRehypeOptions = ProcessorOptions['remarkRehypeOptions'];
|
export type RemarkRehypeOptions = ProcessorOptions['remarkRehypeOptions'];
|
||||||
|
|
||||||
export type ReportingSeverity = 'ignore' | 'log' | 'warn' | 'throw';
|
export type ReportingSeverity = 'ignore' | 'log' | 'warn' | 'throw';
|
||||||
|
@ -251,6 +253,14 @@ export type DocusaurusConfig = {
|
||||||
* @default "warn"
|
* @default "warn"
|
||||||
*/
|
*/
|
||||||
onBrokenAnchors: ReportingSeverity;
|
onBrokenAnchors: ReportingSeverity;
|
||||||
|
/**
|
||||||
|
* The behavior of Docusaurus when it detects any broken link or anchor and generates a report.
|
||||||
|
* This functions runs before generating a report.
|
||||||
|
*
|
||||||
|
* @see https://docusaurus.io/docs/api/docusaurus-config#onReportBrokenLinks
|
||||||
|
* @default "warn"
|
||||||
|
*/
|
||||||
|
onReportBrokenLinks: (brokenLinksMap: BrokenLinksMap) => void;
|
||||||
/**
|
/**
|
||||||
* The behavior of Docusaurus when it detects any broken markdown link.
|
* The behavior of Docusaurus when it detects any broken markdown link.
|
||||||
*
|
*
|
||||||
|
|
6
packages/docusaurus-types/src/index.d.ts
vendored
6
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -90,3 +90,9 @@ export {
|
||||||
} from './routing';
|
} from './routing';
|
||||||
|
|
||||||
export {UseDataOptions} from './utils';
|
export {UseDataOptions} from './utils';
|
||||||
|
|
||||||
|
export {
|
||||||
|
BrokenLinksMap,
|
||||||
|
BrokenLink
|
||||||
|
} from './bronkenLinks';
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ async function executePluginsPostBuild({
|
||||||
async function executeBrokenLinksCheck({
|
async function executeBrokenLinksCheck({
|
||||||
props: {
|
props: {
|
||||||
routes,
|
routes,
|
||||||
siteConfig: {onBrokenLinks, onBrokenAnchors},
|
siteConfig: {onBrokenLinks, onBrokenAnchors, onReportBrokenLinks},
|
||||||
},
|
},
|
||||||
collectedData,
|
collectedData,
|
||||||
}: {
|
}: {
|
||||||
|
@ -172,9 +172,10 @@ async function executeBrokenLinksCheck({
|
||||||
}));
|
}));
|
||||||
await handleBrokenLinks({
|
await handleBrokenLinks({
|
||||||
collectedLinks,
|
collectedLinks,
|
||||||
routes,
|
|
||||||
onBrokenLinks,
|
onBrokenLinks,
|
||||||
onBrokenAnchors,
|
onBrokenAnchors,
|
||||||
|
routes,
|
||||||
|
onReportBrokenLinks
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import {jest} from '@jest/globals';
|
import {jest} from '@jest/globals';
|
||||||
import reactRouterConfig from 'react-router-config';
|
import reactRouterConfig from 'react-router-config';
|
||||||
import {handleBrokenLinks} from '../brokenLinks';
|
import {handleBrokenLinks} from '../brokenLinks';
|
||||||
import type {RouteConfig} from '@docusaurus/types';
|
import type {BrokenLinksMap, RouteConfig} from '@docusaurus/types';
|
||||||
|
|
||||||
type Params = Parameters<typeof handleBrokenLinks>[0];
|
type Params = Parameters<typeof handleBrokenLinks>[0];
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ async function testBrokenLinks(params: {
|
||||||
onBrokenLinks?: Params['onBrokenLinks'];
|
onBrokenLinks?: Params['onBrokenLinks'];
|
||||||
onBrokenAnchors?: Params['onBrokenAnchors'];
|
onBrokenAnchors?: Params['onBrokenAnchors'];
|
||||||
routes?: SimpleRoute[];
|
routes?: SimpleRoute[];
|
||||||
|
onReportBrokenLinks?: Params['onReportBrokenLinks'];
|
||||||
}) {
|
}) {
|
||||||
await handleBrokenLinks({
|
await handleBrokenLinks({
|
||||||
collectedLinks: {},
|
collectedLinks: {},
|
||||||
|
@ -34,6 +35,7 @@ async function testBrokenLinks(params: {
|
||||||
...params,
|
...params,
|
||||||
// Unsafe but convenient for tests
|
// Unsafe but convenient for tests
|
||||||
routes: (params.routes ?? []) as RouteConfig[],
|
routes: (params.routes ?? []) as RouteConfig[],
|
||||||
|
onReportBrokenLinks: params.onReportBrokenLinks,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -728,6 +730,31 @@ describe('handleBrokenLinks', () => {
|
||||||
warnMock.mockRestore();
|
warnMock.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can warn for broken links and remove them before building the report', async () => {
|
||||||
|
const warnMock = jest.spyOn(console, 'warn');
|
||||||
|
|
||||||
|
await testBrokenLinks({
|
||||||
|
onBrokenLinks: 'warn',
|
||||||
|
routes: [{path: '/page1'}],
|
||||||
|
collectedLinks: {
|
||||||
|
'/page1': {
|
||||||
|
links: ['/page2'],
|
||||||
|
anchors: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onReportBrokenLinks: (brokenLinksMap: BrokenLinksMap) => {
|
||||||
|
for (const pathname in brokenLinksMap) {
|
||||||
|
if (pathname.startsWith('/page1')) {
|
||||||
|
delete brokenLinksMap[pathname];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(warnMock).toHaveBeenCalledTimes(0);
|
||||||
|
warnMock.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
it('can warn for broken anchors', async () => {
|
it('can warn for broken anchors', async () => {
|
||||||
const warnMock = jest.spyOn(console, 'warn');
|
const warnMock = jest.spyOn(console, 'warn');
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,12 @@ import {
|
||||||
type URLPath,
|
type URLPath,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils-common';
|
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils-common';
|
||||||
import type {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
import type {
|
||||||
|
RouteConfig,
|
||||||
|
ReportingSeverity,
|
||||||
|
BrokenLink,
|
||||||
|
BrokenLinksMap,
|
||||||
|
} from '@docusaurus/types';
|
||||||
|
|
||||||
function matchRoutes(routeConfig: RouteConfig[], pathname: string) {
|
function matchRoutes(routeConfig: RouteConfig[], pathname: string) {
|
||||||
// @ts-expect-error: React router types RouteConfig with an actual React
|
// @ts-expect-error: React router types RouteConfig with an actual React
|
||||||
|
@ -24,14 +29,6 @@ function matchRoutes(routeConfig: RouteConfig[], pathname: string) {
|
||||||
return reactRouterMatchRoutes(routeConfig, pathname);
|
return reactRouterMatchRoutes(routeConfig, pathname);
|
||||||
}
|
}
|
||||||
|
|
||||||
type BrokenLink = {
|
|
||||||
link: string;
|
|
||||||
resolvedLink: string;
|
|
||||||
anchor: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type BrokenLinksMap = {[pathname: string]: BrokenLink[]};
|
|
||||||
|
|
||||||
// The linking data that has been collected on Docusaurus pages during SSG
|
// The linking data that has been collected on Docusaurus pages during SSG
|
||||||
// {rendered page pathname => links and anchors collected on that page}
|
// {rendered page pathname => links and anchors collected on that page}
|
||||||
type CollectedLinks = {
|
type CollectedLinks = {
|
||||||
|
@ -404,11 +401,13 @@ export async function handleBrokenLinks({
|
||||||
onBrokenLinks,
|
onBrokenLinks,
|
||||||
onBrokenAnchors,
|
onBrokenAnchors,
|
||||||
routes,
|
routes,
|
||||||
|
onReportBrokenLinks,
|
||||||
}: {
|
}: {
|
||||||
collectedLinks: CollectedLinks;
|
collectedLinks: CollectedLinks;
|
||||||
onBrokenLinks: ReportingSeverity;
|
onBrokenLinks: ReportingSeverity;
|
||||||
onBrokenAnchors: ReportingSeverity;
|
onBrokenAnchors: ReportingSeverity;
|
||||||
routes: RouteConfig[];
|
routes: RouteConfig[];
|
||||||
|
onReportBrokenLinks?: (brokenLinksMap: BrokenLinksMap) => void;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
|
if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
|
||||||
return;
|
return;
|
||||||
|
@ -417,5 +416,10 @@ export async function handleBrokenLinks({
|
||||||
routes,
|
routes,
|
||||||
collectedLinks: normalizeCollectedLinks(collectedLinks),
|
collectedLinks: normalizeCollectedLinks(collectedLinks),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (onReportBrokenLinks) {
|
||||||
|
onReportBrokenLinks(brokenLinks);
|
||||||
|
}
|
||||||
|
|
||||||
reportBrokenLinks({brokenLinks, onBrokenLinks, onBrokenAnchors});
|
reportBrokenLinks({brokenLinks, onBrokenLinks, onBrokenAnchors});
|
||||||
}
|
}
|
||||||
|
|
|
@ -343,6 +343,7 @@ export const ConfigSchema = Joi.object<DocusaurusConfig>({
|
||||||
onBrokenAnchors: Joi.string()
|
onBrokenAnchors: Joi.string()
|
||||||
.equal('ignore', 'log', 'warn', 'throw')
|
.equal('ignore', 'log', 'warn', 'throw')
|
||||||
.default(DEFAULT_CONFIG.onBrokenAnchors),
|
.default(DEFAULT_CONFIG.onBrokenAnchors),
|
||||||
|
onReportBrokenLinks: Joi.function(),
|
||||||
onBrokenMarkdownLinks: Joi.string()
|
onBrokenMarkdownLinks: Joi.string()
|
||||||
.equal('ignore', 'log', 'warn', 'throw')
|
.equal('ignore', 'log', 'warn', 'throw')
|
||||||
.default(DEFAULT_CONFIG.onBrokenMarkdownLinks),
|
.default(DEFAULT_CONFIG.onBrokenMarkdownLinks),
|
||||||
|
|
|
@ -271,6 +271,33 @@ The behavior of Docusaurus when it detects any broken anchor declared with the `
|
||||||
|
|
||||||
By default, it prints a warning, to let you know about your broken anchors.
|
By default, it prints a warning, to let you know about your broken anchors.
|
||||||
|
|
||||||
|
### `onReportBrokenLinks` {#onReportBrokenLinks}
|
||||||
|
|
||||||
|
- Type: `function`
|
||||||
|
|
||||||
|
When Docusaurus detects a broken link or anchor, it generates a report detailing the issues.
|
||||||
|
|
||||||
|
This function runs before the report is generated, allowing you to modify its contents before final output.
|
||||||
|
|
||||||
|
#### USAGE
|
||||||
|
|
||||||
|
In this example we will delete broken links that start with the path "/api" from the final report.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { Config, BrokenLinksMap } from '@docusaurus/types';
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
onReportBrokenLinks: (brokenLinksMap: BrokenLinksMap) => {
|
||||||
|
for (const pathname in brokenLinksMap) {
|
||||||
|
if (pathname.startsWith('/api')) {
|
||||||
|
delete brokenLinksMap[pathname];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### `onBrokenMarkdownLinks` {#onBrokenMarkdownLinks}
|
### `onBrokenMarkdownLinks` {#onBrokenMarkdownLinks}
|
||||||
|
|
||||||
- Type: `'ignore' | 'log' | 'warn' | 'throw'`
|
- Type: `'ignore' | 'log' | 'warn' | 'throw'`
|
||||||
|
|
Loading…
Add table
Reference in a new issue