Skip to content

Commit

Permalink
hmr error improvements (#30616)
Browse files Browse the repository at this point in the history
Co-authored-by: Hannes Bornö <hannes.borno@vercel.com>
Co-authored-by: Hannes Bornö <borno.hannes@gmail.com>
  • Loading branch information
3 people committed Jan 6, 2023
1 parent 3c87e1b commit 3b91ca9
Show file tree
Hide file tree
Showing 25 changed files with 231 additions and 40 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .prettierignore_staged
Original file line number Diff line number Diff line change
Expand Up @@ -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/**
Original file line number Diff line number Diff line change
Expand Up @@ -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}`)
Expand Down Expand Up @@ -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')
}

Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/dev/hot-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export class WebpackHotMiddleware {
hash: true,
warnings: true,
errors: true,
moduleTrace: true,
})

this.eventStream.publish({
Expand Down
17 changes: 8 additions & 9 deletions packages/next/src/server/dev/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`] = `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`] = `
Expand Down Expand Up @@ -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`] = `
Expand All @@ -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`] = `
Expand All @@ -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"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -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`] = `
Expand Down Expand Up @@ -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`] = `
Expand All @@ -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`] = `
Expand All @@ -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"
`;
121 changes: 121 additions & 0 deletions test/development/basic/hmr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
5 changes: 5 additions & 0 deletions test/development/basic/hmr/components/parse-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This
is
}}}
invalid
js
5 changes: 5 additions & 0 deletions test/development/basic/hmr/components/parse-error.xyz
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This
is
}}}
invalid
js
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about1.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about3.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about5.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about6.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
2 changes: 1 addition & 1 deletion test/development/basic/hmr/pages/hmr/about7.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export default () => {
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
Expand Down
7 changes: 7 additions & 0 deletions test/development/basic/hmr/pages/hmr/about8.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}
7 changes: 7 additions & 0 deletions test/development/basic/hmr/pages/hmr/about9.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function Page() {
return (
<div className="hmr-about-page">
<p>This is the about page.</p>
</div>
)
}

0 comments on commit 3b91ca9

Please sign in to comment.