From 3ce6aa49bf25b26a4cb1bf18e9770271d132772d Mon Sep 17 00:00:00 2001 From: Karim <46734059+h-karim@users.noreply.github.com> Date: Fri, 21 Mar 2025 19:49:56 -0400 Subject: [PATCH] fix(ogImage): update socialImage path to include base URL if defined (#1858) * fix(ogImage): update socialImage path to include base URL if defined * feat(path): add function to check if a file path is absolute * fix(ogImage): handle absolute paths for user defined og image paths * docs(CustomOgImages): update socialImage property to accept full URLs * fix(ogImage): typo * fix(ogImage): improve user-defined OG image path handling * Update docs/plugins/CustomOgImages.md Co-authored-by: Jacky Zhao * Update quartz/plugins/emitters/ogImage.tsx Co-authored-by: Jacky Zhao * refactor(path): remove isAbsoluteFilePath function * fix(ogImage): update user-defined OG image path handling to support relative URLs * feat(ogImage): enhance user-defined OG image path handling with absolute URL support * refactor(ogImage): remove debug log for ogImagePath * feat(path): add isAbsoluteURL function and corresponding tests * refactor(path): remove unused URL import for isomorphic compatibility --------- Co-authored-by: Karim H Co-authored-by: Jacky Zhao --- docs/plugins/CustomOgImages.md | 2 +- quartz/plugins/emitters/ogImage.tsx | 12 +++++++++--- quartz/util/path.test.ts | 11 +++++++++++ quartz/util/path.ts | 10 ++++++++++ 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 @@ The following properties can be used to customize your link previews: | `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 @@ export const CustomOgImages: QuartzEmitterPlugin> = 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 @@ describe("typeguards", () => { 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 @@ export function isRelativeURL(s: string): s is RelativeURL { 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