Skip to content

Commit

Permalink
fix: Improve prerender errors (#1756)
Browse files Browse the repository at this point in the history
* fix: Improve prerender error logic

* fix: Disable SSR JS minification for better stack trace positions

* test: Fix output sizes

* docs: Adding changeset
  • Loading branch information
rschristian committed Dec 18, 2022
1 parent 696ccc0 commit a41d498
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 89 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-insects-yawn.md
@@ -0,0 +1,5 @@
---
'preact-cli': patch
---

Improves prerender error message's output and positioning
160 changes: 73 additions & 87 deletions packages/cli/src/lib/webpack/prerender.js
Expand Up @@ -45,112 +45,98 @@ async function handlePrerenderError(err, env, stack, entry) {
let errorMessage = err.toString();
let isReferenceError = errorMessage.startsWith('ReferenceError');
let methodName = stack.getMethodName();
let sourceMapContent, position, sourcePath, sourceLines, sourceCodeHighlight;

process.stderr.write('\n');
process.stderr.write(red(`\n${errorMessage}\n`));
try {
sourceMapContent = JSON.parse(readFileSync(`${entry}.map`, 'utf-8'));
} catch (err) {
process.stderr.write(red(`\n\nUnable to read sourcemap: ${entry}.map\n`));
}

// If a methodName exists, it's likely user code
if (methodName) {
let sourceMapContent,
position,
sourcePath,
sourceLines,
sourceCodeHighlight;
try {
sourceMapContent = JSON.parse(readFileSync(`${entry}.map`, 'utf-8'));
} catch (err) {
process.stderr.write(red(`\n\nUnable to read sourcemap: ${entry}.map\n`));
}
if (sourceMapContent) {
position = await SourceMapConsumer.with(sourceMapContent, null, consumer =>
consumer.originalPositionFor({
line: stack.getLineNumber(),
column: stack.getColumnNumber(),
})
);

if (sourceMapContent) {
await SourceMapConsumer.with(sourceMapContent, null, consumer => {
position = consumer.originalPositionFor({
line: stack.getLineNumber(),
column: stack.getColumnNumber(),
});
});

if (position.source) {
position.source = position.source
.replace('webpack://', '.')
.replace(/^.*~\/((?:@[^/]+\/)?[^/]+)/, (s, name) =>
require
.resolve(name)
.replace(/^(.*?\/node_modules\/(@[^/]+\/)?[^/]+)(\/.*)$/, '$1')
);

sourcePath = resolve(env.src, position.source);
sourceLines;
if (position.source) {
position.source = position.source
.replace('webpack://', '.')
.replace(/^.*~\/((?:@[^/]+\/)?[^/]+)/, (s, name) =>
require
.resolve(name)
.replace(/^(.*?\/node_modules\/(@[^/]+\/)?[^/]+)(\/.*)$/, '$1')
);

sourcePath = resolve(env.src, position.source);
sourceLines;
try {
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
} catch (err) {
try {
sourceLines = readFileSync(sourcePath, 'utf-8').split('\n');
sourceLines = readFileSync(
require.resolve(position.source),
'utf-8'
).split('\n');
} catch (err) {
try {
sourceLines = readFileSync(
require.resolve(position.source),
'utf-8'
).split('\n');
} catch (err) {
process.stderr.write(
red(`\n\nUnable to read file: ${sourcePath}\n`)
);
}
process.stderr.write(red(`\n\nUnable to read file: ${sourcePath}\n`));
}
sourceCodeHighlight = '';
}
sourceCodeHighlight = '';
}

if (sourceLines) {
for (var i = -4; i <= 4; i++) {
let color = i === 0 ? red : yellow;
let line = position.line + i;
let sourceLine = sourceLines[line - 1];
sourceCodeHighlight += sourceLine ? `${color(sourceLine)}\n` : '';
}
if (sourceLines) {
for (var i = -4; i <= 4; i++) {
let color = i === 0 ? red : yellow;
let line = position.line + i;
let sourceLine = sourceLines[line - 1];
sourceCodeHighlight += sourceLine ? `${color(sourceLine)}\n` : '\n';
}
}
}

process.stderr.write('\n');
process.stderr.write(red(`\n${errorMessage}\n`));

if (sourceMapContent && sourceCodeHighlight) {
process.stderr.write(`method: ${methodName}\n`);
if (sourceMapContent & sourceCodeHighlight) {
process.stderr.write(
`at: ${sourcePath}:${position.line}:${position.column}\n`
);
process.stderr.write('\n');
process.stderr.write('Source code:\n\n');
process.stderr.write(sourceCodeHighlight);
process.stderr.write('\n');
} else {
process.stderr.write('\n');
process.stderr.write('Stack:\n\n');
process.stderr.write(JSON.stringify(stack, null, 4) + '\n');
}
} else {
process.stderr.write(
yellow(
'Cannot determine error position. This most likely means it originated in node_modules.\n\n'
)
`at: ${sourcePath}:${position.line}:${position.column}\n`
);
process.stderr.write('\n');
process.stderr.write('Source code:\n');
process.stderr.write(sourceCodeHighlight);
process.stderr.write('\n');
} else {
process.stderr.write('\n');
process.stderr.write('Stack:\n\n');
process.stderr.write(JSON.stringify(stack, null, 4) + '\n');
}

process.stderr.write(
`This ${
const message = `
This ${
isReferenceError ? 'is most likely' : 'could be'
} caused by using DOM or Web APIs.\n`
);
process.stderr.write(
'Pre-render runs in node and has no access to globals available in browsers.\n\n'
);
process.stderr.write(
'Consider wrapping code producing error in: "if (typeof window !== "undefined") { ... }"\n'
);
} caused by using DOM or Web APIs.
Pre-rendering runs in Node and therefore has no access browser globals.
Consider wrapping the code producing the error in: 'if (typeof window !== "undefined") { ... }'
${
methodName === 'componentWillMount'
? 'or place logic in "componentDidMount" method.'
: ''
}
if (methodName === 'componentWillMount') {
process.stderr.write('or place logic in "componentDidMount" method.\n');
}
process.stderr.write('\n');
process.stderr.write(
'Alternatively use "preact build --no-prerender" to disable prerendering.\n\n'
);
Alternatively, disable prerendering altogether with 'preact build --no-prerender'.
See https://github.com/preactjs/preact-cli#pre-rendering for further information.
`;
process.stderr.write(
'See https://github.com/preactjs/preact-cli#pre-rendering for further information.\n\n'
message
.trim()
.replace(/^\t+/gm, '')
.replace(/\n\n\n/, '\n\n') + '\n\n'
);
process.exit(1);
}
3 changes: 3 additions & 0 deletions packages/cli/src/lib/webpack/webpack-server-config.js
Expand Up @@ -26,6 +26,9 @@ function serverConfig(env) {
async: resolve(__dirname, './dummy-loader'),
},
},
optimization: {
minimize: false,
},
};
}

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/tests/images/build.js
Expand Up @@ -13,8 +13,8 @@ exports.default = Object.assign({}, common, {
'assets/favicon.ico': 15086,
'ssr-build/ssr-bundle.89e23.css': 1281,
'ssr-build/ssr-bundle.89e23.css.map': 2070,
'ssr-build/ssr-bundle.js': 11937,
'ssr-build/ssr-bundle.js.map': 42893,
'ssr-build/ssr-bundle.js': 28830,
'ssr-build/ssr-bundle.js.map': 52686,
'ssr-build/asset-manifest.json': 178,
'bundle.259c5.css': 901,
'bundle.f79fb.js': 21429,
Expand Down

0 comments on commit a41d498

Please sign in to comment.