Anton Bulakh
2024-12-27 99011cb1b0d20da9cb523729ed0e9ff4e9c27405
fix(spa): handle HTML redirects for aliases (#1680)

3 files modified
25 ■■■■■ changed files
quartz/components/scripts/popover.inline.ts 3 ●●●● patch | view | raw | blame | history
quartz/components/scripts/spa.inline.ts 3 ●●●● patch | view | raw | blame | history
quartz/components/scripts/util.ts 19 ●●●●● patch | view | raw | blame | history
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)
  })
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")) {
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
}