From 3ce6aa49bf25b26a4cb1bf18e9770271d132772d Mon Sep 17 00:00:00 2001
From: Karim <46734059+h-karim@users.noreply.github.com>
Date: Fri, 21 Mar 2025 23:49:56 +0000
Subject: [PATCH] fix(ogImage): update socialImage path to include base URL if defined (#1858)

---
 docs/plugins/CustomOgImages.md      |    2 +-
 quartz/util/path.test.ts            |   11 +++++++++++
 quartz/util/path.ts                 |   10 ++++++++++
 quartz/plugins/emitters/ogImage.tsx |   12 +++++++++---
 4 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/docs/plugins/CustomOgImages.md b/docs/plugins/CustomOgImages.md
index 5d47c41..4b5bf03 100644
--- a/docs/plugins/CustomOgImages.md
+++ b/docs/plugins/CustomOgImages.md
@@ -62,7 +62,7 @@
 | `socialDescription` | `description`    | Description to be used for preview. |
 | `socialImage`       | `image`, `cover` | Link to preview image.              |
 
-The `socialImage` property should contain a link to an image relative to `quartz/static`. If you have a folder for all your images in `quartz/static/my-images`, an example for `socialImage` could be `"my-images/cover.png"`.
+The `socialImage` property should contain a link to an image either relative to `quartz/static`, or a full URL. If you have a folder for all your images in `quartz/static/my-images`, an example for `socialImage` could be `"my-images/cover.png"`. Alternatively, you can use a fully qualified URL like `"https://example.com/cover.png"`.
 
 > [!info] Info
 >
diff --git a/quartz/plugins/emitters/ogImage.tsx b/quartz/plugins/emitters/ogImage.tsx
index 0b78695..7fad8c5 100644
--- a/quartz/plugins/emitters/ogImage.tsx
+++ b/quartz/plugins/emitters/ogImage.tsx
@@ -1,7 +1,7 @@
 import { QuartzEmitterPlugin } from "../types"
 import { i18n } from "../../i18n"
 import { unescapeHTML } from "../../util/escape"
-import { FullSlug, getFileExtension, joinSegments, QUARTZ } from "../../util/path"
+import { FullSlug, getFileExtension, isAbsoluteURL, joinSegments, QUARTZ } from "../../util/path"
 import { ImageOptions, SocialImageOptions, defaultImage, getSatoriFonts } from "../../util/og"
 import sharp from "sharp"
 import satori, { SatoriOptions } from "satori"
@@ -144,13 +144,19 @@
         additionalHead: [
           (pageData) => {
             const isRealFile = pageData.filePath !== undefined
-            const userDefinedOgImagePath = pageData.frontmatter?.socialImage
+            let userDefinedOgImagePath = pageData.frontmatter?.socialImage
+
+            if (userDefinedOgImagePath) {
+              userDefinedOgImagePath = isAbsoluteURL(userDefinedOgImagePath)
+                ? userDefinedOgImagePath
+                : `https://${baseUrl}/static/${userDefinedOgImagePath}`
+            }
+
             const generatedOgImagePath = isRealFile
               ? `https://${baseUrl}/${pageData.slug!}-og-image.webp`
               : undefined
             const defaultOgImagePath = `https://${baseUrl}/static/og-image.png`
             const ogImagePath = userDefinedOgImagePath ?? generatedOgImagePath ?? defaultOgImagePath
-
             const ogImageMimeType = `image/${getFileExtension(ogImagePath) ?? "png"}`
             return (
               <>
diff --git a/quartz/util/path.test.ts b/quartz/util/path.test.ts
index 29d845d..9f94c68 100644
--- a/quartz/util/path.test.ts
+++ b/quartz/util/path.test.ts
@@ -38,6 +38,17 @@
     assert(!path.isRelativeURL("./abc/def.md"))
   })
 
+  test("isAbsoluteURL", () => {
+    assert(path.isAbsoluteURL("https://example.com"))
+    assert(path.isAbsoluteURL("http://example.com"))
+    assert(path.isAbsoluteURL("ftp://example.com/a/b/c"))
+    assert(path.isAbsoluteURL("http://host/%25"))
+    assert(path.isAbsoluteURL("file://host/twoslashes?more//slashes"))
+
+    assert(!path.isAbsoluteURL("example.com/abc/def"))
+    assert(!path.isAbsoluteURL("abc"))
+  })
+
   test("isFullSlug", () => {
     assert(path.isFullSlug("index"))
     assert(path.isFullSlug("abc/def"))
diff --git a/quartz/util/path.ts b/quartz/util/path.ts
index 0681fae..b957701 100644
--- a/quartz/util/path.ts
+++ b/quartz/util/path.ts
@@ -1,6 +1,7 @@
 import { slug as slugAnchor } from "github-slugger"
 import type { Element as HastElement } from "hast"
 import { clone } from "./clone"
+
 // this file must be isomorphic so it can't use node libs (e.g. path)
 
 export const QUARTZ = "quartz"
@@ -39,6 +40,15 @@
   return validStart && validEnding && ![".md", ".html"].includes(getFileExtension(s) ?? "")
 }
 
+export function isAbsoluteURL(s: string): boolean {
+  try {
+    new URL(s)
+  } catch {
+    return false
+  }
+  return true
+}
+
 export function getFullSlug(window: Window): FullSlug {
   const res = window.document.body.dataset.slug! as FullSlug
   return res

--
Gitblit v1.10.0