From 99011cb1b0d20da9cb523729ed0e9ff4e9c27405 Mon Sep 17 00:00:00 2001
From: Anton Bulakh <him@necauq.ua>
Date: Fri, 27 Dec 2024 14:18:22 +0000
Subject: [PATCH] fix(spa): handle HTML redirects for aliases (#1680)

---
 quartz/components/scripts/util.ts           |   19 +++++++++++++++++++
 quartz/components/scripts/popover.inline.ts |    3 ++-
 quartz/components/scripts/spa.inline.ts     |    3 ++-
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/quartz/components/scripts/popover.inline.ts b/quartz/components/scripts/popover.inline.ts
index 49f4382..b01af0e 100644
--- a/quartz/components/scripts/popover.inline.ts
+++ b/quartz/components/scripts/popover.inline.ts
@@ -1,5 +1,6 @@
 import { computePosition, flip, inline, shift } from "@floating-ui/dom"
 import { normalizeRelativeURLs } from "../../util/path"
+import { fetchCanonical } from "./util"
 
 const p = new DOMParser()
 async function mouseEnterHandler(
@@ -37,7 +38,7 @@
   targetUrl.hash = ""
   targetUrl.search = ""
 
-  const response = await fetch(`${targetUrl}`).catch((err) => {
+  const response = await fetchCanonical(targetUrl).catch((err) => {
     console.error(err)
   })
 
diff --git a/quartz/components/scripts/spa.inline.ts b/quartz/components/scripts/spa.inline.ts
index b67dad0..df48f04 100644
--- a/quartz/components/scripts/spa.inline.ts
+++ b/quartz/components/scripts/spa.inline.ts
@@ -1,5 +1,6 @@
 import micromorph from "micromorph"
 import { FullSlug, RelativeURL, getFullSlug, normalizeRelativeURLs } from "../../util/path"
+import { fetchCanonical } from "./util"
 
 // adapted from `micromorph`
 // https://github.com/natemoo-re/micromorph
@@ -59,7 +60,7 @@
 async function navigate(url: URL, isBack: boolean = false) {
   startLoading()
   p = p || new DOMParser()
-  const contents = await fetch(`${url}`)
+  const contents = await fetchCanonical(url)
     .then((res) => {
       const contentType = res.headers.get("content-type")
       if (contentType?.startsWith("text/html")) {
diff --git a/quartz/components/scripts/util.ts b/quartz/components/scripts/util.ts
index d0a16c6..c1db8ba 100644
--- a/quartz/components/scripts/util.ts
+++ b/quartz/components/scripts/util.ts
@@ -24,3 +24,22 @@
     node.removeChild(node.firstChild)
   }
 }
+
+// AliasRedirect emits HTML redirects which also have the link[rel="canonical"]
+// containing the URL it's redirecting to.
+// Extracting it here with regex is _probably_ faster than parsing the entire HTML
+// with a DOMParser effectively twice (here and later in the SPA code), even if
+// way less robust - we only care about our own generated redirects after all.
+const canonicalRegex = /<link rel="canonical" href="([^"]*)">/
+
+export async function fetchCanonical(url: URL): Promise<Response> {
+  const res = await fetch(`${url}`)
+  if (!res.headers.get("content-type")?.startsWith("text/html")) {
+    return res
+  }
+  // reading the body can only be done once, so we need to clone the response
+  // to allow the caller to read it if it's was not a redirect
+  const text = await res.clone().text()
+  const [_, redirect] = text.match(canonicalRegex) ?? []
+  return redirect ? fetch(redirect) : res
+}

--
Gitblit v1.10.0