@easyops-cn/docusaurus-search-local 0.40.0 → 0.41.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/CHANGELOG.md +14 -0
- package/dist/client/client/theme/SearchBar/SuggestionTemplate.js +12 -7
- package/dist/client/client/theme/SearchPage/SearchPage.jsx +14 -7
- package/dist/client/client/utils/SearchSourceFactory.js +2 -1
- package/dist/client/client/utils/processTreeStatusOfSearchResults.js +6 -1
- package/dist/client/client/utils/sortSearchResults.js +12 -10
- package/dist/client/shared/interfaces.js +8 -1
- package/dist/server/server/utils/parseDocument.js +3 -1
- package/dist/server/server/utils/parsePage.js +4 -0
- package/dist/server/server/utils/scanDocuments.js +28 -2
- package/dist/server/shared/interfaces.js +9 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [0.41.0](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.40.1...v0.41.0) (2024-05-23)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* support search based on description and keywords ([0c12b59](https://github.com/easyops-cn/docusaurus-search-local/commit/0c12b59866c6d9b7bfa342b4f87e40b01b5f7d48)), closes [#344](https://github.com/easyops-cn/docusaurus-search-local/issues/344)
|
|
11
|
+
|
|
12
|
+
## [0.40.1](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.40.0...v0.40.1) (2023-12-23)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* fix search page no results, closes [#389](https://github.com/easyops-cn/docusaurus-search-local/issues/389) ([64e26c2](https://github.com/easyops-cn/docusaurus-search-local/commit/64e26c2bd9f20257e77089adf9dc697d353e5c0e))
|
|
18
|
+
|
|
5
19
|
## [0.40.0](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.39.0...v0.40.0) (2023-12-19)
|
|
6
20
|
|
|
7
21
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SearchDocumentType, } from "../../../shared/interfaces";
|
|
1
2
|
import { concatDocumentPath } from "../../utils/concatDocumentPath";
|
|
2
3
|
import { getStemmedPositions } from "../../utils/getStemmedPositions";
|
|
3
4
|
import { highlight } from "../../utils/highlight";
|
|
@@ -6,8 +7,10 @@ import { explicitSearchResultPath } from "../../utils/proxiedGenerated";
|
|
|
6
7
|
import { iconAction, iconContent, iconHeading, iconTitle, iconTreeInter, iconTreeLast, } from "./icons";
|
|
7
8
|
import styles from "./SearchBar.module.css";
|
|
8
9
|
export function SuggestionTemplate({ document, type, page, metadata, tokens, isInterOfTree, isLastOfTree, }) {
|
|
9
|
-
const isTitle = type ===
|
|
10
|
-
const
|
|
10
|
+
const isTitle = type === SearchDocumentType.Title;
|
|
11
|
+
const isKeywords = type === SearchDocumentType.Keywords;
|
|
12
|
+
const isTitleRelated = isTitle || isKeywords;
|
|
13
|
+
const isHeading = type === SearchDocumentType.Heading;
|
|
11
14
|
const tree = [];
|
|
12
15
|
if (isInterOfTree) {
|
|
13
16
|
tree.push(iconTreeInter);
|
|
@@ -16,20 +19,22 @@ export function SuggestionTemplate({ document, type, page, metadata, tokens, isI
|
|
|
16
19
|
tree.push(iconTreeLast);
|
|
17
20
|
}
|
|
18
21
|
const treeWrapper = tree.map((item) => `<span class="${styles.hitTree}">${item}</span>`);
|
|
19
|
-
const icon = `<span class="${styles.hitIcon}">${
|
|
22
|
+
const icon = `<span class="${styles.hitIcon}">${isTitleRelated ? iconTitle : isHeading ? iconHeading : iconContent}</span>`;
|
|
20
23
|
const wrapped = [
|
|
21
|
-
`<span class="${styles.hitTitle}">${
|
|
24
|
+
`<span class="${styles.hitTitle}">${isKeywords
|
|
25
|
+
? highlight(document.s, tokens)
|
|
26
|
+
: highlightStemmed(document.t, getStemmedPositions(metadata, "t"), tokens)}</span>`,
|
|
22
27
|
];
|
|
23
28
|
const needsExplicitHitPath = !isInterOfTree && !isLastOfTree && explicitSearchResultPath;
|
|
24
29
|
if (needsExplicitHitPath) {
|
|
25
30
|
const pathItems = page
|
|
26
|
-
?
|
|
27
|
-
|
|
31
|
+
? page.b
|
|
32
|
+
?.concat(page.t)
|
|
28
33
|
.concat(!document.s || document.s === page.t ? [] : document.s)
|
|
29
34
|
: document.b;
|
|
30
35
|
wrapped.push(`<span class="${styles.hitPath}">${concatDocumentPath(pathItems ?? [])}</span>`);
|
|
31
36
|
}
|
|
32
|
-
else if (!
|
|
37
|
+
else if (!isTitleRelated) {
|
|
33
38
|
wrapped.push(`<span class="${styles.hitPath}">${highlight(page.t ||
|
|
34
39
|
// Todo(weareoutman): This is for EasyOps only.
|
|
35
40
|
// istanbul ignore next
|
|
@@ -9,6 +9,7 @@ import clsx from "clsx";
|
|
|
9
9
|
import useSearchQuery from "../hooks/useSearchQuery";
|
|
10
10
|
import { fetchIndexes } from "../SearchBar/fetchIndexes";
|
|
11
11
|
import { SearchSourceFactory } from "../../utils/SearchSourceFactory";
|
|
12
|
+
import { SearchDocumentType, } from "../../../shared/interfaces";
|
|
12
13
|
import { highlight } from "../../utils/highlight";
|
|
13
14
|
import { highlightStemmed } from "../../utils/highlightStemmed";
|
|
14
15
|
import { getStemmedPositions } from "../../utils/getStemmedPositions";
|
|
@@ -68,7 +69,9 @@ function SearchPageContent() {
|
|
|
68
69
|
}, [searchValue]);
|
|
69
70
|
useEffect(() => {
|
|
70
71
|
async function doFetchIndexes() {
|
|
71
|
-
const { wrappedIndexes, zhDictionary } =
|
|
72
|
+
const { wrappedIndexes, zhDictionary } = !Array.isArray(searchContextByPaths) ||
|
|
73
|
+
searchContext ||
|
|
74
|
+
useAllContextsWithNoSearchContext
|
|
72
75
|
? await fetchIndexes(versionUrl, searchContext)
|
|
73
76
|
: { wrappedIndexes: [], zhDictionary: [] };
|
|
74
77
|
setSearchSource(() => SearchSourceFactory(wrappedIndexes, zhDictionary, 100));
|
|
@@ -144,11 +147,15 @@ function SearchPageContent() {
|
|
|
144
147
|
</React.Fragment>);
|
|
145
148
|
}
|
|
146
149
|
function SearchResultItem({ searchResult: { document, type, page, tokens, metadata }, }) {
|
|
147
|
-
const isTitle = type ===
|
|
148
|
-
const
|
|
150
|
+
const isTitle = type === SearchDocumentType.Title;
|
|
151
|
+
const isKeywords = type === SearchDocumentType.Keywords;
|
|
152
|
+
const isDescription = type === SearchDocumentType.Description;
|
|
153
|
+
const isDescriptionOrKeywords = isDescription || isKeywords;
|
|
154
|
+
const isTitleRelated = isTitle || isDescriptionOrKeywords;
|
|
155
|
+
const isContent = type === SearchDocumentType.Content;
|
|
149
156
|
const pathItems = (isTitle ? document.b : page.b).slice();
|
|
150
|
-
const articleTitle = (isContent ? document.s : document.t);
|
|
151
|
-
if (!
|
|
157
|
+
const articleTitle = (isContent || isDescriptionOrKeywords ? document.s : document.t);
|
|
158
|
+
if (!isTitleRelated) {
|
|
152
159
|
pathItems.push(page.t);
|
|
153
160
|
}
|
|
154
161
|
let search = "";
|
|
@@ -162,7 +169,7 @@ function SearchResultItem({ searchResult: { document, type, page, tokens, metada
|
|
|
162
169
|
return (<article className={styles.searchResultItem}>
|
|
163
170
|
<h2>
|
|
164
171
|
<Link to={document.u + search + (document.h || "")} dangerouslySetInnerHTML={{
|
|
165
|
-
__html: isContent
|
|
172
|
+
__html: isContent || isDescriptionOrKeywords
|
|
166
173
|
? highlight(articleTitle, tokens)
|
|
167
174
|
: highlightStemmed(articleTitle, getStemmedPositions(metadata, "t"), tokens, 100),
|
|
168
175
|
}}></Link>
|
|
@@ -170,7 +177,7 @@ function SearchResultItem({ searchResult: { document, type, page, tokens, metada
|
|
|
170
177
|
{pathItems.length > 0 && (<p className={styles.searchResultItemPath}>
|
|
171
178
|
{concatDocumentPath(pathItems)}
|
|
172
179
|
</p>)}
|
|
173
|
-
{isContent && (<p className={styles.searchResultItemSummary} dangerouslySetInnerHTML={{
|
|
180
|
+
{(isContent || isDescription) && (<p className={styles.searchResultItemSummary} dangerouslySetInnerHTML={{
|
|
174
181
|
__html: highlightStemmed(document.t, getStemmedPositions(metadata, "t"), tokens, 100),
|
|
175
182
|
}}/>)}
|
|
176
183
|
</article>);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { tokenize } from "./tokenize";
|
|
2
2
|
import { smartQueries } from "./smartQueries";
|
|
3
|
+
import { SearchDocumentType, } from "../../shared/interfaces";
|
|
3
4
|
import { sortSearchResults } from "./sortSearchResults";
|
|
4
5
|
import { processTreeStatusOfSearchResults } from "./processTreeStatusOfSearchResults";
|
|
5
6
|
import { language } from "./proxiedGenerated";
|
|
@@ -32,7 +33,7 @@ export function SearchSourceFactory(wrappedIndexes, zhDictionary, resultsLimit)
|
|
|
32
33
|
return {
|
|
33
34
|
document,
|
|
34
35
|
type,
|
|
35
|
-
page: type !==
|
|
36
|
+
page: type !== SearchDocumentType.Title &&
|
|
36
37
|
wrappedIndexes[0].documents.find((doc) => doc.i === document.p),
|
|
37
38
|
metadata: result.matchData.metadata,
|
|
38
39
|
tokens,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { SearchDocumentType, } from "../../shared/interfaces";
|
|
1
2
|
export function processTreeStatusOfSearchResults(results) {
|
|
2
3
|
results.forEach((item, i) => {
|
|
3
4
|
if (i > 0 &&
|
|
4
5
|
item.page &&
|
|
5
|
-
results
|
|
6
|
+
results
|
|
7
|
+
.slice(0, i)
|
|
8
|
+
.some((prev) => (prev.type === SearchDocumentType.Keywords
|
|
9
|
+
? prev.page
|
|
10
|
+
: prev.document) === item.page)) {
|
|
6
11
|
if (i < results.length - 1 && results[i + 1].page === item.page) {
|
|
7
12
|
item.isInterOfTree = true;
|
|
8
13
|
}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
import { SearchDocumentType, } from "../../shared/interfaces";
|
|
1
2
|
export function sortSearchResults(results) {
|
|
2
3
|
results.forEach((item, index) => {
|
|
3
4
|
item.index = index;
|
|
4
5
|
});
|
|
5
|
-
// Put search results of headings
|
|
6
|
+
// Put search results of headings/contents/descriptions just after
|
|
6
7
|
// their belonged page's title, if existed.
|
|
7
8
|
results.sort((a, b) => {
|
|
8
|
-
let aPageIndex = a.type
|
|
9
|
+
let aPageIndex = (a.type === SearchDocumentType.Heading ||
|
|
10
|
+
a.type === SearchDocumentType.Content ||
|
|
11
|
+
a.type === SearchDocumentType.Description) &&
|
|
12
|
+
a.page
|
|
9
13
|
? results.findIndex((item) => item.document === a.page)
|
|
10
14
|
: a.index;
|
|
11
|
-
let bPageIndex = b.type
|
|
15
|
+
let bPageIndex = (b.type === SearchDocumentType.Heading ||
|
|
16
|
+
b.type === SearchDocumentType.Content ||
|
|
17
|
+
b.type === SearchDocumentType.Description) &&
|
|
18
|
+
b.page
|
|
12
19
|
? results.findIndex((item) => item.document === b.page)
|
|
13
20
|
: b.index;
|
|
14
21
|
if (aPageIndex === -1) {
|
|
@@ -18,13 +25,8 @@ export function sortSearchResults(results) {
|
|
|
18
25
|
bPageIndex = b.index;
|
|
19
26
|
}
|
|
20
27
|
if (aPageIndex === bPageIndex) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
if (b.type === 0) {
|
|
25
|
-
return 1;
|
|
26
|
-
}
|
|
27
|
-
return a.index - b.index;
|
|
28
|
+
const diff = (b.type === 0 ? 1 : 0) - (a.type === 0 ? 1 : 0);
|
|
29
|
+
return diff === 0 ? a.index - b.index : diff;
|
|
28
30
|
}
|
|
29
31
|
return aPageIndex - bPageIndex;
|
|
30
32
|
});
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export var SearchDocumentType;
|
|
2
|
+
(function (SearchDocumentType) {
|
|
3
|
+
SearchDocumentType[SearchDocumentType["Title"] = 0] = "Title";
|
|
4
|
+
SearchDocumentType[SearchDocumentType["Heading"] = 1] = "Heading";
|
|
5
|
+
SearchDocumentType[SearchDocumentType["Description"] = 2] = "Description";
|
|
6
|
+
SearchDocumentType[SearchDocumentType["Keywords"] = 3] = "Keywords";
|
|
7
|
+
SearchDocumentType[SearchDocumentType["Content"] = 4] = "Content";
|
|
8
|
+
})(SearchDocumentType || (SearchDocumentType = {}));
|
|
@@ -8,6 +8,8 @@ const HEADINGS = "h1, h2, h3";
|
|
|
8
8
|
function parseDocument($) {
|
|
9
9
|
const $pageTitle = $("article h1").first();
|
|
10
10
|
const pageTitle = $pageTitle.text();
|
|
11
|
+
const description = $("meta[name='description']").attr("content") || "";
|
|
12
|
+
const keywords = $("meta[name='keywords']").attr("content") || "";
|
|
11
13
|
const sections = [];
|
|
12
14
|
const breadcrumb = [];
|
|
13
15
|
const navbarActiveItem = $(".navbar__link--active");
|
|
@@ -82,6 +84,6 @@ function parseDocument($) {
|
|
|
82
84
|
content,
|
|
83
85
|
});
|
|
84
86
|
});
|
|
85
|
-
return { pageTitle, sections, breadcrumb };
|
|
87
|
+
return { pageTitle, description, keywords, sections, breadcrumb };
|
|
86
88
|
}
|
|
87
89
|
exports.parseDocument = parseDocument;
|
|
@@ -10,12 +10,16 @@ function parsePage($, url) {
|
|
|
10
10
|
$pageTitle = $("title");
|
|
11
11
|
}
|
|
12
12
|
const pageTitle = $pageTitle.text();
|
|
13
|
+
const description = $("meta[name='description']").attr("content") || "";
|
|
14
|
+
const keywords = $("meta[name='keywords']").attr("content") || "";
|
|
13
15
|
const $main = $("main");
|
|
14
16
|
if ($main.length === 0) {
|
|
15
17
|
(0, debug_1.debugWarn)("page has no <main>, therefore no content was indexed for this page %o", url);
|
|
16
18
|
}
|
|
17
19
|
return {
|
|
18
20
|
pageTitle,
|
|
21
|
+
description,
|
|
22
|
+
keywords,
|
|
19
23
|
sections: [
|
|
20
24
|
{
|
|
21
25
|
title: pageTitle,
|
|
@@ -16,8 +16,16 @@ function scanDocuments(DocInfoWithFilePathList, config) {
|
|
|
16
16
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
17
17
|
const titleDocuments = [];
|
|
18
18
|
const headingDocuments = [];
|
|
19
|
+
const descriptionDocuments = [];
|
|
20
|
+
const keywordsDocuments = [];
|
|
19
21
|
const contentDocuments = [];
|
|
20
|
-
const allDocuments = [
|
|
22
|
+
const allDocuments = [
|
|
23
|
+
titleDocuments,
|
|
24
|
+
headingDocuments,
|
|
25
|
+
descriptionDocuments,
|
|
26
|
+
keywordsDocuments,
|
|
27
|
+
contentDocuments,
|
|
28
|
+
];
|
|
21
29
|
yield Promise.all(DocInfoWithFilePathList.map(({ filePath, url, type }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
22
30
|
(0, debug_1.debugVerbose)(`parsing %s file %o of %o`, type, path_1.default.relative(process.cwd(), filePath), url);
|
|
23
31
|
const html = yield readFileAsync(filePath, { encoding: "utf8" });
|
|
@@ -26,7 +34,7 @@ function scanDocuments(DocInfoWithFilePathList, config) {
|
|
|
26
34
|
// Unlisted content
|
|
27
35
|
return;
|
|
28
36
|
}
|
|
29
|
-
const { pageTitle, sections, breadcrumb } = parsed;
|
|
37
|
+
const { pageTitle, description, keywords, sections, breadcrumb } = parsed;
|
|
30
38
|
const titleId = getNextDocId();
|
|
31
39
|
titleDocuments.push({
|
|
32
40
|
i: titleId,
|
|
@@ -34,6 +42,24 @@ function scanDocuments(DocInfoWithFilePathList, config) {
|
|
|
34
42
|
u: url,
|
|
35
43
|
b: breadcrumb,
|
|
36
44
|
});
|
|
45
|
+
if (description) {
|
|
46
|
+
descriptionDocuments.push({
|
|
47
|
+
i: titleId,
|
|
48
|
+
t: description,
|
|
49
|
+
s: pageTitle,
|
|
50
|
+
u: url,
|
|
51
|
+
p: titleId,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (keywords) {
|
|
55
|
+
keywordsDocuments.push({
|
|
56
|
+
i: titleId,
|
|
57
|
+
t: keywords,
|
|
58
|
+
s: pageTitle,
|
|
59
|
+
u: url,
|
|
60
|
+
p: titleId,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
37
63
|
for (const section of sections) {
|
|
38
64
|
if (section.title !== pageTitle) {
|
|
39
65
|
headingDocuments.push({
|
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SearchDocumentType = void 0;
|
|
4
|
+
var SearchDocumentType;
|
|
5
|
+
(function (SearchDocumentType) {
|
|
6
|
+
SearchDocumentType[SearchDocumentType["Title"] = 0] = "Title";
|
|
7
|
+
SearchDocumentType[SearchDocumentType["Heading"] = 1] = "Heading";
|
|
8
|
+
SearchDocumentType[SearchDocumentType["Description"] = 2] = "Description";
|
|
9
|
+
SearchDocumentType[SearchDocumentType["Keywords"] = 3] = "Keywords";
|
|
10
|
+
SearchDocumentType[SearchDocumentType["Content"] = 4] = "Content";
|
|
11
|
+
})(SearchDocumentType || (exports.SearchDocumentType = SearchDocumentType = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easyops-cn/docusaurus-search-local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "An offline/local search plugin for Docusaurus v3",
|
|
5
5
|
"repository": "https://github.com/easyops-cn/docusaurus-search-local",
|
|
6
6
|
"homepage": "https://github.com/easyops-cn/docusaurus-search-local",
|