@docusaurus/plugin-sitemap 3.2.1 → 3.3.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.
@@ -4,12 +4,10 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import type { DocusaurusConfig, RouteConfig } from '@docusaurus/types';
8
- import type { HelmetServerState } from 'react-helmet-async';
7
+ import type { CreateSitemapItemsParams } from './types';
9
8
  import type { PluginOptions } from './options';
10
- type CreateSitemapParams = {
11
- siteConfig: DocusaurusConfig;
12
- routes: RouteConfig[];
9
+ import type { HelmetServerState } from 'react-helmet-async';
10
+ type CreateSitemapParams = CreateSitemapItemsParams & {
13
11
  head: {
14
12
  [location: string]: HelmetServerState;
15
13
  };
@@ -9,23 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const utils_1 = require("@docusaurus/utils");
10
10
  const xml_1 = require("./xml");
11
11
  const createSitemapItem_1 = require("./createSitemapItem");
12
- // Maybe we want to add a routeConfig.metadata.noIndex instead?
13
- // But using Helmet is more reliable for third-party plugins...
14
- function isNoIndexMetaRoute({ head, route, }) {
15
- const isNoIndexMetaTag = ({ name, content, }) => {
16
- if (!name || !content) {
17
- return false;
18
- }
19
- return (
20
- // meta name is not case-sensitive
21
- name.toLowerCase() === 'robots' &&
22
- // Robots directives are not case-sensitive
23
- content.toLowerCase().includes('noindex'));
24
- };
25
- // https://github.com/staylor/react-helmet-async/pull/167
26
- const meta = head[route]?.meta.toComponent();
27
- return meta?.some((tag) => isNoIndexMetaTag({ name: tag.props.name, content: tag.props.content }));
28
- }
12
+ const head_1 = require("./head");
29
13
  // Not all routes should appear in the sitemap, and we should filter:
30
14
  // - parent routes, used for layouts
31
15
  // - routes matching options.ignorePatterns
@@ -34,27 +18,42 @@ function getSitemapRoutes({ routes, head, options }) {
34
18
  const { ignorePatterns } = options;
35
19
  const ignoreMatcher = (0, utils_1.createMatcher)(ignorePatterns);
36
20
  function isRouteExcluded(route) {
37
- return (ignoreMatcher(route.path) || isNoIndexMetaRoute({ head, route: route.path }));
21
+ return (ignoreMatcher(route.path) || (0, head_1.isNoIndexMetaRoute)({ head, route: route.path }));
38
22
  }
39
23
  return (0, utils_1.flattenRoutes)(routes).filter((route) => !isRouteExcluded(route));
40
24
  }
41
- async function createSitemapItems(params) {
42
- const sitemapRoutes = getSitemapRoutes(params);
43
- if (sitemapRoutes.length === 0) {
44
- return [];
45
- }
46
- return Promise.all(sitemapRoutes.map((route) => (0, createSitemapItem_1.createSitemapItem)({
47
- route,
48
- siteConfig: params.siteConfig,
49
- options: params.options,
50
- })));
25
+ // Our default implementation receives some additional parameters on purpose
26
+ // Params such as "head" are "messy" and not directly exposed to the user
27
+ function createDefaultCreateSitemapItems(internalParams) {
28
+ return async (params) => {
29
+ const sitemapRoutes = getSitemapRoutes({ ...params, ...internalParams });
30
+ if (sitemapRoutes.length === 0) {
31
+ return [];
32
+ }
33
+ return Promise.all(sitemapRoutes.map((route) => (0, createSitemapItem_1.createSitemapItem)({
34
+ route,
35
+ siteConfig: params.siteConfig,
36
+ options: internalParams.options,
37
+ })));
38
+ };
51
39
  }
52
40
  async function createSitemap(params) {
53
- const items = await createSitemapItems(params);
54
- if (items.length === 0) {
41
+ const { head, options, routes, siteConfig } = params;
42
+ const defaultCreateSitemapItems = createDefaultCreateSitemapItems({ head, options });
43
+ const sitemapItems = params.options.createSitemapItems
44
+ ? await params.options.createSitemapItems({
45
+ routes,
46
+ siteConfig,
47
+ defaultCreateSitemapItems,
48
+ })
49
+ : await defaultCreateSitemapItems({
50
+ routes,
51
+ siteConfig,
52
+ });
53
+ if (sitemapItems.length === 0) {
55
54
  return null;
56
55
  }
57
- const xmlString = await (0, xml_1.sitemapItemsToXmlString)(items, {
56
+ const xmlString = await (0, xml_1.sitemapItemsToXmlString)(sitemapItems, {
58
57
  lastmod: params.options.lastmod,
59
58
  });
60
59
  return xmlString;
package/lib/head.d.ts ADDED
@@ -0,0 +1,13 @@
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
+ import type { HelmetServerState } from 'react-helmet-async';
8
+ export declare function isNoIndexMetaRoute({ head, route, }: {
9
+ head: {
10
+ [location: string]: HelmetServerState;
11
+ };
12
+ route: string;
13
+ }): boolean;
package/lib/head.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isNoIndexMetaRoute = void 0;
10
+ // Maybe we want to add a routeConfig.metadata.noIndex instead?
11
+ // But using Helmet is more reliable for third-party plugins...
12
+ function isNoIndexMetaRoute({ head, route, }) {
13
+ const isNoIndexMetaTag = ({ name, content, }) => {
14
+ if (!name || !content) {
15
+ return false;
16
+ }
17
+ return (
18
+ // meta name is not case-sensitive
19
+ name.toLowerCase() === 'robots' &&
20
+ // Robots directives are not case-sensitive
21
+ content.toLowerCase().includes('noindex'));
22
+ };
23
+ // https://github.com/staylor/react-helmet-async/pull/167
24
+ const meta = head[route]?.meta.toComponent();
25
+ return (meta?.some((tag) => isNoIndexMetaTag({ name: tag.props.name, content: tag.props.content })) ?? false);
26
+ }
27
+ exports.isNoIndexMetaRoute = isNoIndexMetaRoute;
package/lib/options.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { OptionValidationContext } from '@docusaurus/types';
8
- import type { ChangeFreq, LastModOption } from './types';
8
+ import type { ChangeFreq, LastModOption, SitemapItem, CreateSitemapItemsFn, CreateSitemapItemsParams } from './types';
9
9
  export type PluginOptions = {
10
10
  /**
11
11
  * The path to the created sitemap file, relative to the output directory.
@@ -36,7 +36,13 @@ export type PluginOptions = {
36
36
  * @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions
37
37
  */
38
38
  priority: number | null;
39
+ /** Allow control over the construction of SitemapItems */
40
+ createSitemapItems?: CreateSitemapItemsOption;
39
41
  };
42
+ type CreateSitemapItemsOption = (params: CreateSitemapItemsParams & {
43
+ defaultCreateSitemapItems: CreateSitemapItemsFn;
44
+ }) => Promise<SitemapItem[]>;
40
45
  export type Options = Partial<PluginOptions>;
41
46
  export declare const DEFAULT_OPTIONS: PluginOptions;
42
47
  export declare function validateOptions({ validate, options, }: OptionValidationContext<Options, PluginOptions>): PluginOptions;
48
+ export {};
package/lib/options.js CHANGED
@@ -43,6 +43,7 @@ const PluginOptionSchema = utils_validation_1.Joi.object({
43
43
  lastmod: utils_validation_1.Joi.string()
44
44
  .valid(null, ...types_1.LastModOptionList)
45
45
  .default(exports.DEFAULT_OPTIONS.lastmod),
46
+ createSitemapItems: utils_validation_1.Joi.function(),
46
47
  ignorePatterns: utils_validation_1.Joi.array()
47
48
  .items(utils_validation_1.Joi.string())
48
49
  .default(exports.DEFAULT_OPTIONS.ignorePatterns),
package/lib/types.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
+ import type { DocusaurusConfig, RouteConfig } from '@docusaurus/types';
7
8
  export declare const LastModOptionList: readonly ["date", "datetime"];
8
9
  export type LastModOption = (typeof LastModOptionList)[number];
9
10
  export declare const ChangeFreqList: readonly ["hourly", "daily", "weekly", "monthly", "yearly", "always", "never"];
@@ -45,3 +46,8 @@ export type SitemapItem = {
45
46
  */
46
47
  priority?: number | null;
47
48
  };
49
+ export type CreateSitemapItemsParams = {
50
+ siteConfig: DocusaurusConfig;
51
+ routes: RouteConfig[];
52
+ };
53
+ export type CreateSitemapItemsFn = (params: CreateSitemapItemsParams) => Promise<SitemapItem[]>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-sitemap",
3
- "version": "3.2.1",
3
+ "version": "3.3.1",
4
4
  "description": "Simple sitemap generation plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -18,12 +18,12 @@
18
18
  },
19
19
  "license": "MIT",
20
20
  "dependencies": {
21
- "@docusaurus/core": "3.2.1",
22
- "@docusaurus/logger": "3.2.1",
23
- "@docusaurus/types": "3.2.1",
24
- "@docusaurus/utils": "3.2.1",
25
- "@docusaurus/utils-common": "3.2.1",
26
- "@docusaurus/utils-validation": "3.2.1",
21
+ "@docusaurus/core": "3.3.1",
22
+ "@docusaurus/logger": "3.3.0",
23
+ "@docusaurus/types": "3.3.0",
24
+ "@docusaurus/utils": "3.3.0",
25
+ "@docusaurus/utils-common": "3.3.0",
26
+ "@docusaurus/utils-validation": "3.3.0",
27
27
  "fs-extra": "^11.1.1",
28
28
  "sitemap": "^7.1.1",
29
29
  "tslib": "^2.6.0"
@@ -38,5 +38,5 @@
38
38
  "engines": {
39
39
  "node": ">=18.0"
40
40
  },
41
- "gitHead": "f268e15264e208e6faf26117258162e988b53773"
41
+ "gitHead": "f3524cf332e803ff82783ee9c1c86c4342fc1f71"
42
42
  }
@@ -5,57 +5,14 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import type {ReactElement} from 'react';
9
8
  import {createMatcher, flattenRoutes} from '@docusaurus/utils';
10
9
  import {sitemapItemsToXmlString} from './xml';
11
10
  import {createSitemapItem} from './createSitemapItem';
12
- import type {SitemapItem} from './types';
13
- import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
14
- import type {HelmetServerState} from 'react-helmet-async';
11
+ import {isNoIndexMetaRoute} from './head';
12
+ import type {CreateSitemapItemsFn, CreateSitemapItemsParams} from './types';
13
+ import type {RouteConfig} from '@docusaurus/types';
15
14
  import type {PluginOptions} from './options';
16
-
17
- type CreateSitemapParams = {
18
- siteConfig: DocusaurusConfig;
19
- routes: RouteConfig[];
20
- head: {[location: string]: HelmetServerState};
21
- options: PluginOptions;
22
- };
23
-
24
- // Maybe we want to add a routeConfig.metadata.noIndex instead?
25
- // But using Helmet is more reliable for third-party plugins...
26
- function isNoIndexMetaRoute({
27
- head,
28
- route,
29
- }: {
30
- head: {[location: string]: HelmetServerState};
31
- route: string;
32
- }) {
33
- const isNoIndexMetaTag = ({
34
- name,
35
- content,
36
- }: {
37
- name?: string;
38
- content?: string;
39
- }): boolean => {
40
- if (!name || !content) {
41
- return false;
42
- }
43
- return (
44
- // meta name is not case-sensitive
45
- name.toLowerCase() === 'robots' &&
46
- // Robots directives are not case-sensitive
47
- content.toLowerCase().includes('noindex')
48
- );
49
- };
50
-
51
- // https://github.com/staylor/react-helmet-async/pull/167
52
- const meta = head[route]?.meta.toComponent() as unknown as
53
- | ReactElement<{name?: string; content?: string}>[]
54
- | undefined;
55
- return meta?.some((tag) =>
56
- isNoIndexMetaTag({name: tag.props.name, content: tag.props.content}),
57
- );
58
- }
15
+ import type {HelmetServerState} from 'react-helmet-async';
59
16
 
60
17
  // Not all routes should appear in the sitemap, and we should filter:
61
18
  // - parent routes, used for layouts
@@ -75,32 +32,57 @@ function getSitemapRoutes({routes, head, options}: CreateSitemapParams) {
75
32
  return flattenRoutes(routes).filter((route) => !isRouteExcluded(route));
76
33
  }
77
34
 
78
- async function createSitemapItems(
79
- params: CreateSitemapParams,
80
- ): Promise<SitemapItem[]> {
81
- const sitemapRoutes = getSitemapRoutes(params);
82
- if (sitemapRoutes.length === 0) {
83
- return [];
84
- }
85
- return Promise.all(
86
- sitemapRoutes.map((route) =>
87
- createSitemapItem({
88
- route,
89
- siteConfig: params.siteConfig,
90
- options: params.options,
91
- }),
92
- ),
93
- );
35
+ // Our default implementation receives some additional parameters on purpose
36
+ // Params such as "head" are "messy" and not directly exposed to the user
37
+ function createDefaultCreateSitemapItems(
38
+ internalParams: Pick<CreateSitemapParams, 'head' | 'options'>,
39
+ ): CreateSitemapItemsFn {
40
+ return async (params) => {
41
+ const sitemapRoutes = getSitemapRoutes({...params, ...internalParams});
42
+ if (sitemapRoutes.length === 0) {
43
+ return [];
44
+ }
45
+ return Promise.all(
46
+ sitemapRoutes.map((route) =>
47
+ createSitemapItem({
48
+ route,
49
+ siteConfig: params.siteConfig,
50
+ options: internalParams.options,
51
+ }),
52
+ ),
53
+ );
54
+ };
94
55
  }
95
56
 
57
+ type CreateSitemapParams = CreateSitemapItemsParams & {
58
+ head: {[location: string]: HelmetServerState};
59
+ options: PluginOptions;
60
+ };
61
+
96
62
  export default async function createSitemap(
97
63
  params: CreateSitemapParams,
98
64
  ): Promise<string | null> {
99
- const items = await createSitemapItems(params);
100
- if (items.length === 0) {
65
+ const {head, options, routes, siteConfig} = params;
66
+
67
+ const defaultCreateSitemapItems: CreateSitemapItemsFn =
68
+ createDefaultCreateSitemapItems({head, options});
69
+
70
+ const sitemapItems = params.options.createSitemapItems
71
+ ? await params.options.createSitemapItems({
72
+ routes,
73
+ siteConfig,
74
+ defaultCreateSitemapItems,
75
+ })
76
+ : await defaultCreateSitemapItems({
77
+ routes,
78
+ siteConfig,
79
+ });
80
+
81
+ if (sitemapItems.length === 0) {
101
82
  return null;
102
83
  }
103
- const xmlString = await sitemapItemsToXmlString(items, {
84
+
85
+ const xmlString = await sitemapItemsToXmlString(sitemapItems, {
104
86
  lastmod: params.options.lastmod,
105
87
  });
106
88
  return xmlString;
package/src/head.ts ADDED
@@ -0,0 +1,47 @@
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 type {ReactElement} from 'react';
9
+ import type {HelmetServerState} from 'react-helmet-async';
10
+
11
+ // Maybe we want to add a routeConfig.metadata.noIndex instead?
12
+ // But using Helmet is more reliable for third-party plugins...
13
+ export function isNoIndexMetaRoute({
14
+ head,
15
+ route,
16
+ }: {
17
+ head: {[location: string]: HelmetServerState};
18
+ route: string;
19
+ }): boolean {
20
+ const isNoIndexMetaTag = ({
21
+ name,
22
+ content,
23
+ }: {
24
+ name?: string;
25
+ content?: string;
26
+ }): boolean => {
27
+ if (!name || !content) {
28
+ return false;
29
+ }
30
+ return (
31
+ // meta name is not case-sensitive
32
+ name.toLowerCase() === 'robots' &&
33
+ // Robots directives are not case-sensitive
34
+ content.toLowerCase().includes('noindex')
35
+ );
36
+ };
37
+
38
+ // https://github.com/staylor/react-helmet-async/pull/167
39
+ const meta = head[route]?.meta.toComponent() as unknown as
40
+ | ReactElement<{name?: string; content?: string}>[]
41
+ | undefined;
42
+ return (
43
+ meta?.some((tag) =>
44
+ isNoIndexMetaTag({name: tag.props.name, content: tag.props.content}),
45
+ ) ?? false
46
+ );
47
+ }
package/src/options.ts CHANGED
@@ -8,7 +8,13 @@
8
8
  import {Joi} from '@docusaurus/utils-validation';
9
9
  import {ChangeFreqList, LastModOptionList} from './types';
10
10
  import type {OptionValidationContext} from '@docusaurus/types';
11
- import type {ChangeFreq, LastModOption} from './types';
11
+ import type {
12
+ ChangeFreq,
13
+ LastModOption,
14
+ SitemapItem,
15
+ CreateSitemapItemsFn,
16
+ CreateSitemapItemsParams,
17
+ } from './types';
12
18
 
13
19
  export type PluginOptions = {
14
20
  /**
@@ -44,8 +50,17 @@ export type PluginOptions = {
44
50
  * @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions
45
51
  */
46
52
  priority: number | null;
53
+
54
+ /** Allow control over the construction of SitemapItems */
55
+ createSitemapItems?: CreateSitemapItemsOption;
47
56
  };
48
57
 
58
+ type CreateSitemapItemsOption = (
59
+ params: CreateSitemapItemsParams & {
60
+ defaultCreateSitemapItems: CreateSitemapItemsFn;
61
+ },
62
+ ) => Promise<SitemapItem[]>;
63
+
49
64
  export type Options = Partial<PluginOptions>;
50
65
 
51
66
  export const DEFAULT_OPTIONS: PluginOptions = {
@@ -90,6 +105,8 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
90
105
  .valid(null, ...LastModOptionList)
91
106
  .default(DEFAULT_OPTIONS.lastmod),
92
107
 
108
+ createSitemapItems: Joi.function(),
109
+
93
110
  ignorePatterns: Joi.array()
94
111
  .items(Joi.string())
95
112
  .default(DEFAULT_OPTIONS.ignorePatterns),
package/src/types.ts CHANGED
@@ -5,6 +5,8 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import type {DocusaurusConfig, RouteConfig} from '@docusaurus/types';
9
+
8
10
  export const LastModOptionList = ['date', 'datetime'] as const;
9
11
 
10
12
  export type LastModOption = (typeof LastModOptionList)[number];
@@ -65,3 +67,12 @@ export type SitemapItem = {
65
67
  */
66
68
  priority?: number | null;
67
69
  };
70
+
71
+ export type CreateSitemapItemsParams = {
72
+ siteConfig: DocusaurusConfig;
73
+ routes: RouteConfig[];
74
+ };
75
+
76
+ export type CreateSitemapItemsFn = (
77
+ params: CreateSitemapItemsParams,
78
+ ) => Promise<SitemapItem[]>;