@easyops-cn/docusaurus-search-local 0.29.2 → 0.30.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,27 @@
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.30.0](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.29.4...v0.30.0) (2022-07-22)
6
+
7
+
8
+ ### Features
9
+
10
+ * support saving hash in filename instead of query ([4930a88](https://github.com/easyops-cn/docusaurus-search-local/commit/4930a88779813ffcaa932a56f786066f969aff6e)), closes [#171](https://github.com/easyops-cn/docusaurus-search-local/issues/171)
11
+
12
+ ## [0.29.4](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.29.3...v0.29.4) (2022-07-22)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * fix call stack overflow with large code blocks ([e0b2cfd](https://github.com/easyops-cn/docusaurus-search-local/commit/e0b2cfd6e21f008609a85c42c0159ae25b575d6b)), closes [#164](https://github.com/easyops-cn/docusaurus-search-local/issues/164)
18
+
19
+ ## [0.29.3](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.29.2...v0.29.3) (2022-07-20)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * hide clear button on small screen when input is not focused ([c69fbf6](https://github.com/easyops-cn/docusaurus-search-local/commit/c69fbf64ff6ba11c32e63d89dada010bb1e521dc)), closes [#230](https://github.com/easyops-cn/docusaurus-search-local/issues/230)
25
+
5
26
  ## [0.29.2](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.29.1...v0.29.2) (2022-07-19)
6
27
 
7
28
 
@@ -178,12 +178,15 @@ export default function SearchBar({ handleSearchBarToggle, }) {
178
178
  search.current?.autocomplete.setVal(keywords.join(" "));
179
179
  });
180
180
  }, [location.search, location.pathname]);
181
+ const [focused, setFocused] = useState(false);
181
182
  const onInputFocus = useCallback(() => {
182
183
  focusAfterIndexLoaded.current = true;
183
184
  loadIndex();
185
+ setFocused(true);
184
186
  handleSearchBarToggle?.(true);
185
187
  }, [handleSearchBarToggle, loadIndex]);
186
188
  const onInputBlur = useCallback(() => {
189
+ setFocused(false);
187
190
  handleSearchBarToggle?.(false);
188
191
  }, [handleSearchBarToggle]);
