diff --git a/apps/server/src/projectFaviconRoute.test.ts b/apps/server/src/projectFaviconRoute.test.ts
index b0f0273cfbaf9e8d681b9ae9e955bd85df095753..e8b826d7be74c5d81d3b2b73d29a5d85db51bb0f 100644
--- a/apps/server/src/projectFaviconRoute.test.ts
+++ b/apps/server/src/projectFaviconRoute.test.ts
@@ -115,4 +115,41 @@ describe("tryHandleProjectFaviconRequest", () => {
expect(response.body).toBe("");
});
});
+
+ it("resolves icon link when href appears before rel in HTML", async () => {
+ const projectDir = makeTempDir("t3code-favicon-route-html-order-");
+ const iconPath = path.join(projectDir, "public", "brand", "logo.svg");
+ fs.mkdirSync(path.dirname(iconPath), { recursive: true });
+ fs.writeFileSync(path.join(projectDir, "index.html"), '');
+ fs.writeFileSync(iconPath, "", "utf8");
+
+ await withRouteServer(async (baseUrl) => {
+ const pathname = `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}`;
+ const response = await request(baseUrl, pathname);
+ expect(response.statusCode).toBe(200);
+ expect(response.contentType).toContain("image/svg+xml");
+ expect(response.body).toBe("");
+ });
+ });
+
+ it("resolves object-style icon metadata when href appears before rel", async () => {
+ const projectDir = makeTempDir("t3code-favicon-route-obj-order-");
+ const iconPath = path.join(projectDir, "public", "brand", "obj.svg");
+ fs.mkdirSync(path.dirname(iconPath), { recursive: true });
+ fs.mkdirSync(path.join(projectDir, "src"), { recursive: true });
+ fs.writeFileSync(
+ path.join(projectDir, "src", "root.tsx"),
+ 'const links = [{ href: "/brand/obj.svg", rel: "icon" }];',
+ "utf8",
+ );
+ fs.writeFileSync(iconPath, "", "utf8");
+
+ await withRouteServer(async (baseUrl) => {
+ const pathname = `/api/project-favicon?cwd=${encodeURIComponent(projectDir)}`;
+ const response = await request(baseUrl, pathname);
+ expect(response.statusCode).toBe(200);
+ expect(response.contentType).toContain("image/svg+xml");
+ expect(response.body).toBe("");
+ });
+ });
});
diff --git a/apps/server/src/projectFaviconRoute.ts b/apps/server/src/projectFaviconRoute.ts
index 5b224ca56f46cb679aebc02489f7d16bbacbf8cc..4d66f0e86f3156ef6c7f194969acbab0b0ac2ea5 100644
--- a/apps/server/src/projectFaviconRoute.ts
+++ b/apps/server/src/projectFaviconRoute.ts
@@ -44,9 +44,11 @@ const ICON_SOURCE_FILES = [
"src/index.html",
];
-// Matches or { rel: "icon", href: "..." } patterns.
-const LINK_ICON_HTML_RE = /]*rel=["'](?:icon|shortcut icon)["'][^>]*href=["']([^"'?]+)/i;
-const LINK_ICON_OBJ_RE = /rel:\s*["'](?:icon|shortcut icon)["'][^}]*href:\s*["']([^"'?]+)/i;
+// Matches tags or object-like icon metadata where rel/href can appear in any order.
+const LINK_ICON_HTML_RE =
+ /]*\brel=["'](?:icon|shortcut icon)["'])(?=[^>]*\bhref=["']([^"'?]+))[^>]*>/i;
+const LINK_ICON_OBJ_RE =
+ /(?=[^}]*\brel\s*:\s*["'](?:icon|shortcut icon)["'])(?=[^}]*\bhref\s*:\s*["']([^"'?]+))[^}]*/i;
function extractIconHref(source: string): string | null {
const htmlMatch = source.match(LINK_ICON_HTML_RE);