// src/commands/build/createBuild.ts
import process6 from "process";
import { createBuildApp } from "@vuepress/core";
import { debug, formatMs, fs as fs5, logger as logger3, withSpinner } from "@vuepress/utils";

// src/config/defineUserConfig.ts
var defineUserConfig = (config) => config;

// src/config/loadUserConfig.ts
import { pathToFileURL } from "url";
import { fs, hash, importFileDefault, path } from "@vuepress/utils";
import { build } from "esbuild";
var loadUserConfig = async (userConfigPath) => {
  if (!userConfigPath) {
    return {
      userConfig: {},
      userConfigDependencies: []
    };
  }
  const dirnameVarName = "__vite_injected_original_dirname";
  const filenameVarName = "__vite_injected_original_filename";
  const importMetaUrlVarName = "__vite_injected_original_import_meta_url";
  const result = await build({
    absWorkingDir: process.cwd(),
    entryPoints: [userConfigPath],
    outfile: "out.js",
    write: false,
    target: ["node18"],
    platform: "node",
    bundle: true,
    format: "esm",
    sourcemap: "inline",
    metafile: true,
    define: {
      "__dirname": dirnameVarName,
      "__filename": filenameVarName,
      "import.meta.url": importMetaUrlVarName
    },
    plugins: [
      {
        name: "externalize-deps",
        setup(build2) {
          build2.onResolve({ filter: /.*/ }, ({ path: id }) => {
            if (id[0] !== "." && !path.isAbsolute(id)) {
              return {
                external: true
              };
            }
            return null;
          });
        }
      },
      {
        name: "inject-file-scope-variables",
        setup(build2) {
          build2.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => {
            const contents = await fs.readFile(args.path, "utf8");
            const injectValues = `const ${dirnameVarName} = ${JSON.stringify(
              path.dirname(args.path)
            )};const ${filenameVarName} = ${JSON.stringify(args.path)};const ${importMetaUrlVarName} = ${JSON.stringify(
              pathToFileURL(args.path).href
            )};`;
            return {
              loader: args.path.endsWith("ts") ? "ts" : "js",
              contents: injectValues + contents
            };
          });
        }
      }
    ]
  });
  const { text } = result.outputFiles[0];
  const tempFilePath = `${userConfigPath}.${hash(text)}.mjs`;
  let userConfig;
  try {
    await fs.writeFile(tempFilePath, text);
    userConfig = await importFileDefault(tempFilePath);
  } finally {
    await fs.rm(tempFilePath);
  }
  return {
    userConfig,
    userConfigDependencies: Object.keys(result.metafile?.inputs ?? {})
  };
};

// src/config/resolveAppConfig.ts
import { ensureEndingSlash, ensureLeadingSlash } from "@vuepress/shared";
import { colors, isChildPath, logger } from "@vuepress/utils";
var resolveAppConfig = ({
  defaultAppConfig,
  cliAppConfig,
  userConfig
}) => {
  const appConfig = {
    // allow setting default app config via `cli()`
    ...defaultAppConfig,
    // user config from config file
    ...userConfig,
    // allow cli options to override config file
    ...cliAppConfig
  };
  if (appConfig.bundler === void 0 || appConfig.theme === void 0) {
    logger.error(
      `The ${colors.magenta("bundler")} or ${colors.magenta("theme")} option is missing. For more details: ${colors.green("https://v2.vuepress.vuejs.org/guide/troubleshooting.html#the-bundler-theme-option-is-missing")}`
    );
    return null;
  }
  if (appConfig.base && !(appConfig.base.startsWith("/") && appConfig.base.endsWith("/"))) {
    const rawBase = appConfig.base;
    appConfig.base = ensureLeadingSlash(ensureEndingSlash(rawBase));
    logger.warn(
      `${colors.magenta("base")} should start and end with a slash (/), so it has been normalized from ${colors.magenta(rawBase)} to ${colors.magenta(appConfig.base)}`
    );
  }
  if (appConfig.dest && isChildPath(appConfig.source, appConfig.dest)) {
    logger.warn(
      `${colors.magenta("dest")} directory would be emptied during build, so we fallback it to the default directory for the safety of your source files`
    );
    delete appConfig.dest;
  }
  return appConfig;
};

