Skip to content

Commit

Permalink
Merge pull request #107 from preactjs/feat/better-prerender-errors
Browse files Browse the repository at this point in the history
Feat: Better prerender errors
  • Loading branch information
rschristian authored Feb 20, 2024
2 parents 070c20c + b268bcf commit 4af090d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 17 deletions.
3 changes: 3 additions & 0 deletions demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
76 changes: 66 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,23 @@
"kolorist": "^1.8.0",
"magic-string": "0.30.5",
"node-html-parser": "^6.1.10",
"resolve": "^1.22.8"
"resolve": "^1.22.8",
"source-map": "^0.7.4",
"stack-trace": "^1.0.0-pre2"
},
"peerDependencies": {
"@babel/core": "7.x",
"vite": "2.x || 3.x || 4.x || 5.x"
},
"devDependencies": {
"@babel/core": "^7.15.8",
"@types/babel__code-frame": "^7.0.6",
"@types/babel__core": "^7.1.14",
"@types/debug": "^4.1.5",
"@types/estree": "^0.0.50",
"@types/node": "^14.14.33",
"@types/resolve": "^1.20.1",
"@types/stack-trace": "^0.0.33",
"lint-staged": "^10.5.4",
"preact": "^10.19.2",
"preact-iso": "^2.3.2",
Expand Down
56 changes: 50 additions & 6 deletions src/prerender.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import path from "node:path";

import { promises as fs } from "node:fs";

import MagicString from "magic-string";
import { parse as htmlParse } from "node-html-parser";
import { SourceMapConsumer } from "source-map";
import { parse as StackTraceParse } from "stack-trace";
import { codeFrameColumns } from "@babel/code-frame";

import type { Plugin, ResolvedConfig } from "vite";

Expand Down Expand Up @@ -116,6 +118,9 @@ export function PrerenderPlugin({
apply: "build",
enforce: "post",
configResolved(config) {
// Enable sourcemaps for generating more actionable error messages
config.build.sourcemap = true;

viteConfig = config;
},
async options(opts) {
Expand Down Expand Up @@ -212,7 +217,7 @@ export function PrerenderPlugin({
JSON.stringify({ type: "module" }),
);

let prerenderEntry;
let prerenderEntry: OutputChunk | undefined;
for (const output of Object.keys(bundle)) {
if (!/\.js$/.test(output) || bundle[output].type !== "chunk") continue;

Expand All @@ -222,7 +227,7 @@ export function PrerenderPlugin({
);

if ((bundle[output] as OutputChunk).exports?.includes("prerender")) {
prerenderEntry = bundle[output];
prerenderEntry = bundle[output] as OutputChunk;
}
}
if (!prerenderEntry) {
Expand All @@ -238,22 +243,61 @@ export function PrerenderPlugin({
);
prerender = m.prerender;
} catch (e) {
const isReferenceError = e instanceof ReferenceError;
const stack = StackTraceParse(e as Error).find(s =>
s.getFileName().includes(tmpDir),
);

const message = `
const isReferenceError = e instanceof ReferenceError;
let message = `\n
${e}
This ${
isReferenceError ? "is most likely" : "could be"
} caused by using DOM/Web APIs which are not available
available to the prerendering process which runs in Node. Consider
available to the prerendering process running in Node. Consider
wrapping the offending code in a window check like so:
if (typeof window !== "undefined") {
// do something in browsers only
}
`.replace(/^\t{5}/gm, "");

const sourceMapContent = prerenderEntry.map;
if (stack && sourceMapContent) {
await SourceMapConsumer.with(
sourceMapContent,
null,
async consumer => {
let { source, line, column } = consumer.originalPositionFor({
line: stack.getLineNumber(),
column: stack.getColumnNumber(),
});

if (!source || line == null || column == null) {
message += `\nUnable to locate source map for error!\n`;
this.error(message);
}

// `source-map` returns 0-indexed column numbers
column += 1;

const sourcePath = path.join(
viteConfig.root,
source.replace(/^(..\/)*/, ""),
);
const sourceContent = await fs.readFile(sourcePath, "utf-8");

const frame = codeFrameColumns(sourceContent, {
start: { line, column },
});
message += `
> ${sourcePath}:${line}:${column}\n
${frame}
`.replace(/^\t{7}/gm, "");
},
);
}

this.error(message);
}

Expand Down

0 comments on commit 4af090d

Please sign in to comment.