Skip to content

Commit 4d6b4d3

Browse files
committed
refactor(dashboard): extract favicon canonical host rules to lib
Single longest-first rule list and resolveFaviconCanonicalHost(); FaviconImage imports the helper.
1 parent e8c0617 commit 4d6b4d3

File tree

2 files changed

+43
-35
lines changed

2 files changed

+43
-35
lines changed

apps/dashboard/components/analytics/favicon-image.tsx

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { GlobeIcon } from "@phosphor-icons/react";
44
import Image from "next/image";
55
import { useState } from "react";
6+
import { resolveFaviconCanonicalHost } from "@/lib/favicon-domain";
67

78
interface FaviconImageProps {
89
domain: string;
@@ -15,40 +16,6 @@ interface FaviconImageProps {
1516
const hostnameRegex = /^https?:\/\//;
1617
const wwwRegex = /^www\./;
1718

18-
const FAVICON_DOMAIN_MAP: Record<string, string> = {
19-
"framercdn.com": "framer.com",
20-
"plugins.framercdn.com": "framer.com",
21-
"figma.design": "figma.com",
22-
"canva.me": "canva.com",
23-
"vercel.app": "vercel.com",
24-
"netlify.app": "netlify.com",
25-
"pages.dev": "cloudflare.com",
26-
"workers.dev": "cloudflare.com",
27-
"checkout.stripe.com": "stripe.com",
28-
"billing.stripe.com": "stripe.com",
29-
"invoice.stripe.com": "stripe.com",
30-
"googleadservices.com": "ads.google.com",
31-
"googleads.g.doubleclick.net": "ads.google.com",
32-
"syndicatedsearch.goog": "ads.google.com",
33-
"googlesyndication.com": "ads.google.com",
34-
};
35-
36-
function getFaviconDomain(hostname: string): string {
37-
// Check exact match first
38-
if (hostname in FAVICON_DOMAIN_MAP) {
39-
return FAVICON_DOMAIN_MAP[hostname];
40-
}
41-
42-
// Check if it's a subdomain of a mapped domain
43-
for (const [pattern, canonical] of Object.entries(FAVICON_DOMAIN_MAP)) {
44-
if (hostname.endsWith(`.${pattern}`)) {
45-
return canonical;
46-
}
47-
}
48-
49-
return hostname;
50-
}
51-
5219
export function FaviconImage({
5320
domain,
5421
altText,
@@ -66,7 +33,7 @@ export function FaviconImage({
6633
.split("?")[0]
6734
.split("#")[0];
6835

69-
const faviconHost = getFaviconDomain(hostname);
36+
const faviconHost = resolveFaviconCanonicalHost(hostname);
7037

7138
const invalid =
7239
!hostname ||
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Resolves a hostname to a canonical host for DuckDuckGo favicon URLs
3+
* (`https://icons.duckduckgo.com/ip3/{host}.ico`). Use when several CDN /
4+
* product domains should show the same brand icon as the public marketing site.
5+
*
6+
* Rules are ordered longest-`pattern` first so more specific hosts win (e.g.
7+
* `googleads.g.doubleclick.net` before any hypothetical shorter suffix).
8+
*/
9+
const FAVICON_CANONICAL_RULES: ReadonlyArray<{
10+
pattern: string;
11+
canonical: string;
12+
}> = [
13+
{ pattern: "googleads.g.doubleclick.net", canonical: "ads.google.com" },
14+
{ pattern: "plugins.framercdn.com", canonical: "framer.com" },
15+
{ pattern: "googlesyndication.com", canonical: "ads.google.com" },
16+
{ pattern: "syndicatedsearch.goog", canonical: "ads.google.com" },
17+
{ pattern: "googleadservices.com", canonical: "ads.google.com" },
18+
{ pattern: "checkout.stripe.com", canonical: "stripe.com" },
19+
{ pattern: "billing.stripe.com", canonical: "stripe.com" },
20+
{ pattern: "invoice.stripe.com", canonical: "stripe.com" },
21+
{ pattern: "framercdn.com", canonical: "framer.com" },
22+
{ pattern: "netlify.app", canonical: "netlify.com" },
23+
{ pattern: "workers.dev", canonical: "cloudflare.com" },
24+
{ pattern: "figma.design", canonical: "figma.com" },
25+
{ pattern: "vercel.app", canonical: "vercel.com" },
26+
{ pattern: "pages.dev", canonical: "cloudflare.com" },
27+
{ pattern: "canva.me", canonical: "canva.com" },
28+
];
29+
30+
function hostnameMatchesPattern(hostname: string, pattern: string): boolean {
31+
return hostname === pattern || hostname.endsWith(`.${pattern}`);
32+
}
33+
34+
export function resolveFaviconCanonicalHost(hostname: string): string {
35+
for (const { pattern, canonical } of FAVICON_CANONICAL_RULES) {
36+
if (hostnameMatchesPattern(hostname, pattern)) {
37+
return canonical;
38+
}
39+
}
40+
return hostname;
41+
}

0 commit comments

Comments
 (0)