Skip to content

Commit 377d80c

Browse files
committed
chore: prefer unstorage driver for fonts
1 parent dab7c74 commit 377d80c

File tree

2 files changed

+47
-38
lines changed

2 files changed

+47
-38
lines changed

src/module.ts

+39-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as fs from 'node:fs'
2-
import { readFile, writeFile } from 'node:fs/promises'
2+
import { readFile } from 'node:fs/promises'
33
import {
44
type AddComponentOptions,
55
addComponent,
@@ -19,11 +19,13 @@ import type { SatoriOptions } from 'satori'
1919
import { installNuxtSiteConfig } from 'nuxt-site-config-kit'
2020
import { isDevelopment } from 'std-env'
2121
import { hash } from 'ohash'
22-
import { basename, join, relative } from 'pathe'
22+
import { basename, isAbsolute, join, relative } from 'pathe'
2323
import type { ResvgRenderOptions } from '@resvg/resvg-js'
2424
import type { SharpOptions } from 'sharp'
2525
import { defu } from 'defu'
2626
import { readPackageJSON } from 'pkg-types'
27+
import { createStorage } from 'unstorage'
28+
import fsDriver from 'unstorage/drivers/fs'
2729
import type {
2830
CompatibilityFlagEnvOverrides,
2931
FontConfig,
@@ -45,7 +47,7 @@ import { setupGenerateHandler } from './build/generate'
4547
import { setupPrerenderHandler } from './build/prerender'
4648
import { setupBuildHandler } from './build/build'
4749
import { checkLocalChrome, checkPlaywrightDependency, downloadFont, isUndefinedOrTruthy } from './util'
48-
import { normaliseFontInput } from './runtime/utils.pure'
50+
import { normaliseFontInput } from './runtime/pure'
4951

5052
export interface ModuleOptions {
5153
/**
@@ -260,45 +262,48 @@ export default defineNuxtModule<ModuleOptions>({
260262
addServerPlugin(resolve('./runtime/nitro/plugins/nuxt-content'))
261263

262264
// default font is inter
263-
if (!config.fonts.length)
264-
config.fonts = ['Inter:400', 'Inter:700']
265-
266-
if (preset === 'cloudflare' || preset === 'cloudflare-module') {
267-
config.fonts = config.fonts.filter((f) => {
268-
if (typeof f !== 'string' && f.path) {
269-
logger.warn(`The ${f.name}:${f.weight} font was skipped because remote fonts are not available in Cloudflare Workers, please use a Google Font.`)
270-
return false
271-
}
272-
return true
273-
})
265+
if (!config.fonts.length) {
266+
config.fonts = [
267+
{ name: 'Inter', weight: 400, path: resolve('./assets/Inter-400.ttf.base64') },
268+
{ name: 'Inter', weight: 700, path: resolve('./assets/Inter-700.ttf.base64') },
269+
]
274270
}
275-
const serverFontsDir = resolve('./runtime/nitro/fonts')
276-
config.fonts = await Promise.all(normaliseFontInput(config.fonts)
271+
272+
const serverFontsDir = resolve(nuxt.options.buildDir, 'cache', `nuxt-og-image@${version}`, '_fonts')
273+
// mkdir@
274+
const fontStorage = createStorage({
275+
driver: fsDriver({
276+
base: serverFontsDir,
277+
}),
278+
})
279+
config.fonts = (await Promise.all(normaliseFontInput(config.fonts)
277280
.map(async (f) => {
278281
if (!f.key && !f.path) {
279-
if (await downloadFont(f, serverFontsDir, config.googleFontMirror)) {
282+
if (preset === 'stackblitz') {
283+
logger.warn(`The ${f.name}:${f.weight} font was skipped because remote fonts are not available in StackBlitz, please use a local font.`)
284+
return false
285+
}
286+
if (await downloadFont(f, fontStorage, config.googleFontMirror)) {
280287
// move file to serverFontsDir
281288
f.key = `nuxt-og-image:fonts:${f.name}-${f.weight}.ttf.base64`
282289
}
290+
else {
291+
logger.warn(`Failed to download font ${f.name}:${f.weight}. You may be offline or behind a firewall blocking Google. Consider setting \`googleFontMirror: true\`.`)
292+
return false
293+
}
283294
}
284295
else if (f.path) {
285-
// move to assets folder as base64 and set key
286-
const fontPath = join(nuxt.options.rootDir, nuxt.options.dir.public, f.path)
287-
const fontData = await readFile(fontPath, 'base64')
296+
// resolve relative paths from public dir
297+
if (!isAbsolute(f.path)) {
298+
// move to assets folder as base64 and set key
299+
f.path = join(nuxt.options.rootDir, nuxt.options.dir.public, f.path)
300+
}
301+
const fontData = await readFile(f.path, 'base64')
288302
f.key = `nuxt-og-image:fonts:${f.name}-${f.weight}.ttf.base64`
289-
await writeFile(resolve(serverFontsDir, `${basename(f.path)}.base64`), fontData)
303+
await fontStorage.setItem(`${basename(f.path)}.base64`, fontData)
290304
}
291305
return f
292-
}))
293-
config.fonts = config.fonts.map((f) => {
294-
if (preset === 'stackblitz') {
295-
if (typeof f === 'string' || (!f.path && !f.key)) {
296-
logger.warn(`The ${typeof f === 'string' ? f : `${f.name}:${f.weight}`} font was skipped because remote fonts are not available in StackBlitz, please use a local font.`)
297-
return false
298-
}
299-
}
300-
return f
301-
}).filter(Boolean) as InputFontConfig[]
306+
}))).filter(Boolean) as InputFontConfig[]
302307

303308
// bundle fonts within nitro runtime
304309
nuxt.options.nitro.serverAssets = nuxt.options.nitro.serverAssets || []
@@ -459,6 +464,9 @@ declare module '#nuxt-og-image/components' {
459464
${componentImports}
460465
}
461466
}
467+
declare module '#nuxt-og-image/unocss-config' {
468+
export type theme = any
469+
}
462470
`
463471
})
464472

src/util.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { writeFile } from 'node:fs/promises'
2-
import { existsSync } from 'node:fs'
31
import { Launcher } from 'chrome-launcher'
42
import { tryResolveModule } from '@nuxt/kit'
53
import { isCI } from 'std-env'
64
import { $fetch } from 'ofetch'
7-
import { join } from 'pathe'
5+
import type { Storage } from 'unstorage'
86
import type { ResolvedFontConfig } from './runtime/types'
97

108
export const isUndefinedOrTruthy = (v?: any) => typeof v === 'undefined' || v !== false
@@ -26,20 +24,23 @@ export async function checkPlaywrightDependency() {
2624
return !!(await tryResolveModule('playwright'))
2725
}
2826

29-
export async function downloadFont(font: ResolvedFontConfig, outputPath: string, mirror?: true | string) {
27+
export async function downloadFont(font: ResolvedFontConfig, storage: Storage, mirror?: true | string) {
3028
const { name, weight } = font
31-
const fontPath = join(outputPath, `${name}-${weight}.ttf.base64`)
32-
if (existsSync(fontPath))
29+
const key = `${name}-${weight}.ttf.base64`
30+
if (await storage.hasItem(key))
3331
return true
3432

3533
const host = typeof mirror === 'undefined' ? 'fonts.googleapis.com' : mirror === true ? 'fonts.font.im' : mirror
3634
// using H3Event $fetch will cause the request headers not to be sent
3735
const css = await $fetch(`https://${host}/css2?family=${name}:wght@${weight}`, {
36+
timeout: 10 * 1000, // 10 second timeout
3837
headers: {
3938
// Make sure it returns TTF.
4039
'User-Agent':
4140
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1',
4241
},
42+
}).catch(() => {
43+
return false
4344
})
4445
if (!css)
4546
return false
@@ -50,7 +51,7 @@ export async function downloadFont(font: ResolvedFontConfig, outputPath: string,
5051
// need to base 64 the buf
5152
const base64Font = Buffer.from(buf).toString('base64')
5253
// output to outputPath
53-
await writeFile(fontPath, base64Font)
54+
await storage.setItem(key, base64Font)
5455
return true
5556
}
5657
return false

0 commit comments

Comments
 (0)