import fs from "fs";
import npath from "path";
import { fileURLToPath } from "url";
import glob from "fast-glob";
import vite from "../vite.js";
import { debug, error } from "../../core/logger.js";
import { createBuildInternals } from "../../core/build/internal.js";
import { rollupPluginAstroBuildCSS } from "../../vite-plugin-build-css/index.js";
import { getParamsAndProps } from "../ssr/index.js";
import { createResult } from "../ssr/result.js";
import { renderPage } from "../../runtime/server/index.js";
import { prepareOutDir } from "./fs.js";
const MAX_CONCURRENT_RENDERS = 10;
function addPageName(pathname, opts) {
  const pathrepl = opts.astroConfig.buildOptions.pageUrlFormat === "directory" ? "/index.html" : pathname === "/" ? "index.html" : ".html";
  opts.pageNames.push(pathname.replace(/\/?$/, pathrepl).replace(/^\//, ""));
}
function chunkIsPage(output, internals) {
  if (output.type !== "chunk") {
    return false;
  }
  const chunk = output;
  return chunk.facadeModuleId && (internals.entrySpecifierToBundleMap.has(chunk.facadeModuleId) || internals.entrySpecifierToBundleMap.has("/" + chunk.facadeModuleId));
}
function* throttle(max, inPaths) {
  let tmp = [];
  let i = 0;
  for (let path of inPaths) {
    tmp.push(path);
    if (i === max) {
      yield tmp;
      tmp.length = 0;
      i = 0;
    } else {
      i++;
    }
  }
  if (tmp.length) {
    yield tmp;
  }
}
async function staticBuild(opts) {
  const { allPages, astroConfig } = opts;
  const pageInput = new Set();
  const jsInput = new Set();
  const facadeIdToPageDataMap = new Map();
  const polyfills = getRenderers(opts).flatMap((renderer) => {
    return (renderer.polyfills || []).concat(renderer.hydrationPolyfills || []);
  });
  for (const polyfill of polyfills) {
    jsInput.add(polyfill);
  }
  for (const [component, pageData] of Object.entries(allPages)) {
    const [renderers, mod] = pageData.preload;
    const metadata = mod.$$metadata;
    const topLevelImports = new Set([
      ...metadata.hydratedComponentPaths(),
      ...metadata.hydrationDirectiveSpecifiers(),
      ...renderers.filter((renderer) => !!renderer.source).map((renderer) => renderer.source)
    ]);
    for (const specifier of topLevelImports) {
      jsInput.add(specifier);
    }
    let astroModuleId = new URL("./" + component, astroConfig.projectRoot).pathname;
    pageInput.add(astroModuleId);
    facadeIdToPageDataMap.set(astroModuleId, pageData);
  }
  const internals = createBuildInternals();
  prepareOutDir(astroConfig);
  const [ssrResult] = await Promise.all([ssrBuild(opts, internals, pageInput), clientBuild(opts, internals, jsInput)]);
  await generatePages(ssrResult, opts, internals, facadeIdToPageDataMap);
  await cleanSsrOutput(opts);
}
async function ssrBuild(opts, internals, input) {
  const { astroConfig, viteConfig } = opts;
  return await vite.build({
    logLevel: "error",
    mode: "production",
    build: {
      emptyOutDir: false,
      minify: false,
      outDir: fileURLToPath(astroConfig.dist),
      ssr: true,
      rollupOptions: {
        input: Array.from(input),
        output: {
          format: "esm"
        }
      },
      target: "es2020"
    },
    plugins: [
      vitePluginNewBuild(input, internals, "mjs"),
      rollupPluginAstroBuildCSS({
        internals
      }),
      ...viteConfig.plugins || []
    ],
    publicDir: viteConfig.publicDir,
    root: viteConfig.root,
    envPrefix: "PUBLIC_",
    server: viteConfig.server,
    base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : "/"
  });
}
async function clientBuild(opts, internals, input) {
  const { astroConfig, viteConfig } = opts;
  if (!input.size) {
    return null;
  }
  return await vite.build({
    logLevel: "error",
    mode: "production",
    build: {
      emptyOutDir: false,
      minify: "esbuild",
      outDir: fileURLToPath(astroConfig.dist),
      rollupOptions: {
        input: Array.from(input),
        output: {
          format: "esm"
        },
        preserveEntrySignatures: "exports-only"
      },
      target: "es2020"
    },
    plugins: [
      vitePluginNewBuild(input, internals, "js"),
      rollupPluginAstroBuildCSS({
        internals
      }),
      ...viteConfig.plugins || []
    ],
    publicDir: viteConfig.publicDir,
    root: viteConfig.root,
    envPrefix: "PUBLIC_",
    server: viteConfig.server,
    base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : "/"
  });
}
function getRenderers(opts) {
  const pageData = Object.values(opts.allPages)[0];
  const viteLoadedRenderers = pageData.preload[0];
  return viteLoadedRenderers;
}
async function collectRenderers(opts) {
  const viteLoadedRenderers = getRenderers(opts);
  const renderers = await Promise.all(viteLoadedRenderers.map(async (r) => {
    const mod = await import(r.serverEntry);
    return Object.create(r, {
      ssr: {
        value: mod.default
      }
    });
  }));
  return renderers;
}
async function generatePages(result, opts, internals, facadeIdToPageDataMap) {
  debug(opts.logging, "generate", "End build step, now generating");
  const renderers = await collectRenderers(opts);
  const generationPromises = [];
  for (let output of result.output) {
    if (chunkIsPage(output, internals)) {
      generationPromises.push(generatePage(output, opts, internals, facadeIdToPageDataMap, renderers));
    }
  }
  await Promise.all(generationPromises);
}
async function generatePage(output, opts, internals, facadeIdToPageDataMap, renderers) {
  const { astroConfig } = opts;
  let url = new URL("./" + output.fileName, astroConfig.dist);
  const facadeId = output.facadeModuleId;
  let pageData = facadeIdToPageDataMap.get(facadeId) || facadeIdToPageDataMap.get("/" + facadeId);
  if (!pageData) {
    throw new Error(`Unable to find a PageBuildData for the Astro page: ${facadeId}. There are the PageBuilDatas we have ${Array.from(facadeIdToPageDataMap.keys()).join(", ")}`);
  }
  let linkIds = internals.facadeIdToAssetsMap.get(facadeId) || [];
  let compiledModule = await import(url.toString());
  let Component = compiledModule.default;
  const generationOptions = {
    pageData,
    internals,
    linkIds,
    Component,
    renderers
  };
  const renderPromises = [];
  for (const paths of throttle(MAX_CONCURRENT_RENDERS, pageData.paths)) {
    for (const path of paths) {
      renderPromises.push(generatePath(path, opts, generationOptions));
    }
    await Promise.all(renderPromises);
    renderPromises.length = 0;
  }
}
async function generatePath(pathname, opts, gopts) {
  const { astroConfig, logging, origin, pageNames, routeCache } = opts;
  const { Component, internals, linkIds, pageData, renderers } = gopts;
  addPageName(pathname, opts);
  const [, mod] = pageData.preload;
  try {
    const [params, pageProps] = await getParamsAndProps({
      route: pageData.route,
      routeCache,
      logging,
      pathname,
      mod,
      validate: false
    });
    debug(logging, "generate", `Generating: ${pathname}`);
    const rootpath = new URL(astroConfig.buildOptions.site || "http://localhost/").pathname;
    const result = createResult({ astroConfig, origin, params, pathname, renderers });
    result.links = new Set(linkIds.map((href) => ({
      props: {
        rel: "stylesheet",
        href: npath.posix.join(rootpath, href)
      },
      children: ""
    })));
    result.resolve = async (specifier) => {
      const hashedFilePath = internals.entrySpecifierToBundleMap.get(specifier);
      if (typeof hashedFilePath !== "string") {
        throw new Error(`Cannot find the built path for ${specifier}`);
      }
      const relPath = npath.posix.relative(pathname, "/" + hashedFilePath);
      const fullyRelativePath = relPath[0] === "." ? relPath : "./" + relPath;
      return fullyRelativePath;
    };
    let html = await renderPage(result, Component, pageProps, null);
    const outFolder = new URL("." + pathname + "/", astroConfig.dist);
    const outFile = new URL("./index.html", outFolder);
    await fs.promises.mkdir(outFolder, { recursive: true });
    await fs.promises.writeFile(outFile, html, "utf-8");
  } catch (err) {
    error(opts.logging, "build", `Error rendering:`, err);
  }
}
async function cleanSsrOutput(opts) {
  const files = await glob("**/*.mjs", {
    cwd: opts.astroConfig.dist.pathname
  });
  await Promise.all(files.map(async (filename) => {
    const url = new URL(filename, opts.astroConfig.dist);
    await fs.promises.rm(url);
  }));
}
function vitePluginNewBuild(input, internals, ext) {
  return {
    name: "@astro/rollup-plugin-new-build",
    config(config, options) {
      var _a;
      const extra = {};
      const noExternal = [], external = [];
      if (options.command === "build" && ((_a = config.build) == null ? void 0 : _a.ssr)) {
        noExternal.push("astro");
        external.push("shiki");
      }
      extra.ssr = {
        external,
        noExternal
      };
      return extra;
    },
    configResolved(resolvedConfig) {
      const plugins = resolvedConfig.plugins;
      const viteAsset = plugins.find((p) => p.name === "vite:asset");
      if (viteAsset) {
        delete viteAsset.generateBundle;
      }
    },
    outputOptions(outputOptions) {
      Object.assign(outputOptions, {
        entryFileNames(_chunk) {
          return "assets/[name].[hash]." + ext;
        },
        chunkFileNames(_chunk) {
          return "assets/[name].[hash]." + ext;
        }
      });
      return outputOptions;
    },
    async generateBundle(_options, bundle) {
      const promises = [];
      const mapping = new Map();
      for (const specifier of input) {
        promises.push(this.resolve(specifier).then((result) => {
          if (result) {
            mapping.set(result.id, specifier);
          }
        }));
      }
      await Promise.all(promises);
      for (const [, chunk] of Object.entries(bundle)) {
        if (chunk.type === "chunk" && chunk.facadeModuleId && mapping.has(chunk.facadeModuleId)) {
          const specifier = mapping.get(chunk.facadeModuleId);
          internals.entrySpecifierToBundleMap.set(specifier, chunk.fileName);
        }
      }
    }
  };
}
export {
  staticBuild,
  vitePluginNewBuild
};
//# sourceMappingURL=data:application/json;base64, 
