|
1 | 1 | import crypto from 'crypto'
|
2 | 2 | import fs from 'fs'
|
| 3 | +import chalk from 'chalk' |
3 | 4 | import { IncomingMessage, ServerResponse } from 'http'
|
4 | 5 | import { Worker } from 'jest-worker'
|
5 | 6 | import AmpHtmlValidator from 'next/dist/compiled/amphtml-validator'
|
@@ -47,6 +48,12 @@ import {
|
47 | 48 | loadDefaultErrorComponents,
|
48 | 49 | } from '../load-components'
|
49 | 50 | import { DecodeError } from '../../shared/lib/utils'
|
| 51 | +import { parseStack } from '@next/react-dev-overlay/lib/internal/helpers/parseStack' |
| 52 | +import { |
| 53 | + createOriginalStackFrame, |
| 54 | + getSourceById, |
| 55 | +} from '@next/react-dev-overlay/lib/middleware' |
| 56 | +import * as Log from '../../build/output/log' |
50 | 57 |
|
51 | 58 | // Load ReactDevOverlay only when needed
|
52 | 59 | let ReactDevOverlayImpl: React.FunctionComponent
|
@@ -325,6 +332,15 @@ export default class DevServer extends Server {
|
325 | 332 | )
|
326 | 333 | // This is required by the tracing subsystem.
|
327 | 334 | setGlobal('telemetry', telemetry)
|
| 335 | + |
| 336 | + process.on('unhandledRejection', (reason) => { |
| 337 | + this.logErrorWithOriginalStack(reason, 'unhandledRejection').catch( |
| 338 | + () => {} |
| 339 | + ) |
| 340 | + }) |
| 341 | + process.on('uncaughtException', (err) => { |
| 342 | + this.logErrorWithOriginalStack(err, 'uncaughtException').catch(() => {}) |
| 343 | + }) |
328 | 344 | }
|
329 | 345 |
|
330 | 346 | protected async close(): Promise<void> {
|
@@ -431,8 +447,85 @@ export default class DevServer extends Server {
|
431 | 447 | // if they should match against the basePath or not
|
432 | 448 | parsedUrl.pathname = originalPathname
|
433 | 449 | }
|
| 450 | + try { |
| 451 | + return await super.run(req, res, parsedUrl) |
| 452 | + } catch (err) { |
| 453 | + res.statusCode = 500 |
| 454 | + try { |
| 455 | + this.logErrorWithOriginalStack(err).catch(() => {}) |
| 456 | + return await this.renderError(err, req, res, pathname!, { |
| 457 | + __NEXT_PAGE: err?.page || pathname, |
| 458 | + }) |
| 459 | + } catch (internalErr) { |
| 460 | + console.error(internalErr) |
| 461 | + res.end('Internal Server Error') |
| 462 | + } |
| 463 | + } |
| 464 | + } |
434 | 465 |
|
435 |
| - return super.run(req, res, parsedUrl) |
| 466 | + private async logErrorWithOriginalStack( |
| 467 | + possibleError?: any, |
| 468 | + type?: 'unhandledRejection' | 'uncaughtException' |
| 469 | + ) { |
| 470 | + let usedOriginalStack = false |
| 471 | + |
| 472 | + if (possibleError?.name && possibleError?.stack && possibleError?.message) { |
| 473 | + const err: Error & { stack: string } = possibleError |
| 474 | + try { |
| 475 | + const frames = parseStack(err.stack) |
| 476 | + const frame = frames[0] |
| 477 | + |
| 478 | + if (frame.lineNumber && frame?.file) { |
| 479 | + const compilation = this.hotReloader?.serverStats?.compilation |
| 480 | + const moduleId = frame.file!.replace( |
| 481 | + /^(webpack-internal:\/\/\/|file:\/\/)/, |
| 482 | + '' |
| 483 | + ) |
| 484 | + |
| 485 | + const source = await getSourceById( |
| 486 | + !!frame.file?.startsWith(sep) || !!frame.file?.startsWith('file:'), |
| 487 | + moduleId, |
| 488 | + compilation, |
| 489 | + this.hotReloader!.isWebpack5 |
| 490 | + ) |
| 491 | + |
| 492 | + const originalFrame = await createOriginalStackFrame({ |
| 493 | + line: frame.lineNumber!, |
| 494 | + column: frame.column, |
| 495 | + source, |
| 496 | + frame, |
| 497 | + modulePath: moduleId, |
| 498 | + rootDirectory: this.dir, |
| 499 | + }) |
| 500 | + |
| 501 | + if (originalFrame) { |
| 502 | + const { originalCodeFrame, originalStackFrame } = originalFrame |
| 503 | + const { file, lineNumber, column, methodName } = originalStackFrame |
| 504 | + |
| 505 | + console.error( |
| 506 | + chalk.red('error') + |
| 507 | + ' - ' + |
| 508 | + `${file} (${lineNumber}:${column}) @ ${methodName}` |
| 509 | + ) |
| 510 | + console.error(`${chalk.red(err.name)}: ${err.message}`) |
| 511 | + console.error(originalCodeFrame) |
| 512 | + usedOriginalStack = true |
| 513 | + } |
| 514 | + } |
| 515 | + } catch (_) { |
| 516 | + // failed to load original stack using source maps |
| 517 | + // this un-actionable by users so we don't show the |
| 518 | + // internal error and only show the provided stack |
| 519 | + } |
| 520 | + } |
| 521 | + |
| 522 | + if (!usedOriginalStack) { |
| 523 | + if (type) { |
| 524 | + Log.error(`${type}:`, possibleError) |
| 525 | + } else { |
| 526 | + Log.error(possibleError) |
| 527 | + } |
| 528 | + } |
436 | 529 | }
|
437 | 530 |
|
438 | 531 | // override production loading of routes-manifest
|
|
0 commit comments