UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

181 lines (180 loc) • 6.98 kB
import { fileURLToPath } from "node:url"; import * as vite from "vite"; import { globalContentLayer } from "../../content/instance.js"; import { attachContentServerListeners } from "../../content/server-listeners.js"; import { eventCliSession, telemetry } from "../../events/index.js"; import { runHookConfigDone, runHookConfigSetup } from "../../integrations/hooks.js"; import { SETTINGS_FILE } from "../../preferences/constants.js"; import { getPrerenderDefault } from "../../prerender/utils.js"; import { createSettings, resolveConfig } from "../config/index.js"; import { clearCrawlCache, createVite } from "../create-vite.js"; import { collectErrorMetadata } from "../errors/dev/utils.js"; import { isAstroConfigZodError } from "../errors/errors.js"; import { createSafeError } from "../errors/index.js"; import { loadOrCreateNodeLogger } from "../logger/load.js"; import { formatErrorMessage, warnIfCspWithShiki } from "../messages/runtime.js"; import { createRoutesList } from "../routing/create-manifest.js"; import { createContainer } from "./container.js"; const configRE = /.*astro.config.(?:mjs|mts|cjs|cts|js|ts)$/; function shouldRestartContainer({ settings, inlineConfig, restartInFlight }, changedFile) { if (restartInFlight) return false; let shouldRestart = false; const normalizedChangedFile = vite.normalizePath(changedFile); if (inlineConfig.configFile) { shouldRestart = vite.normalizePath(inlineConfig.configFile) === normalizedChangedFile; } else { shouldRestart = configRE.test(normalizedChangedFile); const settingsPath = vite.normalizePath( fileURLToPath(new URL(SETTINGS_FILE, settings.dotAstroDir)) ); if (settingsPath.endsWith(normalizedChangedFile)) { shouldRestart = settings.preferences.ignoreNextPreferenceReload ? false : true; settings.preferences.ignoreNextPreferenceReload = false; } } if (!shouldRestart && settings.watchFiles.length > 0) { shouldRestart = settings.watchFiles.some( (path) => vite.normalizePath(path) === vite.normalizePath(changedFile) ); } return shouldRestart; } async function restartContainerInPlace(container) { const { logger, settings: existingSettings, inlineConfig, fs } = container; container.restartInFlight = true; clearCrawlCache(); try { const { astroConfig } = await resolveConfig(inlineConfig, "dev", fs); warnIfCspWithShiki(astroConfig, logger); let settings = await createSettings( astroConfig, inlineConfig.logLevel, fileURLToPath(existingSettings.config.root) ); settings = await runHookConfigSetup({ settings, command: "dev", logger, isRestart: true }); if (!settings.adapter?.adapterFeatures?.buildOutput) { settings.buildOutput = getPrerenderDefault(settings.config) ? "static" : "server"; } await runHookConfigDone({ settings, logger, command: "dev" }); const mode = inlineConfig?.mode ?? "development"; const { server: { host, headers, allowedHosts } } = settings.config; const rendererClientEntries = settings.renderers.map((r) => r.clientEntrypoint).filter(Boolean); const routesList = await createRoutesList({ settings, fsMod: fs }, logger, { dev: true }); const address = container.viteServer.httpServer?.address(); const port = address !== null && typeof address === "object" ? address.port : void 0; const newViteConfig = await createVite( { server: { host, headers, allowedHosts, port }, optimizeDeps: { include: rendererClientEntries } }, { settings, logger, mode, command: "dev", fs, sync: false, routesList } ); container.viteServer.config = await vite.resolveConfig(newViteConfig, "serve"); await container.viteServer.restart(); container.settings = settings; return settings; } catch (_err) { const error = createSafeError(_err); if (!isAstroConfigZodError(_err)) { logger.error( "config", formatErrorMessage(collectErrorMetadata(error), logger.level() === "debug") + "\n" ); } container.viteServer.environments?.client?.hot?.send({ type: "error", err: { message: error.message, stack: error.stack || "" } }); logger.error(null, "Continuing with previous valid configuration\n"); return error; } finally { container.restartInFlight = false; } } async function createContainerWithAutomaticRestart({ inlineConfig, fs }) { const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, "dev", fs); const logger = await loadOrCreateNodeLogger(astroConfig, inlineConfig ?? {}); warnIfCspWithShiki(astroConfig, logger); telemetry.record(eventCliSession("dev", userConfig)); const settings = await createSettings( astroConfig, inlineConfig?.logLevel, fileURLToPath(astroConfig.root) ); const initialContainer = await createContainer({ settings, logger, inlineConfig, fs }); let resolveRestart; let restartComplete = new Promise((resolve) => { resolveRestart = resolve; }); let restart = { container: initialContainer, bindCLIShortcuts() { const customShortcuts = [ { key: "r", description: "" }, { key: "u", description: "" }, { key: "c", description: "" } ]; customShortcuts.push({ key: "s", description: "sync content layer", action: () => { globalContentLayer.get()?.sync(); } }); restart.container.viteServer.bindCLIShortcuts({ customShortcuts }); }, restarted() { return restartComplete; } }; function handleChangeRestart(logMsg) { return async function(changedFile) { if (shouldRestartContainer(restart.container, changedFile)) { logger.info(null, (logMsg + " Restarting...").trim()); const result = await restartContainerInPlace(restart.container); if (result instanceof Error) { resolveRestart(result); } else { setupContainer(); await attachContentServerListeners(restart.container); resolveRestart(null); } restartComplete = new Promise((resolve) => { resolveRestart = resolve; }); } }; } let changeHandler; let unlinkHandler; let addHandler; function setupContainer() { const watcher = restart.container.viteServer.watcher; if (changeHandler) watcher.off("change", changeHandler); if (unlinkHandler) watcher.off("unlink", unlinkHandler); if (addHandler) watcher.off("add", addHandler); changeHandler = handleChangeRestart("Configuration file updated."); unlinkHandler = handleChangeRestart("Configuration file removed."); addHandler = handleChangeRestart("Configuration file added."); watcher.on("change", changeHandler); watcher.on("unlink", unlinkHandler); watcher.on("add", addHandler); } setupContainer(); return restart; } export { createContainerWithAutomaticRestart };