@netlify/zip-it-and-ship-it
Version:
63 lines (62 loc) • 2.6 kB
JavaScript
import { RUNTIME } from '../runtimes/runtime.js';
import { FunctionBundlingUserError } from './error.js';
import { nonNullable } from './non_nullable.js';
import { ExtendedURLPattern } from './urlpattern.js';
// Based on https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API.
const isExpression = (part) => part.includes('*') || part.startsWith(':') || part.includes('{') || part.includes('[') || part.includes('(');
// Detects whether a path can be represented as a literal or whether it needs
// a regular expression.
const isPathLiteral = (path) => {
const parts = path.split('/');
return parts.every((part) => !isExpression(part));
};
/**
* Takes an element from a `path` declaration and returns a Route element that
* represents it.
*/
const getRoute = (functionName, path) => {
if (typeof path !== 'string') {
throw new FunctionBundlingUserError(`'path' property must be a string, found '${JSON.stringify(path)}'`, {
functionName,
runtime: RUNTIME.JAVASCRIPT,
});
}
if (!path.startsWith('/')) {
throw new FunctionBundlingUserError(`'path' property must start with a '/'`, {
functionName,
runtime: RUNTIME.JAVASCRIPT,
});
}
if (isPathLiteral(path)) {
return { pattern: path, literal: path };
}
try {
const pattern = new ExtendedURLPattern({ pathname: path });
// Removing the `^` and `$` delimiters because we'll need to modify what's
// between them.
const regex = pattern.regexp.pathname.source.slice(1, -1);
// Wrapping the expression source with `^` and `$`. Also, adding an optional
// trailing slash, so that a declaration of `path: "/foo"` matches requests
// for both `/foo` and `/foo/`.
const normalizedRegex = `^${regex}\\/?$`;
return { pattern: path, expression: normalizedRegex };
}
catch {
throw new FunctionBundlingUserError(`'${path}' is not a valid path according to the URLPattern specification`, {
functionName,
runtime: RUNTIME.JAVASCRIPT,
});
}
};
/**
* Takes a `path` declaration, normalizes it into an array, and processes the
* individual elements to obtain an array of `Route` expressions.
*/
export const getRoutes = (functionName, pathOrPaths) => {
if (!pathOrPaths) {
return [];
}
const paths = [...new Set(Array.isArray(pathOrPaths) ? pathOrPaths : [pathOrPaths])];
const routes = paths.map((path) => getRoute(functionName, path)).filter(nonNullable);
return routes;
};