UNPKG

astro

Version:

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

139 lines (138 loc) • 5.79 kB
import fs from "node:fs"; import { performance } from "node:perf_hooks"; import colors from "piccolore"; import { getMajor, getMinor, getPatch, isGreaterThan } from "verkit"; import { getDataStoreChunkSize, getDataStoreDir, getDataStoreFile } from "../../content/paths.js"; import { globalContentLayer } from "../../content/instance.js"; import { attachContentServerListeners, attachDataStoreInvalidation } from "../../content/index.js"; import { MutableDataStore } from "../../content/mutable-data-store.js"; import { globalContentConfigObserver } from "../../content/utils.js"; import { telemetry } from "../../events/index.js"; import * as msg from "../messages/runtime.js"; import { newVersionAvailable } from "../messages/node.js"; import { ensureProcessNodeEnv } from "../util.js"; import { startContainer } from "./container.js"; import { createContainerWithAutomaticRestart } from "./restart.js"; import { fetchLatestAstroVersion, MAX_PATCH_DISTANCE, shouldCheckForUpdates } from "./update-check.js"; import { BuildTimeAstroVersionProvider } from "../../cli/infra/build-time-astro-version-provider.js"; import { piccoloreTextStyler } from "../../cli/infra/piccolore-text-styler.js"; /** * Runs Astro’s development server. This is a local HTTP server that doesn’t bundle assets. * It uses Hot Module Replacement (HMR) to update your browser as you save changes in your editor. * * @experimental The JavaScript API is experimental */ export default async function dev(inlineConfig) { ensureProcessNodeEnv("development"); const devStart = performance.now(); await telemetry.record([]); // Create a container which sets up the Vite server. const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs }); const logger = restart.container.logger; const currentVersion = "7.4.2" ?? "0.0.0"; const isPrerelease = currentVersion.includes("-"); if (!isPrerelease) { try { // Don't await this, we don't want to block the dev server from starting shouldCheckForUpdates(restart.container.settings.preferences).then(async (shouldCheck) => { if (shouldCheck) { const version = await fetchLatestAstroVersion(restart.container.settings.preferences); if (isGreaterThan(version, currentVersion)) { // Only update the latestAstroVersion if the latest version is greater than the current version, that way we don't need to check that again // whenever we check for the latest version elsewhere restart.container.settings.latestAstroVersion = version; const sameMajor = getMajor(version) === getMajor(currentVersion); const sameMinor = getMinor(version) === getMinor(currentVersion); const patchDistance = getPatch(version) - getPatch(currentVersion); if (sameMajor && sameMinor && patchDistance < MAX_PATCH_DISTANCE) { // Don't bother the user with a log if they're only a few patch versions behind // We can still tell them in the dev toolbar, which has a more opt-in nature return; } logger.warn("SKIP_FORMAT", await newVersionAvailable({ latestVersion: version })); } } }).catch(() => {}); } catch {} } let store; try { const chunkSize = getDataStoreChunkSize(restart.container.settings); if (chunkSize !== undefined) { const dataStoreDir = getDataStoreDir(restart.container.settings, true); store = await MutableDataStore.fromDir(dataStoreDir, chunkSize, logger); } else { const dataStoreFile = getDataStoreFile(restart.container.settings, true); store = await MutableDataStore.fromFile(dataStoreFile); } } catch (err) { logger.error("content", err.message); } if (!store) { logger.error("content", "Failed to create data store"); } else { // Invalidate the content virtual modules directly when the store is // written, rather than relying on the file watcher to observe the write. // On Windows the watcher can miss it, leaving dev serving stale content. attachDataStoreInvalidation(store, restart.container.viteServer, restart.container.settings); } await attachContentServerListeners(restart.container); const config = globalContentConfigObserver.get(); if (config.status === "error") { logger.error("content", config.error.message); } if (config.status === "loaded" && store) { const contentLayer = globalContentLayer.init({ settings: restart.container.settings, logger, watcher: restart.container.viteServer.watcher, store }); contentLayer.watchContentConfig(); await contentLayer.sync(); } else if (config.status !== "does-not-exist") { logger.warn("content", "Content config not loaded"); } // Start listening to the port const devServerAddressInfo = await startContainer(restart.container); restart.bindCLIShortcuts(); logger.info("SKIP_FORMAT", msg.serverStart({ startupTime: performance.now() - devStart, resolvedUrls: restart.container.viteServer.resolvedUrls || { local: [], network: [] }, host: restart.container.settings.config.server.host, base: restart.container.settings.config.base, astroVersionProvider: new BuildTimeAstroVersionProvider(), textStyler: piccoloreTextStyler })); if (isPrerelease) { logger.warn("SKIP_FORMAT", msg.prerelease({ currentVersion })); } if (restart.container.viteServer.config.server?.fs?.strict === false) { logger.warn("SKIP_FORMAT", msg.fsStrictWarning()); } logger.info(null, colors.green("watching for file changes...")); return { address: devServerAddressInfo, get resolvedUrls() { return restart.container.viteServer.resolvedUrls || { local: [], network: [] }; }, get watcher() { return restart.container.viteServer.watcher; }, handle(req, res) { return restart.container.handle(req, res); }, async stop() { await restart.container.close(); } }; }