serwist
Version:
A Swiss Army knife for service workers.
77 lines (69 loc) • 3.28 kB
text/typescript
import { RegExpRoute } from "../RegExpRoute.js";
import { Route } from "../Route.js";
import type { HTTPMethod } from "../constants.js";
import type { RouteHandler, RouteMatchCallback } from "../types.js";
import { SerwistError } from "./SerwistError.js";
import { logger } from "./logger.js";
/**
* Parses a `RegExp`, string, or function with a caching strategy into a {@linkcode Route}. This is for
* when you want to create a {@linkcode Route}, but you don't want to register it just yet: sometimes
* you want to call {@linkcode Route.setCatchHandler} first, for example.
*
* @param capture If the capture param is a {@linkcode Route} object, all other arguments will be ignored.
* @param handler A callback function that returns a promise resulting in a response.
* This parameter is required if `capture` is not a {@linkcode Route} object.
* @param method The HTTP method to match the route against. Defaults to `'GET'`.
* @returns The generated {@linkcode Route}.
*/
export const parseRoute = (capture: RegExp | string | RouteMatchCallback | Route, handler?: RouteHandler, method?: HTTPMethod): Route => {
if (typeof capture === "string") {
const captureUrl = new URL(capture, location.href);
if (process.env.NODE_ENV !== "production") {
if (!(capture.startsWith("/") || capture.startsWith("http"))) {
throw new SerwistError("invalid-string", {
moduleName: "serwist",
funcName: "parseRoute",
paramName: "capture",
});
}
// We want to check if Express-style wildcards are in the pathname only.
// TODO: Remove this log message in v4.
const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
// See https://github.com/pillarjs/path-to-regexp#parameters
const wildcards = "[*:?+]";
if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
logger.debug(
`The '$capture' parameter contains an Express-style wildcard character (${wildcards}). Strings are now always interpreted as exact matches; use a RegExp for partial or wildcard matches.`,
);
}
}
const matchCallback: RouteMatchCallback = ({ url }) => {
if (process.env.NODE_ENV !== "production") {
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
logger.debug(
`${capture} only partially matches the cross-origin URL ${url.toString()}. This route will only handle cross-origin requests if they match the entire URL.`,
);
}
}
return url.href === captureUrl.href;
};
// If `capture` is a string then `handler` and `method` must be present.
return new Route(matchCallback, handler!, method);
}
if (capture instanceof RegExp) {
// If `capture` is a `RegExp` then `handler` and `method` must be present.
return new RegExpRoute(capture, handler!, method);
}
if (typeof capture === "function") {
// If `capture` is a function then `handler` and `method` must be present.
return new Route(capture, handler!, method);
}
if (capture instanceof Route) {
return capture;
}
throw new SerwistError("unsupported-route-type", {
moduleName: "serwist",
funcName: "parseRoute",
paramName: "capture",
});
};