astro
Version:
Astro is a modern site builder with web best practices, performance, and DX front-of-mind.
75 lines (74 loc) • 2.88 kB
JavaScript
import { escape, unescape } from "html-escaper";
/**
* Generates a minimal HTML redirect page used for SSR redirects.
*/
export function redirectTemplate({ status, absoluteLocation, relativeLocation, from }) {
// A short delay causes Google to interpret the redirect as temporary.
// https://developers.google.com/search/docs/crawling-indexing/301-redirects#metarefresh
const delay = status === 302 ? 2 : 0;
const rel = escape(String(relativeLocation));
const abs = escape(String(absoluteLocation));
const fromHtml = from ? `from <code>${escape(from)}</code> ` : "";
return `<!doctype html>
<title>Redirecting to: ${rel}</title>
<meta http-equiv="refresh" content="${delay};url=${rel}">
<meta name="robots" content="noindex">
<link rel="canonical" href="${abs}">
<body>
<a href="${rel}">Redirecting ${fromHtml}to <code>${rel}</code></a>
</body>`;
}
/** Every `<meta …>` tag in a document. */
const META_TAG_RE = /<meta\s[^>]*>/gi;
/**
* An attribute's value, quoted with `"`, with `'`, or bare, decoded back to the
* text it stands for. Both Astro's renderer and `redirectTemplate` escape
* attribute values, so a destination like `/new?a=1&b=2` reaches us written as
* `/new?a=1&b=2`.
*/
function attributeValue(tag, name) {
const pattern = new RegExp(`\\s${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
const match = pattern.exec(tag);
if (!match) return undefined;
const value = match[1] ?? match[2] ?? match[3];
return value === undefined ? undefined : unescape(value);
}
/**
* Extracts the target of a meta refresh `content` value — the part after
* `<delay>;url=` — or `undefined` when the value has no `url` directive.
*/
function refreshTarget(content) {
const separator = content.indexOf(";");
if (separator === -1) return undefined;
const directive = content.slice(separator + 1).trim();
const equals = directive.indexOf("=");
if (equals === -1 || directive.slice(0, equals).trim().toLowerCase() !== "url") {
return undefined;
}
return stripQuotes(directive.slice(equals + 1).trim());
}
function stripQuotes(value) {
const first = value[0];
if ((first === "\"" || first === "'") && value.length > 1 && value.endsWith(first)) {
return value.slice(1, -1);
}
return value;
}
/**
* Reports whether `html` contains a `<meta http-equiv="refresh">` tag pointing at
* `location`.
*
* In a static build the meta refresh tag *is* the redirect — there is no `Location`
* header — so a redirect page that omits it, or points it somewhere else, silently
* strands visitors.
*/
export function hasMetaRefreshTo(html, location) {
for (const [tag] of html.matchAll(META_TAG_RE)) {
if (attributeValue(tag, "http-equiv")?.trim().toLowerCase() !== "refresh") continue;
const content = attributeValue(tag, "content");
if (content !== undefined && refreshTarget(content) === location) {
return true;
}
}
return false;
}