mirror of
https://gitlab.silvrtree.co.uk/martind2000/frontexpress.git
synced 2025-01-25 18:16:16 +00:00
323 lines
7.6 KiB
JavaScript
Executable File
323 lines
7.6 KiB
JavaScript
Executable File
/**
|
|
* Module dependencies.
|
|
* @private
|
|
*/
|
|
|
|
import HTTP_METHODS from './methods';
|
|
import {toParameters} from './application';
|
|
import Middleware from './middleware';
|
|
|
|
|
|
/**
|
|
* Route object.
|
|
* @private
|
|
*/
|
|
|
|
export class Route {
|
|
|
|
|
|
/**
|
|
* Initialize the route.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
constructor(router, uriPart, method, middleware) {
|
|
this.router = router;
|
|
this.uriPart = uriPart;
|
|
this.method = method;
|
|
this.middleware = middleware;
|
|
this.visited = false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Return route's uri.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
get uri() {
|
|
if (!this.uriPart && !this.method) {
|
|
return undefined;
|
|
}
|
|
|
|
if (this.uriPart instanceof RegExp) {
|
|
return this.uriPart;
|
|
}
|
|
|
|
if (this.router.baseUri instanceof RegExp) {
|
|
return this.router.baseUri;
|
|
}
|
|
|
|
if (this.router.baseUri) {
|
|
const baseUri = this.router.baseUri.trim();
|
|
if (this.uriPart) {
|
|
return ( baseUri + this.uriPart.trim()).replace(/\/{2,}/, '/');
|
|
}
|
|
return baseUri;
|
|
}
|
|
|
|
return this.uriPart;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Router object.
|
|
* @public
|
|
*/
|
|
|
|
const error_middleware_message = 'method takes at least a middleware';
|
|
export default class Router {
|
|
|
|
|
|
/**
|
|
* Initialize the router.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
constructor(uri) {
|
|
this._baseUri = uri;
|
|
this._routes = [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Do some checks and set _baseUri.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
set baseUri(uri) {
|
|
if (!uri) {
|
|
return;
|
|
}
|
|
|
|
if (!this._baseUri) {
|
|
this._baseUri = uri;
|
|
return;
|
|
}
|
|
|
|
if (typeof this._baseUri !== typeof uri) {
|
|
throw new TypeError('router cannot mix regexp and uri');
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Return router's _baseUri.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
get baseUri() {
|
|
return this._baseUri;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add a route to the router.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
_add(route) {
|
|
this._routes.push(route);
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Gather routes from routers filtered by _uri_ and HTTP _method_.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
routes(application, request) {
|
|
request.params = request.params || {};
|
|
const isRouteMatch = application.get('route matcher');
|
|
return this._routes.filter((route) => {
|
|
return isRouteMatch(request, route);
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Gather visited routes from routers.
|
|
*
|
|
* @private
|
|
*/
|
|
|
|
visited() {
|
|
return this._routes.filter(route => route.visited);
|
|
}
|
|
|
|
|
|
/**
|
|
* Use the given middleware function or object on this router.
|
|
*
|
|
* // middleware function
|
|
* router.use((req, res, next) => {console.log('Hello')});
|
|
*
|
|
* // middleware object
|
|
* router.use(new Middleware());
|
|
*
|
|
* @param {Middleware|Function} middleware object or function
|
|
* @return {Router} for chaining
|
|
*
|
|
* @public
|
|
*/
|
|
|
|
use(middleware) {
|
|
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
|
throw new TypeError(error_middleware_message);
|
|
}
|
|
|
|
this._add(new Route(this, undefined, undefined, middleware));
|
|
|
|
return this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Use the given middleware function or object on this router for
|
|
* all HTTP methods.
|
|
*
|
|
* // middleware function
|
|
* router.all((req, res, next) => {console.log('Hello')});
|
|
*
|
|
* // middleware object
|
|
* router.all(new Middleware());
|
|
*
|
|
* @param {Middleware|Function} middleware object or function
|
|
* @return {Router} for chaining
|
|
*
|
|
* @public
|
|
*/
|
|
|
|
all(...args) {
|
|
const {middleware} = toParameters(args);
|
|
if (!middleware) {
|
|
throw new TypeError(error_middleware_message);
|
|
}
|
|
|
|
HTTP_METHODS.forEach((method) => {
|
|
this[method.toLowerCase()](...args);
|
|
});
|
|
return this;
|
|
}
|
|
}
|
|
|
|
HTTP_METHODS.forEach((method) => {
|
|
|
|
|
|
/**
|
|
* Use the given middleware function or object, with optional _uri_ on
|
|
* HTTP methods: get, post, put, delete...
|
|
* Default _uri_ is "/".
|
|
*
|
|
* // middleware function will be applied on path "/"
|
|
* router.get((req, res, next) => {console.log('Hello')});
|
|
*
|
|
* // middleware object will be applied on path "/" and
|
|
* router.get(new Middleware());
|
|
*
|
|
* // middleware function will be applied on path "/user"
|
|
* router.post('/user', (req, res, next) => {console.log('Hello')});
|
|
*
|
|
* // middleware object will be applied on path "/user" and
|
|
* router.post('/user', new Middleware());
|
|
*
|
|
* @param {String} uri
|
|
* @param {Middleware|Function} middleware object or function
|
|
* @return {Router} for chaining
|
|
* @public
|
|
*/
|
|
|
|
const methodName = method.toLowerCase();
|
|
Router.prototype[methodName] = function(...args) {
|
|
const {baseUri, middleware} = toParameters(args);
|
|
if (!middleware) {
|
|
throw new TypeError(error_middleware_message);
|
|
}
|
|
|
|
if (baseUri && this._baseUri && this._baseUri instanceof RegExp) {
|
|
throw new TypeError('router cannot mix uri/regexp');
|
|
}
|
|
|
|
this._add(new Route(this, baseUri, method, middleware));
|
|
|
|
return this;
|
|
};
|
|
});
|
|
|
|
export function routeMatcher(request, route) {
|
|
// check if http method are equals
|
|
if (route.method && route.method !== request.method) {
|
|
return false;
|
|
}
|
|
|
|
|
|
// route and uri not defined always match
|
|
if (!route.uri || !request.uri) {
|
|
return true;
|
|
}
|
|
|
|
//remove query string and anchor from uri to test
|
|
const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
|
|
const baseUriToCheck = match[1] || match[2] || match[3];
|
|
|
|
// if route is a regexp path
|
|
if (route.uri instanceof RegExp) {
|
|
return baseUriToCheck.match(route.uri) !== null;
|
|
}
|
|
|
|
// if route is parameterized path
|
|
if (route.uri.indexOf(':') !== -1) {
|
|
|
|
const decodeParmeterValue = (v) => {
|
|
return !isNaN(parseFloat(v)) && isFinite(v) ? (Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v)) : v;
|
|
};
|
|
|
|
// figure out key names
|
|
const keys = [];
|
|
const keysRE = /:([^\/\?]+)\??/g;
|
|
let keysMatch = keysRE.exec(route.uri);
|
|
while (keysMatch != null) {
|
|
keys.push(keysMatch[1]);
|
|
keysMatch = keysRE.exec(route.uri);
|
|
}
|
|
|
|
// change parameterized path to regexp
|
|
const regExpUri = route.uri
|
|
// :parameter?
|
|
.replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
|
|
// :parameter
|
|
.replace(/:[^\/]+/g, '([^\/]+)')
|
|
// escape all /
|
|
.replace('/', '\\/');
|
|
|
|
// checks if uri match
|
|
const routeMatch = baseUriToCheck.match(new RegExp(`^${regExpUri}$`));
|
|
if (!routeMatch) {
|
|
return false;
|
|
}
|
|
|
|
// update params in request with keys
|
|
request.params = Object.assign(request.params, keys.reduce((acc, key, index) => {
|
|
let value = routeMatch[index + 1];
|
|
if (value) {
|
|
value = value.indexOf(',') !== -1 ? value.split(',').map(v => decodeParmeterValue(v)) : value = decodeParmeterValue(value);
|
|
}
|
|
acc[key] = value;
|
|
return acc;
|
|
}, {}));
|
|
return true;
|
|
}
|
|
|
|
// if route is a simple path
|
|
return route.uri === baseUriToCheck;
|
|
}
|