wip
This commit is contained in:
parent
5111f44efa
commit
d8cef56dbd
4 changed files with 76 additions and 26 deletions
|
|
@ -48,7 +48,7 @@ export function objectToAst(obj: CssInJs): AstNode[] {
|
|||
let ast: AstNode[] = []
|
||||
|
||||
for (let [name, value] of Object.entries(obj)) {
|
||||
if (typeof value === 'string') {
|
||||
if (typeof value !== 'object') {
|
||||
if (!name.startsWith('--') && value === '@slot') {
|
||||
ast.push(rule(name, [rule('@slot', [])]))
|
||||
} else {
|
||||
|
|
@ -56,9 +56,9 @@ export function objectToAst(obj: CssInJs): AstNode[] {
|
|||
// https://github.com/postcss/postcss-js/blob/b3db658b932b42f6ac14ca0b1d50f50c4569805b/parser.js#L30-L35
|
||||
name = name.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||
|
||||
ast.push(decl(name, value))
|
||||
ast.push(decl(name, String(value)))
|
||||
}
|
||||
} else {
|
||||
} else if (value !== null) {
|
||||
ast.push(rule(name, objectToAst(value)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { PluginAPI } from '../api'
|
||||
import type { CorePluginName } from '../generated/core-utils'
|
||||
import type { PluginUtils } from './utils'
|
||||
import type { PluginUtils } from './resolve-config'
|
||||
|
||||
export type ImportableFile = string | [path: string, options: Record<string, any>]
|
||||
|
||||
type PluginAPI = {}
|
||||
|
||||
type FilePath = string
|
||||
type RawFile = { raw: string; extension?: string }
|
||||
export type RawPreset = Omit<UserConfig, 'content'>
|
||||
|
|
@ -55,7 +55,6 @@ export interface UserConfig {
|
|||
presets?: Preset[]
|
||||
plugins?: Plugin[]
|
||||
theme?: ThemeConfig
|
||||
corePlugins?: Partial<Record<CorePluginName, boolean>> | CorePluginName[] | boolean
|
||||
}
|
||||
|
||||
export type ThemeValue = ResolvableTo<Record<string, unknown>> | null | undefined
|
||||
|
|
@ -74,5 +73,4 @@ export interface ResolvedConfig {
|
|||
darkMode: DarkModeConfig
|
||||
plugins: RawPlugin[]
|
||||
theme: Record<string, Record<string, unknown>>
|
||||
corePlugins: Record<CorePluginName, boolean>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,4 +255,44 @@ describe('theme', async () => {
|
|||
"
|
||||
`)
|
||||
})
|
||||
|
||||
test('wip', async ({ expect }) => {
|
||||
let input = css`
|
||||
@tailwind utilities;
|
||||
@plugin "my-plugin";
|
||||
`
|
||||
|
||||
let compiler = await compile(input, {
|
||||
loadPlugin: async () => {
|
||||
return plugin(
|
||||
function ({ matchUtilities, theme }) {
|
||||
matchUtilities(
|
||||
{
|
||||
duration: (value) => ({ 'animation-duration': value }),
|
||||
},
|
||||
{
|
||||
values: theme('animationDuration'),
|
||||
},
|
||||
)
|
||||
},
|
||||
{
|
||||
theme: {
|
||||
extend: {
|
||||
animationDuration: ({ theme }: { theme: (path: string) => any }) => ({
|
||||
...theme('transitionDuration'),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
expect(compiler.build(['duration-316'])).toMatchInlineSnapshot(`
|
||||
".duration-316 {
|
||||
animation-duration: 316ms;
|
||||
}
|
||||
"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import { objectToAst, rule, type AstNode, type CssInJs } from './ast'
|
|||
import { resolveConfig } from './compat/config/resolve-config'
|
||||
import type { UserConfig } from './compat/config/types'
|
||||
import type { DesignSystem } from './design-system'
|
||||
import type { Theme } from './theme'
|
||||
import { withAlpha, withNegative } from './utilities'
|
||||
import { inferDataType } from './utils/infer-data-type'
|
||||
import { segment } from './utils/segment'
|
||||
|
||||
export type Config = UserConfig
|
||||
export type PluginFn = (api: PluginAPI) => void
|
||||
export type PluginWithConfig = { handler: PluginFn; config?: UserConfig }
|
||||
export type PluginWithOptions<T> = {
|
||||
|
|
@ -25,9 +27,7 @@ export type PluginAPI = {
|
|||
options?: Partial<{
|
||||
type: string | string[]
|
||||
supportsNegativeValues: boolean
|
||||
values: Record<string, string> & {
|
||||
[BARE_VALUE]?: (value: string) => string | null | undefined
|
||||
}
|
||||
values: Record<string, string>
|
||||
modifiers: 'any' | Record<string, string>
|
||||
}>,
|
||||
): void
|
||||
|
|
@ -136,7 +136,7 @@ export function buildPluginApi(
|
|||
let value: string | null = null
|
||||
|
||||
{
|
||||
let values = options?.values ?? {}
|
||||
let values: Record<string, string> = options?.values ?? {}
|
||||
|
||||
if (isColor) {
|
||||
// Color utilities implicitly support `inherit`, `transparent`, and `currentColor`
|
||||
|
|
@ -156,11 +156,17 @@ export function buildPluginApi(
|
|||
} else if (values[candidate.value.value]) {
|
||||
value = values[candidate.value.value]
|
||||
} else if (values[BARE_VALUE]) {
|
||||
value = values[BARE_VALUE](candidate.value.value) ?? null
|
||||
// We've snuk the bare value in here as a function even though values are
|
||||
// typically only ever strings. This is a backwards compatibility hack.
|
||||
let handleBareValue = values[BARE_VALUE] as unknown as (
|
||||
value: string,
|
||||
) => string | null
|
||||
|
||||
value = handleBareValue(candidate.value.value) ?? null
|
||||
}
|
||||
}
|
||||
|
||||
if (!value) return
|
||||
if (value === null) return
|
||||
|
||||
// Resolve the modifier value
|
||||
let modifier: string | null
|
||||
|
|
@ -182,12 +188,12 @@ export function buildPluginApi(
|
|||
}
|
||||
|
||||
// A modifier was provided but is invalid
|
||||
if (candidate.modifier && !modifier) {
|
||||
if (candidate.modifier && modifier !== null) {
|
||||
// For arbitrary values, return `null` to avoid falling through to the next utility
|
||||
return candidate.value?.kind === 'arbitrary' ? null : undefined
|
||||
}
|
||||
|
||||
if (isColor && modifier) {
|
||||
if (isColor && modifier !== null) {
|
||||
value = withAlpha(value, modifier)
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +230,10 @@ export function buildPluginApi(
|
|||
}
|
||||
}
|
||||
|
||||
const BARE_VALUE = Symbol('BARE_VALUE')
|
||||
// We can't use a symbol here because a lot of code in the ecosystem assumes an object with string / number keys
|
||||
// for example using Object.entries() + Object.fromEntries() to modify an object which removes symbol keys
|
||||
// We use `Math.random()` to ensure that users are unable to rely on the name of thiskey
|
||||
const BARE_VALUE = `__BARE_VALUE__${Math.random().toString(36).slice(2)}`
|
||||
|
||||
export function registerPlugins(plugins: Plugin[], designSystem: DesignSystem, ast: AstNode[]) {
|
||||
let pluginObjects = []
|
||||
|
|
@ -249,7 +258,7 @@ export function registerPlugins(plugins: Plugin[], designSystem: DesignSystem, a
|
|||
|
||||
// Now merge all the configs and make all that crap work
|
||||
let resolvedConfig = resolveConfig([
|
||||
compatabilityConfig,
|
||||
createCompatabilityConfig(designSystem.theme),
|
||||
...pluginObjects.map(({ config }) => config ?? {}),
|
||||
])
|
||||
|
||||
|
|
@ -261,14 +270,17 @@ export function registerPlugins(plugins: Plugin[], designSystem: DesignSystem, a
|
|||
}
|
||||
}
|
||||
|
||||
export const compatabilityConfig = {
|
||||
theme: {
|
||||
transitionDuration: {
|
||||
[BARE_VALUE]: (value: string) => {
|
||||
if (!Number.isNaN(Number(value))) {
|
||||
return `${value}ms`
|
||||
}
|
||||
export function createCompatabilityConfig(theme: Theme) {
|
||||
return {
|
||||
theme: {
|
||||
transitionDuration: {
|
||||
DEFAULT: theme.get(['--default-transition-duration']) ?? null,
|
||||
[BARE_VALUE]: (value: string) => {
|
||||
if (!Number.isNaN(Number(value))) {
|
||||
return `${value}ms`
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue