@docusaurus/theme-search-algolia 0.0.0-5419 → 0.0.0-5426

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,4 +4,6 @@
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
+ export { useAlgoliaThemeConfig } from './useAlgoliaThemeConfig';
7
8
  export { useAlgoliaContextualFacetFilters } from './useAlgoliaContextualFacetFilters';
9
+ export { useSearchResultUrlProcessor } from './useSearchResultUrlProcessor';
@@ -4,4 +4,6 @@
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
+ export { useAlgoliaThemeConfig } from './useAlgoliaThemeConfig';
7
8
  export { useAlgoliaContextualFacetFilters } from './useAlgoliaContextualFacetFilters';
9
+ export { useSearchResultUrlProcessor } from './useSearchResultUrlProcessor';
@@ -0,0 +1,2 @@
1
+ import type { ThemeConfig } from '@docusaurus/theme-search-algolia';
2
+ export declare function useAlgoliaThemeConfig(): ThemeConfig;
@@ -0,0 +1,11 @@
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 useDocusaurusContext from '@docusaurus/useDocusaurusContext';
8
+ export function useAlgoliaThemeConfig() {
9
+ const { siteConfig: { themeConfig }, } = useDocusaurusContext();
10
+ return themeConfig;
11
+ }
@@ -0,0 +1,11 @@
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
+ * Process the search result url from Algolia to its final form, ready to be
9
+ * navigated to or used as a link
10
+ */
11
+ export declare function useSearchResultUrlProcessor(): (url: string) => string;
@@ -0,0 +1,33 @@
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 { useCallback } from 'react';
8
+ import { isRegexpStringMatch } from '@docusaurus/theme-common';
9
+ import { useBaseUrlUtils } from '@docusaurus/useBaseUrl';
10
+ import { useAlgoliaThemeConfig } from './useAlgoliaThemeConfig';
11
+ function replacePathname(pathname, replaceSearchResultPathname) {
12
+ return replaceSearchResultPathname
13
+ ? pathname.replaceAll(new RegExp(replaceSearchResultPathname.from, 'g'), replaceSearchResultPathname.to)
14
+ : pathname;
15
+ }
16
+ /**
17
+ * Process the search result url from Algolia to its final form, ready to be
18
+ * navigated to or used as a link
19
+ */
20
+ export function useSearchResultUrlProcessor() {
21
+ const { withBaseUrl } = useBaseUrlUtils();
22
+ const { algolia: { externalUrlRegex, replaceSearchResultPathname }, } = useAlgoliaThemeConfig();
23
+ return useCallback((url) => {
24
+ const parsedURL = new URL(url);
25
+ // Algolia contains an external domain => navigate to URL
26
+ if (isRegexpStringMatch(externalUrlRegex, parsedURL.href)) {
27
+ return url;
28
+ }
29
+ // Otherwise => transform to relative URL for SPA navigation
30
+ const relativeUrl = `${parsedURL.pathname + parsedURL.hash}`;
31
+ return withBaseUrl(replacePathname(relativeUrl, replaceSearchResultPathname));
32
+ }, [withBaseUrl, externalUrlRegex, replaceSearchResultPathname]);
33
+ }
@@ -4,18 +4,20 @@
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 React, {useState, useRef, useCallback, useMemo} from 'react';
8
- import {createPortal} from 'react-dom';
9
- import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
10
- import {useHistory} from '@docusaurus/router';
11
- import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
12
- import Link from '@docusaurus/Link';
7
+ import React, {useCallback, useMemo, useRef, useState} from 'react';
8
+ import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
13
9
  import Head from '@docusaurus/Head';
10
+ import Link from '@docusaurus/Link';
11
+ import {useHistory} from '@docusaurus/router';
14
12
  import {isRegexpStringMatch} from '@docusaurus/theme-common';
15
13
  import {useSearchPage} from '@docusaurus/theme-common/internal';
