fix(es/minifer): Fix handling of callable expressions (#2379)

swc_ecma_minifier:
 - `sequences`: Don't inline into arrow expression or function expressions.
 - Inject variables to arrow expressions correctly.
This commit is contained in:
Donny/강동윤 2021-10-10 12:47:11 +09:00 committed by GitHub
parent be3dca295b
commit ab687a0f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 3667 additions and 246 deletions

2
Cargo.lock generated
View File

@ -3446,7 +3446,7 @@ dependencies = [
[[package]]
name = "swc_ecma_minifier"
version = "0.37.0"
version = "0.37.1"
dependencies = [
"ansi_term 0.12.1",
"anyhow",

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
license = "Apache-2.0/MIT"
name = "swc_ecma_minifier"
repository = "https://github.com/swc-project/swc.git"
version = "0.37.0"
version = "0.37.1"
[features]
debug = ["backtrace"]

View File

@ -7,7 +7,7 @@ use self::{
use crate::{
analyzer::{analyze, ProgramData, UsageAnalyzer},
compress::hoist_decls::decl_hoister,
debug::{dump, invoke},
debug::dump,
marks::Marks,
mode::Mode,
option::CompressOptions,
@ -217,6 +217,8 @@ where
}
loop {
n.invoke();
self.changed = false;
self.optimize_unit(n);
self.pass += 1;
@ -439,8 +441,6 @@ where
n.visit_mut_children_with(self);
self.optimize_unit_repeatedly(n);
invoke(&*n);
}
fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {

View File

@ -19,8 +19,8 @@ use swc_common::{
};
use swc_ecma_ast::*;
use swc_ecma_utils::{
ident::IdentLike, undefined, ExprExt, ExprFactory, Id, IsEmpty, ModuleItemLike, StmtLike, Type,
Value,
ident::IdentLike, prepend_stmts, undefined, ExprExt, ExprFactory, Id, IsEmpty, ModuleItemLike,
StmtLike, Type, Value,
};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
use tracing::{span, Level};
@ -1482,12 +1482,39 @@ where
noop_visit_mut_type!();
fn visit_mut_arrow_expr(&mut self, n: &mut ArrowExpr) {
let prepend = self.prepend_stmts.take();
let ctx = Ctx {
can_inline_arguments: true,
..self.ctx
};
n.visit_mut_children_with(&mut *self.with_ctx(ctx));
if !self.prepend_stmts.is_empty() {
let mut stmts = self.prepend_stmts.take();
match &mut n.body {
BlockStmtOrExpr::BlockStmt(v) => {
prepend_stmts(&mut v.stmts, stmts.into_iter());
}
BlockStmtOrExpr::Expr(v) => {
self.changed = true;
if cfg!(feature = "debug") {
tracing::debug!("Convertng a body of an arrow expression to BlockStmt");
}
stmts.push(Stmt::Return(ReturnStmt {
span: DUMMY_SP,
arg: Some(v.take()),
}));
n.body = BlockStmtOrExpr::BlockStmt(BlockStmt {
span: DUMMY_SP,
stmts,
});
}
}
}
self.prepend_stmts = prepend;
}
fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
@ -1564,6 +1591,18 @@ where
n.visit_mut_children_with(&mut *self.with_ctx(ctx));
}
fn visit_mut_block_stmt_or_expr(&mut self, n: &mut BlockStmtOrExpr) {
n.visit_mut_children_with(self);
match n {
BlockStmtOrExpr::BlockStmt(n) => {
self.merge_if_returns(&mut n.stmts, false, true);
self.drop_else_token(&mut n.stmts);
}
BlockStmtOrExpr::Expr(_) => {}
}
}
fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
let inline_prevented = self.ctx.inline_prevented || self.has_noinline(e.span);

View File

@ -950,7 +950,7 @@ where
}
match b {
Expr::Update(..) => return false,
Expr::Update(..) | Expr::Arrow(..) | Expr::Fn(..) => return false,
Expr::Cond(b) => {
tracing::trace!("seq: Try test of cond");

View File

@ -90,6 +90,16 @@ where
});
}
fn optimize_fn_stmts(&mut self, stmts: &mut Vec<Stmt>) {
self.remove_useless_return(stmts);
self.negate_if_terminate(stmts, true, false);
if let Some(last) = stmts.last_mut() {
self.drop_unused_stmt_at_end_of_fn(last);
}
}
/// Visit `nodes`, maybe in parallel.
fn visit_par<N>(&mut self, nodes: &mut Vec<N>)
where
@ -170,6 +180,11 @@ where
fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
body.visit_mut_children_with(self);
match body {
BlockStmtOrExpr::BlockStmt(b) => self.optimize_fn_stmts(&mut b.stmts),
BlockStmtOrExpr::Expr(_) => {}
}
self.optimize_arrow_body(body);
}
@ -328,13 +343,7 @@ where
}
if let Some(body) = &mut f.body {
self.remove_useless_return(&mut body.stmts);
self.negate_if_terminate(&mut body.stmts, true, false);
if let Some(last) = body.stmts.last_mut() {
self.drop_unused_stmt_at_end_of_fn(last);
}
self.optimize_fn_stmts(&mut body.stmts)
}
}

View File

@ -102,5 +102,9 @@ pub(crate) fn invoke(module: &Module) {
);
}
tracing::info!("[SWC_RUN]\n{}", String::from_utf8_lossy(&output.stdout))
tracing::info!(
"[SWC_RUN]\n{}\n{}",
code,
String::from_utf8_lossy(&output.stdout)
)
}

View File

@ -18,6 +18,8 @@ pub(crate) trait CompileUnit:
V: VisitMut;
fn remove_mark(&mut self) -> Mark;
fn invoke(&self);
}
impl CompileUnit for Module {
@ -39,6 +41,10 @@ impl CompileUnit for Module {
fn remove_mark(&mut self) -> Mark {
Mark::root()
}
fn invoke(&self) {
crate::debug::invoke(self)
}
}
impl CompileUnit for FnExpr {
@ -60,4 +66,6 @@ impl CompileUnit for FnExpr {
fn remove_mark(&mut self) -> Mark {
self.function.span.remove_mark()
}
fn invoke(&self) {}
}

View File

@ -2897,15 +2897,15 @@
}, {
unsafe: !0
});
} else $DataView = function(buffer, byteOffset, byteLength) {
anInstance(this, $DataView, "DataView"), anInstance(buffer, $ArrayBuffer = function(length) {
anInstance(this, $ArrayBuffer, "ArrayBuffer");
var byteLength = toIndex(length);
setInternalState(this, {
bytes: arrayFill.call(new Array(byteLength), 0),
byteLength: byteLength
}), DESCRIPTORS || (this.byteLength = byteLength);
}, "DataView");
} else $ArrayBuffer = function(length) {
anInstance(this, $ArrayBuffer, "ArrayBuffer");
var byteLength = toIndex(length);
setInternalState(this, {
bytes: arrayFill.call(new Array(byteLength), 0),
byteLength: byteLength
}), DESCRIPTORS || (this.byteLength = byteLength);
}, $DataView = function(buffer, byteOffset, byteLength) {
anInstance(this, $DataView, "DataView"), anInstance(buffer, $ArrayBuffer, "DataView");
var bufferLength = getInternalState(buffer).byteLength, offset = toInteger(byteOffset);
if (offset < 0 || offset > bufferLength) throw RangeError("Wrong offset");
if (offset + (byteLength = void 0 === byteLength ? bufferLength - offset : toLength(byteLength)) > bufferLength) throw RangeError("Wrong length");
@ -7678,23 +7678,23 @@
var fixRegExpWellKnownSymbolLogic = __webpack_require__(29045), isRegExp = __webpack_require__(78202), anObject = __webpack_require__(83941), requireObjectCoercible = __webpack_require__(79602), speciesConstructor = __webpack_require__(94850), advanceStringIndex = __webpack_require__(88770), toLength = __webpack_require__(31998), toString = __webpack_require__(72729), getMethod = __webpack_require__(84316), callRegExpExec = __webpack_require__(21135), regexpExec = __webpack_require__(72384), stickyHelpers = __webpack_require__(44725), fails = __webpack_require__(60232), UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y, arrayPush = [].push, min = Math.min;
fixRegExpWellKnownSymbolLogic("split", function(SPLIT, nativeSplit, maybeCallNative) {
var internalSplit;
return [function(separator, limit) {
return internalSplit = "c" == "abbc".split(/(b)*/)[1] || 4 != "test".split(/(?:)/, -1).length || 2 != "ab".split(/(?:ab)*/).length || 4 != ".".split(/(.?)(.?)/).length || ".".split(/()()/).length > 1 || "".split(/.?/).length ? function(separator, limit) {
var match, lastIndex, lastLength, string = toString(requireObjectCoercible(this)), lim = void 0 === limit ? 4294967295 : limit >>> 0;
if (0 === lim) return [];
if (void 0 === separator) return [
string
];
if (!isRegExp(separator)) return nativeSplit.call(string, separator, lim);
for(var output = [], flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.unicode ? "u" : "") + (separator.sticky ? "y" : ""), lastLastIndex = 0, separatorCopy = new RegExp(separator.source, flags + "g"); match = regexpExec.call(separatorCopy, string);){
if ((lastIndex = separatorCopy.lastIndex) > lastLastIndex && (output.push(string.slice(lastLastIndex, match.index)), match.length > 1 && match.index < string.length && arrayPush.apply(output, match.slice(1)), lastLength = match[0].length, lastLastIndex = lastIndex, output.length >= lim)) break;
separatorCopy.lastIndex === match.index && separatorCopy.lastIndex++;
}
return lastLastIndex === string.length ? (lastLength || !separatorCopy.test("")) && output.push("") : output.push(string.slice(lastLastIndex)), output.length > lim ? output.slice(0, lim) : output;
} : "0".split(void 0, 0).length ? function(separator, limit) {
return void 0 === separator && 0 === limit ? [] : nativeSplit.call(this, separator, limit);
} : nativeSplit, [function(separator, limit) {
var O = requireObjectCoercible(this), splitter = void 0 == separator ? void 0 : getMethod(separator, SPLIT);
return splitter ? splitter.call(separator, O, limit) : (internalSplit = "c" == "abbc".split(/(b)*/)[1] || 4 != "test".split(/(?:)/, -1).length || 2 != "ab".split(/(?:ab)*/).length || 4 != ".".split(/(.?)(.?)/).length || ".".split(/()()/).length > 1 || "".split(/.?/).length ? function(separator, limit) {
var match, lastIndex, lastLength, string = toString(requireObjectCoercible(this)), lim = void 0 === limit ? 4294967295 : limit >>> 0;
if (0 === lim) return [];
if (void 0 === separator) return [
string
];
if (!isRegExp(separator)) return nativeSplit.call(string, separator, lim);
for(var output = [], flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.unicode ? "u" : "") + (separator.sticky ? "y" : ""), lastLastIndex = 0, separatorCopy = new RegExp(separator.source, flags + "g"); match = regexpExec.call(separatorCopy, string);){
if ((lastIndex = separatorCopy.lastIndex) > lastLastIndex && (output.push(string.slice(lastLastIndex, match.index)), match.length > 1 && match.index < string.length && arrayPush.apply(output, match.slice(1)), lastLength = match[0].length, lastLastIndex = lastIndex, output.length >= lim)) break;
separatorCopy.lastIndex === match.index && separatorCopy.lastIndex++;
}
return lastLastIndex === string.length ? (lastLength || !separatorCopy.test("")) && output.push("") : output.push(string.slice(lastLastIndex)), output.length > lim ? output.slice(0, lim) : output;
} : "0".split(void 0, 0).length ? function(separator, limit) {
return void 0 === separator && 0 === limit ? [] : nativeSplit.call(this, separator, limit);
} : nativeSplit).call(toString(O), separator, limit);
return splitter ? splitter.call(separator, O, limit) : internalSplit.call(toString(O), separator, limit);
}, function(string, limit) {
var rx = anObject(this), S = toString(string), res = maybeCallNative(internalSplit, rx, S, limit, internalSplit !== nativeSplit);
if (res.done) return res.value;
@ -9315,21 +9315,21 @@
Object.assign(current, value);
}
});
var extendStatics, lib_router = router, __extends = function(d, b) {
var extendStatics, lib_router = router, __extends = (extendStatics = function(d, b) {
return (extendStatics = Object.setPrototypeOf || ({
__proto__: []
}) instanceof Array && function(d, b) {
d.__proto__ = b;
} || function(d, b) {
for(var p in b)Object.prototype.hasOwnProperty.call(b, p) && (d[p] = b[p]);
})(d, b);
}, function(d, b) {
if ("function" != typeof b && null !== b) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
function __() {
this.constructor = d;
}
(extendStatics = function(d, b) {
return (extendStatics = Object.setPrototypeOf || ({
__proto__: []
}) instanceof Array && function(d, b) {
d.__proto__ = b;
} || function(d, b) {
for(var p in b)Object.prototype.hasOwnProperty.call(b, p) && (d[p] = b[p]);
})(d, b);
})(d, b), d.prototype = null === b ? Object.create(b) : (__.prototype = b.prototype, new __());
}, visibleListeners = {
extendStatics(d, b), d.prototype = null === b ? Object.create(b) : (__.prototype = b.prototype, new __());
}), visibleListeners = {
};
function addPageLifeCycle(cycle, callback) {
var _a, pathname = lib_router.current.pathname;
@ -9411,16 +9411,16 @@
}) : "browser" === type ? (0, esm_history.createBrowserHistory)({
basename: basename
}) : (0, esm_history.createMemoryHistory)();
}, initHistory = function(appConfig, initialContext) {
}, initHistory = (createHistory = createHistory1, function(appConfig, initialContext) {
void 0 === initialContext && (initialContext = null), appConfig.router || (appConfig.router = DEFAULT_APP_CONFIG.router);
var router = appConfig.router, _a = router.type, type = void 0 === _a ? DEFAULT_APP_CONFIG.router.type : _a, basename = router.basename, customHistory = router.history, newHistory = (createHistory = createHistory1)({
var router = appConfig.router, _a = router.type, type = void 0 === _a ? DEFAULT_APP_CONFIG.router.type : _a, basename = router.basename, customHistory = router.history, newHistory = createHistory({
type: type,
basename: basename,
location: initialContext ? initialContext.location : null,
customHistory: customHistory
});
appConfig.router.history = newHistory, setHistory(newHistory);
}, web_history = createHistory1, web_initAppLifeCycles = function() {
}), web_history = createHistory1, web_initAppLifeCycles = function() {
"undefined" != typeof document && "undefined" != typeof window && (document.addEventListener("visibilitychange", function() {
var history = getHistory();
(history ? history.location.pathname : lib_router.current.pathname) === lib_router.current.pathname && (lib_router.current.visibilityState = !lib_router.current.visibilityState, lib_router.current.visibilityState ? (emit("show"), pageLifeCycles_emit("show", lib_router.current.pathname)) : (pageLifeCycles_emit("hide", lib_router.current.pathname), emit("hide")));
@ -10616,8 +10616,7 @@
case "index":
return (key)=>(result, value)=>{
const index = result.length;
if (void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value) return result;
if (null === value) return [
return void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value ? result : null === value ? [
...result,
[
encode(key, options),
@ -10625,8 +10624,7 @@
index,
"]"
].join("")
];
return [
] : [
...result,
[
encode(key, options),
@ -10639,60 +10637,48 @@
}
;
case "bracket":
return (key)=>(result, value)=>{
if (void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value) return result;
if (null === value) return [
return (key)=>(result, value)=>void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value ? result : null === value ? [
...result,
[
encode(key, options),
"[]"
].join("")
];
return [
] : [
...result,
[
encode(key, options),
"[]=",
encode(value, options)
].join("")
];
}
]
;
case "comma":
case "separator":
return (key)=>(result, value)=>{
if (null == value || 0 === value.length) return result;
if (0 === result.length) return [
return (key)=>(result, value)=>null == value || 0 === value.length ? result : 0 === result.length ? [
[
encode(key, options),
"=",
encode(value, options)
].join("")
];
return [
] : [
[
result,
encode(value, options)
].join(options.arrayFormatSeparator)
];
}
]
;
default:
return (key)=>(result, value)=>{
if (void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value) return result;
if (null === value) return [
return (key)=>(result, value)=>void 0 === value || options.skipNull && null === value || options.skipEmptyString && "" === value ? result : null === value ? [
...result,
encode(key, options)
];
return [
] : [
...result,
[
encode(key, options),
"=",
encode(value, options)
].join("")
];
}
]
;
}
}(options), objectCopy = {
@ -10701,10 +10687,7 @@
const keys = Object.keys(objectCopy);
return !1 !== options.sort && keys.sort(options.sort), keys.map((key)=>{
const value = object[key];
if (void 0 === value) return "";
if (null === value) return encode(key, options);
if (Array.isArray(value)) return value.reduce(formatter(key), []).join("&");
return encode(key, options) + "=" + encode(value, options);
return void 0 === value ? "" : null === value ? encode(key, options) : Array.isArray(value) ? value.reduce(formatter(key), []).join("&") : encode(key, options) + "=" + encode(value, options);
}).filter((x)=>x.length > 0
).join("&");
}, exports.parseUrl = (url, options)=>{
@ -16425,9 +16408,9 @@
var _Provider$childContex, _Consumer$contextType, contextProp = "__create-react-context-" + (commonjsGlobal["__global_unique_id__"] = (commonjsGlobal["__global_unique_id__"] || 0) + 1) + "__", Provider = function(_Component) {
function Provider() {
var _this, value, handlers;
return _this = _Component.apply(this, arguments) || this, _this.emitter = {
return _this = _Component.apply(this, arguments) || this, value = _this.props.value, handlers = [], _this.emitter = {
on: function(handler) {
(handlers = []).push(handler);
handlers.push(handler);
},
off: function(handler) {
handlers = handlers.filter(function(h) {
@ -16435,7 +16418,7 @@
});
},
get: function() {
return value = _this.props.value;
return value;
},
set: function(newValue, changedBits) {
value = newValue, handlers.forEach(function(handler) {
@ -17637,10 +17620,9 @@
string
];
const separatorIndex = string.indexOf(separator);
if (-1 === separatorIndex) return [
return -1 === separatorIndex ? [
string
];
return [
] : [
string.slice(0, separatorIndex),
string.slice(separatorIndex + separator.length)
];

View File

@ -17,9 +17,7 @@ const isMultiIndexContext = (widget)=>hasMultipleIndices({
, isIndexWidgetEqualIndex = (widget, indexId)=>widget.props.indexId === indexId
, sortIndexWidgetsFirst = (firstWidget, secondWidget)=>{
const isFirstWidgetIndex = isIndexWidget(firstWidget), isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) return -1;
if (!isFirstWidgetIndex && isSecondWidgetIndex) return 1;
return 0;
return isFirstWidgetIndex && !isSecondWidgetIndex ? -1 : !isFirstWidgetIndex && isSecondWidgetIndex ? 1 : 0;
};
function serializeQueryParameters(parameters) {
const isObjectOrArray = (value)=>"[object Object]" === Object.prototype.toString.call(value) || "[object Array]" === Object.prototype.toString.call(value)

View File

@ -37,9 +37,7 @@ const isMultiIndexContext = (widget)=>hasMultipleIndices({
, isIndexWidgetEqualIndex = (widget, indexId)=>widget.props.indexId === indexId
, sortIndexWidgetsFirst = (firstWidget, secondWidget)=>{
const isFirstWidgetIndex = isIndexWidget(firstWidget), isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) return -1;
if (!isFirstWidgetIndex && isSecondWidgetIndex) return 1;
return 0;
return isFirstWidgetIndex && !isSecondWidgetIndex ? -1 : !isFirstWidgetIndex && isSecondWidgetIndex ? 1 : 0;
};
function serializeQueryParameters(parameters) {
const isObjectOrArray = (value)=>"[object Object]" === Object.prototype.toString.call(value) || "[object Array]" === Object.prototype.toString.call(value)

View File

@ -0,0 +1,548 @@
import * as swcHelpers from "@swc/helpers";
import algoliasearchHelper from 'algoliasearch-helper';
import createWidgetsManager from './createWidgetsManager';
import { HIGHLIGHT_TAGS } from './highlight';
import { hasMultipleIndices } from './indexUtils';
import { version as ReactVersion } from 'react';
import version from './version';
function addAlgoliaAgents(searchClient) {
if (typeof searchClient.addAlgoliaAgent === 'function') {
searchClient.addAlgoliaAgent("react (".concat(ReactVersion, ")"));
searchClient.addAlgoliaAgent("react-instantsearch (".concat(version, ")"));
}
}
var isMultiIndexContext = function (widget) {
return hasMultipleIndices({
ais: widget.props.contextValue,
multiIndexContext: widget.props.indexContextValue
});
};
var isTargetedIndexEqualIndex = function (widget, indexId) {
return widget.props.indexContextValue.targetedIndex === indexId;
};
// Relying on the `indexId` is a bit brittle to detect the `Index` widget.
// Since it's a class we could rely on `instanceof` or similar. We never
// had an issue though. Works for now.
var isIndexWidget = function (widget) {
return Boolean(widget.props.indexId);
};
var isIndexWidgetEqualIndex = function (widget, indexId) {
return widget.props.indexId === indexId;
};
var sortIndexWidgetsFirst = function (firstWidget, secondWidget) {
var isFirstWidgetIndex = isIndexWidget(firstWidget);
var isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) {
return -1;
}
if (!isFirstWidgetIndex && isSecondWidgetIndex) {
return 1;
}
return 0;
};
// This function is copied from the algoliasearch v4 API Client. If modified,
// consider updating it also in `serializeQueryParameters` from `@algolia/transporter`.
function serializeQueryParameters(parameters) {
var isObjectOrArray = function (value) {
return Object.prototype.toString.call(value) === '[object Object]' || Object.prototype.toString.call(value) === '[object Array]';
};
var encode = function (format) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
var i = 0;
return format.replace(/%s/g, function () {
return encodeURIComponent(args[i++]);
});
};
return Object.keys(parameters).map(function (key) {
return encode('%s=%s', key, isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key]);
}).join('&');
}
var _obj;
/**
* Creates a new instance of the InstantSearchManager which controls the widgets and
* trigger the search when the widgets are updated.
* @param {string} indexName - the main index name
* @param {object} initialState - initial widget state
* @param {object} SearchParameters - optional additional parameters to send to the algolia API
* @param {number} stalledSearchDelay - time (in ms) after the search is stalled
* @return {InstantSearchManager} a new instance of InstantSearchManager
*/ export default function createInstantSearchManager(param) {
var indexName = param.indexName, _initialState = param.initialState, initialState = _initialState === void 0 ? {
} : _initialState, searchClient = param.searchClient, resultsState = param.resultsState, stalledSearchDelay = param.stalledSearchDelay;
var createStore = function createStore(initialState) {
var state = initialState;
var listeners = [];
return {
getState: function () {
return state;
},
setState: function (nextState) {
state = nextState;
listeners.forEach(function (listener) {
return listener();
});
},
subscribe: function (listener) {
listeners.push(listener);
return function unsubscribe() {
listeners.splice(listeners.indexOf(listener), 1);
};
}
};
};
var skipSearch = function skipSearch() {
skip = true;
};
var updateClient = function updateClient(client) {
addAlgoliaAgents(client);
helper.setClient(client);
search();
};
var clearCache = function clearCache() {
helper.clearCache();
search();
};
var getMetadata = function getMetadata(state) {
return widgetsManager.getWidgets().filter(function (widget) {
return Boolean(widget.getMetadata);
}).map(function (widget) {
return widget.getMetadata(state);
});
};
var getSearchParameters = function getSearchParameters() {
var sharedParameters = widgetsManager.getWidgets().filter(function (widget) {
return Boolean(widget.getSearchParameters);
}).filter(function (widget) {
return !isMultiIndexContext(widget) && !isIndexWidget(widget);
}).reduce(function (res, widget) {
return widget.getSearchParameters(res);
}, initialSearchParameters);
var mainParameters = widgetsManager.getWidgets().filter(function (widget) {
return Boolean(widget.getSearchParameters);
}).filter(function (widget) {
var targetedIndexEqualMainIndex = isMultiIndexContext(widget) && isTargetedIndexEqualIndex(widget, indexName);
var subIndexEqualMainIndex = isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexEqualMainIndex || subIndexEqualMainIndex;
})// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst).reduce(function (res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters);
var derivedIndices = widgetsManager.getWidgets().filter(function (widget) {
return Boolean(widget.getSearchParameters);
}).filter(function (widget) {
var targetedIndexNotEqualMainIndex = isMultiIndexContext(widget) && !isTargetedIndexEqualIndex(widget, indexName);
var subIndexNotEqualMainIndex = isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex;
})// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst).reduce(function (indices, widget) {
var indexId = isMultiIndexContext(widget) ? widget.props.indexContextValue.targetedIndex : widget.props.indexId;
var widgets = indices[indexId] || [];
return swcHelpers.objectSpread({
}, indices, swcHelpers.defineProperty({
}, indexId, widgets.concat(widget)));
}, {
});
var derivedParameters = Object.keys(derivedIndices).map(function (indexId) {
return {
parameters: derivedIndices[indexId].reduce(function (res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters),
indexId: indexId
};
});
return {
mainParameters: mainParameters,
derivedParameters: derivedParameters
};
};
var search = function search() {
if (!skip) {
var ref = getSearchParameters(helper.state), mainParameters = ref.mainParameters, derivedParameters = ref.derivedParameters;
// We have to call `slice` because the method `detach` on the derived
// helpers mutates the value `derivedHelpers`. The `forEach` loop does
// not iterate on each value and we're not able to correctly clear the
// previous derived helpers (memory leak + useless requests).
helper.derivedHelpers.slice().forEach(function (derivedHelper) {
// Since we detach the derived helpers on **every** new search they
// won't receive intermediate results in case of a stalled search.
// Only the last result is dispatched by the derived helper because
// they are not detached yet:
//
// - a -> main helper receives results
// - ap -> main helper receives results
// - app -> main helper + derived helpers receive results
//
// The quick fix is to avoid to detach them on search but only once they
// received the results. But it means that in case of a stalled search
// all the derived helpers not detached yet register a new search inside
// the helper. The number grows fast in case of a bad network and it's
// not deterministic.
derivedHelper.detach();
});
derivedParameters.forEach(function (param) {
var indexId = param.indexId, parameters = param.parameters;
var derivedHelper = helper.derive(function () {
return parameters;
});
derivedHelper.on('result', handleSearchSuccess({
indexId: indexId
})).on('error', handleSearchError);
});
helper.setState(mainParameters);
helper.search();
}
};
var handleSearchSuccess = function handleSearchSuccess(param) {
var indexId = param.indexId;
return function (event) {
var state = store.getState();
var isDerivedHelpersEmpty = !helper.derivedHelpers.length;
var results = state.results ? state.results : {
};
// Switching from mono index to multi index and vice versa must reset the
// results to an empty object, otherwise we keep reference of stalled and
// unused results.
results = !isDerivedHelpersEmpty && results.getFacetByName ? {
} : results;
if (!isDerivedHelpersEmpty) {
results = swcHelpers.objectSpread({
}, results, swcHelpers.defineProperty({
}, indexId, event.results));
} else {
results = event.results;
}
var currentState = store.getState();
var nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
stalledSearchTimer = null;
nextIsSearchStalled = false;
}
var resultsFacetValues = currentState.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(currentState, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
results: results,
isSearchStalled: nextIsSearchStalled,
searching: false,
error: null
}));
};
};
var handleSearchError = function handleSearchError(param) {
var error = param.error;
var currentState = store.getState();
var nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
nextIsSearchStalled = false;
}
var resultsFacetValues = currentState.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(currentState, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: nextIsSearchStalled,
error: error,
searching: false
}));
};
var handleNewSearch = function handleNewSearch() {
if (!stalledSearchTimer) {
stalledSearchTimer = setTimeout(function () {
var _ref = store.getState(), resultsFacetValues = _ref.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(_ref, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: true
}));
}, stalledSearchDelay);
}
};
var hydrateSearchClient = function hydrateSearchClient(client, results) {
if (!results) {
return;
}
// Disable cache hydration on:
// - Algoliasearch API Client < v4 with cache disabled
// - Third party clients (detected by the `addAlgoliaAgent` function missing)
if ((!client.transporter || client._cacheHydrated) && (!client._useCache || typeof client.addAlgoliaAgent !== 'function')) {
return;
}
// Algoliasearch API Client >= v4
// To hydrate the client we need to populate the cache with the data from
// the server (done in `hydrateSearchClientWithMultiIndexRequest` or
// `hydrateSearchClientWithSingleIndexRequest`). But since there is no way
// for us to compute the key the same way as `algoliasearch-client` we need
// to populate it on a custom key and override the `search` method to
// search on it first.
if (client.transporter && !client._cacheHydrated) {
client._cacheHydrated = true;
var baseMethod = client.search;
client.search = function (requests) {
for (var _len = arguments.length, methodArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
methodArgs[_key - 1] = arguments[_key];
}
var requestsWithSerializedParams = requests.map(function (request) {
return swcHelpers.objectSpread({
}, request, {
params: serializeQueryParameters(request.params)
});
});
return client.transporter.responsesCache.get({
method: 'search',
args: [
requestsWithSerializedParams
].concat(swcHelpers.toConsumableArray(methodArgs))
}, function () {
return baseMethod.apply(void 0, [
requests
].concat(swcHelpers.toConsumableArray(methodArgs)));
});
};
}
if (Array.isArray(results.results)) {
hydrateSearchClientWithMultiIndexRequest(client, results.results);
return;
}
hydrateSearchClientWithSingleIndexRequest(client, results);
};
var hydrateSearchClientWithMultiIndexRequest = function hydrateSearchClientWithMultiIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set({
method: 'search',
args: [
results.reduce(function (acc, result) {
return acc.concat(result.rawResults.map(function (request) {
return {
indexName: request.index,
params: request.params
};
}));
}, []),
]
}, {
results: results.reduce(function (acc, result) {
return acc.concat(result.rawResults);
}, [])
});
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.reduce(function (acc, result) {
return acc.concat(result.rawResults.map(function (request) {
return {
indexName: request.index,
params: request.params
};
}));
}, [])
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.reduce(function (acc, result) {
return acc.concat(result.rawResults);
}, [])
})));
};
var hydrateSearchClientWithSingleIndexRequest = function hydrateSearchClientWithSingleIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set({
method: 'search',
args: [
results.rawResults.map(function (request) {
return {
indexName: request.index,
params: request.params
};
}),
]
}, {
results: results.rawResults
});
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.rawResults.map(function (request) {
return {
indexName: request.index,
params: request.params
};
})
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.rawResults
})));
};
var hydrateResultsState = function hydrateResultsState(results) {
if (!results) {
return null;
}
if (Array.isArray(results.results)) {
return results.results.reduce(function (acc, result) {
return swcHelpers.objectSpread({
}, acc, swcHelpers.defineProperty({
}, result._internalIndexId, new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(result.state), result.rawResults)));
}, {
});
}
return new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(results.state), results.rawResults);
};
var onWidgetsUpdate = // Called whenever a widget has been rendered with new props.
function onWidgetsUpdate() {
var metadata = getMetadata(store.getState().widgets);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
metadata: metadata,
searching: true
}));
// Since the `getSearchParameters` method of widgets also depends on props,
// the result search parameters might have changed.
search();
};
var transitionState = function transitionState(nextSearchState) {
var searchState = store.getState().widgets;
return widgetsManager.getWidgets().filter(function (widget) {
return Boolean(widget.transitionState);
}).reduce(function (res, widget) {
return widget.transitionState(searchState, res);
}, nextSearchState);
};
var onExternalStateUpdate = function onExternalStateUpdate(nextSearchState) {
var metadata = getMetadata(nextSearchState);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
widgets: nextSearchState,
metadata: metadata,
searching: true
}));
search();
};
var onSearchForFacetValues = function onSearchForFacetValues(param) {
var facetName = param.facetName, query = param.query, _maxFacetHits = param.maxFacetHits, maxFacetHits = _maxFacetHits === void 0 ? 10 : _maxFacetHits;
// The values 1, 100 are the min / max values that the engine accepts.
// see: https://www.algolia.com/doc/api-reference/api-parameters/maxFacetHits
var maxFacetHitsWithinRange = Math.max(1, Math.min(maxFacetHits, 100));
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: true
}));
helper.searchForFacetValues(facetName, query, maxFacetHitsWithinRange).then(function (content) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
error: null,
searchingForFacetValues: false,
resultsFacetValues: swcHelpers.objectSpread({
}, store.getState().resultsFacetValues, (_obj = {
}, swcHelpers.defineProperty(_obj, facetName, content.facetHits), swcHelpers.defineProperty(_obj, "query", query), _obj))
}));
}, function (error) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: false,
error: error
}));
}).catch(function (error) {
// Since setState is synchronous, any error that occurs in the render of a
// component will be swallowed by this promise.
// This is a trick to make the error show up correctly in the console.
// See http://stackoverflow.com/a/30741722/969302
setTimeout(function () {
throw error;
});
});
};
var updateIndex = function updateIndex(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
// No need to trigger a new search here as the widgets will also update and trigger it if needed.
};
var getWidgetsIds = function getWidgetsIds() {
return store.getState().metadata.reduce(function (res, meta) {
return typeof meta.id !== 'undefined' ? res.concat(meta.id) : res;
}, []);
};
var helper = algoliasearchHelper(searchClient, indexName, swcHelpers.objectSpread({
}, HIGHLIGHT_TAGS));
addAlgoliaAgents(searchClient);
helper.on('search', handleNewSearch).on('result', handleSearchSuccess({
indexId: indexName
})).on('error', handleSearchError);
var skip = false;
var stalledSearchTimer = null;
var initialSearchParameters = helper.state;
var widgetsManager = createWidgetsManager(onWidgetsUpdate);
hydrateSearchClient(searchClient, resultsState);
var store = createStore({
widgets: initialState,
metadata: hydrateMetadata(resultsState),
results: hydrateResultsState(resultsState),
error: null,
searching: false,
isSearchStalled: true,
searchingForFacetValues: false
});
return {
store: store,
widgetsManager: widgetsManager,
getWidgetsIds: getWidgetsIds,
getSearchParameters: getSearchParameters,
onSearchForFacetValues: onSearchForFacetValues,
onExternalStateUpdate: onExternalStateUpdate,
transitionState: transitionState,
updateClient: updateClient,
updateIndex: updateIndex,
clearCache: clearCache,
skipSearch: skipSearch
};
};
function hydrateMetadata(resultsState) {
if (!resultsState) {
return [];
}
// add a value noop, which gets replaced once the widgets are mounted
return resultsState.metadata.map(function (datum) {
return swcHelpers.objectSpread({
value: function () {
return {
};
}
}, datum, {
items: datum.items && datum.items.map(function (item) {
return swcHelpers.objectSpread({
value: function () {
return {
};
}
}, item, {
items: item.items && item.items.map(function (nestedItem) {
return swcHelpers.objectSpread({
value: function () {
return {
};
}
}, nestedItem);
})
});
})
});
});
}

