website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


tests/helpers/static-server.js
09c6a56 1
import { createServer } from "node:http";
09c6a56 2
import { readFile, stat } from "node:fs/promises";
09c6a56 3
import path from "node:path";
09c6a56 4
09c6a56 5
const MIME_TYPES = {
09c6a56 6
  ".html": "text/html; charset=utf-8",
09c6a56 7
  ".css": "text/css; charset=utf-8",
09c6a56 8
  ".js": "text/javascript; charset=utf-8",
09c6a56 9
  ".json": "application/json; charset=utf-8",
09c6a56 10
  ".svg": "image/svg+xml",
09c6a56 11
  ".png": "image/png",
09c6a56 12
  ".jpg": "image/jpeg",
09c6a56 13
  ".jpeg": "image/jpeg",
09c6a56 14
  ".ico": "image/x-icon",
09c6a56 15
  ".xml": "application/xml; charset=utf-8",
09c6a56 16
  ".txt": "text/plain; charset=utf-8",
09c6a56 17
  ".woff2": "font/woff2",
09c6a56 18
  ".pdf": "application/pdf",
09c6a56 19
};
09c6a56 20
09c6a56 21
async function resolveFile(rootDir, urlPath) {
09c6a56 22
  const decoded = decodeURIComponent(urlPath.split("?")[0]);
09c6a56 23
  const safeSuffix = path.normalize(decoded).replace(/^(\.\.[/\\])+/, "");
09c6a56 24
  const requestedPath = path.join(rootDir, safeSuffix);
09c6a56 25
09c6a56 26
  const candidates = requestedPath.endsWith("/")
09c6a56 27
    ? [path.join(requestedPath, "index.html")]
09c6a56 28
    : [requestedPath, path.join(requestedPath, "index.html"), `${requestedPath}.html`];
09c6a56 29
09c6a56 30
  for (const candidate of candidates) {
09c6a56 31
    try {
09c6a56 32
      const info = await stat(candidate);
09c6a56 33
      if (info.isFile()) return candidate;
09c6a56 34
    } catch {
09c6a56 35
      // try next candidate
09c6a56 36
    }
09c6a56 37
  }
09c6a56 38
  return null;
09c6a56 39
}
09c6a56 40
09c6a56 41
/**
09c6a56 42
 * Serves a pre-built static site directory (e.g. Eleventy's `dist/`) over HTTP,
09c6a56 43
 * falling back to `404.html` so tests see the same not-found page as production.
09c6a56 44
 */
09c6a56 45
export async function startStaticServer(rootDir, { notFoundFile = "404.html" } = {}) {
09c6a56 46
  const server = createServer(async (req, res) => {
09c6a56 47
    try {
09c6a56 48
      const filePath = await resolveFile(rootDir, req.url ?? "/");
09c6a56 49
      if (filePath) {
09c6a56 50
        const body = await readFile(filePath);
09c6a56 51
        res.writeHead(200, { "content-type": MIME_TYPES[path.extname(filePath)] ?? "application/octet-stream" });
09c6a56 52
        res.end(body);
09c6a56 53
        return;
09c6a56 54
      }
09c6a56 55
      const fallback = path.join(rootDir, notFoundFile);
09c6a56 56
      try {
09c6a56 57
        const body = await readFile(fallback);
09c6a56 58
        res.writeHead(404, { "content-type": "text/html; charset=utf-8" });
09c6a56 59
        res.end(body);
09c6a56 60
      } catch {
09c6a56 61
        res.writeHead(404, { "content-type": "text/plain" });
09c6a56 62
        res.end("Not found");
09c6a56 63
      }
09c6a56 64
    } catch (err) {
09c6a56 65
      res.writeHead(500, { "content-type": "text/plain" });
09c6a56 66
      res.end(String(err));
09c6a56 67
    }
09c6a56 68
  });
09c6a56 69
09c6a56 70
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
09c6a56 71
  const { port } = server.address();
09c6a56 72
  return {
09c6a56 73
    url: `http://127.0.0.1:${port}`,
09c6a56 74
    close: () => new Promise((resolve) => server.close(resolve)),
09c6a56 75
  };
09c6a56 76
}