@docusaurus/plugin-client-redirects 2.0.0-beta.ff31de0ff → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/lib/collectRedirects.d.ts +2 -2
  2. package/lib/collectRedirects.js +45 -47
  3. package/lib/createRedirectPageContent.d.ts +2 -4
  4. package/lib/createRedirectPageContent.js +2 -4
  5. package/lib/extensionRedirects.d.ts +11 -3
  6. package/lib/extensionRedirects.js +22 -17
  7. package/lib/index.d.ts +5 -3
  8. package/lib/index.js +11 -8
  9. package/lib/options.d.ts +35 -0
  10. package/lib/options.js +36 -0
  11. package/lib/redirectValidation.d.ts +2 -2
  12. package/lib/types.d.ts +14 -16
  13. package/lib/writeRedirectFiles.d.ts +5 -5
  14. package/lib/writeRedirectFiles.js +41 -14
  15. package/package.json +16 -13
  16. package/src/collectRedirects.ts +76 -83
  17. package/src/createRedirectPageContent.ts +8 -10
  18. package/src/deps.d.ts +16 -0
  19. package/src/extensionRedirects.ts +32 -25
  20. package/src/index.ts +21 -14
  21. package/src/options.ts +75 -0
  22. package/src/redirectValidation.ts +3 -3
  23. package/src/types.ts +16 -30
  24. package/src/writeRedirectFiles.ts +53 -25
  25. package/lib/.tsbuildinfo +0 -1970
  26. package/lib/normalizePluginOptions.d.ts +0 -9
  27. package/lib/normalizePluginOptions.js +0 -44
  28. package/src/__tests__/__snapshots__/collectRedirects.test.ts.snap +0 -27
  29. package/src/__tests__/__snapshots__/createRedirectPageContent.test.ts.snap +0 -29
  30. package/src/__tests__/__snapshots__/normalizePluginOptions.test.ts.snap +0 -35
  31. package/src/__tests__/__snapshots__/redirectValidation.test.ts.snap +0 -11
  32. package/src/__tests__/__snapshots__/writeRedirectFiles.test.ts.snap +0 -71
  33. package/src/__tests__/collectRedirects.test.ts +0 -255
  34. package/src/__tests__/createRedirectPageContent.test.ts +0 -26
  35. package/src/__tests__/extensionRedirects.test.ts +0 -109
  36. package/src/__tests__/normalizePluginOptions.test.ts +0 -76
  37. package/src/__tests__/redirectValidation.test.ts +0 -66
  38. package/src/__tests__/writeRedirectFiles.test.ts +0 -146
  39. package/src/normalizePluginOptions.ts +0 -55
  40. package/tsconfig.json +0 -10
@@ -5,40 +5,68 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {flatten, uniqBy, difference, groupBy} from 'lodash';
9
- import {
10
- PluginContext,
11
- RedirectMetadata,
12
- PluginOptions,
13
- RedirectOption,
14
- } from './types';
8
+ import _ from 'lodash';
9
+ import logger from '@docusaurus/logger';
10
+ import {applyTrailingSlash} from '@docusaurus/utils-common';
15
11
  import {
16
12
  createFromExtensionsRedirects,
17
13
  createToExtensionsRedirects,
18
14
  } from './extensionRedirects';
19
15
  import {validateRedirect} from './redirectValidation';
20
-
21
- import chalk from 'chalk';
16
+ import type {PluginOptions, RedirectOption} from './options';
17
+ import type {PluginContext, RedirectItem} from './types';
22
18
 
23
19
  export default function collectRedirects(
24
20
  pluginContext: PluginContext,
25
- ): RedirectMetadata[] {
26
- const redirects = doCollectRedirects(pluginContext);
21
+ trailingSlash: boolean | undefined,
22
+ ): RedirectItem[] {
23
+ // For each plugin config option, create the appropriate redirects
24
+ const redirects = [
25
+ ...createFromExtensionsRedirects(
26
+ pluginContext.relativeRoutesPaths,
27
+ pluginContext.options.fromExtensions,
28
+ ),
29
+ ...createToExtensionsRedirects(
30
+ pluginContext.relativeRoutesPaths,
31
+ pluginContext.options.toExtensions,
32
+ ),
33
+ ...createRedirectsOptionRedirects(pluginContext.options.redirects),
34
+ ...createCreateRedirectsOptionRedirects(
35
+ pluginContext.relativeRoutesPaths,
36
+ pluginContext.options.createRedirects,
37
+ ),
38
+ ].map((redirect) => ({
39
+ ...redirect,
40
+ // Given a redirect with `to: "/abc"` and `trailingSlash` enabled:
41
+ //
42
+ // - We don't want to reject `to: "/abc"`, as that unambiguously points to
43
+ // `/abc/` now;
44
+ // - We want to redirect `to: /abc/` without the user having to change all
45
+ // her redirect plugin options
46
+ //
47
+ // It should be easy to toggle `trailingSlash` option without having to
48
+ // change other configs
49
+ to: applyTrailingSlash(redirect.to, {
50
+ trailingSlash,
51
+ baseUrl: pluginContext.baseUrl,
52
+ }),
53
+ }));
54
+
27
55
  validateCollectedRedirects(redirects, pluginContext);
28
56
  return filterUnwantedRedirects(redirects, pluginContext);
29
57
  }
30
58
 
