mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-04 20:57:17 +02:00
feat(client-redirects): make plugin respect onDuplicateRoutes config (#7649)
This commit is contained in:
parent
51d7899b0d
commit
4b5a47ba37
5 changed files with 66 additions and 13 deletions
|
@ -9,18 +9,21 @@ import {removeTrailingSlash} from '@docusaurus/utils';
|
||||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||||
import collectRedirects from '../collectRedirects';
|
import collectRedirects from '../collectRedirects';
|
||||||
import {validateOptions} from '../options';
|
import {validateOptions} from '../options';
|
||||||
|
import type {DocusaurusConfig} from '@docusaurus/types';
|
||||||
import type {Options} from '../options';
|
import type {Options} from '../options';
|
||||||
import type {PluginContext} from '../types';
|
import type {PluginContext} from '../types';
|
||||||
|
|
||||||
function createTestPluginContext(
|
function createTestPluginContext(
|
||||||
options?: Options,
|
options?: Options,
|
||||||
relativeRoutesPaths: string[] = [],
|
relativeRoutesPaths: string[] = [],
|
||||||
|
siteConfig: Partial<DocusaurusConfig> = {},
|
||||||
): PluginContext {
|
): PluginContext {
|
||||||
return {
|
return {
|
||||||
outDir: '/tmp',
|
outDir: '/tmp',
|
||||||
baseUrl: 'https://docusaurus.io',
|
baseUrl: 'https://docusaurus.io',
|
||||||
relativeRoutesPaths,
|
relativeRoutesPaths,
|
||||||
options: validateOptions({validate: normalizePluginOptions, options}),
|
options: validateOptions({validate: normalizePluginOptions, options}),
|
||||||
|
siteConfig: {onDuplicateRoutes: 'warn', ...siteConfig} as DocusaurusConfig,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,9 +311,10 @@ describe('collectRedirects', () => {
|
||||||
collectRedirects(
|
collectRedirects(
|
||||||
createTestPluginContext(
|
createTestPluginContext(
|
||||||
{
|
{
|
||||||
createRedirects: (routePath) => {
|
// @ts-expect-error: for test
|
||||||
|
createRedirects(routePath) {
|
||||||
if (routePath === '/') {
|
if (routePath === '/') {
|
||||||
return [[`/fromPath`]] as unknown as string;
|
return [[`/fromPath`]];
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
@ -356,4 +360,44 @@ describe('collectRedirects', () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('throws when creating duplicate redirect routes and onDuplicateRoutes=throw', () => {
|
||||||
|
expect(() =>
|
||||||
|
collectRedirects(
|
||||||
|
createTestPluginContext(
|
||||||
|
{
|
||||||
|
createRedirects() {
|
||||||
|
return '/random-path';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
['/path-one', '/path-two'],
|
||||||
|
{onDuplicateRoutes: 'throw'},
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname: "/random-path"
|
||||||
|
It is not possible to redirect the same pathname to multiple destinations:
|
||||||
|
- {"from":"/random-path","to":"/path-one"}
|
||||||
|
- {"from":"/random-path","to":"/path-two"}"
|
||||||
|
`);
|
||||||
|
expect(() =>
|
||||||
|
collectRedirects(
|
||||||
|
createTestPluginContext(
|
||||||
|
{
|
||||||
|
redirects: [
|
||||||
|
{from: '/path-three', to: '/path-one'},
|
||||||
|
{from: '/path-two', to: '/path-one'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
['/path-one', '/path-two'],
|
||||||
|
{onDuplicateRoutes: 'throw'},
|
||||||
|
),
|
||||||
|
undefined,
|
||||||
|
),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored:
|
||||||
|
- {"from":"/path-two","to":"/path-one"}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,8 +102,10 @@ function filterUnwantedRedirects(
|
||||||
Object.entries(_.groupBy(redirects, (redirect) => redirect.from)).forEach(
|
Object.entries(_.groupBy(redirects, (redirect) => redirect.from)).forEach(
|
||||||
([from, groupedFromRedirects]) => {
|
([from, groupedFromRedirects]) => {
|
||||||
if (groupedFromRedirects.length > 1) {
|
if (groupedFromRedirects.length > 1) {
|
||||||
logger.error`name=${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: path=${from}
|
logger.report(
|
||||||
It is not possible to redirect the same pathname to multiple destinations: ${groupedFromRedirects.map(
|
pluginContext.siteConfig.onDuplicateRoutes,
|
||||||
|
)`name=${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: path=${from}
|
||||||
|
It is not possible to redirect the same pathname to multiple destinations:${groupedFromRedirects.map(
|
||||||
(r) => JSON.stringify(r),
|
(r) => JSON.stringify(r),
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
@ -111,18 +113,18 @@ It is not possible to redirect the same pathname to multiple destinations: ${gro
|
||||||
);
|
);
|
||||||
const collectedRedirects = _.uniqBy(redirects, (redirect) => redirect.from);
|
const collectedRedirects = _.uniqBy(redirects, (redirect) => redirect.from);
|
||||||
|
|
||||||
// We don't want to override an already existing route with a redirect file!
|
const {false: newRedirects = [], true: redirectsOverridingExistingPath = []} =
|
||||||
const redirectsOverridingExistingPath = collectedRedirects.filter(
|
_.groupBy(collectedRedirects, (redirect) =>
|
||||||
(redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from),
|
pluginContext.relativeRoutesPaths.includes(redirect.from),
|
||||||
);
|
);
|
||||||
if (redirectsOverridingExistingPath.length > 0) {
|
if (redirectsOverridingExistingPath.length > 0) {
|
||||||
logger.error`name=${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored: ${redirectsOverridingExistingPath.map(
|
logger.report(
|
||||||
|
pluginContext.siteConfig.onDuplicateRoutes,
|
||||||
|
)`name=${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored:${redirectsOverridingExistingPath.map(
|
||||||
(r) => JSON.stringify(r),
|
(r) => JSON.stringify(r),
|
||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
return collectedRedirects.filter(
|
return newRedirects;
|
||||||
(redirect) => !pluginContext.relativeRoutesPaths.includes(redirect.from),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRedirectsOptionRedirects(
|
function createRedirectsOptionRedirects(
|
||||||
|
|
|
@ -31,6 +31,7 @@ export default function pluginClientRedirectsPages(
|
||||||
baseUrl: props.baseUrl,
|
baseUrl: props.baseUrl,
|
||||||
outDir: props.outDir,
|
outDir: props.outDir,
|
||||||
options,
|
options,
|
||||||
|
siteConfig: props.siteConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
const redirects: RedirectItem[] = collectRedirects(
|
const redirects: RedirectItem[] = collectRedirects(
|
||||||
|
|
|
@ -11,7 +11,7 @@ import type {PluginOptions} from './options';
|
||||||
/**
|
/**
|
||||||
* The minimal infos the plugin needs to work
|
* The minimal infos the plugin needs to work
|
||||||
*/
|
*/
|
||||||
export type PluginContext = Pick<Props, 'outDir' | 'baseUrl'> & {
|
export type PluginContext = Pick<Props, 'outDir' | 'baseUrl' | 'siteConfig'> & {
|
||||||
options: PluginOptions;
|
options: PluginOptions;
|
||||||
relativeRoutesPaths: string[];
|
relativeRoutesPaths: string[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,12 @@ Accepted fields:
|
||||||
</APITable>
|
</APITable>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
This plugin will also read the [`siteConfig.onDuplicateRoutes`](../docusaurus.config.js.md#onDuplicateRoutes) config to adjust its logging level when multiple files will be emitted to the same location.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
### Types {#types}
|
### Types {#types}
|
||||||
|
|
||||||
#### `RedirectRule` {#RedirectRule}
|
#### `RedirectRule` {#RedirectRule}
|
||||||
|
|
Loading…
Add table
Reference in a new issue