Misc impl optimizations

This commit is contained in:
Stephen Tse 2025-04-26 18:24:12 -07:00
parent 99add6f782
commit e80ade76be
2 changed files with 31 additions and 20 deletions

View File

@ -7,12 +7,14 @@ import { Argv } from "../../util/ctx"
import { QuartzConfig } from "../../cfg" import { QuartzConfig } from "../../cfg"
import sharp from "sharp" import sharp from "sharp"
// Sharp doesn't support BMP input out of the box: // - Sharp doesn't support BMP input out of the box:
// https://github.com/lovell/sharp/issues/543 // https://github.com/lovell/sharp/issues/543
// GIF processing can be very slow or ineffective (larger file size when CPU effort is // - GIF / GIFV processing can be very slow or ineffective (larger file size when
// set to a low number); only enable after testing: // CPU effort is set to a low number); only enable after testing:
// https://github.com/lovell/sharp/issues/3176 // https://github.com/lovell/sharp/issues/3176
const imageExtsToOptimize: Set<string> = new Set([".png", ".jpg", ".jpeg"]) export const imageExtsToOptimize: ReadonlySet<string> = 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) => { const filesToCopy = async (argv: Argv, cfg: QuartzConfig) => {
// glob all non MD files in content folder and copy it over // 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 doOptimizeImage = cfg.configuration.optimizeImages && imageExtsToOptimize.has(srcExt)
const name = doOptimizeImage const name = doOptimizeImage
? ((slugifyFilePath(fp, true) + ".webp") as FullSlug) ? ((slugifyFilePath(fp, true) + targetOptimizedImageExt) as FullSlug)
: slugifyFilePath(fp) : slugifyFilePath(fp)
const dest = joinSegments(argv.output, name) as FilePath 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) { async function processImage(src: FilePath, dest: FilePath) {
const originalFile = await fs.promises.readFile(src) const originalFile = await fs.promises.readFile(src)
const convertedFile = await sharp(originalFile) await sharp(originalFile, { animated: false })
.webp({ quality: 90, smartSubsample: true, effort: 6 }) .webp({ quality: 90, smartSubsample: true, effort: 6 })
.toBuffer() // .avif({ quality: 90, effort: 9, chromaSubsampling: "4:2:0", bitdepth: 8 })
await fs.promises.writeFile(dest, convertedFile) .toFile(dest)
} }
export const Assets: QuartzEmitterPlugin = () => { export const Assets: QuartzEmitterPlugin = () => {
@ -70,7 +72,7 @@ export const Assets: QuartzEmitterPlugin = () => {
const doOptimizeImage = const doOptimizeImage =
ctx.cfg.configuration.optimizeImages && imageExtsToOptimize.has(ext) ctx.cfg.configuration.optimizeImages && imageExtsToOptimize.has(ext)
const name = doOptimizeImage const name = doOptimizeImage
? ((slugifyFilePath(changeEvent.path, true) + ".webp") as FullSlug) ? ((slugifyFilePath(changeEvent.path, true) + targetOptimizedImageExt) as FullSlug)
: slugifyFilePath(changeEvent.path) : slugifyFilePath(changeEvent.path)
const dest = joinSegments(ctx.argv.output, name) as FilePath const dest = joinSegments(ctx.argv.output, name) as FilePath
await fs.promises.unlink(dest) await fs.promises.unlink(dest)

View File

@ -27,6 +27,7 @@ import { toHast } from "mdast-util-to-hast"
import { toHtml } from "hast-util-to-html" import { toHtml } from "hast-util-to-html"
import { capitalize } from "../../util/lang" import { capitalize } from "../../util/lang"
import { PluggableList } from "unified" import { PluggableList } from "unified"
import { imageExtsToOptimize, targetOptimizedImageExt } from "../emitters/assets"
export interface Options { export interface Options {
comments: boolean comments: boolean
@ -146,6 +147,18 @@ const wikilinkImageEmbedRegex = new RegExp(
/^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/, /^(?<alt>(?!^\d*x?\d*$).*?)?(\|?\s*?(?<width>\d+)(x(?<height>\d+))?)?$/,
) )
const supportedImageExts: ReadonlySet<string> = new Set([
".png",
".jpg",
".jpeg",
".gif",
".gifv",
".bmp",
".svg",
".webp",
".avif",
])
export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => { export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts } const opts = { ...defaultOptions, ...userOpts }
@ -228,16 +241,12 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
if (value.startsWith("!")) { if (value.startsWith("!")) {
const ext: string = path.extname(fp).toLowerCase() const ext: string = path.extname(fp).toLowerCase()
let url = slugifyFilePath(fp as FilePath) let url = slugifyFilePath(fp as FilePath)
if ( if (supportedImageExts.has(ext)) {
[".png", ".jpg", ".jpeg", ".gif", ".gifv", ".bmp", ".svg", ".webp"].includes( // Replace extension of eligible image files with target extension if image optimization is enabled.
ext,
)
) {
// Replace extension of eligible image files with ".webp" if image optimization is enabled.
url = url =
ctx.cfg.configuration.optimizeImages && ctx.cfg.configuration.optimizeImages && imageExtsToOptimize.has(ext)
[".png", ".jpg", ".jpeg"].includes(ext) ? ((slugifyFilePath(fp as FilePath, true) +
? ((slugifyFilePath(fp as FilePath, true) + ".webp") as FullSlug) targetOptimizedImageExt) as FullSlug)
: url : url
const match = wikilinkImageEmbedRegex.exec(alias ?? "") const match = wikilinkImageEmbedRegex.exec(alias ?? "")
const alt = match?.groups?.alt ?? "" const alt = match?.groups?.alt ?? ""