Allow resolve config to take in optional paths alongside configs

This commit is contained in:
Jordan Pittman 2024-08-22 13:12:48 -04:00 • committed by Philipp Spiess
parent f876304557
commit 20f001cb3c
3 changed files with 90 additions and 67 deletions

View file

@ -8,27 +8,33 @@ test('top level theme keys are replaced', ({ expect }) => {
let config = resolveConfig(design, [
{
theme: {
colors: {
red: 'red',
},
config: {
theme: {
colors: {
red: 'red',
},
fontFamily: {
sans: 'SF Pro Display',
fontFamily: {
sans: 'SF Pro Display',
},
},
},
},
{
theme: {
colors: {
green: 'green',
config: {
theme: {
colors: {
green: 'green',
},
},
},
},
{
theme: {
colors: {
blue: 'blue',
config: {
theme: {
colors: {
blue: 'blue',
},
},
},
},
@ -51,21 +57,25 @@ test('theme can be extended', ({ expect }) => {
let config = resolveConfig(design, [
{
theme: {
colors: {
red: 'red',
},
config: {
theme: {
colors: {
red: 'red',
},
fontFamily: {
sans: 'SF Pro Display',
fontFamily: {
sans: 'SF Pro Display',
},
},
},
},
{
theme: {
extend: {
colors: {
blue: 'blue',
config: {
theme: {
extend: {
colors: {
blue: 'blue',
},
},
},
},
@ -92,31 +102,37 @@ test('theme keys can reference other theme keys using the theme function regardl
let config = resolveConfig(design, [
{
theme: {
colors: {
red: 'red',
},
placeholderColor: {
green: 'green',
config: {
theme: {
colors: {
red: 'red',
},
placeholderColor: {
green: 'green',
},
},
},
},
{
theme: {
extend: {
colors: ({ theme }) => ({
...theme('placeholderColor'),
blue: 'blue',
}),
config: {
theme: {
extend: {
colors: ({ theme }) => ({
...theme('placeholderColor'),
blue: 'blue',
}),
},
},
},
},
{
theme: {
extend: {
caretColor: ({ theme }) => theme('accentColor'),
accentColor: ({ theme }) => theme('backgroundColor'),
backgroundColor: ({ theme }) => theme('colors'),
config: {
theme: {
extend: {
caretColor: ({ theme }) => theme('accentColor'),
accentColor: ({ theme }) => theme('backgroundColor'),
backgroundColor: ({ theme }) => theme('colors'),
},
},
},
},
@ -156,23 +172,25 @@ test('theme keys can read from the CSS theme', ({ expect }) => {
let config = resolveConfig(design, [
{
theme: {
colors: ({ theme }) => ({
// Reads from the --color-* namespace
...theme('color'),
red: 'red',
}),
accentColor: ({ theme }) => ({
// Reads from the --color-* namespace through `colors`
...theme('colors'),
}),
placeholderColor: ({ theme }) => ({
// Reads from the --color-* namespace through `colors`
primary: theme('colors.green'),
config: {
theme: {
colors: ({ theme }) => ({
// Reads from the --color-* namespace
...theme('color'),
red: 'red',
}),
accentColor: ({ theme }) => ({
// Reads from the --color-* namespace through `colors`
...theme('colors'),
}),
placeholderColor: ({ theme }) => ({
// Reads from the --color-* namespace through `colors`
primary: theme('colors.green'),
// Reads from the --color-* namespace directly
secondary: theme('color.green'),
}),
// Reads from the --color-* namespace directly
secondary: theme('color.green'),
}),
},
},
},
])

View file

@ -9,6 +9,11 @@ import {
type UserConfig,
} from './types'
export interface ConfigFile {
path?: string
config: UserConfig
}
interface ResolutionContext {
design: DesignSystem
configs: UserConfig[]
@ -23,7 +28,7 @@ let minimal: ResolvedConfig = {
plugins: [],
}
export function resolveConfig(design: DesignSystem, configs: UserConfig[]): ResolvedConfig {
export function resolveConfig(design: DesignSystem, files: ConfigFile[]): ResolvedConfig {
let ctx: ResolutionContext = {
design,
configs: [],
@ -35,8 +40,8 @@ export function resolveConfig(design: DesignSystem, configs: UserConfig[]): Reso
result: structuredClone(minimal),
}
for (let config of configs) {
resolveInternal(ctx, config)
for (let file of files) {
resolveInternal(ctx, file)
}
// Merge themes
@ -80,11 +85,11 @@ export interface PluginUtils {
theme(keypath: string, defaultValue?: any): any
}
function resolveInternal(ctx: ResolutionContext, user: UserConfig): void {
function resolveInternal(ctx: ResolutionContext, { config, path }: ConfigFile): void {
let plugins: PluginWithConfig[] = []
// Normalize plugins so they share the same shape
for (let plugin of user.plugins ?? []) {
for (let plugin of config.plugins ?? []) {
if ('__isOptionsFunction' in plugin) {
// Happens with `plugin.withOptions()` when no options were passed:
// e.g. `require("my-plugin")` instead of `require("my-plugin")(options)`
@ -103,12 +108,12 @@ function resolveInternal(ctx: ResolutionContext, user: UserConfig): void {
}
// Apply configs from presets
if (Array.isArray(user.presets) && user.presets.length === 0) {
if (Array.isArray(config.presets) && config.presets.length === 0) {
throw new Error('The empty preset `[]` is not supported')
}
for (let preset of user.presets ?? []) {
resolveInternal(ctx, preset)
for (let preset of config.presets ?? []) {
resolveInternal(ctx, { path, config: preset })
}
// Apply configs from plugins
@ -116,12 +121,12 @@ function resolveInternal(ctx: ResolutionContext, user: UserConfig): void {
ctx.plugins.push(plugin)
if (plugin.config) {
resolveInternal(ctx, plugin.config)
resolveInternal(ctx, { path, config: plugin.config })
}
}
// Then apply the "user" config
ctx.configs.push(user)
ctx.configs.push(config)
}
function mergeTheme(ctx: ResolutionContext) {

View file

@ -332,8 +332,8 @@ export function registerPlugins(
ast: AstNode[],
) {
let resolvedConfig = resolveConfig(designSystem, [
createCompatConfig(designSystem.theme),
{ plugins },
{ config: createCompatConfig(designSystem.theme) },
{ config: { plugins } },
])
let pluginApi = buildPluginApi(designSystem, ast, resolvedConfig)