Add support for the theme function to plugins
This commit is contained in:
parent
2fe2499635
commit
c763a7799c
3 changed files with 143 additions and 0 deletions
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { compileCandidates } from './compile'
|
||||
import * as CSS from './css-parser'
|
||||
import { buildDesignSystem, type DesignSystem } from './design-system'
|
||||
import { buildPluginApi } from './plugin-api'
|
||||
import { Theme } from './theme'
|
||||
import { segment } from './utils/segment'
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ const IS_VALID_UTILITY_NAME = /^[a-z][a-zA-Z0-9/%._-]*$/
|
|||
|
||||
type PluginAPI = {
|
||||
addVariant(name: string, variant: string | string[] | CssInJs): void
|
||||
theme(path: string, fallback?: any): any
|
||||
}
|
||||
|
||||
type Plugin = (api: PluginAPI) => void
|
||||
|
|
@ -280,6 +282,8 @@ export function compile(
|
|||
designSystem.variants.fromAst(name, objectToAst(variant))
|
||||
}
|
||||
},
|
||||
|
||||
...buildPluginApi(designSystem),
|
||||
}
|
||||
|
||||
for (let plugin of plugins) {
|
||||
|
|
|
|||
53
packages/tailwindcss/src/plugin-api.ts
Normal file
53
packages/tailwindcss/src/plugin-api.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import type { DesignSystem } from './design-system'
|
||||
|
||||
export function buildPluginApi(designSystem: DesignSystem) {
|
||||
return {
|
||||
theme(path: string, fallback?: any) {
|
||||
if (path.startsWith('--')) {
|
||||
if (path.endsWith('-*')) {
|
||||
return Object.fromEntries(
|
||||
designSystem.theme.namespace(path.slice(0, -2) as any).entries(),
|
||||
)
|
||||
}
|
||||
|
||||
return designSystem.theme.resolveValue(null, [path] as any) ?? fallback ?? null
|
||||
}
|
||||
|
||||
let original = path
|
||||
// Escape dots used inside square brackets
|
||||
.replace(/\[(.*?)\]/g, (_, value) => `-${value.replace('.', '_')}`)
|
||||
// Replace dots with dashes
|
||||
.replace(/\./g, '-')
|
||||
// Replace camelCase with dashes
|
||||
.replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}-${b.toLowerCase()}`)
|
||||
|
||||
// Prepend with `--` to match CSS variables
|
||||
original = `--${original}`
|
||||
|
||||
let map = designSystem.theme.namespace(original as any)
|
||||
|
||||
// Does the requested value exist in the theme
|
||||
if (map.has(null)) {
|
||||
// Yes, and there are multiple values in the requested theme namespace
|
||||
if (map.size > 1) {
|
||||
return {
|
||||
DEFAULT: map.get(null),
|
||||
...Object.fromEntries(Array.from(map.entries()).filter(([key]) => key !== null)),
|
||||
}
|
||||
}
|
||||
|
||||
// Nope, just the one
|
||||
return map.get(null)
|
||||
}
|
||||
|
||||
// There is at least one value in the requested theme namespace
|
||||
// but no default value
|
||||
if (map.size > 0) {
|
||||
return Object.fromEntries(map.entries())
|
||||
}
|
||||
|
||||
return fallback ?? null
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15150,3 +15150,89 @@ describe('custom utilities', () => {
|
|||
).toThrowError(/should be alphanumeric/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('theme function in plugins', () => {
|
||||
let input = css`
|
||||
@plugin "my-plugin";
|
||||
@theme reference {
|
||||
--size-2_5: 2.5rem;
|
||||
|
||||
--scrollbar-big: 20px;
|
||||
--scrollbar-big-properties: auto-hidden;
|
||||
|
||||
--scrollbar-color-light: white;
|
||||
--scrollbar-color-dark: black;
|
||||
},
|
||||
`
|
||||
|
||||
test('CSS property notation', () => {
|
||||
expect.hasAssertions()
|
||||
|
||||
compile(input, {
|
||||
loadPlugin() {
|
||||
return ({ theme }) => {
|
||||
expect(theme('--scrollbar')).toEqual(null)
|
||||
expect(theme('--scrollbar-*')).toEqual({
|
||||
big: '20px',
|
||||
'big-properties': 'auto-hidden',
|
||||
'color-dark': 'black',
|
||||
'color-light': 'white',
|
||||
})
|
||||
|
||||
expect(theme('--scrollbar-big')).toEqual('20px')
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('legacy dot notation', () => {
|
||||
expect.hasAssertions()
|
||||
|
||||
compile(input, {
|
||||
loadPlugin() {
|
||||
return ({ theme }) => {
|
||||
// Accessing via legacy dot notation
|
||||
expect(theme('size.2_5')).toEqual('2.5rem')
|
||||
expect(theme('scrollbar')).toEqual({
|
||||
big: '20px',
|
||||
'big-properties': 'auto-hidden',
|
||||
'color-dark': 'black',
|
||||
'color-light': 'white',
|
||||
})
|
||||
|
||||
expect(theme('scrollbar.big')).toEqual({
|
||||
DEFAULT: '20px',
|
||||
properties: 'auto-hidden',
|
||||
})
|
||||
expect(theme('scrollbar.big.properties')).toEqual('auto-hidden')
|
||||
|
||||
expect(theme('scrollbar.color')).toEqual({
|
||||
light: 'white',
|
||||
dark: 'black',
|
||||
})
|
||||
|
||||
expect(theme('scrollbar.foo', 'nope')).toEqual('nope')
|
||||
expect(theme('somekey', 'nope')).toEqual('nope')
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('legacy square bracket notation', () => {
|
||||
expect.hasAssertions()
|
||||
|
||||
compile(input, {
|
||||
loadPlugin() {
|
||||
return ({ theme }) => {
|
||||
expect(theme('size[2.5]')).toEqual('2.5rem')
|
||||
|
||||
expect(theme('scrollbar[big]')).toEqual({
|
||||
DEFAULT: '20px',
|
||||
properties: 'auto-hidden',
|
||||
})
|
||||
expect(theme('scrollbar[big][properties]')).toEqual('auto-hidden')
|
||||
}
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue