@whatwg-node/router
Version:
Fetch API compliant HTTP Router
112 lines (109 loc) • 4.59 kB
JavaScript
import { createServerAdapter } from '@whatwg-node/server';
export { withCORS, withErrorHandling } from '@whatwg-node/server';
import { Request, URLPattern } from '@whatwg-node/fetch';
export { Response, URLPattern } from '@whatwg-node/fetch';
const HTTP_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
function createRouter(options) {
var _a;
const routesByMethod = new Map();
function addHandlersToMethod(method, path, ...handlers) {
let methodPatternMaps = routesByMethod.get(method);
if (!methodPatternMaps) {
methodPatternMaps = new Map();
routesByMethod.set(method, methodPatternMaps);
}
const basePath = (options === null || options === void 0 ? void 0 : options.base) || '/';
let fullPath = '';
if (basePath === '/') {
fullPath = path;
}
else if (path === '/') {
fullPath = basePath;
}
else {
fullPath = `${basePath}${path}`;
}
const pattern = new URLPattern({ pathname: fullPath });
methodPatternMaps.set(pattern, handlers);
}
async function handleRequest(request, context) {
const method = request.method;
let _parsedUrl;
function getParsedUrl() {
if (!_parsedUrl) {
_parsedUrl = new URL(request.url);
}
return _parsedUrl;
}
const methodPatternMaps = routesByMethod.get(method);
if (methodPatternMaps) {
const queryProxy = new Proxy({}, {
get(_, prop) {
const parsedUrl = getParsedUrl();
const allQueries = parsedUrl.searchParams.getAll(prop.toString());
return allQueries.length === 1 ? allQueries[0] : allQueries;
},
has(_, prop) {
const parsedUrl = getParsedUrl();
return parsedUrl.searchParams.has(prop.toString());
},
});
for (const [pattern, handlers] of methodPatternMaps) {
const match = pattern.exec(request.url);
if (match) {
const routerRequest = new Proxy(request, {
get(target, prop) {
if (prop === 'parsedUrl') {
return getParsedUrl();
}
if (prop === 'params') {
return match.pathname.groups;
}
if (prop === 'query') {
return queryProxy;
}
const targetProp = target[prop];
if (typeof targetProp === 'function') {
return targetProp.bind(target);
}
return targetProp;
},
has(target, prop) {
return prop in target || prop === 'parsedUrl' || prop === 'params' || prop === 'query';
},
});
for (const handler of handlers) {
const result = await handler(routerRequest, context);
if (result) {
return result;
}
}
}
}
}
}
let routerBaseObject = new Proxy({}, {
get(_, prop) {
if (prop === 'handle') {
return handleRequest;
}
const method = prop.toString().toLowerCase();
return function routeMethodKeyFn(path, ...handlers) {
if (method === 'all') {
for (const httpMethod of HTTP_METHODS) {
addHandlersToMethod(httpMethod, path, ...handlers);
}
}
else {
addHandlersToMethod(method.toUpperCase(), path, ...handlers);
}
return this;
};
},
});
(_a = options === null || options === void 0 ? void 0 : options.plugins) === null || _a === void 0 ? void 0 : _a.forEach(plugin => {
routerBaseObject = plugin(routerBaseObject);
});
return createServerAdapter(routerBaseObject, (options === null || options === void 0 ? void 0 : options.RequestCtor) || Request);
}
export { createRouter };