View File

@ -0,0 +1,376 @@
import * as swcHelpers from "@swc/helpers";
import algoliasearchHelper from "algoliasearch-helper";
import createWidgetsManager from "./createWidgetsManager";
import { HIGHLIGHT_TAGS } from "./highlight";
import { hasMultipleIndices } from "./indexUtils";
import { version as ReactVersion } from "react";
import version from "./version";
function addAlgoliaAgents(searchClient) {
"function" == typeof searchClient.addAlgoliaAgent && (searchClient.addAlgoliaAgent("react (".concat(ReactVersion, ")")), searchClient.addAlgoliaAgent("react-instantsearch (".concat(version, ")")));
}
var _obj, isMultiIndexContext = function(widget) {
return hasMultipleIndices({
ais: widget.props.contextValue,
multiIndexContext: widget.props.indexContextValue
});
}, isTargetedIndexEqualIndex = function(widget, indexId) {
return widget.props.indexContextValue.targetedIndex === indexId;
}, isIndexWidget = function(widget) {
return Boolean(widget.props.indexId);
}, isIndexWidgetEqualIndex = function(widget, indexId) {
return widget.props.indexId === indexId;
}, sortIndexWidgetsFirst = function(firstWidget, secondWidget) {
var isFirstWidgetIndex = isIndexWidget(firstWidget), isSecondWidgetIndex = isIndexWidget(secondWidget);
return isFirstWidgetIndex && !isSecondWidgetIndex ? -1 : !isFirstWidgetIndex && isSecondWidgetIndex ? 1 : 0;
};
function serializeQueryParameters(parameters) {
var encode = function(format) {
for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++)args[_key - 1] = arguments[_key];
var i = 0;
return format.replace(/%s/g, function() {
return encodeURIComponent(args[i++]);
});
};
return Object.keys(parameters).map(function(key) {
var value;
return encode("%s=%s", key, (value = parameters[key], "[object Object]" === Object.prototype.toString.call(value) || "[object Array]" === Object.prototype.toString.call(value)) ? JSON.stringify(parameters[key]) : parameters[key]);
}).join("&");
}
export default function createInstantSearchManager(param) {
var indexName = param.indexName, _initialState = param.initialState, searchClient = param.searchClient, resultsState = param.resultsState, stalledSearchDelay = param.stalledSearchDelay, getMetadata = function(state) {
return widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getMetadata);
}).map(function(widget) {
return widget.getMetadata(state);
});
}, getSearchParameters = function() {
var sharedParameters = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
return !isMultiIndexContext(widget) && !isIndexWidget(widget);
}).reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, initialSearchParameters), mainParameters = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
var targetedIndexEqualMainIndex = isMultiIndexContext(widget) && isTargetedIndexEqualIndex(widget, indexName), subIndexEqualMainIndex = isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexEqualMainIndex || subIndexEqualMainIndex;
}).sort(sortIndexWidgetsFirst).reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters), derivedIndices = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
var targetedIndexNotEqualMainIndex = isMultiIndexContext(widget) && !isTargetedIndexEqualIndex(widget, indexName), subIndexNotEqualMainIndex = isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex;
}).sort(sortIndexWidgetsFirst).reduce(function(indices, widget) {
var indexId = isMultiIndexContext(widget) ? widget.props.indexContextValue.targetedIndex : widget.props.indexId, widgets = indices[indexId] || [];
return swcHelpers.objectSpread({
}, indices, swcHelpers.defineProperty({
}, indexId, widgets.concat(widget)));
}, {
});
return {
mainParameters: mainParameters,
derivedParameters: Object.keys(derivedIndices).map(function(indexId) {
return {
parameters: derivedIndices[indexId].reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters),
indexId: indexId
};
})
};
}, search = function() {
if (!skip) {
var ref = getSearchParameters(helper.state), mainParameters = ref.mainParameters, derivedParameters = ref.derivedParameters;
helper.derivedHelpers.slice().forEach(function(derivedHelper) {
derivedHelper.detach();
}), derivedParameters.forEach(function(param) {
var indexId = param.indexId, parameters = param.parameters;
helper.derive(function() {
return parameters;
}).on("result", handleSearchSuccess({
indexId: indexId
})).on("error", handleSearchError);
}), helper.setState(mainParameters), helper.search();
}
}, handleSearchSuccess = function(param) {
var indexId = param.indexId;
return function(event) {
var state = store.getState(), isDerivedHelpersEmpty = !helper.derivedHelpers.length, results = state.results ? state.results : {
};
results = !isDerivedHelpersEmpty && results.getFacetByName ? {
} : results, results = isDerivedHelpersEmpty ? event.results : swcHelpers.objectSpread({
}, results, swcHelpers.defineProperty({
}, indexId, event.results));
var currentState = store.getState(), nextIsSearchStalled = currentState.isSearchStalled;
helper.hasPendingRequests() || (clearTimeout(stalledSearchTimer), stalledSearchTimer = null, nextIsSearchStalled = !1), currentState.resultsFacetValues;
var partialState = swcHelpers.objectWithoutProperties(currentState, [
"resultsFacetValues"
]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
results: results,
isSearchStalled: nextIsSearchStalled,
searching: !1,
error: null
}));
};
}, handleSearchError = function(param) {
var error = param.error, currentState = store.getState(), nextIsSearchStalled = currentState.isSearchStalled;
helper.hasPendingRequests() || (clearTimeout(stalledSearchTimer), nextIsSearchStalled = !1), currentState.resultsFacetValues;
var partialState = swcHelpers.objectWithoutProperties(currentState, [
"resultsFacetValues"
]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: nextIsSearchStalled,
error: error,
searching: !1
}));
}, hydrateSearchClientWithMultiIndexRequest = function(client, results) {
if (client.transporter) {
client.transporter.responsesCache.set({
method: "search",
args: [
results.reduce(function(acc, result) {
return acc.concat(result.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}));
}, []),
]
}, {
results: results.reduce(function(acc, result) {
return acc.concat(result.rawResults);
}, [])
});
return;
}
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.reduce(function(acc, result) {
return acc.concat(result.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}));
}, [])
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.reduce(function(acc, result) {
return acc.concat(result.rawResults);
}, [])
})));
}, hydrateSearchClientWithSingleIndexRequest = function(client, results) {
if (client.transporter) {
client.transporter.responsesCache.set({
method: "search",
args: [
results.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}),
]
}, {
results: results.rawResults
});
return;
}
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
})
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.rawResults
})));
}, helper = algoliasearchHelper(searchClient, indexName, swcHelpers.objectSpread({
}, HIGHLIGHT_TAGS));
addAlgoliaAgents(searchClient), helper.on("search", function() {
stalledSearchTimer || (stalledSearchTimer = setTimeout(function() {
var _ref = store.getState(), resultsFacetValues = _ref.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(_ref, [
"resultsFacetValues"
]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: !0
}));
}, stalledSearchDelay));
}).on("result", handleSearchSuccess({
indexId: indexName
})).on("error", handleSearchError);
var skip = !1, stalledSearchTimer = null, initialSearchParameters = helper.state, widgetsManager = createWidgetsManager(function() {
var metadata = getMetadata(store.getState().widgets);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
metadata: metadata,
searching: !0
})), search();
});
!function(client, results) {
if (results && (client.transporter && !client._cacheHydrated || client._useCache && "function" == typeof client.addAlgoliaAgent)) {
if (client.transporter && !client._cacheHydrated) {
client._cacheHydrated = !0;
var baseMethod = client.search;
client.search = function(requests) {
for(var _len = arguments.length, methodArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++)methodArgs[_key - 1] = arguments[_key];
var requestsWithSerializedParams = requests.map(function(request) {
return swcHelpers.objectSpread({
}, request, {
params: serializeQueryParameters(request.params)
});
});
return client.transporter.responsesCache.get({
method: "search",
args: [
requestsWithSerializedParams
].concat(swcHelpers.toConsumableArray(methodArgs))
}, function() {
return baseMethod.apply(void 0, [
requests
].concat(swcHelpers.toConsumableArray(methodArgs)));
});
};
}
if (Array.isArray(results.results)) {
hydrateSearchClientWithMultiIndexRequest(client, results.results);
return;
}
hydrateSearchClientWithSingleIndexRequest(client, results);
}
}(searchClient, resultsState);
var results, state, listeners, store = (state = {
widgets: void 0 === _initialState ? {
} : _initialState,
metadata: hydrateMetadata(resultsState),
results: (results = resultsState) ? Array.isArray(results.results) ? results.results.reduce(function(acc, result) {
return swcHelpers.objectSpread({
}, acc, swcHelpers.defineProperty({
}, result._internalIndexId, new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(result.state), result.rawResults)));
}, {
}) : new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(results.state), results.rawResults) : null,
error: null,
searching: !1,
isSearchStalled: !0,
searchingForFacetValues: !1
}, listeners = [], {
getState: function() {
return state;
},
setState: function(nextState) {
state = nextState, listeners.forEach(function(listener) {
return listener();
});
},
subscribe: function(listener) {
return listeners.push(listener), function() {
listeners.splice(listeners.indexOf(listener), 1);
};
}
});
return {
store: store,
widgetsManager: widgetsManager,
getWidgetsIds: function() {
return store.getState().metadata.reduce(function(res, meta) {
return void 0 !== meta.id ? res.concat(meta.id) : res;
}, []);
},
getSearchParameters: getSearchParameters,
onSearchForFacetValues: function(param) {
var facetName = param.facetName, query = param.query, _maxFacetHits = param.maxFacetHits;
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: !0
})), helper.searchForFacetValues(facetName, query, Math.max(1, Math.min(void 0 === _maxFacetHits ? 10 : _maxFacetHits, 100))).then(function(content) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
error: null,
searchingForFacetValues: !1,
resultsFacetValues: swcHelpers.objectSpread({
}, store.getState().resultsFacetValues, (_obj = {
}, swcHelpers.defineProperty(_obj, facetName, content.facetHits), swcHelpers.defineProperty(_obj, "query", query), _obj))
}));
}, function(error) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: !1,
error: error
}));
}).catch(function(error) {
setTimeout(function() {
throw error;
});
});
},
onExternalStateUpdate: function(nextSearchState) {
var metadata = getMetadata(nextSearchState);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
widgets: nextSearchState,
metadata: metadata,
searching: !0
})), search();
},
transitionState: function(nextSearchState) {
var searchState = store.getState().widgets;
return widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.transitionState);
}).reduce(function(res, widget) {
return widget.transitionState(searchState, res);
}, nextSearchState);
},
updateClient: function(client) {
addAlgoliaAgents(client), helper.setClient(client), search();
},
updateIndex: function(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
},
clearCache: function() {
helper.clearCache(), search();
},
skipSearch: function() {
skip = !0;
}
};
};
function hydrateMetadata(resultsState) {
return resultsState ? resultsState.metadata.map(function(datum) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, datum, {
items: datum.items && datum.items.map(function(item) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, item, {
items: item.items && item.items.map(function(nestedItem) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, nestedItem);
})
});
})
});
}) : [];
}

View File

@ -585,6 +585,7 @@ inline/inline_annotation_2/input.js
inline/inline_func_with_name_existing_in_block_scope/input.js
inline/inline_into_scope_conflict_enclosed/input.js
inline/inline_into_scope_conflict_enclosed_2/input.js
inline/issue_308/input.js
inline/noinline_annotation/input.js
inline/noinline_annotation_2/input.js
inline/noinline_annotation_3/input.js

View File

@ -193,7 +193,6 @@ inline/dont_inline_funcs_into_default_param_2/input.js
inline/inline_annotation/input.js
inline/inline_into_scope_conflict/input.js
inline/inline_within_extends_1/input.js
inline/issue_308/input.js
issue_1034/non_hoisted_function_after_return/input.js
issue_1034/non_hoisted_function_after_return_2a/input.js
issue_1034/non_hoisted_function_after_return_2b/input.js

View File

