mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-30 02:37:59 +02:00
fix(v2): sitemap plugin should handle siteConfig.trailingSlash automatically (#4950)
* create new @docusaurus/utils-common and move applyTrailingSlash there * sitemap plugin should handle siteConfig.trailingSlash automatically * typo
This commit is contained in:
parent
4e5f0febb9
commit
aeb8e9da51
18 changed files with 127 additions and 35 deletions
|
@ -20,6 +20,7 @@
|
||||||
"@docusaurus/core": "2.0.0-beta.0",
|
"@docusaurus/core": "2.0.0-beta.0",
|
||||||
"@docusaurus/types": "2.0.0-beta.0",
|
"@docusaurus/types": "2.0.0-beta.0",
|
||||||
"@docusaurus/utils": "2.0.0-beta.0",
|
"@docusaurus/utils": "2.0.0-beta.0",
|
||||||
|
"@docusaurus/utils-common": "2.0.0-beta.0",
|
||||||
"@docusaurus/utils-validation": "2.0.0-beta.0",
|
"@docusaurus/utils-validation": "2.0.0-beta.0",
|
||||||
"fs-extra": "^10.0.0",
|
"fs-extra": "^10.0.0",
|
||||||
"sitemap": "^7.0.0",
|
"sitemap": "^7.0.0",
|
||||||
|
|
|
@ -30,8 +30,13 @@ describe('normalizeSitemapPluginOptions', () => {
|
||||||
priority: 0.9,
|
priority: 0.9,
|
||||||
trailingSlash: false,
|
trailingSlash: false,
|
||||||
};
|
};
|
||||||
const {value} = await PluginOptionSchema.validate(userOptions);
|
const {value, warning} = await PluginOptionSchema.validate(userOptions);
|
||||||
expect(value).toEqual(userOptions);
|
expect(value).toEqual(userOptions);
|
||||||
|
|
||||||
|
expect(warning?.details?.length).toEqual(1);
|
||||||
|
expect(warning?.details[0].message).toMatchInlineSnapshot(
|
||||||
|
`"Option \\"trailingSlash\\" of the sitemap plugin is deprecated: Please use the new Docusaurus global trailingSlash config instead, and the sitemaps plugin will use it."`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should reject out-of-range priority inputs', () => {
|
test('should reject out-of-range priority inputs', () => {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {SitemapStream, streamToPromise} from 'sitemap';
|
||||||
import {PluginOptions} from './types';
|
import {PluginOptions} from './types';
|
||||||
import {DocusaurusConfig} from '@docusaurus/types';
|
import {DocusaurusConfig} from '@docusaurus/types';
|
||||||
import {addTrailingSlash} from '@docusaurus/utils';
|
import {addTrailingSlash} from '@docusaurus/utils';
|
||||||
|
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||||
|
|
||||||
export default async function createSitemap(
|
export default async function createSitemap(
|
||||||
siteConfig: DocusaurusConfig,
|
siteConfig: DocusaurusConfig,
|
||||||
|
@ -25,11 +26,21 @@ export default async function createSitemap(
|
||||||
hostname,
|
hostname,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function applySitemapTrailingSlash(routePath: string): string {
|
||||||
|
// kept for retrocompatibility
|
||||||
|
// TODO remove deprecated trailingSlash option before 2022
|
||||||
|
if (options.trailingSlash) {
|
||||||
|
return addTrailingSlash(routePath);
|
||||||
|
} else {
|
||||||
|
return applyTrailingSlash(routePath, trailingSlash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
routesPaths
|
routesPaths
|
||||||
.filter((route) => !route.endsWith('404.html'))
|
.filter((route) => !route.endsWith('404.html'))
|
||||||
.map((routePath) =>
|
.map((routePath) =>
|
||||||
sitemapStream.write({
|
sitemapStream.write({
|
||||||
url: trailingSlash ? addTrailingSlash(routePath) : routePath,
|
url: applySitemapTrailingSlash(routePath),
|
||||||
changefreq,
|
changefreq,
|
||||||
priority,
|
priority,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -25,5 +25,11 @@ export const PluginOptionSchema = Joi.object({
|
||||||
.valid(...Object.values(EnumChangefreq))
|
.valid(...Object.values(EnumChangefreq))
|
||||||
.default(DEFAULT_OPTIONS.changefreq),
|
.default(DEFAULT_OPTIONS.changefreq),
|
||||||
priority: Joi.number().min(0).max(1).default(DEFAULT_OPTIONS.priority),
|
priority: Joi.number().min(0).max(1).default(DEFAULT_OPTIONS.priority),
|
||||||
trailingSlash: Joi.bool().default(false),
|
trailingSlash: Joi.bool().default(false).warning('deprecate.error', {
|
||||||
|
msg:
|
||||||
|
'Please use the new Docusaurus global trailingSlash config instead, and the sitemaps plugin will use it.',
|
||||||
|
}),
|
||||||
|
}).messages({
|
||||||
|
'deprecate.error':
|
||||||
|
'Option {#label} of the sitemap plugin is deprecated: {#msg}',
|
||||||
});
|
});
|
||||||
|
|
3
packages/docusaurus-utils-common/README.md
Normal file
3
packages/docusaurus-utils-common/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# `@docusaurus/utils`
|
||||||
|
|
||||||
|
Common (Node/Browser) utility functions for Docusaurus packages.
|
27
packages/docusaurus-utils-common/package.json
Normal file
27
packages/docusaurus-utils-common/package.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/utils-common",
|
||||||
|
"version": "2.0.0-beta.0",
|
||||||
|
"description": "Common (Node/Browser) utility functions for Docusaurus packages.",
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"types": "./lib/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"watch": "tsc --watch"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/facebook/docusaurus.git",
|
||||||
|
"directory": "packages/docusaurus-utils-common"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@docusaurus/types": "2.0.0-beta.0",
|
||||||
|
"tslib": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.13.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,4 +86,25 @@ describe('applyTrailingSlash', () => {
|
||||||
'/abc/?search#anchor',
|
'/abc/?search#anchor',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should apply to fully qualified urls', () => {
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc?search#anchor', true),
|
||||||
|
).toEqual('https://xyz.com/abc/?search#anchor');
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc?search#anchor', false),
|
||||||
|
).toEqual('https://xyz.com/abc?search#anchor');
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc?search#anchor', undefined),
|
||||||
|
).toEqual('https://xyz.com/abc?search#anchor');
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc/?search#anchor', true),
|
||||||
|
).toEqual('https://xyz.com/abc/?search#anchor');
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc/?search#anchor', false),
|
||||||
|
).toEqual('https://xyz.com/abc?search#anchor');
|
||||||
|
expect(
|
||||||
|
applyTrailingSlash('https://xyz.com/abc/?search#anchor', undefined),
|
||||||
|
).toEqual('https://xyz.com/abc/?search#anchor');
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -14,12 +14,14 @@ export default function applyTrailingSlash(
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO deduplicate: also present in @docusaurus/utils
|
||||||
function addTrailingSlash(str: string): string {
|
function addTrailingSlash(str: string): string {
|
||||||
return str.endsWith('/') ? str : `${str}/`;
|
return str.endsWith('/') ? str : `${str}/`;
|
||||||
}
|
}
|
||||||
function removeTrailingSlash(str: string): string {
|
function removeTrailingSlash(str: string): string {
|
||||||
return str.endsWith('/') ? str.slice(0, -1) : str;
|
return str.endsWith('/') ? str.slice(0, -1) : str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// undefined = legacy retrocompatible behavior
|
// undefined = legacy retrocompatible behavior
|
||||||
if (typeof trailingSlash === 'undefined') {
|
if (typeof trailingSlash === 'undefined') {
|
||||||
return path;
|
return path;
|
8
packages/docusaurus-utils-common/src/index.ts
Normal file
8
packages/docusaurus-utils-common/src/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export {default as applyTrailingSlash} from './applyTrailingSlash';
|
10
packages/docusaurus-utils-common/tsconfig.json
Normal file
10
packages/docusaurus-utils-common/tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"incremental": true,
|
||||||
|
"tsBuildInfoFile": "./lib/.tsbuildinfo",
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "lib",
|
||||||
|
"noEmitHelpers": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,15 @@ export const logValidationBugReportHint = (): void => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function printWarning(warning?: Joi.ValidationError) {
|
||||||
|
if (warning) {
|
||||||
|
const warningMessages = warning.details
|
||||||
|
.map(({message}) => message)
|
||||||
|
.join('\n');
|
||||||
|
console.log(chalk.yellow(warningMessages));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function normalizePluginOptions<T extends {id?: string}>(
|
export function normalizePluginOptions<T extends {id?: string}>(
|
||||||
schema: Joi.ObjectSchema<T>,
|
schema: Joi.ObjectSchema<T>,
|
||||||
options: Partial<T>,
|
options: Partial<T>,
|
||||||
|
@ -44,9 +53,12 @@ export function normalizePluginOptions<T extends {id?: string}>(
|
||||||
const finalSchema = schema.append({
|
const finalSchema = schema.append({
|
||||||
id: PluginIdSchema,
|
id: PluginIdSchema,
|
||||||
});
|
});
|
||||||
const {error, value} = finalSchema.validate(options, {
|
const {error, warning, value} = finalSchema.validate(options, {
|
||||||
convert: false,
|
convert: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
printWarning(warning);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logValidationBugReportHint();
|
logValidationBugReportHint();
|
||||||
if (isValidationDisabledEscapeHatch) {
|
if (isValidationDisabledEscapeHatch) {
|
||||||
|
@ -56,6 +68,7 @@ export function normalizePluginOptions<T extends {id?: string}>(
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,10 +81,12 @@ export function normalizeThemeConfig<T>(
|
||||||
// otherwise one theme would fail validating the data of another theme
|
// otherwise one theme would fail validating the data of another theme
|
||||||
const finalSchema = schema.unknown();
|
const finalSchema = schema.unknown();
|
||||||
|
|
||||||
const {error, value} = finalSchema.validate(themeConfig, {
|
const {error, warning, value} = finalSchema.validate(themeConfig, {
|
||||||
convert: false,
|
convert: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
printWarning(warning);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logValidationBugReportHint();
|
logValidationBugReportHint();
|
||||||
if (isValidationDisabledEscapeHatch) {
|
if (isValidationDisabledEscapeHatch) {
|
||||||
|
@ -112,6 +127,8 @@ export function validateFrontMatter<T>(
|
||||||
abortEarly: false,
|
abortEarly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
printWarning(warning);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
const frontMatterString = JSON.stringify(frontMatter, null, 2);
|
const frontMatterString = JSON.stringify(frontMatter, null, 2);
|
||||||
const errorDetails = error.details;
|
const errorDetails = error.details;
|
||||||
|
@ -132,12 +149,5 @@ export function validateFrontMatter<T>(
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warning) {
|
|
||||||
const warningMessages = warning.details
|
|
||||||
.map(({message}) => message)
|
|
||||||
.join('\n');
|
|
||||||
console.log(chalk.yellow(warningMessages));
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# `@docusaurus/utils`
|
# `@docusaurus/utils`
|
||||||
|
|
||||||
Node validation utility functions for Docusaurus packages.
|
Node utility functions for Docusaurus packages.
|
||||||
|
|
|
@ -313,13 +313,15 @@ export function resolvePathname(to: string, from?: string): string {
|
||||||
export function addLeadingSlash(str: string): string {
|
export function addLeadingSlash(str: string): string {
|
||||||
return str.startsWith('/') ? str : `/${str}`;
|
return str.startsWith('/') ? str : `/${str}`;
|
||||||
}
|
}
|
||||||
export function addTrailingSlash(str: string): string {
|
|
||||||
return str.endsWith('/') ? str : `${str}/`;
|
|
||||||
}
|
|
||||||
export function addTrailingPathSeparator(str: string): string {
|
export function addTrailingPathSeparator(str: string): string {
|
||||||
return str.endsWith(path.sep) ? str : `${str}${path.sep}`;
|
return str.endsWith(path.sep) ? str : `${str}${path.sep}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO deduplicate: also present in @docusaurus/utils-common
|
||||||
|
export function addTrailingSlash(str: string): string {
|
||||||
|
return str.endsWith('/') ? str : `${str}/`;
|
||||||
|
}
|
||||||
export function removeTrailingSlash(str: string): string {
|
export function removeTrailingSlash(str: string): string {
|
||||||
return removeSuffix(str, '/');
|
return removeSuffix(str, '/');
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
"@docusaurus/react-loadable": "5.5.0",
|
"@docusaurus/react-loadable": "5.5.0",
|
||||||
"@docusaurus/types": "2.0.0-beta.0",
|
"@docusaurus/types": "2.0.0-beta.0",
|
||||||
"@docusaurus/utils": "2.0.0-beta.0",
|
"@docusaurus/utils": "2.0.0-beta.0",
|
||||||
|
"@docusaurus/utils-common": "2.0.0-beta.0",
|
||||||
"@docusaurus/utils-validation": "2.0.0-beta.0",
|
"@docusaurus/utils-validation": "2.0.0-beta.0",
|
||||||
"@slorber/static-site-generator-webpack-plugin": "^4.0.0",
|
"@slorber/static-site-generator-webpack-plugin": "^4.0.0",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@svgr/webpack": "^5.5.0",
|
||||||
|
|
|
@ -13,7 +13,7 @@ import isInternalUrl from './isInternalUrl';
|
||||||
import ExecutionEnvironment from './ExecutionEnvironment';
|
import ExecutionEnvironment from './ExecutionEnvironment';
|
||||||
import {useLinksCollector} from '../LinksCollector';
|
import {useLinksCollector} from '../LinksCollector';
|
||||||
import {useBaseUrlUtils} from './useBaseUrl';
|
import {useBaseUrlUtils} from './useBaseUrl';
|
||||||
import applyTrailingSlash from './applyTrailingSlash';
|
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||||
|
|
||||||
import type {LinkProps} from '@docusaurus/Link';
|
import type {LinkProps} from '@docusaurus/Link';
|
||||||
import type docusaurus from '../docusaurus';
|
import type docusaurus from '../docusaurus';
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {RouteConfig} from '@docusaurus/types';
|
import {RouteConfig} from '@docusaurus/types';
|
||||||
import {addTrailingSlash, removeTrailingSlash} from '@docusaurus/utils';
|
import {applyTrailingSlash} from '@docusaurus/utils-common';
|
||||||
|
|
||||||
export default function applyRouteTrailingSlash(
|
export default function applyRouteTrailingSlash(
|
||||||
route: RouteConfig,
|
route: RouteConfig,
|
||||||
|
@ -17,23 +17,9 @@ export default function applyRouteTrailingSlash(
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNewRoutePath() {
|
|
||||||
// undefined = legacy retrocompatible behavior
|
|
||||||
if (typeof trailingSlash === 'undefined') {
|
|
||||||
return route.path;
|
|
||||||
}
|
|
||||||
// The trailing slash should be handled before the ?search#hash !
|
|
||||||
// For routing #anchor is normally not possible, but querystring remains possible
|
|
||||||
const [pathname] = route.path.split(/[#?]/);
|
|
||||||
const newPathname = trailingSlash
|
|
||||||
? addTrailingSlash(pathname)
|
|
||||||
: removeTrailingSlash(pathname);
|
|
||||||
return route.path.replace(pathname, newPathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...route,
|
...route,
|
||||||
path: getNewRoutePath(),
|
path: applyTrailingSlash(route.path, trailingSlash),
|
||||||
...(route.routes && {
|
...(route.routes && {
|
||||||
routes: route.routes.map((subroute) =>
|
routes: route.routes.map((subroute) =>
|
||||||
applyRouteTrailingSlash(subroute, trailingSlash),
|
applyRouteTrailingSlash(subroute, trailingSlash),
|
||||||
|
|
|
@ -10,7 +10,6 @@ Object {
|
||||||
"@docusaurus/Link": "../../client/exports/Link.tsx",
|
"@docusaurus/Link": "../../client/exports/Link.tsx",
|
||||||
"@docusaurus/Noop": "../../client/exports/Noop.ts",
|
"@docusaurus/Noop": "../../client/exports/Noop.ts",
|
||||||
"@docusaurus/Translate": "../../client/exports/Translate.tsx",
|
"@docusaurus/Translate": "../../client/exports/Translate.tsx",
|
||||||
"@docusaurus/applyTrailingSlash": "../../client/exports/applyTrailingSlash.tsx",
|
|
||||||
"@docusaurus/constants": "../../client/exports/constants.ts",
|
"@docusaurus/constants": "../../client/exports/constants.ts",
|
||||||
"@docusaurus/context": "../../client/exports/context.ts",
|
"@docusaurus/context": "../../client/exports/context.ts",
|
||||||
"@docusaurus/isInternalUrl": "../../client/exports/isInternalUrl.ts",
|
"@docusaurus/isInternalUrl": "../../client/exports/isInternalUrl.ts",
|
||||||
|
|
Loading…
Add table
Reference in a new issue