107 lines
4.0 KiB
JavaScript
107 lines
4.0 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const http_1 = require("../utils/http");
|
|
const crypto_md5_1 = __importDefault(require("crypto-md5"));
|
|
const url_1 = require("../utils/url");
|
|
const lodash_1 = require("lodash");
|
|
const PARAM_RE = /^{(\S+)}$/;
|
|
function buildRouteParamsMap(routeMatch, paramNames) {
|
|
return paramNames.reduce((params, paramName, i) => {
|
|
params[paramName] = routeMatch[i + 1];
|
|
return params;
|
|
}, {});
|
|
}
|
|
class Router {
|
|
constructor(options = {}) {
|
|
this.routes = new Map();
|
|
this.routesWithParams = [];
|
|
this.options = options;
|
|
}
|
|
_registerRoute(route, method, handler) {
|
|
const tokens = route.split('/');
|
|
const isRouteWithParams = tokens.some(token => PARAM_RE.test(token));
|
|
if (isRouteWithParams && typeof handler === 'function')
|
|
this._registerRouteWithParams(tokens, method, handler);
|
|
else {
|
|
const routeName = `${method} ${route}`;
|
|
if (typeof handler !== 'function') {
|
|
this._processStaticContent(handler);
|
|
handler.etag = (0, crypto_md5_1.default)(handler.content);
|
|
this.routes.set(routeName, { handler, isStatic: true });
|
|
}
|
|
else
|
|
this.routes.set(routeName, { handler, isStatic: false });
|
|
}
|
|
}
|
|
_prepareParamInfo(tokens, method) {
|
|
const paramNames = [];
|
|
const reParts = tokens.map(token => {
|
|
const paramMatch = token.match(PARAM_RE);
|
|
if (paramMatch) {
|
|
paramNames.push(paramMatch[1]);
|
|
return '(\\S+?)';
|
|
}
|
|
return token;
|
|
});
|
|
return {
|
|
paramNames,
|
|
re: new RegExp(`^${method} ${reParts.join('/')}$`),
|
|
};
|
|
}
|
|
_registerRouteWithParams(tokens, method, handler) {
|
|
const { paramNames, re } = this._prepareParamInfo(tokens, method);
|
|
this.routesWithParams.push({ handler, paramNames, re });
|
|
}
|
|
_unregisterRouteWithParams(tokens, method) {
|
|
const { paramNames, re } = this._prepareParamInfo(tokens, method);
|
|
const routeIndex = this.routesWithParams.findIndex(routeWithParam => {
|
|
return (0, lodash_1.isEqual)(routeWithParam.re, re) && (0, lodash_1.isEqual)(routeWithParam.paramNames, paramNames);
|
|
});
|
|
if (routeIndex !== -1)
|
|
this.routesWithParams.splice(routeIndex, 1);
|
|
}
|
|
_route(req, res, serverInfo) {
|
|
const routerQuery = `${req.method} ${(0, url_1.getPathname)(req.url || '')}`;
|
|
const route = this.routes.get(routerQuery);
|
|
if (route) {
|
|
if (route.isStatic)
|
|
(0, http_1.respondStatic)(req, res, route.handler, this.options.staticContentCaching);
|
|
else
|
|
route.handler(req, res, serverInfo);
|
|
return true;
|
|
}
|
|
for (const routeWithParams of this.routesWithParams) {
|
|
const routeMatch = routerQuery.match(routeWithParams.re);
|
|
if (routeMatch) {
|
|
const params = buildRouteParamsMap(routeMatch, routeWithParams.paramNames);
|
|
routeWithParams.handler(req, res, serverInfo, params);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// API
|
|
GET(route, handler) {
|
|
this._registerRoute(route, 'GET', handler);
|
|
}
|
|
POST(route, handler) {
|
|
this._registerRoute(route, 'POST', handler);
|
|
}
|
|
OPTIONS(route, handler) {
|
|
this._registerRoute(route, 'OPTIONS', handler);
|
|
}
|
|
unRegisterRoute(route, method) {
|
|
const tokens = route.split('/');
|
|
const isRouteWithParams = tokens.some(token => PARAM_RE.test(token));
|
|
if (isRouteWithParams)
|
|
this._unregisterRouteWithParams(tokens, method);
|
|
const routeName = `${method} ${route}`;
|
|
this.routes.delete(routeName);
|
|
}
|
|
}
|
|
exports.default = Router;module.exports = exports.default;
|
|
|