// src/config/resolveCliAppConfig.ts
import process2 from "process";
import { path as path2 } from "@vuepress/utils";
var OPTIONS_COMMON = ["debug", "open", "port", "host"];
var OPTIONS_DIRECTORY = ["cache", "dest", "temp"];
var resolveCliAppConfig = (sourceDir, commandOptions, cwd = process2.cwd()) => {
  const source = path2.resolve(cwd, sourceDir);
  const appConfig = {
    source
  };
  OPTIONS_COMMON.forEach((name) => {
    if (commandOptions[name] !== void 0) {
      appConfig[name] = commandOptions[name];
    }
  });
  OPTIONS_DIRECTORY.forEach((name) => {
    if (commandOptions[name] !== void 0) {
      appConfig[name] = path2.resolve(cwd, commandOptions[name]);
    }
  });
  return appConfig;
};

// src/config/resolveUserConfigConventionalPath.ts
import process3 from "process";
import { fs as fs2, path as path3 } from "@vuepress/utils";
var resolveUserConfigConventionalPath = (source, cwd = process3.cwd()) => [
  path3.resolve(cwd, "vuepress.config.ts"),
  path3.resolve(cwd, "vuepress.config.js"),
  path3.resolve(cwd, "vuepress.config.mjs"),
  path3.resolve(source, ".vuepress/config.ts"),
  path3.resolve(source, ".vuepress/config.js"),
  path3.resolve(source, ".vuepress/config.mjs")
].find((item) => fs2.pathExistsSync(item));

// src/config/resolveUserConfigPath.ts
import process4 from "process";
import { colors as colors2, fs as fs3, logger as logger2, path as path4 } from "@vuepress/utils";
var resolveUserConfigPath = (config, cwd = process4.cwd()) => {
  const configPath = path4.resolve(cwd, config);
  if (!fs3.pathExistsSync(configPath)) {
    throw logger2.createError(
      `config file does not exist: ${colors2.magenta(config)}`
    );
  }
  return configPath;
};

// src/config/transformUserConfigToPlugin.ts
import process5 from "process";
import { fs as fs4, path as path5 } from "@vuepress/utils";
var transformUserConfigToPlugin = (userConfig, source, cwd = process5.cwd()) => {
  const userConfigPlugin = {
    name: "user-config",
    ...userConfig
  };
  if (userConfigPlugin.clientConfigFile === void 0) {
    userConfigPlugin.clientConfigFile = [
      path5.resolve(cwd, "vuepress.client.ts"),
      path5.resolve(cwd, "vuepress.client.js"),
      path5.resolve(cwd, "vuepress.client.mjs"),
      path5.resolve(source, ".vuepress/client.ts"),
      path5.resolve(source, ".vuepress/client.js"),
      path5.resolve(source, ".vuepress/client.mjs")
    ].find((item) => fs4.pathExistsSync(item));
  }
  return userConfigPlugin;
};

// src/commands/build/createBuild.ts
var log = debug("vuepress:cli/build");
var createBuild = (defaultAppConfig) => async (sourceDir = ".", commandOptions = {}) => {
  const start = Date.now();
  log(`commandOptions:`, commandOptions);
  if (process6.env.NODE_ENV === void 0) {
    process6.env.NODE_ENV = "production";
  }
  const cliAppConfig = resolveCliAppConfig(sourceDir, commandOptions);
  const userConfigPath = commandOptions.config ? resolveUserConfigPath(commandOptions.config) : resolveUserConfigConventionalPath(cliAppConfig.source);
  log(`userConfigPath:`, userConfigPath);
  const { userConfig } = await loadUserConfig(userConfigPath);
  const appConfig = resolveAppConfig({
    defaultAppConfig,
    cliAppConfig,
    userConfig
  });
  if (appConfig === null) {
    return;
  }
  const app = createBuildApp(appConfig);
  app.use(transformUserConfigToPlugin(userConfig, cliAppConfig.source));
  if (commandOptions.cleanTemp === true) {
    await withSpinner("Cleaning temp")(() => {
      return fs5.remove(app.dir.temp());
    });
  }
  if (commandOptions.cleanCache === true) {
    await withSpinner("Cleaning cache")(() => {
      return fs5.remove(app.dir.cache());
    });
  }
  await fs5.emptyDir(app.dir.dest());
  await withSpinner("Initializing and preparing data")(async () => {
    await app.init();
    await app.prepare();
  });
  await app.build();
  await app.pluginApi.hooks.onGenerated.process(app);
  logger3.success(
    `VuePress build completed in ${formatMs(Date.now() - start)}!`
  );
};

