Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
"prepublishOnly": "turbo run build",
"lint-staged": "lint-staged",
"next-with-deps": "./scripts/next-with-deps.sh",
"next": "cross-env NEXT_TELEMETRY_DISABLED=1 node --trace-deprecation --enable-source-maps packages/next/dist/bin/next",
"next-no-sourcemaps": "cross-env NEXT_TELEMETRY_DISABLED=1 node --trace-deprecation packages/next/dist/bin/next",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't just add --disable-source-maps here since our args parser (Commander) would treat it as the positional. Yargs would treat it different e.g. next --foo bar would be equivalent to next bar --foo but is not with Commander.

"next": "cross-env NEXT_TELEMETRY_DISABLED=1 NODE_OPTIONS=\"--trace-deprecation --enable-source-maps\" next",
"next-no-sourcemaps": "echo 'No longer supported. Use `pnpm next --disable-source-maps` instead'; exit 1;",
"clean-trace-jaeger": "node scripts/rm.mjs test/integration/basic/.next && TRACE_TARGET=JAEGER pnpm next build test/integration/basic",
"debug": "cross-env NEXT_TELEMETRY_DISABLED=1 node --inspect --trace-deprecation --enable-source-maps packages/next/dist/bin/next",
"postinstall": "node scripts/git-configure.mjs && node scripts/install-native.mjs",
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ program
'-H, --hostname <hostname>',
'Specify a hostname on which to start the application (default: 0.0.0.0).'
)
.option(
'--disable-source-maps',
"Don't start the Dev server with `--enable-source-maps`.",
false
)
.option(
'--experimental-https',
'Starts the server with HTTPS and generates a self-signed certificate.'
Expand Down
7 changes: 7 additions & 0 deletions packages/next/src/cli/next-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { flushAllTraces, trace } from '../trace'
import { traceId } from '../trace/shared'

export type NextDevOptions = {
disableSourceMaps: boolean
turbo?: boolean
turbopack?: boolean
port: number
Expand Down Expand Up @@ -267,6 +268,12 @@ const nextDev = async (
delete nodeOptions['max_old_space_size']
}

if (options.disableSourceMaps) {
delete nodeOptions['enable-source-maps']
} else {
nodeOptions['enable-source-maps'] = true
}

if (nodeDebugType) {
const address = getParsedDebugAddress()
address.port = address.port + 1
Expand Down
4 changes: 4 additions & 0 deletions run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ ${ENDGROUP}`)
// unset CI env so CI behavior is only explicitly
// tested when enabled
CI: '',
// But some tests need to fork based on machine? CI? behavior differences
// Only use read this in tests.
// For implementation forks, use `process.env.CI` instead
NEXT_TEST_CI: process.env.CI,

...(options.local
? {}
Expand Down
121 changes: 72 additions & 49 deletions test/development/middleware-errors/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
assertNoRedbox,
check,
getRedboxSource,
retry,
} from 'next-test-utils'
import stripAnsi from 'strip-ansi'
import { nextTestSetup } from 'e2e-utils'
Expand Down Expand Up @@ -31,23 +32,22 @@ describe('middleware - development errors', () => {

it('logs the error correctly', async () => {
await next.fetch('/')
const output = stripAnsi(next.cliOutput)
await check(() => {
if (isTurbopack) {
expect(stripAnsi(next.cliOutput)).toMatch(
/middleware.js \(\d+:\d+\) @ __TURBOPACK__default__export__/
)
} else {
expect(stripAnsi(next.cliOutput)).toMatch(
/middleware.js \(\d+:\d+\) @ default/
)
}

expect(stripAnsi(next.cliOutput)).toMatch(/boom/)
return 'success'
}, 'success')
expect(output).not.toContain(
'webpack-internal:///(middleware)/./middleware.js'

await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain('boom')
})
// TODO: assert on full, ignore-listed stack
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n ⨯ middleware.js (3:15) @ __TURBOPACK__default__export__' +
'\n ⨯ Error: boom' +
'\n at __TURBOPACK__default__export__ (./middleware.js:3:15)'
: '\n ⨯ middleware.js (3:15) @ default' +
'\n ⨯ boom' +
'\n 1 |' +
'\n 2 | export default function () {' +
"\n> 3 | throw new Error('boom')" +
'\n | ^'
)
})

Expand Down Expand Up @@ -79,13 +79,20 @@ describe('middleware - development errors', () => {

it('logs the error correctly', async () => {
await next.fetch('/')
await check(
() => stripAnsi(next.cliOutput),
new RegExp(`unhandledRejection: Error: async boom!`, 'm')

await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain(
'unhandledRejection: Error: async boom!'
)
})
// TODO: assert on full, ignore-listed stack
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? 'unhandledRejection: Error: async boom!\n at throwError ('
: 'unhandledRejection: Error: async boom!' +
'\n at throwError (webpack-internal:///(middleware)/./middleware.js:8:11)' +
'\n at __WEBPACK_DEFAULT_EXPORT__ (webpack-internal:///(middleware)/./middleware.js:11:5)'
)
// expect(output).not.toContain(
// 'webpack-internal:///(middleware)/./middleware.js'
// )
})

it('does not render the error', async () => {
Expand All @@ -112,17 +119,37 @@ describe('middleware - development errors', () => {

it('logs the error correctly', async () => {
await next.fetch('/')
// const output = stripAnsi(next.cliOutput)
await check(() => {
expect(stripAnsi(next.cliOutput)).toMatch(
/middleware.js \(\d+:\d+\) @ eval/

await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain('Dynamic Code Evaluation')
})
// TODO: assert on full, ignore-listed stack
if (isTurbopack) {
// Locally, prefixes the "test is not defined".
// In CI, it prefixes "Dynamic Code Evaluation".
expect(stripAnsi(next.cliOutput)).toContain(
'\n ⚠ middleware.js (3:22) @ __TURBOPACK__default__export__' +
'\n ⨯ middleware.js (4:9) @ eval'
)
expect(stripAnsi(next.cliOutput)).toMatch(/test is not defined/)
return 'success'
}, 'success')
// expect(output).not.toContain(
// 'webpack-internal:///(middleware)/./middleware.js'
// )
}
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n ⨯ Error: test is not defined' +
'\n at eval (./middleware.js:4:9)' +
'\n at <unknown> (./middleware.js:4:9'
: '\n ⨯ Error [ReferenceError]: test is not defined' +
'\n at eval (file://webpack-internal:///(middleware)/./middleware.js)' +
'\n at eval (webpack://_N_E/middleware.js?3bcb:4:8)'
)
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? "\n ⚠ Error: Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" +
'\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation' +
'\n at __TURBOPACK__default__export__ (./middleware.js:3:22)'
: '\n ⚠ middleware.js (4:9) @ eval' +
"\n ⚠ Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime" +
'\nLearn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation'
)
})

it('renders the error correctly and recovers', async () => {
Expand Down Expand Up @@ -150,22 +177,18 @@ describe('middleware - development errors', () => {

it('logs the error correctly', async () => {
await next.fetch('/')
const output = stripAnsi(next.cliOutput)
await check(() => {
if (isTurbopack) {
expect(stripAnsi(next.cliOutput)).toMatch(
/middleware.js \(\d+:\d+\) @ \[project\]\/middleware\.js \[middleware\] \(ecmascript\)/
)
} else {
expect(stripAnsi(next.cliOutput)).toMatch(
/middleware.js \(\d+:\d+\) @ <unknown>/
)
}
expect(stripAnsi(next.cliOutput)).toMatch(/booooom!/)
return 'success'
}, 'success')
expect(output).not.toContain(
'webpack-internal:///(middleware)/./middleware.js'

await retry(() => {
expect(stripAnsi(next.cliOutput)).toContain(`Error: booooom!`)
})
// TODO: assert on full, ignore-listed stack
expect(stripAnsi(next.cliOutput)).toContain(
isTurbopack
? '\n ⨯ middleware.js (3:13) @ [project]/middleware.js [middleware] (ecmascript)' +
'\n ⨯ Error: booooom!' +
'\n at <unknown> ([project]/middleware.js [middleware] (ecmascript) (./middleware.js:3:13)'
: '\n ⨯ Error: booooom!' +
'\n at <unknown> (webpack://_N_E/middleware.js'
)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
pnpm-lock.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { run } from 'internal-pkg'

function logError() {
const error = new Error('Boom')
console.error(error)
}

export default function Page() {
run(() => logError())
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use client'
import { run } from 'internal-pkg'

function logError() {
const error = new Error('Boom')
console.error(error)
}

export default function Page() {
run(() => logError())
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

function logError() {
const error = new Error('Boom')
console.error(error)
}

export default function Page() {
logError()
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function run(fn) {
return fn()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "internal-pkg",
"private": true,
"type": "module",
"exports": {
".": {
"default": "./index.js"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"internal-pkg": "file:./internal-pkg"
}
}
Loading
Loading