@whatwg-node/router
Version:
Fetch API compliant HTTP Router
159 lines (154 loc) • 5.51 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
const DefaultFetchAPI = require('@whatwg-node/fetch');
const server = require('@whatwg-node/server');
const HTTP_METHODS = [
'GET',
'HEAD',
'POST',
'PUT',
'DELETE',
'CONNECT',
'OPTIONS',
'TRACE',
'PATCH',
];
function createRouterBase({ fetchAPI: givenFetchAPI, base: basePath = '/', } = {}) {
const fetchAPI = {
...givenFetchAPI,
...DefaultFetchAPI,
};
const routesByMethod = new Map();
function addHandlersToMethod(method, path, ...handlers) {
let methodPatternMaps = routesByMethod.get(method);
if (!methodPatternMaps) {
methodPatternMaps = new Map();
routesByMethod.set(method, methodPatternMaps);
}
let fullPath = '';
if (basePath === '/') {
fullPath = path;
}
else if (path === '/') {
fullPath = basePath;
}
else {
fullPath = `${basePath}${path}`;
}
const pattern = new fetchAPI.URLPattern({ pathname: fullPath });
methodPatternMaps.set(pattern, handlers);
}
async function handleRequest(request, context) {
const method = request.method;
let _parsedUrl;
function getParsedUrl() {
if (!_parsedUrl) {
_parsedUrl = new fetchAPI.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 new Proxy(match.pathname.groups, {
get(_, prop) {
const value = match.pathname.groups[prop.toString()];
if (value != null) {
return decodeURIComponent(value);
}
return value;
},
});
}
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;
}
}
}
}
}
}
return 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;
};
},
});
}
function createRouter(options) {
const routerBaseObject = createRouterBase(options);
return server.createServerAdapter(routerBaseObject, options);
}
Object.defineProperty(exports, 'Response', {
enumerable: true,
get: function () {
return DefaultFetchAPI.Response;
}
});
Object.defineProperty(exports, 'URLPattern', {
enumerable: true,
get: function () {
return DefaultFetchAPI.URLPattern;
}
});
Object.defineProperty(exports, 'useCORS', {
enumerable: true,
get: function () {
return server.useCORS;
}
});
Object.defineProperty(exports, 'useErrorHandling', {
enumerable: true,
get: function () {
return server.useErrorHandling;
}
});
exports.createRouter = createRouter;
exports.createRouterBase = createRouterBase;