Skip to content

Commit 3da6272

Browse files
authored
Fix greedy regex in error message markdown rendering (#15230)
Fixes #15068
1 parent 8cbc86c commit 3da6272

File tree

3 files changed

+113
-1
lines changed

3 files changed

+113
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes greedy regex in error message markdown rendering that caused link syntax examples to capture extra characters

packages/astro/src/core/errors/dev/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ export function getDocsForError(err: ErrorWithMetadata): string | undefined {
241241
}
242242
}
243243

244-
const linkRegex = /\[([^[]+)\]\((.*)\)/g;
244+
const linkRegex = /\[([^[]+)\]\(([^)]*)\)/g;
245245
const boldRegex = /\*\*(.+)\*\*/g;
246246
const urlRegex = / ((?:https?|ftp):\/\/[-\w+&@#\\/%?=~|!:,.;]*[-\w+&@#\\/%=~|])/gi;
247247
const codeRegex = /`([^`]+)`/g;
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import * as assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
import { renderErrorMarkdown } from '../../../dist/core/errors/dev/utils.js';
4+
5+
describe('renderErrorMarkdown', () => {
6+
describe('html target', () => {
7+
it('converts markdown links to HTML anchor tags', () => {
8+
const input = 'Check the [documentation](https://docs.astro.build)';
9+
const result = renderErrorMarkdown(input, 'html');
10+
assert.equal(
11+
result,
12+
'Check the <a href="https://docs.astro.build" target="_blank">documentation</a>',
13+
);
14+
});
15+
16+
it('converts bold text to HTML b tags', () => {
17+
const input = 'This is **important** text';
18+
const result = renderErrorMarkdown(input, 'html');
19+
assert.equal(result, 'This is <b>important</b> text');
20+
});
21+
22+
it('converts inline code to HTML code tags', () => {
23+
const input = 'Use the `console.log` function';
24+
const result = renderErrorMarkdown(input, 'html');
25+
assert.equal(result, 'Use the <code>console.log</code> function');
26+
});
27+
28+
it('converts bare URLs to HTML anchor tags', () => {
29+
const input = 'Visit https://astro.build for more info';
30+
const result = renderErrorMarkdown(input, 'html');
31+
assert.equal(
32+
result,
33+
'Visit <a href="https://astro.build" target="_blank">https://astro.build</a> for more info',
34+
);
35+
});
36+
37+
it('escapes HTML entities in the input', () => {
38+
const input = 'Use <script> tags carefully';
39+
const result = renderErrorMarkdown(input, 'html');
40+
assert.ok(result.includes('&lt;script&gt;'));
41+
});
42+
43+
it('handles multiple markdown elements', () => {
44+
const input = 'Check **bold** and `code` and [link](https://example.com)';
45+
const result = renderErrorMarkdown(input, 'html');
46+
assert.ok(result.includes('<b>bold</b>'));
47+
assert.ok(result.includes('<code>code</code>'));
48+
assert.ok(result.includes('<a href="https://example.com" target="_blank">link</a>'));
49+
});
50+
51+
it('handles link with parentheses followed by more content', () => {
52+
// This is the bug case from issue #15068
53+
// The link [text](url) should not consume content after it
54+
const input = 'use [text](url) for links';
55+
const result = renderErrorMarkdown(input, 'html');
56+
assert.equal(result, 'use <a href="url" target="_blank">text</a> for links');
57+
});
58+
59+
it('handles link followed by closing parenthesis', () => {
60+
// Edge case: link inside parentheses like "(use [text](url))"
61+
const input = '(use [text](url))';
62+
const result = renderErrorMarkdown(input, 'html');
63+
// The link should only capture 'url', not 'url)'
64+
assert.equal(result, '(use <a href="url" target="_blank">text</a>)');
65+
});
66+
67+
it('handles escaped HTML followed by link syntax', () => {
68+
// This simulates the MDX error message case
69+
const input = 'use <code>[text](url)</code>';
70+
const result = renderErrorMarkdown(input, 'html');
71+
// After HTML escaping, <code> becomes &lt;code&gt;
72+
// The link should still be parsed correctly without consuming &gt;)
73+
assert.ok(result.includes('<a href="url" target="_blank">text</a>'));
74+
assert.ok(result.includes('&lt;code&gt;'));
75+
assert.ok(result.includes('&lt;/code&gt;'));
76+
});
77+
78+
it('handles multiple links in the same message', () => {
79+
const input = 'See [docs](https://docs.astro.build) and [guide](https://guide.astro.build)';
80+
const result = renderErrorMarkdown(input, 'html');
81+
assert.ok(result.includes('<a href="https://docs.astro.build" target="_blank">docs</a>'));
82+
assert.ok(result.includes('<a href="https://guide.astro.build" target="_blank">guide</a>'));
83+
});
84+
});
85+
86+
describe('cli target', () => {
87+
it('formats markdown links for CLI output', () => {
88+
const input = 'Check the [documentation](https://docs.astro.build)';
89+
const result = renderErrorMarkdown(input, 'cli');
90+
// CLI output should contain the link text and URL
91+
assert.ok(result.includes('documentation'));
92+
assert.ok(result.includes('https://docs.astro.build'));
93+
});
94+
95+
it('formats bold text for CLI output', () => {
96+
const input = 'This is **important** text';
97+
const result = renderErrorMarkdown(input, 'cli');
98+
assert.ok(result.includes('important'));
99+
});
100+
101+
it('formats bare URLs for CLI output', () => {
102+
const input = 'Visit https://astro.build for more info';
103+
const result = renderErrorMarkdown(input, 'cli');
104+
assert.ok(result.includes('https://astro.build'));
105+
});
106+
});
107+
});

0 commit comments

Comments
 (0)