feat: serialize search-index

This commit is contained in:
Roman Hotsiy 2018-03-06 13:10:08 +02:00
parent 8e4184b407
commit e94f84283d
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
5 changed files with 51 additions and 19 deletions

View File

@ -35,12 +35,12 @@ async function init() {
init(); init();
if (module.hot) { if (module.hot) {
const reload = (reloadStore = false) => () => { const reload = (reloadStore = false) => async () => {
if (reloadStore) { if (reloadStore) {
// create a new Store // create a new Store
store.dispose(); store.dispose();
const state = store.toJS(); const state = await store.toJS();
store = AppStore.fromJS(state); store = AppStore.fromJS(state);
} }

View File

@ -72,7 +72,7 @@
"webpack": "^3.10.0", "webpack": "^3.10.0",
"webpack-dev-server": "^2.9.5", "webpack-dev-server": "^2.9.5",
"webpack-node-externals": "^1.6.0", "webpack-node-externals": "^1.6.0",
"workerize-loader": "^1.0.1", "workerize-loader": "^1.0.2",
"yaml-js": "^0.2.3" "yaml-js": "^0.2.3"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -17,6 +17,7 @@ interface StoreData {
url: string; url: string;
data: any; data: any;
}; };
searchIndex: any;
options: RedocRawOptions; options: RedocRawOptions;
} }
@ -36,9 +37,10 @@ export class AppStore {
*/ */
// TODO: // TODO:
static fromJS(state: StoreData): AppStore { static fromJS(state: StoreData): AppStore {
const inst = new AppStore(state.spec.data, state.spec.url, state.options); const inst = new AppStore(state.spec.data, state.spec.url, state.options, false);
inst.menu.activeItemIdx = state.menu.activeItemIdx || 0; inst.menu.activeItemIdx = state.menu.activeItemIdx || 0;
inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]); inst.menu.activate(inst.menu.flatItems[inst.menu.activeItemIdx]);
inst.search.load(state.searchIndex);
return inst; return inst;
} }
@ -52,7 +54,12 @@ export class AppStore {
private scroll: ScrollService; private scroll: ScrollService;
private disposer; private disposer;
constructor(spec: OpenAPISpec, specUrl?: string, options: RedocRawOptions = {}) { constructor(
spec: OpenAPISpec,
specUrl?: string,
options: RedocRawOptions = {},
createSearchIndex: boolean = true,
) {
this.rawOptions = options; this.rawOptions = options;
this.options = new RedocNormalizedOptions(options); this.options = new RedocNormalizedOptions(options);
this.scroll = new ScrollService(this.options); this.scroll = new ScrollService(this.options);
@ -60,8 +67,9 @@ export class AppStore {
this.menu = new MenuStore(this.spec, this.scroll); this.menu = new MenuStore(this.spec, this.scroll);
this.search = new SearchStore(); this.search = new SearchStore();
this.search.indexItems(this.menu.items); if (createSearchIndex) {
this.search.done(); this.search.indexItems(this.menu.items);
}
this.disposer = observe(this.menu, 'activeItemIdx', change => { this.disposer = observe(this.menu, 'activeItemIdx', change => {
this.updateMarkOnMenu(change.newValue as number); this.updateMarkOnMenu(change.newValue as number);
@ -106,7 +114,7 @@ export class AppStore {
* **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION** * **SUPER HACKY AND NOT OPTIMAL IMPLEMENTATION**
*/ */
// TODO: // TODO:
toJS(): StoreData { async toJS(): Promise<StoreData> {
return { return {
menu: { menu: {
activeItemIdx: this.menu.activeItemIdx, activeItemIdx: this.menu.activeItemIdx,
@ -115,6 +123,7 @@ export class AppStore {
url: this.spec.parser.specUrl, url: this.spec.parser.specUrl,
data: this.spec.parser.spec, data: this.spec.parser.spec,
}, },
searchIndex: await this.search.toJS(),
options: this.rawOptions, options: this.rawOptions,
}; };
} }

View File

@ -6,23 +6,32 @@ export class SearchStore {
searchWorker = new worker(); searchWorker = new worker();
indexItems(groups: Array<IMenuItem | OperationModel>) { indexItems(groups: Array<IMenuItem | OperationModel>) {
groups.forEach(group => { const recurse = groups => {
if (group.type !== 'group') { groups.forEach(group => {
this.add(group.name, group.description || '', group.id); if (group.type !== 'group') {
} this.add(group.name, group.description || '', group.id);
this.indexItems(group.items); }
}); recurse(group.items);
});
};
recurse(groups);
this.searchWorker.done();
} }
add(title: string, body: string, ref: string) { add(title: string, body: string, ref: string) {
this.searchWorker.add(title, body, ref); this.searchWorker.add(title, body, ref);
} }
done() {
this.searchWorker.done();
}
search(q: string) { search(q: string) {
return this.searchWorker.search(q); return this.searchWorker.search(q);
} }
async toJS() {
return this.searchWorker.toJS();
}
load(state: any) {
this.searchWorker.load(state);
}
} }

View File

@ -5,6 +5,8 @@ export default class Worker {
add = add; add = add;
done = done; done = done;
search = search; search = search;
toJS = toJS;
load = load;
} }
export interface SearchDocument { export interface SearchDocument {
@ -17,7 +19,7 @@ export interface SearchResult extends SearchDocument {
score: number; score: number;
} }
const store: { [id: string]: SearchDocument } = {}; let store: { [id: string]: SearchDocument } = {};
let resolveIndex: (v: lunr.Index) => void; let resolveIndex: (v: lunr.Index) => void;
const index: Promise<lunr.Index> = new Promise(resolve => { const index: Promise<lunr.Index> = new Promise(resolve => {
@ -43,6 +45,18 @@ export async function done() {
resolveIndex(builder.build()); resolveIndex(builder.build());
} }
export async function toJS() {
return {
store: store,
index: (await index).toJSON(),
};
}
export async function load(state: any) {
store = state.store;
resolveIndex(lunr.Index.load(state.index));
}
export async function search(q: string): Promise<SearchResult[]> { export async function search(q: string): Promise<SearchResult[]> {
if (q.trim().length === 0) { if (q.trim().length === 0) {
return []; return [];