@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 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 === 0;
10
- const isHeading = type === 1;
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}">${isTitle ? iconTitle : isHeading ? iconHeading : iconContent}</span>`;
22
+ const icon = `<span class="${styles.hitIcon}">${isTitleRelated ? iconTitle : isHeading ? iconHeading : iconContent}</span>`;
20
23
  const wrapped = [
21
- `<span class="${styles.hitTitle}">${highlightStemmed(document.t, getStemmedPositions(metadata, "t"), tokens)}</span>`,
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
- ? (page.b ?? [])
27
- .concat(page.t)
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 (!isTitle) {
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 } = searchContext || useAllContextsWithNoSearchContext
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 === 0;
148
- const isContent = type === 2;
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 (!isTitle) {
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 !== 0 &&
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.some((prev) => prev.document === item.page)) {
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 and contents just after
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 > 0 && a.page
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 > 0 && b.page
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
- if (a.type === 0) {
22
- return -1;
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 = [titleDocuments, headingDocuments, contentDocuments];
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.40.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",