Skip to content

Commit

Permalink
chore: add support for nested CSS
Browse files Browse the repository at this point in the history
  • Loading branch information
onmax committed Nov 13, 2024
1 parent b726fa2 commit 2b5506a
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 29 deletions.
70 changes: 41 additions & 29 deletions src/plugins/transform.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createUnplugin } from 'unplugin'
import { parse, walk } from 'css-tree'
import { parse, walk, type CssNode } from 'css-tree'
import MagicString from 'magic-string'
import { transform } from 'esbuild'
import type { TransformOptions } from 'esbuild'
Expand Down Expand Up @@ -103,37 +103,49 @@ export const FontFamilyInjectionPlugin = (options: FontFamilyInjectionPluginOpti

// Collect existing `@font-face` declarations (to skip adding them)
const existingFontFamilies = new Set<string>()
walk(ast, {
visit: 'Declaration',
enter(node) {
if (this.atrule?.name === 'font-face' && node.property === 'font-family') {
for (const family of extractFontFamilies(node)) {
existingFontFamilies.add(family)

function processNode(node: CssNode) {
walk(node, {
visit: 'Declaration',
enter(node) {
if (this.atrule?.name === 'font-face' && node.property === 'font-family') {
for (const family of extractFontFamilies(node)) {
existingFontFamilies.add(family)
}
}
}
},
})
},
})

walk(ast, {
visit: 'Declaration',
enter(node) {
if (((node.property !== 'font-family' && node.property !== 'font') && (!options.processCSSVariables || !node.property.startsWith('--'))) || this.atrule?.name === 'font-face') {
return
}
walk(node, {
visit: 'Declaration',
enter(node) {
if (((node.property !== 'font-family' && node.property !== 'font') && (!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)
if (fontFamily && !existingFontFamilies.has(fontFamily)) {
promises.push(addFontFaceDeclaration(fontFamily, node.value.type !== 'Raw'
? {
fallbacks,
generic: extractGeneric(node),
index: extractEndOfFirstChild(node)!,
}
: undefined))
}
},
})
// Only add @font-face for the first font-family in the list and treat the rest as fallbacks
const [fontFamily, ...fallbacks] = extractFontFamilies(node)
if (fontFamily && !existingFontFamilies.has(fontFamily)) {
promises.push(addFontFaceDeclaration(fontFamily, node.value.type !== 'Raw'
? {
fallbacks,
generic: extractGeneric(node),
index: extractEndOfFirstChild(node)!,
}
: undefined))
}
},
})

walk(node, {
visit: 'Block',
enter(node) {
node.children.forEach(child => processNode(child))
},
})
}

processNode(ast)

await Promise.all(promises)

Expand Down
44 changes: 44 additions & 0 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,50 @@ describe('parsing', () => {
:root { font-family: 'Poppins', "Poppins Fallback: Times New Roman", "Poppins Fallback: Arial", 'Arial', sans-serif }"
`)
})

it('should handle nested CSS for `font-family`', async () => {
const expected = await transform(`.lato { font-family: 'Poppins', 'Arial', sans-serif; & p { font-family: 'Lato' } }`)
expect(expected).toMatchInlineSnapshot(`
"@font-face {
font-family: 'Poppins';
src: url("/poppins.woff2") format(woff2);
font-display: swap;
}
@font-face {
font-family: "Poppins Fallback: Times New Roman";
src: local("Times New Roman");
size-adjust: 123.0769%;
ascent-override: 85.3125%;
descent-override: 28.4375%;
line-gap-override: 8.125%;
}
@font-face {
font-family: "Poppins Fallback: Arial";
src: local("Arial");
size-adjust: 112.1577%;
ascent-override: 93.6182%;
descent-override: 31.2061%;
line-gap-override: 8.916%;
}
@font-face {
font-family: 'Lato';
src: url("/lato.woff2") format(woff2);
font-display: swap;
}
@font-face {
font-family: "Lato Fallback: Times New Roman";
src: local("Times New Roman");
size-adjust: 107.2%;
ascent-override: 92.0709%;
descent-override: 19.8694%;
line-gap-override: 0%;
}
.lato { font-family: 'Poppins', "Poppins Fallback: Times New Roman", "Poppins Fallback: Arial", "Poppins Fallback: Times New Roman", "Poppins Fallback: Arial", 'Arial', sans-serif; & p { font-family: 'Lato', "Lato Fallback: Times New Roman", "Lato Fallback: Times New Roman", "Lato Fallback: Times New Roman", "Lato Fallback: Times New Roman" } }"
`)
})
})

describe('parsing css', () => {
Expand Down

0 comments on commit 2b5506a

Please sign in to comment.