@ -584,7 +584,7 @@
if (isDefined(unsupported)) throw jqLiteMinErr("onargs", "jqLite#on() does not support the `selector` or `eventData` parameters");
var element1, events, eventHandler, events1 = jqLiteExpandoStore(element, "events"), handle = jqLiteExpandoStore(element, "handle");
events1 || jqLiteExpandoStore(element, "events", events1 = {
}), handle || jqLiteExpandoStore(element, "handle", handle = ((eventHandler = function(event, type) {
}), handle || jqLiteExpandoStore(element, "handle", handle = (element1 = element, events = events1, (eventHandler = function(event, type) {
if (event.preventDefault || (event.preventDefault = function() {
event.returnValue = !1;
}), event.stopPropagation || (event.stopPropagation = function() {
@ -597,8 +597,8 @@
}
event.isDefaultPrevented = function() {
return event.defaultPrevented || !1 === event.returnValue;
}, forEach((events = events1)[type || event.type], function(fn) {
fn.call(element1 = element, event);
}, forEach(events[type || event.type], function(fn) {
fn.call(element1, event);
}), msie <= 8 ? (event.preventDefault = null, event.stopPropagation = null, event.isDefaultPrevented = null) : (delete event.preventDefault, delete event.stopPropagation, delete event.isDefaultPrevented);
}).elem = element1, eventHandler)), forEach(type.split(" "), function(type) {
var eventFns = events1[type];
@ -876,13 +876,13 @@
this.enter(element, parent, after, done);
},
addClass: function(element, className, done) {
forEach(element, function(element) {
jqLiteAddClass(element, className = isString(className) ? className : isArray(className) ? className.join(" ") : "");
className = isString(className) ? className : isArray(className) ? className.join(" ") : "", forEach(element, function(element) {
jqLiteAddClass(element, className);
}), done && $timeout(done, 0, !1);
},
removeClass: function(element, className, done) {
forEach(element, function(element) {
jqLiteRemoveClass(element, className = isString(className) ? className : isArray(className) ? className.join(" ") : "");
className = isString(className) ? className : isArray(className) ? className.join(" ") : "", forEach(element, function(element) {
jqLiteRemoveClass(element, className);
}), done && $timeout(done, 0, !1);
},
enabled: noop
@ -915,11 +915,11 @@
var pollTimeout, pollFns = [];
self.addPollFn = function(fn) {
var setTimeout1;
return isUndefined(pollTimeout) && (function check() {
return isUndefined(pollTimeout) && (setTimeout1 = setTimeout, (function check() {
forEach(pollFns, function(pollFn) {
pollFn();
}), pollTimeout = (setTimeout1 = setTimeout)(check, 100);
})(), pollFns.push(fn), fn;
}), pollTimeout = setTimeout1(check, 100);
})()), pollFns.push(fn), fn;
};
var lastBrowserUrl = location.href, baseElement = document.find("base"), newLocation = null;
self.url = function(url, replace) {
@ -1229,9 +1229,9 @@
if (value = null, elementControllers && "data" === retrievalMethod && (value = elementControllers[require]), !(value = value || $element[retrievalMethod]("$" + require + "Controller")) && !optional) throw $compileMinErr("ctreq", "Controller '{0}', required by directive '{1}', can't be found!", require, directiveName);
return value;
}
return isArray(require) && forEach(require, function(require) {
(value = []).push(getControllers(require, $element, elementControllers));
}), value;
return isArray(require) && (value = [], forEach(require, function(require) {
value.push(getControllers(require, $element, elementControllers));
})), value;
}
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var attrs, $element, i, ii, linkFn, controller, isolateScope, transcludeFn, elementControllers = {
@ -2378,8 +2378,8 @@
},
assignment: function() {
var right, token, left = this.ternary();
return (token = this.expect("=")) ? (left.assign || this.throwError("implies assignment but [" + this.text.substring(0, token.index) + "] can not be assigned to", token), function(scope, locals) {
return left.assign(scope, (right = this.ternary())(scope, locals), locals);
return (token = this.expect("=")) ? (left.assign || this.throwError("implies assignment but [" + this.text.substring(0, token.index) + "] can not be assigned to", token), right = this.ternary(), function(scope, locals) {
return left.assign(scope, right(scope, locals), locals);
}) : left;
},
ternary: function() {
@ -3644,9 +3644,9 @@
if (ctrl.$isEmpty(value) || regexp.test(value)) return ctrl.$setValidity("pattern", !0), value;
ctrl.$setValidity("pattern", !1);
};
if (pattern && (patternValidator = (match = pattern.match(/^\/(.*)\/([gim]*)$/)) ? function(value) {
return validate(pattern = new RegExp(match[1], match[2]), value);
} : function(value) {
if (pattern && ((match = pattern.match(/^\/(.*)\/([gim]*)$/)) ? (pattern = new RegExp(match[1], match[2]), patternValidator = function(value) {
return validate(pattern, value);
}) : patternValidator = function(value) {
var patternObj = scope.$eval(pattern);
if (!patternObj || !patternObj.test) throw minErr("ngPattern")("noregexp", "Expected {0} to be a RegExp but was {1}. Element: {2}", pattern, patternObj, startingTag(element));
return validate(patternObj, value);
@ -3983,9 +3983,9 @@
$id: hashKey
};
if (!match) throw ngRepeatMinErr("iexp", "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", expression);
if (lhs = match[1], rhs = match[2], (trackByExp = match[4]) ? trackByIdExpFn = function(key, value, index) {
return keyIdentifier && (hashFnLocals[keyIdentifier] = key), hashFnLocals[valueIdentifier] = value, hashFnLocals.$index = index, (trackByExpGetter = $parse(trackByExp))($scope, hashFnLocals);
} : (trackByIdArrayFn = function(key, value) {
if (lhs = match[1], rhs = match[2], (trackByExp = match[4]) ? (trackByExpGetter = $parse(trackByExp), trackByIdExpFn = function(key, value, index) {
return keyIdentifier && (hashFnLocals[keyIdentifier] = key), hashFnLocals[valueIdentifier] = value, hashFnLocals.$index = index, trackByExpGetter($scope, hashFnLocals);
}) : (trackByIdArrayFn = function(key, value) {
return hashKey(value);
}, trackByIdObjFn = function(key) {
return key;

View File

@ -236,9 +236,9 @@
access: function(elems, fn, key, value, chainable, emptyGet, raw) {
var i = 0, length = elems.length, bulk = null == key;
if ("object" === jQuery.type(key)) for(i in chainable = !0, key)jQuery.access(elems, fn, i, key[i], !0, emptyGet, raw);
else if (value !== undefined && (chainable = !0, jQuery.isFunction(value) || (raw = !0), bulk && (raw ? (fn.call(elems, value), fn = null) : fn = function(elem, key, value) {
return (bulk = fn).call(jQuery(elem), value);
}), fn)) for(; i < length; i++)fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key)));
else if (value !== undefined && (chainable = !0, jQuery.isFunction(value) || (raw = !0), bulk && (raw ? (fn.call(elems, value), fn = null) : (bulk = fn, fn = function(elem, key, value) {
return bulk.call(jQuery(elem), value);
})), fn)) for(; i < length; i++)fn(elems[i], key, raw ? value : value.call(elems[i], i, fn(elems[i], key)));
return chainable ? elems : bulk ? fn.call(elems) : length ? fn(elems[0], key) : emptyGet;
},
now: function() {
@ -1093,8 +1093,8 @@
}
if (null == data && null == fn ? (fn = selector, data = selector = undefined) : null == fn && ("string" == typeof selector ? (fn = data, data = undefined) : (fn = data, data = selector, selector = undefined)), !1 === fn) fn = returnFalse;
else if (!fn) return this;
return 1 === one && ((fn = function(event) {
return jQuery().off(event), (origFn = fn).apply(this, arguments);
return 1 === one && (origFn = fn, (fn = function(event) {
return jQuery().off(event), origFn.apply(this, arguments);
}).guid = origFn.guid || (origFn.guid = jQuery.guid++)), this.each(function() {
jQuery.event.add(this, types, fn, data, selector);
});
@ -1231,8 +1231,8 @@
}
function createPositionalPseudo(fn) {
return markFunction(function(argument) {
return markFunction(function(seed, matches) {
for(var j, matchIndexes = fn([], seed.length, argument = +argument), i = matchIndexes.length; i--;)seed[j = matchIndexes[i]] && (seed[j] = !(matches[j] = seed[j]));
return argument = +argument, markFunction(function(seed, matches) {
for(var j, matchIndexes = fn([], seed.length, argument), i = matchIndexes.length; i--;)seed[j = matchIndexes[i]] && (seed[j] = !(matches[j] = seed[j]));
});
});
}
@ -1409,15 +1409,15 @@
TAG: function(nodeName) {
return "*" === nodeName ? function() {
return !0;
} : function(elem) {
return elem.nodeName && elem.nodeName.toLowerCase() === (nodeName = nodeName.replace(runescape, funescape).toLowerCase());
};
} : (nodeName = nodeName.replace(runescape, funescape).toLowerCase(), function(elem) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
});
},
CLASS: function(className) {
var pattern = classCache[className + " "];
return pattern || classCache(className, function(elem) {
return (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")).test(elem.className || void 0 !== elem.getAttribute && elem.getAttribute("class") || "");
});
return pattern || (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)"), classCache(className, function(elem) {
return pattern.test(elem.className || void 0 !== elem.getAttribute && elem.getAttribute("class") || "");
}));
},
ATTR: function(name, operator, check) {
return function(elem) {
@ -2257,15 +2257,15 @@
for(name in ret = callback.apply(elem, args || []), options)elem.style[name] = old[name];
return ret;
}
}), window.getComputedStyle ? curCSS = function(elem, name, _computed) {
var width, minWidth, maxWidth, computed = _computed || (getStyles = function(elem) {
return window.getComputedStyle(elem, null);
})(elem), ret = computed ? computed.getPropertyValue(name) || computed[name] : undefined, style = elem.style;
}), window.getComputedStyle ? (getStyles = function(elem) {
return window.getComputedStyle(elem, null);
}, curCSS = function(elem, name, _computed) {
var width, minWidth, maxWidth, computed = _computed || getStyles(elem), ret = computed ? computed.getPropertyValue(name) || computed[name] : undefined, style = elem.style;
return computed && ("" !== ret || jQuery.contains(elem.ownerDocument, elem) || (ret = jQuery.style(elem, name)), rnumnonpx.test(ret) && rmargin.test(name) && (width = style.width, minWidth = style.minWidth, maxWidth = style.maxWidth, style.minWidth = style.maxWidth = style.width = ret, ret = computed.width, style.width = width, style.minWidth = minWidth, style.maxWidth = maxWidth)), ret;
} : document.documentElement.currentStyle && (curCSS = function(elem, name, _computed) {
var left, rs, rsLeft, computed = _computed || (getStyles = function(elem) {
return elem.currentStyle;
})(elem), ret = computed ? computed[name] : undefined, style = elem.style;
}) : document.documentElement.currentStyle && (getStyles = function(elem) {
return elem.currentStyle;
}, curCSS = function(elem, name, _computed) {
var left, rs, rsLeft, computed = _computed || getStyles(elem), ret = computed ? computed[name] : undefined, style = elem.style;
return null == ret && style && style[name] && (ret = style[name]), rnumnonpx.test(ret) && !rposition.test(name) && (left = style.left, (rsLeft = (rs = elem.runtimeStyle) && rs.left) && (rs.left = elem.currentStyle.left), style.left = "fontSize" === name ? "1em" : ret, ret = style.pixelLeft + "px", style.left = left, rsLeft && (rs.left = rsLeft)), "" === ret ? "auto" : ret;
}), jQuery.each([
"height",

View File

@ -4496,8 +4496,8 @@
_setWidget: function(widget) {
if (!this._widget && widget) {
var self, orig;
this._widget = widget, self = this, this._widget._setOptions = function(options) {
(orig = this._widget._setOptions).call(this, options), self._syncTextInputOptions(options);
this._widget = widget, self = this, orig = this._widget._setOptions, this._widget._setOptions = function(options) {
orig.call(this, options), self._syncTextInputOptions(options);
};
}
return this._widget && (this._syncTextInputOptions(this._widget.options), "listview" === this._widget.widgetName && (this._widget.options.hideDividers = !0, this._widget.element.listview("refresh"))), !!this._widget;

View File

@ -1748,8 +1748,8 @@ var Element = function(tag, props) {
}
return document.newElement(tag, props);
};
Browser.Element && (Element.prototype = Browser.Element.prototype, Element.prototype._fireEvent = function(type, event) {
return (fireEvent = Element.prototype.fireEvent).call(this, type, event);
Browser.Element && (Element.prototype = Browser.Element.prototype, fireEvent = Element.prototype.fireEvent, Element.prototype._fireEvent = function(type, event) {
return fireEvent.call(this, type, event);
}), new Type("Element", Element).mirror(function(name) {
if (!Array.prototype[name]) {
var obj = {
@ -1784,9 +1784,9 @@ var IFrame = new Type("IFrame", function() {
props.id,
props.name,
iframe ? iframe.id || iframe.name : "IFrame_" + String.uniqueID()
].pick();
].pick(), iframe = new Element(iframe || "iframe", props);
var onLoad = function() {
onload.call((iframe = new Element(iframe || "iframe", props)).contentWindow);
onload.call(iframe.contentWindow);
};
return window.frames[props.id] ? onLoad() : iframe.addListener("load", onLoad), iframe;
}), Elements = this.Elements = function(nodes) {
@ -3345,8 +3345,9 @@ Elements.prototype = {
return trans;
}
}), Fx.Transition = function(transition, params) {
params = Array.from(params);
var easeIn = function(pos) {
return transition(pos, params = Array.from(params));
return transition(pos, params);
};
return Object.append(easeIn, {
easeIn: easeIn,

View File

@ -242,13 +242,13 @@
}
if (key || ref) {
var props, displayName, warnAboutAccessingKey, props1, displayName1, warnAboutAccessingRef, displayName2 = "function" == typeof type ? type.displayName || type.name || "Unknown" : type;
key && (props = props3, (warnAboutAccessingKey = function() {
specialPropKeyWarningShown || (specialPropKeyWarningShown = !0, error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", displayName = displayName2));
key && (props = props3, displayName = displayName2, (warnAboutAccessingKey = function() {
specialPropKeyWarningShown || (specialPropKeyWarningShown = !0, error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", displayName));
}).isReactWarning = !0, Object.defineProperty(props, "key", {
get: warnAboutAccessingKey,
configurable: !0
})), ref && (props1 = props3, (warnAboutAccessingRef = function() {
specialPropRefWarningShown || (specialPropRefWarningShown = !0, error("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", displayName1 = displayName2));
})), ref && (props1 = props3, displayName1 = displayName2, (warnAboutAccessingRef = function() {
specialPropRefWarningShown || (specialPropRefWarningShown = !0, error("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://reactjs.org/link/special-props)", displayName1));
}).isReactWarning = !0, Object.defineProperty(props1, "ref", {
get: warnAboutAccessingRef,
configurable: !0

View File

@ -1871,31 +1871,33 @@
}
var didWarnValueNull = !1, validateProperty$1 = function() {
}, warnedProperties$1 = {
}, _hasOwnProperty = Object.prototype.hasOwnProperty, EVENT_NAME_REGEX = /^on./, INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/, rARIA$1 = /^(aria)-[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, rARIACamel$1 = /^(aria)[A-Z][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, warnUnknownProperties = function(type, props, eventRegistry) {
}, _hasOwnProperty = Object.prototype.hasOwnProperty, EVENT_NAME_REGEX = /^on./, INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/, rARIA$1 = /^(aria)-[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/, rARIACamel$1 = /^(aria)[A-Z][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/;
validateProperty$1 = function(tagName, name, value, eventRegistry) {
if (_hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) return !0;
var lowerCasedName = name.toLowerCase();
if ("onfocusin" === lowerCasedName || "onfocusout" === lowerCasedName) return error("React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React."), warnedProperties$1[name] = !0, !0;
if (null != eventRegistry) {
var registrationNameDependencies = eventRegistry.registrationNameDependencies, possibleRegistrationNames = eventRegistry.possibleRegistrationNames;
if (registrationNameDependencies.hasOwnProperty(name)) return !0;
var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null;
if (null != registrationName) return error("Invalid event handler property `%s`. Did you mean `%s`?", name, registrationName), warnedProperties$1[name] = !0, !0;
if (EVENT_NAME_REGEX.test(name)) return error("Unknown event handler property `%s`. It will be ignored.", name), warnedProperties$1[name] = !0, !0;
} else if (EVENT_NAME_REGEX.test(name)) return INVALID_EVENT_NAME_REGEX.test(name) && error("Invalid event handler property `%s`. React events use the camelCase naming convention, for example `onClick`.", name), warnedProperties$1[name] = !0, !0;
if (rARIA$1.test(name) || rARIACamel$1.test(name)) return !0;
if ("innerhtml" === lowerCasedName) return error("Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`."), warnedProperties$1[name] = !0, !0;
if ("aria" === lowerCasedName) return error("The `aria` attribute is reserved for future use in React. Pass individual `aria-` attributes instead."), warnedProperties$1[name] = !0, !0;
if ("is" === lowerCasedName && null != value && "string" != typeof value) return error("Received a `%s` for a string attribute `is`. If this is expected, cast the value to a string.", typeof value), warnedProperties$1[name] = !0, !0;
if ("number" == typeof value && isNaN(value)) return error("Received NaN for the `%s` attribute. If this is expected, cast the value to a string.", name), warnedProperties$1[name] = !0, !0;
var propertyInfo = getPropertyInfo(name), isReserved = null !== propertyInfo && 0 === propertyInfo.type;
if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
var standardName = possibleStandardNames[lowerCasedName];
if (standardName !== name) return error("Invalid DOM property `%s`. Did you mean `%s`?", name, standardName), warnedProperties$1[name] = !0, !0;
} else if (!isReserved && name !== lowerCasedName) return error("React does not recognize the `%s` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `%s` instead. If you accidentally passed it from a parent component, remove it from the DOM element.", name, lowerCasedName), warnedProperties$1[name] = !0, !0;
return "boolean" == typeof value && shouldRemoveAttributeWithWarning(name, value, propertyInfo, !1) ? (value ? error("Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s=\"%s\" or %s={value.toString()}.", value, name, name, value, name) : error("Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s=\"%s\" or %s={value.toString()}.\n\nIf you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.", value, name, name, value, name, name, name), warnedProperties$1[name] = !0, !0) : !!isReserved || (shouldRemoveAttributeWithWarning(name, value, propertyInfo, !1) ? (warnedProperties$1[name] = !0, !1) : "false" !== value && "true" !== value || null === propertyInfo || 3 !== propertyInfo.type || (error("Received the string `%s` for the boolean attribute `%s`. %s Did you mean %s={%s}?", value, name, "false" === value ? "The browser will interpret it as a truthy value." : "Although this works, it will not work as expected if you pass the string \"false\".", name, value), warnedProperties$1[name] = !0, !0));
};
var warnUnknownProperties = function(type, props, eventRegistry) {
var unknownProps = [];
for(var key in props)(validateProperty$1 = function(tagName, name, value, eventRegistry) {
if (_hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) return !0;
var lowerCasedName = name.toLowerCase();
if ("onfocusin" === lowerCasedName || "onfocusout" === lowerCasedName) return error("React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React."), warnedProperties$1[name] = !0, !0;
if (null != eventRegistry) {
var registrationNameDependencies = eventRegistry.registrationNameDependencies, possibleRegistrationNames = eventRegistry.possibleRegistrationNames;
if (registrationNameDependencies.hasOwnProperty(name)) return !0;
var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null;
if (null != registrationName) return error("Invalid event handler property `%s`. Did you mean `%s`?", name, registrationName), warnedProperties$1[name] = !0, !0;
if (EVENT_NAME_REGEX.test(name)) return error("Unknown event handler property `%s`. It will be ignored.", name), warnedProperties$1[name] = !0, !0;
} else if (EVENT_NAME_REGEX.test(name)) return INVALID_EVENT_NAME_REGEX.test(name) && error("Invalid event handler property `%s`. React events use the camelCase naming convention, for example `onClick`.", name), warnedProperties$1[name] = !0, !0;
if (rARIA$1.test(name) || rARIACamel$1.test(name)) return !0;
if ("innerhtml" === lowerCasedName) return error("Directly setting property `innerHTML` is not permitted. For more information, lookup documentation on `dangerouslySetInnerHTML`."), warnedProperties$1[name] = !0, !0;
if ("aria" === lowerCasedName) return error("The `aria` attribute is reserved for future use in React. Pass individual `aria-` attributes instead."), warnedProperties$1[name] = !0, !0;
if ("is" === lowerCasedName && null != value && "string" != typeof value) return error("Received a `%s` for a string attribute `is`. If this is expected, cast the value to a string.", typeof value), warnedProperties$1[name] = !0, !0;
if ("number" == typeof value && isNaN(value)) return error("Received NaN for the `%s` attribute. If this is expected, cast the value to a string.", name), warnedProperties$1[name] = !0, !0;
var propertyInfo = getPropertyInfo(name), isReserved = null !== propertyInfo && 0 === propertyInfo.type;
if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
var standardName = possibleStandardNames[lowerCasedName];
if (standardName !== name) return error("Invalid DOM property `%s`. Did you mean `%s`?", name, standardName), warnedProperties$1[name] = !0, !0;
} else if (!isReserved && name !== lowerCasedName) return error("React does not recognize the `%s` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `%s` instead. If you accidentally passed it from a parent component, remove it from the DOM element.", name, lowerCasedName), warnedProperties$1[name] = !0, !0;
return "boolean" == typeof value && shouldRemoveAttributeWithWarning(name, value, propertyInfo, !1) ? (value ? error("Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s=\"%s\" or %s={value.toString()}.", value, name, name, value, name) : error("Received `%s` for a non-boolean attribute `%s`.\n\nIf you want to write it to the DOM, pass a string instead: %s=\"%s\" or %s={value.toString()}.\n\nIf you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.", value, name, name, value, name, name, name), warnedProperties$1[name] = !0, !0) : !!isReserved || (shouldRemoveAttributeWithWarning(name, value, propertyInfo, !1) ? (warnedProperties$1[name] = !0, !1) : "false" !== value && "true" !== value || null === propertyInfo || 3 !== propertyInfo.type || (error("Received the string `%s` for the boolean attribute `%s`. %s Did you mean %s={%s}?", value, name, "false" === value ? "The browser will interpret it as a truthy value." : "Although this works, it will not work as expected if you pass the string \"false\".", name, value), warnedProperties$1[name] = !0, !0));
})(type, key, props[key], eventRegistry) || unknownProps.push(key);
for(var key in props)validateProperty$1(type, key, props[key], eventRegistry) || unknownProps.push(key);
var unknownPropString = unknownProps.map(function(prop) {
return "`" + prop + "`";
}).join(", ");
@ -6221,6 +6223,74 @@
return currentHookNameInDev = "useOpaqueIdentifier", warnInvalidHookAccess(), mountHookTypesDev(), mountOpaqueIdentifier();
},
unstable_isNewReconciler: enableNewReconciler
}, InvalidNestedHooksDispatcherOnUpdateInDEV = {
readContext: function(context, observedBits) {
return warnInvalidContextAccess(), readContext(context, observedBits);
},
useCallback: function(callback, deps) {
return currentHookNameInDev = "useCallback", warnInvalidHookAccess(), updateHookTypesDev(), updateCallback(callback, deps);
},
useContext: function(context, observedBits) {
return currentHookNameInDev = "useContext", warnInvalidHookAccess(), updateHookTypesDev(), readContext(context, observedBits);
},
useEffect: function(create, deps) {
return currentHookNameInDev = "useEffect", warnInvalidHookAccess(), updateHookTypesDev(), updateEffect(create, deps);
},
useImperativeHandle: function(ref, create, deps) {
return currentHookNameInDev = "useImperativeHandle", warnInvalidHookAccess(), updateHookTypesDev(), updateImperativeHandle(ref, create, deps);
},
useLayoutEffect: function(create, deps) {
return currentHookNameInDev = "useLayoutEffect", warnInvalidHookAccess(), updateHookTypesDev(), updateLayoutEffect(create, deps);
},
useMemo: function(create, deps) {
currentHookNameInDev = "useMemo", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateMemo(create, deps);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useReducer: function(reducer, initialArg, init) {
currentHookNameInDev = "useReducer", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateReducer(reducer, initialArg, init);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useRef: function(initialValue) {
return currentHookNameInDev = "useRef", warnInvalidHookAccess(), updateHookTypesDev(), updateRef();
},
useState: function(initialState) {
currentHookNameInDev = "useState", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateState(initialState);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useDebugValue: function(value, formatterFn) {
return currentHookNameInDev = "useDebugValue", warnInvalidHookAccess(), updateHookTypesDev(), updateDebugValue();
},
useDeferredValue: function(value) {
return currentHookNameInDev = "useDeferredValue", warnInvalidHookAccess(), updateHookTypesDev(), updateDeferredValue(value);
},
useTransition: function() {
return currentHookNameInDev = "useTransition", warnInvalidHookAccess(), updateHookTypesDev(), updateTransition();
},
useMutableSource: function(source, getSnapshot, subscribe) {
return currentHookNameInDev = "useMutableSource", warnInvalidHookAccess(), updateHookTypesDev(), updateMutableSource(source, getSnapshot, subscribe);
},
useOpaqueIdentifier: function() {
return currentHookNameInDev = "useOpaqueIdentifier", warnInvalidHookAccess(), updateHookTypesDev(), updateOpaqueIdentifier();
},
unstable_isNewReconciler: enableNewReconciler
}, InvalidNestedHooksDispatcherOnRerenderInDEV = {
readContext: function(context, observedBits) {
return warnInvalidContextAccess(), readContext(context, observedBits);
@ -6243,75 +6313,7 @@
useMemo: function(create, deps) {
currentHookNameInDev = "useMemo", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV = {
readContext: function(context, observedBits) {
return warnInvalidContextAccess(), readContext(context, observedBits);
},
useCallback: function(callback, deps) {
return currentHookNameInDev = "useCallback", warnInvalidHookAccess(), updateHookTypesDev(), updateCallback(callback, deps);
},
useContext: function(context, observedBits) {
return currentHookNameInDev = "useContext", warnInvalidHookAccess(), updateHookTypesDev(), readContext(context, observedBits);
},
useEffect: function(create, deps) {
return currentHookNameInDev = "useEffect", warnInvalidHookAccess(), updateHookTypesDev(), updateEffect(create, deps);
},
useImperativeHandle: function(ref, create, deps) {
return currentHookNameInDev = "useImperativeHandle", warnInvalidHookAccess(), updateHookTypesDev(), updateImperativeHandle(ref, create, deps);
},
useLayoutEffect: function(create, deps) {
return currentHookNameInDev = "useLayoutEffect", warnInvalidHookAccess(), updateHookTypesDev(), updateLayoutEffect(create, deps);
},
useMemo: function(create, deps) {
currentHookNameInDev = "useMemo", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateMemo(create, deps);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useReducer: function(reducer, initialArg, init) {
currentHookNameInDev = "useReducer", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateReducer(reducer, initialArg, init);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useRef: function(initialValue) {
return currentHookNameInDev = "useRef", warnInvalidHookAccess(), updateHookTypesDev(), updateRef();
},
useState: function(initialState) {
currentHookNameInDev = "useState", warnInvalidHookAccess(), updateHookTypesDev();
var prevDispatcher = ReactCurrentDispatcher$1.current;
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateState(initialState);
} finally{
ReactCurrentDispatcher$1.current = prevDispatcher;
}
},
useDebugValue: function(value, formatterFn) {
return currentHookNameInDev = "useDebugValue", warnInvalidHookAccess(), updateHookTypesDev(), updateDebugValue();
},
useDeferredValue: function(value) {
return currentHookNameInDev = "useDeferredValue", warnInvalidHookAccess(), updateHookTypesDev(), updateDeferredValue(value);
},
useTransition: function() {
return currentHookNameInDev = "useTransition", warnInvalidHookAccess(), updateHookTypesDev(), updateTransition();
},
useMutableSource: function(source, getSnapshot, subscribe) {
return currentHookNameInDev = "useMutableSource", warnInvalidHookAccess(), updateHookTypesDev(), updateMutableSource(source, getSnapshot, subscribe);
},
useOpaqueIdentifier: function() {
return currentHookNameInDev = "useOpaqueIdentifier", warnInvalidHookAccess(), updateHookTypesDev(), updateOpaqueIdentifier();
},
unstable_isNewReconciler: enableNewReconciler
};
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
try {
return updateMemo(create, deps);
} finally{

View File

@ -1 +1,3 @@
console.log(1, 2, void 0, void 0);
const undef = (a)=>{
};
console.log(1, 2, undef(3), undef(4));

View File

@ -21,5 +21,6 @@
"lazy": false,
"noInterop": false
},
"sourceMaps": true
"sourceMaps": true,
"inlineSourcesContent": true
}

View File

@ -7,5 +7,8 @@
"sources": [
"../../input/index.ts"
],
"sourcesContent": [
"\n\nexport const foo = {\n arr: []\n};"
],
"version": 3
}

View File

@ -0,0 +1,19 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"dynamicImport": false,
"decorators": false
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es5",
"loose": false,
"externalHelpers": true
},
"sourceMaps": true,
"inlineSourcesContent": true
}

View File

@ -0,0 +1,13 @@
export default function StaticPage({ data }) {
return <div>{data.foo}</div>
}
export async function getStaticProps() {
return {
props: {
data: {
foo: 'bar',
},
},
}
}

View File

@ -0,0 +1,29 @@
import * as swcHelpers from "@swc/helpers";
import regeneratorRuntime from "regenerator-runtime";
export default function StaticPage(param) {
var data = param.data;
return(/*#__PURE__*/ React.createElement("div", null, data.foo));
};
function _getStaticProps() {
_getStaticProps = swcHelpers.asyncToGenerator(regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_ctx) {
while(1)switch(_ctx.prev = _ctx.next){
case 0:
return _ctx.abrupt("return", {
props: {
data: {
foo: 'bar'
}
}
});
case 1:
case "end":
return _ctx.stop();
}
}, _callee);
}));
return _getStaticProps.apply(this, arguments);
}
export function getStaticProps() {
return _getStaticProps.apply(this, arguments);
}

View File

@ -0,0 +1,18 @@
{
"mappings": ";;eAAe,QAAQ,CAAC,UAAU,CAAC,KAAQ,EAAE,CAAC;QAAT,IAAI,GAAN,KAAQ,CAAN,IAAI;IACrC,MAAM,mCAAE,CAAG,YAAE,IAAI,CAAC,GAAG;AACzB,CAAC;SAEqB,eAAc;IAAd,eAAc,uDAA7B,QAAQ,WAAwB,CAAC;;;;iDAC7B,CAAC;wBACJ,KAAK,EAAE,CAAC;4BACJ,IAAI,EAAE,CAAC;gCACH,GAAG,EAAE,CAAK;4BACd,CAAC;wBACL,CAAC;oBACL,CAAC;;;;;;IACL,CAAC;WARqB,eAAc;;gBAAd,cAAc;WAAd,eAAc",
"names": [
"StaticPage",
"data",
"div",
"foo",
"getStaticProps",
"props"
],
"sources": [
"../../input/index.js"
],
"sourcesContent": [
"export default function StaticPage({ data }) {\n return <div>{data.foo}</div>\n}\n\nexport async function getStaticProps() {\n return {\n props: {\n data: {\n foo: 'bar',\n },\n },\n }\n}"
],
"version": 3
}

View File

@ -0,0 +1,24 @@
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true,
"dynamicImport": false,
"decorators": false
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es5",
"loose": false,
"externalHelpers": true,
"minify": {
"compress": true,
"mangle": true
}
},
"minify": true,
"sourceMaps": true,
"inlineSourcesContent": true
}

View File

@ -0,0 +1,13 @@
export default function StaticPage({ data }) {
return <div>{data.foo}</div>
}
export async function getStaticProps() {
return {
props: {
data: {
foo: 'bar',
},
},
}
}

View File

@ -0,0 +1 @@
import*as a from"@swc/helpers";import b from"regenerator-runtime";export default function StaticPage(c){var d=c.data;return React.createElement("div",null,d.foo)};function _getStaticProps(){return(_getStaticProps=a.asyncToGenerator(b.mark(function _callee(){return b.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",{props:{data:{foo:"bar"}}});case 1:case"end":return e.stop()}},_callee)}))).apply(this,arguments)}export function getStaticProps(){return _getStaticProps.apply(this,arguments)}

View File

@ -0,0 +1,18 @@
{
"mappings": "iFAAe,QAAQ,CAAC,UAAU,CAAC,CAAQ,CAAE,CAAC,IAAT,CAAI,CAAN,CAAQ,CAAN,IAAI,CACrC,MAAM,qBAAE,CAAG,UAAE,CAAI,CAAC,GAAG,CACzB,CAAC,UAEqB,eAAc,UAAd,eAAc,2BAA7B,QAAQ,UAAwB,CAAC,uFAC7B,CAAC,AACJ,KAAK,CAAE,CAAC,AACJ,IAAI,CAAE,CAAC,AACH,GAAG,CAAE,CAAK,IACd,CAAC,AACL,CAAC,AACL,CAAC,6CACL,CAAC,0CARqB,cAAc,UAAd,eAAc",
"names": [
"StaticPage",
"data",
"div",
"foo",
"getStaticProps",
"props"
],
"sources": [
"../../input/index.js"
],
"sourcesContent": [
"export default function StaticPage({ data }) {\n return <div>{data.foo}</div>\n}\n\nexport async function getStaticProps() {\n return {\n props: {\n data: {\n foo: 'bar',\n },\n },\n }\n}"
],
"version": 3
}

View File

@ -790,6 +790,7 @@ fn should_visit() {
}
#[testing::fixture("tests/fixture/**/input/")]
#[testing::fixture("tests/vercel/**/input/")]
fn tests(input_dir: PathBuf) {
let output = input_dir.parent().unwrap().join("output");

24
tests/vercel/full/.swcrc Normal file
View File

@ -0,0 +1,24 @@
{
"jsc": {
"target": "es5",
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic",
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"useBuiltins": true
}
},
"minify": {
"compress": true,
"mangle": true,
"toplevel": true
},
"externalHelpers": true
}
}

View File

@ -0,0 +1,19 @@
export default function createStore(initialState) {
let state = initialState;
const listeners = [];
return {
getState() {
return state;
},
setState(nextState) {
state = nextState;
listeners.forEach(listener => listener());
},
subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
listeners.splice(listeners.indexOf(listener), 1);
};
},
};
}

View File

@ -0,0 +1,18 @@
export default function createStore(a) {
var b = a, c = [];
return {
getState: function() {
return b;
},
setState: function(d) {
b = d, c.forEach(function(e) {
return e();
});
},
subscribe: function(f) {
return c.push(f), function() {
c.splice(c.indexOf(f), 1);
};
}
};
};

View File

@ -0,0 +1,658 @@
import algoliasearchHelper from 'algoliasearch-helper';
import createWidgetsManager from './createWidgetsManager';
import { HIGHLIGHT_TAGS } from './highlight';
import { hasMultipleIndices } from './indexUtils';
import { version as ReactVersion } from 'react';
import version from './version';
function addAlgoliaAgents(searchClient) {
if (typeof searchClient.addAlgoliaAgent === 'function') {
searchClient.addAlgoliaAgent(`react (${ReactVersion})`);
searchClient.addAlgoliaAgent(`react-instantsearch (${version})`);
}
}
const isMultiIndexContext = widget =>
hasMultipleIndices({
ais: widget.props.contextValue,
multiIndexContext: widget.props.indexContextValue,
});
const isTargetedIndexEqualIndex = (widget, indexId) =>
widget.props.indexContextValue.targetedIndex === indexId;
// Relying on the `indexId` is a bit brittle to detect the `Index` widget.
// Since it's a class we could rely on `instanceof` or similar. We never
// had an issue though. Works for now.
const isIndexWidget = widget => Boolean(widget.props.indexId);
const isIndexWidgetEqualIndex = (widget, indexId) =>
widget.props.indexId === indexId;
const sortIndexWidgetsFirst = (firstWidget, secondWidget) => {
const isFirstWidgetIndex = isIndexWidget(firstWidget);
const isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) {
return -1;
}
if (!isFirstWidgetIndex && isSecondWidgetIndex) {
return 1;
}
return 0;
};
// This function is copied from the algoliasearch v4 API Client. If modified,
// consider updating it also in `serializeQueryParameters` from `@algolia/transporter`.
function serializeQueryParameters(parameters) {
const isObjectOrArray = value =>
Object.prototype.toString.call(value) === '[object Object]' ||
Object.prototype.toString.call(value) === '[object Array]';
const encode = (format, ...args) => {
let i = 0;
return format.replace(/%s/g, () => encodeURIComponent(args[i++]));
};
return Object.keys(parameters)
.map(key =>
encode(
'%s=%s',
key,
isObjectOrArray(parameters[key])
? JSON.stringify(parameters[key])
: parameters[key]
)
)
.join('&');
}
/**
* Creates a new instance of the InstantSearchManager which controls the widgets and
* trigger the search when the widgets are updated.
* @param {string} indexName - the main index name
* @param {object} initialState - initial widget state
* @param {object} SearchParameters - optional additional parameters to send to the algolia API
* @param {number} stalledSearchDelay - time (in ms) after the search is stalled
* @return {InstantSearchManager} a new instance of InstantSearchManager
*/
export default function createInstantSearchManager({
indexName,
initialState = {},
searchClient,
resultsState,
stalledSearchDelay,
}) {
function createStore(initialState) {
let state = initialState;
const listeners = [];
return {
getState() {
return state;
},
setState(nextState) {
state = nextState;
listeners.forEach(listener => listener());
},
subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
listeners.splice(listeners.indexOf(listener), 1);
};
},
};
}
const helper = algoliasearchHelper(searchClient, indexName, {
...HIGHLIGHT_TAGS,
});
addAlgoliaAgents(searchClient);
helper
.on('search', handleNewSearch)
.on('result', handleSearchSuccess({ indexId: indexName }))
.on('error', handleSearchError);
let skip = false;
let stalledSearchTimer = null;
let initialSearchParameters = helper.state;
const widgetsManager = createWidgetsManager(onWidgetsUpdate);
hydrateSearchClient(searchClient, resultsState);
const store = createStore({
widgets: initialState,
metadata: hydrateMetadata(resultsState),
results: hydrateResultsState(resultsState),
error: null,
searching: false,
isSearchStalled: true,
searchingForFacetValues: false,
});
function skipSearch() {
skip = true;
}
function updateClient(client) {
addAlgoliaAgents(client);
helper.setClient(client);
search();
}
function clearCache() {
helper.clearCache();
search();
}
function getMetadata(state) {
return widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getMetadata))
.map(widget => widget.getMetadata(state));
}
function getSearchParameters() {
const sharedParameters = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => !isMultiIndexContext(widget) && !isIndexWidget(widget))
.reduce(
(res, widget) => widget.getSearchParameters(res),
initialSearchParameters
);
const mainParameters = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => {
const targetedIndexEqualMainIndex =
isMultiIndexContext(widget) &&
isTargetedIndexEqualIndex(widget, indexName);
const subIndexEqualMainIndex =
isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexEqualMainIndex || subIndexEqualMainIndex;
})
// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst)
.reduce(
(res, widget) => widget.getSearchParameters(res),
sharedParameters
);
const derivedIndices = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => {
const targetedIndexNotEqualMainIndex =
isMultiIndexContext(widget) &&
!isTargetedIndexEqualIndex(widget, indexName);
const subIndexNotEqualMainIndex =
isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex;
})
// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst)
.reduce((indices, widget) => {
const indexId = isMultiIndexContext(widget)
? widget.props.indexContextValue.targetedIndex
: widget.props.indexId;
const widgets = indices[indexId] || [];
return {
...indices,
[indexId]: widgets.concat(widget),
};
}, {});
const derivedParameters = Object.keys(derivedIndices).map(indexId => ({
parameters: derivedIndices[indexId].reduce(
(res, widget) => widget.getSearchParameters(res),
sharedParameters
),
indexId,
}));
return {
mainParameters,
derivedParameters,
};
}
function search() {
if (!skip) {
const { mainParameters, derivedParameters } = getSearchParameters(
helper.state
);
// We have to call `slice` because the method `detach` on the derived
// helpers mutates the value `derivedHelpers`. The `forEach` loop does
// not iterate on each value and we're not able to correctly clear the
// previous derived helpers (memory leak + useless requests).
helper.derivedHelpers.slice().forEach(derivedHelper => {
// Since we detach the derived helpers on **every** new search they
// won't receive intermediate results in case of a stalled search.
// Only the last result is dispatched by the derived helper because
// they are not detached yet:
//
// - a -> main helper receives results
// - ap -> main helper receives results
// - app -> main helper + derived helpers receive results
//
// The quick fix is to avoid to detach them on search but only once they
// received the results. But it means that in case of a stalled search
// all the derived helpers not detached yet register a new search inside
// the helper. The number grows fast in case of a bad network and it's
// not deterministic.
derivedHelper.detach();
});
derivedParameters.forEach(({ indexId, parameters }) => {
const derivedHelper = helper.derive(() => parameters);
derivedHelper
.on('result', handleSearchSuccess({ indexId }))
.on('error', handleSearchError);
});
helper.setState(mainParameters);
helper.search();
}
}
function handleSearchSuccess({ indexId }) {
return event => {
const state = store.getState();
const isDerivedHelpersEmpty = !helper.derivedHelpers.length;
let results = state.results ? state.results : {};
// Switching from mono index to multi index and vice versa must reset the
// results to an empty object, otherwise we keep reference of stalled and
// unused results.
results = !isDerivedHelpersEmpty && results.getFacetByName ? {} : results;
if (!isDerivedHelpersEmpty) {
results = { ...results, [indexId]: event.results };
} else {
results = event.results;
}
const currentState = store.getState();
let nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
stalledSearchTimer = null;
nextIsSearchStalled = false;
}
const { resultsFacetValues, ...partialState } = currentState;
store.setState({
...partialState,
results,
isSearchStalled: nextIsSearchStalled,
searching: false,
error: null,
});
};
}
function handleSearchError({ error }) {
const currentState = store.getState();
let nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
nextIsSearchStalled = false;
}
const { resultsFacetValues, ...partialState } = currentState;
store.setState({
...partialState,
isSearchStalled: nextIsSearchStalled,
error,
searching: false,
});
}
function handleNewSearch() {
if (!stalledSearchTimer) {
stalledSearchTimer = setTimeout(() => {
const { resultsFacetValues, ...partialState } = store.getState();
store.setState({
...partialState,
isSearchStalled: true,
});
}, stalledSearchDelay);
}
}
function hydrateSearchClient(client, results) {
if (!results) {
return;
}
// Disable cache hydration on:
// - Algoliasearch API Client < v4 with cache disabled
// - Third party clients (detected by the `addAlgoliaAgent` function missing)
if (
(!client.transporter || client._cacheHydrated) &&
(!client._useCache || typeof client.addAlgoliaAgent !== 'function')
) {
return;
}
// Algoliasearch API Client >= v4
// To hydrate the client we need to populate the cache with the data from
// the server (done in `hydrateSearchClientWithMultiIndexRequest` or
// `hydrateSearchClientWithSingleIndexRequest`). But since there is no way
// for us to compute the key the same way as `algoliasearch-client` we need
// to populate it on a custom key and override the `search` method to
// search on it first.
if (client.transporter && !client._cacheHydrated) {
client._cacheHydrated = true;
const baseMethod = client.search;
client.search = (requests, ...methodArgs) => {
const requestsWithSerializedParams = requests.map(request => ({
...request,
params: serializeQueryParameters(request.params),
}));
return client.transporter.responsesCache.get(
{
method: 'search',
args: [requestsWithSerializedParams, ...methodArgs],
},
() => {
return baseMethod(requests, ...methodArgs);
}
);
};
}
if (Array.isArray(results.results)) {
hydrateSearchClientWithMultiIndexRequest(client, results.results);
return;
}
hydrateSearchClientWithSingleIndexRequest(client, results);
}
function hydrateSearchClientWithMultiIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set(
{
method: 'search',
args: [
results.reduce(
(acc, result) =>
acc.concat(
result.rawResults.map(request => ({
indexName: request.index,
params: request.params,
}))
),
[]
),
],
},
{
results: results.reduce(
(acc, result) => acc.concat(result.rawResults),
[]
),
}
);
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
const key = `/1/indexes/*/queries_body_${JSON.stringify({
requests: results.reduce(
(acc, result) =>
acc.concat(
result.rawResults.map(request => ({
indexName: request.index,
params: request.params,
}))
),
[]
),
})}`;
client.cache = {
...client.cache,
[key]: JSON.stringify({
results: results.reduce(
(acc, result) => acc.concat(result.rawResults),
[]
),
}),
};
}
function hydrateSearchClientWithSingleIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set(
{
method: 'search',
args: [
results.rawResults.map(request => ({
indexName: request.index,
params: request.params,
})),
],
},
{
results: results.rawResults,
}
);
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
const key = `/1/indexes/*/queries_body_${JSON.stringify({
requests: results.rawResults.map(request => ({
indexName: request.index,
params: request.params,
})),
})}`;
client.cache = {
...client.cache,
[key]: JSON.stringify({
results: results.rawResults,
}),
};
}
function hydrateResultsState(results) {
if (!results) {
return null;
}
if (Array.isArray(results.results)) {
return results.results.reduce(
(acc, result) => ({
...acc,
[result._internalIndexId]: new algoliasearchHelper.SearchResults(
new algoliasearchHelper.SearchParameters(result.state),
result.rawResults
),
}),
{}
);
}
return new algoliasearchHelper.SearchResults(
new algoliasearchHelper.SearchParameters(results.state),
results.rawResults
);
}
// Called whenever a widget has been rendered with new props.
function onWidgetsUpdate() {
const metadata = getMetadata(store.getState().widgets);
store.setState({
...store.getState(),
metadata,
searching: true,
});
// Since the `getSearchParameters` method of widgets also depends on props,
// the result search parameters might have changed.
search();
}
function transitionState(nextSearchState) {
const searchState = store.getState().widgets;
return widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.transitionState))
.reduce(
(res, widget) => widget.transitionState(searchState, res),
nextSearchState
);
}
function onExternalStateUpdate(nextSearchState) {
const metadata = getMetadata(nextSearchState);
store.setState({
...store.getState(),
widgets: nextSearchState,
metadata,
searching: true,
});
search();
}
function onSearchForFacetValues({ facetName, query, maxFacetHits = 10 }) {
// The values 1, 100 are the min / max values that the engine accepts.
// see: https://www.algolia.com/doc/api-reference/api-parameters/maxFacetHits
const maxFacetHitsWithinRange = Math.max(1, Math.min(maxFacetHits, 100));
store.setState({
...store.getState(),
searchingForFacetValues: true,
});
helper
.searchForFacetValues(facetName, query, maxFacetHitsWithinRange)
.then(
content => {
store.setState({
...store.getState(),
error: null,
searchingForFacetValues: false,
resultsFacetValues: {
...store.getState().resultsFacetValues,
[facetName]: content.facetHits,
query,
},
});
},
error => {
store.setState({
...store.getState(),
searchingForFacetValues: false,
error,
});
}
)
.catch(error => {
// Since setState is synchronous, any error that occurs in the render of a
// component will be swallowed by this promise.
// This is a trick to make the error show up correctly in the console.
// See http://stackoverflow.com/a/30741722/969302
setTimeout(() => {
throw error;
});
});
}
function updateIndex(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
// No need to trigger a new search here as the widgets will also update and trigger it if needed.
}
function getWidgetsIds() {
return store
.getState()
.metadata.reduce(
(res, meta) =>
typeof meta.id !== 'undefined' ? res.concat(meta.id) : res,
[]
);
}
return {
store,
widgetsManager,
getWidgetsIds,
getSearchParameters,
onSearchForFacetValues,
onExternalStateUpdate,
transitionState,
updateClient,
updateIndex,
clearCache,
skipSearch,
};
}
function hydrateMetadata(resultsState) {
if (!resultsState) {
return [];
}
// add a value noop, which gets replaced once the widgets are mounted
return resultsState.metadata.map(datum => ({
value: () => ({}),
...datum,
items:
datum.items &&
datum.items.map(item => ({
value: () => ({}),
...item,
items:
item.items &&
item.items.map(nestedItem => ({
value: () => ({}),
...nestedItem,
})),
})),
}));
}

View File

@ -0,0 +1,370 @@
import * as a from "@swc/helpers";
import b from "algoliasearch-helper";
import c from "./createWidgetsManager";
import { HIGHLIGHT_TAGS as d } from "./highlight";
import { hasMultipleIndices as e } from "./indexUtils";
import { version as f } from "react";
import g from "./version";
function addAlgoliaAgents(h) {
"function" == typeof h.addAlgoliaAgent && (h.addAlgoliaAgent("react (".concat(f, ")")), h.addAlgoliaAgent("react-instantsearch (".concat(g, ")")));
}
var _obj, isMultiIndexContext = function(i) {
return e({
ais: i.props.contextValue,
multiIndexContext: i.props.indexContextValue
});
}, isTargetedIndexEqualIndex = function(j, k) {
return j.props.indexContextValue.targetedIndex === k;
}, isIndexWidget = function(l) {
return Boolean(l.props.indexId);
}, isIndexWidgetEqualIndex = function(m, n) {
return m.props.indexId === n;
}, sortIndexWidgetsFirst = function(o, p) {
var q = isIndexWidget(o), r = isIndexWidget(p);
return q && !r ? -1 : !q && r ? 1 : 0;
};
function serializeQueryParameters(s) {
var t = function(u) {
for(var v = arguments.length, w = new Array(v > 1 ? v - 1 : 0), x = 1; x < v; x++)w[x - 1] = arguments[x];
var y = 0;
return u.replace(/%s/g, function() {
return encodeURIComponent(w[y++]);
});
};
return Object.keys(s).map(function(z) {
var A;
return t("%s=%s", z, (A = s[z], "[object Object]" === Object.prototype.toString.call(A) || "[object Array]" === Object.prototype.toString.call(A)) ? JSON.stringify(s[z]) : s[z]);
}).join("&");
}
export default function createInstantSearchManager(B) {
var C = B.indexName, D = B.initialState, E = B.searchClient, F = B.resultsState, G = B.stalledSearchDelay, H = function(I) {
return J.getWidgets().filter(function(K) {
return Boolean(K.getMetadata);
}).map(function(L) {
return L.getMetadata(I);
});
}, M = function() {
var N = J.getWidgets().filter(function(O) {
return Boolean(O.getSearchParameters);
}).filter(function(P) {
return !isMultiIndexContext(P) && !isIndexWidget(P);
}).reduce(function(Q, R) {
return R.getSearchParameters(Q);
}, S), T = J.getWidgets().filter(function(U) {
return Boolean(U.getSearchParameters);
}).filter(function(V) {
var W = isMultiIndexContext(V) && isTargetedIndexEqualIndex(V, C), X = isIndexWidget(V) && isIndexWidgetEqualIndex(V, C);
return W || X;
}).sort(sortIndexWidgetsFirst).reduce(function(Y, Z) {
return Z.getSearchParameters(Y);
}, N), $ = J.getWidgets().filter(function(_) {
return Boolean(_.getSearchParameters);
}).filter(function(aa) {
var ba = isMultiIndexContext(aa) && !isTargetedIndexEqualIndex(aa, C), ca = isIndexWidget(aa) && !isIndexWidgetEqualIndex(aa, C);
return ba || ca;
}).sort(sortIndexWidgetsFirst).reduce(function(da, ea) {
var fa = isMultiIndexContext(ea) ? ea.props.indexContextValue.targetedIndex : ea.props.indexId, ga = da[fa] || [];
return a.objectSpread({
}, da, a.defineProperty({
}, fa, ga.concat(ea)));
}, {
});
return {
mainParameters: T,
derivedParameters: Object.keys($).map(function(ha) {
return {
parameters: $[ha].reduce(function(ia, ja) {
return ja.getSearchParameters(ia);
}, N),
indexId: ha
};
})
};
}, ka = function() {
if (!la) {
var ma = M(na.state), oa = ma.mainParameters, pa = ma.derivedParameters;
na.derivedHelpers.slice().forEach(function(qa) {
qa.detach();
}), pa.forEach(function(ra) {
var sa = ra.indexId, ta = ra.parameters;
na.derive(function() {
return ta;
}).on("result", ua({
indexId: sa
})).on("error", va);
}), na.setState(oa), na.search();
}
}, ua = function(wa) {
var xa = wa.indexId;
return function(ya) {
var za = Aa.getState(), Ba = !na.derivedHelpers.length, Ca = za.results ? za.results : {
};
Ca = !Ba && Ca.getFacetByName ? {
} : Ca, Ca = Ba ? ya.results : a.objectSpread({
}, Ca, a.defineProperty({
}, xa, ya.results));
var Da = Aa.getState(), Ea = Da.isSearchStalled;
na.hasPendingRequests() || (clearTimeout(Fa), Fa = null, Ea = !1), Da.resultsFacetValues;
var Ga = a.objectWithoutProperties(Da, ["resultsFacetValues"]);
Aa.setState(a.objectSpread({
}, Ga, {
results: Ca,
isSearchStalled: Ea,
searching: !1,
error: null
}));
};
}, va = function(Ha) {
var Ia = Ha.error, Ja = Aa.getState(), Ka = Ja.isSearchStalled;
na.hasPendingRequests() || (clearTimeout(Fa), Ka = !1), Ja.resultsFacetValues;
var La = a.objectWithoutProperties(Ja, ["resultsFacetValues"]);
Aa.setState(a.objectSpread({
}, La, {
isSearchStalled: Ka,
error: Ia,
searching: !1
}));
}, Ma = function(Na, Oa) {
if (Na.transporter) {
Na.transporter.responsesCache.set({
method: "search",
args: [
Oa.reduce(function(Pa, Qa) {
return Pa.concat(Qa.rawResults.map(function(Ra) {
return {
indexName: Ra.index,
params: Ra.params
};
}));
}, []),
]
}, {
results: Oa.reduce(function(Sa, Ta) {
return Sa.concat(Ta.rawResults);
}, [])
});
return;
}
var Ua = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: Oa.reduce(function(Va, Wa) {
return Va.concat(Wa.rawResults.map(function(Xa) {
return {
indexName: Xa.index,
params: Xa.params
};
}));
}, [])
}));
Na.cache = a.objectSpread({
}, Na.cache, a.defineProperty({
}, Ua, JSON.stringify({
results: Oa.reduce(function(Ya, Za) {
return Ya.concat(Za.rawResults);
}, [])
})));
}, $a = function(_a, ab) {
if (_a.transporter) {
_a.transporter.responsesCache.set({
method: "search",
args: [
ab.rawResults.map(function(bb) {
return {
indexName: bb.index,
params: bb.params
};
}),
]
}, {
results: ab.rawResults
});
return;
}
var cb = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: ab.rawResults.map(function(db) {
return {
indexName: db.index,
params: db.params
};
})
}));
_a.cache = a.objectSpread({
}, _a.cache, a.defineProperty({
}, cb, JSON.stringify({
results: ab.rawResults
})));
}, na = b(E, C, a.objectSpread({
}, d));
addAlgoliaAgents(E), na.on("search", function() {
Fa || (Fa = setTimeout(function() {
var eb = Aa.getState(), fb = eb.resultsFacetValues, gb = a.objectWithoutProperties(eb, ["resultsFacetValues"]);
Aa.setState(a.objectSpread({
}, gb, {
isSearchStalled: !0
}));
}, G));
}).on("result", ua({
indexId: C
})).on("error", va);
var la = !1, Fa = null, S = na.state, J = c(function() {
var hb = H(Aa.getState().widgets);
Aa.setState(a.objectSpread({
}, Aa.getState(), {
metadata: hb,
searching: !0
})), ka();
});
!function(ib, jb) {
if (jb && (ib.transporter && !ib._cacheHydrated || ib._useCache && "function" == typeof ib.addAlgoliaAgent)) {
if (ib.transporter && !ib._cacheHydrated) {
ib._cacheHydrated = !0;
var kb = ib.search;
ib.search = function(lb) {
for(var mb = arguments.length, nb = new Array(mb > 1 ? mb - 1 : 0), ob = 1; ob < mb; ob++)nb[ob - 1] = arguments[ob];
var pb = lb.map(function(qb) {
return a.objectSpread({
}, qb, {
params: serializeQueryParameters(qb.params)
});
});
return ib.transporter.responsesCache.get({
method: "search",
args: [
pb
].concat(a.toConsumableArray(nb))
}, function() {
return kb.apply(void 0, [
lb
].concat(a.toConsumableArray(nb)));
});
};
}
if (Array.isArray(jb.results)) {
Ma(ib, jb.results);
return;
}
$a(ib, jb);
}
}(E, F);
var rb, sb, tb, Aa = (sb = {
widgets: void 0 === D ? {
} : D,
metadata: hydrateMetadata(F),
results: (rb = F) ? Array.isArray(rb.results) ? rb.results.reduce(function(ub, vb) {
return a.objectSpread({
}, ub, a.defineProperty({
}, vb._internalIndexId, new b.SearchResults(new b.SearchParameters(vb.state), vb.rawResults)));
}, {
}) : new b.SearchResults(new b.SearchParameters(rb.state), rb.rawResults) : null,
error: null,
searching: !1,
isSearchStalled: !0,
searchingForFacetValues: !1
}, tb = [], {
getState: function() {
return sb;
},
setState: function(wb) {
sb = wb, tb.forEach(function(xb) {
return xb();
});
},
subscribe: function(yb) {
return tb.push(yb), function() {
tb.splice(tb.indexOf(yb), 1);
};
}
});
return {
store: Aa,
widgetsManager: J,
getWidgetsIds: function() {
return Aa.getState().metadata.reduce(function(zb, Ab) {
return void 0 !== Ab.id ? zb.concat(Ab.id) : zb;
}, []);
},
getSearchParameters: M,
onSearchForFacetValues: function(Bb) {
var Cb = Bb.facetName, Db = Bb.query, Eb = Bb.maxFacetHits;
Aa.setState(a.objectSpread({
}, Aa.getState(), {
searchingForFacetValues: !0
})), na.searchForFacetValues(Cb, Db, Math.max(1, Math.min(void 0 === Eb ? 10 : Eb, 100))).then(function(Fb) {
Aa.setState(a.objectSpread({
}, Aa.getState(), {
error: null,
searchingForFacetValues: !1,
resultsFacetValues: a.objectSpread({
}, Aa.getState().resultsFacetValues, (_obj = {
}, a.defineProperty(_obj, Cb, Fb.facetHits), a.defineProperty(_obj, "query", Db), _obj))
}));
}, function(Gb) {
Aa.setState(a.objectSpread({
}, Aa.getState(), {
searchingForFacetValues: !1,
error: Gb
}));
}).catch(function(Hb) {
setTimeout(function() {
throw Hb;
});
});
},
onExternalStateUpdate: function(Ib) {
var Jb = H(Ib);
Aa.setState(a.objectSpread({
}, Aa.getState(), {
widgets: Ib,
metadata: Jb,
searching: !0
})), ka();
},
transitionState: function(Kb) {
var Lb = Aa.getState().widgets;
return J.getWidgets().filter(function(Mb) {
return Boolean(Mb.transitionState);
}).reduce(function(Nb, Ob) {
return Ob.transitionState(Lb, Nb);
}, Kb);
},
updateClient: function(Pb) {
addAlgoliaAgents(Pb), na.setClient(Pb), ka();
},
updateIndex: function(Qb) {
S = S.setIndex(Qb);
},
clearCache: function() {
na.clearCache(), ka();
},
skipSearch: function() {
la = !0;
}
};
};
function hydrateMetadata(Rb) {
return Rb ? Rb.metadata.map(function(Sb) {
return a.objectSpread({
value: function() {
return {
};
}
}, Sb, {
items: Sb.items && Sb.items.map(function(Tb) {
return a.objectSpread({
value: function() {
return {
};
}
}, Tb, {
items: Tb.items && Tb.items.map(function(Ub) {
return a.objectSpread({
value: function() {
return {
};
}
}, Ub);
})
});
})
});
}) : [];
}

View File

@ -0,0 +1,19 @@
{
"jsc": {
"target": "es5",
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic",
"pragma": "React.createElement",
"pragmaFrag": "React.Fragment",
"throwIfNamespace": true,
"useBuiltins": true
}
},
"externalHelpers": true
}
}

View File

@ -0,0 +1,658 @@
import algoliasearchHelper from 'algoliasearch-helper';
import createWidgetsManager from './createWidgetsManager';
import { HIGHLIGHT_TAGS } from './highlight';
import { hasMultipleIndices } from './indexUtils';
import { version as ReactVersion } from 'react';
import version from './version';
function addAlgoliaAgents(searchClient) {
if (typeof searchClient.addAlgoliaAgent === 'function') {
searchClient.addAlgoliaAgent(`react (${ReactVersion})`);
searchClient.addAlgoliaAgent(`react-instantsearch (${version})`);
}
}
const isMultiIndexContext = widget =>
hasMultipleIndices({
ais: widget.props.contextValue,
multiIndexContext: widget.props.indexContextValue,
});
const isTargetedIndexEqualIndex = (widget, indexId) =>
widget.props.indexContextValue.targetedIndex === indexId;
// Relying on the `indexId` is a bit brittle to detect the `Index` widget.
// Since it's a class we could rely on `instanceof` or similar. We never
// had an issue though. Works for now.
const isIndexWidget = widget => Boolean(widget.props.indexId);
const isIndexWidgetEqualIndex = (widget, indexId) =>
widget.props.indexId === indexId;
const sortIndexWidgetsFirst = (firstWidget, secondWidget) => {
const isFirstWidgetIndex = isIndexWidget(firstWidget);
const isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) {
return -1;
}
if (!isFirstWidgetIndex && isSecondWidgetIndex) {
return 1;
}
return 0;
};
// This function is copied from the algoliasearch v4 API Client. If modified,
// consider updating it also in `serializeQueryParameters` from `@algolia/transporter`.
function serializeQueryParameters(parameters) {
const isObjectOrArray = value =>
Object.prototype.toString.call(value) === '[object Object]' ||
Object.prototype.toString.call(value) === '[object Array]';
const encode = (format, ...args) => {
let i = 0;
return format.replace(/%s/g, () => encodeURIComponent(args[i++]));
};
return Object.keys(parameters)
.map(key =>
encode(
'%s=%s',
key,
isObjectOrArray(parameters[key])
? JSON.stringify(parameters[key])
: parameters[key]
)
)
.join('&');
}
/**
* Creates a new instance of the InstantSearchManager which controls the widgets and
* trigger the search when the widgets are updated.
* @param {string} indexName - the main index name
* @param {object} initialState - initial widget state
* @param {object} SearchParameters - optional additional parameters to send to the algolia API
* @param {number} stalledSearchDelay - time (in ms) after the search is stalled
* @return {InstantSearchManager} a new instance of InstantSearchManager
*/
export default function createInstantSearchManager({
indexName,
initialState = {},
searchClient,
resultsState,
stalledSearchDelay,
}) {
function createStore(initialState) {
let state = initialState;
const listeners = [];
return {
getState() {
return state;
},
setState(nextState) {
state = nextState;
listeners.forEach(listener => listener());
},
subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
listeners.splice(listeners.indexOf(listener), 1);
};
},
};
}
const helper = algoliasearchHelper(searchClient, indexName, {
...HIGHLIGHT_TAGS,
});
addAlgoliaAgents(searchClient);
helper
.on('search', handleNewSearch)
.on('result', handleSearchSuccess({ indexId: indexName }))
.on('error', handleSearchError);
let skip = false;
let stalledSearchTimer = null;
let initialSearchParameters = helper.state;
const widgetsManager = createWidgetsManager(onWidgetsUpdate);
hydrateSearchClient(searchClient, resultsState);
const store = createStore({
widgets: initialState,
metadata: hydrateMetadata(resultsState),
results: hydrateResultsState(resultsState),
error: null,
searching: false,
isSearchStalled: true,
searchingForFacetValues: false,
});
function skipSearch() {
skip = true;
}
function updateClient(client) {
addAlgoliaAgents(client);
helper.setClient(client);
search();
}
function clearCache() {
helper.clearCache();
search();
}
function getMetadata(state) {
return widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getMetadata))
.map(widget => widget.getMetadata(state));
}
function getSearchParameters() {
const sharedParameters = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => !isMultiIndexContext(widget) && !isIndexWidget(widget))
.reduce(
(res, widget) => widget.getSearchParameters(res),
initialSearchParameters
);
const mainParameters = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => {
const targetedIndexEqualMainIndex =
isMultiIndexContext(widget) &&
isTargetedIndexEqualIndex(widget, indexName);
const subIndexEqualMainIndex =
isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexEqualMainIndex || subIndexEqualMainIndex;
})
// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst)
.reduce(
(res, widget) => widget.getSearchParameters(res),
sharedParameters
);
const derivedIndices = widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.getSearchParameters))
.filter(widget => {
const targetedIndexNotEqualMainIndex =
isMultiIndexContext(widget) &&
!isTargetedIndexEqualIndex(widget, indexName);
const subIndexNotEqualMainIndex =
isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex;
})
// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst)
.reduce((indices, widget) => {
const indexId = isMultiIndexContext(widget)
? widget.props.indexContextValue.targetedIndex
: widget.props.indexId;
const widgets = indices[indexId] || [];
return {
...indices,
[indexId]: widgets.concat(widget),
};
}, {});
const derivedParameters = Object.keys(derivedIndices).map(indexId => ({
parameters: derivedIndices[indexId].reduce(
(res, widget) => widget.getSearchParameters(res),
sharedParameters
),
indexId,
}));
return {
mainParameters,
derivedParameters,
};
}
function search() {
if (!skip) {
const { mainParameters, derivedParameters } = getSearchParameters(
helper.state
);
// We have to call `slice` because the method `detach` on the derived
// helpers mutates the value `derivedHelpers`. The `forEach` loop does
// not iterate on each value and we're not able to correctly clear the
// previous derived helpers (memory leak + useless requests).
helper.derivedHelpers.slice().forEach(derivedHelper => {
// Since we detach the derived helpers on **every** new search they
// won't receive intermediate results in case of a stalled search.
// Only the last result is dispatched by the derived helper because
// they are not detached yet:
//
// - a -> main helper receives results
// - ap -> main helper receives results
// - app -> main helper + derived helpers receive results
//
// The quick fix is to avoid to detach them on search but only once they
// received the results. But it means that in case of a stalled search
// all the derived helpers not detached yet register a new search inside
// the helper. The number grows fast in case of a bad network and it's
// not deterministic.
derivedHelper.detach();
});
derivedParameters.forEach(({ indexId, parameters }) => {
const derivedHelper = helper.derive(() => parameters);
derivedHelper
.on('result', handleSearchSuccess({ indexId }))
.on('error', handleSearchError);
});
helper.setState(mainParameters);
helper.search();
}
}
function handleSearchSuccess({ indexId }) {
return event => {
const state = store.getState();
const isDerivedHelpersEmpty = !helper.derivedHelpers.length;
let results = state.results ? state.results : {};
// Switching from mono index to multi index and vice versa must reset the
// results to an empty object, otherwise we keep reference of stalled and
// unused results.
results = !isDerivedHelpersEmpty && results.getFacetByName ? {} : results;
if (!isDerivedHelpersEmpty) {
results = { ...results, [indexId]: event.results };
} else {
results = event.results;
}
const currentState = store.getState();
let nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
stalledSearchTimer = null;
nextIsSearchStalled = false;
}
const { resultsFacetValues, ...partialState } = currentState;
store.setState({
...partialState,
results,
isSearchStalled: nextIsSearchStalled,
searching: false,
error: null,
});
};
}
function handleSearchError({ error }) {
const currentState = store.getState();
let nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
nextIsSearchStalled = false;
}
const { resultsFacetValues, ...partialState } = currentState;
store.setState({
...partialState,
isSearchStalled: nextIsSearchStalled,
error,
searching: false,
});
}
function handleNewSearch() {
if (!stalledSearchTimer) {
stalledSearchTimer = setTimeout(() => {
const { resultsFacetValues, ...partialState } = store.getState();
store.setState({
...partialState,
isSearchStalled: true,
});
}, stalledSearchDelay);
}
}
function hydrateSearchClient(client, results) {
if (!results) {
return;
}
// Disable cache hydration on:
// - Algoliasearch API Client < v4 with cache disabled
// - Third party clients (detected by the `addAlgoliaAgent` function missing)
if (
(!client.transporter || client._cacheHydrated) &&
(!client._useCache || typeof client.addAlgoliaAgent !== 'function')
) {
return;
}
// Algoliasearch API Client >= v4
// To hydrate the client we need to populate the cache with the data from
// the server (done in `hydrateSearchClientWithMultiIndexRequest` or
// `hydrateSearchClientWithSingleIndexRequest`). But since there is no way
// for us to compute the key the same way as `algoliasearch-client` we need
// to populate it on a custom key and override the `search` method to
// search on it first.
if (client.transporter && !client._cacheHydrated) {
client._cacheHydrated = true;
const baseMethod = client.search;
client.search = (requests, ...methodArgs) => {
const requestsWithSerializedParams = requests.map(request => ({
...request,
params: serializeQueryParameters(request.params),
}));
return client.transporter.responsesCache.get(
{
method: 'search',
args: [requestsWithSerializedParams, ...methodArgs],
},
() => {
return baseMethod(requests, ...methodArgs);
}
);
};
}
if (Array.isArray(results.results)) {
hydrateSearchClientWithMultiIndexRequest(client, results.results);
return;
}
hydrateSearchClientWithSingleIndexRequest(client, results);
}
function hydrateSearchClientWithMultiIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set(
{
method: 'search',
args: [
results.reduce(
(acc, result) =>
acc.concat(
result.rawResults.map(request => ({
indexName: request.index,
params: request.params,
}))
),
[]
),
],
},
{
results: results.reduce(
(acc, result) => acc.concat(result.rawResults),
[]
),
}
);
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
const key = `/1/indexes/*/queries_body_${JSON.stringify({
requests: results.reduce(
(acc, result) =>
acc.concat(
result.rawResults.map(request => ({
indexName: request.index,
params: request.params,
}))
),
[]
),
})}`;
client.cache = {
...client.cache,
[key]: JSON.stringify({
results: results.reduce(
(acc, result) => acc.concat(result.rawResults),
[]
),
}),
};
}
function hydrateSearchClientWithSingleIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set(
{
method: 'search',
args: [
results.rawResults.map(request => ({
indexName: request.index,
params: request.params,
})),
],
},
{
results: results.rawResults,
}
);
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
const key = `/1/indexes/*/queries_body_${JSON.stringify({
requests: results.rawResults.map(request => ({
indexName: request.index,
params: request.params,
})),
})}`;
client.cache = {
...client.cache,
[key]: JSON.stringify({
results: results.rawResults,
}),
};
}
function hydrateResultsState(results) {
if (!results) {
return null;
}
if (Array.isArray(results.results)) {
return results.results.reduce(
(acc, result) => ({
...acc,
[result._internalIndexId]: new algoliasearchHelper.SearchResults(
new algoliasearchHelper.SearchParameters(result.state),
result.rawResults
),
}),
{}
);
}
return new algoliasearchHelper.SearchResults(
new algoliasearchHelper.SearchParameters(results.state),
results.rawResults
);
}
// Called whenever a widget has been rendered with new props.
function onWidgetsUpdate() {
const metadata = getMetadata(store.getState().widgets);
store.setState({
...store.getState(),
metadata,
searching: true,
});
// Since the `getSearchParameters` method of widgets also depends on props,
// the result search parameters might have changed.
search();
}
function transitionState(nextSearchState) {
const searchState = store.getState().widgets;
return widgetsManager
.getWidgets()
.filter(widget => Boolean(widget.transitionState))
.reduce(
(res, widget) => widget.transitionState(searchState, res),
nextSearchState
);
}
function onExternalStateUpdate(nextSearchState) {
const metadata = getMetadata(nextSearchState);
store.setState({
...store.getState(),
widgets: nextSearchState,
metadata,
searching: true,
});
search();
}
function onSearchForFacetValues({ facetName, query, maxFacetHits = 10 }) {
// The values 1, 100 are the min / max values that the engine accepts.
// see: https://www.algolia.com/doc/api-reference/api-parameters/maxFacetHits
const maxFacetHitsWithinRange = Math.max(1, Math.min(maxFacetHits, 100));
store.setState({
...store.getState(),
searchingForFacetValues: true,
});
helper
.searchForFacetValues(facetName, query, maxFacetHitsWithinRange)
.then(
content => {
store.setState({
...store.getState(),
error: null,
searchingForFacetValues: false,
resultsFacetValues: {
...store.getState().resultsFacetValues,
[facetName]: content.facetHits,
query,
},
});
},
error => {
store.setState({
...store.getState(),
searchingForFacetValues: false,
error,
});
}
)
.catch(error => {
// Since setState is synchronous, any error that occurs in the render of a
// component will be swallowed by this promise.
// This is a trick to make the error show up correctly in the console.
// See http://stackoverflow.com/a/30741722/969302
setTimeout(() => {
throw error;
});
});
}
function updateIndex(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
// No need to trigger a new search here as the widgets will also update and trigger it if needed.
}
function getWidgetsIds() {
return store
.getState()
.metadata.reduce(
(res, meta) =>
typeof meta.id !== 'undefined' ? res.concat(meta.id) : res,
[]
);
}
return {
store,
widgetsManager,
getWidgetsIds,
getSearchParameters,
onSearchForFacetValues,
onExternalStateUpdate,
transitionState,
updateClient,
updateIndex,
clearCache,
skipSearch,
};
}
function hydrateMetadata(resultsState) {
if (!resultsState) {
return [];
}
// add a value noop, which gets replaced once the widgets are mounted
return resultsState.metadata.map(datum => ({
value: () => ({}),
...datum,
items:
datum.items &&
datum.items.map(item => ({
value: () => ({}),
...item,
items:
item.items &&
item.items.map(nestedItem => ({
value: () => ({}),
...nestedItem,
})),
})),
}));
}

