Use resolveConfig to normalize plugins
This commit is contained in:
parent
d9e3fd613b
commit
3553b5912e
3 changed files with 55 additions and 26 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import type { DesignSystem } from '../../design-system'
|
||||
import type { PluginWithConfig } from '../../plugin-api'
|
||||
import { createThemeFn } from '../../theme-fn'
|
||||
import { deepMerge, isPlainObject } from './deep-merge'
|
||||
import {
|
||||
|
|
@ -11,6 +12,7 @@ import {
|
|||
interface ResolutionContext {
|
||||
design: DesignSystem
|
||||
configs: UserConfig[]
|
||||
plugins: PluginWithConfig[]
|
||||
theme: Record<string, ThemeValue>
|
||||
extend: Record<string, ThemeValue[]>
|
||||
result: ResolvedConfig
|
||||
|
|
@ -18,12 +20,14 @@ interface ResolutionContext {
|
|||
|
||||
let minimal: ResolvedConfig = {
|
||||
theme: {},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
export function resolveConfig(design: DesignSystem, configs: UserConfig[]): ResolvedConfig {
|
||||
let ctx: ResolutionContext = {
|
||||
design,
|
||||
configs,
|
||||
configs: [],
|
||||
plugins: [],
|
||||
theme: {},
|
||||
extend: {},
|
||||
|
||||
|
|
@ -31,11 +35,16 @@ export function resolveConfig(design: DesignSystem, configs: UserConfig[]): Reso
|
|||
result: structuredClone(minimal),
|
||||
}
|
||||
|
||||
for (let config of configs) {
|
||||
resolveInternal(ctx, config)
|
||||
}
|
||||
|
||||
// Merge themes
|
||||
mergeTheme(ctx)
|
||||
|
||||
return {
|
||||
theme: ctx.theme as ResolvedConfig['theme'],
|
||||
plugins: ctx.plugins,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +80,41 @@ export interface PluginUtils {
|
|||
theme(keypath: string, defaultValue?: any): any
|
||||
}
|
||||
|
||||
function resolveInternal(ctx: ResolutionContext, user: UserConfig): void {
|
||||
let plugins: PluginWithConfig[] = []
|
||||
|
||||
// Normalize plugins so they share the same shape
|
||||
for (let plugin of user.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)`
|
||||
plugins.push(plugin())
|
||||
} else if ('handler' in plugin) {
|
||||
// Happens with `plugin(…)`:
|
||||
// e.g. `require("my-plugin")`
|
||||
//
|
||||
// or with `plugin.withOptions()` when the user passed options:
|
||||
// e.g. `require("my-plugin")(options)`
|
||||
plugins.push(plugin)
|
||||
} else {
|
||||
// Just a plain function without using the plugin(…) API
|
||||
plugins.push({ handler: plugin })
|
||||
}
|
||||
}
|
||||
|
||||
// Apply configs from plugins
|
||||
for (let plugin of plugins) {
|
||||
ctx.plugins.push(plugin)
|
||||
|
||||
if (plugin.config) {
|
||||
resolveInternal(ctx, plugin.config)
|
||||
}
|
||||
}
|
||||
|
||||
// Then apply the "user" config
|
||||
ctx.configs.push(user)
|
||||
}
|
||||
|
||||
function mergeTheme(ctx: ResolutionContext) {
|
||||
let api: PluginUtils = {
|
||||
theme: createThemeFn(ctx.design, () => ctx.theme, resolveValue),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import type { Plugin, PluginWithConfig } from '../../plugin-api'
|
||||
import type { PluginUtils } from './resolve-config'
|
||||
|
||||
export type ResolvableTo<T> = T | ((utils: PluginUtils) => T)
|
||||
|
||||
export interface UserConfig {
|
||||
theme?: ThemeConfig
|
||||
plugins?: Plugin[]
|
||||
}
|
||||
|
||||
export type ThemeValue = ResolvableTo<Record<string, unknown>> | null | undefined
|
||||
|
|
@ -15,4 +17,5 @@ export type ThemeConfig = Record<string, ThemeValue> & {
|
|||
|
||||
export interface ResolvedConfig {
|
||||
theme: Record<string, Record<string, unknown>>
|
||||
plugins: PluginWithConfig[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -326,37 +326,19 @@ function objectToAst(rules: CssInJs | CssInJs[]): AstNode[] {
|
|||
return ast
|
||||
}
|
||||
|
||||
export function registerPlugins(plugins: Plugin[], designSystem: DesignSystem, ast: AstNode[]) {
|
||||
let pluginObjects = []
|
||||
|
||||
for (let plugin of 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)`
|
||||
pluginObjects.push(plugin())
|
||||
} else if ('handler' in plugin) {
|
||||
// Happens with `plugin(…)`:
|
||||
// e.g. `require("my-plugin")`
|
||||
//
|
||||
// or with `plugin.withOptions()` when the user passed options:
|
||||
// e.g. `require("my-plugin")(options)`
|
||||
pluginObjects.push(plugin)
|
||||
} else {
|
||||
// Just a plain function without using the plugin(…) API
|
||||
pluginObjects.push({ handler: plugin, config: {} as UserConfig })
|
||||
}
|
||||
}
|
||||
|
||||
// Now merge all the configs and make all that crap work
|
||||
export function registerPlugins(
|
||||
plugins: Plugin[],
|
||||
designSystem: DesignSystem,
|
||||
ast: AstNode[],
|
||||
) {
|
||||
let resolvedConfig = resolveConfig(designSystem, [
|
||||
createCompatConfig(designSystem.theme),
|
||||
...pluginObjects.map(({ config }) => config ?? {}),
|
||||
{ plugins },
|
||||
])
|
||||
|
||||
let pluginApi = buildPluginApi(designSystem, ast, resolvedConfig)
|
||||
|
||||
// Loop over the handlers and run them all with the resolved config + CSS theme probably somehow
|
||||
for (let { handler } of pluginObjects) {
|
||||
for (let { handler } of resolvedConfig.plugins) {
|
||||
handler(pluginApi)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue