From e80ade76be99a584c280ef9fc8e704dde0837d32 Mon Sep 17 00:00:00 2001 From: Stephen Tse Date: Sat, 26 Apr 2025 18:24:12 -0700 Subject: [PATCH] Misc impl optimizations --- quartz/plugins/emitters/assets.ts | 24 +++++++++++++----------- quartz/plugins/transformers/ofm.ts | 27 ++++++++++++++++++--------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/quartz/plugins/emitters/assets.ts b/quartz/plugins/emitters/assets.ts index 077ec94e2..615d35930 100644 --- a/quartz/plugins/emitters/assets.ts +++ b/quartz/plugins/emitters/assets.ts @@ -7,12 +7,14 @@ import { Argv } from "../../util/ctx" import { QuartzConfig } from "../../cfg" import sharp from "sharp" -// Sharp doesn't support BMP input out of the box: -// https://github.com/lovell/sharp/issues/543 -// GIF processing can be very slow or ineffective (larger file size when CPU effort is -// set to a low number); only enable after testing: -// https://github.com/lovell/sharp/issues/3176 -const imageExtsToOptimize: Set = new Set([".png", ".jpg", ".jpeg"]) +// - Sharp doesn't support BMP input out of the box: +// https://github.com/lovell/sharp/issues/543 +// - GIF / GIFV processing can be very slow or ineffective (larger file size when +// CPU effort is set to a low number); only enable after testing: +// https://github.com/lovell/sharp/issues/3176 +export const imageExtsToOptimize: ReadonlySet = new Set([".png", ".jpg", ".jpeg"]) +// Remember to also update sharp to use the right extension. +export const targetOptimizedImageExt = ".webp" const filesToCopy = async (argv: Argv, cfg: QuartzConfig) => { // glob all non MD files in content folder and copy it over @@ -26,7 +28,7 @@ const copyFile = async (argv: Argv, cfg: QuartzConfig, fp: FilePath) => { const doOptimizeImage = cfg.configuration.optimizeImages && imageExtsToOptimize.has(srcExt) const name = doOptimizeImage - ? ((slugifyFilePath(fp, true) + ".webp") as FullSlug) + ? ((slugifyFilePath(fp, true) + targetOptimizedImageExt) as FullSlug) : slugifyFilePath(fp) const dest = joinSegments(argv.output, name) as FilePath @@ -44,10 +46,10 @@ const copyFile = async (argv: Argv, cfg: QuartzConfig, fp: FilePath) => { async function processImage(src: FilePath, dest: FilePath) { const originalFile = await fs.promises.readFile(src) - const convertedFile = await sharp(originalFile) + await sharp(originalFile, { animated: false }) .webp({ quality: 90, smartSubsample: true, effort: 6 }) - .toBuffer() - await fs.promises.writeFile(dest, convertedFile) + // .avif({ quality: 90, effort: 9, chromaSubsampling: "4:2:0", bitdepth: 8 }) + .toFile(dest) } export const Assets: QuartzEmitterPlugin = () => { @@ -70,7 +72,7 @@ export const Assets: QuartzEmitterPlugin = () => { const doOptimizeImage = ctx.cfg.configuration.optimizeImages && imageExtsToOptimize.has(ext) const name = doOptimizeImage - ? ((slugifyFilePath(changeEvent.path, true) + ".webp") as FullSlug) + ? ((slugifyFilePath(changeEvent.path, true) + targetOptimizedImageExt) as FullSlug) : slugifyFilePath(changeEvent.path) const dest = joinSegments(ctx.argv.output, name) as FilePath await fs.promises.unlink(dest) diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index e829a0ada..abbc55511 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -27,6 +27,7 @@ import { toHast } from "mdast-util-to-hast" import { toHtml } from "hast-util-to-html" import { capitalize } from "../../util/lang" import { PluggableList } from "unified" +import { imageExtsToOptimize, targetOptimizedImageExt } from "../emitters/assets" export interface Options { comments: boolean @@ -146,6 +147,18 @@ const wikilinkImageEmbedRegex = new RegExp( /^(?(?!^\d*x?\d*$).*?)?(\|?\s*?(?\d+)(x(?\d+))?)?$/, ) +const supportedImageExts: ReadonlySet = new Set([ + ".png", + ".jpg", + ".jpeg", + ".gif", + ".gifv", + ".bmp", + ".svg", + ".webp", + ".avif", +]) + export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> = (userOpts) => { const opts = { ...defaultOptions, ...userOpts } @@ -228,16 +241,12 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> if (value.startsWith("!")) { const ext: string = path.extname(fp).toLowerCase() let url = slugifyFilePath(fp as FilePath) - if ( - [".png", ".jpg", ".jpeg", ".gif", ".gifv", ".bmp", ".svg", ".webp"].includes( - ext, - ) - ) { - // Replace extension of eligible image files with ".webp" if image optimization is enabled. + if (supportedImageExts.has(ext)) { + // Replace extension of eligible image files with target extension if image optimization is enabled. url = - ctx.cfg.configuration.optimizeImages && - [".png", ".jpg", ".jpeg"].includes(ext) - ? ((slugifyFilePath(fp as FilePath, true) + ".webp") as FullSlug) + ctx.cfg.configuration.optimizeImages && imageExtsToOptimize.has(ext) + ? ((slugifyFilePath(fp as FilePath, true) + + targetOptimizedImageExt) as FullSlug) : url const match = wikilinkImageEmbedRegex.exec(alias ?? "") const alt = match?.groups?.alt ?? ""