| | |
| | | import { FullSlug, _stripSlashes, joinSegments, pathToRoot } from "../util/path" |
| | | import { JSResourceToScriptElement } from "../util/resources" |
| | | import { QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | |
| | | import { i18n } from "../i18n" |
| | | import { FullSlug, getFileExtension, joinSegments, pathToRoot } from "../util/path" |
| | | import { CSSResourceToStyleElement, JSResourceToScriptElement } from "../util/resources" |
| | | import { googleFontHref, googleFontSubsetHref } from "../util/theme" |
| | | import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" |
| | | import { unescapeHTML } from "../util/escape" |
| | | import { CustomOgImagesEmitterName } from "../plugins/emitters/ogImage" |
| | | export default (() => { |
| | | function Head({ cfg, fileData, externalResources }: QuartzComponentProps) { |
| | | const title = fileData.frontmatter?.title ?? "Untitled" |
| | | const description = fileData.description?.trim() ?? "No description provided" |
| | | const { css, js } = externalResources |
| | | const Head: QuartzComponent = ({ |
| | | cfg, |
| | | fileData, |
| | | externalResources, |
| | | ctx, |
| | | }: QuartzComponentProps) => { |
| | | const titleSuffix = cfg.pageTitleSuffix ?? "" |
| | | const title = |
| | | (fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title) + titleSuffix |
| | | const description = |
| | | fileData.frontmatter?.socialDescription ?? |
| | | fileData.frontmatter?.description ?? |
| | | unescapeHTML(fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description) |
| | | |
| | | const { css, js, additionalHead } = externalResources |
| | | |
| | | const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`) |
| | | const path = url.pathname as FullSlug |
| | | const baseDir = fileData.slug === "404" ? path : pathToRoot(fileData.slug!) |
| | | |
| | | const iconPath = joinSegments(baseDir, "static/icon.png") |
| | | const ogImagePath = `https://${cfg.baseUrl}/static/og-image.png` |
| | | const manifest = |
| | | cfg.baseUrl == undefined ? "/manifest.json" : `https://${cfg.baseUrl}/manifest.json` |
| | | |
| | | // Url of current page |
| | | const socialUrl = |
| | | fileData.slug === "404" ? url.toString() : joinSegments(url.toString(), fileData.slug!) |
| | | |
| | | const usesCustomOgImage = ctx.cfg.plugins.emitters.some( |
| | | (e) => e.name === CustomOgImagesEmitterName, |
| | | ) |
| | | const ogImageDefaultPath = `https://${cfg.baseUrl}/static/og-image.png` |
| | | |
| | | return ( |
| | | <head> |
| | | <title>{title}</title> |
| | | <meta charSet="utf-8" /> |
| | | {cfg.theme.cdnCaching && cfg.theme.fontOrigin === "googleFonts" && ( |
| | | <> |
| | | <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| | | <link rel="preconnect" href="https://fonts.gstatic.com" /> |
| | | <link rel="stylesheet" href={googleFontHref(cfg.theme)} /> |
| | | {cfg.theme.typography.title && ( |
| | | <link rel="stylesheet" href={googleFontSubsetHref(cfg.theme, cfg.pageTitle)} /> |
| | | )} |
| | | </> |
| | | )} |
| | | <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossOrigin="anonymous" /> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| | | |
| | | <meta name="og:site_name" content={cfg.pageTitle}></meta> |
| | | <meta property="og:title" content={title} /> |
| | | <meta property="og:type" content="website" /> |
| | | <meta name="twitter:card" content="summary_large_image" /> |
| | | <meta name="twitter:title" content={title} /> |
| | | <meta name="twitter:description" content={description} /> |
| | | <meta property="og:description" content={description} /> |
| | | {cfg.baseUrl && <meta property="og:image" content={ogImagePath} />} |
| | | <meta property="og:width" content="1200" /> |
| | | <meta property="og:height" content="675" /> |
| | | <meta name="theme-color" content="#faf8f8" /> |
| | | <meta property="og:image:alt" content={description} /> |
| | | |
| | | {!usesCustomOgImage && ( |
| | | <> |
| | | <meta property="og:image" content={ogImageDefaultPath} /> |
| | | <meta property="og:image:url" content={ogImageDefaultPath} /> |
| | | <meta name="twitter:image" content={ogImageDefaultPath} /> |
| | | <meta |
| | | property="og:image:type" |
| | | content={`image/${getFileExtension(ogImageDefaultPath) ?? "png"}`} |
| | | /> |
| | | </> |
| | | )} |
| | | |
| | | {cfg.baseUrl && ( |
| | | <> |
| | | <meta property="twitter:domain" content={cfg.baseUrl}></meta> |
| | | <meta property="og:url" content={socialUrl}></meta> |
| | | <meta property="twitter:url" content={socialUrl}></meta> |
| | | </> |
| | | )} |
| | | |
| | | <link rel="icon" href={iconPath} /> |
| | | <link rel="manifest" href={manifest} /> |
| | | <meta name="description" content={description} /> |
| | | <meta name="generator" content="Quartz" /> |
| | | <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| | | <link rel="preconnect" href="https://fonts.gstatic.com" /> |
| | | {css.map((href) => ( |
| | | <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve /> |
| | | ))} |
| | | |
| | | {css.map((resource) => CSSResourceToStyleElement(resource, true))} |
| | | {js |
| | | .filter((resource) => resource.loadTime === "beforeDOMReady") |
| | | .map((res) => JSResourceToScriptElement(res, true))} |
| | | {additionalHead.map((resource) => { |
| | | if (typeof resource === "function") { |
| | | return resource(fileData) |
| | | } else { |
| | | return resource |
| | | } |
| | | })} |
| | | </head> |
| | | ) |
| | | } |