// src/commands/dev/createDev.ts
import process8 from "process";
import { createDevApp } from "@vuepress/core";
import { debug as debug2, fs as fs6, logger as logger6, withSpinner as withSpinner2 } from "@vuepress/utils";

// src/commands/dev/watchPageFiles.ts
import { colors as colors3, logger as logger4 } from "@vuepress/utils";
import chokidar from "chokidar";

// src/commands/dev/handlePageAdd.ts
import {
  createPage,
  preparePageChunk,
  preparePageComponent,
  prepareRoutes
} from "@vuepress/core";
var handlePageAdd = async (app, filePath) => {
  const pageIndex = app.pages.findIndex((page2) => page2.filePath === filePath);
  if (pageIndex !== -1) {
    return null;
  }
  const page = await createPage(app, {
    filePath
  });
  app.pages.push(page);
  await preparePageComponent(app, page);
  await preparePageChunk(app, page);
  await prepareRoutes(app);
  return page;
};

// src/commands/dev/handlePageChange.ts
import {
  createPage as createPage2,
  preparePageChunk as preparePageChunk2,
  preparePageComponent as preparePageComponent2,
  prepareRoutes as prepareRoutes2
} from "@vuepress/core";
var handlePageChange = async (app, filePath) => {
  const pageIndex = app.pages.findIndex((page) => page.filePath === filePath);
  if (pageIndex === -1) {
    return null;
  }
  const pageOld = app.pages[pageIndex];
  const pageNew = await createPage2(app, {
    filePath
  });
  app.pages.splice(pageIndex, 1, pageNew);
  await preparePageComponent2(app, pageNew);
  await preparePageChunk2(app, pageNew);
  const isPathChanged = pageOld.path !== pageNew.path;
  const isRouteMetaChanged = JSON.stringify(pageOld.routeMeta) !== JSON.stringify(pageNew.routeMeta);
  if (isPathChanged || isRouteMetaChanged) {
    await prepareRoutes2(app);
  }
  return [pageOld, pageNew];
};

// src/commands/dev/handlePageUnlink.ts
import { prepareRoutes as prepareRoutes3 } from "@vuepress/core";
var handlePageUnlink = async (app, filePath) => {
  const pageIndex = app.pages.findIndex((page2) => page2.filePath === filePath);
  if (pageIndex === -1) {
    return null;
  }
  const page = app.pages[pageIndex];
  app.pages.splice(pageIndex, 1);
  await prepareRoutes3(app);
  return page;
};

// src/commands/dev/pageDepsHelper.ts
var createPageDepsHelper = () => {
  const store = /* @__PURE__ */ new Map();
  return {
    add: ({ deps, filePathRelative }) => {
      const depsAdded = [];
      if (filePathRelative) {
        deps.forEach((item) => {
          if (!store.has(item)) {
            store.set(item, /* @__PURE__ */ new Set());
            depsAdded.push(item);
          }
          store.get(item)?.add(filePathRelative);
        });
      }
      return depsAdded;
    },
    remove: ({ deps, filePathRelative }) => {
      const depsRemoved = [];
      if (filePathRelative) {
        deps.forEach((item) => {
          const pagePathsSet = store.get(item);
          pagePathsSet?.delete(filePathRelative);
          if (pagePathsSet?.size === 0) {
            store.delete(item);
            depsRemoved.push(item);
          }
        });
      }
      return depsRemoved;
    },
    get: (dep) => {
      const pagePathsSet = store.get(dep);
      return pagePathsSet ? [...pagePathsSet] : [];
    }
  };
};

