var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value;
var __spreadValues = (a, b) => {
  for (var prop in b || (b = {}))
    if (__hasOwnProp.call(b, prop))
      __defNormalProp(a, prop, b[prop]);
  if (__getOwnPropSymbols)
    for (var prop of __getOwnPropSymbols(b)) {
      if (__propIsEnum.call(b, prop))
        __defNormalProp(a, prop, b[prop]);
    }
  return a;
};
import {fileURLToPath} from "url";
import {promisify} from "util";
import connect from "connect";
import mime from "mime";
import {performance} from "perf_hooks";
import stripAnsi from "strip-ansi";
import vite from "../vite.js";
import {defaultLogOptions, error, info} from "../logger.js";
import {ssr} from "../ssr/index.js";
import {createRouteManifest, matchRoute} from "../ssr/routing.js";
import {createVite} from "../create-vite.js";
import * as msg from "./messages.js";
import {errorTemplate} from "./template/error.js";
async function dev(config, options = {logging: defaultLogOptions}) {
  const server = new AstroDevServer(config, options);
  await server.start();
  process.on("SIGTERM", () => server.stop());
  return {
    hostname: server.hostname,
    port: server.port,
    server: server.app,
    stop: () => server.stop()
  };
}
class AstroDevServer {
  constructor(config, options) {
    this.app = connect();
    this.routeCache = {};
    this.config = config;
    this.hostname = config.devOptions.hostname || "localhost";
    this.logging = options.logging;
    this.port = config.devOptions.port;
    this.origin = `http://localhost:${this.port}`;
    this.manifest = createRouteManifest({config});
  }
  async start() {
    const devStart = performance.now();
    this.viteServer = await this.createViteServer();
    this.app.use((req, res, next) => this.handleRequest(req, res, next));
    this.app.use(this.viteServer.middlewares);
    this.app.use((req, res, next) => this.renderError(req, res, next));
    await this.listen(devStart);
  }
  async stop() {
    if (this.viteServer) {
      await this.viteServer.close();
    }
    if (this.httpServer) {
      await promisify(this.httpServer.close)();
    }
  }
  async handleHotUpdate({file, modules}) {
    var _a;
    if (!this.viteServer)
      throw new Error(`AstroDevServer.start() not called`);
    for (const module of modules) {
      this.viteServer.moduleGraph.invalidateModule(module);
    }
    const route = this.mostRecentRoute;
    const pathname = (_a = route == null ? void 0 : route.pathname) != null ? _a : "/";
    if (!route) {
      this.viteServer.ws.send({
        type: "full-reload"
      });
      return [];
    }
    try {
      const html = await ssr({
        astroConfig: this.config,
        filePath: new URL(`./${route.component}`, this.config.projectRoot),
        logging: this.logging,
        mode: "development",
        origin: this.origin,
        pathname,
        route,
        routeCache: this.routeCache,
        viteServer: this.viteServer
      });
      this.viteServer.ws.send({
        type: "custom",
        event: "astro:reload",
        data: {html}
      });
      return [];
    } catch (e) {
      const err = e;
      this.viteServer.ssrFixStacktrace(err);
      console.error(err.stack);
      this.viteServer.ws.send({
        type: "full-reload"
      });
      return [];
    }
  }
  listen(devStart) {
    let showedPortTakenMsg = false;
    return new Promise((resolve, reject) => {
      const listen = () => {
        var _a;
        this.httpServer = this.app.listen(this.port, this.hostname, () => {
          info(this.logging, "astro", msg.devStart({startupTime: performance.now() - devStart}));
          info(this.logging, "astro", msg.devHost({host: `http://${this.hostname}:${this.port}`}));
          resolve();
        });
        (_a = this.httpServer) == null ? void 0 : _a.on("error", onError);
      };
      const onError = (err) => {
        var _a;
        if (err.code && err.code === "EADDRINUSE") {
          if (!showedPortTakenMsg) {
            info(this.logging, "astro", msg.portInUse({port: this.port}));
            showedPortTakenMsg = true;
          }
          this.port++;
          return listen();
        } else {
          error(this.logging, "astro", err.stack);
          (_a = this.httpServer) == null ? void 0 : _a.removeListener("error", onError);
          reject(err);
        }
      };
      listen();
    });
  }
  async createViteServer() {
    const viteConfig = await createVite(__spreadValues({
      mode: "development",
      server: {
        middlewareMode: "ssr",
        host: this.hostname
      }
    }, this.config.vite || {}), {astroConfig: this.config, logging: this.logging, devServer: this});
    const viteServer = await vite.createServer(viteConfig);
    const pagesDirectory = fileURLToPath(this.config.pages);
    viteServer.watcher.on("add", (file) => {
      if (!file.startsWith(pagesDirectory)) {
        return;
      }
      this.routeCache = {};
      this.manifest = createRouteManifest({config: this.config});
    });
    viteServer.watcher.on("unlink", (file) => {
      if (!file.startsWith(pagesDirectory)) {
        return;
      }
      this.routeCache = {};
      this.manifest = createRouteManifest({config: this.config});
    });
    viteServer.watcher.on("change", () => {
      this.routeCache = {};
    });
    return viteServer;
  }
  async handleRequest(req, res, next) {
    if (!this.viteServer)
      throw new Error(`AstroDevServer.start() not called`);
    let pathname = req.url || "/";
    const reqStart = performance.now();
    let filePath;
    try {
      const route = matchRoute(pathname, this.manifest);
      if (!route) {
        next();
        return;
      }
      this.mostRecentRoute = route;
      filePath = new URL(`./${route.component}`, this.config.projectRoot);
      const html = await ssr({
        astroConfig: this.config,
        filePath,
        logging: this.logging,
        mode: "development",
        origin: this.origin,
        pathname,
        route,
        routeCache: this.routeCache,
        viteServer: this.viteServer
      });
      info(this.logging, "astro", msg.req({url: pathname, statusCode: 200, reqTime: performance.now() - reqStart}));
      res.writeHead(200, {
        "Content-Type": mime.getType(".html"),
        "Content-Length": Buffer.byteLength(html, "utf8")
      });
      res.write(html);
      res.end();
    } catch (err) {
      this.viteServer.ssrFixStacktrace(err);
      this.viteServer.ws.send({type: "error", err});
      const statusCode = 500;
      const html = errorTemplate({
        statusCode,
        title: "Internal Error",
        tabTitle: "500: Error",
        message: stripAnsi(err.message)
      });
      info(this.logging, "astro", msg.req({url: pathname, statusCode: 500, reqTime: performance.now() - reqStart}));
      res.writeHead(statusCode, {
        "Content-Type": mime.getType(".html"),
        "Content-Length": Buffer.byteLength(html, "utf8")
      });
      res.write(html);
      res.end();
    }
  }
  async renderError(req, res, next) {
    if (!this.viteServer)
      throw new Error(`AstroDevServer.start() not called`);
    const pathname = req.url || "/";
    const reqStart = performance.now();
    let html = "";
    const statusCode = 404;
    const relPages = this.config.pages.href.replace(this.config.projectRoot.href, "");
    const userDefined404 = this.manifest.routes.find((route) => route.component === relPages + "404.astro");
    if (userDefined404) {
      html = await ssr({
        astroConfig: this.config,
        filePath: new URL(`./${userDefined404.component}`, this.config.projectRoot),
        logging: this.logging,
        mode: "development",
        pathname: `/${userDefined404.component}`,
        origin: this.origin,
        routeCache: this.routeCache,
        viteServer: this.viteServer
      });
    } else {
      html = errorTemplate({statusCode, title: "Not found", tabTitle: "404: Not Found", message: pathname});
    }
    info(this.logging, "astro", msg.req({url: pathname, statusCode, reqTime: performance.now() - reqStart}));
    res.writeHead(statusCode, {
      "Content-Type": mime.getType(".html"),
      "Content-Length": Buffer.byteLength(html, "utf8")
    });
    res.write(html);
    res.end();
  }
}
export {
  AstroDevServer,
  dev as default
};
//# sourceMappingURL=data:application/json;base64, 
