Skip to content

Commit

Permalink
feat: add experimental support for CSS variables
Browse files Browse the repository at this point in the history
resolves #48
  • Loading branch information
danielroe committed Mar 10, 2024
1 parent 6f11a55 commit 0fa5d3a
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 11 deletions.
8 changes: 6 additions & 2 deletions src/css/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export function extractFontFaceData (css: string): NormalizedFontFaceData[] {
return mergeFontSources(fontFaces)
}

function processRawValue (value: string) {
return value.split(',').map(v => v.trim().replace(/^(?<quote>['"])(.*)\k<quote>$/, '$2'))
}

function extractCSSValue (node: Declaration) {
if (node.value.type == 'Raw') {
return [node.value.value]
return processRawValue(node.value.value)
}

const values = [] as Array<string | number | RemoteFontSource | LocalFontSource>
Expand Down Expand Up @@ -139,7 +143,7 @@ export function extractEndOfFirstChild (node: Declaration) {

export function extractFontFamilies (node: Declaration) {
if (node.value.type == 'Raw') {
return [node.value.value]
return processRawValue(node.value.value)
}

const families = [] as string[]
Expand Down
4 changes: 4 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export default defineNuxtModule<ModuleOptions>({
},
defaults: {
devtools: true,
experimental: {
processCSSVariables: false
},
defaults: {},
assets: {
prefix: '/_fonts'
Expand Down Expand Up @@ -242,6 +245,7 @@ export default defineNuxtModule<ModuleOptions>({

addBuildPlugin(FontFamilyInjectionPlugin({
dev: nuxt.options.dev,
processCSSVariables: options.experimental?.processCSSVariables,
async resolveFontFace (fontFamily, fallbackOptions) {
const override = options.families?.find(f => f.name === fontFamily)

Expand Down
6 changes: 3 additions & 3 deletions src/plugins/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface FontFaceResolution {
interface FontFamilyInjectionPluginOptions {
resolveFontFace: (fontFamily: string, fallbackOptions?: { fallbacks: string[], generic?: GenericCSSFamily }) => Awaitable<undefined | FontFaceResolution>
dev: boolean
processCSSVariables?: boolean
}

const SKIP_RE = /\/node_modules\/(vite-plugin-vue-inspector)\//
Expand Down Expand Up @@ -92,11 +93,10 @@ export const FontFamilyInjectionPlugin = (options: FontFamilyInjectionPluginOpti
}
})

// TODO: handle CSS custom properties
walk(ast, {
visit: 'Declaration',
enter (node) {
if (node.property !== 'font-family' || this.atrule?.name === 'font-face') { return }
if ((node.property !== 'font-family' && (!options.processCSSVariables || !node.property.startsWith('--'))) || this.atrule?.name === 'font-face') { return }

// Only add @font-face for the first font-family in the list and treat the rest as fallbacks
const [fontFamily, ...fallbacks] = extractFontFamilies(node)
Expand All @@ -122,7 +122,7 @@ export const FontFamilyInjectionPlugin = (options: FontFamilyInjectionPluginOpti
},
async transform (code) {
// Early return if no font-family is used in this CSS
if (!code.includes('font-family:')) { return }
if (!options.processCSSVariables && !code.includes('font-family:')) { return }

const s = await transformCSS(code)

Expand Down
19 changes: 13 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,19 @@ export interface ModuleOptions {
* In some cases you may wish to use only one font provider. This is equivalent to disabling all other font providers.
*/
provider?: FontProviderName
/**
* Enables support for Nuxt DevTools.
*
* @default true
*/
devtools?: boolean
/**
* Enables support for Nuxt DevTools.
*
* @default true
*/
devtools?: boolean
experimental?: {
/**
* You can enable support for processing CSS variables for font family names. This may have a performance impact.
* @default false
*/
processCSSVariables?: boolean
}
}

export interface ModuleHooks {
Expand Down
16 changes: 16 additions & 0 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@ describe('parsing', () => {
`)
})

it('should add declarations for `font-family` with CSS variables', async () => {
expect(await transform(`:root { --custom-css-variable: 'CustomFont' }`))
.toMatchInlineSnapshot(`
"@font-face {
font-family: 'CustomFont';
src: url("/customfont.woff2") format(woff2);
font-display: swap;
}
:root { --custom-css-variable: 'CustomFont' }"
`)
})

it('should handle multi word and unquoted font families', async () => {
expect(await transform(`
:root { font-family:Open Sans}
:root { font-family: Open Sans, sans-serif }
:root { --test: Open Sans, sans-serif }
`))
.toMatchInlineSnapshot(`
"@font-face {
Expand All @@ -37,6 +50,7 @@ describe('parsing', () => {
:root { font-family:Open Sans, "Open Sans Fallback: Times New Roman"}
:root { font-family: Open Sans, "Open Sans Fallback: Times New Roman", sans-serif }
:root { --test: Open Sans, sans-serif }
"
`)
})
Expand Down Expand Up @@ -111,6 +125,7 @@ describe('error handling', () => {
it('handles no font details supplied', async () => {
const plugin = FontFamilyInjectionPlugin({
dev: true,
processCSSVariables: true,
resolveFontFace: () => ({ fonts: [] })
}).raw({}, { framework: 'vite' }) as any
expect(await plugin.transform(`:root { font-family: 'Poppins', 'Arial', sans-serif }`).then((r: any) => r?.code)).toMatchInlineSnapshot(`undefined`)
Expand All @@ -121,6 +136,7 @@ const slugify = (str: string) => str.toLowerCase().replace(/[^\d\w]/g, '-')
async function transform (css: string) {
const plugin = FontFamilyInjectionPlugin({
dev: true,
processCSSVariables: true,
resolveFontFace: (family, options) => ({
fonts: [{ src: [{ url: `/${slugify(family)}.woff2`, format: 'woff2' }] }],
fallbacks: options?.fallbacks ? ['Times New Roman', ...options.fallbacks] : undefined
Expand Down

0 comments on commit 0fa5d3a

Please sign in to comment.