import nodeFs from "fs";
import { fileURLToPath } from "url";
import * as vite from "vite";
import { crawlFrameworkPkgs } from "vitefu";
import astroAssetsPlugin from "../assets/vite-plugin-assets.js";
import {
  astroContentAssetPropagationPlugin,
  astroContentImportPlugin,
  astroContentVirtualModPlugin
} from "../content/index.js";
import astroPostprocessVitePlugin from "../vite-plugin-astro-postprocess/index.js";
import { vitePluginAstroServer } from "../vite-plugin-astro-server/index.js";
import astroVitePlugin from "../vite-plugin-astro/index.js";
import configAliasVitePlugin from "../vite-plugin-config-alias/index.js";
import envVitePlugin from "../vite-plugin-env/index.js";
import astroHeadPlugin from "../vite-plugin-head/index.js";
import htmlVitePlugin from "../vite-plugin-html/index.js";
import { astroInjectEnvTsPlugin } from "../vite-plugin-inject-env-ts/index.js";
import astroIntegrationsContainerPlugin from "../vite-plugin-integrations-container/index.js";
import jsxVitePlugin from "../vite-plugin-jsx/index.js";
import astroLoadFallbackPlugin from "../vite-plugin-load-fallback/index.js";
import markdownVitePlugin from "../vite-plugin-markdown/index.js";
import astroScannerPlugin from "../vite-plugin-scanner/index.js";
import astroScriptsPlugin from "../vite-plugin-scripts/index.js";
import astroScriptsPageSSRPlugin from "../vite-plugin-scripts/page-ssr.js";
import { vitePluginSSRManifest } from "../vite-plugin-ssr-manifest/index.js";
import astroTransitions from "../transitions/vite-plugin-transitions.js";
import { joinPaths } from "./path.js";
const ALWAYS_NOEXTERNAL = [
  // This is only because Vite's native ESM doesn't resolve "exports" correctly.
  "astro",
  // Vite fails on nested `.astro` imports without bundling
  "astro/components",
  // Handle recommended nanostores. Only @nanostores/preact is required from our testing!
  // Full explanation and related bug report: https://github.com/withastro/astro/pull/3667
  "@nanostores/preact",
  // fontsource packages are CSS that need to be processed
  "@fontsource/*"
];
const ONLY_DEV_EXTERNAL = [
  // Imported by `<Code/>` which is processed by Vite
  "shiki",
  // Imported by `@astrojs/prism` which exposes `<Prism/>` that is processed by Vite
  "prismjs/components/index.js",
  // Imported by `astro/assets` -> `packages/astro/src/core/logger/core.ts`
  "string-width"
];
async function createVite(commandConfig, { settings, logging, mode, command, fs = nodeFs }) {
  var _a, _b;
  const astroPkgsConfig = await crawlFrameworkPkgs({
    root: fileURLToPath(settings.config.root),
    isBuild: mode === "build",
    viteUserConfig: settings.config.vite,
    isFrameworkPkgByJson(pkgJson) {
      var _a2, _b2, _c, _d, _e;
      if (((_a2 = pkgJson == null ? void 0 : pkgJson.astro) == null ? void 0 : _a2.external) === true) {
        return false;
      }
      return (
        // Attempt: package relies on `astro`. ✅ Definitely an Astro package
        ((_b2 = pkgJson.peerDependencies) == null ? void 0 : _b2.astro) || ((_c = pkgJson.dependencies) == null ? void 0 : _c.astro) || // Attempt: package is tagged with `astro` or `astro-component`. ✅ Likely a community package
        ((_d = pkgJson.keywords) == null ? void 0 : _d.includes("astro")) || ((_e = pkgJson.keywords) == null ? void 0 : _e.includes("astro-component")) || // Attempt: package is named `astro-something` or `@scope/astro-something`. ✅ Likely a community package
        /^(@[^\/]+\/)?astro\-/.test(pkgJson.name)
      );
    },
    isFrameworkPkgByName(pkgName) {
      const isNotAstroPkg = isCommonNotAstro(pkgName);
      if (isNotAstroPkg) {
        return false;
      } else {
        return void 0;
      }
    }
  });
  const commonConfig = {
    cacheDir: fileURLToPath(new URL("./node_modules/.vite/", settings.config.root)),
    // using local caches allows Astro to be used in monorepos, etc.
    clearScreen: false,
    // we want to control the output, not Vite
    logLevel: "warn",
    // log warnings and errors only
    appType: "custom",
    optimizeDeps: {
      entries: ["src/**/*"],
      exclude: ["astro", "node-fetch"]
    },
    plugins: [
      configAliasVitePlugin({ settings }),
      astroLoadFallbackPlugin({ fs, root: settings.config.root }),
      astroVitePlugin({ settings, logging }),
      astroScriptsPlugin({ settings }),
      // The server plugin is for dev only and having it run during the build causes
      // the build to run very slow as the filewatcher is triggered often.
      mode !== "build" && vitePluginAstroServer({ settings, logging, fs }),
      envVitePlugin({ settings }),
      markdownVitePlugin({ settings, logging }),
      htmlVitePlugin(),
      jsxVitePlugin({ settings, logging }),
      astroPostprocessVitePlugin(),
      mode === "dev" && astroIntegrationsContainerPlugin({ settings, logging }),
      astroScriptsPageSSRPlugin({ settings }),
      astroHeadPlugin(),
      astroScannerPlugin({ settings }),
      astroInjectEnvTsPlugin({ settings, logging, fs }),
      astroContentVirtualModPlugin({ settings }),
      astroContentImportPlugin({ fs, settings }),
      astroContentAssetPropagationPlugin({ mode, settings }),
      vitePluginSSRManifest(),
      settings.config.experimental.assets ? [astroAssetsPlugin({ settings, logging, mode })] : [],
      astroTransitions()
    ],
    publicDir: fileURLToPath(settings.config.publicDir),
    root: fileURLToPath(settings.config.root),
    envPrefix: ((_a = settings.config.vite) == null ? void 0 : _a.envPrefix) ?? "PUBLIC_",
    define: {
      "import.meta.env.SITE": settings.config.site ? JSON.stringify(settings.config.site) : "undefined"
    },
    server: {
      hmr: process.env.NODE_ENV === "test" || process.env.NODE_ENV === "production" ? false : void 0,
      // disable HMR for test
      // handle Vite URLs
      proxy: {
        // add proxies here
      },
      watch: {
        // Prevent watching during the build to speed it up
        ignored: mode === "build" ? ["**"] : void 0
      }
    },
    resolve: {
      alias: [
        {
          // This is needed for Deno compatibility, as the non-browser version
          // of this module depends on Node `crypto`
          find: "randombytes",
          replacement: "randombytes/browser"
        },
        {
          // Typings are imported from 'astro' (e.g. import { Type } from 'astro')
          find: /^astro$/,
          replacement: fileURLToPath(new URL("../@types/astro", import.meta.url))
        }
      ],
      conditions: ["astro"],
      // Astro imports in third-party packages should use the same version as root
      dedupe: ["astro"]
    },
    ssr: {
      noExternal: [...ALWAYS_NOEXTERNAL, ...astroPkgsConfig.ssr.noExternal],
      external: [...mode === "dev" ? ONLY_DEV_EXTERNAL : [], ...astroPkgsConfig.ssr.external]
    }
  };
  const assetsPrefix = settings.config.build.assetsPrefix;
  if (assetsPrefix) {
    commonConfig.experimental = {
      renderBuiltUrl(filename, { type }) {
        if (type === "asset") {
          return joinPaths(assetsPrefix, filename);
        }
      }
    };
  }
  let result = commonConfig;
  if (command && ((_b = settings.config.vite) == null ? void 0 : _b.plugins)) {
    let { plugins, ...rest } = settings.config.vite;
    const applyToFilter = command === "build" ? "serve" : "build";
    const applyArgs = [
      { ...settings.config.vite, mode },
      { command: command === "dev" ? "serve" : command, mode }
    ];
    plugins = plugins.flat(Infinity).filter((p) => {
      if (!p || (p == null ? void 0 : p.apply) === applyToFilter) {
        return false;
      }
      if (typeof p.apply === "function") {
        return p.apply(applyArgs[0], applyArgs[1]);
      }
      return true;
    });
    result = vite.mergeConfig(result, { ...rest, plugins });
  } else {
    result = vite.mergeConfig(result, settings.config.vite || {});
  }
  result = vite.mergeConfig(result, commandConfig);
  if (result.plugins) {
    sortPlugins(result.plugins);
  }
  result.customLogger = vite.createLogger(result.logLevel ?? "warn");
  return result;
}
function isVitePlugin(plugin) {
  return Boolean(plugin == null ? void 0 : plugin.hasOwnProperty("name"));
}
function findPluginIndexByName(pluginOptions, name) {
  return pluginOptions.findIndex(function(pluginOption) {
    return isVitePlugin(pluginOption) && pluginOption.name === name;
  });
}
function sortPlugins(pluginOptions) {
  const mdxPluginIndex = findPluginIndexByName(pluginOptions, "@mdx-js/rollup");
  if (mdxPluginIndex === -1)
    return;
  const jsxPluginIndex = findPluginIndexByName(pluginOptions, "astro:jsx");
  const mdxPlugin = pluginOptions[mdxPluginIndex];
  pluginOptions.splice(mdxPluginIndex, 1);
  pluginOptions.splice(jsxPluginIndex, 0, mdxPlugin);
}
const COMMON_DEPENDENCIES_NOT_ASTRO = [
  "autoprefixer",
  "react",
  "react-dom",
  "preact",
  "preact-render-to-string",
  "vue",
  "svelte",
  "solid-js",
  "lit",
  "cookie",
  "dotenv",
  "esbuild",
  "eslint",
  "jest",
  "postcss",
  "prettier",
  "astro",
  "tslib",
  "typescript",
  "vite"
];
const COMMON_PREFIXES_NOT_ASTRO = [
  "@webcomponents/",
  "@fontsource/",
  "@postcss-plugins/",
  "@rollup/",
  "@astrojs/renderer-",
  "@types/",
  "@typescript-eslint/",
  "eslint-",
  "jest-",
  "postcss-plugin-",
  "prettier-plugin-",
  "remark-",
  "rehype-",
  "rollup-plugin-",
  "vite-plugin-"
];
function isCommonNotAstro(dep) {
  return COMMON_DEPENDENCIES_NOT_ASTRO.includes(dep) || COMMON_PREFIXES_NOT_ASTRO.some(
    (prefix) => prefix.startsWith("@") ? dep.startsWith(prefix) : dep.substring(dep.lastIndexOf("/") + 1).startsWith(prefix)
    // check prefix omitting @scope/
  );
}
export {
  createVite
};
