vue-instantsearch 3.8.1 → 3.9.0
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 +14 -0
- package/dist/vue-instantsearch.common.js +1 -1
- package/dist/vue-instantsearch.common.js.map +1 -1
- package/dist/vue-instantsearch.js +1 -1
- package/dist/vue-instantsearch.js.map +1 -1
- package/es/package.json.js +1 -1
- package/es/src/mixins/widget.js +1 -1
- package/es/src/mixins/widget.js.map +1 -1
- package/es/src/util/createServerRootMixin.js +1 -1
- package/es/src/util/createServerRootMixin.js.map +1 -1
- package/package.json +4 -4
- package/src/mixins/widget.js +1 -1
- package/src/util/__tests__/createServerRootMixin.test.js +229 -83
- package/src/util/createServerRootMixin.js +40 -104
- package/src/util/testutils/helper.js +2 -2
package/es/package.json.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var r="3.
|
|
1
|
+
var r="3.9.0";export{r as version};
|
|
2
2
|
//# sourceMappingURL=package.json.js.map
|
package/es/src/mixins/widget.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{warn as t}from"../util/warn.js";var e=function(e){void 0===e&&(e={});var n=e.connector;return{inject:{instantSearchInstance:{from:"$_ais_instantSearchInstance",default:function(){var t=this.$options._componentTag;throw new TypeError('It looks like you forgot to wrap your Algolia search component "<'+t+'>" inside of an "<ais-instant-search>" component.')}},getParentIndex:{from:"$_ais_getParentIndex",default:function(){var t=this;return function(){return t.instantSearchInstance.mainIndex}}}},data:function(){return{state:null}},created:function(){if("function"==typeof n){if(this.factory=n(this.updateState,function(){}),this.widget=this.factory(this.widgetParams),this.getParentIndex().addWidgets([this.widget]),this.instantSearchInstance.
|
|
1
|
+
import{warn as t}from"../util/warn.js";var e=function(e){void 0===e&&(e={});var n=e.connector;return{inject:{instantSearchInstance:{from:"$_ais_instantSearchInstance",default:function(){var t=this.$options._componentTag;throw new TypeError('It looks like you forgot to wrap your Algolia search component "<'+t+'>" inside of an "<ais-instant-search>" component.')}},getParentIndex:{from:"$_ais_getParentIndex",default:function(){var t=this;return function(){return t.instantSearchInstance.mainIndex}}}},data:function(){return{state:null}},created:function(){if("function"==typeof n){if(this.factory=n(this.updateState,function(){}),this.widget=this.factory(this.widgetParams),this.getParentIndex().addWidgets([this.widget]),this.instantSearchInstance._initialResults&&!this.instantSearchInstance.started){if("function"!=typeof this.instantSearchInstance.__forceRender)throw new Error("You are using server side rendering with <ais-instant-search> instead of <ais-instant-search-ssr>.");this.instantSearchInstance.__forceRender(this.widget,this.getParentIndex())}}else!0!==n&&t("You are using the InstantSearch widget mixin, but didn't provide a connector.\nWhile this is technically possible, and will give you access to the Helper,\nit's not the recommended way of making custom components.\n\nIf you want to disable this message, pass { connector: true } to the mixin.\n\nRead more on using connectors: https://alg.li/vue-custom")},beforeDestroy:function(){this.widget&&this.getParentIndex().removeWidgets([this.widget])},watch:{widgetParams:{handler:function(t){this.state=null,this.getParentIndex().removeWidgets([this.widget]),this.widget=this.factory(t),this.getParentIndex().addWidgets([this.widget])},deep:!0}},methods:{updateState:function(t,e){void 0===t&&(t={}),e||(this.state=t)}}}};export{e as createWidgetMixin};
|
|
2
2
|
//# sourceMappingURL=widget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget.js","sources":["../../../src/mixins/widget.js"],"sourcesContent":["import { warn } from '../util/warn';\n\nexport const createWidgetMixin = ({ connector } = {}) => ({\n inject: {\n instantSearchInstance: {\n from: '$_ais_instantSearchInstance',\n default() {\n const tag = this.$options._componentTag;\n throw new TypeError(\n `It looks like you forgot to wrap your Algolia search component \"<${tag}>\" inside of an \"<ais-instant-search>\" component.`\n );\n },\n },\n getParentIndex: {\n from: '$_ais_getParentIndex',\n default() {\n return () => this.instantSearchInstance.mainIndex;\n },\n },\n },\n data() {\n return {\n state: null,\n };\n },\n created() {\n if (typeof connector === 'function') {\n this.factory = connector(this.updateState, () => {});\n this.widget = this.factory(this.widgetParams);\n this.getParentIndex().addWidgets([this.widget]);\n\n if (\n this.instantSearchInstance.
|
|
1
|
+
{"version":3,"file":"widget.js","sources":["../../../src/mixins/widget.js"],"sourcesContent":["import { warn } from '../util/warn';\n\nexport const createWidgetMixin = ({ connector } = {}) => ({\n inject: {\n instantSearchInstance: {\n from: '$_ais_instantSearchInstance',\n default() {\n const tag = this.$options._componentTag;\n throw new TypeError(\n `It looks like you forgot to wrap your Algolia search component \"<${tag}>\" inside of an \"<ais-instant-search>\" component.`\n );\n },\n },\n getParentIndex: {\n from: '$_ais_getParentIndex',\n default() {\n return () => this.instantSearchInstance.mainIndex;\n },\n },\n },\n data() {\n return {\n state: null,\n };\n },\n created() {\n if (typeof connector === 'function') {\n this.factory = connector(this.updateState, () => {});\n this.widget = this.factory(this.widgetParams);\n this.getParentIndex().addWidgets([this.widget]);\n\n if (\n this.instantSearchInstance._initialResults &&\n !this.instantSearchInstance.started\n ) {\n if (typeof this.instantSearchInstance.__forceRender !== 'function') {\n throw new Error(\n 'You are using server side rendering with <ais-instant-search> instead of <ais-instant-search-ssr>.'\n );\n }\n this.instantSearchInstance.__forceRender(\n this.widget,\n this.getParentIndex()\n );\n }\n } else if (connector !== true) {\n warn(\n `You are using the InstantSearch widget mixin, but didn't provide a connector.\nWhile this is technically possible, and will give you access to the Helper,\nit's not the recommended way of making custom components.\n\nIf you want to disable this message, pass { connector: true } to the mixin.\n\nRead more on using connectors: https://alg.li/vue-custom`\n );\n }\n },\n beforeDestroy() {\n if (this.widget) {\n this.getParentIndex().removeWidgets([this.widget]);\n }\n },\n watch: {\n widgetParams: {\n handler(nextWidgetParams) {\n this.state = null;\n this.getParentIndex().removeWidgets([this.widget]);\n this.widget = this.factory(nextWidgetParams);\n this.getParentIndex().addWidgets([this.widget]);\n },\n deep: true,\n },\n },\n methods: {\n updateState(state = {}, isFirstRender) {\n if (!isFirstRender) {\n // Avoid updating the state on first render\n // otherwise there will be a flash of placeholder data\n this.state = state;\n }\n },\n },\n});\n"],"names":["createWidgetMixin","ref","inject","instantSearchInstance","from","default","const","tag","this","$options","_componentTag","TypeError","getParentIndex","mainIndex","data","state","created","connector","factory","updateState","widget","widgetParams","addWidgets","_initialResults","started","__forceRender","Error","warn","beforeDestroy","removeWidgets","watch","handler","nextWidgetParams","deep","methods","isFirstRender"],"mappings":"uCAEY,IAACA,WAAqBC,kBAAgB,6BAChDC,OAAQ,CACNC,sBAAuB,CACrBC,KAAM,8BACNC,mBACEC,IAAMC,EAAMC,KAAKC,SAASC,cAC1B,MAAM,IAAIC,8EAC4DJ,yDAI1EK,eAAgB,CACdR,KAAM,uBACNC,8BACE,yBAAaG,EAAKL,sBAAsBU,cAI9CC,gBACE,MAAO,CACLC,MAAO,OAGXC,mBACE,GAAyB,mBAAdC,GAKT,GAJAT,KAAKU,QAAUD,EAAUT,KAAKW,0BAC9BX,KAAKY,OAASZ,KAAKU,QAAQV,KAAKa,cAChCb,KAAKI,iBAAiBU,WAAW,CAACd,KAAKY,SAGrCZ,KAAKL,sBAAsBoB,kBAC1Bf,KAAKL,sBAAsBqB,QAC5B,CACA,GAAwD,mBAA7ChB,KAAKL,sBAAsBsB,cACpC,MAAM,IAAIC,MACR,sGAGJlB,KAAKL,sBAAsBsB,cACzBjB,KAAKY,OACLZ,KAAKI,wBAGc,IAAdK,GACTU,EACE,qWAUNC,yBACMpB,KAAKY,QACPZ,KAAKI,iBAAiBiB,cAAc,CAACrB,KAAKY,UAG9CU,MAAO,CACLT,aAAc,CACZU,iBAAQC,GACNxB,KAAKO,MAAQ,KACbP,KAAKI,iBAAiBiB,cAAc,CAACrB,KAAKY,SAC1CZ,KAAKY,OAASZ,KAAKU,QAAQc,GAC3BxB,KAAKI,iBAAiBU,WAAW,CAACd,KAAKY,UAEzCa,MAAM,IAGVC,QAAS,CACPf,qBAAYJ,EAAYoB,kBAAJ,IACbA,IAGH3B,KAAKO,MAAQA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{warn as e}from"./warn.js";import t from"vue";import
|
|
1
|
+
import{warn as e}from"./warn.js";import t from"vue";import r from"instantsearch.js/es";function n(e){var r={serverPrefetch:void 0,fetch:void 0,_base:void 0,name:"ais-ssr-root-component",router:e.$router,store:e.$store},n=new(e.$vnode?e.$vnode.componentOptions.Ctor.extend(r):t.component(r.name,Object.assign({},e.$options,r)))({propsData:e.$options.propsData});return n.$slots=e.$slots,n.$root=e.$root,n.$options.serverPrefetch=[],n}function o(t,n){var o,s=r(t);return s.findResultsState=function(e){var t,r,a;try{t=require("vue-server-renderer/basic")}catch(e){}if(!t)throw new Error("you need to install vue-server-renderer");return Promise.resolve().then(function(){r=n(e),(a=r.instantsearch).start(),a.started=!1}).then(function(){return function(e,t){return new Promise(function(r,n){return t(e,function(e,t){e&&n(e),r(t)})})}(r,t)}).then(function(){return e=a.mainHelper,new Promise(function(t,r){e.searchOnlyWithDerivedHelpers(),e.derivedHelpers[0].on("result",function(){t()}),e.derivedHelpers.forEach(function(e){return e.on("error",function(e){r(e)})})});var e}).then(function(){return o={},function e(t,r){return r(t),t.getWidgets().forEach(function(t){"ais.index"===t.$$type&&(r(t),e(t,r))})}(a.mainIndex,function(e){var t=e.getResults(),r=t._state,n=t._rawResults;o[e.getIndexId()]={state:Object.keys(r).reduce(function(e,t){return e[t]=r[t],e},{}),results:n}}),s.hydrate(o),s.getState()})},s.getState=function(){if(!o)throw new Error("You need to wait for findResultsState to finish");return o},s.__forceRender=function(e,t){var r=t.getResults();if(null!==r){var n=r._state,o=t.getHelper();o.state=n,e.render({helper:o,results:r,scopedResults:t.getScopedResults(),parent:t,state:n,templatesConfig:{},createURL:t.createURL,instantSearchInstance:s,searchMetadata:{isSearchStalled:!1}})}},s.hydrate=function(t){t?(s._initialResults=t,s.start(),s.started=!1):e("The result of `findResultsState()` needs to be passed to `hydrate()`.")},s}function s(e){void 0===e&&(e={});var t=e.$cloneComponent;void 0===t&&(t=n);var r=o(e,t);return{provide:function(){return{$_ais_ssrInstantSearchInstance:this.instantsearch}},data:function(){return{instantsearch:r}}}}export{s as createServerRootMixin};
|
|
2
2
|
//# sourceMappingURL=createServerRootMixin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createServerRootMixin.js","sources":["../../../src/util/createServerRootMixin.js"],"sourcesContent":["import Vue from 'vue';\nimport instantsearch from 'instantsearch.js/es';\nimport algoliaHelper from 'algoliasearch-helper';\nconst { SearchResults, SearchParameters } = algoliaHelper;\nimport { warn } from './warn';\n\nfunction walkIndex(indexWidget, visit) {\n visit(indexWidget);\n\n return indexWidget.getWidgets().forEach(widget => {\n if (widget.$$type !== 'ais.index') return;\n visit(widget);\n walkIndex(widget, visit);\n });\n}\n\nfunction renderToString(app, _renderToString) {\n return new Promise((resolve, reject) =>\n _renderToString(app, (err, res) => {\n if (err) reject(err);\n resolve(res);\n })\n );\n}\n\nfunction searchOnlyWithDerivedHelpers(helper) {\n return new Promise((resolve, reject) => {\n helper.searchOnlyWithDerivedHelpers();\n\n // we assume all derived helpers resolve at least in the same tick\n helper.derivedHelpers[0].on('result', () => {\n resolve();\n });\n\n helper.derivedHelpers.forEach(derivedHelper =>\n derivedHelper.on('error', e => {\n reject(e);\n })\n );\n });\n}\n\nfunction defaultCloneComponent(componentInstance) {\n const options = {\n serverPrefetch: undefined,\n fetch: undefined,\n _base: undefined,\n name: 'ais-ssr-root-component',\n // copy over global Vue APIs\n router: componentInstance.$router,\n store: componentInstance.$store,\n };\n\n const Extended = componentInstance.$vnode\n ? componentInstance.$vnode.componentOptions.Ctor.extend(options)\n : Vue.component(Object.assign({}, componentInstance.$options, options));\n\n const app = new Extended({\n propsData: componentInstance.$options.propsData,\n });\n\n // https://stackoverflow.com/a/48195006/3185307\n app.$slots = componentInstance.$slots;\n app.$root = componentInstance.$root;\n app.$options.serverPrefetch = [];\n\n return app;\n}\n\nfunction augmentInstantSearch(\n instantSearchOptions,\n searchClient,\n indexName,\n cloneComponent\n) {\n /* eslint-disable no-param-reassign */\n\n const helper = algoliaHelper(searchClient, indexName);\n const search = instantsearch(instantSearchOptions);\n\n let resultsState;\n\n /**\n * main API for SSR, called in serverPrefetch of a root component which contains instantsearch\n * @param {object} componentInstance the calling component's `this`\n * @returns {Promise} result of the search, to save for .hydrate\n */\n search.findResultsState = function(componentInstance) {\n let _renderToString;\n try {\n _renderToString = require('vue-server-renderer/basic');\n } catch (e) {\n // error is handled by regular if, in case it's `undefined`\n }\n if (!_renderToString) {\n throw new Error('you need to install vue-server-renderer');\n }\n\n let app;\n\n return Promise.resolve()\n .then(() => {\n app = cloneComponent(componentInstance);\n\n app.instantsearch.helper = helper;\n app.instantsearch.mainHelper = helper;\n\n app.instantsearch.mainIndex.init({\n instantSearchInstance: app.instantsearch,\n parent: null,\n uiState: app.instantsearch._initialUiState,\n });\n })\n .then(() => renderToString(app, _renderToString))\n .then(() => searchOnlyWithDerivedHelpers(helper))\n .then(() => {\n const results = {};\n walkIndex(app.instantsearch.mainIndex, widget => {\n results[widget.getIndexId()] = widget.getResults();\n });\n\n search.hydrate(results);\n\n resultsState = Object.keys(results)\n .map(indexId => {\n const { _state, _rawResults } = results[indexId];\n return [\n indexId,\n {\n // copy just the values of SearchParameters, not the functions\n _state: Object.keys(_state).reduce((acc, key) => {\n acc[key] = _state[key];\n return acc;\n }, {}),\n _rawResults,\n },\n ];\n })\n .reduce(\n (acc, [key, val]) => {\n acc[key] = val;\n return acc;\n },\n {\n __identifier: 'stringified',\n }\n );\n return search.getState();\n });\n };\n\n /**\n * @returns {Promise} result state to serialize and enter into .hydrate\n */\n search.getState = function() {\n if (!resultsState) {\n throw new Error('You need to wait for findResultsState to finish');\n }\n return resultsState;\n };\n\n /**\n * make sure correct data is available in each widget's state.\n * called in widget mixin with (this.widget, this)\n *\n * @param {object} widget The widget instance\n * @param {object} parent The local parent index\n * @returns {void}\n */\n search.__forceRender = function(widget, parent) {\n const localHelper = parent.getHelper();\n\n const results = search.__initialSearchResults[parent.getIndexId()];\n\n // this happens when a different InstantSearch gets rendered initially,\n // after the hydrate finished. There's thus no initial results available.\n if (!results) {\n return;\n }\n\n const state = results._state;\n\n // helper gets created in init, but that means it doesn't get the injected\n // parameters, because those are from the lastResults\n localHelper.state = state;\n\n widget.render({\n helper: localHelper,\n results,\n scopedResults: parent.getScopedResults().map(result =>\n Object.assign(result, {\n results: search.__initialSearchResults[result.indexId],\n })\n ),\n state,\n templatesConfig: {},\n createURL: parent.createURL,\n instantSearchInstance: search,\n searchMetadata: {\n isSearchStalled: false,\n },\n });\n };\n\n /**\n * Called both in server\n * @param {object} results a map of indexId: SearchResults\n * @returns {void}\n */\n search.hydrate = function(results) {\n if (!results) {\n warn(\n 'The result of `findResultsState()` needs to be passed to `hydrate()`.'\n );\n return;\n }\n\n const initialResults =\n results.__identifier === 'stringified'\n ? Object.keys(results).reduce((acc, indexId) => {\n if (indexId === '__identifier') {\n return acc;\n }\n acc[indexId] = new SearchResults(\n new SearchParameters(results[indexId]._state),\n results[indexId]._rawResults\n );\n return acc;\n }, {})\n : results;\n\n search.__initialSearchResults = initialResults;\n\n search.helper = helper;\n search.mainHelper = helper;\n\n search.mainIndex.init({\n instantSearchInstance: search,\n parent: null,\n uiState: search._initialUiState,\n });\n };\n\n /* eslint-enable no-param-reassign */\n return search;\n}\n\nexport function createServerRootMixin(instantSearchOptions = {}) {\n const {\n searchClient,\n indexName,\n $cloneComponent = defaultCloneComponent,\n } = instantSearchOptions;\n\n if (!searchClient || !indexName) {\n throw new Error(\n 'createServerRootMixin requires `searchClient` and `indexName` in the first argument'\n );\n }\n\n const search = augmentInstantSearch(\n instantSearchOptions,\n searchClient,\n indexName,\n $cloneComponent\n );\n\n // put this in the user's root Vue instance\n // we can then reuse that InstantSearch instance seamlessly from `ais-instant-search-ssr`\n const rootMixin = {\n provide() {\n return {\n $_ais_ssrInstantSearchInstance: this.instantsearch,\n };\n },\n data() {\n return {\n // this is in data, so that the real & duplicated render do not share\n // the same instantsearch instance.\n instantsearch: search,\n };\n },\n };\n\n return rootMixin;\n}\n"],"names":["defaultCloneComponent","componentInstance","const","options","serverPrefetch","undefined","fetch","_base","name","router","$router","store","$store","app","$vnode","componentOptions","Ctor","extend","Vue","component","Object","assign","$options","propsData","$slots","$root","augmentInstantSearch","instantSearchOptions","searchClient","indexName","cloneComponent","resultsState","helper","algoliaHelper","search","instantsearch","findResultsState","let","_renderToString","require","e","Error","Promise","resolve","then","mainHelper","mainIndex","init","instantSearchInstance","parent","uiState","_initialUiState","reject","err","res","renderToString","searchOnlyWithDerivedHelpers","derivedHelpers","on","forEach","derivedHelper","results","walkIndex","indexWidget","visit","getWidgets","widget","$$type","getIndexId","getResults","hydrate","keys","map","indexId","_state","reduce","acc","key","_rawResults","ref","val","__identifier","getState","__forceRender","localHelper","getHelper","__initialSearchResults","state","render","scopedResults","getScopedResults","result","templatesConfig","createURL","searchMetadata","isSearchStalled","initialResults","SearchResults","SearchParameters","warn","createServerRootMixin","$cloneComponent","provide","$_ais_ssrInstantSearchInstance","this","data"],"mappings":"2HAGA,2CAuCA,SAASA,EAAsBC,GAC7BC,IAAMC,EAAU,CACdC,oBAAgBC,EAChBC,WAAOD,EACPE,WAAOF,EACPG,KAAM,yBAENC,OAAQR,EAAkBS,QAC1BC,MAAOV,EAAkBW,QAOrBC,EAAM,IAJKZ,EAAkBa,OAC/Bb,EAAkBa,OAAOC,iBAAiBC,KAAKC,OAAOd,GACtDe,EAAIC,UAAUC,OAAOC,OAAO,GAAIpB,EAAkBqB,SAAUnB,KAEvC,CACvBoB,UAAWtB,EAAkBqB,SAASC,YAQxC,OAJAV,EAAIW,OAASvB,EAAkBuB,OAC/BX,EAAIY,MAAQxB,EAAkBwB,MAC9BZ,EAAIS,SAASlB,eAAiB,GAEvBS,EAGT,SAASa,EACPC,EACAC,EACAC,EACAC,GAIA5B,IAGI6B,EAHEC,EAASC,EAAcL,EAAcC,GACrCK,EAASC,EAAcR,GAsK7B,OA7JAO,EAAOE,iBAAmB,SAASnC,GACjCoC,IAAIC,EAUAzB,EATJ,IACEyB,EAAkBC,QAAQ,6BAC1B,MAAOC,IAGT,IAAKF,EACH,MAAM,IAAIG,MAAM,2CAKlB,OAAOC,QAAQC,UACZC,iBACC/B,EAAMiB,EAAe7B,IAEjBkC,cAAcH,OAASA,EAC3BnB,EAAIsB,cAAcU,WAAab,EAE/BnB,EAAIsB,cAAcW,UAAUC,KAAK,CAC/BC,sBAAuBnC,EAAIsB,cAC3Bc,OAAQ,KACRC,QAASrC,EAAIsB,cAAcgB,oBAG9BP,uBAjGP,SAAwB/B,EAAKyB,GAC3B,OAAO,IAAII,iBAASC,EAASS,UAC3Bd,EAAgBzB,WAAMwC,EAAKC,GACrBD,GAAKD,EAAOC,GAChBV,EAAQW,OA6FIC,CAAe1C,EAAKyB,KAC/BM,uBAzFP,SAAsCZ,GACpC,OAAO,IAAIU,iBAASC,EAASS,GAC3BpB,EAAOwB,+BAGPxB,EAAOyB,eAAe,GAAGC,GAAG,oBAC1Bf,MAGFX,EAAOyB,eAAeE,iBAAQC,UAC5BA,EAAcF,GAAG,iBAASlB,GACxBY,EAAOZ,SA8EGgB,CAA6BxB,KACxCY,gBACC1C,IAAM2D,EAAU,GA+BhB,OA7IR,SAASC,EAAUC,EAAaC,GAG9B,OAFAA,EAAMD,GAECA,EAAYE,aAAaN,iBAAQO,GAChB,cAAlBA,EAAOC,SACXH,EAAME,GACNJ,EAAUI,EAAQF,MAyGdF,CAAUjD,EAAIsB,cAAcW,mBAAWoB,GACrCL,EAAQK,EAAOE,cAAgBF,EAAOG,eAGxCnC,EAAOoC,QAAQT,GAEf9B,EAAeX,OAAOmD,KAAKV,GACxBW,aAAIC,GACH,MAAgCZ,EAAQY,8BACxC,MAAO,CACLA,EACA,CAEEC,OAAQtD,OAAOmD,KAAKG,GAAQC,gBAAQC,EAAKC,GAEvC,OADAD,EAAIC,GAAOH,EAAOG,GACXD,GACN,gBACHE,MAILH,gBACEC,EAAKG,qBAEJ,OADAH,EAAIC,GAAOG,EACJJ,GAET,CACEK,aAAc,gBAGb/C,EAAOgD,cAOpBhD,EAAOgD,SAAW,WAChB,IAAKnD,EACH,MAAM,IAAIU,MAAM,mDAElB,OAAOV,GAWTG,EAAOiD,cAAgB,SAASjB,EAAQjB,GACtC/C,IAAMkF,EAAcnC,EAAOoC,YAErBxB,EAAU3B,EAAOoD,uBAAuBrC,EAAOmB,cAIrD,GAAKP,EAAL,CAIA3D,IAAMqF,EAAQ1B,EAAQa,OAItBU,EAAYG,MAAQA,EAEpBrB,EAAOsB,OAAO,CACZxD,OAAQoD,UACRvB,EACA4B,cAAexC,EAAOyC,mBAAmBlB,aAAImB,UAC3CvE,OAAOC,OAAOsE,EAAQ,CACpB9B,QAAS3B,EAAOoD,uBAAuBK,EAAOlB,mBAGlDc,EACAK,gBAAiB,GACjBC,UAAW5C,EAAO4C,UAClB7C,sBAAuBd,EACvB4D,eAAgB,CACdC,iBAAiB,OAUvB7D,EAAOoC,QAAU,SAAST,GACxB,GAAKA,EAAL,CAOA3D,IAAM8F,EACqB,gBAAzBnC,EAAQoB,aACJ7D,OAAOmD,KAAKV,GAASc,gBAAQC,EAAKH,GAChC,MAAgB,iBAAZA,EACKG,GAETA,EAAIH,GAAW,IAAIwB,EACjB,IAAIC,EAAiBrC,EAAQY,GAASC,QACtCb,EAAQY,GAASK,aAEZF,IACN,IACHf,EAEN3B,EAAOoD,uBAAyBU,EAEhC9D,EAAOF,OAASA,EAChBE,EAAOW,WAAab,EAEpBE,EAAOY,UAAUC,KAAK,CACpBC,sBAAuBd,EACvBe,OAAQ,KACRC,QAAShB,EAAOiB,uBA5BhBgD,EACE,0EAgCCjE,EAGF,SAASkE,EAAsBzE,kBAAuB,IAC3D,uDAMA,kBAHoB3B,IAGf4B,IAAiBC,EACpB,MAAM,IAAIY,MACR,uFAIJvC,IAAMgC,EAASR,EACbC,EACAC,EACAC,EACAwE,GAoBF,MAfkB,CAChBC,mBACE,MAAO,CACLC,+BAAgCC,KAAKrE,gBAGzCsE,gBACE,MAAO,CAGLtE,cAAeD"}
|
|
1
|
+
{"version":3,"file":"createServerRootMixin.js","sources":["../../../src/util/createServerRootMixin.js"],"sourcesContent":["import Vue from 'vue';\nimport instantsearch from 'instantsearch.js/es';\nimport { warn } from './warn';\n\nfunction walkIndex(indexWidget, visit) {\n visit(indexWidget);\n\n return indexWidget.getWidgets().forEach(widget => {\n if (widget.$$type !== 'ais.index') return;\n visit(widget);\n walkIndex(widget, visit);\n });\n}\n\nfunction renderToString(app, _renderToString) {\n return new Promise((resolve, reject) =>\n _renderToString(app, (err, res) => {\n if (err) reject(err);\n resolve(res);\n })\n );\n}\n\nfunction searchOnlyWithDerivedHelpers(helper) {\n return new Promise((resolve, reject) => {\n helper.searchOnlyWithDerivedHelpers();\n\n // we assume all derived helpers resolve at least in the same tick\n helper.derivedHelpers[0].on('result', () => {\n resolve();\n });\n\n helper.derivedHelpers.forEach(derivedHelper =>\n derivedHelper.on('error', e => {\n reject(e);\n })\n );\n });\n}\n\nfunction defaultCloneComponent(componentInstance) {\n const options = {\n serverPrefetch: undefined,\n fetch: undefined,\n _base: undefined,\n name: 'ais-ssr-root-component',\n // copy over global Vue APIs\n router: componentInstance.$router,\n store: componentInstance.$store,\n };\n\n const Extended = componentInstance.$vnode\n ? componentInstance.$vnode.componentOptions.Ctor.extend(options)\n : Vue.component(\n options.name,\n Object.assign({}, componentInstance.$options, options)\n );\n\n const app = new Extended({\n propsData: componentInstance.$options.propsData,\n });\n\n // https://stackoverflow.com/a/48195006/3185307\n app.$slots = componentInstance.$slots;\n app.$root = componentInstance.$root;\n app.$options.serverPrefetch = [];\n\n return app;\n}\n\nfunction augmentInstantSearch(instantSearchOptions, cloneComponent) {\n const search = instantsearch(instantSearchOptions);\n\n let initialResults;\n\n /**\n * main API for SSR, called in serverPrefetch of a root component which contains instantsearch\n * @param {object} componentInstance the calling component's `this`\n * @returns {Promise} result of the search, to save for .hydrate\n */\n search.findResultsState = function(componentInstance) {\n let _renderToString;\n try {\n _renderToString = require('vue-server-renderer/basic');\n } catch (e) {\n // error is handled by regular if, in case it's `undefined`\n }\n if (!_renderToString) {\n throw new Error('you need to install vue-server-renderer');\n }\n\n let app;\n let instance;\n\n return Promise.resolve()\n .then(() => {\n app = cloneComponent(componentInstance);\n\n instance = app.instantsearch;\n\n instance.start();\n // although we use start for initializing the main index,\n // we don't want to send search requests yet\n instance.started = false;\n })\n .then(() => renderToString(app, _renderToString))\n .then(() => searchOnlyWithDerivedHelpers(instance.mainHelper))\n .then(() => {\n initialResults = {};\n walkIndex(instance.mainIndex, widget => {\n const { _state, _rawResults } = widget.getResults();\n\n initialResults[widget.getIndexId()] = {\n // copy just the values of SearchParameters, not the functions\n state: Object.keys(_state).reduce((acc, key) => {\n // eslint-disable-next-line no-param-reassign\n acc[key] = _state[key];\n return acc;\n }, {}),\n results: _rawResults,\n };\n });\n\n search.hydrate(initialResults);\n return search.getState();\n });\n };\n\n /**\n * @returns {Promise} result state to serialize and enter into .hydrate\n */\n search.getState = function() {\n if (!initialResults) {\n throw new Error('You need to wait for findResultsState to finish');\n }\n return initialResults;\n };\n\n /**\n * make sure correct data is available in each widget's state.\n * called in widget mixin with (this.widget, this)\n *\n * @param {object} widget The widget instance\n * @param {object} parent The local parent index\n * @returns {void}\n */\n search.__forceRender = function(widget, parent) {\n const results = parent.getResults();\n\n // this happens when a different InstantSearch gets rendered initially,\n // after the hydrate finished. There's thus no initial results available.\n if (results === null) {\n return;\n }\n\n const state = results._state;\n\n const localHelper = parent.getHelper();\n // helper gets created in init, but that means it doesn't get the injected\n // parameters, because those are from the lastResults\n localHelper.state = state;\n\n widget.render({\n helper: localHelper,\n results,\n scopedResults: parent.getScopedResults(),\n parent,\n state,\n templatesConfig: {},\n createURL: parent.createURL,\n instantSearchInstance: search,\n searchMetadata: {\n isSearchStalled: false,\n },\n });\n };\n\n /**\n * Called both in server\n * @param {object} results a map of indexId: SearchResults\n * @returns {void}\n */\n search.hydrate = function(results) {\n if (!results) {\n warn(\n 'The result of `findResultsState()` needs to be passed to `hydrate()`.'\n );\n return;\n }\n\n search._initialResults = results;\n\n search.start();\n search.started = false;\n };\n return search;\n}\n\nexport function createServerRootMixin(instantSearchOptions = {}) {\n const { $cloneComponent = defaultCloneComponent } = instantSearchOptions;\n\n const search = augmentInstantSearch(instantSearchOptions, $cloneComponent);\n\n // put this in the user's root Vue instance\n // we can then reuse that InstantSearch instance seamlessly from `ais-instant-search-ssr`\n const rootMixin = {\n provide() {\n return {\n $_ais_ssrInstantSearchInstance: this.instantsearch,\n };\n },\n data() {\n return {\n // this is in data, so that the real & cloned render do not share\n // the same instantsearch instance.\n instantsearch: search,\n };\n },\n };\n\n return rootMixin;\n}\n"],"names":["defaultCloneComponent","componentInstance","const","options","serverPrefetch","undefined","fetch","_base","name","router","$router","store","$store","app","$vnode","componentOptions","Ctor","extend","Vue","component","Object","assign","$options","propsData","$slots","$root","augmentInstantSearch","instantSearchOptions","cloneComponent","initialResults","search","instantsearch","findResultsState","let","_renderToString","instance","require","e","Error","Promise","resolve","then","start","started","reject","err","res","renderToString","helper","mainHelper","searchOnlyWithDerivedHelpers","derivedHelpers","on","forEach","derivedHelper","walkIndex","indexWidget","visit","getWidgets","widget","$$type","mainIndex","getResults","getIndexId","state","keys","_state","reduce","acc","key","results","_rawResults","hydrate","getState","__forceRender","parent","localHelper","getHelper","render","scopedResults","getScopedResults","templatesConfig","createURL","instantSearchInstance","searchMetadata","isSearchStalled","_initialResults","warn","createServerRootMixin","$cloneComponent","provide","$_ais_ssrInstantSearchInstance","this","data"],"mappings":"uFAwCA,SAASA,EAAsBC,GAC7BC,IAAMC,EAAU,CACdC,oBAAgBC,EAChBC,WAAOD,EACPE,WAAOF,EACPG,KAAM,yBAENC,OAAQR,EAAkBS,QAC1BC,MAAOV,EAAkBW,QAUrBC,EAAM,IAPKZ,EAAkBa,OAC/Bb,EAAkBa,OAAOC,iBAAiBC,KAAKC,OAAOd,GACtDe,EAAIC,UACFhB,EAAQK,KACRY,OAAOC,OAAO,GAAIpB,EAAkBqB,SAAUnB,KAG3B,CACvBoB,UAAWtB,EAAkBqB,SAASC,YAQxC,OAJAV,EAAIW,OAASvB,EAAkBuB,OAC/BX,EAAIY,MAAQxB,EAAkBwB,MAC9BZ,EAAIS,SAASlB,eAAiB,GAEvBS,EAGT,SAASa,EAAqBC,EAAsBC,GAClD1B,IAEI2B,EAFEC,EAASC,EAAcJ,GA4H7B,OAnHAG,EAAOE,iBAAmB,SAAS/B,GACjCgC,IAAIC,EAUArB,EACAsB,EAVJ,IACED,EAAkBE,QAAQ,6BAC1B,MAAOC,IAGT,IAAKH,EACH,MAAM,IAAII,MAAM,2CAMlB,OAAOC,QAAQC,UACZC,gBACC5B,EAAMe,EAAe3B,IAErBkC,EAAWtB,EAAIkB,eAENW,QAGTP,EAASQ,SAAU,IAEpBF,uBA3FP,SAAwB5B,EAAKqB,GAC3B,OAAO,IAAIK,iBAASC,EAASI,UAC3BV,EAAgBrB,WAAMgC,EAAKC,GACrBD,GAAKD,EAAOC,GAChBL,EAAQM,OAuFIC,CAAelC,EAAKqB,KAC/BO,uBAnF+BO,EAmFSb,EAASc,WAlF/C,IAAIV,iBAASC,EAASI,GAC3BI,EAAOE,+BAGPF,EAAOG,eAAe,GAAGC,GAAG,oBAC1BZ,MAGFQ,EAAOG,eAAeE,iBAAQC,UAC5BA,EAAcF,GAAG,iBAASf,GACxBO,EAAOP,SAXf,IAAsCW,IAoF/BP,gBAiBC,OAhBAZ,EAAiB,GAxGzB,SAAS0B,EAAUC,EAAaC,GAG9B,OAFAA,EAAMD,GAECA,EAAYE,aAAaL,iBAAQM,GAChB,cAAlBA,EAAOC,SACXH,EAAME,GACNJ,EAAUI,EAAQF,MAmGdF,CAAUpB,EAAS0B,mBAAWF,GAC5B,MAAgCA,EAAOG,wCAEvCjC,EAAe8B,EAAOI,cAAgB,CAEpCC,MAAO5C,OAAO6C,KAAKC,GAAQC,gBAAQC,EAAKC,GAGtC,OADAD,EAAIC,GAAOH,EAAOG,GACXD,GACN,IACHE,QAASC,KAIbzC,EAAO0C,QAAQ3C,GACRC,EAAO2C,cAOpB3C,EAAO2C,SAAW,WAChB,IAAK5C,EACH,MAAM,IAAIS,MAAM,mDAElB,OAAOT,GAWTC,EAAO4C,cAAgB,SAASf,EAAQgB,GACtCzE,IAAMoE,EAAUK,EAAOb,aAIvB,GAAgB,OAAZQ,EAAJ,CAIApE,IAAM8D,EAAQM,EAAQJ,OAEhBU,EAAcD,EAAOE,YAG3BD,EAAYZ,MAAQA,EAEpBL,EAAOmB,OAAO,CACZ9B,OAAQ4B,UACRN,EACAS,cAAeJ,EAAOK,0BACtBL,QACAX,EACAiB,gBAAiB,GACjBC,UAAWP,EAAOO,UAClBC,sBAAuBrD,EACvBsD,eAAgB,CACdC,iBAAiB,OAUvBvD,EAAO0C,QAAU,SAASF,GACnBA,GAOLxC,EAAOwD,gBAAkBhB,EAEzBxC,EAAOY,QACPZ,EAAOa,SAAU,GATf4C,EACE,0EAUCzD,EAGF,SAAS0D,EAAsB7D,kBAAuB,IAC3D,uCAA0B3B,GAE1BE,IAAM4B,EAASJ,EAAqBC,EAAsB8D,GAmB1D,MAfkB,CAChBC,mBACE,MAAO,CACLC,+BAAgCC,KAAK7D,gBAGzC8D,gBACE,MAAO,CAGL9D,cAAeD"}
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"autocomplete"
|
|
17
17
|
],
|
|
18
18
|
"license": "MIT",
|
|
19
|
-
"version": "3.
|
|
19
|
+
"version": "3.9.0",
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
22
|
"src",
|
|
@@ -45,8 +45,7 @@
|
|
|
45
45
|
"release": "shipjs prepare"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"
|
|
49
|
-
"instantsearch.js": "^4.25.0"
|
|
48
|
+
"instantsearch.js": "^4.34.0"
|
|
50
49
|
},
|
|
51
50
|
"peerDependencies": {
|
|
52
51
|
"algoliasearch": ">= 3.32.0 < 5",
|
|
@@ -67,6 +66,7 @@
|
|
|
67
66
|
"@wdio/spec-reporter": "^5.11.7",
|
|
68
67
|
"@wdio/static-server-service": "^5.11.0",
|
|
69
68
|
"algoliasearch": "4.0.1",
|
|
69
|
+
"algoliasearch-helper": "3.6.2",
|
|
70
70
|
"babel-eslint": "10.0.1",
|
|
71
71
|
"babel-jest": "23.6.0",
|
|
72
72
|
"babel-preset-es2015": "6.24.1",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"bundlesize": [
|
|
115
115
|
{
|
|
116
116
|
"path": "./dist/vue-instantsearch.js",
|
|
117
|
-
"maxSize": "
|
|
117
|
+
"maxSize": "56.50 kB"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"path": "./dist/vue-instantsearch.common.js",
|
package/src/mixins/widget.js
CHANGED
|
@@ -30,7 +30,7 @@ export const createWidgetMixin = ({ connector } = {}) => ({
|
|
|
30
30
|
this.getParentIndex().addWidgets([this.widget]);
|
|
31
31
|
|
|
32
32
|
if (
|
|
33
|
-
this.instantSearchInstance.
|
|
33
|
+
this.instantSearchInstance._initialResults &&
|
|
34
34
|
!this.instantSearchInstance.started
|
|
35
35
|
) {
|
|
36
36
|
if (typeof this.instantSearchInstance.__forceRender !== 'function') {
|
|
@@ -10,11 +10,7 @@ import SearchBox from '../../components/SearchBox.vue';
|
|
|
10
10
|
import { createWidgetMixin } from '../../mixins/widget';
|
|
11
11
|
import { createFakeClient } from '../testutils/client';
|
|
12
12
|
import { createSerializedState } from '../testutils/helper';
|
|
13
|
-
import {
|
|
14
|
-
SearchResults,
|
|
15
|
-
SearchParameters,
|
|
16
|
-
AlgoliaSearchHelper,
|
|
17
|
-
} from 'algoliasearch-helper';
|
|
13
|
+
import { SearchParameters, SearchResults } from 'algoliasearch-helper';
|
|
18
14
|
|
|
19
15
|
jest.unmock('instantsearch.js/es');
|
|
20
16
|
|
|
@@ -54,9 +50,11 @@ describe('createServerRootMixin', () => {
|
|
|
54
50
|
}),
|
|
55
51
|
],
|
|
56
52
|
})
|
|
57
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
).toThrowErrorMatchingInlineSnapshot(`
|
|
54
|
+
"The \`searchClient\` option is required.
|
|
55
|
+
|
|
56
|
+
See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/"
|
|
57
|
+
`);
|
|
60
58
|
});
|
|
61
59
|
|
|
62
60
|
it('requires indexName', () => {
|
|
@@ -70,9 +68,11 @@ describe('createServerRootMixin', () => {
|
|
|
70
68
|
}),
|
|
71
69
|
],
|
|
72
70
|
})
|
|
73
|
-
).toThrowErrorMatchingInlineSnapshot(
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
).toThrowErrorMatchingInlineSnapshot(`
|
|
72
|
+
"The \`indexName\` option is required.
|
|
73
|
+
|
|
74
|
+
See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/"
|
|
75
|
+
`);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
it('creates an instantsearch instance on "data"', () => {
|
|
@@ -246,6 +246,7 @@ Array [
|
|
|
246
246
|
h(SearchBox),
|
|
247
247
|
]);
|
|
248
248
|
},
|
|
249
|
+
|
|
249
250
|
serverPrefetch() {
|
|
250
251
|
return this.instantsearch.findResultsState(this);
|
|
251
252
|
},
|
|
@@ -388,7 +389,7 @@ Array [
|
|
|
388
389
|
this.instantsearch.mainIndex.getWidgets().map(w => w.$$type)
|
|
389
390
|
).toEqual(['ais.configure']);
|
|
390
391
|
|
|
391
|
-
expect(res.hello.
|
|
392
|
+
expect(res.hello.state.hitsPerPage).toBe(100);
|
|
392
393
|
})
|
|
393
394
|
// jest throws an error we need to catch, since stuck in the flow
|
|
394
395
|
.catch(e => {
|
|
@@ -423,7 +424,7 @@ Array [
|
|
|
423
424
|
|
|
424
425
|
expect.assertions(2);
|
|
425
426
|
|
|
426
|
-
const App =
|
|
427
|
+
const App = {
|
|
427
428
|
mixins: [
|
|
428
429
|
forceIsServerMixin,
|
|
429
430
|
createServerRootMixin({
|
|
@@ -431,11 +432,8 @@ Array [
|
|
|
431
432
|
indexName: 'hello',
|
|
432
433
|
}),
|
|
433
434
|
],
|
|
434
|
-
render
|
|
435
|
-
|
|
436
|
-
this.$scopedSlots.default({ test: true }),
|
|
437
|
-
]);
|
|
438
|
-
},
|
|
435
|
+
render: h =>
|
|
436
|
+
h(InstantSearchSsr, {}, [this.$scopedSlots.default({ test: true })]),
|
|
439
437
|
serverPrefetch() {
|
|
440
438
|
return (
|
|
441
439
|
this.instantsearch
|
|
@@ -453,26 +451,23 @@ Array [
|
|
|
453
451
|
})
|
|
454
452
|
);
|
|
455
453
|
},
|
|
456
|
-
}
|
|
454
|
+
};
|
|
457
455
|
|
|
458
456
|
const wrapper = new Vue({
|
|
459
457
|
mixins: [forceIsServerMixin],
|
|
460
|
-
render
|
|
461
|
-
|
|
458
|
+
render: h =>
|
|
459
|
+
h(App, {
|
|
462
460
|
scopedSlots: {
|
|
463
461
|
default({ test }) {
|
|
464
462
|
if (test) {
|
|
465
463
|
return h(Configure, {
|
|
466
|
-
|
|
467
|
-
hitsPerPage: 100,
|
|
468
|
-
},
|
|
464
|
+
hitsPerPage: 100,
|
|
469
465
|
});
|
|
470
466
|
}
|
|
471
467
|
return null;
|
|
472
468
|
},
|
|
473
469
|
},
|
|
474
|
-
})
|
|
475
|
-
},
|
|
470
|
+
}),
|
|
476
471
|
});
|
|
477
472
|
|
|
478
473
|
await renderToString(wrapper);
|
|
@@ -519,63 +514,76 @@ Array [
|
|
|
519
514
|
|
|
520
515
|
await renderToString(wrapper);
|
|
521
516
|
});
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
describe('hydrate', () => {
|
|
525
|
-
it('sets __initialSearchResults', () => {
|
|
526
|
-
const serialized = createSerializedState();
|
|
527
517
|
|
|
518
|
+
it('searches only once', async () => {
|
|
519
|
+
const searchClient = createFakeClient();
|
|
528
520
|
const app = {
|
|
529
521
|
mixins: [
|
|
522
|
+
forceIsServerMixin,
|
|
530
523
|
createServerRootMixin({
|
|
531
|
-
searchClient
|
|
524
|
+
searchClient,
|
|
532
525
|
indexName: 'hello',
|
|
533
526
|
}),
|
|
534
527
|
],
|
|
535
|
-
render
|
|
536
|
-
|
|
528
|
+
render: h =>
|
|
529
|
+
/**
|
|
530
|
+
* This code triggers this warning in Vue 3:
|
|
531
|
+
* > Non-function value encountered for default slot. Prefer function slots for better performance.
|
|
532
|
+
*
|
|
533
|
+
* To fix it, replace the third argument
|
|
534
|
+
* > [h(...), h(...)]
|
|
535
|
+
* with
|
|
536
|
+
* > { default: () => [h(...), h(...)] }
|
|
537
|
+
*
|
|
538
|
+
* but it's not important (and not compatible in vue2), we're leaving it as-is.
|
|
539
|
+
*/
|
|
540
|
+
h(InstantSearchSsr, {}, [
|
|
537
541
|
h(Configure, {
|
|
538
542
|
attrs: {
|
|
539
543
|
hitsPerPage: 100,
|
|
540
544
|
},
|
|
541
545
|
}),
|
|
542
546
|
h(SearchBox),
|
|
543
|
-
])
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
created() {
|
|
547
|
-
this.instantsearch.hydrate({
|
|
548
|
-
__identifier: 'stringified',
|
|
549
|
-
hello: serialized,
|
|
550
|
-
});
|
|
547
|
+
]),
|
|
548
|
+
serverPrefetch() {
|
|
549
|
+
return this.instantsearch.findResultsState(this);
|
|
551
550
|
},
|
|
552
551
|
};
|
|
553
552
|
|
|
554
|
-
const {
|
|
555
|
-
|
|
556
|
-
|
|
553
|
+
const wrapper = new Vue({
|
|
554
|
+
mixins: [forceIsServerMixin],
|
|
555
|
+
render: h => h(app),
|
|
556
|
+
});
|
|
557
557
|
|
|
558
|
-
|
|
559
|
-
expect.objectContaining({ hello: expect.any(SearchResults) })
|
|
560
|
-
);
|
|
558
|
+
await renderToString(wrapper);
|
|
561
559
|
|
|
562
|
-
expect(
|
|
563
|
-
|
|
564
|
-
|
|
560
|
+
expect(searchClient.search).toHaveBeenCalledTimes(1);
|
|
561
|
+
expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
|
|
562
|
+
Array [
|
|
563
|
+
Object {
|
|
564
|
+
"indexName": "hello",
|
|
565
|
+
"params": Object {
|
|
566
|
+
"facets": Array [],
|
|
567
|
+
"hitsPerPage": 100,
|
|
568
|
+
"query": "",
|
|
569
|
+
"tagFilters": "",
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
]
|
|
573
|
+
`);
|
|
565
574
|
});
|
|
575
|
+
});
|
|
566
576
|
|
|
567
|
-
|
|
577
|
+
describe('hydrate', () => {
|
|
578
|
+
it('sets _initialResults', () => {
|
|
568
579
|
const serialized = createSerializedState();
|
|
569
|
-
const nonSerialized = new SearchResults(
|
|
570
|
-
new SearchParameters(serialized._state),
|
|
571
|
-
serialized._rawResults
|
|
572
|
-
);
|
|
573
580
|
|
|
574
|
-
|
|
581
|
+
let instantsearch;
|
|
582
|
+
const app = new Vue({
|
|
575
583
|
mixins: [
|
|
576
584
|
createServerRootMixin({
|
|
577
585
|
searchClient: createFakeClient(),
|
|
578
|
-
indexName: '
|
|
586
|
+
indexName: 'hello',
|
|
579
587
|
}),
|
|
580
588
|
],
|
|
581
589
|
render(h) {
|
|
@@ -590,27 +598,35 @@ Array [
|
|
|
590
598
|
},
|
|
591
599
|
// in test, beforeCreated doesn't have $data yet, but IRL it does
|
|
592
600
|
created() {
|
|
601
|
+
instantsearch = this.instantsearch;
|
|
593
602
|
this.instantsearch.hydrate({
|
|
594
|
-
|
|
603
|
+
hello: serialized,
|
|
595
604
|
});
|
|
596
605
|
},
|
|
597
|
-
};
|
|
606
|
+
});
|
|
598
607
|
|
|
599
|
-
|
|
600
|
-
vm: { instantsearch },
|
|
601
|
-
} = mount(app);
|
|
608
|
+
mount(app);
|
|
602
609
|
|
|
603
|
-
expect(instantsearch.
|
|
604
|
-
expect.objectContaining({
|
|
610
|
+
expect(instantsearch._initialResults).toEqual(
|
|
611
|
+
expect.objectContaining({
|
|
612
|
+
hello: {
|
|
613
|
+
state: expect.any(Object),
|
|
614
|
+
results: expect.any(Object),
|
|
615
|
+
},
|
|
616
|
+
})
|
|
605
617
|
);
|
|
606
618
|
|
|
607
|
-
expect(instantsearch.
|
|
619
|
+
expect(instantsearch._initialResults.hello).toEqual(
|
|
620
|
+
expect.objectContaining(serialized)
|
|
621
|
+
);
|
|
608
622
|
});
|
|
609
623
|
|
|
610
624
|
it('inits the main index', () => {
|
|
611
625
|
const serialized = createSerializedState();
|
|
612
626
|
|
|
613
|
-
|
|
627
|
+
let instantsearch;
|
|
628
|
+
|
|
629
|
+
const app = new Vue({
|
|
614
630
|
mixins: [
|
|
615
631
|
createServerRootMixin({
|
|
616
632
|
searchClient: createFakeClient(),
|
|
@@ -627,28 +643,30 @@ Array [
|
|
|
627
643
|
h(SearchBox),
|
|
628
644
|
]);
|
|
629
645
|
},
|
|
630
|
-
|
|
646
|
+
created() {
|
|
647
|
+
instantsearch = this.instantsearch;
|
|
648
|
+
},
|
|
649
|
+
});
|
|
631
650
|
|
|
632
|
-
|
|
633
|
-
vm: { instantsearch },
|
|
634
|
-
} = mount(app);
|
|
651
|
+
mount(app);
|
|
635
652
|
|
|
636
653
|
expect(instantsearch.mainIndex.getHelper()).toBe(null);
|
|
637
654
|
|
|
638
655
|
instantsearch.hydrate({
|
|
639
|
-
__identifier: 'stringified',
|
|
640
656
|
hello: serialized,
|
|
641
657
|
});
|
|
642
658
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
659
|
+
expect(instantsearch.mainIndex.getHelper().constructor.name).toBe(
|
|
660
|
+
'AlgoliaSearchHelper'
|
|
661
|
+
);
|
|
646
662
|
});
|
|
647
663
|
|
|
648
664
|
it('sets helper & mainHelper', () => {
|
|
649
665
|
const serialized = createSerializedState();
|
|
650
666
|
|
|
651
|
-
|
|
667
|
+
let instantsearch;
|
|
668
|
+
|
|
669
|
+
const app = new Vue({
|
|
652
670
|
mixins: [
|
|
653
671
|
createServerRootMixin({
|
|
654
672
|
searchClient: createFakeClient(),
|
|
@@ -665,22 +683,96 @@ Array [
|
|
|
665
683
|
h(SearchBox),
|
|
666
684
|
]);
|
|
667
685
|
},
|
|
668
|
-
|
|
686
|
+
created() {
|
|
687
|
+
instantsearch = this.instantsearch;
|
|
688
|
+
},
|
|
689
|
+
});
|
|
669
690
|
|
|
670
|
-
|
|
671
|
-
vm: { instantsearch },
|
|
672
|
-
} = mount(app);
|
|
691
|
+
mount(app);
|
|
673
692
|
|
|
674
693
|
expect(instantsearch.helper).toBe(null);
|
|
675
694
|
expect(instantsearch.mainHelper).toBe(null);
|
|
676
695
|
|
|
677
696
|
instantsearch.hydrate({
|
|
678
|
-
__identifier: 'stringified',
|
|
679
697
|
hello: serialized,
|
|
680
698
|
});
|
|
681
699
|
|
|
682
|
-
expect(instantsearch.helper).
|
|
683
|
-
expect(instantsearch.mainHelper).
|
|
700
|
+
expect(instantsearch.helper.constructor.name).toBe('AlgoliaSearchHelper');
|
|
701
|
+
expect(instantsearch.mainHelper.constructor.name).toBe(
|
|
702
|
+
'AlgoliaSearchHelper'
|
|
703
|
+
);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
it('works when component is at root (and therefore has no $vnode)', async () => {
|
|
707
|
+
const searchClient = createFakeClient();
|
|
708
|
+
let mainIndex;
|
|
709
|
+
|
|
710
|
+
const app = {
|
|
711
|
+
render: h =>
|
|
712
|
+
/**
|
|
713
|
+
* This code triggers this warning in Vue 3:
|
|
714
|
+
* > Non-function value encountered for default slot. Prefer function slots for better performance.
|
|
715
|
+
*
|
|
716
|
+
* To fix it, replace the third argument
|
|
717
|
+
* > [h(...), h(...)]
|
|
718
|
+
* with
|
|
719
|
+
* > { default: () => [h(...), h(...)] }
|
|
720
|
+
*
|
|
721
|
+
* but it's not important (and not compatible in vue2), we're leaving it as-is.
|
|
722
|
+
*/
|
|
723
|
+
h(InstantSearchSsr, {}, [
|
|
724
|
+
h(Configure, {
|
|
725
|
+
attrs: {
|
|
726
|
+
hitsPerPage: 100,
|
|
727
|
+
},
|
|
728
|
+
}),
|
|
729
|
+
h(SearchBox),
|
|
730
|
+
]),
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const wrapper = new Vue({
|
|
734
|
+
mixins: [
|
|
735
|
+
forceIsServerMixin,
|
|
736
|
+
createServerRootMixin({
|
|
737
|
+
searchClient,
|
|
738
|
+
indexName: 'hello',
|
|
739
|
+
}),
|
|
740
|
+
],
|
|
741
|
+
serverPrefetch() {
|
|
742
|
+
return this.instantsearch.findResultsState(this);
|
|
743
|
+
},
|
|
744
|
+
created() {
|
|
745
|
+
mainIndex = this.instantsearch.mainIndex;
|
|
746
|
+
},
|
|
747
|
+
render: h => h(app),
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
await renderToString(wrapper);
|
|
751
|
+
|
|
752
|
+
expect(mainIndex.getWidgetState()).toMatchInlineSnapshot(`
|
|
753
|
+
Object {
|
|
754
|
+
"hello": Object {
|
|
755
|
+
"configure": Object {
|
|
756
|
+
"hitsPerPage": 100,
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
}
|
|
760
|
+
`);
|
|
761
|
+
|
|
762
|
+
expect(searchClient.search).toHaveBeenCalledTimes(1);
|
|
763
|
+
expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
|
|
764
|
+
Array [
|
|
765
|
+
Object {
|
|
766
|
+
"indexName": "hello",
|
|
767
|
+
"params": Object {
|
|
768
|
+
"facets": Array [],
|
|
769
|
+
"hitsPerPage": 100,
|
|
770
|
+
"query": "",
|
|
771
|
+
"tagFilters": "",
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
]
|
|
775
|
+
`);
|
|
684
776
|
});
|
|
685
777
|
});
|
|
686
778
|
|
|
@@ -727,6 +819,7 @@ Array [
|
|
|
727
819
|
results: expect.anything(),
|
|
728
820
|
}),
|
|
729
821
|
]),
|
|
822
|
+
parent: expect.anything(),
|
|
730
823
|
state: expect.anything(),
|
|
731
824
|
instantSearchInstance: expect.anything(),
|
|
732
825
|
},
|
|
@@ -735,6 +828,7 @@ Object {
|
|
|
735
828
|
"createURL": [Function],
|
|
736
829
|
"helper": Anything,
|
|
737
830
|
"instantSearchInstance": Anything,
|
|
831
|
+
"parent": Anything,
|
|
738
832
|
"results": Anything,
|
|
739
833
|
"scopedResults": ArrayContaining [
|
|
740
834
|
ObjectContaining {
|
|
@@ -753,6 +847,58 @@ Object {
|
|
|
753
847
|
);
|
|
754
848
|
});
|
|
755
849
|
|
|
850
|
+
it('uses the results passed to hydrate for rendering', () => {
|
|
851
|
+
let instantSearchInstance;
|
|
852
|
+
mount({
|
|
853
|
+
mixins: [
|
|
854
|
+
createServerRootMixin({
|
|
855
|
+
searchClient: createFakeClient(),
|
|
856
|
+
indexName: 'lol',
|
|
857
|
+
}),
|
|
858
|
+
],
|
|
859
|
+
created() {
|
|
860
|
+
instantSearchInstance = this.instantsearch;
|
|
861
|
+
},
|
|
862
|
+
render() {},
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
const widget = {
|
|
866
|
+
init: jest.fn(),
|
|
867
|
+
render: jest.fn(),
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
const resultsState = createSerializedState();
|
|
871
|
+
const state = new SearchParameters(resultsState.state);
|
|
872
|
+
const results = new SearchResults(state, resultsState.results);
|
|
873
|
+
|
|
874
|
+
instantSearchInstance.hydrate({
|
|
875
|
+
lol: resultsState,
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
instantSearchInstance.__forceRender(
|
|
879
|
+
widget,
|
|
880
|
+
instantSearchInstance.mainIndex
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
expect(widget.init).toHaveBeenCalledTimes(0);
|
|
884
|
+
expect(widget.render).toHaveBeenCalledTimes(1);
|
|
885
|
+
|
|
886
|
+
const renderArgs = widget.render.mock.calls[0][0];
|
|
887
|
+
|
|
888
|
+
expect(renderArgs).toEqual(
|
|
889
|
+
expect.objectContaining({
|
|
890
|
+
state,
|
|
891
|
+
results,
|
|
892
|
+
scopedResults: [
|
|
893
|
+
expect.objectContaining({
|
|
894
|
+
indexId: 'lol',
|
|
895
|
+
results,
|
|
896
|
+
}),
|
|
897
|
+
],
|
|
898
|
+
})
|
|
899
|
+
);
|
|
900
|
+
});
|
|
901
|
+
|
|
756
902
|
describe('createURL', () => {
|
|
757
903
|
it('returns # if instantsearch has no routing', () => {
|
|
758
904
|
const app = new Vue({
|