Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import { PathnameReader } from './pathname-reader'

export function InnerWrapper() {
return (
<p>
Current path: <PathnameReader />
</p>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import { InnerWrapper } from './inner-wrapper'

export function MiddleWrapper() {
return (
<div>
<InnerWrapper />
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use client'

import { MiddleWrapper } from './middle-wrapper'

export function OuterWrapper() {
return (
<section>
<MiddleWrapper />
</section>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Instant } from 'next'
import { OuterWrapper } from './outer-wrapper'

export const unstable_instant: Instant = {
level: 'experimental-error',
unstable_samples: [
{
params: {
one: '123',
// two: <missing>
},
},
],
}

export default function Page() {
return (
<main>
<p>
usePathname() nested three wrappers deep should still point at the
actual call site.
</p>
<OuterWrapper />
</main>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client'

import { usePathname } from 'next/navigation'

export function PathnameReader() {
const pathname = usePathname()
return <span data-testid="pathname">{pathname}</span>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Instant } from 'next'
import { PathnameReader } from './pathname-reader'

export const unstable_instant: Instant = {
level: 'experimental-error',
unstable_samples: [
{
params: {
one: '123',
// two: <missing>
},
},
],
}

export default function Page() {
return (
<main>
<p>
usePathname() without an ensureThrows wrapper should point at the call
site in the user&apos;s source.
</p>
<PathnameReader />
</main>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use client'

import { usePathname } from 'next/navigation'

export function PathnameReader() {
const pathname = usePathname()
return <span data-testid="pathname">{pathname}</span>
}
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,69 @@ describe('instant-validation-build', () => {
expect(result.cliOutput).not.toContain('AssertionError')
expect(result.exitCode).toBe(1)
})

it('error - usePathname() at the top of a Client Component body (no ensureThrows wrapper)', async () => {
const result = await prerender(
'/(default)/pathname/invalid-use-pathname-missing-params-no-wrapper/[one]/[two]'
)
// Today the function name is `<unknown>` instead of `PathnameReader`.
expect(extractBuildValidationError(result.cliOutput))
.toMatchInlineSnapshot(`
"Error: Route "/pathname/invalid-use-pathname-missing-params-no-wrapper/[one]/[two]" called usePathname() but param "two" is not defined in the \`unstable_samples\` of \`unstable_instant\`. usePathname() requires all route params to be provided.
at <unknown> (app/(default)/pathname/invalid-use-pathname-missing-params-no-wrapper/[one]/[two]/pathname-reader.tsx:6:20)
Comment on lines +827 to +828
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That doesn't really tell us stack attribution is working because it's just the sync error from misuing samples. The codeframe is the one we finally want. But it's broken at the moment.

4 |
5 | export function PathnameReader() {
> 6 | const pathname = usePathname()
| ^
7 | return <span data-testid="pathname">{pathname}</span>
8 | }
9 | {
digest: 'INSTANT_VALIDATION_ERROR'
}
Build-time instant validation failed for route "/pathname/invalid-use-pathname-missing-params-no-wrapper/[one]/[two]".
To get a more detailed stack trace and pinpoint the issue, try one of the following:
- Start the app in development mode by running \`next dev\`, then open "/pathname/invalid-use-pathname-missing-params-no-wrapper/[one]/[two]" in your browser to investigate the error.
- Rerun the production build with \`next build --debug-prerender\` to generate better stack traces.
Stopping prerender due to instant validation errors."
`)
expect(result.cliOutput).not.toContain('AssertionError')
expect(result.exitCode).toBe(1)
})

it('error - usePathname() called from a Client Component nested three wrappers deep', async () => {
const result = await prerender(
'/(default)/pathname/invalid-use-pathname-deep-component/[one]/[two]'
)
// The minified function name is not deterministic, so normalize it.
const error = extractBuildValidationError(result.cliOutput).replace(
/at [a-z] \(app\/\(default\)/g,
'at <minified> (app/(default)'
)
expect(error).toMatchInlineSnapshot(`
"Error: Route "/pathname/invalid-use-pathname-deep-component/[one]/[two]" called usePathname() but param "two" is not defined in the \`unstable_samples\` of \`unstable_instant\`. usePathname() requires all route params to be provided.
at <minified> (app/(default)/pathname/invalid-use-pathname-deep-component/[one]/[two]/pathname-reader.tsx:6:20)
4 |
5 | export function PathnameReader() {
> 6 | const pathname = usePathname()
| ^
7 | return <span data-testid="pathname">{pathname}</span>
8 | }
9 | {
digest: 'INSTANT_VALIDATION_ERROR'
}
Build-time instant validation failed for route "/pathname/invalid-use-pathname-deep-component/[one]/[two]".
To get a more detailed stack trace and pinpoint the issue, try one of the following:
- Start the app in development mode by running \`next dev\`, then open "/pathname/invalid-use-pathname-deep-component/[one]/[two]" in your browser to investigate the error.
- Rerun the production build with \`next build --debug-prerender\` to generate better stack traces.
Stopping prerender due to instant validation errors."
`)
const rawError = extractBuildValidationError(result.cliOutput)
expect(rawError).not.toContain('outer-wrapper.tsx')
expect(rawError).not.toContain('middle-wrapper.tsx')
expect(rawError).not.toContain('inner-wrapper.tsx')
expect(result.cliOutput).not.toContain('AssertionError')
expect(result.exitCode).toBe(1)
})
})

describe('root params', () => {
Expand Down
Loading