// src/commands/dev/watchPageFiles.ts
var watchPageFiles = (app) => {
  const depsWatcher = chokidar.watch([], {
    disableGlobbing: true,
    ignoreInitial: true
  });
  const depsHelper = createPageDepsHelper();
  const addDeps = (page) => {
    const depsToAdd = depsHelper.add(page);
    depsWatcher.add(depsToAdd);
  };
  const removeDeps = (page) => {
    const depsToRemove = depsHelper.remove(page);
    depsWatcher.unwatch(depsToRemove);
  };
  const depsListener = async (dep) => {
    const pagePaths = depsHelper.get(dep);
    if (!pagePaths) return;
    for (const filePathRelative of pagePaths) {
      logger4.info(
        `dependency of page ${colors3.magenta(filePathRelative)} is modified`
      );
      await handlePageChange(app, app.dir.source(filePathRelative));
    }
  };
  depsWatcher.on("add", depsListener);
  depsWatcher.on("change", depsListener);
  depsWatcher.on("unlink", depsListener);
  app.pages.forEach((page) => addDeps(page));
  const pagesWatcher = chokidar.watch(app.options.pagePatterns, {
    cwd: app.dir.source(),
    ignoreInitial: true
  });
  pagesWatcher.on("add", async (filePathRelative) => {
    logger4.info(`page ${colors3.magenta(filePathRelative)} is created`);
    const page = await handlePageAdd(app, app.dir.source(filePathRelative));
    if (page === null) return;
    addDeps(page);
  });
  pagesWatcher.on("change", async (filePathRelative) => {
    logger4.info(`page ${colors3.magenta(filePathRelative)} is modified`);
    const result = await handlePageChange(app, app.dir.source(filePathRelative));
    if (result === null) return;
    const [pageOld, pageNew] = result;
    removeDeps(pageOld);
    addDeps(pageNew);
  });
  pagesWatcher.on("unlink", async (filePathRelative) => {
    logger4.info(`page ${colors3.magenta(filePathRelative)} is removed`);
    const page = await handlePageUnlink(app, app.dir.source(filePathRelative));
    if (page === null) return;
    removeDeps(page);
  });
  return [pagesWatcher, depsWatcher];
};

// src/commands/dev/watchUserConfigFile.ts
import process7 from "process";
import { colors as colors4, logger as logger5 } from "@vuepress/utils";
import chokidar2 from "chokidar";
var watchUserConfigFile = ({
  userConfigPath,
  userConfigDependencies,
  restart
}) => {
  const cwd = process7.cwd();
  const configWatcher = chokidar2.watch(userConfigPath, {
    cwd,
    ignoreInitial: true
  });
  configWatcher.on("change", (configFile) => {
    logger5.info(`config ${colors4.magenta(configFile)} is modified`);
    restart();
  });
  const depsWatcher = chokidar2.watch(userConfigDependencies, {
    cwd,
    ignoreInitial: true
  });
  depsWatcher.on("change", (depFile) => {
    logger5.info(`config dependency ${colors4.magenta(depFile)} is modified`);
    restart();
  });
  return [configWatcher, depsWatcher];
};

// src/commands/dev/createDev.ts
var log2 = debug2("vuepress:cli/dev");
var createDev = (defaultAppConfig) => {
  const dev = async (sourceDir = ".", commandOptions = {}) => {
    log2(`commandOptions:`, commandOptions);
    if (process8.env.NODE_ENV === void 0) {
      process8.env.NODE_ENV = "development";
    }
    const cliAppConfig = resolveCliAppConfig(sourceDir, commandOptions);
    const userConfigPath = commandOptions.config ? resolveUserConfigPath(commandOptions.config) : resolveUserConfigConventionalPath(cliAppConfig.source);
    log2(`userConfigPath:`, userConfigPath);
    const { userConfig, userConfigDependencies } = await loadUserConfig(userConfigPath);
    const appConfig = resolveAppConfig({
      defaultAppConfig,
      cliAppConfig,
      userConfig
    });
    if (appConfig === null) {
      return;
    }
    const app = createDevApp(appConfig);
    app.use(transformUserConfigToPlugin(userConfig, cliAppConfig.source));
    if (commandOptions.cleanTemp === true) {
      await withSpinner2("Cleaning temp")(() => {
        return fs6.remove(app.dir.temp());
      });
    }
    if (commandOptions.cleanCache === true) {
      await withSpinner2("Cleaning cache")(() => {
        return fs6.remove(app.dir.cache());
      });
    }
    await withSpinner2("Initializing and preparing data")(async () => {
      await app.init();
      await app.prepare();
    });
    const close = await app.dev();
    if (commandOptions.watch === false) {
      return;
    }
    const watchers = [];
    const restart = async () => {
      await Promise.all([
        // close all watchers
        ...watchers.map((item) => item.close()),
        // close current dev server
        close()
      ]);
      await dev(sourceDir, {
        ...commandOptions,
        // do not clean cache and temp on restart
        cleanCache: false,
        cleanTemp: false
      });
      logger6.tip(`dev server has restarted, please refresh your browser`);
    };
    watchers.push(...watchPageFiles(app));
    if (userConfigPath) {
      watchers.push(
        ...watchUserConfigFile({
          userConfigPath,
          userConfigDependencies,
          restart
        })
      );
    }
    await app.pluginApi.hooks.onWatched.process(app, watchers, restart);
  };
  return dev;
};

// src/commands/info.ts
import { ora } from "@vuepress/utils";
import envinfo from "envinfo";
var info = async () => {
  const spinner = ora();
  spinner.start("Collecting Environment Info");
  const result = await envinfo.run(
    {
      System: ["OS", "CPU", "Memory", "Shell"],
      Binaries: ["bun", "Node", "npm", "pnpm", "Yarn"],
      Utilities: ["Git"],
      Browsers: ["Chrome", "Edge", "Firefox", "Safari"],
      npmPackages: [
        "@vuepress/bundler-vite",
        "@vuepress/bundler-webpack",
        "@vuepress/cli",
        "@vuepress/client",
        "@vuepress/core",
        "@vuepress/markdown",
        "@vuepress/shared",
        "@vuepress/utils",
        "vuepress",
        "vue",
        "vue-router"
      ]
    },
    {
      showNotFound: true,
      duplicates: true,
      fullTree: true
    }
  );
  spinner.stop();
  console.info(result);
};

// src/cli.ts
import { createRequire } from "module";
import process9 from "process";
import { colors as colors5 } from "@vuepress/utils";
import { cac } from "cac";
var require2 = createRequire(import.meta.url);
var cli = (defaultAppConfig = {}) => {
  const program = cac("vuepress");
  const versionCli = require2("../package.json").version;
  const versionCore = require2("@vuepress/core/package.json").version;
  program.version(`core@${versionCore} vuepress/cli@${versionCli}`);
  program.help();
  program.command("dev [sourceDir]", "Start development server").option("-c, --config <config>", "Set path to config file").option("-p, --port <port>", "Use specified port (default: 8080)").option("-t, --temp <temp>", "Set the directory of the temporary files").option("--host <host>", "Use specified host (default: 0.0.0.0)").option("--cache <cache>", "Set the directory of the cache files").option("--clean-temp", "Clean the temporary files before dev").option("--clean-cache", "Clean the cache files before dev").option("--open", "Open browser when ready").option("--debug", "Enable debug mode").option("--no-watch", "Disable watching page and config files").action(createDev(defaultAppConfig));
  program.command("build [sourceDir]", "Build to static site").option("-c, --config <config>", "Set path to config file").option(
    "-d, --dest <dest>",
    "Set the directory build output (default: .vuepress/dist)"
  ).option("-t, --temp <temp>", "Set the directory of the temporary files").option("--cache <cache>", "Set the directory of the cache files").option("--clean-temp", "Clean the temporary files before build").option("--clean-cache", "Clean the cache files before build").option("--debug", "Enable debug mode").action(createBuild(defaultAppConfig));
  program.command("info", "Display environment information").action(info);
  program.parse(process9.argv, { run: false });
  if (program.matchedCommand) {
    program.runMatchedCommand().catch((err) => {
      console.error(colors5.red(err.stack));
      process9.exit(1);
    });
  } else {
    program.outputHelp();
  }
};
export {
  cli,
  createBuild,
  createDev,
  defineUserConfig,
  handlePageAdd,
  handlePageChange,
  handlePageUnlink,
  info,
  loadUserConfig,
  resolveAppConfig,
  resolveCliAppConfig,
  resolveUserConfigConventionalPath,
  resolveUserConfigPath,
  transformUserConfigToPlugin,
  watchPageFiles,
  watchUserConfigFile
};
