From 4d3d9ec995bc2cbdddb74e62cf1b7eb0fcb50a2e Mon Sep 17 00:00:00 2001 From: krmax44 Date: Sat, 17 Aug 2019 17:05:41 +0200 Subject: [PATCH 01/11] feat: saber-plugin-local-search --- packages/saber-plugin-local-search/README.md | 59 ++++++++++++ .../saber-plugin-local-search/lib/index.js | 96 +++++++++++++++++++ .../lib/saber-browser.js | 51 ++++++++++ .../saber-plugin-local-search/package.json | 17 ++++ yarn.lock | 7 +- 5 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 packages/saber-plugin-local-search/README.md create mode 100644 packages/saber-plugin-local-search/lib/index.js create mode 100644 packages/saber-plugin-local-search/lib/saber-browser.js create mode 100644 packages/saber-plugin-local-search/package.json diff --git a/packages/saber-plugin-local-search/README.md b/packages/saber-plugin-local-search/README.md new file mode 100644 index 000000000..476efc2f3 --- /dev/null +++ b/packages/saber-plugin-local-search/README.md @@ -0,0 +1,59 @@ +# saber-plugin-local-search + +Adds a hyper-fast, easy to integrate and highly customizable search to your app. + +## Install + +```bash +yarn add saber-plugin-local-search +``` + +## Usage + +In your `saber-config.yml`: + +```yml +plugins: + - resolve: saber-plugin-local-search + options: + index: + - title + - excerpt + - permalink +``` + +You can index any value from `page` or `page.attributes`. Then, create a search component: + +```html + + + + + +``` + +Pretty simple, right? diff --git a/packages/saber-plugin-local-search/lib/index.js b/packages/saber-plugin-local-search/lib/index.js new file mode 100644 index 000000000..d64152673 --- /dev/null +++ b/packages/saber-plugin-local-search/lib/index.js @@ -0,0 +1,96 @@ +const { join } = require('path') + +const ID = 'local-search' + +exports.name = ID + +let db = {} + +function getLocale(locale) { + return db[locale] +} + +exports.apply = (api, options) => { + api.browserApi.add(join(__dirname, 'saber-browser.js')) + + const index = (options && options.index) || ['title', 'excerpt', 'permalink'] + const { fs } = api.utils + + async function generateLocale(localePath) { + const pages = [] + + await Promise.all( + [...api.pages.values()].map(async page => { + if (page.draft) { + return + } + + const matchedLocalePath = api.pages.getMatchedLocalePath(page.permalink) + if (localePath !== matchedLocalePath) { + return + } + + const item = {} + + for (const element of index) { + if (element === 'content') { + item.content = await api.renderer.renderPageContent(page.permalink) + } else { + item[element] = page[element] || page.attributes[element] + } + } + + pages.push(item) + }) + ) + + return pages + } + + async function generateDatabase() { + const allLocalePaths = ['/'].concat(Object.keys(api.config.locales || {})) + + const results = await Promise.all( + allLocalePaths.map(localePath => generateLocale(localePath)) + ) + + const localDb = {} + results.forEach((result, i) => { + const locale = allLocalePaths[i] === '/' ? 'default' : allLocalePaths[i] + localDb[locale] = result + }) + + return localDb + } + + if (api.dev) { + api.hooks.onCreatePages.tapPromise(ID, async () => { + db = await generateDatabase() + }) + + api.hooks.onCreateServer.tap(ID, server => { + server.get('/_saber/search/:locale.json', (req, res) => { + const db = getLocale(req.params.locale) + if (db) { + res.writeHead(200, { + 'Content-Type': 'application/json' + }) + return res.end(JSON.stringify(db)) + } + + res.statusCode = 404 + res.end() + }) + }) + } else { + api.hooks.afterGenerate.tapPromise(ID, async () => { + const db = await generateDatabase() + for (const locale of Object.keys(db)) { + const items = db[locale] + const path = api.resolveOutDir('_saber', 'search', `${locale}.json`) + await fs.ensureDir(api.resolveOutDir('_saber', 'search')) + await fs.writeJson(path, items) + } + }) + } +} diff --git a/packages/saber-plugin-local-search/lib/saber-browser.js b/packages/saber-plugin-local-search/lib/saber-browser.js new file mode 100644 index 000000000..6b2b63805 --- /dev/null +++ b/packages/saber-plugin-local-search/lib/saber-browser.js @@ -0,0 +1,51 @@ +import FuzzySearch from 'fuzzy-search' +import debounce from 'lodash.debounce' + +let searchData +let fuzzySearch + +export default ({ Vue }) => { + Vue.prototype.$searchPages = debounce(performSearch, 50, { + leading: true, + trailing: true + }) +} + +async function performSearch(query) { + if (!searchData) { + let publicUrl = this.$siteConfig.publicUrl || '/' + publicUrl += publicUrl.endsWith('/') ? '' : '/' + const locale = this.$localePath === '/' ? 'default' : this.$localePath + const request = await fetch(`${publicUrl}_saber/search/${locale}.json`) // eslint-disable-line no-undef + const data = await request.json() + const indexes = [] + searchData = data + .map(item => { + if (item.excerpt) item.excerpt = stripTags(item.excerpt) + if (item.content) item.content = stripTags(item.content) + + Object.keys(item).forEach( + index => indexes.includes(index) || indexes.push(index) + ) + + return item + }) + .map(item => { + indexes.forEach(index => { + item[index] = item[index] || '' + }) + + return item + }) + + fuzzySearch = new FuzzySearch(searchData, indexes, { sort: true }) + } + + return fuzzySearch.search(query) +} + +const stripTags = html => { + const template = document.createElement('template') // eslint-disable-line no-undef + template.innerHTML = html + return template.content.textContent.trim() +} diff --git a/packages/saber-plugin-local-search/package.json b/packages/saber-plugin-local-search/package.json new file mode 100644 index 000000000..6f02e4f76 --- /dev/null +++ b/packages/saber-plugin-local-search/package.json @@ -0,0 +1,17 @@ +{ + "name": "saber-plugin-local-search", + "version": "0.0.1", + "description": "Add a fast, local search to your app.", + "license": "MIT", + "main": "lib/index.js", + "files": [ + "lib" + ], + "peerDependencies": { + "saber": ">=0.7.0" + }, + "dependencies": { + "fuzzy-search": "^3.0.1", + "lodash.debounce": "^4.0.8" + } +} diff --git a/yarn.lock b/yarn.lock index 0e10cba2e..9243fb20c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5562,6 +5562,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +fuzzy-search@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fuzzy-search/-/fuzzy-search-3.0.1.tgz#14a4964508a9607d6e9a88818e7ff634108260b6" + integrity sha512-rjUvzdsMlOyarm0oD5k6zVQwgvt4Tb5Xe3YdIGU+Vogw54+ueAGPUTMU2B9jfPQEie5cD11i/S9J9d+MNBSQ3Q== + g-status@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97" @@ -7735,7 +7740,7 @@ lodash.clonedeep@^4.5.0: lodash.debounce@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= lodash.get@^4.4.2: From e5247dcf34de1dd0c8b1745862676cbeecdef57d Mon Sep 17 00:00:00 2001 From: krmax44 Date: Sat, 17 Aug 2019 18:23:26 +0200 Subject: [PATCH 02/11] fix: only add valid pages --- packages/saber-plugin-local-search/lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/saber-plugin-local-search/lib/index.js b/packages/saber-plugin-local-search/lib/index.js index d64152673..f1b948ce2 100644 --- a/packages/saber-plugin-local-search/lib/index.js +++ b/packages/saber-plugin-local-search/lib/index.js @@ -21,7 +21,7 @@ exports.apply = (api, options) => { await Promise.all( [...api.pages.values()].map(async page => { - if (page.draft) { + if (page.draft || !page.type) { return } From 45170abcc204c754ec89a638d65e2c8e8d3b98d5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 19 Aug 2019 17:38:57 +0200 Subject: [PATCH 03/11] use global public url Co-Authored-By: Koyuki (EGOIST) <0x142857@gmail.com> --- packages/saber-plugin-local-search/lib/saber-browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/saber-plugin-local-search/lib/saber-browser.js b/packages/saber-plugin-local-search/lib/saber-browser.js index 6b2b63805..475092893 100644 --- a/packages/saber-plugin-local-search/lib/saber-browser.js +++ b/packages/saber-plugin-local-search/lib/saber-browser.js @@ -13,7 +13,7 @@ export default ({ Vue }) => { async function performSearch(query) { if (!searchData) { - let publicUrl = this.$siteConfig.publicUrl || '/' + let publicUrl = __PUBLIC_URL__ publicUrl += publicUrl.endsWith('/') ? '' : '/' const locale = this.$localePath === '/' ? 'default' : this.$localePath const request = await fetch(`${publicUrl}_saber/search/${locale}.json`) // eslint-disable-line no-undef From ba2a4a79cb6f4d4d993fe2f93490c7547079a15b Mon Sep 17 00:00:00 2001 From: krmax44 Date: Mon, 19 Aug 2019 17:40:59 +0200 Subject: [PATCH 04/11] shorten syntax --- .../saber-plugin-local-search/lib/saber-browser.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/saber-plugin-local-search/lib/saber-browser.js b/packages/saber-plugin-local-search/lib/saber-browser.js index 475092893..9067c9b45 100644 --- a/packages/saber-plugin-local-search/lib/saber-browser.js +++ b/packages/saber-plugin-local-search/lib/saber-browser.js @@ -13,11 +13,15 @@ export default ({ Vue }) => { async function performSearch(query) { if (!searchData) { - let publicUrl = __PUBLIC_URL__ + let publicUrl = __PUBLIC_URL__ // eslint-disable-line no-undef publicUrl += publicUrl.endsWith('/') ? '' : '/' const locale = this.$localePath === '/' ? 'default' : this.$localePath - const request = await fetch(`${publicUrl}_saber/search/${locale}.json`) // eslint-disable-line no-undef - const data = await request.json() + + // eslint-disable-next-line no-undef + const data = await fetch(`${publicUrl}_saber/search/${locale}.json`).then( + res => res.json() + ) + const indexes = [] searchData = data .map(item => { From 72e5f215181bffd04fcb014721f2f9d44513f4ad Mon Sep 17 00:00:00 2001 From: krmax44 Date: Wed, 21 Aug 2019 20:19:48 +0200 Subject: [PATCH 05/11] rename plugin to `saber-plugin-search` --- packages/saber-plugin-local-search/README.md | 59 ------------ .../saber-plugin-local-search/lib/index.js | 96 ------------------- .../lib/saber-browser.js | 55 ----------- .../package.json | 5 +- 4 files changed, 3 insertions(+), 212 deletions(-) delete mode 100644 packages/saber-plugin-local-search/README.md delete mode 100644 packages/saber-plugin-local-search/lib/index.js delete mode 100644 packages/saber-plugin-local-search/lib/saber-browser.js rename packages/{saber-plugin-local-search => saber-plugin-search}/package.json (67%) diff --git a/packages/saber-plugin-local-search/README.md b/packages/saber-plugin-local-search/README.md deleted file mode 100644 index 476efc2f3..000000000 --- a/packages/saber-plugin-local-search/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# saber-plugin-local-search - -Adds a hyper-fast, easy to integrate and highly customizable search to your app. - -## Install - -```bash -yarn add saber-plugin-local-search -``` - -## Usage - -In your `saber-config.yml`: - -```yml -plugins: - - resolve: saber-plugin-local-search - options: - index: - - title - - excerpt - - permalink -``` - -You can index any value from `page` or `page.attributes`. Then, create a search component: - -```html - - - - - -``` - -Pretty simple, right? diff --git a/packages/saber-plugin-local-search/lib/index.js b/packages/saber-plugin-local-search/lib/index.js deleted file mode 100644 index f1b948ce2..000000000 --- a/packages/saber-plugin-local-search/lib/index.js +++ /dev/null @@ -1,96 +0,0 @@ -const { join } = require('path') - -const ID = 'local-search' - -exports.name = ID - -let db = {} - -function getLocale(locale) { - return db[locale] -} - -exports.apply = (api, options) => { - api.browserApi.add(join(__dirname, 'saber-browser.js')) - - const index = (options && options.index) || ['title', 'excerpt', 'permalink'] - const { fs } = api.utils - - async function generateLocale(localePath) { - const pages = [] - - await Promise.all( - [...api.pages.values()].map(async page => { - if (page.draft || !page.type) { - return - } - - const matchedLocalePath = api.pages.getMatchedLocalePath(page.permalink) - if (localePath !== matchedLocalePath) { - return - } - - const item = {} - - for (const element of index) { - if (element === 'content') { - item.content = await api.renderer.renderPageContent(page.permalink) - } else { - item[element] = page[element] || page.attributes[element] - } - } - - pages.push(item) - }) - ) - - return pages - } - - async function generateDatabase() { - const allLocalePaths = ['/'].concat(Object.keys(api.config.locales || {})) - - const results = await Promise.all( - allLocalePaths.map(localePath => generateLocale(localePath)) - ) - - const localDb = {} - results.forEach((result, i) => { - const locale = allLocalePaths[i] === '/' ? 'default' : allLocalePaths[i] - localDb[locale] = result - }) - - return localDb - } - - if (api.dev) { - api.hooks.onCreatePages.tapPromise(ID, async () => { - db = await generateDatabase() - }) - - api.hooks.onCreateServer.tap(ID, server => { - server.get('/_saber/search/:locale.json', (req, res) => { - const db = getLocale(req.params.locale) - if (db) { - res.writeHead(200, { - 'Content-Type': 'application/json' - }) - return res.end(JSON.stringify(db)) - } - - res.statusCode = 404 - res.end() - }) - }) - } else { - api.hooks.afterGenerate.tapPromise(ID, async () => { - const db = await generateDatabase() - for (const locale of Object.keys(db)) { - const items = db[locale] - const path = api.resolveOutDir('_saber', 'search', `${locale}.json`) - await fs.ensureDir(api.resolveOutDir('_saber', 'search')) - await fs.writeJson(path, items) - } - }) - } -} diff --git a/packages/saber-plugin-local-search/lib/saber-browser.js b/packages/saber-plugin-local-search/lib/saber-browser.js deleted file mode 100644 index 9067c9b45..000000000 --- a/packages/saber-plugin-local-search/lib/saber-browser.js +++ /dev/null @@ -1,55 +0,0 @@ -import FuzzySearch from 'fuzzy-search' -import debounce from 'lodash.debounce' - -let searchData -let fuzzySearch - -export default ({ Vue }) => { - Vue.prototype.$searchPages = debounce(performSearch, 50, { - leading: true, - trailing: true - }) -} - -async function performSearch(query) { - if (!searchData) { - let publicUrl = __PUBLIC_URL__ // eslint-disable-line no-undef - publicUrl += publicUrl.endsWith('/') ? '' : '/' - const locale = this.$localePath === '/' ? 'default' : this.$localePath - - // eslint-disable-next-line no-undef - const data = await fetch(`${publicUrl}_saber/search/${locale}.json`).then( - res => res.json() - ) - - const indexes = [] - searchData = data - .map(item => { - if (item.excerpt) item.excerpt = stripTags(item.excerpt) - if (item.content) item.content = stripTags(item.content) - - Object.keys(item).forEach( - index => indexes.includes(index) || indexes.push(index) - ) - - return item - }) - .map(item => { - indexes.forEach(index => { - item[index] = item[index] || '' - }) - - return item - }) - - fuzzySearch = new FuzzySearch(searchData, indexes, { sort: true }) - } - - return fuzzySearch.search(query) -} - -const stripTags = html => { - const template = document.createElement('template') // eslint-disable-line no-undef - template.innerHTML = html - return template.content.textContent.trim() -} diff --git a/packages/saber-plugin-local-search/package.json b/packages/saber-plugin-search/package.json similarity index 67% rename from packages/saber-plugin-local-search/package.json rename to packages/saber-plugin-search/package.json index 6f02e4f76..01c1f37b2 100644 --- a/packages/saber-plugin-local-search/package.json +++ b/packages/saber-plugin-search/package.json @@ -1,7 +1,7 @@ { - "name": "saber-plugin-local-search", + "name": "saber-plugin-search", "version": "0.0.1", - "description": "Add a fast, local search to your app.", + "description": "Add a fast search to your app.", "license": "MIT", "main": "lib/index.js", "files": [ @@ -11,6 +11,7 @@ "saber": ">=0.7.0" }, "dependencies": { + "algoliasearch": "^3.33.0", "fuzzy-search": "^3.0.1", "lodash.debounce": "^4.0.8" } From 7aadaa3b8914653830920f2898395ae3600129bc Mon Sep 17 00:00:00 2001 From: krmax44 Date: Wed, 21 Aug 2019 20:21:18 +0200 Subject: [PATCH 06/11] feat: add multiple adapter support --- packages/saber-plugin-search/README.md | 70 ++++++++++ packages/saber-plugin-search/lib/index.js | 124 ++++++++++++++++++ .../saber-plugin-search/lib/saber-browser.js | 73 +++++++++++ yarn.lock | 77 ++++++++++- 4 files changed, 340 insertions(+), 4 deletions(-) create mode 100644 packages/saber-plugin-search/README.md create mode 100644 packages/saber-plugin-search/lib/index.js create mode 100644 packages/saber-plugin-search/lib/saber-browser.js diff --git a/packages/saber-plugin-search/README.md b/packages/saber-plugin-search/README.md new file mode 100644 index 000000000..2e0363fea --- /dev/null +++ b/packages/saber-plugin-search/README.md @@ -0,0 +1,70 @@ +# saber-plugin-search + +Adds a hyper-fast, easy to integrate and highly customizable search to your app. + +## Install + +```bash +yarn add saber-plugin-search +``` + +## Usage + +In your `saber-config.yml`: + +```yml +plugins: + - resolve: saber-plugin-search + options: + adapter: local + index: + - title + - excerpt + - permalink +``` + +This plugin comes with its own search adapter, which uses [Fuzzy Search](https://www.npmjs.com/package/fuzzy-search) - if you feel uncontent with that, you can also choose between `algolia` (please then also provide `algoliaId`, `algoliaSearchKey` and `algoliaAdminKey` with the options) or even implement your own search adapter: + +```ts +type adapterFunction = (searchData: searchData[], query: string) => searchData[] + +interface searchData { + [index: string]: string +} +``` + +You can index any stringable value from `page` or `page.attributes`. Then, create a search component: + +```html + + + + + +``` + +Pretty simple, right? diff --git a/packages/saber-plugin-search/lib/index.js b/packages/saber-plugin-search/lib/index.js new file mode 100644 index 000000000..12e59f734 --- /dev/null +++ b/packages/saber-plugin-search/lib/index.js @@ -0,0 +1,124 @@ +const { join } = require('path') +const algoliasearch = require('algoliasearch') + +const ID = 'local-search' + +exports.name = ID + +let db = {} + +function getLocale(locale) { + return db[locale] +} + +exports.apply = (api, options) => { + api.browserApi.add(join(__dirname, 'saber-browser.js')) + + options = Object.assign( + { + index: ['title', 'excerpt', 'permalink'], + adapter: 'local' + }, + options + ) + + const { index, adapter } = options + const { fs } = api.utils + + async function generateLocale(localePath) { + const pages = [] + + await Promise.all( + [...api.pages.values()].map(async page => { + if (page.draft || !page.type) { + return + } + + const matchedLocalePath = api.pages.getMatchedLocalePath(page.permalink) + if (localePath !== matchedLocalePath) { + return + } + + const item = {} + + for (const element of index) { + if (element === 'content') { + item.content = await api.renderer.renderPageContent(page.permalink) + } else { + item[element] = page[element] || page.attributes[element] + } + } + + pages.push(item) + }) + ) + + return pages + } + + async function generateDatabase() { + const allLocalePaths = ['/'].concat(Object.keys(api.config.locales || {})) + + const results = await Promise.all( + allLocalePaths.map(localePath => generateLocale(localePath)) + ) + + const localDb = {} + results.forEach((result, i) => { + const locale = allLocalePaths[i] === '/' ? 'default' : allLocalePaths[i] + localDb[locale] = result + }) + + return localDb + } + + if (api.dev) { + api.hooks.onCreatePages.tapPromise(ID, async () => { + db = await generateDatabase() + }) + + if (adapter === 'local' || typeof adapter === 'function') { + api.hooks.onCreateServer.tap(ID, server => { + server.get('/_saber/search/:locale.json', (req, res) => { + const db = getLocale(req.params.locale) + if (db) { + res.writeHead(200, { + 'Content-Type': 'application/json' + }) + return res.end(JSON.stringify(db)) + } + + res.statusCode = 404 + res.end() + }) + }) + } + } else { + api.hooks.afterGenerate.tapPromise(ID, async () => { + const db = await generateDatabase() + if (adapter === 'local' || typeof adapter === 'function') { + for (const locale of Object.keys(db)) { + const items = db[locale] + const path = api.resolveOutDir('_saber', 'search', `${locale}.json`) + await fs.ensureDir(api.resolveOutDir('_saber', 'search')) + await fs.writeJson(path, items) + } + } else if (adapter === 'algolia') { + const client = algoliasearch(options.algoliaId, options.algoliaAdminKey) + const index = client.initIndex('pages') + index.addObjects(db) + } + }) + } + + api.hooks.chainWebpack.tap(ID, config => { + const safeOptions = options + delete safeOptions.algoliaAdminKey + + config.plugin('constants').tap(([constants]) => [ + Object.assign(constants, { + __SABER_SEARCH_OPTIONS__: safeOptions + }) + ]) + }) +} diff --git a/packages/saber-plugin-search/lib/saber-browser.js b/packages/saber-plugin-search/lib/saber-browser.js new file mode 100644 index 000000000..3b2575c1b --- /dev/null +++ b/packages/saber-plugin-search/lib/saber-browser.js @@ -0,0 +1,73 @@ +import FuzzySearch from 'fuzzy-search' +import debounce from 'lodash.debounce' +import algoliasearch from 'algoliasearch/lite' + +let searchData +let fuzzySearch + +const options = __SABER_SEARCH_OPTIONS__ // eslint-disable-line no-undef + +export default ({ Vue }) => { + Vue.prototype.$searchPages = debounce(performSearch, 50, { + leading: true, + trailing: true + }) +} + +async function performSearch(query) { + if (options.adapter === 'algolia') { + const client = algoliasearch(options.algoliaId, options.algoliaSearchKey) + const index = client.initIndex('pages') + try { + const results = await index.search({ query }) + return results.hits + } catch (error) { + console.error(error) + } + } else { + if (!searchData) { + let publicUrl = __PUBLIC_URL__ // eslint-disable-line no-undef + publicUrl += publicUrl.endsWith('/') ? '' : '/' + const locale = this.$localePath === '/' ? 'default' : this.$localePath + + // eslint-disable-next-line no-undef + const data = await fetch(`${publicUrl}_saber/search/${locale}.json`).then( + res => res.json() + ) + + if (options.adapter === 'local') { + const indexes = [] + searchData = data + .map(item => { + if (item.excerpt) item.excerpt = stripTags(item.excerpt) + if (item.content) item.content = stripTags(item.content) + + Object.keys(item).forEach( + index => indexes.includes(index) || indexes.push(index) + ) + + return item + }) + .map(item => { + indexes.forEach(index => { + item[index] = item[index] || '' + }) + + return item + }) + + fuzzySearch = new FuzzySearch(searchData, indexes, { sort: true }) + } else if (typeof options.adapter === 'function') { + return Promise.resolve(options.adapter(data, query)) + } + } + + return fuzzySearch.search(query) + } +} + +const stripTags = html => { + const template = document.createElement('template') // eslint-disable-line no-undef + template.innerHTML = html + return template.content.textContent.trim() +} diff --git a/yarn.lock b/yarn.lock index 9243fb20c..322aab549 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2404,6 +2404,11 @@ agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: dependencies: es6-promisify "^5.0.0" +agentkeepalive@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" + integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= + agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -2431,6 +2436,27 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +algoliasearch@^3.33.0: + version "3.33.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.33.0.tgz#83b541124ebb0db54643009d4e660866b3177cdf" + integrity sha512-9DaVmOd7cvcZeYyV0BWAeJHVWJmgOL2DNUEBY/DTR4MzD1wCWs4Djl7LAlfvkGwGBdRHZCG+l0HA1572w3T8zg== + dependencies: + agentkeepalive "^2.2.0" + debug "^2.6.9" + envify "^4.0.0" + es6-promise "^4.1.0" + events "^1.1.0" + foreach "^2.0.5" + global "^4.3.2" + inherits "^2.0.1" + isarray "^2.0.1" + load-script "^1.0.0" + object-keys "^1.0.11" + querystring-es3 "^0.2.1" + reduce "^1.0.1" + semver "^5.1.0" + tunnel-agent "^0.6.0" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -4647,6 +4673,14 @@ env-paths@^2.2.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== +envify@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" + integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== + dependencies: + esprima "^4.0.0" + through "~2.3.4" + err-code@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" @@ -4692,6 +4726,11 @@ es6-promise@^4.0.3: resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054" integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== +es6-promise@^4.1.0: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" @@ -5043,7 +5082,7 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@^1.0.0: +events@^1.0.0, events@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= @@ -5448,6 +5487,11 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5799,6 +5843,14 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" +global@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" @@ -6848,6 +6900,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7686,6 +7743,11 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +load-script@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" + integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= + loader-runner@^2.3.0: version "2.3.1" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" @@ -8756,7 +8818,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-keys@^1.0.11: +object-keys@^1.0.11, object-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -10114,7 +10176,7 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0: +querystring-es3@^0.2.0, querystring-es3@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -10348,6 +10410,13 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +reduce@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" + integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== + dependencies: + object-keys "^1.1.0" + regenerate-unicode-properties@^8.0.2: version "8.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" @@ -11674,7 +11743,7 @@ through2@^3.0.0: dependencies: readable-stream "2 || 3" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3.4: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= From bb116d32d2b7de079a65d81118ca4e96a4578d43 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Thu, 22 Aug 2019 14:40:46 +0800 Subject: [PATCH 07/11] make it more low-level --- packages/saber-plugin-search/README.md | 99 +++++++++++-------- packages/saber-plugin-search/lib/index.js | 72 +++++--------- .../saber-plugin-search/lib/saber-browser.js | 76 ++------------ packages/saber-plugin-search/package.json | 5 - website/saber-config.js | 3 + yarn.lock | 82 +-------------- 6 files changed, 97 insertions(+), 240 deletions(-) diff --git a/packages/saber-plugin-search/README.md b/packages/saber-plugin-search/README.md index 2e0363fea..b3eb2a31b 100644 --- a/packages/saber-plugin-search/README.md +++ b/packages/saber-plugin-search/README.md @@ -15,56 +15,69 @@ In your `saber-config.yml`: ```yml plugins: - resolve: saber-plugin-search - options: - adapter: local - index: - - title - - excerpt - - permalink ``` -This plugin comes with its own search adapter, which uses [Fuzzy Search](https://www.npmjs.com/package/fuzzy-search) - if you feel uncontent with that, you can also choose between `algolia` (please then also provide `algoliaId`, `algoliaSearchKey` and `algoliaAdminKey` with the options) or even implement your own search adapter: +Then in your Vue components, you can call `this.$fetchSearchDatabase()` to get the database that you can query from, this method returns a Promise which resolves an array of `Page` objects: -```ts -type adapterFunction = (searchData: searchData[], query: string) => searchData[] +```js +;[ + { + type: 'page', + title: 'About this site', + excerpt: '...', + permalink: '/about.html' + }, + { + type: 'post', + title: 'Hello World', + excerpt: '...', + permalink: '/posts/hello-world.html' + } +] +``` -interface searchData { - [index: string]: string -} +Now you can query a keyword like this: + +```js +const database = await this.$fetchSearchDatabase() +// Typically you need to get the keyword from an `input` element +// We hardcoded it for convenience +const keyword = 'hello' +const matchedResults = database.filter(page => { + return page.title.includes(keyword) || page.excerpt.includes(keyword) +}) ``` -You can index any stringable value from `page` or `page.attributes`. Then, create a search component: - -```html - - - - - + ] +} +const fuse = new Fuse(database, options) +const matchedResults = fuse.search(keyword) ``` -Pretty simple, right? +## Plugin Options + +### index + +- Type: `string[]` +- Default: `['type', 'title', 'excerpt', 'permalink']` + +Only specified page properties will be included in the generated database. + +## License + +MIT. diff --git a/packages/saber-plugin-search/lib/index.js b/packages/saber-plugin-search/lib/index.js index 12e59f734..5fc69dac6 100644 --- a/packages/saber-plugin-search/lib/index.js +++ b/packages/saber-plugin-search/lib/index.js @@ -1,7 +1,6 @@ const { join } = require('path') -const algoliasearch = require('algoliasearch') -const ID = 'local-search' +const ID = 'search' exports.name = ID @@ -12,17 +11,13 @@ function getLocale(locale) { } exports.apply = (api, options) => { - api.browserApi.add(join(__dirname, 'saber-browser.js')) - - options = Object.assign( + const { index } = Object.assign( { - index: ['title', 'excerpt', 'permalink'], - adapter: 'local' + index: ['type', 'title', 'excerpt', 'permalink'] }, options ) - const { index, adapter } = options const { fs } = api.utils async function generateLocale(localePath) { @@ -65,60 +60,47 @@ exports.apply = (api, options) => { const localDb = {} results.forEach((result, i) => { - const locale = allLocalePaths[i] === '/' ? 'default' : allLocalePaths[i] + const locale = allLocalePaths[i].slice(1) || 'default' localDb[locale] = result }) return localDb } + api.browserApi.add(join(__dirname, 'saber-browser.js')) + if (api.dev) { api.hooks.onCreatePages.tapPromise(ID, async () => { db = await generateDatabase() }) - if (adapter === 'local' || typeof adapter === 'function') { - api.hooks.onCreateServer.tap(ID, server => { - server.get('/_saber/search/:locale.json', (req, res) => { - const db = getLocale(req.params.locale) - if (db) { - res.writeHead(200, { - 'Content-Type': 'application/json' - }) - return res.end(JSON.stringify(db)) - } + api.hooks.onCreateServer.tap(ID, server => { + server.get('/_saber/plugin-search/:locale.json', (req, res) => { + const db = getLocale(req.params.locale) + if (db) { + res.writeHead(200, { + 'Content-Type': 'application/json' + }) + return res.end(JSON.stringify(db)) + } - res.statusCode = 404 - res.end() - }) + res.statusCode = 404 + res.end() }) - } + }) } else { api.hooks.afterGenerate.tapPromise(ID, async () => { const db = await generateDatabase() - if (adapter === 'local' || typeof adapter === 'function') { - for (const locale of Object.keys(db)) { - const items = db[locale] - const path = api.resolveOutDir('_saber', 'search', `${locale}.json`) - await fs.ensureDir(api.resolveOutDir('_saber', 'search')) - await fs.writeJson(path, items) - } - } else if (adapter === 'algolia') { - const client = algoliasearch(options.algoliaId, options.algoliaAdminKey) - const index = client.initIndex('pages') - index.addObjects(db) + for (const locale of Object.keys(db)) { + const items = db[locale] + const path = api.resolveOutDir( + '_saber', + 'plugin-search', + `${locale}.json` + ) + await fs.ensureDir(api.resolveOutDir('_saber', 'plugin-search')) + await fs.writeJson(path, items) } }) } - - api.hooks.chainWebpack.tap(ID, config => { - const safeOptions = options - delete safeOptions.algoliaAdminKey - - config.plugin('constants').tap(([constants]) => [ - Object.assign(constants, { - __SABER_SEARCH_OPTIONS__: safeOptions - }) - ]) - }) } diff --git a/packages/saber-plugin-search/lib/saber-browser.js b/packages/saber-plugin-search/lib/saber-browser.js index 3b2575c1b..bcbb33887 100644 --- a/packages/saber-plugin-search/lib/saber-browser.js +++ b/packages/saber-plugin-search/lib/saber-browser.js @@ -1,73 +1,11 @@ -import FuzzySearch from 'fuzzy-search' -import debounce from 'lodash.debounce' -import algoliasearch from 'algoliasearch/lite' - -let searchData -let fuzzySearch - -const options = __SABER_SEARCH_OPTIONS__ // eslint-disable-line no-undef +/* eslint-env browser */ +/* globals __PUBLIC_URL__ */ export default ({ Vue }) => { - Vue.prototype.$searchPages = debounce(performSearch, 50, { - leading: true, - trailing: true - }) -} - -async function performSearch(query) { - if (options.adapter === 'algolia') { - const client = algoliasearch(options.algoliaId, options.algoliaSearchKey) - const index = client.initIndex('pages') - try { - const results = await index.search({ query }) - return results.hits - } catch (error) { - console.error(error) - } - } else { - if (!searchData) { - let publicUrl = __PUBLIC_URL__ // eslint-disable-line no-undef - publicUrl += publicUrl.endsWith('/') ? '' : '/' - const locale = this.$localePath === '/' ? 'default' : this.$localePath - - // eslint-disable-next-line no-undef - const data = await fetch(`${publicUrl}_saber/search/${locale}.json`).then( - res => res.json() - ) - - if (options.adapter === 'local') { - const indexes = [] - searchData = data - .map(item => { - if (item.excerpt) item.excerpt = stripTags(item.excerpt) - if (item.content) item.content = stripTags(item.content) - - Object.keys(item).forEach( - index => indexes.includes(index) || indexes.push(index) - ) - - return item - }) - .map(item => { - indexes.forEach(index => { - item[index] = item[index] || '' - }) - - return item - }) - - fuzzySearch = new FuzzySearch(searchData, indexes, { sort: true }) - } else if (typeof options.adapter === 'function') { - return Promise.resolve(options.adapter(data, query)) - } - } - - return fuzzySearch.search(query) + Vue.prototype.$fetchSearchDatabase = function() { + const locale = this.$localePath.slice(1) || 'default' + return window + .fetch(`${__PUBLIC_URL__}_saber/plugin-search/${locale}.json`) + .then(res => res.json()) } } - -const stripTags = html => { - const template = document.createElement('template') // eslint-disable-line no-undef - template.innerHTML = html - return template.content.textContent.trim() -} diff --git a/packages/saber-plugin-search/package.json b/packages/saber-plugin-search/package.json index 01c1f37b2..7122b4058 100644 --- a/packages/saber-plugin-search/package.json +++ b/packages/saber-plugin-search/package.json @@ -9,10 +9,5 @@ ], "peerDependencies": { "saber": ">=0.7.0" - }, - "dependencies": { - "algoliasearch": "^3.33.0", - "fuzzy-search": "^3.0.1", - "lodash.debounce": "^4.0.8" } } diff --git a/website/saber-config.js b/website/saber-config.js index 6946235a5..afafbb72c 100644 --- a/website/saber-config.js +++ b/website/saber-config.js @@ -92,6 +92,9 @@ module.exports = { ] } } + }, + { + resolve: '../packages/saber-plugin-search' } ] } diff --git a/yarn.lock b/yarn.lock index 322aab549..cd3fee9e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2404,11 +2404,6 @@ agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: dependencies: es6-promisify "^5.0.0" -agentkeepalive@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" - integrity sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8= - agentkeepalive@^3.4.1: version "3.5.2" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" @@ -2436,27 +2431,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.5.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -algoliasearch@^3.33.0: - version "3.33.0" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.33.0.tgz#83b541124ebb0db54643009d4e660866b3177cdf" - integrity sha512-9DaVmOd7cvcZeYyV0BWAeJHVWJmgOL2DNUEBY/DTR4MzD1wCWs4Djl7LAlfvkGwGBdRHZCG+l0HA1572w3T8zg== - dependencies: - agentkeepalive "^2.2.0" - debug "^2.6.9" - envify "^4.0.0" - es6-promise "^4.1.0" - events "^1.1.0" - foreach "^2.0.5" - global "^4.3.2" - inherits "^2.0.1" - isarray "^2.0.1" - load-script "^1.0.0" - object-keys "^1.0.11" - querystring-es3 "^0.2.1" - reduce "^1.0.1" - semver "^5.1.0" - tunnel-agent "^0.6.0" - align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -4673,14 +4647,6 @@ env-paths@^2.2.0: resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== -envify@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" - integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== - dependencies: - esprima "^4.0.0" - through "~2.3.4" - err-code@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" @@ -4726,11 +4692,6 @@ es6-promise@^4.0.3: resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054" integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== -es6-promise@^4.1.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" @@ -5082,7 +5043,7 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@^1.0.0, events@^1.1.0: +events@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= @@ -5487,11 +5448,6 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5606,11 +5562,6 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -fuzzy-search@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/fuzzy-search/-/fuzzy-search-3.0.1.tgz#14a4964508a9607d6e9a88818e7ff634108260b6" - integrity sha512-rjUvzdsMlOyarm0oD5k6zVQwgvt4Tb5Xe3YdIGU+Vogw54+ueAGPUTMU2B9jfPQEie5cD11i/S9J9d+MNBSQ3Q== - g-status@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97" @@ -5843,14 +5794,6 @@ global-prefix@^1.0.1: is-windows "^1.0.1" which "^1.2.14" -global@^4.3.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - global@~4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" @@ -6900,11 +6843,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isarray@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7743,11 +7681,6 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -load-script@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" - integrity sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ= - loader-runner@^2.3.0: version "2.3.1" resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979" @@ -8818,7 +8751,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-keys@^1.0.11, object-keys@^1.1.0: +object-keys@^1.0.11: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -10176,7 +10109,7 @@ query-string@^4.1.0: object-assign "^4.1.0" strict-uri-encode "^1.0.0" -querystring-es3@^0.2.0, querystring-es3@^0.2.1: +querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= @@ -10410,13 +10343,6 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" -reduce@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" - integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== - dependencies: - object-keys "^1.1.0" - regenerate-unicode-properties@^8.0.2: version "8.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" @@ -11743,7 +11669,7 @@ through2@^3.0.0: dependencies: readable-stream "2 || 3" -through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3.4: +through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= From ef97e6a543ab4367a08ceb983f641bf922967396 Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Thu, 22 Aug 2019 14:59:36 +0800 Subject: [PATCH 08/11] strip html tags --- packages/saber-plugin-search/lib/index.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/saber-plugin-search/lib/index.js b/packages/saber-plugin-search/lib/index.js index 5fc69dac6..5a04cf711 100644 --- a/packages/saber-plugin-search/lib/index.js +++ b/packages/saber-plugin-search/lib/index.js @@ -10,6 +10,10 @@ function getLocale(locale) { return db[locale] } +function stripHTML(input) { + return input.replace(/<(?:.|\n)*?>/gm, '') +} + exports.apply = (api, options) => { const { index } = Object.assign( { @@ -37,10 +41,15 @@ exports.apply = (api, options) => { const item = {} for (const element of index) { - if (element === 'content') { - item.content = await api.renderer.renderPageContent(page.permalink) - } else { - item[element] = page[element] || page.attributes[element] + const value = page[element] + if (value !== undefined) { + if (element === 'content') { + item.content = stripHTML( + await api.renderer.renderPageContent(page.permalink) + ) + } else { + item[element] = stripHTML(page[element]) + } } } From 785160763fd50a8f2934ff9ac4ee8a1a82424f1e Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Thu, 22 Aug 2019 15:00:59 +0800 Subject: [PATCH 09/11] Add LICENSE --- packages/saber-plugin-search/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 packages/saber-plugin-search/LICENSE diff --git a/packages/saber-plugin-search/LICENSE b/packages/saber-plugin-search/LICENSE new file mode 100644 index 000000000..0fa9bb511 --- /dev/null +++ b/packages/saber-plugin-search/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From 524f2514bc7d8a0846c814abbee1088f9ddfc1df Mon Sep 17 00:00:00 2001 From: EGOIST <0x142857@gmail.com> Date: Thu, 22 Aug 2019 15:03:13 +0800 Subject: [PATCH 10/11] tweaks --- packages/saber-plugin-search/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/saber-plugin-search/README.md b/packages/saber-plugin-search/README.md index b3eb2a31b..c53d1fa19 100644 --- a/packages/saber-plugin-search/README.md +++ b/packages/saber-plugin-search/README.md @@ -48,7 +48,7 @@ const matchedResults = database.filter(page => { }) ``` -The above example simply uses `Array.prototype.includes` to check if the page matches the keyword, however you can use a more powerful library like [Fuse.js](https://fusejs.io/) if you want more accurate result: +The above example simply uses `String.prototype.includes` to check if the page matches the keyword, however you can use a more powerful library like [Fuse.js](https://fusejs.io/) if you want more accurate result: ```js import Fuse from 'fuse.js' From 9faa65e7aa5a1ff85ae29bea6db7238e93eeb0b8 Mon Sep 17 00:00:00 2001 From: krmax44 Date: Thu, 22 Aug 2019 13:27:40 +0200 Subject: [PATCH 11/11] docs: update readme --- packages/saber-plugin-search/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/saber-plugin-search/README.md b/packages/saber-plugin-search/README.md index c53d1fa19..be30aa4cd 100644 --- a/packages/saber-plugin-search/README.md +++ b/packages/saber-plugin-search/README.md @@ -63,7 +63,8 @@ const options = { name: 'excerpt', weight: 0.4 } - ] + ], + shouldSort: true // sorts the results by score } const fuse = new Fuse(database, options) const matchedResults = fuse.search(keyword)