@docusaurus/theme-search-algolia 3.3.1 → 3.4.0
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.
- package/lib/index.js +8 -44
- package/lib/opensearch.d.ts +16 -0
- package/lib/opensearch.js +66 -0
- package/lib/theme/SearchBar/index.js +26 -26
- package/lib/theme/SearchPage/index.js +19 -13
- package/package.json +10 -10
- package/src/index.ts +12 -57
- package/src/opensearch.ts +115 -0
- package/src/theme/SearchBar/index.tsx +27 -26
- package/src/theme/SearchPage/index.tsx +22 -14
package/lib/index.js
CHANGED
|
@@ -7,23 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.validateThemeConfig = void 0;
|
|
10
|
-
const tslib_1 = require("tslib");
|
|
11
|
-
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
|
-
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
13
|
-
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
14
|
-
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
15
|
-
const eta_1 = require("eta");
|
|
16
10
|
const utils_1 = require("@docusaurus/utils");
|
|
17
11
|
const theme_translations_1 = require("@docusaurus/theme-translations");
|
|
18
|
-
const opensearch_1 =
|
|
19
|
-
const getCompiledOpenSearchTemplate = lodash_1.default.memoize(() => (0, eta_1.compile)(opensearch_1.default.trim()));
|
|
20
|
-
function renderOpenSearchTemplate(data) {
|
|
21
|
-
const compiled = getCompiledOpenSearchTemplate();
|
|
22
|
-
return compiled(data, eta_1.defaultConfig);
|
|
23
|
-
}
|
|
24
|
-
const OPEN_SEARCH_FILENAME = 'opensearch.xml';
|
|
12
|
+
const opensearch_1 = require("./opensearch");
|
|
25
13
|
function themeSearchAlgolia(context) {
|
|
26
|
-
const { baseUrl, siteConfig: {
|
|
14
|
+
const { baseUrl, siteConfig: { themeConfig }, i18n: { currentLocale }, } = context;
|
|
27
15
|
const { algolia: { searchPagePath }, } = themeConfig;
|
|
28
16
|
return {
|
|
29
17
|
name: 'docusaurus-theme-search-algolia',
|
|
@@ -48,40 +36,16 @@ function themeSearchAlgolia(context) {
|
|
|
48
36
|
});
|
|
49
37
|
}
|
|
50
38
|
},
|
|
51
|
-
async postBuild(
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
await fs_extra_1.default.writeFile(path_1.default.join(outDir, OPEN_SEARCH_FILENAME), renderOpenSearchTemplate({
|
|
56
|
-
title,
|
|
57
|
-
siteUrl,
|
|
58
|
-
searchUrl: (0, utils_1.normalizeUrl)([siteUrl, searchPagePath]),
|
|
59
|
-
faviconUrl: favicon ? (0, utils_1.normalizeUrl)([siteUrl, favicon]) : null,
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
catch (err) {
|
|
63
|
-
logger_1.default.error('Generating OpenSearch file failed.');
|
|
64
|
-
throw err;
|
|
65
|
-
}
|
|
39
|
+
async postBuild() {
|
|
40
|
+
if ((0, opensearch_1.shouldCreateOpenSearchFile)({ context })) {
|
|
41
|
+
await (0, opensearch_1.createOpenSearchFile)({ context });
|
|
66
42
|
}
|
|
67
43
|
},
|
|
68
44
|
injectHtmlTags() {
|
|
69
|
-
if (
|
|
70
|
-
return {};
|
|
45
|
+
if ((0, opensearch_1.shouldCreateOpenSearchFile)({ context })) {
|
|
46
|
+
return { headTags: (0, opensearch_1.createOpenSearchHeadTags)({ context }) };
|
|
71
47
|
}
|
|
72
|
-
return {
|
|
73
|
-
headTags: [
|
|
74
|
-
{
|
|
75
|
-
tagName: 'link',
|
|
76
|
-
attributes: {
|
|
77
|
-
rel: 'search',
|
|
78
|
-
type: 'application/opensearchdescription+xml',
|
|
79
|
-
title,
|
|
80
|
-
href: (0, utils_1.normalizeUrl)([baseUrl, OPEN_SEARCH_FILENAME]),
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
],
|
|
84
|
-
};
|
|
48
|
+
return {};
|
|
85
49
|
},
|
|
86
50
|
};
|
|
87
51
|
}
|
|
@@ -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
|
+
import type { HtmlTags, LoadContext } from '@docusaurus/types';
|
|
8
|
+
export declare function shouldCreateOpenSearchFile({ context, }: {
|
|
9
|
+
context: LoadContext;
|
|
10
|
+
}): boolean;
|
|
11
|
+
export declare function createOpenSearchFile({ context, }: {
|
|
12
|
+
context: LoadContext;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
export declare function createOpenSearchHeadTags({ context, }: {
|
|
15
|
+
context: LoadContext;
|
|
16
|
+
}): HtmlTags;
|
|
@@ -0,0 +1,66 @@
|
|
|
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.createOpenSearchHeadTags = exports.createOpenSearchFile = exports.shouldCreateOpenSearchFile = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
13
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
14
|
+
const eta_1 = require("eta");
|
|
15
|
+
const utils_1 = require("@docusaurus/utils");
|
|
16
|
+
const opensearch_1 = tslib_1.__importDefault(require("./templates/opensearch"));
|
|
17
|
+
const getCompiledOpenSearchTemplate = lodash_1.default.memoize(() => (0, eta_1.compile)(opensearch_1.default.trim()));
|
|
18
|
+
function renderOpenSearchTemplate(data) {
|
|
19
|
+
const compiled = getCompiledOpenSearchTemplate();
|
|
20
|
+
return compiled(data, eta_1.defaultConfig);
|
|
21
|
+
}
|
|
22
|
+
const OPEN_SEARCH_FILENAME = 'opensearch.xml';
|
|
23
|
+
function shouldCreateOpenSearchFile({ context, }) {
|
|
24
|
+
const { siteConfig: { themeConfig, future: { experimental_router: router }, }, } = context;
|
|
25
|
+
const { algolia: { searchPagePath }, } = themeConfig;
|
|
26
|
+
return !!searchPagePath && router !== 'hash';
|
|
27
|
+
}
|
|
28
|
+
exports.shouldCreateOpenSearchFile = shouldCreateOpenSearchFile;
|
|
29
|
+
function createOpenSearchFileContent({ context, searchPagePath, }) {
|
|
30
|
+
const { baseUrl, siteConfig: { title, url, favicon }, } = context;
|
|
31
|
+
const siteUrl = (0, utils_1.normalizeUrl)([url, baseUrl]);
|
|
32
|
+
return renderOpenSearchTemplate({
|
|
33
|
+
title,
|
|
34
|
+
siteUrl,
|
|
35
|
+
searchUrl: (0, utils_1.normalizeUrl)([siteUrl, searchPagePath]),
|
|
36
|
+
faviconUrl: favicon ? (0, utils_1.normalizeUrl)([siteUrl, favicon]) : null,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function createOpenSearchFile({ context, }) {
|
|
40
|
+
const { outDir, siteConfig: { themeConfig }, } = context;
|
|
41
|
+
const { algolia: { searchPagePath }, } = themeConfig;
|
|
42
|
+
if (!searchPagePath) {
|
|
43
|
+
throw new Error('no searchPagePath provided in themeConfig.algolia');
|
|
44
|
+
}
|
|
45
|
+
const fileContent = createOpenSearchFileContent({ context, searchPagePath });
|
|
46
|
+
try {
|
|
47
|
+
await fs_extra_1.default.writeFile(path_1.default.join(outDir, OPEN_SEARCH_FILENAME), fileContent);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw new Error('Generating OpenSearch file failed.', { cause: err });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.createOpenSearchFile = createOpenSearchFile;
|
|
54
|
+
function createOpenSearchHeadTags({ context, }) {
|
|
55
|
+
const { baseUrl, siteConfig: { title }, } = context;
|
|
56
|
+
return {
|
|
57
|
+
tagName: 'link',
|
|
58
|
+
attributes: {
|
|
59
|
+
rel: 'search',
|
|
60
|
+
type: 'application/opensearchdescription+xml',
|
|
61
|
+
title,
|
|
62
|
+
href: (0, utils_1.normalizeUrl)([baseUrl, OPEN_SEARCH_FILENAME]),
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
exports.createOpenSearchHeadTags = createOpenSearchHeadTags;
|
|
@@ -73,29 +73,29 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
73
73
|
DocSearchModal = Modal;
|
|
74
74
|
});
|
|
75
75
|
}, []);
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
const prepareSearchContainer = useCallback(() => {
|
|
77
|
+
if (!searchContainer.current) {
|
|
78
|
+
const divElement = document.createElement('div');
|
|
79
|
+
searchContainer.current = divElement;
|
|
80
|
+
document.body.insertBefore(divElement, document.body.firstChild);
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
83
|
+
const openModal = useCallback(() => {
|
|
84
|
+
prepareSearchContainer();
|
|
85
|
+
importDocSearchModalIfNeeded().then(() => setIsOpen(true));
|
|
86
|
+
}, [importDocSearchModalIfNeeded, prepareSearchContainer]);
|
|
87
|
+
const closeModal = useCallback(() => {
|
|
87
88
|
setIsOpen(false);
|
|
88
|
-
searchContainer.current?.remove();
|
|
89
89
|
searchButtonRef.current?.focus();
|
|
90
|
-
}, [
|
|
91
|
-
const
|
|
90
|
+
}, []);
|
|
91
|
+
const handleInput = useCallback(
|
|
92
92
|
(event) => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// prevents duplicate key insertion in the modal input
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
setInitialQuery(event.key);
|
|
96
|
+
openModal();
|
|
97
97
|
},
|
|
98
|
-
[
|
|
98
|
+
[openModal],
|
|
99
99
|
);
|
|
100
100
|
const navigator = useRef({
|
|
101
101
|
navigate({itemUrl}) {
|
|
@@ -122,8 +122,8 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
122
122
|
() =>
|
|
123
123
|
// eslint-disable-next-line react/no-unstable-nested-components
|
|
124
124
|
(footerProps) =>
|
|
125
|
-
<ResultsFooter {...footerProps} onClose={
|
|
126
|
-
[
|
|
125
|
+
<ResultsFooter {...footerProps} onClose={closeModal} />,
|
|
126
|
+
[closeModal],
|
|
127
127
|
);
|
|
128
128
|
const transformSearchClient = useCallback(
|
|
129
129
|
(searchClient) => {
|
|
@@ -137,9 +137,9 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
137
137
|
);
|
|
138
138
|
useDocSearchKeyboardEvents({
|
|
139
139
|
isOpen,
|
|
140
|
-
onOpen,
|
|
141
|
-
onClose,
|
|
142
|
-
onInput,
|
|
140
|
+
onOpen: openModal,
|
|
141
|
+
onClose: closeModal,
|
|
142
|
+
onInput: handleInput,
|
|
143
143
|
searchButtonRef,
|
|
144
144
|
});
|
|
145
145
|
return (
|
|
@@ -159,7 +159,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
159
159
|
onTouchStart={importDocSearchModalIfNeeded}
|
|
160
160
|
onFocus={importDocSearchModalIfNeeded}
|
|
161
161
|
onMouseOver={importDocSearchModalIfNeeded}
|
|
162
|
-
onClick={
|
|
162
|
+
onClick={openModal}
|
|
163
163
|
ref={searchButtonRef}
|
|
164
164
|
translations={translations.button}
|
|
165
165
|
/>
|
|
@@ -169,7 +169,7 @@ function DocSearch({contextualSearch, externalUrlRegex, ...props}) {
|
|
|
169
169
|
searchContainer.current &&
|
|
170
170
|
createPortal(
|
|
171
171
|
<DocSearchModal
|
|
172
|
-
onClose={
|
|
172
|
+
onClose={closeModal}
|
|
173
173
|
initialScrollY={window.scrollY}
|
|
174
174
|
initialQuery={initialQuery}
|
|
175
175
|
navigator={navigator}
|
|
@@ -119,7 +119,7 @@ function SearchPageContent() {
|
|
|
119
119
|
i18n: {currentLocale},
|
|
120
120
|
} = useDocusaurusContext();
|
|
121
121
|
const {
|
|
122
|
-
algolia: {appId, apiKey, indexName},
|
|
122
|
+
algolia: {appId, apiKey, indexName, contextualSearch},
|
|
123
123
|
} = useAlgoliaThemeConfig();
|
|
124
124
|
const processSearchResultUrl = useSearchResultUrlProcessor();
|
|
125
125
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
|
@@ -169,11 +169,15 @@ function SearchPageContent() {
|
|
|
169
169
|
},
|
|
170
170
|
initialSearchResultState,
|
|
171
171
|
);
|
|
172
|
+
// respect settings from the theme config for facets
|
|
173
|
+
const disjunctiveFacets = contextualSearch
|
|
174
|
+
? ['language', 'docusaurus_tag']
|
|
175
|
+
: [];
|
|
172
176
|
const algoliaClient = algoliaSearch(appId, apiKey);
|
|
173
177
|
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
174
178
|
hitsPerPage: 15,
|
|
175
179
|
advancedSyntax: true,
|
|
176
|
-
disjunctiveFacets
|
|
180
|
+
disjunctiveFacets,
|
|
177
181
|
});
|
|
178
182
|
algoliaHelper.on(
|
|
179
183
|
'result',
|
|
@@ -256,16 +260,18 @@ function SearchPageContent() {
|
|
|
256
260
|
description: 'The search page title for empty query',
|
|
257
261
|
});
|
|
258
262
|
const makeSearch = useEvent((page = 0) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
263
|
+
if (contextualSearch) {
|
|
264
|
+
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
|
265
|
+
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
|
266
|
+
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
|
|
267
|
+
([pluginId, searchVersion]) => {
|
|
268
|
+
algoliaHelper.addDisjunctiveFacetRefinement(
|
|
269
|
+
'docusaurus_tag',
|
|
270
|
+
`docs-${pluginId}-${searchVersion}`,
|
|
271
|
+
);
|
|
272
|
+
},
|
|
273
|
+
);
|
|
274
|
+
}
|
|
269
275
|
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
|
270
276
|
});
|
|
271
277
|
useEffect(() => {
|
|
@@ -335,7 +341,7 @@ function SearchPageContent() {
|
|
|
335
341
|
/>
|
|
336
342
|
</div>
|
|
337
343
|
|
|
338
|
-
{docsSearchVersionsHelpers.versioningEnabled && (
|
|
344
|
+
{contextualSearch && docsSearchVersionsHelpers.versioningEnabled && (
|
|
339
345
|
<SearchVersionSelectList
|
|
340
346
|
docsSearchVersionsHelpers={docsSearchVersionsHelpers}
|
|
341
347
|
/>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/theme-search-algolia",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
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.5.2",
|
|
37
|
-
"@docusaurus/core": "3.
|
|
38
|
-
"@docusaurus/logger": "3.
|
|
39
|
-
"@docusaurus/plugin-content-docs": "3.
|
|
40
|
-
"@docusaurus/theme-common": "3.
|
|
41
|
-
"@docusaurus/theme-translations": "3.
|
|
42
|
-
"@docusaurus/utils": "3.
|
|
43
|
-
"@docusaurus/utils-validation": "3.
|
|
37
|
+
"@docusaurus/core": "3.4.0",
|
|
38
|
+
"@docusaurus/logger": "3.4.0",
|
|
39
|
+
"@docusaurus/plugin-content-docs": "3.4.0",
|
|
40
|
+
"@docusaurus/theme-common": "3.4.0",
|
|
41
|
+
"@docusaurus/theme-translations": "3.4.0",
|
|
42
|
+
"@docusaurus/utils": "3.4.0",
|
|
43
|
+
"@docusaurus/utils-validation": "3.4.0",
|
|
44
44
|
"algoliasearch": "^4.18.0",
|
|
45
45
|
"algoliasearch-helper": "^3.13.3",
|
|
46
46
|
"clsx": "^2.0.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"utility-types": "^3.10.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@docusaurus/module-type-aliases": "3.
|
|
54
|
+
"@docusaurus/module-type-aliases": "3.4.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"react": "^18.0.0",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=18.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "49e9a2143274a8dd795659b417b470bc42abbd6e"
|
|
64
64
|
}
|
package/src/index.ts
CHANGED
|
@@ -5,38 +5,21 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import fs from 'fs-extra';
|
|
10
|
-
import _ from 'lodash';
|
|
11
|
-
import logger from '@docusaurus/logger';
|
|
12
|
-
import {defaultConfig, compile} from 'eta';
|
|
13
8
|
import {normalizeUrl} from '@docusaurus/utils';
|
|
14
9
|
import {readDefaultCodeTranslationMessages} from '@docusaurus/theme-translations';
|
|
15
|
-
import
|
|
10
|
+
import {
|
|
11
|
+
createOpenSearchFile,
|
|
12
|
+
createOpenSearchHeadTags,
|
|
13
|
+
shouldCreateOpenSearchFile,
|
|
14
|
+
} from './opensearch';
|
|
16
15
|
|
|
17
16
|
import type {LoadContext, Plugin} from '@docusaurus/types';
|
|
18
17
|
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
|
19
18
|
|
|
20
|
-
const getCompiledOpenSearchTemplate = _.memoize(() =>
|
|
21
|
-
compile(openSearchTemplate.trim()),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
function renderOpenSearchTemplate(data: {
|
|
25
|
-
title: string;
|
|
26
|
-
siteUrl: string;
|
|
27
|
-
searchUrl: string;
|
|
28
|
-
faviconUrl: string | null;
|
|
29
|
-
}) {
|
|
30
|
-
const compiled = getCompiledOpenSearchTemplate();
|
|
31
|
-
return compiled(data, defaultConfig);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const OPEN_SEARCH_FILENAME = 'opensearch.xml';
|
|
35
|
-
|
|
36
19
|
export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
|
|
37
20
|
const {
|
|
38
21
|
baseUrl,
|
|
39
|
-
siteConfig: {
|
|
22
|
+
siteConfig: {themeConfig},
|
|
40
23
|
i18n: {currentLocale},
|
|
41
24
|
} = context;
|
|
42
25
|
const {
|
|
@@ -70,45 +53,17 @@ export default function themeSearchAlgolia(context: LoadContext): Plugin<void> {
|
|
|
70
53
|
}
|
|
71
54
|
},
|
|
72
55
|
|
|
73
|
-
async postBuild(
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
await fs.writeFile(
|
|
79
|
-
path.join(outDir, OPEN_SEARCH_FILENAME),
|
|
80
|
-
renderOpenSearchTemplate({
|
|
81
|
-
title,
|
|
82
|
-
siteUrl,
|
|
83
|
-
searchUrl: normalizeUrl([siteUrl, searchPagePath]),
|
|
84
|
-
faviconUrl: favicon ? normalizeUrl([siteUrl, favicon]) : null,
|
|
85
|
-
}),
|
|
86
|
-
);
|
|
87
|
-
} catch (err) {
|
|
88
|
-
logger.error('Generating OpenSearch file failed.');
|
|
89
|
-
throw err;
|
|
90
|
-
}
|
|
56
|
+
async postBuild() {
|
|
57
|
+
if (shouldCreateOpenSearchFile({context})) {
|
|
58
|
+
await createOpenSearchFile({context});
|
|
91
59
|
}
|
|
92
60
|
},
|
|
93
61
|
|
|
94
62
|
injectHtmlTags() {
|
|
95
|
-
if (
|
|
96
|
-
return {};
|
|
63
|
+
if (shouldCreateOpenSearchFile({context})) {
|
|
64
|
+
return {headTags: createOpenSearchHeadTags({context})};
|
|
97
65
|
}
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
headTags: [
|
|
101
|
-
{
|
|
102
|
-
tagName: 'link',
|
|
103
|
-
attributes: {
|
|
104
|
-
rel: 'search',
|
|
105
|
-
type: 'application/opensearchdescription+xml',
|
|
106
|
-
title,
|
|
107
|
-
href: normalizeUrl([baseUrl, OPEN_SEARCH_FILENAME]),
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
],
|
|
111
|
-
};
|
|
66
|
+
return {};
|
|
112
67
|
},
|
|
113
68
|
};
|
|
114
69
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
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 path from 'path';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import _ from 'lodash';
|
|
11
|
+
import {defaultConfig, compile} from 'eta';
|
|
12
|
+
import {normalizeUrl} from '@docusaurus/utils';
|
|
13
|
+
import openSearchTemplate from './templates/opensearch';
|
|
14
|
+
|
|
15
|
+
import type {HtmlTags, LoadContext} from '@docusaurus/types';
|
|
16
|
+
import type {ThemeConfig} from '@docusaurus/theme-search-algolia';
|
|
17
|
+
|
|
18
|
+
const getCompiledOpenSearchTemplate = _.memoize(() =>
|
|
19
|
+
compile(openSearchTemplate.trim()),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
function renderOpenSearchTemplate(data: {
|
|
23
|
+
title: string;
|
|
24
|
+
siteUrl: string;
|
|
25
|
+
searchUrl: string;
|
|
26
|
+
faviconUrl: string | null;
|
|
27
|
+
}) {
|
|
28
|
+
const compiled = getCompiledOpenSearchTemplate();
|
|
29
|
+
return compiled(data, defaultConfig);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const OPEN_SEARCH_FILENAME = 'opensearch.xml';
|
|
33
|
+
|
|
34
|
+
export function shouldCreateOpenSearchFile({
|
|
35
|
+
context,
|
|
36
|
+
}: {
|
|
37
|
+
context: LoadContext;
|
|
38
|
+
}): boolean {
|
|
39
|
+
const {
|
|
40
|
+
siteConfig: {
|
|
41
|
+
themeConfig,
|
|
42
|
+
future: {experimental_router: router},
|
|
43
|
+
},
|
|
44
|
+
} = context;
|
|
45
|
+
const {
|
|
46
|
+
algolia: {searchPagePath},
|
|
47
|
+
} = themeConfig as ThemeConfig;
|
|
48
|
+
|
|
49
|
+
return !!searchPagePath && router !== 'hash';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createOpenSearchFileContent({
|
|
53
|
+
context,
|
|
54
|
+
searchPagePath,
|
|
55
|
+
}: {
|
|
56
|
+
context: LoadContext;
|
|
57
|
+
searchPagePath: string;
|
|
58
|
+
}): string {
|
|
59
|
+
const {
|
|
60
|
+
baseUrl,
|
|
61
|
+
siteConfig: {title, url, favicon},
|
|
62
|
+
} = context;
|
|
63
|
+
|
|
64
|
+
const siteUrl = normalizeUrl([url, baseUrl]);
|
|
65
|
+
|
|
66
|
+
return renderOpenSearchTemplate({
|
|
67
|
+
title,
|
|
68
|
+
siteUrl,
|
|
69
|
+
searchUrl: normalizeUrl([siteUrl, searchPagePath]),
|
|
70
|
+
faviconUrl: favicon ? normalizeUrl([siteUrl, favicon]) : null,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function createOpenSearchFile({
|
|
75
|
+
context,
|
|
76
|
+
}: {
|
|
77
|
+
context: LoadContext;
|
|
78
|
+
}): Promise<void> {
|
|
79
|
+
const {
|
|
80
|
+
outDir,
|
|
81
|
+
siteConfig: {themeConfig},
|
|
82
|
+
} = context;
|
|
83
|
+
const {
|
|
84
|
+
algolia: {searchPagePath},
|
|
85
|
+
} = themeConfig as ThemeConfig;
|
|
86
|
+
if (!searchPagePath) {
|
|
87
|
+
throw new Error('no searchPagePath provided in themeConfig.algolia');
|
|
88
|
+
}
|
|
89
|
+
const fileContent = createOpenSearchFileContent({context, searchPagePath});
|
|
90
|
+
try {
|
|
91
|
+
await fs.writeFile(path.join(outDir, OPEN_SEARCH_FILENAME), fileContent);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
throw new Error('Generating OpenSearch file failed.', {cause: err});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function createOpenSearchHeadTags({
|
|
98
|
+
context,
|
|
99
|
+
}: {
|
|
100
|
+
context: LoadContext;
|
|
101
|
+
}): HtmlTags {
|
|
102
|
+
const {
|
|
103
|
+
baseUrl,
|
|
104
|
+
siteConfig: {title},
|
|
105
|
+
} = context;
|
|
106
|
+
return {
|
|
107
|
+
tagName: 'link',
|
|
108
|
+
attributes: {
|
|
109
|
+
rel: 'search',
|
|
110
|
+
type: 'application/opensearchdescription+xml',
|
|
111
|
+
title,
|
|
112
|
+
href: normalizeUrl([baseUrl, OPEN_SEARCH_FILENAME]),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
@@ -136,31 +136,32 @@ function DocSearch({
|
|
|
136
136
|
});
|
|
137
137
|
}, []);
|
|
138
138
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
139
|
+
const prepareSearchContainer = useCallback(() => {
|
|
140
|
+
if (!searchContainer.current) {
|
|
141
|
+
const divElement = document.createElement('div');
|
|
142
|
+
searchContainer.current = divElement;
|
|
143
|
+
document.body.insertBefore(divElement, document.body.firstChild);
|
|
144
|
+
}
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
147
|
+
const openModal = useCallback(() => {
|
|
148
|
+
prepareSearchContainer();
|
|
149
|
+
importDocSearchModalIfNeeded().then(() => setIsOpen(true));
|
|
150
|
+
}, [importDocSearchModalIfNeeded, prepareSearchContainer]);
|
|
149
151
|
|
|
150
|
-
const
|
|
152
|
+
const closeModal = useCallback(() => {
|
|
151
153
|
setIsOpen(false);
|
|
152
|
-
searchContainer.current?.remove();
|
|
153
154
|
searchButtonRef.current?.focus();
|
|
154
|
-
}, [
|
|
155
|
+
}, []);
|
|
155
156
|
|
|
156
|
-
const
|
|
157
|
+
const handleInput = useCallback(
|
|
157
158
|
(event: KeyboardEvent) => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
// prevents duplicate key insertion in the modal input
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
setInitialQuery(event.key);
|
|
162
|
+
openModal();
|
|
162
163
|
},
|
|
163
|
-
[
|
|
164
|
+
[openModal],
|
|
164
165
|
);
|
|
165
166
|
|
|
166
167
|
const navigator = useRef({
|
|
@@ -192,8 +193,8 @@ function DocSearch({
|
|
|
192
193
|
() =>
|
|
193
194
|
// eslint-disable-next-line react/no-unstable-nested-components
|
|
194
195
|
(footerProps: Omit<ResultsFooterProps, 'onClose'>): JSX.Element =>
|
|
195
|
-
<ResultsFooter {...footerProps} onClose={
|
|
196
|
-
[
|
|
196
|
+
<ResultsFooter {...footerProps} onClose={closeModal} />,
|
|
197
|
+
[closeModal],
|
|
197
198
|
);
|
|
198
199
|
|
|
199
200
|
const transformSearchClient = useCallback(
|
|
@@ -210,9 +211,9 @@ function DocSearch({
|
|
|
210
211
|
|
|
211
212
|
useDocSearchKeyboardEvents({
|
|
212
213
|
isOpen,
|
|
213
|
-
onOpen,
|
|
214
|
-
onClose,
|
|
215
|
-
onInput,
|
|
214
|
+
onOpen: openModal,
|
|
215
|
+
onClose: closeModal,
|
|
216
|
+
onInput: handleInput,
|
|
216
217
|
searchButtonRef,
|
|
217
218
|
});
|
|
218
219
|
|
|
@@ -233,7 +234,7 @@ function DocSearch({
|
|
|
233
234
|
onTouchStart={importDocSearchModalIfNeeded}
|
|
234
235
|
onFocus={importDocSearchModalIfNeeded}
|
|
235
236
|
onMouseOver={importDocSearchModalIfNeeded}
|
|
236
|
-
onClick={
|
|
237
|
+
onClick={openModal}
|
|
237
238
|
ref={searchButtonRef}
|
|
238
239
|
translations={translations.button}
|
|
239
240
|
/>
|
|
@@ -243,7 +244,7 @@ function DocSearch({
|
|
|
243
244
|
searchContainer.current &&
|
|
244
245
|
createPortal(
|
|
245
246
|
<DocSearchModal
|
|
246
|
-
onClose={
|
|
247
|
+
onClose={closeModal}
|
|
247
248
|
initialScrollY={window.scrollY}
|
|
248
249
|
initialQuery={initialQuery}
|
|
249
250
|
navigator={navigator}
|
|
@@ -159,8 +159,9 @@ function SearchPageContent(): JSX.Element {
|
|
|
159
159
|
i18n: {currentLocale},
|
|
160
160
|
} = useDocusaurusContext();
|
|
161
161
|
const {
|
|
162
|
-
algolia: {appId, apiKey, indexName},
|
|
162
|
+
algolia: {appId, apiKey, indexName, contextualSearch},
|
|
163
163
|
} = useAlgoliaThemeConfig();
|
|
164
|
+
|
|
164
165
|
const processSearchResultUrl = useSearchResultUrlProcessor();
|
|
165
166
|
const documentsFoundPlural = useDocumentsFoundPlural();
|
|
166
167
|
|
|
@@ -213,11 +214,16 @@ function SearchPageContent(): JSX.Element {
|
|
|
213
214
|
initialSearchResultState,
|
|
214
215
|
);
|
|
215
216
|
|
|
217
|
+
// respect settings from the theme config for facets
|
|
218
|
+
const disjunctiveFacets = contextualSearch
|
|
219
|
+
? ['language', 'docusaurus_tag']
|
|
220
|
+
: [];
|
|
221
|
+
|
|
216
222
|
const algoliaClient = algoliaSearch(appId, apiKey);
|
|
217
223
|
const algoliaHelper = algoliaSearchHelper(algoliaClient, indexName, {
|
|
218
224
|
hitsPerPage: 15,
|
|
219
225
|
advancedSyntax: true,
|
|
220
|
-
disjunctiveFacets
|
|
226
|
+
disjunctiveFacets,
|
|
221
227
|
});
|
|
222
228
|
|
|
223
229
|
algoliaHelper.on(
|
|
@@ -313,17 +319,19 @@ function SearchPageContent(): JSX.Element {
|
|
|
313
319
|
});
|
|
314
320
|
|
|
315
321
|
const makeSearch = useEvent((page: number = 0) => {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
322
|
+
if (contextualSearch) {
|
|
323
|
+
algoliaHelper.addDisjunctiveFacetRefinement('docusaurus_tag', 'default');
|
|
324
|
+
algoliaHelper.addDisjunctiveFacetRefinement('language', currentLocale);
|
|
325
|
+
|
|
326
|
+
Object.entries(docsSearchVersionsHelpers.searchVersions).forEach(
|
|
327
|
+
([pluginId, searchVersion]) => {
|
|
328
|
+
algoliaHelper.addDisjunctiveFacetRefinement(
|
|
329
|
+
'docusaurus_tag',
|
|
330
|
+
`docs-${pluginId}-${searchVersion}`,
|
|
331
|
+
);
|
|
332
|
+
},
|
|
333
|
+
);
|
|
334
|
+
}
|
|
327
335
|
|
|
328
336
|
algoliaHelper.setQuery(searchQuery).setPage(page).search();
|
|
329
337
|
});
|
|
@@ -401,7 +409,7 @@ function SearchPageContent(): JSX.Element {
|
|
|
401
409
|
/>
|
|
402
410
|
</div>
|
|
403
411
|
|
|
404
|
-
{docsSearchVersionsHelpers.versioningEnabled && (
|
|
412
|
+
{contextualSearch && docsSearchVersionsHelpers.versioningEnabled && (
|
|
405
413
|
<SearchVersionSelectList
|
|
406
414
|
docsSearchVersionsHelpers={docsSearchVersionsHelpers}
|
|
407
415
|
/>
|