Skip to content

Commit 1c8dd00

Browse files
feat: outdent error message
1 parent 73ba72b commit 1c8dd00

File tree

3 files changed

+71
-88
lines changed

3 files changed

+71
-88
lines changed

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

+65-88
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ const { readFileSync } = require('fs');
44
const stackTrace = require('stack-trace');
55
const { SourceMapConsumer } = require('source-map');
66
const { error, info } = require('../../util');
7+
const outdent = require('outdent');
78

8-
module.exports = function(env, params) {
9+
module.exports = function prerender(env, params) {
910
params = params || {};
1011

1112
let entry = resolve(env.dest, './ssr-build/ssr-bundle.js');
@@ -47,12 +48,26 @@ module.exports = function(env, params) {
4748
}
4849
};
4950

51+
function getLines(env, position) {
52+
let sourcePath;
53+
try {
54+
sourcePath = resolve(env.src, position.source);
55+
return readFileSync(sourcePath, 'utf-8').split('\n');
56+
} catch (err) {
57+
try {
58+
sourcePath = resolve(env.cwd, position.source);
59+
return readFileSync(sourcePath, 'utf-8').split('\n');
60+
} catch (err) {
61+
error(`Unable to read file: ${sourcePath} (${position.source})\n`);
62+
}
63+
}
64+
}
65+
5066
async function handlePrerenderError(err, env, stack, entry) {
5167
const errorMessage = err.toString();
5268
const isReferenceError = errorMessage.startsWith('ReferenceError');
5369
const methodName = stack.getMethodName();
5470
const fileName = stack.getFileName().replace(/\\/g, '/');
55-
let sourceCodeHighlight = '';
5671

5772
let position;
5873

@@ -65,7 +80,9 @@ async function handlePrerenderError(err, env, stack, entry) {
6580
};
6681
} else {
6782
try {
68-
const sourceMapContent = JSON.parse(readFileSync(`${entry}.map`));
83+
const sourceMapContent = JSON.parse(
84+
readFileSync(`${entry}.map`, 'utf-8')
85+
);
6986

7087
await SourceMapConsumer.with(sourceMapContent, null, consumer => {
7188
position = consumer.originalPositionFor({
@@ -75,7 +92,6 @@ async function handlePrerenderError(err, env, stack, entry) {
7592
});
7693
} catch (err) {
7794
error(`Unable to read sourcemap: ${entry}.map`);
78-
return;
7995
}
8096
}
8197

@@ -89,59 +105,6 @@ async function handlePrerenderError(err, env, stack, entry) {
89105
.replace(/^(.*?\/node_modules\/(@[^/]+\/)?[^/]+)(\/.*)$/, '$1')
90106
);
91107
info(position.source);
92-
93-
let sourcePath;
94-
let sourceLines;
95-
try {
96-
sourcePath = resolve(env.src, position.source);
97-
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
98-
} catch (err) {
99-
try {
100-
sourcePath = resolve(env.cwd, position.source);
101-
// sourcePath = require.resolve(position.source);
102-
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
103-
} catch (err) {
104-
error(`Unable to read file: ${sourcePath} (${position.source})\n`);
105-
return;
106-
}
107-
}
108-
109-
if (sourceLines) {
110-
let lnrl = position.line.toString().length + 1;
111-
sourceCodeHighlight +=
112-
gray(
113-
(position.line - 2 || '').toString().padStart(lnrl) +
114-
' | ' +
115-
sourceLines[position.line - 3] || ''
116-
) + '\n';
117-
sourceCodeHighlight +=
118-
gray(
119-
(position.line - 1 || '').toString().padStart(lnrl) +
120-
' | ' +
121-
sourceLines[position.line - 2] || ''
122-
) + '\n';
123-
sourceCodeHighlight +=
124-
red(position.line.toString().padStart(lnrl)) +
125-
gray(' | ') +
126-
sourceLines[position.line - 1] +
127-
'\n';
128-
sourceCodeHighlight +=
129-
gray('| '.padStart(lnrl + 3)) +
130-
red('^'.padStart(position.column + 1)) +
131-
'\n';
132-
sourceCodeHighlight +=
133-
gray(
134-
(position.line + 1).toString().padStart(lnrl) +
135-
' | ' +
136-
sourceLines[position.line + 0] || ''
137-
) + '\n';
138-
sourceCodeHighlight +=
139-
gray(
140-
(position.line + 2).toString().padStart(lnrl) +
141-
' | ' +
142-
sourceLines[position.line + 1] || ''
143-
) + '\n';
144-
}
145108
} else {
146109
position = {
147110
source: stack.getFileName(),
@@ -150,37 +113,51 @@ async function handlePrerenderError(err, env, stack, entry) {
150113
};
151114
}
152115

153-
process.stderr.write('\n');
154-
process.stderr.write(`[PrerenderError]: ${red(`${errorMessage}\n`)}`);
155-
process.stderr.write(
156-
` --> ${position.source}:${position.line}:${
157-
position.column
158-
} (${methodName || '<anonymous>'})\n`
159-
);
160-
process.stderr.write(sourceCodeHighlight + '\n');
161-
process.stderr.write(red(`${err.stack}\n`));
162-
163-
process.stderr.write(
164-
`This ${
165-
isReferenceError ? 'is most likely' : 'could be'
166-
} caused by using DOM or Web APIs.\n`
167-
);
168-
process.stderr.write(
169-
`Pre-render runs in node and has no access to globals available in browsers.\n`
170-
);
171-
process.stderr.write(
172-
`Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }'\n`
173-
);
174-
175-
if (methodName === 'componentWillMount') {
176-
process.stderr.write(`or place logic in 'componentDidMount' method.\n`);
116+
const sourceLines = getLines(env, position);
117+
118+
let sourceCodeHighlight = '';
119+
if (sourceLines) {
120+
const lnrl = position.line.toString().length + 2;
121+
const line = position.line;
122+
const un = undefined;
123+
124+
const pad = l =>
125+
(l === undefined ? '' : (line + l || '') + '').padStart(lnrl);
126+
127+
sourceCodeHighlight = gray(outdent`
128+
${pad(-2)} | ${sourceLines[line - 3] || ''}
129+
${pad(-1)} | ${sourceLines[line - 2] || ''}
130+
${pad(-0)} | ${sourceLines[line - 1] || ''}
131+
${pad(un)} | ${red('^'.padStart(position.column + 1))}
132+
${pad(+1)} | ${sourceLines[line + 0] || ''}
133+
${pad(+2)} | ${sourceLines[line + 1] || ''}
134+
`);
177135
}
178-
process.stderr.write('\n');
179-
process.stderr.write(
180-
'Alternatively use `preact build --no-prerender` to disable prerendering.\n'
181-
);
182-
process.stderr.write(
183-
'See https://github.com/developit/preact-cli#pre-rendering for further information.'
184-
);
136+
137+
const stderr = process.stderr.write.bind(process.stderr);
138+
139+
stderr('\n');
140+
stderr(outdent`
141+
[PrerenderError]: ${red(`${errorMessage}`)}
142+
--> ${position.source}:${position.line}:${position.column} (${methodName ||
143+
'<anonymous>'})
144+
${sourceCodeHighlight}
145+
146+
${red(`${err.stack}`)}
147+
148+
This ${
149+
isReferenceError ? 'is most likely' : 'could be'
150+
} caused by using DOM or Web APIs.
151+
Pre-render runs in node and has no access to globals available in browsers.
152+
Consider wrapping code producing error in: 'if (typeof window !== "undefined") { ... }\
153+
${
154+
methodName === 'componentWillMount'
155+
? `\nor place logic in 'componentDidMount' method.`
156+
: ''
157+
}
158+
159+
Alternatively use \`preact build --no-prerender\` to disable prerendering.
160+
See https://github.com/developit/preact-cli#pre-rendering for further information.
161+
`);
185162
process.exit(1);
186163
}

packages/cli/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"minimatch": "^3.0.3",
106106
"optimize-css-assets-webpack-plugin": "^5.0.1",
107107
"ora": "^3.4.0",
108+
"outdent": "^0.7.0",
108109
"postcss-loader": "^3.0.0",
109110
"progress-bar-webpack-plugin": "^1.12.1",
110111
"promise-polyfill": "^8.1.0",

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -10070,6 +10070,11 @@ osenv@0, osenv@^0.1.4, osenv@^0.1.5:
1007010070
os-homedir "^1.0.0"
1007110071
os-tmpdir "^1.0.0"
1007210072

10073+
outdent@^0.7.0:
10074+
version "0.7.0"
10075+
resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.7.0.tgz#cfd1f1956305141e0cf3e898ada6547373c1997a"
10076+
integrity sha512-Ue462G+UIFoyQmOzapGIKWS3d/9NHeD/018WGEDZIhN2/VaQpVXbofMcZX0socv1fw4/tmEn7Vd3McOdPZfKzQ==
10077+
1007310078
p-cancelable@^1.0.0:
1007410079
version "1.1.0"
1007510080
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"

0 commit comments

Comments
 (0)