16
- import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
17
- import {useAlgoliaContextualFacetFilters} from '@docusaurus/theme-search-algolia/client';
14
+ import {
15
+ useAlgoliaContextualFacetFilters,
16
+ useSearchResultUrlProcessor,
17
+ } from '@docusaurus/theme-search-algolia/client';
18
18
  import Translate from '@docusaurus/Translate';
19
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
20
+ import {createPortal} from 'react-dom';
19
21
  import translations from '@theme/SearchTranslations';
20
22
  let DocSearchModal = null;
21
23
  function Hit({hit, children}) {
@@ -39,6 +41,7 @@ function mergeFacetFilters(f1, f2) {
39
41
  }
40
42
  function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
41
43
  const {siteMetadata} = useDocusaurusContext();
44
+ const processSearchResultUrl = useSearchResultUrlProcessor();
42
45
  const contextualSearchFacetFilters = useAlgoliaContextualFacetFilters();
43
46
  const configFacetFilters = props.searchParameters?.facetFilters ?? [];
44
47
  const facetFilters = contextualSearch
@@ -51,7 +54,6 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
51
54
  ...props.searchParameters,
52
55
  facetFilters,
53
56
  };
54
- const {withBaseUrl} = useBaseUrlUtils();
55
57
  const history = useHistory();
56
58
  const searchContainer = useRef(null);
57
59
  const searchButtonRef = useRef(null);
@@ -104,19 +106,10 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
104
106
  },
105
107
  }).current;
106
108
  const transformItems = useRef((items) =>
107
- items.map((item) => {
108
- // If Algolia contains a external domain, we should navigate without
109
- // relative URL
110
- if (isRegexpStringMatch(externalUrlRegex, item.url)) {
111
- return item;
112
- }
113
- // We transform the absolute URL into a relative URL.
114
- const url = new URL(item.url);
115
- return {
116
- ...item,
117
- url: withBaseUrl(`${url.pathname}${url.hash}`),
118
- };
119
- }),
109
+ items.map((item) => ({
110
+ ...item,
111
+ url: processSearchResultUrl(item.url),
112
+ })),
120
113
  ).current;
