Skip to content

Commit 42f2548

Browse files
authored
feat(search): use dexie.js instead of localStorage (#2464)
1 parent 7cbd532 commit 42f2548

File tree

4 files changed

+66
-29
lines changed

4 files changed

+66
-29
lines changed

package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"*.js": "eslint --fix"
3737
},
3838
"dependencies": {
39+
"dexie": "^4.0.8",
3940
"medium-zoom": "^1.1.0",
4041
"opencollective-postinstall": "^2.0.2",
4142
"prismjs": "^1.29.0",

src/plugins/search/search.js

+59-24
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,33 @@ import {
22
getAndRemoveConfig,
33
getAndRemoveDocsifyIgnoreConfig,
44
} from '../../core/render/utils.js';
5+
import Dexie from 'dexie';
6+
7+
let INDEXES = {};
8+
9+
const db = new Dexie('docsify');
10+
db.version(1).stores({
11+
search: 'slug, title, body, path, indexKey',
12+
expires: 'key, value',
13+
});
14+
15+
async function saveData(maxAge, expireKey) {
16+
INDEXES = Object.values(INDEXES).flatMap(innerData =>
17+
Object.values(innerData),
18+
);
19+
await db.search.bulkPut(INDEXES);
20+
await db.expires.put({ key: expireKey, value: Date.now() + maxAge });
21+
}
522

6-
let INDEXS = {};
23+
async function getData(key, isExpireKey = false) {
24+
if (isExpireKey) {
25+
const item = await db.expires.get(key);
26+
return item ? item.value : 0;
27+
}
28+
29+
const item = await db.search.where({ indexKey: key }).toArray();
30+
return item ? item : null;
31+
}
732

833
const LOCAL_STORAGE = {
934
EXPIRE_KEY: 'docsify.search.expires',
@@ -73,12 +98,7 @@ function getListData(token) {
7398
return token.text;
7499
}
75100

76-
function saveData(maxAge, expireKey, indexKey) {
77-
localStorage.setItem(expireKey, Date.now() + maxAge);
78-
localStorage.setItem(indexKey, JSON.stringify(INDEXS));
79-
}
80-
81-
export function genIndex(path, content = '', router, depth) {
101+
export function genIndex(path, content = '', router, depth, indexKey) {
82102
const tokens = window.marked.lexer(content);
83103
const slugify = window.Docsify.slugify;
84104
const index = {};
@@ -101,14 +121,22 @@ export function genIndex(path, content = '', router, depth) {
101121
title = getAndRemoveDocsifyIgnoreConfig(str).content;
102122
}
103123

104-
index[slug] = { slug, title: title, body: '' };
124+
index[slug] = {
125+
slug,
126+
title: title,
127+
body: '',
128+
path: path,
129+
indexKey: indexKey,
130+
};
105131
} else {
106132
if (tokenIndex === 0) {
107133
slug = router.toURL(path);
108134
index[slug] = {
109135
slug,
110136
title: path !== '/' ? path.slice(1) : 'Home Page',
111137
body: token.text || '',
138+
path: path,
139+
indexKey: indexKey,
112140
};
113141
}
114142

@@ -129,6 +157,9 @@ export function genIndex(path, content = '', router, depth) {
129157

130158
index[slug].body = token.text || '';
131159
}
160+
161+
index[slug].path = path;
162+
index[slug].indexKey = indexKey;
132163
}
133164
});
134165
slugify.clear();
@@ -148,21 +179,14 @@ export function ignoreDiacriticalMarks(keyword) {
148179
*/
149180
export function search(query) {
150181
const matchingResults = [];
151-
let data = [];
152-
Object.keys(INDEXS).forEach(key => {
153-
data = [
154-
...data,
155-
...Object.keys(INDEXS[key]).map(page => INDEXS[key][page]),
156-
];
157-
});
158182

159183
query = query.trim();
160184
let keywords = query.split(/[\s\-\\/]+/);
161185
if (keywords.length !== 1) {
162186
keywords = [query, ...keywords];
163187
}
164188

165-
for (const post of data) {
189+
for (const post of INDEXES) {
166190
let matchesScore = 0;
167191
let resultStr = '';
168192
let handlePostTitle = '';
@@ -235,7 +259,7 @@ export function search(query) {
235259
return matchingResults.sort((r1, r2) => r2.score - r1.score);
236260
}
237261

238-
export function init(config, vm) {
262+
export async function init(config, vm) {
239263
const isAuto = config.paths === 'auto';
240264
const paths = isAuto ? getAllPaths(vm.router) : config.paths;
241265

@@ -269,12 +293,12 @@ export function init(config, vm) {
269293
const expireKey = resolveExpireKey(config.namespace) + namespaceSuffix;
270294
const indexKey = resolveIndexKey(config.namespace) + namespaceSuffix;
271295

272-
const isExpired = localStorage.getItem(expireKey) < Date.now();
296+
const isExpired = (await getData(expireKey, true)) < Date.now();
273297

274-
INDEXS = JSON.parse(localStorage.getItem(indexKey));
298+
INDEXES = await getData(indexKey);
275299

276300
if (isExpired) {
277-
INDEXS = {};
301+
INDEXES = {};
278302
} else if (!isAuto) {
279303
return;
280304
}
@@ -283,14 +307,25 @@ export function init(config, vm) {
283307
let count = 0;
284308

285309
paths.forEach(path => {
286-
if (INDEXS[path]) {
310+
const pathExists = Array.isArray(INDEXES)
311+
? INDEXES.some(obj => obj.path === path)
312+
: false;
313+
if (pathExists) {
287314
return count++;
288315
}
289316

290317
Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then(
291-
result => {
292-
INDEXS[path] = genIndex(path, result, vm.router, config.depth);
293-
len === ++count && saveData(config.maxAge, expireKey, indexKey);
318+
async result => {
319+
INDEXES[path] = genIndex(
320+
path,
321+
result,
322+
vm.router,
323+
config.depth,
324+
indexKey,
325+
);
326+
if (len === ++count) {
327+
await saveData(config.maxAge, expireKey);
328+
}
294329
},
295330
);
296331
});

test/integration/example.test.js

-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
2020
const docsifyInitConfig = {
2121
config: {
2222
name: 'Docsify Name',
23-
themeColor: 'red',
2423
},
2524
markdown: {
2625
coverpage: `
@@ -58,8 +57,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
5857
scriptURLs: [
5958
// docsifyInit() route
6059
'data-test-scripturls.js',
61-
// Server route
62-
'/dist/plugins/search.js',
6360
],
6461
style: `
6562
body {
@@ -76,7 +73,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
7673

7774
// Verify config options
7875
expect(typeof window.$docsify).toBe('object');
79-
expect(window.$docsify).toHaveProperty('themeColor', 'red');
8076
expect(document.querySelector('.app-name').textContent).toContain(
8177
'Docsify Name',
8278
);
@@ -101,7 +97,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () {
10197

10298
// Verify docsifyInitConfig.scriptURLs were executed
10399
expect(document.body.hasAttribute('data-test-scripturls')).toBe(true);
104-
expect(document.querySelector('.search input[type="search"]')).toBeTruthy();
105100

106101
// Verify docsifyInitConfig.script was added to the DOM
107102
expect(

0 commit comments

Comments
 (0)