nuxt
Version:
70 lines (69 loc) • 2.8 kB
JavaScript
import { captureStackTrace } from "errx";
import { isScriptProtocol } from "ufo";
//#region src/app/utils.ts
const PROBE_BASE_A = "https://a.invalid/probe/";
const PROBE_BASE_B = "https://b.invalid/probe/";
function parseAnchorURL(value, base = PROBE_BASE_A) {
try {
return new URL(value, base);
} catch {
return null;
}
}
function resolvesWithoutBase(value) {
const resolved = parseAnchorURL(value);
return !!resolved && resolved.href === parseAnchorURL(value, PROBE_BASE_B)?.href;
}
/** Whether a URL string is a path rooted at the current origin, so carries no protocol. */
function isRootedPath(value) {
if (value[0] !== "/" || value[1] === "/" || value[1] === "\\") return false;
return value[1] !== " " && value[1] !== "\n" && value[1] !== "\r" || !resolvesWithoutBase(value);
}
/** Whether a URL string carries its own protocol or authority. */
function isAbsoluteHref(value) {
return !!value && !isRootedPath(value) && resolvesWithoutBase(value);
}
/** The script-capable protocol a URL string would navigate to, or `null` when it is safe. */
function getScriptProtocol(value) {
if (!value || isRootedPath(value)) return null;
let resolved = parseAnchorURL(value.replace(/[\u0000-\u001F\u007F\s]+/g, ""));
while (resolved?.protocol === "view-source:") resolved = parseAnchorURL(resolved.pathname);
if (!resolved) return "";
return isScriptProtocol(resolved.protocol) ? resolved.protocol : null;
}
/** Returns the value unchanged when safe to use as an anchor `href`, or `null`. */
function sanitizeAnchorHref(value) {
return getScriptProtocol(value) === null ? value : null;
}
/** @since 3.9.0 */
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
const BOT_RE = /bot\b|chrome-lighthouse|facebookexternalhit|google\b/i;
function isBotUserAgent(userAgent) {
return BOT_RE.test(userAgent);
}
const distURL = import.meta.dev ? import.meta.url.replace(/\/app\/.*$/, "/") : "";
function getUserTrace() {
if (!import.meta.dev) return [];
const trace = captureStackTrace();
const start = trace.findIndex((entry) => !entry.source.startsWith(distURL));
const end = trace.findLastIndex((entry) => !entry.source.includes("node_modules") && !entry.source.startsWith(distURL));
if (start === -1 || end === -1) return [];
return trace.slice(start, end + 1).map((i) => ({
...i,
source: i.source.replace(/^file:\/\//, "")
}));
}
function getUserCaller() {
if (!import.meta.dev) return null;
const { source, line, column } = captureStackTrace().find((entry) => !entry.source.startsWith(distURL)) ?? {};
if (!source) return null;
return {
source: source.replace(/^file:\/\//, ""),
line,
column
};
}
//#endregion
export { getScriptProtocol, getUserCaller, getUserTrace, isAbsoluteHref, isBotUserAgent, isRootedPath, sanitizeAnchorHref, toArray };