mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-01 19:27:48 +02:00
feat(v2): onBrokenMarkdownLinks config (#3658)
* refactor(v2): move `reportMessage` from `core/src/server/utils` to `utils` package * feat(v2): handle broken markdown links by using onBrokenLinks prop from siteconfig * feat(v2): add a new site config prop called `onBrokenMarkdownLinks` works like onBrokenLinks, but only for markdown links * feat(v2): add `onBrokenMarkdownLinks` to API docs * some changes regarding test issues after adding `onBrokenMarkdownLink` * Update website/versioned_docs/version-2.0.0-alpha.66/api/docusaurus.config.js.md Co-authored-by: Sébastien Lorber <slorber@users.noreply.github.com>
This commit is contained in:
parent
52e7511869
commit
8f2d898f22
18 changed files with 71 additions and 38 deletions
|
@ -4,6 +4,7 @@ module.exports = {
|
||||||
url: 'https://your-docusaurus-test-site.com',
|
url: 'https://your-docusaurus-test-site.com',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
organizationName: 'facebook', // Usually your GitHub org/user name.
|
organizationName: 'facebook', // Usually your GitHub org/user name.
|
||||||
projectName: 'docusaurus', // Usually your repo name.
|
projectName: 'docusaurus', // Usually your repo name.
|
||||||
|
|
|
@ -4,6 +4,7 @@ module.exports = {
|
||||||
url: 'https://your-docusaurus-test-site.com',
|
url: 'https://your-docusaurus-test-site.com',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
organizationName: 'facebook', // Usually your GitHub org/user name.
|
organizationName: 'facebook', // Usually your GitHub org/user name.
|
||||||
projectName: 'docusaurus', // Usually your repo name.
|
projectName: 'docusaurus', // Usually your repo name.
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
url: 'https://your-docusaurus-test-site.com',
|
url: 'https://your-docusaurus-test-site.com',
|
||||||
baseUrl: '/',
|
baseUrl: '/',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
favicon: 'img/favicon.ico',
|
favicon: 'img/favicon.ico',
|
||||||
organizationName: 'facebook', // Usually your GitHub org/user name.
|
organizationName: 'facebook', // Usually your GitHub org/user name.
|
||||||
projectName: 'docusaurus', // Usually your repo name.
|
projectName: 'docusaurus', // Usually your repo name.
|
||||||
|
|
|
@ -31,6 +31,7 @@ module.exports = {
|
||||||
facebookAppId: '199138890728411',
|
facebookAppId: '199138890728411',
|
||||||
},
|
},
|
||||||
onBrokenLinks: 'log',
|
onBrokenLinks: 'log',
|
||||||
|
onBrokenMarkdownLinks: 'log',
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'@docusaurus/preset-classic',
|
'@docusaurus/preset-classic',
|
||||||
|
|
|
@ -302,6 +302,7 @@ export function createConfigFile({
|
||||||
favicon: siteConfig.favicon ?? '',
|
favicon: siteConfig.favicon ?? '',
|
||||||
customFields: customConfigFields,
|
customFields: customConfigFields,
|
||||||
onBrokenLinks: 'log',
|
onBrokenLinks: 'log',
|
||||||
|
onBrokenMarkdownLinks: 'log',
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'@docusaurus/preset-classic',
|
'@docusaurus/preset-classic',
|
||||||
|
|
|
@ -38,6 +38,7 @@ export interface VersionTwoConfig {
|
||||||
noIndex?: boolean;
|
noIndex?: boolean;
|
||||||
githubHost?: string;
|
githubHost?: string;
|
||||||
onBrokenLinks: string;
|
onBrokenLinks: string;
|
||||||
|
onBrokenMarkdownLinks: string;
|
||||||
plugins: Array<[string, {[key: string]: any}]>;
|
plugins: Array<[string, {[key: string]: any}]>;
|
||||||
themes?: [];
|
themes?: [];
|
||||||
presets: [[string, ClassicPresetEntries]];
|
presets: [[string, ClassicPresetEntries]];
|
||||||
|
|
|
@ -11,7 +11,12 @@ import {
|
||||||
STATIC_DIR_NAME,
|
STATIC_DIR_NAME,
|
||||||
DEFAULT_PLUGIN_ID,
|
DEFAULT_PLUGIN_ID,
|
||||||
} from '@docusaurus/core/lib/constants';
|
} from '@docusaurus/core/lib/constants';
|
||||||
import {normalizeUrl, docuHash, aliasedSitePath} from '@docusaurus/utils';
|
import {
|
||||||
|
normalizeUrl,
|
||||||
|
docuHash,
|
||||||
|
aliasedSitePath,
|
||||||
|
reportMessage,
|
||||||
|
} from '@docusaurus/utils';
|
||||||
import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types';
|
import {LoadContext, Plugin, RouteConfig} from '@docusaurus/types';
|
||||||
|
|
||||||
import {loadSidebars, createSidebarsUtils} from './sidebars';
|
import {loadSidebars, createSidebarsUtils} from './sidebars';
|
||||||
|
@ -39,13 +44,12 @@ import {OptionsSchema} from './options';
|
||||||
import {flatten, keyBy, compact} from 'lodash';
|
import {flatten, keyBy, compact} from 'lodash';
|
||||||
import {toGlobalDataVersion} from './globalData';
|
import {toGlobalDataVersion} from './globalData';
|
||||||
import {toVersionMetadataProp} from './props';
|
import {toVersionMetadataProp} from './props';
|
||||||
import chalk from 'chalk';
|
|
||||||
|
|
||||||
export default function pluginContentDocs(
|
export default function pluginContentDocs(
|
||||||
context: LoadContext,
|
context: LoadContext,
|
||||||
options: PluginOptions,
|
options: PluginOptions,
|
||||||
): Plugin<LoadedContent, typeof OptionsSchema> {
|
): Plugin<LoadedContent, typeof OptionsSchema> {
|
||||||
const {siteDir, generatedFilesDir, baseUrl} = context;
|
const {siteDir, generatedFilesDir, baseUrl, siteConfig} = context;
|
||||||
|
|
||||||
const versionsMetadata = readVersionsMetadata({context, options});
|
const versionsMetadata = readVersionsMetadata({context, options});
|
||||||
|
|
||||||
|
@ -311,11 +315,13 @@ export default function pluginContentDocs(
|
||||||
sourceToPermalink,
|
sourceToPermalink,
|
||||||
versionsMetadata,
|
versionsMetadata,
|
||||||
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
onBrokenMarkdownLink: (brokenMarkdownLink) => {
|
||||||
// TODO make this warning configurable?
|
if (siteConfig.onBrokenMarkdownLinks === 'ignore') {
|
||||||
console.warn(
|
return;
|
||||||
chalk.yellow(
|
}
|
||||||
`Docs markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath} for version ${brokenMarkdownLink.version.versionName}`,
|
|
||||||
),
|
reportMessage(
|
||||||
|
`Docs markdown link couldn't be resolved: (${brokenMarkdownLink.link}) in ${brokenMarkdownLink.filePath} for version ${brokenMarkdownLink.version.versionName}`,
|
||||||
|
siteConfig.onBrokenMarkdownLinks,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
1
packages/docusaurus-types/src/index.d.ts
vendored
1
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -21,6 +21,7 @@ export interface DocusaurusConfig {
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
onBrokenLinks: ReportingSeverity;
|
onBrokenLinks: ReportingSeverity;
|
||||||
|
onBrokenMarkdownLinks: ReportingSeverity;
|
||||||
onDuplicateRoutes: ReportingSeverity;
|
onDuplicateRoutes: ReportingSeverity;
|
||||||
noIndex: boolean;
|
noIndex: boolean;
|
||||||
organizationName?: string;
|
organizationName?: string;
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@docusaurus/types": "^2.0.0-alpha.66",
|
||||||
|
"chalk": "^3.0.0",
|
||||||
"escape-string-regexp": "^2.0.0",
|
"escape-string-regexp": "^2.0.0",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"gray-matter": "^4.0.2",
|
"gray-matter": "^4.0.2",
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import matter from 'gray-matter';
|
import matter from 'gray-matter';
|
||||||
import {createHash} from 'crypto';
|
import {createHash} from 'crypto';
|
||||||
|
@ -13,6 +14,7 @@ import kebabCase from 'lodash.kebabcase';
|
||||||
import escapeStringRegexp from 'escape-string-regexp';
|
import escapeStringRegexp from 'escape-string-regexp';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {URL} from 'url';
|
import {URL} from 'url';
|
||||||
|
import {ReportingSeverity} from '@docusaurus/types';
|
||||||
|
|
||||||
// @ts-expect-error: no typedefs :s
|
// @ts-expect-error: no typedefs :s
|
||||||
import resolvePathnameUnsafe from 'resolve-pathname';
|
import resolvePathnameUnsafe from 'resolve-pathname';
|
||||||
|
@ -436,3 +438,28 @@ export function getElementsAround<T extends unknown>(
|
||||||
const next = aroundIndex === max ? undefined : array[aroundIndex + 1];
|
const next = aroundIndex === max ? undefined : array[aroundIndex + 1];
|
||||||
return {previous, next};
|
return {previous, next};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function reportMessage(
|
||||||
|
message: string,
|
||||||
|
reportingSeverity: ReportingSeverity,
|
||||||
|
): void {
|
||||||
|
switch (reportingSeverity) {
|
||||||
|
case 'ignore':
|
||||||
|
break;
|
||||||
|
case 'log':
|
||||||
|
console.log(chalk.bold.blue('info ') + chalk.blue(message));
|
||||||
|
break;
|
||||||
|
case 'warn':
|
||||||
|
console.warn(chalk.bold.yellow('warn ') + chalk.yellow(message));
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
console.error(chalk.bold.red('error ') + chalk.red(message));
|
||||||
|
break;
|
||||||
|
case 'throw':
|
||||||
|
throw new Error(message);
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`unexpected reportingSeverity value: ${reportingSeverity}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ Object {
|
||||||
"favicon": "img/docusaurus.ico",
|
"favicon": "img/docusaurus.ico",
|
||||||
"noIndex": false,
|
"noIndex": false,
|
||||||
"onBrokenLinks": "throw",
|
"onBrokenLinks": "throw",
|
||||||
|
"onBrokenMarkdownLinks": "warn",
|
||||||
"onDuplicateRoutes": "warn",
|
"onDuplicateRoutes": "warn",
|
||||||
"organizationName": "endiliey",
|
"organizationName": "endiliey",
|
||||||
"plugins": Array [
|
"plugins": Array [
|
||||||
|
|
|
@ -10,8 +10,8 @@ import resolvePathname from 'resolve-pathname';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {mapValues, pickBy, flatten, countBy} from 'lodash';
|
import {mapValues, pickBy, flatten, countBy} from 'lodash';
|
||||||
import {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
import {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
||||||
import {removePrefix, removeSuffix} from '@docusaurus/utils';
|
import {removePrefix, removeSuffix, reportMessage} from '@docusaurus/utils';
|
||||||
import {getAllFinalRoutes, reportMessage} from './utils';
|
import {getAllFinalRoutes} from './utils';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
function toReactRouterRoutes(routes: RouteConfig[]): RRRouteConfig[] {
|
function toReactRouterRoutes(routes: RouteConfig[]): RRRouteConfig[] {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
export const DEFAULT_CONFIG: Pick<
|
export const DEFAULT_CONFIG: Pick<
|
||||||
DocusaurusConfig,
|
DocusaurusConfig,
|
||||||
| 'onBrokenLinks'
|
| 'onBrokenLinks'
|
||||||
|
| 'onBrokenMarkdownLinks'
|
||||||
| 'onDuplicateRoutes'
|
| 'onDuplicateRoutes'
|
||||||
| 'plugins'
|
| 'plugins'
|
||||||
| 'themes'
|
| 'themes'
|
||||||
|
@ -27,6 +28,7 @@ export const DEFAULT_CONFIG: Pick<
|
||||||
| 'noIndex'
|
| 'noIndex'
|
||||||
> = {
|
> = {
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
onDuplicateRoutes: 'warn',
|
onDuplicateRoutes: 'warn',
|
||||||
plugins: [],
|
plugins: [],
|
||||||
themes: [],
|
themes: [],
|
||||||
|
@ -64,6 +66,9 @@ const ConfigSchema = Joi.object({
|
||||||
onBrokenLinks: Joi.string()
|
onBrokenLinks: Joi.string()
|
||||||
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
||||||
.default(DEFAULT_CONFIG.onBrokenLinks),
|
.default(DEFAULT_CONFIG.onBrokenLinks),
|
||||||
|
onBrokenMarkdownLinks: Joi.string()
|
||||||
|
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
||||||
|
.default(DEFAULT_CONFIG.onBrokenMarkdownLinks),
|
||||||
onDuplicateRoutes: Joi.string()
|
onDuplicateRoutes: Joi.string()
|
||||||
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
.equal('ignore', 'log', 'warn', 'error', 'throw')
|
||||||
.default(DEFAULT_CONFIG.onDuplicateRoutes),
|
.default(DEFAULT_CONFIG.onDuplicateRoutes),
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
import {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
import {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
||||||
import {getAllFinalRoutes, reportMessage} from './utils';
|
import {reportMessage} from '@docusaurus/utils';
|
||||||
|
import {getAllFinalRoutes} from './utils';
|
||||||
|
|
||||||
export function getAllDuplicateRoutes(
|
export function getAllDuplicateRoutes(
|
||||||
pluginsRouteConfigs: RouteConfig[],
|
pluginsRouteConfigs: RouteConfig[],
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
import chalk from 'chalk';
|
|
||||||
import flatMap from 'lodash.flatmap';
|
import flatMap from 'lodash.flatmap';
|
||||||
import {RouteConfig, ReportingSeverity} from '@docusaurus/types';
|
import {RouteConfig} from '@docusaurus/types';
|
||||||
|
|
||||||
// Recursively get the final routes (routes with no subroutes)
|
// Recursively get the final routes (routes with no subroutes)
|
||||||
export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
|
export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
|
||||||
|
@ -15,28 +14,3 @@ export function getAllFinalRoutes(routeConfig: RouteConfig[]): RouteConfig[] {
|
||||||
}
|
}
|
||||||
return flatMap(routeConfig, getFinalRoutes);
|
return flatMap(routeConfig, getFinalRoutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reportMessage(
|
|
||||||
message: string,
|
|
||||||
reportingSeverity: ReportingSeverity,
|
|
||||||
): void {
|
|
||||||
switch (reportingSeverity) {
|
|
||||||
case 'ignore':
|
|
||||||
break;
|
|
||||||
case 'log':
|
|
||||||
console.log(chalk.bold.blue('info ') + chalk.blue(message));
|
|
||||||
break;
|
|
||||||
case 'warn':
|
|
||||||
console.warn(chalk.bold.yellow('warn ') + chalk.yellow(message));
|
|
||||||
break;
|
|
||||||
case 'error':
|
|
||||||
console.error(chalk.bold.red('error ') + chalk.red(message));
|
|
||||||
break;
|
|
||||||
case 'throw':
|
|
||||||
throw new Error(message);
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`unexpected reportingSeverity value: ${reportingSeverity}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -108,6 +108,14 @@ The broken links detection is only available for a production build (`docusaurus
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### `onBrokenMarkdownLinks`
|
||||||
|
|
||||||
|
- Type: `'ignore' | 'log' | 'warn' | 'error' | 'throw'`
|
||||||
|
|
||||||
|
The behavior of Docusaurus, when it detects any broken markdown link.
|
||||||
|
|
||||||
|
By default, it prints a warning, to let you know about your broken markdown link, but you can change this security if needed.
|
||||||
|
|
||||||
### `onDuplicateRoutes`
|
### `onDuplicateRoutes`
|
||||||
|
|
||||||
- Type: `'ignore' | 'log' | 'warn' | 'error' | 'throw'`
|
- Type: `'ignore' | 'log' | 'warn' | 'error' | 'throw'`
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
baseUrl: '/blog-only/',
|
baseUrl: '/blog-only/',
|
||||||
url: 'https://v2.docusaurus.io',
|
url: 'https://v2.docusaurus.io',
|
||||||
onBrokenLinks: 'throw',
|
onBrokenLinks: 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
themes: ['@docusaurus/theme-live-codeblock'],
|
themes: ['@docusaurus/theme-live-codeblock'],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|
|
@ -49,6 +49,7 @@ module.exports = {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
url: 'https://v2.docusaurus.io',
|
url: 'https://v2.docusaurus.io',
|
||||||
onBrokenLinks: isVersioningDisabled ? 'warn' : 'throw',
|
onBrokenLinks: isVersioningDisabled ? 'warn' : 'throw',
|
||||||
|
onBrokenMarkdownLinks: 'warn',
|
||||||
favicon: 'img/docusaurus.ico',
|
favicon: 'img/docusaurus.ico',
|
||||||
customFields: {
|
customFields: {
|
||||||
description:
|
description:
|
||||||
|
|
Loading…
Add table
Reference in a new issue