Skip to content

Commit

Permalink
feat: add bunny provider
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Feb 20, 2024
1 parent 1dee25e commit 3773a9d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ Plug-and-play custom web font optimization and configuration for Nuxt apps.
- [x] built-in providers
- [x] `google`
- [x] `local`
- [x] `bunny`
- [ ] `fontshare`
- [ ] `fontsource`
- [ ] `bunny`
- [x] custom providers for full control
- [x] local download support (until `nuxt/assets` lands)
- [ ] automatic font metric optimisation powered by https://github.com/unjs/fontaine
Expand Down
1 change: 1 addition & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default defineNuxtConfig({
custom: '~/providers/custom'
},
families: [
{ name: 'Abel', provider: 'bunny' },
{ name: 'Kode Mono', provider: 'none' },
{ name: 'MyCustom', src: '/font.woff2' },
{ name: 'CustomGlobal', global: true, src: '/font-global.woff2' },
Expand Down
11 changes: 11 additions & 0 deletions playground/pages/providers/bunny.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<div>
Nuxt module playground!
</div>
</template>

<style scoped>
div {
font-family: 'Abel', sans-serif;
}
</style>
6 changes: 4 additions & 2 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { addBuildPlugin, addTemplate, defineNuxtModule, resolveAlias, resolvePath, useLogger, useNuxt } from '@nuxt/kit'
import jiti from 'jiti'

import google from './providers/google'
import local from './providers/local'
import google from './providers/google'
import bunny from './providers/bunny'

import { FontFamilyInjectionPlugin } from './plugins/transform'
import { generateFontFaces } from './css/render'
Expand Down Expand Up @@ -40,6 +41,7 @@ export default defineNuxtModule<ModuleOptions>({
providers: {
local,
google,
bunny,
},
},
async setup (options, nuxt) {
Expand Down Expand Up @@ -92,7 +94,7 @@ export default defineNuxtModule<ModuleOptions>({
// Handle explicit provider
if (override.provider) {
if (override.provider in providers) {
const result = await providers[override.provider]!.resolveFontFaces!(fontFamily, override as ResolveFontFacesOptions)
const result = await providers[override.provider]!.resolveFontFaces!(fontFamily, defaults as ResolveFontFacesOptions)
if (!result) {
return logger.warn(`Could not produce font face declaration from \`${override.provider}\` for font family \`${fontFamily}\`.`)
}
Expand Down
68 changes: 68 additions & 0 deletions src/providers/bunny.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { $fetch } from 'ofetch'
import type { FontProvider, ResolveFontFacesOptions } from '../types'
import { extractFontFaceData } from '../css/parse'

export default {
async setup () {
await initialiseFontMeta()
},
async resolveFontFaces (fontFamily, defaults) {
if (!isBunnyFont(fontFamily)) { return }

return {
fonts: await getFontDetails(fontFamily, defaults)
}
},
} satisfies FontProvider

const fontAPI = $fetch.create({
baseURL: 'https://fonts.bunny.net'
})

interface BunnyFontMeta {
[key: string]: {
category: string
defSubset: string
familyName: string
isVariable: boolean
styles: string[]
variants: Record<string, number>
weights: number[]
}
}

let fonts: BunnyFontMeta
const familyMap = new Map<string, string>()

// TODO: Fetch and cache
async function initialiseFontMeta () {
fonts = await fontAPI<BunnyFontMeta>('/list', { responseType: 'json' })
for (const id in fonts) {
familyMap.set(fonts[id]!.familyName!, id)
}
}

function isBunnyFont (family: string) {
return familyMap.has(family)
}

async function getFontDetails (family: string, variants: ResolveFontFacesOptions) {
const id = familyMap.get(family) as keyof typeof fonts
const font = fonts[id]!
const weights = variants.weights?.filter(weight => font.weights.includes(Number(weight))) || font.weights
const styleMap = {
italic: 'i',
oblique: 'i',
normal: ''
}
const styles = new Set(variants.styles.map(i => styleMap[i]))
const resolvedVariants = weights.flatMap(w => [...styles].map(s => `${w}${s}`))

const css = await fontAPI('/css', {
query: {
family: id + ':' + resolvedVariants.join(',')
}
})

return extractFontFaceData(css)
}
16 changes: 16 additions & 0 deletions test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@ describe('providers', async () => {
`)
})

it('generates inlined font face rules for `bunny` provider', async () => {
const html = await $fetch('/providers/bunny')
expect(extractFontFaces('Abel', html)).toMatchInlineSnapshot(`
[
"@font-face {
font-family: 'Abel';
src: url("/file.woff2") format(woff2), url("/file.woff") format(woff);
font-display: swap;
unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
font-weight: 400;
font-style: normal;
}",
]
`)
})

it('generates inlined font face rules for `google` provider', async () => {
const html = await $fetch('/providers/google')
const poppins = extractFontFaces('Poppins', html)
Expand Down

0 comments on commit 3773a9d

Please sign in to comment.