diff --git a/package-lock.json b/package-lock.json index c0aa74fc7..af646d716 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "dexie": "^4.0.8", "medium-zoom": "^1.1.0", "opencollective-postinstall": "^2.0.2", "prismjs": "^1.29.0", @@ -5929,6 +5930,11 @@ "node": ">= 0.8.0" } }, + "node_modules/dexie": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.8.tgz", + "integrity": "sha512-1G6cJevS17KMDK847V3OHvK2zei899GwpDiqfEXHP1ASvme6eWJmAp9AU4s1son2TeGkWmC0g3y8ezOBPnalgQ==" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", diff --git a/package.json b/package.json index 6202ae5e9..a4a195651 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "*.js": "eslint --fix" }, "dependencies": { + "dexie": "^4.0.8", "medium-zoom": "^1.1.0", "opencollective-postinstall": "^2.0.2", "prismjs": "^1.29.0", diff --git a/src/plugins/search/search.js b/src/plugins/search/search.js index 0428f69af..31a6948b3 100644 --- a/src/plugins/search/search.js +++ b/src/plugins/search/search.js @@ -2,8 +2,33 @@ import { getAndRemoveConfig, getAndRemoveDocsifyIgnoreConfig, } from '../../core/render/utils.js'; +import Dexie from 'dexie'; + +let INDEXES = {}; + +const db = new Dexie('docsify'); +db.version(1).stores({ + search: 'slug, title, body, path, indexKey', + expires: 'key, value', +}); + +async function saveData(maxAge, expireKey) { + INDEXES = Object.values(INDEXES).flatMap(innerData => + Object.values(innerData), + ); + await db.search.bulkPut(INDEXES); + await db.expires.put({ key: expireKey, value: Date.now() + maxAge }); +} -let INDEXS = {}; +async function getData(key, isExpireKey = false) { + if (isExpireKey) { + const item = await db.expires.get(key); + return item ? item.value : 0; + } + + const item = await db.search.where({ indexKey: key }).toArray(); + return item ? item : null; +} const LOCAL_STORAGE = { EXPIRE_KEY: 'docsify.search.expires', @@ -73,12 +98,7 @@ function getListData(token) { return token.text; } -function saveData(maxAge, expireKey, indexKey) { - localStorage.setItem(expireKey, Date.now() + maxAge); - localStorage.setItem(indexKey, JSON.stringify(INDEXS)); -} - -export function genIndex(path, content = '', router, depth) { +export function genIndex(path, content = '', router, depth, indexKey) { const tokens = window.marked.lexer(content); const slugify = window.Docsify.slugify; const index = {}; @@ -101,7 +121,13 @@ export function genIndex(path, content = '', router, depth) { title = getAndRemoveDocsifyIgnoreConfig(str).content; } - index[slug] = { slug, title: title, body: '' }; + index[slug] = { + slug, + title: title, + body: '', + path: path, + indexKey: indexKey, + }; } else { if (tokenIndex === 0) { slug = router.toURL(path); @@ -109,6 +135,8 @@ export function genIndex(path, content = '', router, depth) { slug, title: path !== '/' ? path.slice(1) : 'Home Page', body: token.text || '', + path: path, + indexKey: indexKey, }; } @@ -129,6 +157,9 @@ export function genIndex(path, content = '', router, depth) { index[slug].body = token.text || ''; } + + index[slug].path = path; + index[slug].indexKey = indexKey; } }); slugify.clear(); @@ -148,13 +179,6 @@ export function ignoreDiacriticalMarks(keyword) { */ export function search(query) { const matchingResults = []; - let data = []; - Object.keys(INDEXS).forEach(key => { - data = [ - ...data, - ...Object.keys(INDEXS[key]).map(page => INDEXS[key][page]), - ]; - }); query = query.trim(); let keywords = query.split(/[\s\-,\\/]+/); @@ -162,7 +186,7 @@ export function search(query) { keywords = [query, ...keywords]; } - for (const post of data) { + for (const post of INDEXES) { let matchesScore = 0; let resultStr = ''; let handlePostTitle = ''; @@ -235,7 +259,7 @@ export function search(query) { return matchingResults.sort((r1, r2) => r2.score - r1.score); } -export function init(config, vm) { +export async function init(config, vm) { const isAuto = config.paths === 'auto'; const paths = isAuto ? getAllPaths(vm.router) : config.paths; @@ -269,12 +293,12 @@ export function init(config, vm) { const expireKey = resolveExpireKey(config.namespace) + namespaceSuffix; const indexKey = resolveIndexKey(config.namespace) + namespaceSuffix; - const isExpired = localStorage.getItem(expireKey) < Date.now(); + const isExpired = (await getData(expireKey, true)) < Date.now(); - INDEXS = JSON.parse(localStorage.getItem(indexKey)); + INDEXES = await getData(indexKey); if (isExpired) { - INDEXS = {}; + INDEXES = {}; } else if (!isAuto) { return; } @@ -283,14 +307,25 @@ export function init(config, vm) { let count = 0; paths.forEach(path => { - if (INDEXS[path]) { + const pathExists = Array.isArray(INDEXES) + ? INDEXES.some(obj => obj.path === path) + : false; + if (pathExists) { return count++; } Docsify.get(vm.router.getFile(path), false, vm.config.requestHeaders).then( - result => { - INDEXS[path] = genIndex(path, result, vm.router, config.depth); - len === ++count && saveData(config.maxAge, expireKey, indexKey); + async result => { + INDEXES[path] = genIndex( + path, + result, + vm.router, + config.depth, + indexKey, + ); + if (len === ++count) { + await saveData(config.maxAge, expireKey); + } }, ); }); diff --git a/test/integration/example.test.js b/test/integration/example.test.js index 3894ea05e..4f6b57378 100644 --- a/test/integration/example.test.js +++ b/test/integration/example.test.js @@ -20,7 +20,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () { const docsifyInitConfig = { config: { name: 'Docsify Name', - themeColor: 'red', }, markdown: { coverpage: ` @@ -58,8 +57,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () { scriptURLs: [ // docsifyInit() route 'data-test-scripturls.js', - // Server route - '/dist/plugins/search.js', ], style: ` body { @@ -76,7 +73,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () { // Verify config options expect(typeof window.$docsify).toBe('object'); - expect(window.$docsify).toHaveProperty('themeColor', 'red'); expect(document.querySelector('.app-name').textContent).toContain( 'Docsify Name', ); @@ -101,7 +97,6 @@ describe('Creating a Docsify site (integration tests in Jest)', function () { // Verify docsifyInitConfig.scriptURLs were executed expect(document.body.hasAttribute('data-test-scripturls')).toBe(true); - expect(document.querySelector('.search input[type="search"]')).toBeTruthy(); // Verify docsifyInitConfig.script was added to the DOM expect(