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:
parent
3c87e1b52a
commit
3b91ca98a3
25 changed files with 231 additions and 40 deletions
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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/**
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ export class WebpackHotMiddleware {
|
|||
hash: true,
|
||||
warnings: true,
|
||||
errors: true,
|
||||
moduleTrace: true,
|
||||
})
|
||||
|
||||
this.eventStream.publish({
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`] = `
|
||||
|
|
|
@ -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"
|
||||
`;
|
||||
|
|
|
@ -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"
|
||||
`;
|
||||
|
|
|
@ -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')
|
||||
|
|
5
test/development/basic/hmr/components/parse-error.js
Normal file
5
test/development/basic/hmr/components/parse-error.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
This
|
||||
is
|
||||
}}}
|
||||
invalid
|
||||
js
|
5
test/development/basic/hmr/components/parse-error.xyz
Normal file
5
test/development/basic/hmr/components/parse-error.xyz
Normal file
|
@ -0,0 +1,5 @@
|
|||
This
|
||||
is
|
||||
}}}
|
||||
invalid
|
||||
js
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export default () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
|
|
7
test/development/basic/hmr/pages/hmr/about8.js
Normal file
7
test/development/basic/hmr/pages/hmr/about8.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
7
test/development/basic/hmr/pages/hmr/about9.js
Normal file
7
test/development/basic/hmr/pages/hmr/about9.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-about-page">
|
||||
<p>This is the about page.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
export default () => (
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="hmr-contact-page">
|
||||
<p>This is the contact page.</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import Link from 'next/link'
|
||||
|
||||
export default () => (
|
||||
export default function Page() {
|
||||
return (
|
||||
<div>
|
||||
<Link href="/hmr/error-in-gip" id="error-in-gip-link">
|
||||
Bad Page
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,6 @@ import dynamic from 'next/dynamic'
|
|||
|
||||
const HmrDynamic = dynamic(import('../../components/hmr/dynamic'))
|
||||
|
||||
export default () => {
|
||||
export default function Page() {
|
||||
return <HmrDynamic />
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue