@nimel/directorr-router
Version:
Router store for directorr
433 lines (418 loc) • 18.1 kB
JavaScript
/* (c) Nikita Melnikov - MIT Licensed 2020 - now */
import { pathToRegexp, compile } from 'path-to-regexp';
import qs from 'query-string';
import { EMPTY_OBJECT, EMPTY_STRING, createActionAndEffect, action, effect, composePropertyDecorators, whenState, whenPayload, whenDestroy, EMPTY_FUNC } from '@nimel/directorr';
import { createBrowserHistory } from 'history';
var ANY_PATH = '(.*)';
var ROOT_URL = '/';
var Cache = /** @class */ (function () {
function Cache(cacheSize) {
if (cacheSize === void 0) { cacheSize = 10000; }
this.store = new Map();
this.cacheSize = cacheSize;
}
Cache.prototype.set = function (key, value) {
if (this.store.size > this.cacheSize) {
this.store.delete(this.store.keys().next().value);
}
return this.store.set(key, value);
};
Cache.prototype.get = function (key) {
return this.store.get(key);
};
Cache.prototype.has = function (key) {
return this.store.has(key);
};
Object.defineProperty(Cache.prototype, "size", {
get: function () {
return this.store.size;
},
enumerable: false,
configurable: true
});
return Cache;
}());
var CACHE_PATTERNS = new Map();
var CACHE_PATHS = new Cache();
function compileRGXP(pattern, end, strict) {
var cacheKey = EMPTY_STRING + end + strict;
if (!CACHE_PATTERNS.has(cacheKey))
CACHE_PATTERNS.set(cacheKey, new Cache());
var patternCache = CACHE_PATTERNS.get(cacheKey);
if (patternCache.has(pattern))
return patternCache.get(pattern);
var keys = [];
var reg = pathToRegexp(pattern, keys, { end: end, strict: strict });
var compiledRGXP = { reg: reg, keys: keys };
patternCache.set(pattern, compiledRGXP);
return compiledRGXP;
}
function matchPath(pathname, urlPattern, exact, strict) {
if (exact === void 0) { exact = true; }
if (strict === void 0) { strict = false; }
var _a = compileRGXP(urlPattern, exact, strict), reg = _a.reg, keys = _a.keys;
var patterns = reg.exec(pathname);
if (patterns) {
return {
patterns: patterns,
keys: keys,
};
}
}
function calcParams(patterns, keys) {
var params = {};
for (var i = 0, l = keys.length, name_1, value = void 0; i < l; ++i) {
name_1 = keys[i].name;
value = patterns[i + 1];
if (name_1) {
params[name_1] = value;
}
}
return params;
}
function calcPath(path, queryObject) {
if (queryObject) {
var query = qs.stringify(queryObject);
return query ? "".concat(path, "?").concat(query) : path;
}
return path;
}
function compilePath(path) {
if (CACHE_PATHS.has(path))
return CACHE_PATHS.get(path);
var genPath = compile(path);
CACHE_PATHS.set(path, genPath);
return genPath;
}
function generatePath(path, params) {
if (path === void 0) { path = ROOT_URL; }
if (params === void 0) { params = EMPTY_OBJECT; }
return path === ROOT_URL ? path : compilePath(path)(params);
}
function reloadWindow() {
window.location.reload();
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __decorate(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
}
function __metadata(metadataKey, metadataValue) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
}
function __values(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
}
function __read(o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
}
function __spreadArray(to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
}
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
function returnTrue() {
return true;
}
function addStoreToPayload(payload, store) {
return __assign(__assign({}, payload), { store: store });
}
function pickSameStore(payload, store) {
return store === payload.store;
}
var actionRouterPush = (_a = __read(createActionAndEffect('ROUTER.PUSH'), 2), _a[0]), effectRouterPush = _a[1];
var actionRouterReplace = (_b = __read(createActionAndEffect('ROUTER.REPLACE'), 2), _b[0]), effectRouterReplace = _b[1];
var actionRouterBack = (_c = __read(createActionAndEffect('ROUTER.BACK'), 2), _c[0]), effectRouterBack = _c[1];
var actionRouterForward = (_d = __read(createActionAndEffect('ROUTER.FORWARD'), 2), _d[0]), effectRouterForward = _d[1];
var actionRouterGoTo = (_e = __read(createActionAndEffect('ROUTER.GOTO'), 2), _e[0]), effectRouterGoTo = _e[1];
var actionRouterReload = (_f = __read(createActionAndEffect('ROUTER.RELOAD'), 2), _f[0]), effectRouterReload = _f[1];
var actionRouterBlock = (_g = __read(createActionAndEffect('ROUTER.BLOCK'), 2), _g[0]), effectRouterBlock = _g[1];
var actionRouterCancelBlock = (_h = __read(createActionAndEffect('ROUTER.CANCEL_BLOCK'), 2), _h[0]), effectRouterCancelBlock = _h[1];
var actionRouterState = (_j = __read(createActionAndEffect('ROUTER.STATE'), 2), _j[0]), effectRouterState = _j[1];
var actionRouterIsPattern = action('ROUTER.IS_PATTERN', addStoreToPayload);
var effectRouterIsPattern = effect(actionRouterIsPattern.type);
var actionRouterIsPatternSuccess = action('ROUTER.IS_PATTERN_SUCCESS');
var effectRouterIsPatternSuccess = composePropertyDecorators([effect(actionRouterIsPatternSuccess.type), whenState(pickSameStore)]);
var actionHistoryPop = (_k = __read(createActionAndEffect('HISTORY.POP'), 2), _k[0]), effectHistoryPop = _k[1];
var actionHistoryPush = (_l = __read(createActionAndEffect('HISTORY.PUSH'), 2), _l[0]), effectHistoryPush = _l[1];
var actionHistoryReplace = (_m = __read(createActionAndEffect('HISTORY.REPLACE'), 2), _m[0]), effectHistoryReplace = _m[1];
var DEFAULT_OPTIONS = {
exact: true,
strict: true,
};
function historyChange(urlPattern, _a) {
var _b = _a === void 0 ? DEFAULT_OPTIONS : _a, exact = _b.exact, strict = _b.strict;
return composePropertyDecorators([
effectHistoryPop,
effectHistoryPush,
effectHistoryReplace,
whenPayload(returnTrue, function (payload) {
var match = matchPath(payload.path, urlPattern, exact, strict);
return __assign(__assign({}, payload), { match: !!match, queryObject: __assign(__assign({}, payload.queryObject), ((match === null || match === void 0 ? void 0 : match.keys.length) && { params: calcParams(match.patterns, match.keys) })) });
}),
]);
}
var ACTION = {
POP: 'POP',
PUSH: 'PUSH',
REPLACE: 'REPLACE',
};
var HistoryStore = /** @class */ (function () {
function HistoryStore(history) {
if (history === void 0) { history = createBrowserHistory(); }
var _this = this;
this.unsubHistory = EMPTY_FUNC;
this.handlersStack = [];
this.blockState = [EMPTY_FUNC, EMPTY_FUNC];
this.subscribe = function (handler) {
_this.handlersStack = __spreadArray(__spreadArray([], __read(_this.handlersStack), false), [handler], false);
};
this.unsubscribe = function (handler) {
_this.handlersStack = _this.handlersStack.filter(function (h) { return h !== handler; });
};
this.toSetState = function (_a) {
var path = _a.path, queryObject = _a.queryObject, state = _a.state;
_this.path = path;
_this.queryObject = queryObject;
_this.state = state;
};
this.toDestroy = function () {
_this.unsubHistory();
};
this.toHistoryPush = function (payload) { return payload; };
this.toHistoryPop = function (payload) { return payload; };
this.toHistoryReplace = function (payload) { return payload; };
this.toPop = function (_a) {
var path = _a.path, queryObject = _a.queryObject, state = _a.state, action = _a.action;
_this.path = path;
_this.queryObject = queryObject;
_this.state = state;
_this.action = action;
};
this.push = function (path, queryObject, state) {
if (queryObject === void 0) { queryObject = EMPTY_OBJECT; }
return ({
path: path,
queryObject: queryObject,
state: state,
});
};
this.replace = function (path, queryObject, state) {
if (queryObject === void 0) { queryObject = EMPTY_OBJECT; }
return ({
path: path,
queryObject: queryObject,
state: state,
});
};
this.back = EMPTY_FUNC;
this.reload = EMPTY_FUNC;
this.toReload = function () { return reloadWindow(); };
this.toPush = function (_a) {
var path = _a.path, queryObject = _a.queryObject, state = _a.state;
return _this.history.push(calcPath(path, queryObject), state);
};
this.toReplace = function (_a) {
var path = _a.path, queryObject = _a.queryObject, state = _a.state;
return _this.history.replace(calcPath(path, queryObject), state);
};
this.toBack = function () { return _this.history.back(); };
this.goTo = function (index) { return ({ index: index }); };
this.toGoTo = function (_a) {
var index = _a.index;
return _this.history.go(index);
};
this.forward = EMPTY_FUNC;
this.toForward = function () { return _this.history.forward(); };
this.block = function (blocker) { return ({ blocker: blocker }); };
this.toBlock = function (_a) {
var blocker = _a.blocker;
_this.blockState = [blocker, _this.history.block(blocker)];
};
this.cancelBlock = function (blocker) { return ({ blocker: blocker }); };
this.toCancelBlock = function (_a) {
var blocker = _a.blocker;
var _b = __read(_this.blockState, 2), lastBlocker = _b[0], handler = _b[1];
if (blocker === lastBlocker) {
handler();
}
};
this.dispatchAction = function (task) {
var e_1, _a;
var location = task.location, action = task.action;
var routerPayload = __assign(__assign({ path: location.pathname, queryObject: qs.parse(location.search) }, (location.state && { state: location.state })), { action: action });
switch (action) {
case ACTION.POP:
_this.toHistoryPop(routerPayload);
break;
case ACTION.REPLACE:
_this.toHistoryReplace(routerPayload);
break;
default:
_this.toHistoryPush(routerPayload);
}
try {
for (var _b = __values(_this.handlersStack), _c = _b.next(); !_c.done; _c = _b.next()) {
var handler = _c.value;
handler(task);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
};
this.history = history;
var _a = this.history, _b = _a.location, pathname = _b.pathname, search = _b.search, state = _b.state, action = _a.action;
this.unsubHistory = this.history.listen(this.dispatchAction);
this.path = pathname;
this.queryObject = qs.parse(search);
this.state = state;
this.action = action;
}
HistoryStore.prototype.toJSON = function () { };
__decorate([
effectRouterState,
__metadata("design:type", Object)
], HistoryStore.prototype, "toSetState", void 0);
__decorate([
whenDestroy,
__metadata("design:type", Object)
], HistoryStore.prototype, "toDestroy", void 0);
__decorate([
actionHistoryPush,
__metadata("design:type", Object)
], HistoryStore.prototype, "toHistoryPush", void 0);
__decorate([
actionHistoryPop,
__metadata("design:type", Object)
], HistoryStore.prototype, "toHistoryPop", void 0);
__decorate([
actionHistoryReplace,
__metadata("design:type", Object)
], HistoryStore.prototype, "toHistoryReplace", void 0);
__decorate([
effectHistoryPop,
effectHistoryPush,
effectHistoryReplace,
__metadata("design:type", Object)
], HistoryStore.prototype, "toPop", void 0);
__decorate([
actionRouterPush,
__metadata("design:type", Object)
], HistoryStore.prototype, "push", void 0);
__decorate([
actionRouterReplace,
__metadata("design:type", Object)
], HistoryStore.prototype, "replace", void 0);
__decorate([
actionRouterBack,
__metadata("design:type", Object)
], HistoryStore.prototype, "back", void 0);
__decorate([
actionRouterReload,
__metadata("design:type", Object)
], HistoryStore.prototype, "reload", void 0);
__decorate([
effectRouterReload,
__metadata("design:type", Object)
], HistoryStore.prototype, "toReload", void 0);
__decorate([
effectRouterPush,
__metadata("design:type", Object)
], HistoryStore.prototype, "toPush", void 0);
__decorate([
effectRouterReplace,
__metadata("design:type", Object)
], HistoryStore.prototype, "toReplace", void 0);
__decorate([
effectRouterBack,
__metadata("design:type", Object)
], HistoryStore.prototype, "toBack", void 0);
__decorate([
actionRouterGoTo,
__metadata("design:type", Object)
], HistoryStore.prototype, "goTo", void 0);
__decorate([
effectRouterGoTo,
__metadata("design:type", Object)
], HistoryStore.prototype, "toGoTo", void 0);
__decorate([
actionRouterForward,
__metadata("design:type", Object)
], HistoryStore.prototype, "forward", void 0);
__decorate([
effectRouterForward,
__metadata("design:type", Object)
], HistoryStore.prototype, "toForward", void 0);
__decorate([
actionRouterBlock,
__metadata("design:type", Object)
], HistoryStore.prototype, "block", void 0);
__decorate([
effectRouterBlock,
__metadata("design:type", Object)
], HistoryStore.prototype, "toBlock", void 0);
__decorate([
actionRouterCancelBlock,
__metadata("design:type", Object)
], HistoryStore.prototype, "cancelBlock", void 0);
__decorate([
effectRouterCancelBlock,
__metadata("design:type", Object)
], HistoryStore.prototype, "toCancelBlock", void 0);
return HistoryStore;
}());
export { ACTION, ANY_PATH, HistoryStore, actionHistoryPop, actionHistoryPush, actionHistoryReplace, actionRouterBack, actionRouterBlock, actionRouterCancelBlock, actionRouterForward, actionRouterGoTo, actionRouterIsPattern, actionRouterIsPatternSuccess, actionRouterPush, actionRouterReload, actionRouterReplace, actionRouterState, addStoreToPayload, calcPath, effectHistoryPop, effectHistoryPush, effectHistoryReplace, effectRouterBack, effectRouterBlock, effectRouterCancelBlock, effectRouterForward, effectRouterGoTo, effectRouterIsPattern, effectRouterIsPatternSuccess, effectRouterPush, effectRouterReload, effectRouterReplace, effectRouterState, generatePath, historyChange, matchPath, pickSameStore };