@easyops-cn/docusaurus-search-local 0.32.1 → 0.33.1

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.33.1](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.33.0...v0.33.1) (2022-10-17)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * refine search context, support `hideSearchBarWithNoSearchContext` ([38908ed](https://github.com/easyops-cn/docusaurus-search-local/commit/38908edd8dea6bbf5a38f2ea225f9a9530b406ed))
11
+
12
+ ## [0.33.0](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.32.1...v0.33.0) (2022-10-16)
13
+
14
+
15
+ ### Features
16
+
17
+ * support search context by paths ([aa6de9a](https://github.com/easyops-cn/docusaurus-search-local/commit/aa6de9a2890cad2570bd40f41cccb0626a5163d6))
18
+
5
19
  ## [0.32.1](https://github.com/easyops-cn/docusaurus-search-local/compare/v0.32.0...v0.32.1) (2022-10-08)
6
20
 
7
21
 
@@ -10,7 +10,7 @@ import { fetchIndexes } from "./fetchIndexes";
10
10
  import { SearchSourceFactory } from "../../utils/SearchSourceFactory";
11
11
  import { SuggestionTemplate } from "./SuggestionTemplate";
12
12
  import { EmptyTemplate } from "./EmptyTemplate";
13
- import { searchResultLimits, Mark, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, } from "../../utils/proxiedGenerated";
13
+ import { searchResultLimits, Mark, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, } from "../../utils/proxiedGenerated";
14
14
  import LoadingRing from "../LoadingRing/LoadingRing";
15
15
  import styles from "./SearchBar.module.css";
16
16
  async function fetchAutoCompleteJS() {
@@ -59,22 +59,47 @@ export default function SearchBar({ handleSearchBarToggle, }) {
59
59
  const history = useHistory();
60
60
  const location = useLocation();
61
61
  const searchBarRef = useRef(null);
62
- const indexState = useRef("empty"); // empty, loaded, done
62
+ const indexStateMap = useRef(new Map());
63
63
  // Should the input be focused after the index is loaded?
64
64
  const focusAfterIndexLoaded = useRef(false);
65
65
  const [loading, setLoading] = useState(false);
66
66
  const [inputChanged, setInputChanged] = useState(false);
67
67
  const [inputValue, setInputValue] = useState("");
68
68
  const search = useRef(null);
69
+ const prevSearchContext = useRef("");
70
+ const [searchContext, setSearchContext] = useState("");
71
+ useEffect(() => {
72
+ if (!Array.isArray(searchContextByPaths)) {
73
+ return;
74
+ }
75
+ let nextSearchContext = "";
76
+ if (location.pathname.startsWith(versionUrl)) {
77
+ const uri = location.pathname.substring(versionUrl.length);
78
+ const matchedPath = searchContextByPaths.find((path) => uri === path || uri.startsWith(`${path}/`));
79
+ if (matchedPath) {
80
+ nextSearchContext = matchedPath;
81
+ }
82
+ }
83
+ if (prevSearchContext.current !== nextSearchContext) {
84
+ // Reset index state map once search context is changed.
85
+ indexStateMap.current.delete(nextSearchContext);
86
+ prevSearchContext.current = nextSearchContext;
87
+ }
88
+ setSearchContext(nextSearchContext);
89
+ }, [location.pathname, versionUrl]);
90
+ const hidden = !!hideSearchBarWithNoSearchContext &&
91
+ Array.isArray(searchContextByPaths) &&
92
+ searchContext === "";
69
93
  const loadIndex = useCallback(async () => {
70
- if (indexState.current !== "empty") {
94
+ if (hidden || indexStateMap.current.get(searchContext)) {
71
95
  // Do not load the index (again) if its already loaded or in the process of being loaded.
72
96
  return;
73
97
  }
74
- indexState.current = "loading";
98
+ indexStateMap.current.set(searchContext, "loading");
99
+ search.current?.autocomplete.destroy();
75
100
  setLoading(true);
76
101
  const [{ wrappedIndexes, zhDictionary }, autoComplete] = await Promise.all([
77
- fetchIndexes(versionUrl),
102
+ fetchIndexes(versionUrl, searchContext),
78
103
  fetchAutoCompleteJS(),
79
104
  ]);
80
105
  search.current = autoComplete(searchBarRef.current, {
@@ -106,7 +131,9 @@ export default function SearchBar({ handleSearchBarToggle, }) {
106
131
  return;
107
132
  }
108
133
  const a = document.createElement("a");
109
- const url = `${baseUrl}search?q=${encodeURIComponent(query)}`;
134
+ const url = `${baseUrl}search?q=${encodeURIComponent(query)}${Array.isArray(searchContextByPaths)
135
+ ? `&ctx=${encodeURIComponent(searchContext)}`
136
+ : ""}`;
110
137
  a.href = url;
111
138
  a.textContent = translate({
112
139
  id: "theme.SearchBar.seeAll",
@@ -115,7 +142,7 @@ export default function SearchBar({ handleSearchBarToggle, }) {
115
142
  a.addEventListener("click", (e) => {
116
143
  if (!e.ctrlKey && !e.metaKey) {
117
144
  e.preventDefault();
118
- search.current.autocomplete.close();
145
+ search.current?.autocomplete.close();
119
146
  history.push(url);
120
147
  }
121
148
  });
@@ -145,16 +172,16 @@ export default function SearchBar({ handleSearchBarToggle, }) {
145
172
  .on("autocomplete:closed", () => {
146
173
  searchBarRef.current?.blur();
147
174
  });
148
- indexState.current = "done";
175
+ indexStateMap.current.set(searchContext, "done");
149
176
  setLoading(false);
150
177
  if (focusAfterIndexLoaded.current) {
151
178
  const input = searchBarRef.current;
152
179
  if (input.value) {
153
- search.current.autocomplete.open();
180
+ search.current?.autocomplete.open();
154
181
  }
155
182
  input.focus();
156
183
  }
157
- }, [baseUrl, versionUrl, history]);
184
+ }, [hidden, searchContext, versionUrl, baseUrl, history]);
158
185
  useEffect(() => {
159
186
  if (!Mark) {
160
187
  return;
@@ -239,7 +266,7 @@ export default function SearchBar({ handleSearchBarToggle, }) {
239
266
  return (<div className={clsx("navbar__search", styles.searchBarContainer, {
240
267
  [styles.searchIndexLoading]: loading && inputChanged,
241
268
  [styles.focused]: focused,
242
- })}>
269
+ })} hidden={hidden}>
243
270
  <input placeholder={translate({
244
271
  id: "theme.SearchBar.label",
245
272
  message: "Search",
@@ -1,8 +1,18 @@
1
1
  import lunr from "lunr";
2
2
  import { searchIndexUrl } from "../../utils/proxiedGenerated";
3
- export async function fetchIndexes(baseUrl) {
3
+ const cache = new Map();
4
+ export function fetchIndexes(baseUrl, searchContext) {
5
+ const cacheKey = `${baseUrl}${searchContext}`;
6
+ let promise = cache.get(cacheKey);
7
+ if (!promise) {
8
+ promise = legacyFetchIndexes(baseUrl, searchContext);
9
+ cache.set(cacheKey, promise);
10
+ }
11
+ return promise;
12
+ }
13
+ export async function legacyFetchIndexes(baseUrl, searchContext) {
4
14
  if (process.env.NODE_ENV === "production") {
5
- const json = (await (await fetch(`${baseUrl}${searchIndexUrl}`)).json());
15
+ const json = (await (await fetch(`${baseUrl}${searchIndexUrl.replace("{dir}", searchContext ? `-${searchContext.replace(/\//g, "-")}` : "")}`)).json());
6
16
  const wrappedIndexes = json.map(({ documents, index }, type) => ({
7
17
  type: type,
8
18
  documents,
@@ -48,7 +48,7 @@ function SearchPageContent() {
48
48
  }
49
49
  }
50
50
  const { selectMessage } = usePluralForm();
51
- const { searchValue, updateSearchPath } = useSearchQuery();
51
+ const { searchValue, searchContext, updateSearchPath } = useSearchQuery();
52
52
  const [searchQuery, setSearchQuery] = useState(searchValue);
53
53
  const [searchSource, setSearchSource] = useState();
54
54
  const [searchResults, setSearchResults] = useState();
@@ -90,11 +90,11 @@ function SearchPageContent() {
90
90
  }, [searchValue]);
91
91
  useEffect(() => {
92
92
  async function doFetchIndexes() {
93
- const { wrappedIndexes, zhDictionary } = await fetchIndexes(versionUrl);
93
+ const { wrappedIndexes, zhDictionary } = await fetchIndexes(versionUrl, searchContext);
94
94
  setSearchSource(() => SearchSourceFactory(wrappedIndexes, zhDictionary, 100));
95
95
  }
96
96
  doFetchIndexes();
97
- }, [versionUrl]);
97
+ }, [searchContext, versionUrl]);
98
98
  return (<React.Fragment>
99
99
  <Head>
100
100
  {/*
@@ -7,15 +7,19 @@
7
7
  import { useHistory, useLocation } from "@docusaurus/router";
8
8
  import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";
9
9
  import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
10
+ import { searchContextByPaths, } from "../../utils/proxiedGenerated";
10
11
  const SEARCH_PARAM_QUERY = "q";
12
+ const SEARCH_PARAM_CONTEXT = "ctx";
11
13
  function useSearchQuery() {
12
14
  const history = useHistory();
13
15
  const location = useLocation();
14
16
  const { siteConfig: { baseUrl }, } = useDocusaurusContext();
17
+ const params = ExecutionEnvironment.canUseDOM ? new URLSearchParams(location.search) : null;
18
+ const searchValue = params?.get(SEARCH_PARAM_QUERY) || "";
19
+ const searchContext = params?.get(SEARCH_PARAM_CONTEXT) || "";
15
20
  return {
16
- searchValue: (ExecutionEnvironment.canUseDOM &&
17
- new URLSearchParams(location.search).get(SEARCH_PARAM_QUERY)) ||
18
- "",
21
+ searchValue,
22
+ searchContext,
19
23
  updateSearchPath: (searchValue) => {
20
24
  const searchParams = new URLSearchParams(location.search);
21
25
  if (searchValue) {
@@ -29,8 +33,10 @@ function useSearchQuery() {
29
33
  });
30
34
  },
31
35
  generateSearchPageLink: (searchValue) => {
36
+ const searchParams = new URLSearchParams(location.search);
37
+ const searchContext = searchParams.get(SEARCH_PARAM_CONTEXT) || "";
32
38
  // Refer to https://github.com/facebook/docusaurus/pull/2838
33
- return `${baseUrl}search?q=${encodeURIComponent(searchValue)}`;
39
+ return `${baseUrl}search?q=${encodeURIComponent(searchValue)}${Array.isArray(searchContextByPaths) ? `&ctx=${encodeURIComponent(searchContext)}` : ""}`;
34
40
  },
35
41
  };
36
42
  }
@@ -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 searchIndexUrl = "search-index.json?_=abc";
4
+ export const searchIndexUrl = "search-index{dir}.json?_=abc";
5
5
  export const searchResultLimits = 8;
6
6
  export const searchResultContextMaxLength = 50;
7
7
  export const explicitSearchResultPath = false;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -6,7 +6,7 @@ const fs_1 = tslib_1.__importDefault(require("fs"));
6
6
  const path_1 = tslib_1.__importDefault(require("path"));
7
7
  const getIndexHash_1 = require("./getIndexHash");
8
8
  function generate(config, dir) {
9
- const { language, removeDefaultStopWordFilter, removeDefaultStemmer, highlightSearchTermsOnTargetPage, searchResultLimits, searchResultContextMaxLength, explicitSearchResultPath, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, } = config;
9
+ const { language, removeDefaultStopWordFilter, removeDefaultStemmer, highlightSearchTermsOnTargetPage, searchResultLimits, searchResultContextMaxLength, explicitSearchResultPath, searchBarShortcut, searchBarShortcutHint, searchBarPosition, docsPluginIdForPreferredVersion, indexDocs, searchContextByPaths, hideSearchBarWithNoSearchContext, } = config;
10
10
  const indexHash = (0, getIndexHash_1.getIndexHash)(config);
11
11
  const contents = [
12
12
  `import lunr from ${JSON.stringify(require.resolve("lunr"))};`,
@@ -35,11 +35,11 @@ function generate(config, dir) {
35
35
  else {
36
36
  contents.push("export const Mark = null;");
37
37
  }
38
- let searchIndexFilename = "search-index.json";
38
+ let searchIndexFilename = "search-index{dir}.json";
39
39
  let searchIndexQuery = "";
40
40
  if (indexHash) {
41
41
  if (config.hashed === "filename") {
42
- searchIndexFilename = `search-index-${indexHash}.json`;
42
+ searchIndexFilename = `search-index{dir}-${indexHash}.json`;
43
43
  }
44
44
  else {
45
45
  searchIndexQuery = `?_=${indexHash}`;
@@ -56,6 +56,10 @@ function generate(config, dir) {
56
56
  ? "undefined"
57
57
  : JSON.stringify(docsPluginIdForPreferredVersion)};`);
58
58
  contents.push(`export const indexDocs = ${JSON.stringify(indexDocs)};`);
59
+ contents.push(`export const searchContextByPaths = ${JSON.stringify(Array.isArray(searchContextByPaths) && searchContextByPaths.length > 0
60
+ ? searchContextByPaths
61
+ : null)};`);
62
+ contents.push(`export const hideSearchBarWithNoSearchContext = ${JSON.stringify(!!hideSearchBarWithNoSearchContext)};`);
59
63
  fs_1.default.writeFileSync(path_1.default.join(dir, "generated.js"), contents.join("\n"));
60
64
  return searchIndexFilename;
61
65
  }
@@ -20,10 +20,55 @@ function postBuildFactory(config, searchIndexFilename) {
20
20
  // Give every index entry a unique id so that the index does not need to store long URLs.
21
21
  const allDocuments = yield (0, scanDocuments_1.scanDocuments)(versionData.paths);
22
22
  (0, debug_1.debugInfo)("building index");
23
- const searchIndex = (0, buildIndex_1.buildIndex)(allDocuments, config);
24
- (0, debug_1.debugInfo)("writing index to disk");
25
- yield writeFileAsync(path_1.default.join(versionData.outDir, searchIndexFilename), JSON.stringify(searchIndex), { encoding: "utf8" });
26
- (0, debug_1.debugInfo)("index written to disk successfully!");
23
+ const docsByDirMap = new Map();
24
+ const { searchContextByPaths, hideSearchBarWithNoSearchContext } = config;
25
+ if (searchContextByPaths) {
26
+ const { baseUrl } = buildData;
27
+ const rootAllDocs = [];
28
+ if (!hideSearchBarWithNoSearchContext) {
29
+ docsByDirMap.set("", rootAllDocs);
30
+ }
31
+ let docIndex = 0;
32
+ for (const documents of allDocuments) {
33
+ rootAllDocs[docIndex] = [];
34
+ for (const doc of documents) {
35
+ if (doc.u.startsWith(baseUrl)) {
36
+ const uri = doc.u.substring(baseUrl.length);
37
+ const matchedPath = searchContextByPaths.find((path) => uri === path || uri.startsWith(`${path}/`));
38
+ if (matchedPath) {
39
+ let dirAllDocs = docsByDirMap.get(matchedPath);
40
+ if (!dirAllDocs) {
41
+ dirAllDocs = [];
42
+ docsByDirMap.set(matchedPath, dirAllDocs);
43
+ }
44
+ let dirDocs = dirAllDocs[docIndex];
45
+ if (!dirDocs) {
46
+ dirAllDocs[docIndex] = dirDocs = [];
47
+ }
48
+ dirDocs.push(doc);
49
+ continue;
50
+ }
51
+ }
52
+ rootAllDocs[docIndex].push(doc);
53
+ }
54
+ docIndex++;
55
+ }
56
+ for (const [k, v] of docsByDirMap) {
57
+ const docsNotEmpty = v.filter((d) => !!d);
58
+ if (docsNotEmpty.length < v.length) {
59
+ docsByDirMap.set(k, docsNotEmpty);
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ docsByDirMap.set("", allDocuments);
65
+ }
66
+ for (const [k, allDocs] of docsByDirMap) {
67
+ const searchIndex = (0, buildIndex_1.buildIndex)(allDocs, config);
68
+ (0, debug_1.debugInfo)(`writing index (/${k}) to disk`);
69
+ yield writeFileAsync(path_1.default.join(versionData.outDir, searchIndexFilename.replace("{dir}", k === "" ? "" : `-${k.replace(/\//g, "-")}`)), JSON.stringify(searchIndex), { encoding: "utf8" });
70
+ (0, debug_1.debugInfo)(`index (/${k}) written to disk successfully!`);
71
+ }
27
72
  }
28
73
  });
29
74
  };
@@ -28,6 +28,8 @@ const schema = utils_validation_1.Joi.object({
28
28
  docsPluginIdForPreferredVersion: utils_validation_1.Joi.string(),
29
29
  zhUserDict: utils_validation_1.Joi.string(),
30
30
  zhUserDictPath: utils_validation_1.Joi.string(),
31
+ searchContextByPaths: utils_validation_1.Joi.array().items(utils_validation_1.Joi.string()),
32
+ hideSearchBarWithNoSearchContext: utils_validation_1.Joi.boolean().default(false),
31
33
  });
32
34
  function validateOptions({ options, validate, }) {
33
35
  return validate(schema, options || {});
@@ -0,0 +1,152 @@
1
+ export interface PluginOptions {
2
+ /**
3
+ * Whether to index docs.
4
+ *
5
+ * @default true
6
+ */
7
+ indexDocs?: boolean;
8
+ /**
9
+ * Whether to index blog.
10
+ *
11
+ * @default true
12
+ */
13
+ indexBlog?: boolean;
14
+ /**
15
+ * Whether to index pages.
16
+ *
17
+ * @default false
18
+ */
19
+ indexPages?: boolean;
20
+ /**
21
+ * Base route path(s) of docs. Slash at beginning is not required.
22
+ *
23
+ * Note: for [docs-only mode](https://docusaurus.io/docs/docs-introduction#docs-only-mode),
24
+ * this needs to be the same as `routeBasePath` in your `@docusaurus/preset-classic` config e.g., `"/"`.
25
+ *
26
+ * @default "/docs"
27
+ */
28
+ docsRouteBasePath?: string | string[];
29
+ /**
30
+ * Base route path(s) of blog. Slash at beginning is not required.
31
+ *
32
+ * @default "/blog"
33
+ */
34
+ blogRouteBasePath?: string | string[];
35
+ /**
36
+ * All [lunr-languages](https://github.com/MihaiValentin/lunr-languages) supported languages, + `zh` 🔥.
37
+ */
38
+ language?: string | string[];
39
+ /**
40
+ * Whether to add a hashed query when fetching index (based on the content hash of all indexed
41
+ * `*.md` in `docsDir` and `blogDir` if applicable). Setting to `"filename"` will save hash in
42
+ * filename instead of query.
43
+ *
44
+ * @default false
45
+ */
46
+ hashed?: boolean | "query" | "filename";
47
+ /**
48
+ * The dir(s) of docs to get the content hash, it's relative to the dir of your project.
49
+ *
50
+ * @default "docs"
51
+ */
52
+ docsDir?: string | string[];
53
+ /**
54
+ * The dir(s) of blog to get the content hash, it's relative to the dir of your project.
55
+ *
56
+ * @default "blog"
57
+ */
58
+ blogDir?: string | string[];
59
+ /**
60
+ * When you're using multi-instance of docs, set the docs plugin id which you'd like to
61
+ * check the preferred version with, for the search index.
62
+ */
63
+ docsPluginIdForPreferredVersion?: string;
64
+ /**
65
+ * Sometimes people (E.g., us) want to keep the English stop words as indexed, since they
66
+ * maybe are relevant in programming docs.
67
+ */
68
+ removeDefaultStopWordFilter?: boolean;
69
+ /**
70
+ * Enable this if you want to be able to search for any partial word at the cost of search performance.
71
+ *
72
+ * @default false
73
+ */
74
+ removeDefaultStemmer?: boolean;
75
+ /**
76
+ * Highlight search terms on target page.
77
+ *
78
+ * @default false
79
+ */
80
+ highlightSearchTermsOnTargetPage?: boolean;
81
+ /**
82
+ * Limit the search results.
83
+ *
84
+ * @default 8
85
+ */
86
+ searchResultLimits?: number;
87
+ /**
88
+ * Set the max length of characters of each search result to show.
89
+ *
90
+ * @default 50
91
+ */
92
+ searchResultContextMaxLength?: number;
93
+ /**
94
+ * Whether an explicit path to a heading should be presented on a suggestion template.
95
+ *
96
+ * @default false
97
+ */
98
+ explicitSearchResultPath?: boolean;
99
+ /**
100
+ * Set the match rules to ignore some routes. Put a string if you want an exact match,
101
+ * or put a regex if you want a partial match. Note: without the website base url.
102
+ *
103
+ * @default []
104
+ */
105
+ ignoreFiles?: string | RegExp | (string | RegExp)[];
106
+ /**
107
+ * Whether to enable keyboard shortcut to focus in search bar.
108
+ *
109
+ * @default true
110
+ */
111
+ searchBarShortcut?: boolean;
112
+ /**
113
+ * Whether to show keyboard shortcut hint in search bar. Disable it if you need to
114
+ * hide the hint while shortcut is still enabled.
115
+ *
116
+ * @default true
117
+ */
118
+ searchBarShortcutHint?: boolean;
119
+ /**
120
+ * The side of the navbar the search bar should appear on. By default,
121
+ * it will try to autodetect based on your docusaurus config according
122
+ * to [the docs](https://docusaurus.io/docs/api/themes/configuration#navbar-search).
123
+ *
124
+ * @default "auto"
125
+ */
126
+ searchBarPosition?: "auto" | "left" | "right";
127
+ /**
128
+ * Provide your custom dict for language of zh,
129
+ * [see here](https://github.com/fxsjy/jieba#%E8%BD%BD%E5%85%A5%E8%AF%8D%E5%85%B8)
130
+ */
131
+ zhUserDict?: string;
132
+ /**
133
+ * Provide the file path to your custom dict for language of zh,
134
+ * E.g.: `path.resolve("./src/zh-dict.txt")`
135
+ */
136
+ zhUserDictPath?: string;
137
+ /**
138
+ * Provide an list of sub-paths as separate search context, E.g.: `["docs", "community", "legacy/resources"]`.
139
+ * It will create multiple search indexes by these paths.
140
+ */
141
+ searchContextByPaths?: string[];
142
+ /**
143
+ * Whether to hide the search bar when no search context was matched.
144
+ *
145
+ * By default, if `searchContextByPaths` is set, pages which are not matched with it will be considered
146
+ * as with a search context of ROOT. By setting `hideSearchBarWithNoSearchContext` to false, these pages
147
+ * will be considered as with NO search context, and the search bar will be hidden.
148
+ *
149
+ * @default false
150
+ */
151
+ hideSearchBarWithNoSearchContext?: boolean;
152
+ }
package/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "@easyops-cn/docusaurus-search-local",
3
- "version": "0.32.1",
3
+ "version": "0.33.1",
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",
7
7
  "scripts": {
8
- "start": "concurrently -k -n client,server \"yarn run start:client\" \"yarn run start:server\"",
8
+ "start": "concurrently -k -n client,server,types \"yarn run start:client\" \"yarn run start:server\" \"yarn run start:types\"",
9
9
  "start:client": "tsc --watch --project tsconfig.client.json",
10
10
  "start:server": "tsc --watch --project tsconfig.server.json",
11
- "build": "rimraf dist && yarn run build:client && yarn run build:server && yarn run copy-static-files",
11
+ "start:types": "tsc --project tsconfig.types.json --watch",
12
+ "build": "rimraf dist && yarn run build:client && yarn run build:server && yarn run build:types && yarn run copy-static-files",
12
13
  "build:client": "tsc --project tsconfig.client.json",
13
14
  "build:server": "tsc --project tsconfig.server.json",
15
+ "build:types": "tsc --project tsconfig.types.json",
14
16
  "copy-static-files": "copyfiles -u 3 \"src/client/theme/**/*.css\" dist/client/client/theme && copyfiles -u 1 \"locales/*.json\" dist/locales"
15
17
  },
16
18
  "main": "dist/server/server/index.js",
19
+ "typings": "dist/types/index.d.ts",
17
20
  "files": [
18
21
  "/dist",
19
22
  "!/dist/generated.js"