From 273dff2da4473a7257112549c6ca4ec30a12dde6 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Fri, 1 Mar 2024 09:21:57 +0100 Subject: [PATCH] Add target="_blank" attribute to external markdown links --- src/components/Markdown.svelte | 8 +++++++- src/lib/markdown.ts | 9 ++++----- tests/e2e/project.spec.ts | 6 ++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/components/Markdown.svelte b/src/components/Markdown.svelte index ee0fb293a..2d3d5f241 100644 --- a/src/components/Markdown.svelte +++ b/src/components/Markdown.svelte @@ -98,8 +98,14 @@ } afterUpdate(async () => { - // Don't underline tags that contain images. for (const e of container.querySelectorAll("a")) { + if (URL.canParse(e.href)) { + const url = new URL(e.href); + if (url.origin !== window.origin) { + e.target = "_blank"; + } + } + // Don't underline tags that contain images. if (e.firstElementChild instanceof HTMLImageElement) { e.classList.add("no-underline"); } diff --git a/src/lib/markdown.ts b/src/lib/markdown.ts index ba9c2ae71..2bae17a50 100644 --- a/src/lib/markdown.ts +++ b/src/lib/markdown.ts @@ -140,11 +140,10 @@ export class Renderer extends BaseRenderer { // By lowercasing we avoid casing mismatches, between headings and links. return `${text}`; } else { - try { - href = new URL(href, this.#baseUrl).href; - } catch { - // Use original href value - } + href = URL.canParse(href, this.#baseUrl) + ? new URL(href, this.#baseUrl).href + : href; + return `${text}`; } } diff --git a/tests/e2e/project.spec.ts b/tests/e2e/project.spec.ts index 1b885aa60..c4356fc46 100644 --- a/tests/e2e/project.spec.ts +++ b/tests/e2e/project.spec.ts @@ -449,13 +449,15 @@ test.describe("browser error handling", () => { }); }); -test("external markdown link", async ({ page }) => { +test("external markdown link", async ({ context, page }) => { await page.route("https://example.com/**", route => { return route.fulfill({ body: "hello", contentType: "text/plain" }); }); await page.goto(`${markdownUrl}/tree/main/footnotes.md`); + const pagePromise = context.waitForEvent("page"); await page.getByRole("link", { name: "https://example.com" }).click(); - await expect(page).toHaveURL("https://example.com"); + const newPage = await pagePromise; + await expect(newPage).toHaveURL("https://example.com"); }); test("internal file markdown link", async ({ page }) => {