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
12 changes: 8 additions & 4 deletions website/modules/blog/utils/render-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@
* - `> ` blockquotes
* - `- ` bulleted lists (custom-positioned markers via `before:` pseudo-element)
* - ```fenced``` code blocks
* - Inline: **bold**, *italic*, `code`, [text](url). An absolute or
* protocol-relative URL opens in a new tab (with a screen-reader cue);
* an internal `/path` link navigates in place.
* - Inline: **bold**, *italic*, `code`, [text](url). The URL may contain
* one level of balanced parens (e.g. a Wikipedia `..._(language)` link).
Comment thread
vivek7405 marked this conversation as resolved.
* An absolute or protocol-relative URL opens in a new tab (with a
* screen-reader cue); an internal `/path` link navigates in place.
*/

function inline(s: string): string {
let out = s
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, t, u) => {
// The URL group allows one level of balanced nested parens, so a link whose
// address contains a `(...)` (e.g. a Wikipedia `..._(programming_language)`
// URL) is captured whole instead of truncated at the first `)`.
out = out.replace(/\[([^\]]+)\]\(((?:[^()]|\([^()]*\))+)\)/g, (_m, t, u) => {
Comment thread
vivek7405 marked this conversation as resolved.
// Absolute (off-site) links open in a new tab so the reader keeps the
// article; internal links (starting with /) navigate in place. Escape any
// `"` in the URL so it cannot break out of the href attribute.
Expand Down
17 changes: 17 additions & 0 deletions website/test/ssr/compare-ssr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ test('a double quote in a link URL cannot break out of the href attribute', () =
assert.match(out, /%22/, 'the quote is percent-escaped in the href');
});

test('a link URL containing balanced parens is captured whole, not truncated', () => {
const out = renderPostBody('[Ruby](https://en.wikipedia.org/wiki/Ruby_(programming_language))');
// Counterfactual: the old `[^)]+` capture stopped at the first `)`, so the
// href lost its `)` tail. The full URL must survive.
assert.match(out, /href="https:\/\/en\.wikipedia\.org\/wiki\/Ruby_\(programming_language\)"/, 'href keeps the full parenthesized URL');
assert.match(out, /target="_blank"/, 'still classified as an external link');
// Counterfactual: the truncated form left a stray `)` right after the closing
// </a>. The whole-URL form emits no such orphan.
assert.doesNotMatch(out, /<\/a>\)/, 'no orphaned closing paren after the link');
Comment thread
vivek7405 marked this conversation as resolved.
});

test('an empty link URL stays literal text (no empty-href anchor)', () => {
const out = renderPostBody('see [x]() here');
assert.doesNotMatch(out, /<a /, 'does not emit an anchor for an empty URL');
assert.match(out, /\[x\]\(\)/, 'leaves the malformed link as literal text');
});

test('the compare page shows a visible outbound link to the competitor site in a new tab', async () => {
const out = await renderToString(await ComparePage({ params: { slug: 'webjs-vs-nextjs' } }));
// A visible, underlined "Visit the Next.js site" link (distinguishable while
Expand Down