UNPKG

astro

Version:

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

108 lines (107 loc) • 3.41 kB
import { EventEmitter } from "node:events"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { collectErrorMetadata } from "../errors/dev/utils.js"; import { getViteErrorPayload } from "../errors/dev/vite.js"; export function createViteLoader(viteServer, ssrEnvironment) { const events = new EventEmitter(); let isTsconfigUpdated = false; function isTsconfigUpdate(filePath) { const result = path.basename(filePath) === "tsconfig.json"; if (result) isTsconfigUpdated = true; return result; } // Skip event emit on tsconfig change as Vite restarts the server, and we don't // want to trigger unnecessary work that will be invalidated shortly. viteServer.watcher.on("add", (...args) => { if (!isTsconfigUpdate(args[0])) { events.emit("file-add", args); } }); viteServer.watcher.on("unlink", (...args) => { if (!isTsconfigUpdate(args[0])) { events.emit("file-unlink", args); } }); viteServer.watcher.on("change", (...args) => { if (!isTsconfigUpdate(args[0])) { events.emit("file-change", args); } }); const _wsSend = viteServer.environments.client.hot.send; viteServer.environments.client.hot.send = function(...args) { // If the tsconfig changed, Vite will trigger a reload as it invalidates the module. // However in Astro, the whole server is restarted when the tsconfig changes. If we // do a restart and reload at the same time, the browser will refetch and the server // is not ready yet, causing a blank page. Here we block that reload from happening. if (isTsconfigUpdated) { isTsconfigUpdated = false; return; } const msg = args[0]; if (msg?.type === "error") { // If we have an error, but it didn't go through our error enhancement program, it means that it's a HMR error from // vite itself, which goes through a different path. We need to enhance it here. if (!msg["__isEnhancedAstroErrorPayload"]) { const err = collectErrorMetadata(msg.err, pathToFileURL(viteServer.config.root)); getViteErrorPayload(err).then((payload) => { events.emit("hmr-error", { type: "error", err: { message: payload.err.message, stack: payload.err.stack } }); args[0] = payload; _wsSend.apply(this, args); }); return; } events.emit("hmr-error", msg); } _wsSend.apply(this, args); }; return { import(src) { return ssrEnvironment.runner.import(src); }, async resolveId(spec, parent) { const ret = await ssrEnvironment.pluginContainer.resolveId(spec, parent); return ret?.id; }, getModuleById(id) { return ssrEnvironment.moduleGraph.getModuleById(id); }, getModulesByFile(file) { return ssrEnvironment.moduleGraph.getModulesByFile(file); }, getModuleInfo(id) { return ssrEnvironment.pluginContainer.getModuleInfo(id); }, eachModule(cb) { return ssrEnvironment.moduleGraph.idToModuleMap.forEach(cb); }, invalidateModule(mod) { ssrEnvironment.moduleGraph.invalidateModule(mod); }, fixStacktrace(err) { return viteServer.ssrFixStacktrace(err); }, clientReload() { viteServer.environments.client.hot.send({ type: "full-reload", path: "*" }); }, webSocketSend(msg) { return viteServer.environments.client.hot.send(msg); }, getSSREnvironment() { return ssrEnvironment; }, isHttps() { return !!ssrEnvironment.config.server.https; }, events }; }