121
114
  const resultsFooterComponent = useMemo(
122
115
  () =>
@@ -5,26 +5,29 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  /* eslint-disable jsx-a11y/no-autofocus */
8
- import React, {useEffect, useState, useReducer, useRef} from 'react';
8
+ import React, {useEffect, useReducer, useRef, useState} from 'react';
9
9
  import clsx from 'clsx';
10
- import algoliaSearch from 'algoliasearch/lite';
11
10
  import algoliaSearchHelper from 'algoliasearch-helper';
11
+ import algoliaSearch from 'algoliasearch/lite';
12
+ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
12
13
  import Head from '@docusaurus/Head';
13
14
  import Link from '@docusaurus/Link';
14
- import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
15
+ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
15
16
  import {
16
17
  HtmlClassNameProvider,
17
- usePluralForm,
18
- isRegexpStringMatch,
19
18
  useEvent,
19
+ usePluralForm,
20
20
  } from '@docusaurus/theme-common';
21
21
  import {
22
- useTitleFormatter,
23
22
  useSearchPage,
23
+ useTitleFormatter,
24
24
  } from '@docusaurus/theme-common/internal';
25
- import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
26
- import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
27
25
  import Translate, {translate} from '@docusaurus/Translate';
26
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
27
+ import {
28
+ useAlgoliaThemeConfig,
29
+ useSearchResultUrlProcessor,
30
+ } from '@docusaurus/theme-search-algolia/client';
28
31
  import Layout from '@theme/Layout';
29
32
  import styles from './styles.module.css';
30
33
  // Very simple pluralization: probably good enough for now
@@ -114,12 +117,12 @@ function SearchVersionSelectList({docsSearchVersionsHelpers}) {
114
117
  }
115
118
  function SearchPageContent() {
116
119
  const {
117
- siteConfig: {themeConfig},
118
120
  i18n: {currentLocale},
119
121
  } = useDocusaurusContext();
120
122
  const {
121
- algolia: {appId, apiKey, indexName, externalUrlRegex},
122
- } = themeConfig;
123
+ algolia: {appId, apiKey, indexName},
124
+ } = useAlgoliaThemeConfig();
125
+ const processSearchResultUrl = useSearchResultUrlProcessor();
123
126
  const documentsFoundPlural = useDocumentsFoundPlural();
124
127
  const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
125
128
  const {searchQuery, setSearchQuery} = useSearchPage();
@@ -191,15 +194,12 @@ function SearchPageContent() {
191
194
  _highlightResult: {hierarchy},
192
195
  _snippetResult: snippet = {},
193
196
  }) => {
194
- const parsedURL = new URL(url);
195
197
  const titles = Object.keys(hierarchy).map((key) =>
196
198
  sanitizeValue(hierarchy[key].value),
197
199
  );
198
200
  return {
199
201
  title: titles.pop(),
200
- url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
201
- ? parsedURL.href
202
- : parsedURL.pathname + parsedURL.hash,
202
+ url: processSearchResultUrl(url),
203
203
  summary: snippet.content
204
204
  ? `${sanitizeValue(snippet.content.value)}...`
205
205
  : '',
@@ -7,6 +7,7 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.validateThemeConfig = exports.Schema = exports.DEFAULT_CONFIG = void 0;
10
+ const utils_1 = require("@docusaurus/utils");
10
11
  const utils_validation_1 = require("@docusaurus/utils-validation");
11
12
  exports.DEFAULT_CONFIG = {
12
13
  // Enabled by default, as it makes sense in most cases
@@ -33,6 +34,18 @@ exports.Schema = utils_validation_1.Joi.object({
33
34
  .try(utils_validation_1.Joi.boolean().invalid(true), utils_validation_1.Joi.string())
34
35
  .allow(null)
35
36
  .default(exports.DEFAULT_CONFIG.searchPagePath),
37
+ replaceSearchResultPathname: utils_validation_1.Joi.object({
38
+ from: utils_validation_1.Joi.custom((from) => {
39
+ if (typeof from === 'string') {
40
+ return (0, utils_1.escapeRegexp)(from);
41
+ }
42
+ else if (from instanceof RegExp) {
43
+ return from.source;
44
+ }
45
+ throw new Error(`it should be a RegExp or a string, but received ${from}`);
46
+ }).required(),
47
+ to: utils_validation_1.Joi.string().required(),
48
+ }).optional(),
36
49
  })
37
50
  .label('themeConfig.algolia')
38
51
  .required()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docusaurus/theme-search-algolia",
3
- "version": "0.0.0-5419",
3
+ "version": "0.0.0-5426",
4
4
  "description": "Algolia search component for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "sideEffects": [
@@ -34,13 +34,13 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@docsearch/react": "^3.2.1",
37
- "@docusaurus/core": "0.0.0-5419",
38
- "@docusaurus/logger": "0.0.0-5419",
39
- "@docusaurus/plugin-content-docs": "0.0.0-5419",
40
- "@docusaurus/theme-common": "0.0.0-5419",
41
- "@docusaurus/theme-translations": "0.0.0-5419",
42
- "@docusaurus/utils": "0.0.0-5419",
43
- "@docusaurus/utils-validation": "0.0.0-5419",
37
+ "@docusaurus/core": "0.0.0-5426",
38
+ "@docusaurus/logger": "0.0.0-5426",
39
+ "@docusaurus/plugin-content-docs": "0.0.0-5426",
40
+ "@docusaurus/theme-common": "0.0.0-5426",
41
+ "@docusaurus/theme-translations": "0.0.0-5426",
42
+ "@docusaurus/utils": "0.0.0-5426",
43
+ "@docusaurus/utils-validation": "0.0.0-5426",
44
44
  "algoliasearch": "^4.14.2",
45
45
  "algoliasearch-helper": "^3.11.0",
46
46
  "clsx": "^1.2.1",
@@ -51,7 +51,7 @@
51
51
  "utility-types": "^3.10.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@docusaurus/module-type-aliases": "0.0.0-5419"
54
+ "@docusaurus/module-type-aliases": "0.0.0-5426"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "react": "^16.8.4 || ^17.0.0",
@@ -60,5 +60,5 @@
60
60
  "engines": {
61
61
  "node": ">=16.14"
62
62
  },
63
- "gitHead": "18cbc502568964a167de9df3f77aa164bccd8188"
63
+ "gitHead": "7bc998a32c3cd26e76d79b8f7e276b06bc11af02"
64
64
  }
@@ -5,4 +5,6 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ export {useAlgoliaThemeConfig} from './useAlgoliaThemeConfig';
8
9
  export {useAlgoliaContextualFacetFilters} from './useAlgoliaContextualFacetFilters';
10
+ export {useSearchResultUrlProcessor} from './useSearchResultUrlProcessor';
@@ -0,0 +1,15 @@
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 useDocusaurusContext from '@docusaurus/useDocusaurusContext';
8
+ import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
9
+
10
+ export function useAlgoliaThemeConfig(): ThemeConfig {
11
+ const {
12
+ siteConfig: {themeConfig},
13
+ } = useDocusaurusContext();
14
+ return themeConfig as ThemeConfig;
15
+ }
@@ -0,0 +1,54 @@
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 {useCallback} from 'react';
9
+ import {isRegexpStringMatch} from '@docusaurus/theme-common';
10
+ import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
11
+ import {useAlgoliaThemeConfig} from './useAlgoliaThemeConfig';
12
+ import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
13
+
14
+ function replacePathname(
15
+ pathname: string,
16
+ replaceSearchResultPathname: ThemeConfig['algolia']['replaceSearchResultPathname'],
17
+ ): string {
18
+ return replaceSearchResultPathname
19
+ ? pathname.replaceAll(
20
+ new RegExp(replaceSearchResultPathname.from, 'g'),
21
+ replaceSearchResultPathname.to,
22
+ )
23
+ : pathname;
24
+ }
25
+
26
+ /**
27
+ * Process the search result url from Algolia to its final form, ready to be
28
+ * navigated to or used as a link
29
+ */
30
+ export function useSearchResultUrlProcessor(): (url: string) => string {
31
+ const {withBaseUrl} = useBaseUrlUtils();
32
+ const {
33
+ algolia: {externalUrlRegex, replaceSearchResultPathname},
34
+ } = useAlgoliaThemeConfig();
35
+
36
+ return useCallback(
37
+ (url: string) => {
38
+ const parsedURL = new URL(url);
39
+
40
+ // Algolia contains an external domain => navigate to URL
41
+ if (isRegexpStringMatch(externalUrlRegex, parsedURL.href)) {
42
+ return url;
43
+ }
44
+
45
+ // Otherwise => transform to relative URL for SPA navigation
46
+ const relativeUrl = `${parsedURL.pathname + parsedURL.hash}`;
47
+
48
+ return withBaseUrl(
49
+ replacePathname(relativeUrl, replaceSearchResultPathname),
50
+ );
51
+ },
52
+ [withBaseUrl, externalUrlRegex, replaceSearchResultPathname],
53
+ );
54
+ }
@@ -5,20 +5,23 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
- import React, {useState, useRef, useCallback, useMemo} from 'react';
9
- import {createPortal} from 'react-dom';
10
- import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
11
- import {useHistory} from '@docusaurus/router';
12
- import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
13
- import Link from '@docusaurus/Link';
8
+ import React, {useCallback, useMemo, useRef, useState} from 'react';
9
+ import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
14
10
  import Head from '@docusaurus/Head';
11
+ import Link from '@docusaurus/Link';
12
+ import {useHistory} from '@docusaurus/router';
15
13
  import {isRegexpStringMatch} from '@docusaurus/theme-common';
16
14
  import {useSearchPage} from '@docusaurus/theme-common/internal';
17
- import {DocSearchButton, useDocSearchKeyboardEvents} from '@docsearch/react';
18
- import {useAlgoliaContextualFacetFilters} from '@docusaurus/theme-search-algolia/client';
15
+ import {
16
+ useAlgoliaContextualFacetFilters,
17
+ useSearchResultUrlProcessor,
18
+ } from '@docusaurus/theme-search-algolia/client';
19
19
  import Translate from '@docusaurus/Translate';
20
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
21
+ import {createPortal} from 'react-dom';
20
22
  import translations from '@theme/SearchTranslations';
21
23
 
24
+ import type {AutocompleteState} from '@algolia/autocomplete-core';
22
25
  import type {
23
26
  DocSearchModal as DocSearchModalType,
24
27
  DocSearchModalProps,
@@ -28,7 +31,6 @@ import type {
28
31
  StoredDocSearchHit,
29
32
  } from '@docsearch/react/dist/esm/types';
30
33
  import type {SearchClient} from 'algoliasearch/lite';
31
- import type {AutocompleteState} from '@algolia/autocomplete-core';
32
34
 
33
35
  type DocSearchProps = Omit<
34
36
  DocSearchModalProps,
@@ -88,6 +90,7 @@ function DocSearch({
88
90
  ...props
89
91
  }: DocSearchProps) {
90
92
  const {siteMetadata} = useDocusaurusContext();
93
+ const processSearchResultUrl = useSearchResultUrlProcessor();
91
94
 
92
95
  const contextualSearchFacetFilters =
93
96
  useAlgoliaContextualFacetFilters() as FacetFilters;
@@ -107,7 +110,6 @@ function DocSearch({
107
110
  facetFilters,
108
111
  };
109
112
 
110
- const {withBaseUrl} = useBaseUrlUtils();
111
113
  const history = useHistory();
112
114
  const searchContainer = useRef<HTMLDivElement | null>(null);
113
115
  const searchButtonRef = useRef<HTMLButtonElement>(null);
@@ -172,20 +174,10 @@ function DocSearch({
172
174
 
173
175
  const transformItems = useRef<DocSearchModalProps['transformItems']>(
174
176
  (items) =>
175
- items.map((item) => {
176
- // If Algolia contains a external domain, we should navigate without
177
- // relative URL
178
- if (isRegexpStringMatch(externalUrlRegex, item.url)) {
179
- return item;
180
- }
181
-
182
- // We transform the absolute URL into a relative URL.
183
- const url = new URL(item.url);
184
- return {
185
- ...item,
186
- url: withBaseUrl(`${url.pathname}${url.hash}`),
187
- };
188
- }),
177
+ items.map((item) => ({
178
+ ...item,
179
+ url: processSearchResultUrl(item.url),
180
+ })),
189
181
  ).current;
190
182
 
191
183
  const resultsFooterComponent: DocSearchProps['resultsFooterComponent'] =
@@ -7,32 +7,33 @@
7
7
 
8
8
  /* eslint-disable jsx-a11y/no-autofocus */
9
9
 
10
- import React, {useEffect, useState, useReducer, useRef} from 'react';
10
+ import React, {useEffect, useReducer, useRef, useState} from 'react';
11
11
  import clsx from 'clsx';
12
12
 
13
- import algoliaSearch from 'algoliasearch/lite';
14
13
  import algoliaSearchHelper from 'algoliasearch-helper';
14
+ import algoliaSearch from 'algoliasearch/lite';
15
15
 
16
+ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
16
17
  import Head from '@docusaurus/Head';
17
18
  import Link from '@docusaurus/Link';
18
- import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
19
+ import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
19
20
  import {
20
21
  HtmlClassNameProvider,
21
- usePluralForm,
22
- isRegexpStringMatch,
23
22
  useEvent,
23
+ usePluralForm,
24
24
  } from '@docusaurus/theme-common';
25
25
  import {
26
- useTitleFormatter,
27
26
  useSearchPage,
27
+ useTitleFormatter,
28
28
  } from '@docusaurus/theme-common/internal';
29
- import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
30
- import {useAllDocsData} from '@docusaurus/plugin-content-docs/client';
31
29
  import Translate, {translate} from '@docusaurus/Translate';
30
+ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
31
+ import {
32
+ useAlgoliaThemeConfig,
33
+ useSearchResultUrlProcessor,
34
+ } from '@docusaurus/theme-search-algolia/client';
32
35
  import Layout from '@theme/Layout';
33
36
 
34
- import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
35
-
36
37
  import styles from './styles.module.css';
37
38
 
38
39
  // Very simple pluralization: probably good enough for now
@@ -157,12 +158,12 @@ type ResultDispatcher =
157
158
 
158
159
  function SearchPageContent(): JSX.Element {
159
160
  const {
160
- siteConfig: {themeConfig},
161
161
  i18n: {currentLocale},
162
162
  } = useDocusaurusContext();
163
163
  const {
164
- algolia: {appId, apiKey, indexName, externalUrlRegex},
165
- } = themeConfig as ThemeConfig;
164
+ algolia: {appId, apiKey, indexName},
165
+ } = useAlgoliaThemeConfig();
166
+ const processSearchResultUrl = useSearchResultUrlProcessor();
166
167
  const documentsFoundPlural = useDocumentsFoundPlural();
167
168
 
168
169
  const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers();
@@ -245,16 +246,12 @@ function SearchPageContent(): JSX.Element {
245
246
  _highlightResult: {hierarchy: {[key: string]: {value: string}}};
246
247
  _snippetResult: {content?: {value: string}};
247
248
  }) => {
248
- const parsedURL = new URL(url);
249
249
  const titles = Object.keys(hierarchy).map((key) =>
250
250
  sanitizeValue(hierarchy[key]!.value),
251
251
  );
252
-
253
252
  return {
254
253
  title: titles.pop()!,
255
- url: isRegexpStringMatch(externalUrlRegex, parsedURL.href)
256
- ? parsedURL.href
257
- : parsedURL.pathname + parsedURL.hash,
254
+ url: processSearchResultUrl(url),
258
255
  summary: snippet.content
259
256
  ? `${sanitizeValue(snippet.content.value)}...`
260
257
  : '',
@@ -17,13 +17,23 @@ declare module '@docusaurus/theme-search-algolia' {
17
17
  indexName: string;
18
18
  searchParameters: {[key: string]: unknown};
19
19
  searchPagePath: string | false | null;
20
+ replaceSearchResultPathname?: {
21
+ from: string;
22
+ to: string;
23
+ };
20
24
  };
21
25
  };
22
26
  export type UserThemeConfig = DeepPartial<ThemeConfig>;
23
27
  }
24
28
 
25
29
  declare module '@docusaurus/theme-search-algolia/client' {
30
+ import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
31
+
32
+ export function useAlgoliaThemeConfig(): ThemeConfig;
33
+
26
34
  export function useAlgoliaContextualFacetFilters(): [string, string[]];
35
+
36
+ export function useSearchResultUrlProcessor(): (url: string) => string;
27
37
  }
28
38
 
29
39
  declare module '@theme/SearchPage' {
@@ -5,6 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import {escapeRegexp} from '@docusaurus/utils';
8
9
  import {Joi} from '@docusaurus/utils-validation';
9
10
  import type {
10
11
  ThemeConfig,
@@ -39,6 +40,19 @@ export const Schema = Joi.object<ThemeConfig>({
39
40
  .try(Joi.boolean().invalid(true), Joi.string())
40
41
  .allow(null)
41
42
  .default(DEFAULT_CONFIG.searchPagePath),
43
+ replaceSearchResultPathname: Joi.object({
44
+ from: Joi.custom((from) => {
45
+ if (typeof from === 'string') {
46
+ return escapeRegexp(from);
47
+ } else if (from instanceof RegExp) {
48
+ return from.source;
49
+ }
50
+ throw new Error(
51
+ `it should be a RegExp or a string, but received ${from}`,
52
+ );
53
+ }).required(),
54
+ to: Joi.string().required(),
55
+ }).optional(),
42
56
  })
43
57
  .label('themeConfig.algolia')
44
58
  .required()