31
59
  function validateCollectedRedirects(
32
- redirects: RedirectMetadata[],
60
+ redirects: RedirectItem[],
33
61
  pluginContext: PluginContext,
34
62
  ) {
35
- const redirectValidationErrors: string[] = redirects
63
+ const redirectValidationErrors = redirects
36
64
  .map((redirect) => {
37
65
  try {
38
66
  validateRedirect(redirect);
39
67
  return undefined;
40
- } catch (e) {
41
- return e.message;
68
+ } catch (err) {
69
+ return (err as Error).message;
42
70
  }
43
71
  })
44
72
  .filter(Boolean);
@@ -52,7 +80,7 @@ function validateCollectedRedirects(
52
80
 
53
81
  const allowedToPaths = pluginContext.relativeRoutesPaths;
54
82
  const toPaths = redirects.map((redirect) => redirect.to);
55
- const illegalToPaths = difference(toPaths, allowedToPaths);
83
+ const illegalToPaths = _.difference(toPaths, allowedToPaths);
56
84
  if (illegalToPaths.length > 0) {
57
85
  throw new Error(
58
86
  `You are trying to create client-side redirections to paths that do not exist:
@@ -66,101 +94,66 @@ Valid paths you can redirect to:
66
94
  }
67
95
 
68
96
  function filterUnwantedRedirects(
69
- redirects: RedirectMetadata[],
97
+ redirects: RedirectItem[],
70
98
  pluginContext: PluginContext,
71
- ): RedirectMetadata[] {
72
- // we don't want to create twice the same redirect
73
- // that would lead to writing twice the same html redirection file
74
- Object.entries(groupBy(redirects, (redirect) => redirect.from)).forEach(
99
+ ): RedirectItem[] {
100
+ // We don't want to create the same redirect twice, since that would lead to
101
+ // writing the same html redirection file twice.
102
+ Object.entries(_.groupBy(redirects, (redirect) => redirect.from)).forEach(
75
103
  ([from, groupedFromRedirects]) => {
76
104
  if (groupedFromRedirects.length > 1) {
77
- console.error(
78
- chalk.red(
79
- `@docusaurus/plugin-client-redirects: multiple redirects are created with the same "from" pathname=${from}
80
- It is not possible to redirect the same pathname to multiple destinations:
81
- - ${groupedFromRedirects.map((r) => JSON.stringify(r)).join('\n- ')}
82
- `,
83
- ),
84
- );
105
+ logger.report(
106
+ pluginContext.siteConfig.onDuplicateRoutes,
107
+ )`name=${'@docusaurus/plugin-client-redirects'}: multiple redirects are created with the same "from" pathname: path=${from}
108
+ It is not possible to redirect the same pathname to multiple destinations:${groupedFromRedirects.map(
109
+ (r) => JSON.stringify(r),
110
+ )}`;
85
111
  }
86
112
  },
87
113
  );
88
- const collectedRedirects = uniqBy(redirects, (redirect) => redirect.from);
114
+ const collectedRedirects = _.uniqBy(redirects, (redirect) => redirect.from);
89
115
 
90
- // We don't want to override an already existing route with a redirect file!
91
- const redirectsOverridingExistingPath = collectedRedirects.filter(
92
- (redirect) => pluginContext.relativeRoutesPaths.includes(redirect.from),
93
- );
94
- if (redirectsOverridingExistingPath.length > 0) {
95
- console.error(
96
- chalk.red(
97
- `@docusaurus/plugin-client-redirects: some redirects would override existing paths, and will be ignored:
98
- - ${redirectsOverridingExistingPath.map((r) => JSON.stringify(r)).join('\n- ')}
99
- `,
100
- ),
116
+ const {false: newRedirects = [], true: redirectsOverridingExistingPath = []} =
117
+ _.groupBy(collectedRedirects, (redirect) =>
118
+ pluginContext.relativeRoutesPaths.includes(redirect.from),
101
119
  );
120
+ if (redirectsOverridingExistingPath.length > 0) {
121
+ logger.report(
122
+ pluginContext.siteConfig.onDuplicateRoutes,
123
+ )`name=${'@docusaurus/plugin-client-redirects'}: some redirects would override existing paths, and will be ignored:${redirectsOverridingExistingPath.map(
124
+ (r) => JSON.stringify(r),
125
+ )}`;
102
126
  }
103
- return collectedRedirects.filter(
104
- (redirect) => !pluginContext.relativeRoutesPaths.includes(redirect.from),
105
- );
106
- }
107
-
108
- // For each plugin config option, create the appropriate redirects
109
- function doCollectRedirects(pluginContext: PluginContext): RedirectMetadata[] {
110
- return [
111
- ...createFromExtensionsRedirects(
112
- pluginContext.relativeRoutesPaths,
113
- pluginContext.options.fromExtensions,
114
- ),
115
- ...createToExtensionsRedirects(
116
- pluginContext.relativeRoutesPaths,
117
- pluginContext.options.toExtensions,
118
- ),
119
- ...createRedirectsOptionRedirects(pluginContext.options.redirects),
120
- ...createCreateRedirectsOptionRedirects(
121
- pluginContext.relativeRoutesPaths,
122
- pluginContext.options.createRedirects,
123
- ),
124
- ];
127
+ return newRedirects;
125
128
  }
126
129
 
127
130
  function createRedirectsOptionRedirects(
128
131
  redirectsOption: PluginOptions['redirects'],
129
- ): RedirectMetadata[] {
130
- // For conveniency, user can use a string or a string[]
131
- function optionToRedirects(option: RedirectOption): RedirectMetadata[] {
132
+ ): RedirectItem[] {
133
+ // For convenience, user can use a string or a string[]
134
+ function optionToRedirects(option: RedirectOption): RedirectItem[] {
132
135
  if (typeof option.from === 'string') {
133
136
  return [{from: option.from, to: option.to}];
134
137
  }
135
- return option.from.map((from) => ({
136
- from,
137
- to: option.to,
138
- }));
138
+ return option.from.map((from) => ({from, to: option.to}));
139
139
  }
140
140
 
141
- return flatten(redirectsOption.map(optionToRedirects));
141
+ return redirectsOption.flatMap(optionToRedirects);
142
142
  }
143
143
 
144
144
  // Create redirects from the "createRedirects" fn provided by the user
145
145
  function createCreateRedirectsOptionRedirects(
146
146
  paths: string[],
147
147
  createRedirects: PluginOptions['createRedirects'],
148
- ): RedirectMetadata[] {
149
- function createPathRedirects(path: string): RedirectMetadata[] {
150
- const fromsMixed: string | string[] = createRedirects
151
- ? createRedirects(path) || []
152
- : [];
148
+ ): RedirectItem[] {
149
+ function createPathRedirects(path: string): RedirectItem[] {
150
+ const fromsMixed: string | string[] = createRedirects?.(path) ?? [];
153
151
 
154
152
  const froms: string[] =
155
153
  typeof fromsMixed === 'string' ? [fromsMixed] : fromsMixed;
156
154
 
157
- return froms.map((from) => {
158
- return {
159
- from,
160
- to: path,
161
- };
162
- });
155
+ return froms.map((from) => ({from, to: path}));
163
156
  }
164
157
 
165
- return flatten(paths.map(createPathRedirects));
158
+ return paths.flatMap(createPathRedirects);
166
159
  }
@@ -5,26 +5,24 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import _ from 'lodash';
8
9
  import * as eta from 'eta';
9
10
  import redirectPageTemplate from './templates/redirectPage.template.html';
10
- import {memoize} from 'lodash';
11
11
 
12
- type CreateRedirectPageOptions = {
13
- toUrl: string;
14
- };
15
-
16
- const getCompiledRedirectPageTemplate = memoize(() => {
17
- return eta.compile(redirectPageTemplate.trim());
18
- });
12
+ const getCompiledRedirectPageTemplate = _.memoize(() =>
13
+ eta.compile(redirectPageTemplate.trim()),
14
+ );
19
15
 
20
- function renderRedirectPageTemplate(data: Record<string, unknown>) {
16
+ function renderRedirectPageTemplate(data: {toUrl: string}) {
21
17
  const compiled = getCompiledRedirectPageTemplate();
22
18
  return compiled(data, eta.defaultConfig);
23
19
  }
24
20
 
25
21
  export default function createRedirectPageContent({
26
22
  toUrl,
27
- }: CreateRedirectPageOptions): string {
23
+ }: {
24
+ toUrl: string;
25
+ }): string {
28
26
  return renderRedirectPageTemplate({
29
27
  toUrl: encodeURI(toUrl),
30
28
  });
package/src/deps.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ // TODO incompatible declaration file
9
+ declare module 'eta' {
10
+ export const defaultConfig: object;
11
+
12
+ export function compile(
13
+ template: string,
14
+ options?: object,
15
+ ): (data: object, config: object) => string;
16
+ }
@@ -5,69 +5,72 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {flatten} from 'lodash';
9
- import {removeSuffix} from '@docusaurus/utils';
10
- import {RedirectMetadata} from './types';
8
+ import {
9
+ addTrailingSlash,
10
+ removeSuffix,
11
+ removeTrailingSlash,
12
+ } from '@docusaurus/utils';
13
+ import type {RedirectItem} from './types';
11
14
 
12
15
  const ExtensionAdditionalMessage =
13
- "If the redirect extension system is not good enough for your usecase, you can create redirects yourself with the 'createRedirects' plugin option.";
16
+ 'If the redirect extension system is not good enough for your use case, you can create redirects yourself with the "createRedirects" plugin option.';
14
17
 
15
18
  const validateExtension = (ext: string) => {
16
19
  if (!ext) {
17
20
  throw new Error(
18
- `Extension=['${String(
19
- ext,
20
- )}'] is not allowed. ${ExtensionAdditionalMessage}`,
21
+ `Extension "${ext}" is not allowed.\n${ExtensionAdditionalMessage}`,
21
22
  );
22
23
  }
23
24
  if (ext.includes('.')) {
24
25
  throw new Error(
25
- `Extension=['${ext}'] contains a . (dot) and is not allowed. ${ExtensionAdditionalMessage}`,
26
+ `Extension "${ext}" contains a "." (dot) which is not allowed.\n${ExtensionAdditionalMessage}`,
26
27
  );
27
28
  }
28
29
  if (ext.includes('/')) {
29
30
  throw new Error(
30
- `Extension=['${ext}'] contains a / and is not allowed. ${ExtensionAdditionalMessage}`,
31
+ `Extension "${ext}" contains a "/" (slash) which is not allowed.\n${ExtensionAdditionalMessage}`,
31
32
  );
32
33
  }
33
34
  if (encodeURIComponent(ext) !== ext) {
34
35
  throw new Error(
35
- `Extension=['${ext}'] contains invalid uri characters. ${ExtensionAdditionalMessage}`,
36
+ `Extension "${ext}" contains invalid URI characters.\n${ExtensionAdditionalMessage}`,
36
37
  );
37
38
  }
38
39
  };
