UNPKG

astro

Version:

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

124 lines (123 loc) • 4.69 kB
import * as fs from "node:fs"; import { fileURLToPath } from "node:url"; import { codeToHtml, createCssVariablesTheme } from "shiki"; import { AstroError } from "../errors.js"; import { FailedToLoadModuleSSR, MdxIntegrationMissingError } from "../errors-data.js"; import { createSafeError } from "../utils.js"; import { getDocsForError, renderErrorMarkdown } from "./runtime.js"; export function enhanceViteSSRError({ error, filePath, loader, renderers }) { // NOTE: We don't know where the error that's coming here comes from, so we need to be defensive regarding what we do // to it to make sure we keep as much information as possible. It's very possible that we receive an error that does not // follow any kind of standard formats (ex: a number, a string etc) let safeError = createSafeError(error); // Vite will give you better stacktraces, using sourcemaps. if (loader) { try { loader.fixStacktrace(safeError); } catch {} } if (filePath) { const path = fileURLToPath(filePath); const content = fs.readFileSync(path).toString(); const lns = content.split("\n"); // Vite has a fairly generic error message when it fails to load a module, let's try to enhance it a bit // https://github.com/vitejs/vite/blob/ee7c28a46a6563d54b828af42570c55f16b15d2c/packages/vite/src/node/ssr/ssrModuleLoader.ts#L91 let importName; if (importName = /Failed to load url (.*?) \(resolved id:/.exec(safeError.message)?.[1]) { safeError.title = FailedToLoadModuleSSR.title; safeError.name = "FailedToLoadModuleSSR"; safeError.message = FailedToLoadModuleSSR.message(importName); safeError.hint = FailedToLoadModuleSSR.hint; const line = lns.findIndex((ln) => ln.includes(importName)); if (line !== -1) { const column = lns[line]?.indexOf(importName); safeError.loc = { file: path, line: line + 1, column }; } } const fileId = safeError.id ?? safeError.loc?.file; // Vite throws a syntax error trying to parse MDX without a plugin. // Suggest installing the MDX integration if none is found. if (fileId && !renderers?.find((r) => r.name === "@astrojs/mdx") && safeError.message.includes("Syntax error") && /.mdx$/.test(fileId)) { safeError = new AstroError({ ...MdxIntegrationMissingError, message: MdxIntegrationMissingError.message(JSON.stringify(fileId)), location: safeError.loc, stack: safeError.stack }); } } return safeError; } // Shiki does not support `mjs` or `cjs` aliases by default. // Map these to `.js` during error highlighting. const ALTERNATIVE_JS_EXTS = ["cjs", "mjs"]; const ALTERNATIVE_MD_EXTS = ["mdoc"]; let _cssVariablesTheme; const cssVariablesTheme = () => _cssVariablesTheme ?? (_cssVariablesTheme = createCssVariablesTheme({ variablePrefix: "--astro-code-" })); /** * Generate a payload for Vite's error overlay */ export async function getViteErrorPayload(err) { let plugin = err.plugin; if (!plugin && err.hint) { plugin = "astro"; } const message = renderErrorMarkdown(err.message.trim(), "html"); const hint = err.hint ? renderErrorMarkdown(err.hint.trim(), "html") : undefined; const docslink = getDocsForError(err); let highlighterLang = err.loc?.file?.split(".").pop(); if (ALTERNATIVE_JS_EXTS.includes(highlighterLang ?? "")) { highlighterLang = "js"; } else if (ALTERNATIVE_MD_EXTS.includes(highlighterLang ?? "")) { highlighterLang = "md"; } const highlightedCode = err.fullCode ? await codeToHtml(err.fullCode, { lang: highlighterLang ?? "text", theme: cssVariablesTheme(), transformers: [transformerCompactLineOptions(err.loc?.line ? [{ line: err.loc.line, classes: ["error-line"] }] : undefined)] }) : undefined; return { __isEnhancedAstroErrorPayload: true, type: "error", err: { ...err, name: err.name, type: err.type, message, hint, frame: err.frame, highlightedCode, docslink, loc: { file: err.loc?.file, line: err.loc?.line, column: err.loc?.column }, plugin, stack: err.stack, cause: err.cause } }; } /** * Transformer for `shiki`'s legacy `lineOptions`, allows adding classes to specific lines * FROM: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/packages/transformers/src/transformers/compact-line-options.ts * LICENSE: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/LICENSE */ function transformerCompactLineOptions(lineOptions = []) { return { name: "@shikijs/transformers:compact-line-options", line(node, line) { const lineOption = lineOptions.find((o) => o.line === line); if (lineOption?.classes) this.addClassToHast(node, lineOption.classes); return node; } }; }