Skip to content

Commit 4da4e1a

Browse files
better prerender errors myb
1 parent 397fb47 commit 4da4e1a

File tree

1 file changed

+102
-53
lines changed

1 file changed

+102
-53
lines changed

packages/cli/lib/lib/webpack/prerender.js

+102-53
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
const { red, yellow } = require('kleur');
1+
const { red, gray } = require('kleur');
22
const { resolve } = require('path');
33
const { readFileSync } = require('fs');
44
const stackTrace = require('stack-trace');
55
const { SourceMapConsumer } = require('source-map');
6+
const { error, info } = require('../../util');
67

78
module.exports = function(env, params) {
89
params = params || {};
@@ -14,8 +15,8 @@ module.exports = function(env, params) {
1415
global.location = { href: url, pathname: url };
1516

1617
try {
17-
let m = require(entry),
18-
app = (m && m.default) || m;
18+
const m = require(entry);
19+
const app = (m && m.default) || m;
1920

2021
if (typeof app !== 'function') {
2122
// eslint-disable-next-line no-console
@@ -25,96 +26,144 @@ module.exports = function(env, params) {
2526
return '';
2627
}
2728

28-
let preact = require('preact'),
29-
renderToString = require('preact-render-to-string');
29+
const preact = require('preact');
30+
const renderToString = require('preact-render-to-string');
3031

3132
return renderToString(preact.h(app, { ...params, url }));
3233
} catch (err) {
33-
let stack = stackTrace.parse(err).filter(s => s.getFileName() === entry)[0];
34+
const stack = stackTrace
35+
.parse(err)
36+
.filter(s => s.getFileName().includes('ssr-build'))[0];
3437
if (!stack) {
35-
throw err;
38+
error(err);
39+
return '';
3640
}
3741

3842
handlePrerenderError(err, env, stack, entry);
43+
return '';
3944
}
4045
};
4146

4247
async function handlePrerenderError(err, env, stack, entry) {
43-
let errorMessage = err.toString();
44-
let isReferenceError = errorMessage.startsWith('ReferenceError');
45-
let methodName = stack.getMethodName();
46-
let sourceMapContent, position, sourcePath, sourceLines, sourceCodeHighlight;
47-
48-
try {
49-
sourceMapContent = JSON.parse(readFileSync(`${entry}.map`));
50-
} catch (err) {
51-
process.stderr.write(red(`Unable to read sourcemap: ${entry}.map\n`));
52-
}
48+
const errorMessage = err.toString();
49+
const isReferenceError = errorMessage.startsWith('ReferenceError');
50+
const methodName = stack.getMethodName();
51+
const fileName = stack.getFileName().replace(/\\/g, '/');
52+
let sourceCodeHighlight = '';
53+
54+
let position;
55+
56+
info(fileName);
57+
if (/webpack:/.test(fileName)) {
58+
position = {
59+
source: fileName.replace(/.+webpack:/, 'webpack://'),
60+
line: stack.getLineNumber(),
61+
column: stack.getColumnNumber(),
62+
};
63+
} else {
64+
try {
65+
const sourceMapContent = JSON.parse(readFileSync(`${entry}.map`));
5366

54-
if (sourceMapContent) {
55-
await SourceMapConsumer.with(sourceMapContent, null, consumer => {
56-
position = consumer.originalPositionFor({
57-
line: stack.getLineNumber(),
58-
column: stack.getColumnNumber(),
67+
await SourceMapConsumer.with(sourceMapContent, null, consumer => {
68+
position = consumer.originalPositionFor({
69+
line: stack.getLineNumber(),
70+
column: stack.getColumnNumber(),
71+
});
5972
});
60-
});
73+
} catch (err) {
74+
error(`Unable to read sourcemap: ${entry}.map`);
75+
return;
76+
}
77+
}
6178

79+
if (position) {
80+
info(position.source);
6281
position.source = position.source
6382
.replace('webpack://', '.')
6483
.replace(/^.*~\/((?:@[^/]+\/)?[^/]+)/, (s, name) =>
6584
require
6685
.resolve(name)
6786
.replace(/^(.*?\/node_modules\/(@[^/]+\/)?[^/]+)(\/.*)$/, '$1')
6887
);
88+
info(position.source);
6989

70-
sourcePath = resolve(env.src, position.source);
71-
sourceLines;
90+
let sourcePath;
91+
let sourceLines;
7292
try {
93+
sourcePath = resolve(env.src, position.source);
7394
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
7495
} catch (err) {
7596
try {
76-
sourceLines = readFileSync(
77-
require.resolve(position.source),
78-
'utf-8'
79-
).split('\n');
97+
sourcePath = resolve(env.cwd, position.source);
98+
// sourcePath = require.resolve(position.source);
99+
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
80100
} catch (err) {
81-
process.stderr.write(red(`Unable to read file: ${sourcePath}\n`));
101+
error(`Unable to read file: ${sourcePath} (${position.source})\n`);
102+
return;
82103
}
83-
// process.stderr.write(red(`Unable to read file: ${sourcePath}\n`));
84104
}
85-
sourceCodeHighlight = '';
86105

87106
if (sourceLines) {
88-
for (var i = -4; i <= 4; i++) {
89-
let color = i === 0 ? red : yellow;
90-
let line = position.line + i;
91-
let sourceLine = sourceLines[line - 1];
92-
sourceCodeHighlight += sourceLine ? `${color(sourceLine)}\n` : '';
93-
}
107+
let lnrl = position.line.toString().length + 1;
108+
sourceCodeHighlight +=
109+
gray(
110+
(position.line - 2 || '').toString().padStart(lnrl) +
111+
' | ' +
112+
sourceLines[position.line - 3] || ''
113+
) + '\n';
114+
sourceCodeHighlight +=
115+
gray(
116+
(position.line - 1 || '').toString().padStart(lnrl) +
117+
' | ' +
118+
sourceLines[position.line - 2] || ''
119+
) + '\n';
120+
sourceCodeHighlight +=
121+
red(position.line.toString().padStart(lnrl)) +
122+
gray(' | ') +
123+
sourceLines[position.line - 1] +
124+
'\n';
125+
sourceCodeHighlight +=
126+
gray('| '.padStart(lnrl + 3)) +
127+
red('^'.padStart(position.column + 1)) +
128+
'\n';
129+
sourceCodeHighlight +=
130+
gray(
131+
(position.line + 1).toString().padStart(lnrl) +
132+
' | ' +
133+
sourceLines[position.line + 0] || ''
134+
) + '\n';
135+
sourceCodeHighlight +=
136+
gray(
137+
(position.line + 2).toString().padStart(lnrl) +
138+
' | ' +
139+
sourceLines[position.line + 1] || ''
140+
) + '\n';
94141
}
142+
} else {
143+
position = {
144+
source: stack.getFileName(),
145+
line: stack.getLineNumber(),
146+
column: stack.getColumnNumber(),
147+
};
95148
}
96149

97150
process.stderr.write('\n');
98-
process.stderr.write(red(`${errorMessage}\n`));
99-
process.stderr.write(`method: ${methodName}\n`);
100-
if (sourceMapContent) {
101-
process.stderr.write(
102-
`at: ${sourcePath}:${position.line}:${position.column}\n`
103-
);
104-
process.stderr.write('\n');
105-
process.stderr.write('Source code:\n\n');
106-
process.stderr.write(sourceCodeHighlight);
107-
process.stderr.write('\n');
108-
} else {
109-
process.stderr.write(stack.toString() + '\n');
110-
}
151+
process.stderr.write(`[PrerenderError]: ${red(`${errorMessage}\n`)}`);
152+
process.stderr.write(
153+
` --> ${position.source}:${position.line}:${
154+
position.column
155+
} (${methodName || '<anonymous>'})\n`
156+
);
157+
process.stderr.write(sourceCodeHighlight + '\n');
158+
process.stderr.write(red(`${err.stack}\n`));
159+
111160
process.stderr.write(
112161
`This ${
113162
isReferenceError ? 'is most likely' : 'could be'
114163
} caused by using DOM or Web APIs.\n`
115164
);
116165
process.stderr.write(
117-
`Pre-render runs in node and has no access to globals available in browsers.\n\n`
166+
`Pre-render runs in node and has no access to globals available in browsers.\n`
118167
);
119168
process.stderr.write(
120169
`Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }'\n`
@@ -125,7 +174,7 @@ async function handlePrerenderError(err, env, stack, entry) {
125174
}
126175
process.stderr.write('\n');
127176
process.stderr.write(
128-
`Alternatively use 'preact build --no-prerender' to disable prerendering.\n\n`
177+
'Alternatively use `preact build --no-prerender` to disable prerendering.\n'
129178
);
130179
process.stderr.write(
131180
'See https://github.com/developit/preact-cli#pre-rendering for further information.'

0 commit comments

Comments
 (0)