39
40
 
40
41
  const addLeadingDot = (extension: string) => `.${extension}`;
41
42
 
42
- // Create new /path that redirects to existing an /path.html
43
+ /**
44
+ * Create new `/path` that redirects to existing an `/path.html`
45
+ */
43
46
  export function createToExtensionsRedirects(
44
47
  paths: string[],
45
48
  extensions: string[],
46
- ): RedirectMetadata[] {
49
+ ): RedirectItem[] {
47
50
  extensions.forEach(validateExtension);
48
51
 
49
52
  const dottedExtensions = extensions.map(addLeadingDot);
50
53
 
51
- const createPathRedirects = (path: string): RedirectMetadata[] => {
54
+ const createPathRedirects = (path: string): RedirectItem[] => {
52
55
  const extensionFound = dottedExtensions.find((ext) => path.endsWith(ext));
53
56
  if (extensionFound) {
54
- const routePathWithoutExtension = removeSuffix(path, extensionFound);
55
- return [routePathWithoutExtension].map((from) => ({
56
- from,
57
- to: path,
58
- }));
57
+ return [{from: removeSuffix(path, extensionFound), to: path}];
59
58
  }
60
59
  return [];
61
60
  };
62
61
 
63
- return flatten(paths.map(createPathRedirects));
62
+ return paths.flatMap(createPathRedirects);
64
63
  }
65
64
 
66
- // Create new /path.html that redirects to existing an /path
65
+ /**
66
+ * Create new `/path.html/index.html` that redirects to existing an `/path`
67
+ * The filename pattern might look weird but it's on purpose (see
68
+ * https://github.com/facebook/docusaurus/issues/5055)
69
+ */
67
70
  export function createFromExtensionsRedirects(
68
71
  paths: string[],
69
72
  extensions: string[],
70
- ): RedirectMetadata[] {
73
+ ): RedirectItem[] {
71
74
  extensions.forEach(validateExtension);
72
75
 
73
76
  const dottedExtensions = extensions.map(addLeadingDot);
@@ -75,15 +78,19 @@ export function createFromExtensionsRedirects(
75
78
  const alreadyEndsWithAnExtension = (str: string) =>
76
79
  dottedExtensions.some((ext) => str.endsWith(ext));
77
80
 
78
- const createPathRedirects = (path: string): RedirectMetadata[] => {
79
- if (path === '' || path.endsWith('/') || alreadyEndsWithAnExtension(path)) {
81
+ const createPathRedirects = (path: string): RedirectItem[] => {
82
+ if (path === '' || path === '/' || alreadyEndsWithAnExtension(path)) {
80
83
  return [];
81
84
  }
82
85
  return extensions.map((ext) => ({
83
- from: `${path}.${ext}`,
86
+ // /path => /path.html
87
+ // /path/ => /path.html/
88
+ from: path.endsWith('/')
89
+ ? addTrailingSlash(`${removeTrailingSlash(path)}.${ext}`)
90
+ : `${path}.${ext}`,
84
91
  to: path,
85
92
  }));
86
93
  };
87
94
 
88
- return flatten(paths.map(createPathRedirects));
95
+ return paths.flatMap(createPathRedirects);
89
96
  }
package/src/index.ts CHANGED
@@ -5,26 +5,25 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {LoadContext, Plugin, Props} from '@docusaurus/types';
9
- import {UserPluginOptions, PluginContext, RedirectMetadata} from './types';
10
-
11
- import normalizePluginOptions from './normalizePluginOptions';
8
+ import {removePrefix, addLeadingSlash} from '@docusaurus/utils';
12
9
  import collectRedirects from './collectRedirects';
13
10
  import writeRedirectFiles, {
14
- toRedirectFilesMetadata,
15
- RedirectFileMetadata,
11
+ toRedirectFiles,
12
+ type RedirectFile,
16
13
  } from './writeRedirectFiles';
17
- import {removePrefix, addLeadingSlash} from '@docusaurus/utils';
14
+ import type {LoadContext, Plugin} from '@docusaurus/types';
15
+ import type {PluginContext, RedirectItem} from './types';
16
+ import type {PluginOptions, Options} from './options';
18
17
 
19
18
  export default function pluginClientRedirectsPages(
20
- _context: LoadContext,
21
- opts: UserPluginOptions,
22
- ): Plugin<unknown> {
23
- const options = normalizePluginOptions(opts);
19
+ context: LoadContext,
20
+ options: PluginOptions,
21
+ ): Plugin<void> {
22
+ const {trailingSlash} = context.siteConfig;
24
23
 
25
24
  return {
26
25
  name: 'docusaurus-plugin-client-redirects',
27
- async postBuild(props: Props) {
26
+ async postBuild(props) {
28
27
  const pluginContext: PluginContext = {
29
28
  relativeRoutesPaths: props.routesPaths.map(
30
29
  (path) => `${addLeadingSlash(removePrefix(path, props.baseUrl))}`,
@@ -32,13 +31,18 @@ export default function pluginClientRedirectsPages(
32
31
  baseUrl: props.baseUrl,
33
32
  outDir: props.outDir,
34
33
  options,
34
+ siteConfig: props.siteConfig,
35
35
  };
36
36
 
37
- const redirects: RedirectMetadata[] = collectRedirects(pluginContext);
37
+ const redirects: RedirectItem[] = collectRedirects(
38
+ pluginContext,
39
+ trailingSlash,
40
+ );
38
41
 
39
- const redirectFiles: RedirectFileMetadata[] = toRedirectFilesMetadata(
42
+ const redirectFiles: RedirectFile[] = toRedirectFiles(
40
43
  redirects,
41
44
  pluginContext,
45
+ trailingSlash,
42
46
  );
43
47
 
44
48
  // Write files only at the end: make code more easy to test without IO
@@ -46,3 +50,6 @@ export default function pluginClientRedirectsPages(
46
50
  },
47
51
  };
48
52
  }
53
+
54
+ export {validateOptions} from './options';
55
+ export type {PluginOptions, Options};
package/src/options.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {Joi, PathnameSchema} from '@docusaurus/utils-validation';
9
+ import type {OptionValidationContext} from '@docusaurus/types';
10
+
11
+ export type RedirectOption = {
12
+ /** Pathname of an existing Docusaurus page */
13
+ to: string;
14
+ /** Pathname of the new page(s) we should create */
15
+ from: string | string[];
16
+ };
17
+
18
+ export type PluginOptions = {
19
+ /** Plugin ID. */
20
+ id: string;
21
+ /** The extensions to be removed from the route after redirecting. */
22
+ fromExtensions: string[];
23
+ /** The extensions to be appended to the route after redirecting. */
24
+ toExtensions: string[];
25
+ /** The list of redirect rules, each one with multiple `from`s → one `to`. */
26
+ redirects: RedirectOption[];
27
+ /**
28
+ * A callback to create a redirect rule. Docusaurus query this callback
29
+ * against every path it has created, and use its return value to output more
30
+ * paths.
31
+ * @returns All the paths from which we should redirect to `path`
32
+ */
33
+ createRedirects?: (
34
+ /** An existing Docusaurus route path */
35
+ path: string,
36
+ ) => string[] | string | null | undefined;
37
+ };
38
+
39
+ export type Options = Partial<PluginOptions>;
40
+
41
+ export const DEFAULT_OPTIONS: Partial<PluginOptions> = {
42
+ fromExtensions: [],
43
+ toExtensions: [],
44
+ redirects: [],
45
+ };
46
+
47
+ const RedirectPluginOptionValidation = Joi.object<RedirectOption>({
48
+ to: PathnameSchema.required(),
49
+ from: Joi.alternatives().try(
50
+ PathnameSchema.required(),
51
+ Joi.array().items(PathnameSchema.required()),
52
+ ),
53
+ });
54
+
55
+ const isString = Joi.string().required().not(null);
56
+
57
+ const UserOptionsSchema = Joi.object<PluginOptions>({
58
+ fromExtensions: Joi.array()
59
+ .items(isString)
60
+ .default(DEFAULT_OPTIONS.fromExtensions),
61
+ toExtensions: Joi.array()
62
+ .items(isString)
63
+ .default(DEFAULT_OPTIONS.toExtensions),
64
+ redirects: Joi.array()
65
+ .items(RedirectPluginOptionValidation)
66
+ .default(DEFAULT_OPTIONS.redirects),
67
+ createRedirects: Joi.function().maxArity(1),
68
+ }).default(DEFAULT_OPTIONS);
69
+
70
+ export function validateOptions({
71
+ validate,
72
+ options: userOptions,
73
+ }: OptionValidationContext<Options | undefined, PluginOptions>): PluginOptions {
74
+ return validate(UserOptionsSchema, userOptions);
75
+ }
@@ -5,15 +5,15 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {RedirectMetadata} from './types';
9
8
  import {Joi, PathnameSchema} from '@docusaurus/utils-validation';
9
+ import type {RedirectItem} from './types';
10
10
 
11
- const RedirectSchema = Joi.object<RedirectMetadata>({
11
+ const RedirectSchema = Joi.object<RedirectItem>({
12
12
  from: PathnameSchema.required(),
13
13
  to: PathnameSchema.required(),
14
14
  });
15
15
 
16
- export function validateRedirect(redirect: RedirectMetadata): void {
16
+ export function validateRedirect(redirect: RedirectItem): void {
17
17
  const {error} = RedirectSchema.validate(redirect, {
18
18
  abortEarly: true,
19
19
  convert: false,
package/src/types.ts CHANGED
@@ -5,39 +5,25 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import {Props} from '@docusaurus/types';
8
+ import type {Props} from '@docusaurus/types';
9
+ import type {PluginOptions} from './options';
9
10
 
10
- export type PluginOptions = {
11
- id: string;
12
- fromExtensions: string[];
13
- toExtensions: string[];
14
- redirects: RedirectOption[];
15
- createRedirects?: CreateRedirectsFnOption;
16
- };
17
-
18
- // For a given existing route path,
19
- // return all the paths from which we should redirect from
20
- export type CreateRedirectsFnOption = (
21
- path: string,
22
- ) => string[] | string | null | undefined;
23
-
24
- export type RedirectOption = {
25
- to: string;
26
- from: string | string[];
27
- };
28
-
29
- export type UserPluginOptions = Partial<PluginOptions>;
30
-
31
- // The minimal infos the plugin needs to work
32
- export type PluginContext = Pick<Props, 'outDir' | 'baseUrl'> & {
11
+ /**
12
+ * The minimal infos the plugin needs to work
13
+ */
14
+ export type PluginContext = Pick<Props, 'outDir' | 'baseUrl' | 'siteConfig'> & {
33
15
  options: PluginOptions;
34
16
  relativeRoutesPaths: string[];
35
17
  };
36
18
 
37
- // In-memory representation of redirects we want: easier to test
38
- // /!\ easy to be confused: "from" is the new page we should create,
39
- // that redirects to "to": the existing Docusaurus page
40
- export type RedirectMetadata = {
41
- from: string; // pathname
42
- to: string; // pathname
19
+ /**
20
+ * In-memory representation of redirects we want: easier to test
21
+ * /!\ easy to be confused: "from" is the new page we should create,
22
+ * that redirects to "to": the existing Docusaurus page
23
+ */
24
+ export type RedirectItem = {
25
+ /** Pathname of the new page we should create */
26
+ from: string;
27
+ /** Pathname of an existing Docusaurus page */
28
+ to: string;
43
29
  };