import { hasProtocol, withHttps, parseURL, getQuery } from 'ufo'; import { toValue } from 'vue'; function normalizeSiteConfig(config) { if (typeof config.indexable !== "undefined") config.indexable = String(config.indexable) !== "false"; if (typeof config.trailingSlash !== "undefined" && !config.trailingSlash) config.trailingSlash = String(config.trailingSlash) !== "false"; if (config.url && !hasProtocol(config.url, { acceptRelative: true, strict: false })) config.url = withHttps(config.url); const keys = Object.keys(config).sort((a, b) => a.localeCompare(b)); const newConfig = {}; for (const k of keys) newConfig[k] = config[k]; return newConfig; } function validateSiteConfigStack(stack) { const resolved = normalizeSiteConfig(stack.get({ // we need the context debug: true })); const errors = []; if (resolved.url) { const val = resolved.url; const context = resolved._context?.url || "unknown"; const url = parseURL(val); if (!url.host) errors.push(`url "${val}" from ${context} is not absolute`); else if (url.pathname && url.pathname !== "/") errors.push(`url "${val}" from ${context} should not contain a path`); else if (url.hash) errors.push(`url "${val}" from ${context} should not contain a hash`); else if (Object.keys(getQuery(val)).length > 0) errors.push(`url "${val}" from ${context} should not contain a query`); else if (url.host === "localhost") errors.push(`url "${val}" from ${context} should not be localhost`); } return errors; } function createSiteConfigStack(options) { const debug = options?.debug || false; const stack = []; function push(input) { if (!input || typeof input !== "object" || Object.keys(input).length === 0) return; if (!input._context && debug) { let lastFunctionName = new Error("tmp").stack?.split("\n")[2].split(" ")[5]; if (lastFunctionName?.includes("/")) lastFunctionName = "anonymous"; input._context = lastFunctionName; } const entry = {}; for (const k in input) { const val = input[k]; if (typeof val !== "undefined" && val !== "") entry[k] = val; } if (Object.keys(entry).filter((k) => !k.startsWith("_")).length > 0) stack.push(entry); } function get(options2) { const siteConfig = {}; if (options2?.debug) siteConfig._context = {}; for (const o in stack.sort((a, b) => (a._priority || 0) - (b._priority || 0))) { for (const k in stack[o]) { const key = k; const val = options2?.resolveRefs ? toValue(stack[o][k]) : stack[o][k]; if (!k.startsWith("_") && typeof val !== "undefined") { siteConfig[k] = val; if (options2?.debug) siteConfig._context[key] = stack[o]._context?.[key] || stack[o]._context || "anonymous"; } } } return options2?.skipNormalize ? siteConfig : normalizeSiteConfig(siteConfig); } return { stack, push, get }; } function envSiteConfig(env) { return Object.fromEntries(Object.entries(env).filter(([k]) => k.startsWith("NUXT_SITE_") || k.startsWith("NUXT_PUBLIC_SITE_")).map(([k, v]) => [ k.replace(/^NUXT_(PUBLIC_)?SITE_/, "").split("_").map((s, i) => i === 0 ? s.toLowerCase() : s[0].toUpperCase() + s.slice(1).toLowerCase()).join(""), v ])); } export { createSiteConfigStack, envSiteConfig, normalizeSiteConfig, validateSiteConfigStack };