View File

@ -0,0 +1,548 @@
import * as swcHelpers from "@swc/helpers";
import algoliasearchHelper from 'algoliasearch-helper';
import createWidgetsManager from './createWidgetsManager';
import { HIGHLIGHT_TAGS } from './highlight';
import { hasMultipleIndices } from './indexUtils';
import { version as ReactVersion } from 'react';
import version from './version';
function addAlgoliaAgents(searchClient) {
if (typeof searchClient.addAlgoliaAgent === 'function') {
searchClient.addAlgoliaAgent("react (".concat(ReactVersion, ")"));
searchClient.addAlgoliaAgent("react-instantsearch (".concat(version, ")"));
}
}
var isMultiIndexContext = function(widget) {
return hasMultipleIndices({
ais: widget.props.contextValue,
multiIndexContext: widget.props.indexContextValue
});
};
var isTargetedIndexEqualIndex = function(widget, indexId) {
return widget.props.indexContextValue.targetedIndex === indexId;
};
// Relying on the `indexId` is a bit brittle to detect the `Index` widget.
// Since it's a class we could rely on `instanceof` or similar. We never
// had an issue though. Works for now.
var isIndexWidget = function(widget) {
return Boolean(widget.props.indexId);
};
var isIndexWidgetEqualIndex = function(widget, indexId) {
return widget.props.indexId === indexId;
};
var sortIndexWidgetsFirst = function(firstWidget, secondWidget) {
var isFirstWidgetIndex = isIndexWidget(firstWidget);
var isSecondWidgetIndex = isIndexWidget(secondWidget);
if (isFirstWidgetIndex && !isSecondWidgetIndex) {
return -1;
}
if (!isFirstWidgetIndex && isSecondWidgetIndex) {
return 1;
}
return 0;
};
// This function is copied from the algoliasearch v4 API Client. If modified,
// consider updating it also in `serializeQueryParameters` from `@algolia/transporter`.
function serializeQueryParameters(parameters) {
var isObjectOrArray = function(value) {
return Object.prototype.toString.call(value) === '[object Object]' || Object.prototype.toString.call(value) === '[object Array]';
};
var encode = function(format) {
for(var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){
args[_key - 1] = arguments[_key];
}
var i = 0;
return format.replace(/%s/g, function() {
return encodeURIComponent(args[i++]);
});
};
return Object.keys(parameters).map(function(key) {
return encode('%s=%s', key, isObjectOrArray(parameters[key]) ? JSON.stringify(parameters[key]) : parameters[key]);
}).join('&');
}
var _obj;
/**
* Creates a new instance of the InstantSearchManager which controls the widgets and
* trigger the search when the widgets are updated.
* @param {string} indexName - the main index name
* @param {object} initialState - initial widget state
* @param {object} SearchParameters - optional additional parameters to send to the algolia API
* @param {number} stalledSearchDelay - time (in ms) after the search is stalled
* @return {InstantSearchManager} a new instance of InstantSearchManager
*/ export default function createInstantSearchManager(param) {
var indexName = param.indexName, _initialState = param.initialState, initialState = _initialState === void 0 ? {
} : _initialState, searchClient = param.searchClient, resultsState = param.resultsState, stalledSearchDelay = param.stalledSearchDelay;
var createStore = function createStore(initialState) {
var state = initialState;
var listeners = [];
return {
getState: function() {
return state;
},
setState: function(nextState) {
state = nextState;
listeners.forEach(function(listener) {
return listener();
});
},
subscribe: function(listener) {
listeners.push(listener);
return function unsubscribe() {
listeners.splice(listeners.indexOf(listener), 1);
};
}
};
};
var skipSearch = function skipSearch() {
skip = true;
};
var updateClient = function updateClient(client) {
addAlgoliaAgents(client);
helper.setClient(client);
search();
};
var clearCache = function clearCache() {
helper.clearCache();
search();
};
var getMetadata = function getMetadata(state) {
return widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getMetadata);
}).map(function(widget) {
return widget.getMetadata(state);
});
};
var getSearchParameters = function getSearchParameters() {
var sharedParameters = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
return !isMultiIndexContext(widget) && !isIndexWidget(widget);
}).reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, initialSearchParameters);
var mainParameters = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
var targetedIndexEqualMainIndex = isMultiIndexContext(widget) && isTargetedIndexEqualIndex(widget, indexName);
var subIndexEqualMainIndex = isIndexWidget(widget) && isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexEqualMainIndex || subIndexEqualMainIndex;
})// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst).reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters);
var derivedIndices = widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.getSearchParameters);
}).filter(function(widget) {
var targetedIndexNotEqualMainIndex = isMultiIndexContext(widget) && !isTargetedIndexEqualIndex(widget, indexName);
var subIndexNotEqualMainIndex = isIndexWidget(widget) && !isIndexWidgetEqualIndex(widget, indexName);
return targetedIndexNotEqualMainIndex || subIndexNotEqualMainIndex;
})// We have to sort the `Index` widgets first so the `index` parameter
// is correctly set in the `reduce` function for the following widgets
.sort(sortIndexWidgetsFirst).reduce(function(indices, widget) {
var indexId = isMultiIndexContext(widget) ? widget.props.indexContextValue.targetedIndex : widget.props.indexId;
var widgets = indices[indexId] || [];
return swcHelpers.objectSpread({
}, indices, swcHelpers.defineProperty({
}, indexId, widgets.concat(widget)));
}, {
});
var derivedParameters = Object.keys(derivedIndices).map(function(indexId) {
return {
parameters: derivedIndices[indexId].reduce(function(res, widget) {
return widget.getSearchParameters(res);
}, sharedParameters),
indexId: indexId
};
});
return {
mainParameters: mainParameters,
derivedParameters: derivedParameters
};
};
var search = function search() {
if (!skip) {
var ref = getSearchParameters(helper.state), mainParameters = ref.mainParameters, derivedParameters = ref.derivedParameters;
// We have to call `slice` because the method `detach` on the derived
// helpers mutates the value `derivedHelpers`. The `forEach` loop does
// not iterate on each value and we're not able to correctly clear the
// previous derived helpers (memory leak + useless requests).
helper.derivedHelpers.slice().forEach(function(derivedHelper) {
// Since we detach the derived helpers on **every** new search they
// won't receive intermediate results in case of a stalled search.
// Only the last result is dispatched by the derived helper because
// they are not detached yet:
//
// - a -> main helper receives results
// - ap -> main helper receives results
// - app -> main helper + derived helpers receive results
//
// The quick fix is to avoid to detach them on search but only once they
// received the results. But it means that in case of a stalled search
// all the derived helpers not detached yet register a new search inside
// the helper. The number grows fast in case of a bad network and it's
// not deterministic.
derivedHelper.detach();
});
derivedParameters.forEach(function(param) {
var indexId = param.indexId, parameters = param.parameters;
var derivedHelper = helper.derive(function() {
return parameters;
});
derivedHelper.on('result', handleSearchSuccess({
indexId: indexId
})).on('error', handleSearchError);
});
helper.setState(mainParameters);
helper.search();
}
};
var handleSearchSuccess = function handleSearchSuccess(param) {
var indexId = param.indexId;
return function(event) {
var state = store.getState();
var isDerivedHelpersEmpty = !helper.derivedHelpers.length;
var results = state.results ? state.results : {
};
// Switching from mono index to multi index and vice versa must reset the
// results to an empty object, otherwise we keep reference of stalled and
// unused results.
results = !isDerivedHelpersEmpty && results.getFacetByName ? {
} : results;
if (!isDerivedHelpersEmpty) {
results = swcHelpers.objectSpread({
}, results, swcHelpers.defineProperty({
}, indexId, event.results));
} else {
results = event.results;
}
var currentState = store.getState();
var nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
stalledSearchTimer = null;
nextIsSearchStalled = false;
}
var resultsFacetValues = currentState.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(currentState, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
results: results,
isSearchStalled: nextIsSearchStalled,
searching: false,
error: null
}));
};
};
var handleSearchError = function handleSearchError(param) {
var error = param.error;
var currentState = store.getState();
var nextIsSearchStalled = currentState.isSearchStalled;
if (!helper.hasPendingRequests()) {
clearTimeout(stalledSearchTimer);
nextIsSearchStalled = false;
}
var resultsFacetValues = currentState.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(currentState, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: nextIsSearchStalled,
error: error,
searching: false
}));
};
var handleNewSearch = function handleNewSearch() {
if (!stalledSearchTimer) {
stalledSearchTimer = setTimeout(function() {
var _ref = store.getState(), resultsFacetValues = _ref.resultsFacetValues, partialState = swcHelpers.objectWithoutProperties(_ref, ["resultsFacetValues"]);
store.setState(swcHelpers.objectSpread({
}, partialState, {
isSearchStalled: true
}));
}, stalledSearchDelay);
}
};
var hydrateSearchClient = function hydrateSearchClient(client, results) {
if (!results) {
return;
}
// Disable cache hydration on:
// - Algoliasearch API Client < v4 with cache disabled
// - Third party clients (detected by the `addAlgoliaAgent` function missing)
if ((!client.transporter || client._cacheHydrated) && (!client._useCache || typeof client.addAlgoliaAgent !== 'function')) {
return;
}
// Algoliasearch API Client >= v4
// To hydrate the client we need to populate the cache with the data from
// the server (done in `hydrateSearchClientWithMultiIndexRequest` or
// `hydrateSearchClientWithSingleIndexRequest`). But since there is no way
// for us to compute the key the same way as `algoliasearch-client` we need
// to populate it on a custom key and override the `search` method to
// search on it first.
if (client.transporter && !client._cacheHydrated) {
client._cacheHydrated = true;
var baseMethod = client.search;
client.search = function(requests) {
for(var _len = arguments.length, methodArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){
methodArgs[_key - 1] = arguments[_key];
}
var requestsWithSerializedParams = requests.map(function(request) {
return swcHelpers.objectSpread({
}, request, {
params: serializeQueryParameters(request.params)
});
});
return client.transporter.responsesCache.get({
method: 'search',
args: [
requestsWithSerializedParams
].concat(swcHelpers.toConsumableArray(methodArgs))
}, function() {
return baseMethod.apply(void 0, [
requests
].concat(swcHelpers.toConsumableArray(methodArgs)));
});
};
}
if (Array.isArray(results.results)) {
hydrateSearchClientWithMultiIndexRequest(client, results.results);
return;
}
hydrateSearchClientWithSingleIndexRequest(client, results);
};
var hydrateSearchClientWithMultiIndexRequest = function hydrateSearchClientWithMultiIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set({
method: 'search',
args: [
results.reduce(function(acc, result) {
return acc.concat(result.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}));
}, []),
]
}, {
results: results.reduce(function(acc, result) {
return acc.concat(result.rawResults);
}, [])
});
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.reduce(function(acc, result) {
return acc.concat(result.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}));
}, [])
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.reduce(function(acc, result) {
return acc.concat(result.rawResults);
}, [])
})));
};
var hydrateSearchClientWithSingleIndexRequest = function hydrateSearchClientWithSingleIndexRequest(client, results) {
// Algoliasearch API Client >= v4
// Populate the cache with the data from the server
if (client.transporter) {
client.transporter.responsesCache.set({
method: 'search',
args: [
results.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
}),
]
}, {
results: results.rawResults
});
return;
}
// Algoliasearch API Client < v4
// Prior to client v4 we didn't have a proper API to hydrate the client
// cache from the outside. The following code populates the cache with
// a single-index result. You can find more information about the
// computation of the key inside the client (see link below).
// https://github.com/algolia/algoliasearch-client-javascript/blob/c27e89ff92b2a854ae6f40dc524bffe0f0cbc169/src/AlgoliaSearchCore.js#L232-L240
var key = "/1/indexes/*/queries_body_".concat(JSON.stringify({
requests: results.rawResults.map(function(request) {
return {
indexName: request.index,
params: request.params
};
})
}));
client.cache = swcHelpers.objectSpread({
}, client.cache, swcHelpers.defineProperty({
}, key, JSON.stringify({
results: results.rawResults
})));
};
var hydrateResultsState = function hydrateResultsState(results) {
if (!results) {
return null;
}
if (Array.isArray(results.results)) {
return results.results.reduce(function(acc, result) {
return swcHelpers.objectSpread({
}, acc, swcHelpers.defineProperty({
}, result._internalIndexId, new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(result.state), result.rawResults)));
}, {
});
}
return new algoliasearchHelper.SearchResults(new algoliasearchHelper.SearchParameters(results.state), results.rawResults);
};
var onWidgetsUpdate = // Called whenever a widget has been rendered with new props.
function onWidgetsUpdate() {
var metadata = getMetadata(store.getState().widgets);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
metadata: metadata,
searching: true
}));
// Since the `getSearchParameters` method of widgets also depends on props,
// the result search parameters might have changed.
search();
};
var transitionState = function transitionState(nextSearchState) {
var searchState = store.getState().widgets;
return widgetsManager.getWidgets().filter(function(widget) {
return Boolean(widget.transitionState);
}).reduce(function(res, widget) {
return widget.transitionState(searchState, res);
}, nextSearchState);
};
var onExternalStateUpdate = function onExternalStateUpdate(nextSearchState) {
var metadata = getMetadata(nextSearchState);
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
widgets: nextSearchState,
metadata: metadata,
searching: true
}));
search();
};
var onSearchForFacetValues = function onSearchForFacetValues(param) {
var facetName = param.facetName, query = param.query, _maxFacetHits = param.maxFacetHits, maxFacetHits = _maxFacetHits === void 0 ? 10 : _maxFacetHits;
// The values 1, 100 are the min / max values that the engine accepts.
// see: https://www.algolia.com/doc/api-reference/api-parameters/maxFacetHits
var maxFacetHitsWithinRange = Math.max(1, Math.min(maxFacetHits, 100));
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: true
}));
helper.searchForFacetValues(facetName, query, maxFacetHitsWithinRange).then(function(content) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
error: null,
searchingForFacetValues: false,
resultsFacetValues: swcHelpers.objectSpread({
}, store.getState().resultsFacetValues, (_obj = {
}, swcHelpers.defineProperty(_obj, facetName, content.facetHits), swcHelpers.defineProperty(_obj, "query", query), _obj))
}));
}, function(error) {
store.setState(swcHelpers.objectSpread({
}, store.getState(), {
searchingForFacetValues: false,
error: error
}));
}).catch(function(error) {
// Since setState is synchronous, any error that occurs in the render of a
// component will be swallowed by this promise.
// This is a trick to make the error show up correctly in the console.
// See http://stackoverflow.com/a/30741722/969302
setTimeout(function() {
throw error;
});
});
};
var updateIndex = function updateIndex(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
// No need to trigger a new search here as the widgets will also update and trigger it if needed.
};
var getWidgetsIds = function getWidgetsIds() {
return store.getState().metadata.reduce(function(res, meta) {
return typeof meta.id !== 'undefined' ? res.concat(meta.id) : res;
}, []);
};
var helper = algoliasearchHelper(searchClient, indexName, swcHelpers.objectSpread({
}, HIGHLIGHT_TAGS));
addAlgoliaAgents(searchClient);
helper.on('search', handleNewSearch).on('result', handleSearchSuccess({
indexId: indexName
})).on('error', handleSearchError);
var skip = false;
var stalledSearchTimer = null;
var initialSearchParameters = helper.state;
var widgetsManager = createWidgetsManager(onWidgetsUpdate);
hydrateSearchClient(searchClient, resultsState);
var store = createStore({
widgets: initialState,
metadata: hydrateMetadata(resultsState),
results: hydrateResultsState(resultsState),
error: null,
searching: false,
isSearchStalled: true,
searchingForFacetValues: false
});
return {
store: store,
widgetsManager: widgetsManager,
getWidgetsIds: getWidgetsIds,
getSearchParameters: getSearchParameters,
onSearchForFacetValues: onSearchForFacetValues,
onExternalStateUpdate: onExternalStateUpdate,
transitionState: transitionState,
updateClient: updateClient,
updateIndex: updateIndex,
clearCache: clearCache,
skipSearch: skipSearch
};
};
function hydrateMetadata(resultsState) {
if (!resultsState) {
return [];
}
// add a value noop, which gets replaced once the widgets are mounted
return resultsState.metadata.map(function(datum) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, datum, {
items: datum.items && datum.items.map(function(item) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, item, {
items: item.items && item.items.map(function(nestedItem) {
return swcHelpers.objectSpread({
value: function() {
return {
};
}
}, nestedItem);
})
});
})
});
});
}