189
192
  const onInputMouseEnter = useCallback(() => {
@@ -232,6 +235,7 @@ export default function SearchBar({ handleSearchBarToggle, }) {
232
235
  }, [location.pathname, location.search, location.hash, history]);
233
236
  return (<div className={clsx("navbar__search", styles.searchBarContainer, {
234
237
  [styles.searchIndexLoading]: loading && inputChanged,
238
+ [styles.focused]: focused,
235
239
  })}>
236
240
  <input placeholder={translate({
237
241
  id: "theme.SearchBar.label",
@@ -237,6 +237,7 @@ html[data-theme="dark"] .noResultsIcon {
237
237
  }
238
238
 
239
239
  @media (max-width: 576px) {
240
+ .searchBarContainer:not(.focused) .searchClearButton,
240
241
  .searchHintContainer {
241
242
  display: none;
242
243
  }
@@ -1,8 +1,8 @@
1
1
  import lunr from "lunr";
2
- import { indexHash } from "../../utils/proxiedGenerated";
2
+ import { searchIndexUrl } from "../../utils/proxiedGenerated";
3
3
  export async function fetchIndexes(baseUrl) {
4
4
  if (process.env.NODE_ENV === "production") {
5
- const json = (await (await fetch(`${baseUrl}search-index.json?_=${indexHash}`)).json());
5
+ const json = (await (await fetch(`${baseUrl}${searchIndexUrl}`)).json());
6
6
  const wrappedIndexes = json.map(({ documents, index }, type) => ({
7
7
  type: type,
8
8
  documents,
@@ -1,7 +1,7 @@
1
1
  export let language = ["en", "zh"];
2
2
  export let removeDefaultStopWordFilter = false;
3
3
  export let removeDefaultStemmer = false;
4
- export const indexHash = "abc";
4
+ export const searchIndexUrl = "search-index.json?_=abc";
5
5
  export const searchResultLimits = 8;
6
6
  export const searchResultContextMaxLength = 50;
7
7
  export const explicitSearchResultPath = false;
@@ -3,14 +3,11 @@ import { highlight } from "./highlight";
3
3
  import { looseTokenize } from "./looseTokenize";
4
4
  import { searchResultContextMaxLength } from "./proxiedGenerated";
5
5
  export function highlightStemmed(content, positions, tokens, maxLength = searchResultContextMaxLength) {
6
- const chunkIndexRef = {
7
- chunkIndex: -1,
8
- };
9
- const chunks = splitIntoChunks(content, positions, tokens, 0, 0, chunkIndexRef);
10
- const leadingChunks = chunks.slice(0, chunkIndexRef.chunkIndex);
11
- const firstChunk = chunks[chunkIndexRef.chunkIndex];
6
+ const { chunkIndex, chunks } = splitIntoChunks(content, positions, tokens);
7
+ const leadingChunks = chunks.slice(0, chunkIndex);
8
+ const firstChunk = chunks[chunkIndex];
12
9
  const html = [firstChunk.html];
13
- const trailingChunks = chunks.slice(chunkIndexRef.chunkIndex + 1);
10
+ const trailingChunks = chunks.slice(chunkIndex + 1);
14
11
  let currentLength = firstChunk.textLength;
15
12
  let leftPadding = 0;
16
13
  let rightPadding = 0;
@@ -54,42 +51,46 @@ export function highlightStemmed(content, positions, tokens, maxLength = searchR
54
51
  }
55
52
  return html.join("");
56
53
  }
57
- export function splitIntoChunks(content, positions, tokens, positionIndex, cursor, chunkIndexRef) {
54
+ export function splitIntoChunks(content, positions, tokens) {
58
55
  const chunks = [];
59
- const [start, length] = positions[positionIndex];
60
- if (start < cursor) {
56
+ let positionIndex = 0;
57
+ let cursor = 0;
58
+ let chunkIndex = -1;
59
+ while (positionIndex < positions.length) {
60
+ const [start, length] = positions[positionIndex];
61
61
  positionIndex += 1;
62
- if (positionIndex < positions.length) {
63
- chunks.push(...splitIntoChunks(content, positions, tokens, positionIndex, cursor));
62
+ if (start < cursor) {
63
+ continue;
64
64
  }
65
- }
66
- else {
67
65
  if (start > cursor) {
68
- chunks.push(...looseTokenize(content.substring(cursor, start)).map((token) => ({
66
+ const leadingChunks = looseTokenize(content.substring(cursor, start)).map((token) => ({
69
67
  html: escapeHtml(token),
70
68
  textLength: token.length,
71
- })));
69
+ }));
70
+ for (const item of leadingChunks) {
71
+ chunks.push(item);
72
+ }
72
73
  }
73
- if (chunkIndexRef) {
74
- chunkIndexRef.chunkIndex = chunks.length;
74
+ if (chunkIndex === -1) {
75
+ chunkIndex = chunks.length;
75
76
  }
77
+ cursor = start + length;
76
78
  chunks.push({
77
- html: highlight(content.substr(start, length), tokens, true),
79
+ html: highlight(content.substring(start, cursor), tokens, true),
78
80
  textLength: length,
79
81
  });
80
- const nextCursor = start + length;
81
- positionIndex += 1;
82
- if (positionIndex < positions.length) {
83
- chunks.push(...splitIntoChunks(content, positions, tokens, positionIndex, nextCursor));
84
- }
85
- else {
86
- if (nextCursor < content.length) {
87
- chunks.push(...looseTokenize(content.substr(nextCursor)).map((token) => ({
88
- html: escapeHtml(token),
89
- textLength: token.length,
90
- })));
91
- }
82
+ }
83
+ if (cursor < content.length) {
84
+ const trailingChunks = looseTokenize(content.substring(cursor)).map((token) => ({
85
+ html: escapeHtml(token),
86
+ textLength: token.length,
87
+ }));
88
+ for (const item of trailingChunks) {
89
+ chunks.push(item);
92
90
  }
93
91
  }
94
- return chunks;
92
+ return {
93
+ chunkIndex,
94
+ chunks,
95
+ };
95
96
  }
@@ -11,11 +11,11 @@ export function looseTokenize(content) {
11
11
  break;
12
12
  }
13
13
  if (match.index > 0) {
14
- tokens.push(text.substr(0, match.index));
14
+ tokens.push(text.substring(0, match.index));
15
15
  }
16
16
  tokens.push(match[0]);
17
17
  start += match.index + match[0].length;
18
- text = content.substr(start);
18
+ text = content.substring(start);
19
19
  }
20
20
  return tokens;
21
21
  }
@@ -14,7 +14,7 @@ function DocusaurusSearchLocalPlugin(context, options) {
14
14
  const config = (0, processPluginOptions_1.processPluginOptions)(options, context.siteDir);
15
15
  const dir = path_1.default.join(context.generatedFilesDir, PLUGIN_NAME, "default");
16
16
  fs_extra_1.default.ensureDirSync(dir);
17
- (0, generate_1.generate)(config, dir);
17
+ const searchIndexFilename = (0, generate_1.generate)(config, dir);
18
18
  const themePath = path_1.default.resolve(__dirname, "../../client/client/theme");
19
19
  const pagePath = path_1.default.join(themePath, "SearchPage/index.js");
20
20
  return {
@@ -22,7 +22,7 @@ function DocusaurusSearchLocalPlugin(context, options) {
22
22
  getThemePath() {
23
23
  return themePath;
24
24
  },
25
- postBuild: (0, postBuildFactory_1.postBuildFactory)(config),
25
+ postBuild: (0, postBuildFactory_1.postBuildFactory)(config, searchIndexFilename),
26
26
  getPathsToWatch() {
27
27
  return [pagePath];
28
28
  },
@@ -35,7 +35,18 @@ function generate(config, dir) {
35
35
  else {
36
36
  contents.push("export const Mark = null;");
37
37
  }
38
- contents.push(`export const indexHash = ${JSON.stringify(indexHash)};`);
38
+ let searchIndexFilename = "search-index.json";
39
+ let searchIndexQuery = "";
40
+ if (indexHash) {
41
+ if (config.hashed === "filename") {
42
+ searchIndexFilename = `search-index-${indexHash}.json`;
43
+ }
44
+ else {
45
+ searchIndexQuery = `?_=${indexHash}`;
46
+ }
47
+ }
48
+ const searchIndexUrl = searchIndexFilename + searchIndexQuery;
49
+ contents.push(`export const searchIndexUrl = ${JSON.stringify(searchIndexUrl)};`);
39
50
  contents.push(`export const searchResultLimits = ${JSON.stringify(searchResultLimits)};`, `export const searchResultContextMaxLength = ${JSON.stringify(searchResultContextMaxLength)};`);
40
51
  contents.push(`export const explicitSearchResultPath = ${JSON.stringify(explicitSearchResultPath)};`);
41
52
  contents.push(`export const searchBarShortcut = ${JSON.stringify(searchBarShortcut)};`);
@@ -44,5 +55,6 @@ function generate(config, dir) {
44
55
  ? "undefined"
45
56
  : JSON.stringify(docsPluginIdForPreferredVersion)};`);
46
57
  fs_1.default.writeFileSync(path_1.default.join(dir, "generated.js"), contents.join("\n"));
58
+ return searchIndexFilename;
47
59
  }
48
60
  exports.generate = generate;
@@ -50,6 +50,9 @@ function getCondensedText(element, $) {
50
50
  return element.data;
51
51
  }
52
52
  if (element.type === "tag") {
53
+ if (element.name === "br") {
54
+ return " ";
55
+ }
53
56
  const content = getText($(element).contents().get());
54
57
  if (BLOCK_TAGS.has(element.name)) {
55
58
  return " " + content + " ";
@@ -10,7 +10,7 @@ const debug_1 = require("./debug");
10
10
  const processDocInfos_1 = require("./processDocInfos");
11
11
  const scanDocuments_1 = require("./scanDocuments");
12
12
  const writeFileAsync = util_1.default.promisify(fs_1.default.writeFile);
13
- function postBuildFactory(config) {
13
+ function postBuildFactory(config, searchIndexFilename) {
14
14
  return function postBuild(buildData) {
15
15
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
16
16
  (0, debug_1.debugInfo)("gathering documents");
@@ -22,7 +22,7 @@ function postBuildFactory(config) {
22
22
  (0, debug_1.debugInfo)("building index");
23
23
  const searchIndex = (0, buildIndex_1.buildIndex)(allDocuments, config);
24
24
  (0, debug_1.debugInfo)("writing index to disk");
25
- yield writeFileAsync(path_1.default.join(versionData.outDir, "search-index.json"), JSON.stringify(searchIndex), { encoding: "utf8" });
25
+ yield writeFileAsync(path_1.default.join(versionData.outDir, searchIndexFilename), JSON.stringify(searchIndex), { encoding: "utf8" });
26
26
  (0, debug_1.debugInfo)("index written to disk successfully!");
27
27
  }
28
28
  });
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateOptions = void 0;
4
4
  const utils_validation_1 = require("@docusaurus/utils-validation");
5
5
  const isStringOrArrayOfStrings = utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.string(), utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()));
6
+ const isBooleanOrString = utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.boolean(), utils_validation_1.Joi.string());
6
7
  const isArrayOfStringsOrRegExpsOrStringOrRegExp = utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.array().items(utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.string(), utils_validation_1.Joi.object().regex())), utils_validation_1.Joi.string(), utils_validation_1.Joi.object().regex());
7
8
  const schema = utils_validation_1.Joi.object({
8
9
  indexDocs: utils_validation_1.Joi.boolean().default(true),
@@ -11,7 +12,7 @@ const schema = utils_validation_1.Joi.object({
11
12
  docsRouteBasePath: isStringOrArrayOfStrings.default(["docs"]),
12
13
  blogRouteBasePath: isStringOrArrayOfStrings.default(["blog"]),
13
14
  language: isStringOrArrayOfStrings.default(["en"]),
14
- hashed: utils_validation_1.Joi.boolean().default(false),
15
+ hashed: isBooleanOrString.default(false),
15
16
  docsDir: isStringOrArrayOfStrings.default(["docs"]),
16
17
  blogDir: isStringOrArrayOfStrings.default(["blog"]),
17
18
  removeDefaultStopWordFilter: utils_validation_1.Joi.boolean().default(false),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easyops-cn/docusaurus-search-local",
3
- "version": "0.29.2",
3
+ "version": "0.30.0",
4
4
  "description": "An offline/local search plugin for Docusaurus v2",
5
5
  "repository": "https://github.com/easyops-cn/docusaurus-search-local",
6
6
  "homepage": "https://github.com/easyops-cn/docusaurus-search-local",