Skip to content

Commit 3f239ee

Browse files
authored
[devtool] highlight all link in error message (#86084)
1 parent 5e7a207 commit 3f239ee

File tree

3 files changed

+34
-16
lines changed

3 files changed

+34
-16
lines changed

packages/next/src/next-devtools/dev-overlay/components/hot-linked-text/index.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const linkRegex = /https?:\/\/[^\s/$.?#].[^\s)'"]*/i
55

66
export const HotlinkedText: React.FC<{
77
text: string
8-
matcher?: (text: string) => boolean
8+
matcher?: (text: string) => string | null
99
}> = function HotlinkedText(props) {
1010
const { text, matcher } = props
1111

@@ -24,17 +24,27 @@ export const HotlinkedText: React.FC<{
2424
if (linkRegex.test(rawPart)) {
2525
const link = linkRegex.exec(rawPart)!
2626
const href = link[0]
27-
// If link matcher is present but the link doesn't match, don't turn it into a link
28-
if (typeof matcher === 'function' && !matcher(href)) {
29-
return (
30-
<React.Fragment key={`link-${outerIndex}-${index}`}>
31-
{rawPart}
32-
</React.Fragment>
33-
)
27+
// If link matcher is present, check if it returns a className
28+
let linkClassName: string | null = null
29+
if (typeof matcher === 'function') {
30+
linkClassName = matcher(href)
31+
// If matcher returns null, don't turn it into a link
32+
if (linkClassName === null) {
33+
return (
34+
<React.Fragment key={`link-${outerIndex}-${index}`}>
35+
{rawPart}
36+
</React.Fragment>
37+
)
38+
}
3439
}
3540
return (
3641
<React.Fragment key={`link-${outerIndex}-${index}`}>
37-
<a href={href} target="_blank" rel="noreferrer noopener">
42+
<a
43+
href={href}
44+
target="_blank"
45+
rel="noreferrer noopener"
46+
className={linkClassName || undefined}
47+
>
3848
{rawPart}
3949
</a>
4050
</React.Fragment>

packages/next/src/next-devtools/dev-overlay/container/errors.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,18 @@ interface ErrorsProps extends ErrorBaseProps {
2929
onClose: () => void
3030
}
3131

32-
function isNextjsLink(text: string): boolean {
33-
return text.startsWith('https://nextjs.org')
32+
function matchLinkType(text: string): string | null {
33+
if (text.startsWith('https://nextjs.org')) {
34+
return 'nextjs-link'
35+
}
36+
if (text.startsWith('https://') || text.startsWith('http://')) {
37+
return 'external-link'
38+
}
39+
return null
3440
}
3541

3642
function HydrationErrorDescription({ message }: { message: string }) {
37-
return <HotlinkedText text={message} matcher={isNextjsLink} />
43+
return <HotlinkedText text={message} matcher={matchLinkType} />
3844
}
3945

4046
function GenericErrorDescription({ error }: { error: Error }) {
@@ -51,7 +57,7 @@ function GenericErrorDescription({ error }: { error: Error }) {
5157

5258
return (
5359
<>
54-
<HotlinkedText text={message} matcher={isNextjsLink} />
60+
<HotlinkedText text={message} matcher={matchLinkType} />
5561
</>
5662
)
5763
}
@@ -412,4 +418,7 @@ export const styles = `
412418
border-radius: var(--rounded-md-2);
413419
border: 1px solid var(--color-gray-alpha-400);
414420
}
421+
.external-link, .external-link:hover {
422+
color:inherit;
423+
}
415424
`

test/development/acceptance-app/ReactRefreshLogBox.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,6 @@ describe('ReactRefreshLogBox app', () => {
889889
}
890890
`)
891891
}
892-
// Do not highlight example.com but do highlight nextjs.org
893892
expect(
894893
await session.evaluate(
895894
() =>
@@ -898,7 +897,7 @@ describe('ReactRefreshLogBox app', () => {
898897
.shadowRoot.querySelectorAll('#nextjs__container_errors_desc a')
899898
.length
900899
)
901-
).toBe(1)
900+
).toBe(2)
902901
expect(
903902
await session.evaluate(
904903
() =>
@@ -922,7 +921,7 @@ describe('ReactRefreshLogBox app', () => {
922921
) as any
923922
).href
924923
)
925-
).toBe(null)
924+
).toBe('http://example.com/')
926925
})
927926

928927
// TODO-APP: Catch errors that happen before useEffect

0 commit comments

Comments
 (0)