fix: parse parallelization chunk arg, inline b64 for og image
This commit is contained in:
		@@ -1,7 +1,7 @@
 | 
			
		||||
import { QuartzEmitterPlugin } from "../types"
 | 
			
		||||
import { i18n } from "../../i18n"
 | 
			
		||||
import { unescapeHTML } from "../../util/escape"
 | 
			
		||||
import { FullSlug, getFileExtension } from "../../util/path"
 | 
			
		||||
import { FullSlug, getFileExtension, joinSegments, QUARTZ } from "../../util/path"
 | 
			
		||||
import { ImageOptions, SocialImageOptions, defaultImage, getSatoriFonts } from "../../util/og"
 | 
			
		||||
import sharp from "sharp"
 | 
			
		||||
import satori, { SatoriOptions } from "satori"
 | 
			
		||||
@@ -10,6 +10,8 @@ import { Readable } from "stream"
 | 
			
		||||
import { write } from "./helpers"
 | 
			
		||||
import { BuildCtx } from "../../util/ctx"
 | 
			
		||||
import { QuartzPluginData } from "../vfile"
 | 
			
		||||
import fs from "node:fs/promises"
 | 
			
		||||
import chalk from "chalk"
 | 
			
		||||
 | 
			
		||||
const defaultOptions: SocialImageOptions = {
 | 
			
		||||
  colorScheme: "lightMode",
 | 
			
		||||
@@ -28,7 +30,25 @@ async function generateSocialImage(
 | 
			
		||||
  userOpts: SocialImageOptions,
 | 
			
		||||
): Promise<Readable> {
 | 
			
		||||
  const { width, height } = userOpts
 | 
			
		||||
  const imageComponent = userOpts.imageStructure(cfg, userOpts, title, description, fonts, fileData)
 | 
			
		||||
  const iconPath = joinSegments(QUARTZ, "static", "icon.png")
 | 
			
		||||
  let iconBase64: string | undefined = undefined
 | 
			
		||||
  try {
 | 
			
		||||
    const iconData = await fs.readFile(iconPath)
 | 
			
		||||
    iconBase64 = `data:image/png;base64,${iconData.toString("base64")}`
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.warn(chalk.yellow(`Warning: Could not find icon at ${iconPath}`))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const imageComponent = userOpts.imageStructure({
 | 
			
		||||
    cfg,
 | 
			
		||||
    userOpts,
 | 
			
		||||
    title,
 | 
			
		||||
    description,
 | 
			
		||||
    fonts,
 | 
			
		||||
    fileData,
 | 
			
		||||
    iconBase64,
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  const svg = await satori(imageComponent, {
 | 
			
		||||
    width,
 | 
			
		||||
    height,
 | 
			
		||||
 
 | 
			
		||||
@@ -172,7 +172,7 @@ export async function parseMarkdown(ctx: BuildCtx, fps: FilePath[]): Promise<Pro
 | 
			
		||||
      workerType: "thread",
 | 
			
		||||
    })
 | 
			
		||||
    const errorHandler = (err: any) => {
 | 
			
		||||
      console.error(`${err}`.replace(/^error:\s*/i, ""))
 | 
			
		||||
      console.error(err)
 | 
			
		||||
      process.exit(1)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -201,7 +201,7 @@ export async function parseMarkdown(ctx: BuildCtx, fps: FilePath[]): Promise<Pro
 | 
			
		||||
 | 
			
		||||
    const markdownToHtmlPromises: WorkerPromise<ProcessedContent[]>[] = []
 | 
			
		||||
    processedFiles = 0
 | 
			
		||||
    for (const [mdChunk, _] of mdResults) {
 | 
			
		||||
    for (const mdChunk of mdResults) {
 | 
			
		||||
      markdownToHtmlPromises.push(pool.exec("processHtml", [serializableCtx, mdChunk]))
 | 
			
		||||
    }
 | 
			
		||||
    const results: ProcessedContent[][] = await Promise.all(
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ export class QuartzLogger {
 | 
			
		||||
        const truncated = truncate(output, columns)
 | 
			
		||||
        process.stdout.write(truncated)
 | 
			
		||||
        this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerChars.length
 | 
			
		||||
      }, 20)
 | 
			
		||||
      }, 50)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import chalk from "chalk"
 | 
			
		||||
 | 
			
		||||
const defaultHeaderWeight = [700]
 | 
			
		||||
const defaultBodyWeight = [400]
 | 
			
		||||
 | 
			
		||||
export async function getSatoriFonts(headerFont: FontSpecification, bodyFont: FontSpecification) {
 | 
			
		||||
  // Get all weights for header and body fonts
 | 
			
		||||
  const headerWeights: FontWeight[] = (
 | 
			
		||||
@@ -134,21 +135,12 @@ export type SocialImageOptions = {
 | 
			
		||||
  excludeRoot: boolean
 | 
			
		||||
  /**
 | 
			
		||||
   * JSX to use for generating image. See satori docs for more info (https://github.com/vercel/satori)
 | 
			
		||||
   * @param cfg global quartz config
 | 
			
		||||
   * @param userOpts options that can be set by user
 | 
			
		||||
   * @param title title of current page
 | 
			
		||||
   * @param description description of current page
 | 
			
		||||
   * @param fonts global font that can be used for styling
 | 
			
		||||
   * @param fileData full fileData of current page
 | 
			
		||||
   * @returns prepared jsx to be used for generating image
 | 
			
		||||
   */
 | 
			
		||||
  imageStructure: (
 | 
			
		||||
    cfg: GlobalConfiguration,
 | 
			
		||||
    userOpts: UserOpts,
 | 
			
		||||
    title: string,
 | 
			
		||||
    description: string,
 | 
			
		||||
    fonts: SatoriOptions["fonts"],
 | 
			
		||||
    fileData: QuartzPluginData,
 | 
			
		||||
    options: ImageOptions & {
 | 
			
		||||
      userOpts: UserOpts
 | 
			
		||||
      iconBase64?: string
 | 
			
		||||
    },
 | 
			
		||||
  ) => JSXInternal.Element
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -178,17 +170,17 @@ export type ImageOptions = {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This is the default template for generated social image.
 | 
			
		||||
export const defaultImage: SocialImageOptions["imageStructure"] = (
 | 
			
		||||
  cfg: GlobalConfiguration,
 | 
			
		||||
  { colorScheme }: UserOpts,
 | 
			
		||||
  title: string,
 | 
			
		||||
  description: string,
 | 
			
		||||
  _fonts: SatoriOptions["fonts"],
 | 
			
		||||
  fileData: QuartzPluginData,
 | 
			
		||||
) => {
 | 
			
		||||
export const defaultImage: SocialImageOptions["imageStructure"] = ({
 | 
			
		||||
  cfg,
 | 
			
		||||
  userOpts,
 | 
			
		||||
  title,
 | 
			
		||||
  description,
 | 
			
		||||
  fileData,
 | 
			
		||||
  iconBase64,
 | 
			
		||||
}) => {
 | 
			
		||||
  const { colorScheme } = userOpts
 | 
			
		||||
  const fontBreakPoint = 32
 | 
			
		||||
  const useSmallerFont = title.length > fontBreakPoint
 | 
			
		||||
  const iconPath = `https://${cfg.baseUrl}/static/icon.png`
 | 
			
		||||
 | 
			
		||||
  // Format date if available
 | 
			
		||||
  const rawDate = getDate(cfg, fileData)
 | 
			
		||||
@@ -226,14 +218,16 @@ export const defaultImage: SocialImageOptions["imageStructure"] = (
 | 
			
		||||
          marginBottom: "0.5rem",
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        {iconBase64 && (
 | 
			
		||||
          <img
 | 
			
		||||
          src={iconPath}
 | 
			
		||||
            src={iconBase64}
 | 
			
		||||
            width={56}
 | 
			
		||||
            height={56}
 | 
			
		||||
            style={{
 | 
			
		||||
              borderRadius: "50%",
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
        <div
 | 
			
		||||
          style={{
 | 
			
		||||
            display: "flex",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user