UNPKG

@fleek-platform/next-on-fleek

Version:

`@fleek-platform/next-on-fleek` is a CLI tool that you can use to build and develop [Next.js](https://nextjs.org/) applications so that they can run on [Fleek Functions](https://fleek.xyz/docs/platform/fleek-functions/).

137 lines (126 loc) 4.7 kB
import type { MatchPCREResult } from './pcre'; import { applyPCREMatches } from './pcre'; /** * Applies a set of headers to a response. * * If the header key is `set-cookie`, the value will be appended. Otherwise, the value will be set * and overridden to prevent duplicates which sometimes happens with headers like `x-matched-path`. * * @param target Headers object to apply to. * @param source Headers to apply. * @param pcreMatch PCRE match result to apply to header values. */ export function applyHeaders( target: Headers, source: Record<string, string> | Headers, pcreMatch?: MatchPCREResult, ): void { const entries = source instanceof Headers ? source.entries() : Object.entries(source); for (const [key, value] of entries) { const lowerKey = key.toLowerCase(); const newValue = pcreMatch?.match ? applyPCREMatches(value, pcreMatch.match, pcreMatch.captureGroupKeys) : value; if (lowerKey === 'set-cookie') { target.append(lowerKey, newValue); } else { target.set(lowerKey, newValue); } } } /** * Checks if a string is an URL. * * @param url String to check. * @returns Whether the string is an URL. */ export function isUrl(url: string): boolean { return /^https?:\/\//.test(url); } /** * Merges search params from one URLSearchParams object to another. * * Only appends the parameter if the target does not contain it, or if the value is different and not undefined. * * For params prefixed with `nxtP`, it also sets the param without the prefix if it does not exist. * The `nxtP` prefix indicates that it is for Next.js dynamic route parameters. In some cases, * Next.js fails to derive the correct route parameters and so we need to set them manually. * https://github.com/vercel/next.js/blob/canary/packages/next/src/lib/constants.ts#L3 * * For params prefixed with `nxtI`, this is a route intercept. It sets the param without the prefix, * and removes any intercepts from the param's value. This is so that the route intercept is able * to have the correct route parameters for the page. * https://github.com/vercel/next.js/blob/cdf2b79ea/packages/next/src/shared/lib/router/utils/route-regex.ts#L6 * * @param target Target that search params will be applied to. * @param source Source search params to apply to the target. */ export function applySearchParams( target: URLSearchParams, source: URLSearchParams, ) { for (const [key, value] of source.entries()) { const nxtParamMatch = /^nxtP(.+)$/.exec(key); const nxtInterceptMatch = /^nxtI(.+)$/.exec(key); if (nxtParamMatch?.[1]) { target.set(key, value); target.set(nxtParamMatch[1], value); } else if (nxtInterceptMatch?.[1]) { target.set(nxtInterceptMatch[1], value.replace(/(\(\.+\))+/, '')); } else if ( !target.has(key) || (!!value && !target.getAll(key).includes(value)) ) { target.append(key, value); } } } /** * Creates a new Request object with the same body, headers, and search params as the original. * * Replaces the URL with the given path, stripping the `.html` extension and `/index.html` for * asset matching. * https://developers.cloudflare.com/pages/platform/serving-pages/#route-matching * * @param req Request object to re-create. * @param path URL to use for the new Request object. * @returns A new Request object with the same body and headers as the original. */ export function createRouteRequest(req: Request, path: string) { const newUrl = new URL(path, req.url); applySearchParams(newUrl.searchParams, new URL(req.url).searchParams); newUrl.pathname = newUrl.pathname .replace(/\/index.html$/, '/') .replace(/\.html$/, ''); return new Request(newUrl, req); } /** * Creates a new Response object with the same body and headers as the original. * * Useful when the response object may be immutable. * * @param resp Response object to re-create. * @returns A new Response object with the same body and headers. */ export function createMutableResponse(resp: Response) { return new Response(resp.body, resp); } /** * Parses the Accept-Language header value and returns an array of locales sorted by quality. * * @param headerValue Accept-Language header value. * @returns Array of locales sorted by quality. */ export function parseAcceptLanguage(headerValue: string): string[] { return headerValue .split(',') .map(val => { const [lang, qual] = val.split(';') as [string, string | undefined]; const quality = parseFloat((qual ?? 'q=1').replace(/q *= */gi, '')); return [lang.trim(), isNaN(quality) ? 1 : quality] as [string, number]; }) .sort((a, b) => b[1] - a[1]) .map(([locale]) => (locale === '*' || locale === '' ? [] : locale)) .flat(); }