Skip to content

Commit 4af090d

Browse files
authored
Merge pull request #107 from preactjs/feat/better-prerender-errors
Feat: Better prerender errors
2 parents 070c20c + b268bcf commit 4af090d

4 files changed

Lines changed: 124 additions & 17 deletions

File tree

demo/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}

package-lock.json

Lines changed: 66 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,23 @@
4242
"kolorist": "^1.8.0",
4343
"magic-string": "0.30.5",
4444
"node-html-parser": "^6.1.10",
45-
"resolve": "^1.22.8"
45+
"resolve": "^1.22.8",
46+
"source-map": "^0.7.4",
47+
"stack-trace": "^1.0.0-pre2"
4648
},
4749
"peerDependencies": {
4850
"@babel/core": "7.x",
4951
"vite": "2.x || 3.x || 4.x || 5.x"
5052
},
5153
"devDependencies": {
5254
"@babel/core": "^7.15.8",
55+
"@types/babel__code-frame": "^7.0.6",
5356
"@types/babel__core": "^7.1.14",
5457
"@types/debug": "^4.1.5",
5558
"@types/estree": "^0.0.50",
5659
"@types/node": "^14.14.33",
5760
"@types/resolve": "^1.20.1",
61+
"@types/stack-trace": "^0.0.33",
5862
"lint-staged": "^10.5.4",
5963
"preact": "^10.19.2",
6064
"preact-iso": "^2.3.2",

src/prerender.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import path from "node:path";
2-
32
import { promises as fs } from "node:fs";
43

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

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

@@ -116,6 +118,9 @@ export function PrerenderPlugin({
116118
apply: "build",
117119
enforce: "post",
118120
configResolved(config) {
121+
// Enable sourcemaps for generating more actionable error messages
122+
config.build.sourcemap = true;
123+
119124
viteConfig = config;
120125
},
121126
async options(opts) {
@@ -212,7 +217,7 @@ export function PrerenderPlugin({
212217
JSON.stringify({ type: "module" }),
213218
);
214219

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

@@ -222,7 +227,7 @@ export function PrerenderPlugin({
222227
);
223228

224229
if ((bundle[output] as OutputChunk).exports?.includes("prerender")) {
225-
prerenderEntry = bundle[output];
230+
prerenderEntry = bundle[output] as OutputChunk;
226231
}
227232
}
228233
if (!prerenderEntry) {
@@ -238,22 +243,61 @@ export function PrerenderPlugin({
238243
);
239244
prerender = m.prerender;
240245
} catch (e) {
241-
const isReferenceError = e instanceof ReferenceError;
246+
const stack = StackTraceParse(e as Error).find(s =>
247+
s.getFileName().includes(tmpDir),
248+
);
242249

243-
const message = `
250+
const isReferenceError = e instanceof ReferenceError;
251+
let message = `\n
244252
${e}
245253
246254
This ${
247255
isReferenceError ? "is most likely" : "could be"
248256
} caused by using DOM/Web APIs which are not available
249-
available to the prerendering process which runs in Node. Consider
257+
available to the prerendering process running in Node. Consider
250258
wrapping the offending code in a window check like so:
251259
252260
if (typeof window !== "undefined") {
253261
// do something in browsers only
254262
}
255263
`.replace(/^\t{5}/gm, "");
256264

265+
const sourceMapContent = prerenderEntry.map;
266+
if (stack && sourceMapContent) {
267+
await SourceMapConsumer.with(
268+
sourceMapContent,
269+
null,
270+
async consumer => {
271+
let { source, line, column } = consumer.originalPositionFor({
272+
line: stack.getLineNumber(),
273+
column: stack.getColumnNumber(),
274+
});
275+
276+
if (!source || line == null || column == null) {
277+
message += `\nUnable to locate source map for error!\n`;
278+
this.error(message);
279+
}
280+
281+
// `source-map` returns 0-indexed column numbers
282+
column += 1;
283+
284+
const sourcePath = path.join(
285+
viteConfig.root,
286+
source.replace(/^(..\/)*/, ""),
287+
);
288+
const sourceContent = await fs.readFile(sourcePath, "utf-8");
289+
290+
const frame = codeFrameColumns(sourceContent, {
291+
start: { line, column },
292+
});
293+
message += `
294+
> ${sourcePath}:${line}:${column}\n
295+
${frame}
296+
`.replace(/^\t{7}/gm, "");
297+
},
298+
);
299+
}
300+
257301
this.error(message);
258302
}
259303

0 commit comments

Comments
 (0)