From 8aaea339b40b71593ffa522cb5e51153ba93a01f Mon Sep 17 00:00:00 2001 From: bhushan4work Date: Tue, 3 Nov 2026 14:28:55 +0530 Subject: [PATCH] fix(meta-fetcher): strip HTML comments before parsing meta tags (#389) Commented-out tags in HTML were parsed as active metadata because the extraction regexes ran over raw HTML. Strip comments first so that commented-out , <meta>, and <link> tags no longer shadow or overwrite their live counterparts. --- src/lib/meta-fetcher.ts | 4 ++ tests/lib/meta-fetcher.test.ts | 93 ++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/src/lib/meta-fetcher.ts b/src/lib/meta-fetcher.ts index b9ff6c8..d033f5e 100644 --- a/src/lib/meta-fetcher.ts +++ b/src/lib/meta-fetcher.ts @@ -43,6 +43,10 @@ export async function fetchMetaTags(url: string): Promise<MetaTags> { } export function parseMetaTags(html: string, baseUrl: string): MetaTags { + // Strip HTML comments before any extraction so that commented-out tags are + // not mistaken for active metadata (issue #389). + html = html.replace(/<!--[\s\S]*?-->/g, ''); + const tags: MetaTags = {}; let inFirstImageGroup = false; diff --git a/tests/lib/meta-fetcher.test.ts b/tests/lib/meta-fetcher.test.ts index 01fc9fc..1a04622 100644 --- a/tests/lib/meta-fetcher.test.ts +++ b/tests/lib/meta-fetcher.test.ts @@ -233,3 +233,96 @@ describe('canonical and favicon attribute order', () => { expect(tags.ogImage).toBe('https://example.com/posts/hello/cover.png'); }); }); + +describe('parseMetaTags – HTML comment stripping (issue #389)', () => { + const base = 'https://example.com/page'; + + it('ignores a commented-out <title> before the live title', () => { + const html = ` + <!-- <title>Wrong Title --> + Correct Title + `; + const tags = parseMetaTags(html, base); + expect(tags.title).toBe('Correct Title'); + }); + + it('does not overwrite a live og:title with a commented-out one that follows it', () => { + const html = ` + + + `; + const tags = parseMetaTags(html, base); + expect(tags.ogTitle).toBe('Real OG Title'); + }); + + it('does not overwrite live og:description with a later commented-out one', () => { + const html = ` + + + `; + const tags = parseMetaTags(html, base); + expect(tags.ogDescription).toBe('Live description'); + }); + + it('does not overwrite live og:image with a later commented-out one', () => { + const html = ` + + + `; + const tags = parseMetaTags(html, base); + expect(tags.ogImage).toBe('https://example.com/live.png'); + }); + + it('ignores a commented-out canonical link before the live one', () => { + const html = ` + + + `; + const tags = parseMetaTags(html, base); + expect(tags.canonical).toBe('https://example.com/correct'); + }); + + it('ignores a commented-out favicon link before the live one', () => { + const html = ` + + + `; + const tags = parseMetaTags(html, base); + expect(tags.favicon).toBe('https://example.com/correct-favicon.ico'); + }); + + it('still parses all normal active metadata when no comments are present', () => { + const html = ` + Page Title + + + + + + + `; + const tags = parseMetaTags(html, base); + expect(tags.title).toBe('Page Title'); + expect(tags.description).toBe('Page description'); + expect(tags.ogTitle).toBe('OG Title'); + expect(tags.ogDescription).toBe('OG Description'); + expect(tags.ogImage).toBe('https://example.com/image.png'); + expect(tags.canonical).toBe('https://example.com/page'); + expect(tags.favicon).toBe('https://example.com/favicon.ico'); + }); + + it('handles multi-line HTML comments correctly', () => { + const html = ` + + Active Title + + `; + const tags = parseMetaTags(html, base); + expect(tags.title).toBe('Active Title'); + expect(tags.ogTitle).toBe('Active OG'); + }); +});