universal-router
Version:
Isomorphic router for JavaScript web applications
161 lines (160 loc) • 6.13 kB
JavaScript
"use strict";
/**
* Universal Router (https://www.kriasoft.com/universal-router/)
*
* Copyright (c) 2015-present Kriasoft.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(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);
};
Object.defineProperty(exports, "__esModule", { value: true });
var path_to_regexp_js_1 = require("./path-to-regexp.js");
function decode(val) {
try {
return decodeURIComponent(val);
}
catch (_a) {
return val;
}
}
function matchRoute(route, baseUrl, options, pathname, parentParams) {
var matchResult;
var childMatches;
var childIndex = 0;
return {
next: function (routeToSkip) {
if (route === routeToSkip) {
return { done: true, value: false };
}
if (!matchResult) {
var rt = route;
var end = !rt.children;
if (!rt.match) {
rt.match = (0, path_to_regexp_js_1.match)(rt.path || '', __assign({ end: end }, options));
}
matchResult = rt.match(pathname);
if (matchResult) {
var path = matchResult.path;
matchResult.path =
!end && path.charAt(path.length - 1) === '/' ? path.substr(1) : path;
matchResult.params = __assign(__assign({}, parentParams), matchResult.params);
return {
done: false,
value: {
route: route,
baseUrl: baseUrl,
path: matchResult.path,
params: matchResult.params,
},
};
}
}
if (matchResult && route.children) {
while (childIndex < route.children.length) {
if (!childMatches) {
var childRoute = route.children[childIndex];
childRoute.parent = route;
childMatches = matchRoute(childRoute, baseUrl + matchResult.path, options, pathname.substr(matchResult.path.length), matchResult.params);
}
var childMatch = childMatches.next(routeToSkip);
if (!childMatch.done) {
return { done: false, value: childMatch.value };
}
childMatches = null;
childIndex++;
}
}
return { done: true, value: false };
},
};
}
function resolveRoute(context, params) {
if (typeof context.route.action === 'function') {
return context.route.action(context, params);
}
return undefined;
}
function isChildRoute(parentRoute, childRoute) {
var route = childRoute;
while (route) {
route = route.parent;
if (route === parentRoute) {
return true;
}
}
return false;
}
var UniversalRouterSync = /** @class */ (function () {
function UniversalRouterSync(routes, options) {
if (!routes || typeof routes !== 'object') {
throw new TypeError('Invalid routes');
}
this.options = __assign({ decode: decode }, options);
this.baseUrl = this.options.baseUrl || '';
this.root = Array.isArray(routes)
? { path: '', children: routes, parent: null }
: routes;
this.root.parent = null;
}
/**
* Traverses the list of routes in the order they are defined until it finds
* the first route that matches provided URL path string and whose action function
* returns anything other than `null` or `undefined`.
*/
UniversalRouterSync.prototype.resolve = function (pathnameOrContext) {
var context = __assign(__assign({ router: this }, this.options.context), (typeof pathnameOrContext === 'string'
? { pathname: pathnameOrContext }
: pathnameOrContext));
var matchResult = matchRoute(this.root, this.baseUrl, this.options, context.pathname.substr(this.baseUrl.length));
var resolve = this.options.resolveRoute || resolveRoute;
var matches;
var nextMatches;
var currentContext = context;
function next(resume, parent, prevResult) {
if (parent === void 0) { parent = !matches.done && matches.value.route; }
var routeToSkip = prevResult === null && !matches.done && matches.value.route;
matches = nextMatches || matchResult.next(routeToSkip);
nextMatches = null;
if (!resume) {
if (matches.done || !isChildRoute(parent, matches.value.route)) {
nextMatches = matches;
return null;
}
}
if (matches.done) {
var error = new Error('Route not found');
error.status = 404;
throw error;
}
currentContext = __assign(__assign({}, context), matches.value);
var result = resolve(currentContext, matches.value.params);
if (result !== null && result !== undefined) {
return result;
}
return next(resume, parent, result);
}
context['next'] = next;
try {
return next(true, this.root);
}
catch (error) {
if (this.options.errorHandler) {
return this.options.errorHandler(error, currentContext);
}
throw error;
}
};
return UniversalRouterSync;
}());
exports.default = UniversalRouterSync;