hmr error improvements (#30616)

Co-authored-by: Hannes Bornö <hannes.borno@vercel.com>
Co-authored-by: Hannes Bornö <borno.hannes@gmail.com>
This commit is contained in:
Tobias Koppers 2023-01-06 20:35:16 +01:00 committed by GitHub
parent 3c87e1b52a
commit 3b91ca98a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 231 additions and 40 deletions

View file

@ -36,3 +36,4 @@ bench/nested-deps/pages/**
bench/nested-deps/components/**
packages/next-bundle-analyzer/index.d.ts
examples/with-typescript-graphql/lib/gql/
test/development/basic/hmr/components/parse-error.js

View file

@ -22,6 +22,7 @@ packages/next-codemod/**/*.d.ts
packages/next-env/**/*.d.ts
test-timings.json
test/**/out/**
test/development/basic/hmr/components/parse-error.js
bench/nested-deps/pages/**/*
bench/nested-deps/components/**/*
pnpm-lock.yaml

View file

@ -7,6 +7,7 @@ packages/next/bundles/webpack/packages/*.runtime.js
lerna.json
packages/next-codemod/transforms/__testfixtures__/**/*
packages/next-codemod/transforms/__tests__/**/*
test/development/basic/hmr/components/parse-error.js
pnpm-lock.yaml
.github/actions/issue-validator/index.mjs
**/convex/_generated/**

View file

@ -64,7 +64,7 @@ function formatMessage(
(message.file ? stripAnsi(message.file) + '\n' : '') +
body +
(message.details && verbose ? '\n' + message.details : '') +
(filteredModuleTrace && filteredModuleTrace.length && verbose
(filteredModuleTrace && filteredModuleTrace.length
? (importTraceNote || '\n\nImport trace for requested module:') +
filteredModuleTrace
.map((trace: any) => `\n${trace.moduleName}`)
@ -161,6 +161,12 @@ function formatMessage(
''
) // at ... ...:x:y
message = message.replace(/^\s*at\s<anonymous>(\n|$)/gm, '') // at <anonymous>
message = message.replace(
/File was processed with these loaders:\n(.+[\\/](next[\\/]dist[\\/].+|@next[\\/]react-refresh-utils[\\/]loader)\.js\n)*You may need an additional loader to handle the result of these loaders.\n/g,
''
)
lines = message.split('\n')
}

View file

@ -192,6 +192,7 @@ export class WebpackHotMiddleware {
hash: true,
warnings: true,
errors: true,
moduleTrace: true,
})
this.eventStream.publish({

View file

@ -111,17 +111,16 @@ const matchNextPageBundleRequest = getPathMatch(
'/_next/static/chunks/pages/:path*.js(\\.map|)'
)
// Recursively look up the issuer till it ends up at the root
// Iteratively look up the issuer till it ends up at the root
function findEntryModule(
compilation: webpack.Compilation,
issuerModule: any
module: webpack.Module,
compilation: webpack.Compilation
): any {
const issuer = compilation.moduleGraph.getIssuer(issuerModule)
if (issuer) {
return findEntryModule(compilation, issuer)
for (;;) {
const issuer = compilation.moduleGraph.getIssuer(module)
if (!issuer) return module
module = issuer
}
return issuerModule
}
function erroredPages(compilation: webpack.Compilation) {
@ -131,7 +130,7 @@ function erroredPages(compilation: webpack.Compilation) {
continue
}
const entryModule = findEntryModule(compilation, error.module)
const entryModule = findEntryModule(error.module, compilation)
const { name } = entryModule
if (!name) {
continue

View file

@ -7,7 +7,12 @@ SassError: Expected expression.
1 │ .button { font-size: :5px; }
│ ^
index.module.scss 1:22 root stylesheet"
index.module.scss 1:22 root stylesheet
Import trace for requested module:
./index.module.scss
./index.js
./app/page.js"
`;
exports[`ReactRefreshLogBox app scss syntax errors 2`] = `

View file

@ -88,7 +88,11 @@ Error:
Caused by:
0: failed to process input file
1: Syntax Error"
1: Syntax Error
Import trace for requested module:
./index.js
./app/page.js"
`;
exports[`ReactRefreshLogBox app module init error not shown 1`] = `
@ -165,7 +169,11 @@ Error:
Caused by:
0: failed to process input file
1: error was recoverable, but proceeding would result in wrong codegen
2: Syntax Error"
2: Syntax Error
Import trace for requested module:
./index.js
./app/page.js"
`;
exports[`ReactRefreshLogBox app syntax > runtime error 3`] = `
@ -183,7 +191,11 @@ Error:
Caused by:
0: failed to process input file
1: error was recoverable, but proceeding would result in wrong codegen
2: Syntax Error"
2: Syntax Error
Import trace for requested module:
./index.js
./app/page.js"
`;
exports[`ReactRefreshLogBox app unterminated JSX 1`] = `
@ -209,5 +221,9 @@ Error:
Caused by:
0: failed to process input file
1: Syntax Error"
1: Syntax Error
Import trace for requested module:
./index.js
./app/page.js"
`;

View file

@ -76,7 +76,10 @@ Error:
Caused by:
0: failed to process input file
1: Syntax Error"
1: Syntax Error
Import trace for requested module:
./index.js"
`;
exports[`ReactRefreshLogBox module init error not shown 1`] = `
@ -153,7 +156,10 @@ Error:
Caused by:
0: failed to process input file
1: error was recoverable, but proceeding would result in wrong codegen
2: Syntax Error"
2: Syntax Error
Import trace for requested module:
./index.js"
`;
exports[`ReactRefreshLogBox syntax > runtime error 3`] = `
@ -171,7 +177,10 @@ Error:
Caused by:
0: failed to process input file
1: error was recoverable, but proceeding would result in wrong codegen
2: Syntax Error"
2: Syntax Error
Import trace for requested module:
./index.js"
`;
exports[`ReactRefreshLogBox unterminated JSX 1`] = `
@ -197,5 +206,8 @@ Error:
Caused by:
0: failed to process input file
1: Syntax Error"
1: Syntax Error
Import trace for requested module:
./index.js"
`;

View file

@ -667,6 +667,127 @@ describe('basic HMR', () => {
}
})
it('should recover after webpack parse error in an imported file', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about8.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.appPort, '/hmr/about8')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'export default',
'import "../../components/parse-error.xyz"\nexport default'
)
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(
`"Failed to compile"`
)
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
"./components/parse-error.xyz
Module parse failed: Unexpected token (3:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| This
| is
> }}}
| invalid
| js
Import trace for requested module:
./components/parse-error.xyz"
`)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
expect(await hasRedbox(browser, false)).toBe(false)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover after loader parse error in an imported file', async () => {
let browser
const aboutPage = join('pages', 'hmr', 'about9.js')
const aboutContent = await next.readFile(aboutPage)
try {
browser = await webdriver(next.appPort, '/hmr/about9')
await check(() => getBrowserBodyText(browser), /This is the about page/)
await next.patchFile(
aboutPage,
aboutContent.replace(
'export default',
'import "../../components/parse-error.js"\nexport default'
)
)
expect(await hasRedbox(browser)).toBe(true)
expect(await getRedboxHeader(browser)).toMatchInlineSnapshot(
`"Failed to compile"`
)
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
"./components/parse-error.js
Error:
x Expression expected
,-[1:1]
1 | This
2 | is
3 | }}}
: ^
4 | invalid
5 | js
\`----
Caused by:
0: failed to process input file
1: Syntax Error
Import trace for requested module:
./components/parse-error.js"
`)
await next.patchFile(aboutPage, aboutContent)
await check(() => getBrowserBodyText(browser), /This is the about page/)
expect(await hasRedbox(browser, false)).toBe(false)
} catch (err) {
await next.patchFile(aboutPage, aboutContent)
if (browser) {
await check(
() => getBrowserBodyText(browser),
/This is the about page/
)
}
throw err
} finally {
if (browser) {
await browser.close()
}
}
})
it('should recover from errors in getInitialProps in client', async () => {
let browser
const erroredPage = join('pages', 'hmr', 'error-in-gip.js')

View file

@ -0,0 +1,5 @@
This
is
}}}
invalid
js

View file

@ -0,0 +1,5 @@
This
is
}}}
invalid
js

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>

View file

@ -0,0 +1,7 @@
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -0,0 +1,7 @@
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

View file

@ -1,5 +1,7 @@
export default () => (
<div className="hmr-contact-page">
<p>This is the contact page.</p>
</div>
)
export default function Page() {
return (
<div className="hmr-contact-page">
<p>This is the contact page.</p>
</div>
)
}

View file

@ -1,5 +1,5 @@
import React from 'react'
export default class extends React.Component {
export default class Page extends React.Component {
static getInitialProps() {
const error = new Error('an-expected-error-in-gip')
throw error

View file

@ -1,9 +1,11 @@
import Link from 'next/link'
export default () => (
<div>
<Link href="/hmr/error-in-gip" id="error-in-gip-link">
Bad Page
</Link>
</div>
)
export default function Page() {
return (
<div>
<Link href="/hmr/error-in-gip" id="error-in-gip-link">
Bad Page
</Link>
</div>
)
}

View file

@ -3,6 +3,6 @@ import dynamic from 'next/dynamic'
const HmrDynamic = dynamic(import('../../components/hmr/dynamic'))
export default () => {
export default function Page() {
return <HmrDynamic />
}