Skip to content

Commit

Permalink
feat(web-fonts): add local font support to web font preset
Browse files Browse the repository at this point in the history
WIP
  • Loading branch information
onmax committed Apr 11, 2024
1 parent 25e97ca commit 3292bd9
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 10 deletions.
5 changes: 1 addition & 4 deletions packages/preset-web-fonts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ export * from './types'
export { normalizedFontMeta }
export { createGoogleCompatibleProvider as createGoogleProvider } from './providers/google'

const userAgentWoff2 = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'
const defaultFetch = async (url: string) => (await import('ofetch')).$fetch(url, { headers: { 'User-Agent': userAgentWoff2 }, retry: 3 })

/**
* Preset for using web fonts by provide just the names.
*
* @see https://unocss.dev/presets/web-fonts
*/
const presetWebFonts = definePreset(createWebFontPreset(defaultFetch))
const presetWebFonts = definePreset(createWebFontPreset())

export default presetWebFonts
63 changes: 63 additions & 0 deletions packages/preset-web-fonts/src/local-font.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Inspired by:
* https://github.com/feat-agency/vite-plugin-webfont-dl/blob/master/src/downloader.ts
*/

import { mkdir, stat, writeFile } from 'node:fs/promises'
import { Buffer } from 'node:buffer'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { WebFontsOptions } from './types'

const fontUrlRegex = /[-a-z0-9@:%_+.~#?&/=]+\.(?:woff2?|eot|ttf|otf|svg)/gi

const dir = dirname(fileURLToPath(import.meta.url))

const cachedUrls = new Map<string, string>()

export async function useLocalFont(css: string, fetch: WebFontsOptions['customFetch']) {
const allUrls = css.match(fontUrlRegex)?.map(url => url.trim()) || []
const newUrls = []
for (const url of allUrls) {
if (cachedUrls.has(url))
css = css.replace(url, cachedUrls.get(url)!)
else
newUrls.push(url)
}

if (newUrls.length === 0)
return css

async function saveFont(url: string, path: string) {
const response = await fetch!(url, { headers: { responseType: 'arraybuffer' } }) as ArrayBuffer
const content = new Uint8Array(response)
await writeFile(path, Buffer.from(content))
return { url, path }
}

// FIXME Currently hardcoded to the playground folder
const fontsFolder = resolve(dir, '../../../playground/assets/fonts')
await mkdir(fontsFolder, { recursive: true })

const fontAlreadyDownloaded = async (path: string) => await stat(path).then(r => r.isFile()).catch(() => false)

const promises = []

for (const url of newUrls) {
const path = resolve(fontsFolder, url.split('/').pop()!)
const ignore = await fontAlreadyDownloaded(path)
if (ignore)
cachedUrls.set(url, path)
else
promises.push(saveFont(url, path))
}

const localFonts = await Promise.all(promises)

for (const localFont of localFonts) {
css = css.replace(localFont.url, localFont.path)
cachedUrls.set(localFont.url, localFont.path)
}

return css
}
24 changes: 19 additions & 5 deletions packages/preset-web-fonts/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { BunnyFontsProvider } from './providers/bunny'
import { GoogleFontsProvider } from './providers/google'
import { FontshareProvider } from './providers/fontshare'
import { NoneProvider } from './providers/none'
import type { Provider, ResolvedWebFontMeta, WebFontMeta, WebFontsOptions, WebFontsProviders } from './types'
import type { CustomFetchOptions, Provider, ResolvedWebFontMeta, WebFontMeta, WebFontsOptions, WebFontsProviders } from './types'
import { useLocalFont } from './local-font'

const builtinProviders = {
google: GoogleFontsProvider,
Expand Down Expand Up @@ -37,14 +38,23 @@ export function normalizedFontMeta(meta: WebFontMeta | string, defaultProvider:
}
}

export function createWebFontPreset(fetcher: (url: string) => Promise<any>) {
const userAgentWoff2 = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'
const defaultHeaders: HeadersInit = { 'User-Agent': userAgentWoff2 }

async function defaultFetch(url: string, { headers = defaultHeaders }: CustomFetchOptions = {}) {
const { $fetch } = (await import('ofetch'))
return await $fetch(url, { headers, retry: 3 })
}

export function createWebFontPreset() {
return (options: WebFontsOptions = {}): Preset<any> => {
const {
provider: defaultProvider = 'google',
extendTheme = true,
inlineImports = true,
themeKey = 'fontFamily',
customFetch = fetcher,
customFetch = defaultFetch,
downloadLocally = false,
} = options

const fontObject = Object.fromEntries(
Expand Down Expand Up @@ -87,8 +97,12 @@ export function createWebFontPreset(fetcher: (url: string) => Promise<any>) {

if (provider.getImportUrl) {
const url = provider.getImportUrl(fontsForProvider)
if (url)
preflights.push(await importUrl(url))
if (url) {
let css = await importUrl(url)
if (downloadLocally)
css = await useLocalFont(css, customFetch)
preflights.push(css)
}
}

preflights.push(provider.getPreflight?.(fontsForProvider))
Expand Down
12 changes: 11 additions & 1 deletion packages/preset-web-fonts/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export interface ResolvedWebFontMeta extends Omit<WebFontMeta, 'provider'> {
provider: Provider
}

export interface CustomFetchOptions {
headers?: HeadersInit
}

export interface WebFontsOptions {
/**
* Provider service of the web fonts
Expand Down Expand Up @@ -52,7 +56,13 @@ export interface WebFontsOptions {
*
* @default undefined
*/
customFetch?: (url: string) => Promise<any>
customFetch?: (url: string, options?: CustomFetchOptions) => Promise<any>

/**
* Whether to download the font locally or not
* @default false
*/
downloadLocally?: boolean
}

export interface Provider {
Expand Down
9 changes: 9 additions & 0 deletions playground/uno.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
import presetWebFonts from '../packages/preset-web-fonts/src/'

export default defineConfig({
theme: {
Expand All @@ -22,6 +23,14 @@ export default defineConfig({
presetAttributify(),
presetUno(),
presetIcons(),
presetWebFonts({
downloadLocally: true,
provider: 'google',
fonts: {
sans: 'Roboto',
mono: ['Fira Code', 'Fira Mono:400,700'],
},
}),
],
transformers: [
transformerCompileClass(),
Expand Down

0 comments on commit 3292bd9

Please sign in to comment.