@tdb/web
Version:
Common condiguration for serving a web-site and testing web-based UI components.
63 lines (50 loc) • 1.55 kB
text/typescript
import { Request, Response, NextFunction } from 'express';
import { parse as parseUrl, Url } from 'url';
import { RedirectPath } from './types';
import { url } from './common';
export function redirectHandler(
fromPath: string,
toPath: RedirectPath | string,
) {
const route = url.Route.create(fromPath);
return (req: Request, res: Response, next: NextFunction) => {
// Only allow GET requests.
if (req.method !== 'GET') {
return next();
}
// Match the URL.
const requestUrl = parseUrl(req.url, true);
const pathname = requestUrl.pathname;
if (route.isMatch(pathname)) {
const params = route.params(pathname);
// Build the URL.
const value =
typeof toPath === 'function'
? toPath({ url: requestUrl, params })
: toPath;
const url = parseUrl(value, true);
let path = url.pathname || '';
const hasQuery = url.search || requestUrl.search;
if (hasQuery) {
const query = toMergedQuery(url, requestUrl);
path = `${path}?${query}`;
}
// Redirect to the new URL.
return res.redirect(path);
}
return next();
};
}
/**
* INTERNAL
*/
const toMergedQuery = (...urls: Url[]) => {
const merged = urls.reduce((acc, url) => {
const query = url.query as object;
return { ...acc, ...query };
}, {});
const toQuery = (key: string, value: string) =>
value ? `${key}=${value}` : key;
const query = Object.keys(merged).map(key => toQuery(key, merged[key]));
return query.join('&');
};