Use eval-source-map
for Server Side Errors (#13123)
This switches to faster source maps in development for the server-side compilation on macOS. We still need to figure out a story for Windows.
This commit is contained in:
parent
99ee63e110
commit
d64e2e1cbe
6 changed files with 54 additions and 17 deletions
|
@ -12,10 +12,10 @@ export const base = curry(function base(
|
|||
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
if (ctx.isDevelopment) {
|
||||
if (ctx.isServer || process.platform === 'win32') {
|
||||
if (process.platform === 'win32') {
|
||||
// Non-eval based source maps are slow to rebuild, so we only enable
|
||||
// them for the server and Windows. Unfortunately, eval source maps
|
||||
// are not supported by Node.js, and are slow on Windows.
|
||||
// them for Windows. Unfortunately, eval source maps are flagged as
|
||||
// suspicious by Windows Defender and block HMR.
|
||||
config.devtool = 'inline-source-map'
|
||||
} else {
|
||||
// `eval-source-map` provides full-fidelity source maps for the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import reactDevOverlayMiddleware from '@next/react-dev-overlay/lib/middleware'
|
||||
import { getOverlayMiddleware } from '@next/react-dev-overlay/lib/middleware'
|
||||
import { NextHandleFunction } from 'connect'
|
||||
import { IncomingMessage, ServerResponse } from 'http'
|
||||
import WebpackDevMiddleware from 'next/dist/compiled/webpack-dev-middleware'
|
||||
|
@ -9,7 +9,7 @@ import webpack from 'webpack'
|
|||
import { createEntrypoints, createPagesMapping } from '../build/entries'
|
||||
import { watchCompilers } from '../build/output'
|
||||
import getBaseWebpackConfig from '../build/webpack-config'
|
||||
import { NEXT_PROJECT_ROOT_DIST_CLIENT, API_ROUTE } from '../lib/constants'
|
||||
import { API_ROUTE, NEXT_PROJECT_ROOT_DIST_CLIENT } from '../lib/constants'
|
||||
import { recursiveDelete } from '../lib/recursive-delete'
|
||||
import {
|
||||
BLOCKED_PAGES,
|
||||
|
@ -133,6 +133,7 @@ export default class HotReloader {
|
|||
private initialized: boolean
|
||||
private config: any
|
||||
private stats: any
|
||||
private serverStats: any
|
||||
private serverPrevDocumentHash: string | null
|
||||
private prevChunkNames?: Set<any>
|
||||
private onDemandEntries: any
|
||||
|
@ -160,6 +161,7 @@ export default class HotReloader {
|
|||
this.webpackHotMiddleware = null
|
||||
this.initialized = false
|
||||
this.stats = null
|
||||
this.serverStats = null
|
||||
this.serverPrevDocumentHash = null
|
||||
|
||||
this.config = config
|
||||
|
@ -309,7 +311,11 @@ export default class HotReloader {
|
|||
const buildTools = await this.prepareBuildTools(multiCompiler)
|
||||
this.assignBuildTools(buildTools)
|
||||
|
||||
this.stats = ((await this.waitUntilValid()) as any).stats[0]
|
||||
// [Client, Server]
|
||||
;[
|
||||
this.stats,
|
||||
this.serverStats,
|
||||
] = ((await this.waitUntilValid()) as any).stats
|
||||
}
|
||||
|
||||
async stop(
|
||||
|
@ -328,6 +334,7 @@ export default class HotReloader {
|
|||
|
||||
async reload(): Promise<void> {
|
||||
this.stats = null
|
||||
this.serverStats = null
|
||||
|
||||
await this.clean()
|
||||
|
||||
|
@ -335,7 +342,11 @@ export default class HotReloader {
|
|||
const compiler = webpack(configs)
|
||||
|
||||
const buildTools = await this.prepareBuildTools(compiler)
|
||||
this.stats = await this.waitUntilValid(buildTools.webpackDevMiddleware)
|
||||
// [Client, Server]
|
||||
;[
|
||||
this.stats,
|
||||
this.serverStats,
|
||||
] = ((await this.waitUntilValid()) as any).stats
|
||||
|
||||
const oldWebpackDevMiddleware = this.webpackDevMiddleware
|
||||
|
||||
|
@ -361,9 +372,10 @@ export default class HotReloader {
|
|||
onDemandEntries.middleware(),
|
||||
webpackHotMiddleware,
|
||||
errorOverlayMiddleware({ dir: this.dir }),
|
||||
reactDevOverlayMiddleware({
|
||||
getOverlayMiddleware({
|
||||
rootDirectory: this.dir,
|
||||
stats: () => this.stats,
|
||||
serverStats: () => this.serverStats,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
@ -375,6 +387,7 @@ export default class HotReloader {
|
|||
multiCompiler.compilers[1].hooks.done.tap(
|
||||
'NextjsHotReloaderForServer',
|
||||
(stats) => {
|
||||
this.serverStats = stats
|
||||
if (!this.initialized) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -64,7 +64,10 @@ async function getErrorByType(
|
|||
id,
|
||||
runtime: true,
|
||||
error: event.reason,
|
||||
frames: await getOriginalStackFrames(event.frames),
|
||||
frames: await getOriginalStackFrames(
|
||||
isNodeError(event.reason),
|
||||
event.frames
|
||||
),
|
||||
}
|
||||
}
|
||||
default: {
|
||||
|
|
|
@ -30,15 +30,22 @@ export type OriginalStackFrame =
|
|||
originalCodeFrame: null
|
||||
}
|
||||
|
||||
export function getOriginalStackFrames(frames: StackFrame[]) {
|
||||
return Promise.all(frames.map((frame) => getOriginalStackFrame(frame)))
|
||||
export function getOriginalStackFrames(
|
||||
isServerSide: boolean,
|
||||
frames: StackFrame[]
|
||||
) {
|
||||
return Promise.all(
|
||||
frames.map((frame) => getOriginalStackFrame(isServerSide, frame))
|
||||
)
|
||||
}
|
||||
|
||||
export function getOriginalStackFrame(
|
||||
isServerSide: boolean,
|
||||
source: StackFrame
|
||||
): Promise<OriginalStackFrame> {
|
||||
async function _getOriginalStackFrame(): Promise<OriginalStackFrame> {
|
||||
const params = new URLSearchParams()
|
||||
params.append('isServerSide', String(isServerSide))
|
||||
for (const key in source) {
|
||||
params.append(key, (source[key] ?? '').toString())
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { launchEditor } from './internal/helpers/launchEditor'
|
|||
export type OverlayMiddlewareOptions = {
|
||||
rootDirectory: string
|
||||
stats(): webpack.Stats
|
||||
serverStats(): webpack.Stats
|
||||
}
|
||||
|
||||
export type OriginalStackFrameResponse = {
|
||||
|
@ -26,7 +27,11 @@ export type OriginalStackFrameResponse = {
|
|||
type Source = { map: () => RawSourceMap } | null
|
||||
|
||||
function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
||||
async function getSourceById(isFile: boolean, id: string): Promise<Source> {
|
||||
async function getSourceById(
|
||||
isServerSide: boolean,
|
||||
isFile: boolean,
|
||||
id: string
|
||||
): Promise<Source> {
|
||||
if (isFile) {
|
||||
const fileContent: string | null = await fs
|
||||
.readFile(id, 'utf-8')
|
||||
|
@ -49,7 +54,9 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
|||
}
|
||||
|
||||
try {
|
||||
const compilation = options.stats()?.compilation
|
||||
const compilation = isServerSide
|
||||
? options.serverStats()?.compilation
|
||||
: options.stats()?.compilation
|
||||
const m = compilation?.modules?.find((m) => m.id === id)
|
||||
return (
|
||||
m?.source(
|
||||
|
@ -71,7 +78,9 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
|||
const { pathname, query } = url.parse(req.url, true)
|
||||
|
||||
if (pathname === '/__nextjs_original-stack-frame') {
|
||||
const frame = (query as unknown) as StackFrame
|
||||
const frame = (query as unknown) as StackFrame & {
|
||||
isServerSide: 'true' | 'false'
|
||||
}
|
||||
if (
|
||||
!(
|
||||
(frame.file?.startsWith('webpack-internal:///') ||
|
||||
|
@ -84,6 +93,7 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
|||
return res.end()
|
||||
}
|
||||
|
||||
const isServerSide = frame.isServerSide === 'true'
|
||||
const moduleId: string = frame.file.replace(
|
||||
/^(webpack-internal:\/\/\/|file:\/\/)/,
|
||||
''
|
||||
|
@ -91,7 +101,11 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
|||
|
||||
let source: Source
|
||||
try {
|
||||
source = await getSourceById(frame.file.startsWith('file:'), moduleId)
|
||||
source = await getSourceById(
|
||||
isServerSide,
|
||||
frame.file.startsWith('file:'),
|
||||
moduleId
|
||||
)
|
||||
} catch (err) {
|
||||
console.log('Failed to get source map:', err)
|
||||
res.statusCode = 500
|
||||
|
@ -212,4 +226,4 @@ function getOverlayMiddleware(options: OverlayMiddlewareOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
export default getOverlayMiddleware
|
||||
export { getOverlayMiddleware }
|
||||
|
|
|
@ -360,7 +360,7 @@ test('module init error not shown', async () => {
|
|||
|
||||
expect(await session.hasRedbox(true)).toBe(true)
|
||||
expect(await session.getRedboxSource()).toMatchInlineSnapshot(`
|
||||
"index.js (4:12) @ Module../index.js
|
||||
"index.js (4:12) @ eval
|
||||
|
||||
2 | // top offset for snapshot
|
||||
3 | import * as React from 'react';
|
||||
|
|
Loading…
Reference in a new issue