89 lines
3.1 KiB
JavaScript
89 lines
3.1 KiB
JavaScript
import * as fs from "node:fs";
|
|
import path from "node:path";
|
|
import { appendForwardSlash, prependForwardSlash } from "@astrojs/internal-helpers/path";
|
|
import colors from "piccolore";
|
|
import { notFoundTemplate, subpathNotUsedTemplate } from "../template/4xx.js";
|
|
import { writeHtmlResponse } from "./response.js";
|
|
function resolveDevRoot(base, site) {
|
|
const effectiveBase = base || "/";
|
|
const siteUrl = site ? new URL(effectiveBase, site) : void 0;
|
|
const devRootURL = new URL(effectiveBase, "http://localhost");
|
|
const devRoot = siteUrl ? siteUrl.pathname : devRootURL.pathname;
|
|
const devRootReplacement = devRoot.endsWith("/") ? "/" : "";
|
|
return { devRoot, devRootReplacement };
|
|
}
|
|
function evaluateBaseRewrite(url, pathname, acceptHeader, devRoot, devRootReplacement) {
|
|
if (pathname.startsWith(devRoot)) {
|
|
let newUrl = url.replace(devRoot, devRootReplacement);
|
|
if (!newUrl.startsWith("/")) newUrl = prependForwardSlash(newUrl);
|
|
return { action: "rewrite", newUrl };
|
|
}
|
|
if (pathname === "/" || pathname === "/index.html") {
|
|
return { action: "not-found-subpath", pathname, devRoot };
|
|
}
|
|
if (acceptHeader?.includes("text/html")) {
|
|
return { action: "not-found", pathname };
|
|
}
|
|
return { action: "check-public" };
|
|
}
|
|
function baseMiddleware(settings, logger) {
|
|
const { config } = settings;
|
|
const { devRoot, devRootReplacement } = resolveDevRoot(config.base, config.site);
|
|
return function devBaseMiddleware(req, res, next) {
|
|
const url = req.url;
|
|
let pathname;
|
|
try {
|
|
pathname = decodeURI(new URL(url, "http://localhost").pathname);
|
|
} catch (e) {
|
|
return next(e);
|
|
}
|
|
const decision = evaluateBaseRewrite(
|
|
url,
|
|
pathname,
|
|
req.headers.accept,
|
|
devRoot,
|
|
devRootReplacement
|
|
);
|
|
switch (decision.action) {
|
|
case "rewrite":
|
|
req.url = decision.newUrl;
|
|
return next();
|
|
case "not-found-subpath": {
|
|
const html = subpathNotUsedTemplate(decision.devRoot, decision.pathname);
|
|
return writeHtmlResponse(res, 404, html);
|
|
}
|
|
case "not-found": {
|
|
const html = notFoundTemplate(decision.pathname);
|
|
return writeHtmlResponse(res, 404, html);
|
|
}
|
|
case "check-public": {
|
|
const publicPath = new URL("." + req.url, config.publicDir);
|
|
fs.stat(publicPath, (_err, stats) => {
|
|
if (stats) {
|
|
const publicDir = appendForwardSlash(
|
|
path.posix.relative(config.root.pathname, config.publicDir.pathname)
|
|
);
|
|
const devRootURL = new URL(devRoot, "http://localhost");
|
|
const expectedLocation = new URL(devRootURL.pathname + url, devRootURL).pathname;
|
|
logger.error(
|
|
"router",
|
|
`Request URLs for ${colors.bold(
|
|
publicDir
|
|
)} assets must also include your base. "${expectedLocation}" expected, but received "${url}".`
|
|
);
|
|
const html = subpathNotUsedTemplate(devRoot, pathname);
|
|
return writeHtmlResponse(res, 404, html);
|
|
} else {
|
|
next();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}
|
|
export {
|
|
baseMiddleware,
|
|
evaluateBaseRewrite,
|
|
resolveDevRoot
|
|
};
|