zyzz 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -19
- package/dist/Config.d.ts +16 -167
- package/dist/Config.d.ts.map +1 -1
- package/dist/Config.js +7 -246
- package/dist/Config.js.map +1 -1
- package/dist/Style.d.ts +15 -12
- package/dist/Style.d.ts.map +1 -1
- package/dist/Style.js +51 -26
- package/dist/Style.js.map +1 -1
- package/dist/Vars.d.ts +114 -0
- package/dist/Vars.d.ts.map +1 -0
- package/dist/Vars.js +42 -0
- package/dist/Vars.js.map +1 -0
- package/dist/compiler/Graph.js +11 -8
- package/dist/compiler/Graph.js.map +1 -1
- package/dist/compiler/Native.d.ts +5 -5
- package/dist/compiler/Native.d.ts.map +1 -1
- package/dist/compiler/Native.js +21 -21
- package/dist/compiler/Native.js.map +1 -1
- package/dist/compiler/Source.d.ts +8 -6
- package/dist/compiler/Source.d.ts.map +1 -1
- package/dist/compiler/Source.js +47 -14
- package/dist/compiler/Source.js.map +1 -1
- package/dist/compiler/Transform.d.ts +1 -1
- package/dist/compiler/Transform.d.ts.map +1 -1
- package/dist/compiler/Transform.js +58 -22
- package/dist/compiler/Transform.js.map +1 -1
- package/dist/compiler/internal/Compositions.d.ts +5 -0
- package/dist/compiler/internal/Compositions.d.ts.map +1 -1
- package/dist/compiler/internal/Compositions.js +22 -3
- package/dist/compiler/internal/Compositions.js.map +1 -1
- package/dist/compiler/internal/Configurations.d.ts.map +1 -1
- package/dist/compiler/internal/Configurations.js +73 -12
- package/dist/compiler/internal/Configurations.js.map +1 -1
- package/dist/compiler/internal/Contract.d.ts +1 -1
- package/dist/compiler/internal/Contract.d.ts.map +1 -1
- package/dist/compiler/internal/Contract.js +155 -13
- package/dist/compiler/internal/Contract.js.map +1 -1
- package/dist/compiler/internal/Contributions.js +1 -1
- package/dist/compiler/internal/Contributions.js.map +1 -1
- package/dist/compiler/internal/Dynamic.js +1 -1
- package/dist/compiler/internal/Dynamic.js.map +1 -1
- package/dist/compiler/internal/Expression.d.ts +3 -0
- package/dist/compiler/internal/Expression.d.ts.map +1 -1
- package/dist/compiler/internal/Expression.js +21 -0
- package/dist/compiler/internal/Expression.js.map +1 -1
- package/dist/compiler/internal/Identifiers.d.ts +1 -1
- package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
- package/dist/compiler/internal/Identifiers.js +2 -2
- package/dist/compiler/internal/Identifiers.js.map +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts +1 -1
- package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
- package/dist/compiler/internal/NativeBindings.js +3 -3
- package/dist/compiler/internal/NativeBindings.js.map +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts +1 -1
- package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
- package/dist/compiler/internal/PackedStyles.js +15 -25
- package/dist/compiler/internal/PackedStyles.js.map +1 -1
- package/dist/compiler/internal/Recipes.js +1 -1
- package/dist/compiler/internal/Recipes.js.map +1 -1
- package/dist/compiler/internal/Selectors.js +1 -1
- package/dist/compiler/internal/Selectors.js.map +1 -1
- package/dist/compiler/internal/Themes.d.ts +12 -3
- package/dist/compiler/internal/Themes.d.ts.map +1 -1
- package/dist/compiler/internal/Themes.js +192 -52
- package/dist/compiler/internal/Themes.js.map +1 -1
- package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
- package/dist/compiler/internal/Vars.d.ts.map +1 -0
- package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
- package/dist/compiler/internal/Vars.js.map +1 -0
- package/dist/default.d.ts +147 -2562
- package/dist/default.d.ts.map +1 -1
- package/dist/default.js +85 -80
- package/dist/default.js.map +2 -2
- package/dist/default.js.zyzz.json +1 -1
- package/dist/esbuild/index.d.ts +3 -0
- package/dist/esbuild/index.d.ts.map +1 -0
- package/dist/esbuild/index.js +5 -0
- package/dist/esbuild/index.js.map +1 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/internal/Authoring.d.ts +1 -1
- package/dist/internal/Authoring.d.ts.map +1 -1
- package/dist/internal/Authoring.js +4 -4
- package/dist/internal/Authoring.js.map +1 -1
- package/dist/internal/Binding.d.ts +1 -1
- package/dist/internal/Binding.d.ts.map +1 -1
- package/dist/internal/Completion.d.ts +19 -0
- package/dist/internal/Completion.d.ts.map +1 -0
- package/dist/internal/Completion.js +2 -0
- package/dist/internal/Completion.js.map +1 -0
- package/dist/internal/Condition.d.ts +3 -1
- package/dist/internal/Condition.d.ts.map +1 -1
- package/dist/internal/Condition.js.map +1 -1
- package/dist/internal/Configuration.d.ts +248 -0
- package/dist/internal/Configuration.d.ts.map +1 -0
- package/dist/internal/Configuration.js +343 -0
- package/dist/internal/Configuration.js.map +1 -0
- package/dist/{Theme.d.ts → internal/Theme.d.ts} +15 -13
- package/dist/internal/Theme.d.ts.map +1 -0
- package/dist/{Theme.js → internal/Theme.js} +39 -12
- package/dist/internal/Theme.js.map +1 -0
- package/dist/internal/Token.d.ts +46 -16
- package/dist/internal/Token.d.ts.map +1 -1
- package/dist/internal/Token.js +89 -5
- package/dist/internal/Token.js.map +1 -1
- package/dist/internal/Typography.d.ts +13 -5
- package/dist/internal/Typography.d.ts.map +1 -1
- package/dist/internal/Typography.js +42 -6
- package/dist/internal/Typography.js.map +1 -1
- package/dist/internal/Value.d.ts +6 -1
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VariableSets.d.ts +20 -0
- package/dist/internal/VariableSets.d.ts.map +1 -0
- package/dist/internal/VariableSets.js +317 -0
- package/dist/internal/VariableSets.js.map +1 -0
- package/dist/next/loader.js +15 -5
- package/dist/next/loader.js.map +1 -1
- package/dist/node/Host.d.ts.map +1 -1
- package/dist/node/Host.js +1 -1
- package/dist/node/Host.js.map +1 -1
- package/dist/react-native/Host.d.ts +2 -2
- package/dist/react-native/Host.d.ts.map +1 -1
- package/dist/react-native/Host.js +4 -4
- package/dist/react-native/Host.js.map +1 -1
- package/dist/react-native/StyleSheet.d.ts +9 -8
- package/dist/react-native/StyleSheet.d.ts.map +1 -1
- package/dist/react-native/StyleSheet.js +29 -23
- package/dist/react-native/StyleSheet.js.map +1 -1
- package/dist/react-native/Variants.d.ts +3 -3
- package/dist/react-native/Variants.d.ts.map +1 -1
- package/dist/react-native/Variants.js +2 -2
- package/dist/react-native/react.d.ts +1 -1
- package/dist/react-native/react.d.ts.map +1 -1
- package/dist/react-native/react.js +5 -5
- package/dist/react-native/react.js.map +1 -1
- package/dist/rollup/index.d.ts +3 -0
- package/dist/rollup/index.d.ts.map +1 -0
- package/dist/rollup/index.js +5 -0
- package/dist/rollup/index.js.map +1 -0
- package/dist/runtime/Appearance.d.ts +7 -7
- package/dist/runtime/Appearance.d.ts.map +1 -1
- package/dist/runtime/Appearance.js +15 -15
- package/dist/runtime/Appearance.js.map +1 -1
- package/dist/runtime/Dynamic.d.ts.map +1 -1
- package/dist/runtime/Dynamic.js +4 -1
- package/dist/runtime/Dynamic.js.map +1 -1
- package/dist/runtime/NativeContext.d.ts +4 -4
- package/dist/runtime/NativeContext.d.ts.map +1 -1
- package/dist/runtime/NativeContext.js +5 -5
- package/dist/runtime/NativeContext.js.map +1 -1
- package/dist/runtime/NativeDynamic.d.ts +1 -1
- package/dist/runtime/NativeDynamic.d.ts.map +1 -1
- package/dist/runtime/NativeDynamic.js.map +1 -1
- package/dist/runtime/Props.d.ts +1 -1
- package/dist/runtime/Props.js +3 -3
- package/dist/runtime/Props.js.map +1 -1
- package/dist/runtime/Selection.d.ts +14 -10
- package/dist/runtime/Selection.d.ts.map +1 -1
- package/dist/runtime/Selection.js +19 -18
- package/dist/runtime/Selection.js.map +1 -1
- package/dist/styleFunction.d.ts +14 -14
- package/dist/styleFunction.d.ts.map +1 -1
- package/dist/styleFunction.js.map +1 -1
- package/dist/unplugin/index.d.ts +20 -0
- package/dist/unplugin/index.d.ts.map +1 -0
- package/dist/unplugin/index.js +498 -0
- package/dist/unplugin/index.js.map +1 -0
- package/dist/variants.d.ts +3 -3
- package/dist/variants.d.ts.map +1 -1
- package/dist/variants.js.map +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +6 -3
- package/dist/vite/index.js.map +1 -1
- package/dist/web/Css.d.ts +4 -3
- package/dist/web/Css.d.ts.map +1 -1
- package/dist/web/Css.js +4 -4
- package/dist/web/Css.js.map +1 -1
- package/dist/web/internal/Themes.d.ts +3 -2
- package/dist/web/internal/Themes.d.ts.map +1 -1
- package/dist/web/internal/Themes.js +42 -6
- package/dist/web/internal/Themes.js.map +1 -1
- package/dist/webpack/index.d.ts +3 -0
- package/dist/webpack/index.d.ts.map +1 -0
- package/dist/webpack/index.js +5 -0
- package/dist/webpack/index.js.map +1 -0
- package/package.json +23 -2
- package/src/Config.ts +43 -685
- package/src/Style.ts +97 -34
- package/src/Vars.ts +299 -0
- package/src/compiler/Graph.ts +14 -9
- package/src/compiler/Native.ts +29 -32
- package/src/compiler/Source.ts +59 -21
- package/src/compiler/Transform.ts +73 -23
- package/src/compiler/internal/Compositions.ts +36 -3
- package/src/compiler/internal/Configurations.ts +83 -11
- package/src/compiler/internal/Contract.ts +207 -15
- package/src/compiler/internal/Contributions.ts +1 -1
- package/src/compiler/internal/Dynamic.ts +1 -1
- package/src/compiler/internal/Expression.ts +26 -0
- package/src/compiler/internal/Identifiers.ts +2 -1
- package/src/compiler/internal/NativeBindings.ts +3 -3
- package/src/compiler/internal/PackedStyles.ts +22 -26
- package/src/compiler/internal/Recipes.ts +1 -1
- package/src/compiler/internal/Selectors.ts +1 -1
- package/src/compiler/internal/Themes.ts +266 -57
- package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
- package/src/default.ts +173 -154
- package/src/esbuild/index.ts +5 -0
- package/src/index.ts +4 -4
- package/src/internal/Authoring.ts +5 -7
- package/src/internal/Binding.ts +1 -1
- package/src/internal/Completion.ts +42 -0
- package/src/internal/Condition.ts +31 -1
- package/src/internal/Configuration.ts +970 -0
- package/src/{Theme.ts → internal/Theme.ts} +75 -20
- package/src/internal/Token.ts +258 -85
- package/src/internal/Typography.ts +71 -16
- package/src/internal/Value.ts +45 -26
- package/src/internal/VariableSets.ts +458 -0
- package/src/next/loader.ts +19 -5
- package/src/node/Host.ts +1 -2
- package/src/react-native/Host.ts +6 -6
- package/src/react-native/StyleSheet.ts +49 -42
- package/src/react-native/Variants.ts +3 -3
- package/src/react-native/react.ts +6 -6
- package/src/rollup/index.ts +5 -0
- package/src/runtime/Appearance.ts +20 -20
- package/src/runtime/Dynamic.ts +4 -1
- package/src/runtime/NativeContext.ts +8 -8
- package/src/runtime/NativeDynamic.ts +2 -4
- package/src/runtime/Props.ts +3 -3
- package/src/runtime/Selection.ts +44 -33
- package/src/styleFunction.ts +22 -19
- package/src/unplugin/index.ts +600 -0
- package/src/variants.ts +7 -5
- package/src/vite/index.ts +5 -4
- package/src/web/Css.ts +10 -7
- package/src/web/internal/Themes.ts +52 -9
- package/src/webpack/index.ts +5 -0
- package/dist/Theme.d.ts.map +0 -1
- package/dist/Theme.js.map +0 -1
- package/dist/compiler/internal/Variables.d.ts.map +0 -1
- package/dist/compiler/internal/Variables.js.map +0 -1
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes explicit configuration into isolated typed authoring contracts.
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
import * as Appearance from '../runtime/Appearance.js'
|
|
6
|
+
import * as Authoring from './Authoring.js'
|
|
7
|
+
import * as Html from '../runtime/CompositionHtml.js'
|
|
8
|
+
import * as Identity from './Identity.js'
|
|
9
|
+
import type * as Binding from './Binding.js'
|
|
10
|
+
import type * as Condition from './Condition.js'
|
|
11
|
+
import * as Literal from './Literal.js'
|
|
12
|
+
import type { style } from '../styleFunction.js'
|
|
13
|
+
import { variants } from '../variants.js'
|
|
14
|
+
import * as Scheme from './Scheme.js'
|
|
15
|
+
import * as Shorthands from './Shorthands.js'
|
|
16
|
+
import type * as Style from '../Style.js'
|
|
17
|
+
import * as Theme from './Theme.js'
|
|
18
|
+
import * as VariableSets from './VariableSets.js'
|
|
19
|
+
import * as Vars from '../Vars.js'
|
|
20
|
+
import * as Selection from '../runtime/Selection.js'
|
|
21
|
+
import * as Token from './Token.js'
|
|
22
|
+
import type * as Completion from './Completion.js'
|
|
23
|
+
|
|
24
|
+
/** Creates token-free authoring without a configuration file or global state. */
|
|
25
|
+
export function create(): create.ReturnType<{}>
|
|
26
|
+
/**
|
|
27
|
+
* Binds inline or reusable themes to an isolated configuration contract.
|
|
28
|
+
* Named themes require an explicit default and identical token paths/domains.
|
|
29
|
+
* Bound helpers reference extracted CSS; this factory emits no CSS rules.
|
|
30
|
+
* @throws {InvalidError} If options, themes, defaults, or layer names are invalid.
|
|
31
|
+
*/
|
|
32
|
+
export function create<const options extends create.Options | VariableOptions>(
|
|
33
|
+
options: options &
|
|
34
|
+
NoInfer<
|
|
35
|
+
options extends VariableOptions
|
|
36
|
+
? VariableValidation<options>
|
|
37
|
+
: Validated<options>
|
|
38
|
+
>,
|
|
39
|
+
): options extends VariableOptions
|
|
40
|
+
? VariableConfig<options>
|
|
41
|
+
: options extends create.Options
|
|
42
|
+
? create.ReturnType<options>
|
|
43
|
+
: never
|
|
44
|
+
export function create(
|
|
45
|
+
options: create.Options | VariableOptions = {},
|
|
46
|
+
): unknown {
|
|
47
|
+
let input = record(options)
|
|
48
|
+
const variableMode = input.vars !== undefined
|
|
49
|
+
const variableCatalog = variableMode && input.defaultVars !== undefined
|
|
50
|
+
const variableMappings = VariableSets.mappings(input.mappings)
|
|
51
|
+
if (variableMode) {
|
|
52
|
+
if (
|
|
53
|
+
input.theme !== undefined ||
|
|
54
|
+
input.themes !== undefined ||
|
|
55
|
+
input.defaultTheme !== undefined
|
|
56
|
+
)
|
|
57
|
+
throw new InvalidError('Use vars without theme options.')
|
|
58
|
+
const normalize = (value: unknown) => {
|
|
59
|
+
const definition =
|
|
60
|
+
value &&
|
|
61
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
62
|
+
Object.getOwnPropertyDescriptor(value, Token.definition)?.value
|
|
63
|
+
? (value as Vars.Definition)
|
|
64
|
+
: Vars.define(value as Vars.Values)
|
|
65
|
+
const theme = VariableSets.theme(definition, variableMappings)
|
|
66
|
+
if (variableMappings === false) return theme
|
|
67
|
+
const metadata = theme[Token.definition]
|
|
68
|
+
const names = new Map<string, string>()
|
|
69
|
+
for (const path of Object.keys(metadata.values)) {
|
|
70
|
+
const [category, ...parts] = path.split('.')
|
|
71
|
+
const targets =
|
|
72
|
+
variableMappings?.[category!] ??
|
|
73
|
+
Object.keys(Literal.rules).filter((property) =>
|
|
74
|
+
Token.accepts(
|
|
75
|
+
category as Token.Group,
|
|
76
|
+
property as keyof Literal.Properties,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
for (const property of targets) {
|
|
80
|
+
const key = `${property}:${parts.join('.')}`
|
|
81
|
+
if (
|
|
82
|
+
names.has(key) &&
|
|
83
|
+
(variableMappings?.[category!] ||
|
|
84
|
+
variableMappings?.[names.get(key)!.split('.')[0]!])
|
|
85
|
+
)
|
|
86
|
+
throw new InvalidError(
|
|
87
|
+
`Ambiguous variable token ${parts.join('.')} for ${property}.`,
|
|
88
|
+
)
|
|
89
|
+
names.set(key, path)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return theme
|
|
93
|
+
}
|
|
94
|
+
const { vars, defaultVars, mappings: _mappings, ...rest } = input
|
|
95
|
+
input = variableCatalog
|
|
96
|
+
? {
|
|
97
|
+
...rest,
|
|
98
|
+
defaultTheme: defaultVars,
|
|
99
|
+
themes: Object.fromEntries(
|
|
100
|
+
Object.entries(record(vars)).map(([name, value]) => [
|
|
101
|
+
name,
|
|
102
|
+
normalize(value),
|
|
103
|
+
]),
|
|
104
|
+
),
|
|
105
|
+
}
|
|
106
|
+
: { ...rest, theme: normalize(vars) }
|
|
107
|
+
} else if (input.mappings !== undefined || input.defaultVars !== undefined) {
|
|
108
|
+
throw new InvalidError('mappings and defaultVars require vars.')
|
|
109
|
+
}
|
|
110
|
+
function finish(result: Record<string, unknown>) {
|
|
111
|
+
if (!variableMode) return Object.freeze(result)
|
|
112
|
+
const theme = result.theme as Theme.Definition
|
|
113
|
+
const catalog = result.themes as
|
|
114
|
+
| Record<string, Theme.Definition>
|
|
115
|
+
| undefined
|
|
116
|
+
const entries = () =>
|
|
117
|
+
catalog
|
|
118
|
+
? Object.entries(catalog).map(
|
|
119
|
+
([name, value]) => [name, value.className] as const,
|
|
120
|
+
)
|
|
121
|
+
: [['default', theme.className] as const]
|
|
122
|
+
let select: ReturnType<typeof Selection.create> | undefined
|
|
123
|
+
const vars = (
|
|
124
|
+
options: {
|
|
125
|
+
set?: string | undefined
|
|
126
|
+
colorScheme?: string | undefined
|
|
127
|
+
} = {},
|
|
128
|
+
) =>
|
|
129
|
+
(select ??= Selection.create(
|
|
130
|
+
entries(),
|
|
131
|
+
input.output === 'html',
|
|
132
|
+
'set',
|
|
133
|
+
variableCatalog ? String(input.defaultTheme) : 'default',
|
|
134
|
+
))(options)
|
|
135
|
+
return Object.freeze({
|
|
136
|
+
appearance: result.appearance,
|
|
137
|
+
script: result.script,
|
|
138
|
+
style: result.style,
|
|
139
|
+
vars: Object.freeze(
|
|
140
|
+
Object.defineProperties(
|
|
141
|
+
vars,
|
|
142
|
+
Object.getOwnPropertyDescriptors(theme.tokens),
|
|
143
|
+
),
|
|
144
|
+
),
|
|
145
|
+
variants: result.variants,
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (const key of Object.keys(input))
|
|
150
|
+
if (
|
|
151
|
+
![
|
|
152
|
+
'cssOutput',
|
|
153
|
+
'id',
|
|
154
|
+
'defaultLayer',
|
|
155
|
+
'defaultTheme',
|
|
156
|
+
'layers',
|
|
157
|
+
'output',
|
|
158
|
+
'shorthands',
|
|
159
|
+
'storageKey',
|
|
160
|
+
'theme',
|
|
161
|
+
'themes',
|
|
162
|
+
].includes(key)
|
|
163
|
+
)
|
|
164
|
+
throw new InvalidError(`Unknown configuration option: ${key}`)
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
input.storageKey !== undefined &&
|
|
168
|
+
(typeof input.storageKey !== 'string' || !input.storageKey)
|
|
169
|
+
)
|
|
170
|
+
throw new InvalidError('storageKey must be a nonempty string.')
|
|
171
|
+
|
|
172
|
+
// Validated above; the record type erases the literal narrowing.
|
|
173
|
+
const storageKey = input.storageKey as string | undefined
|
|
174
|
+
|
|
175
|
+
if (
|
|
176
|
+
input.output !== undefined &&
|
|
177
|
+
input.output !== 'html' &&
|
|
178
|
+
input.output !== 'react'
|
|
179
|
+
)
|
|
180
|
+
throw new InvalidError('output must be html or react.')
|
|
181
|
+
|
|
182
|
+
if (
|
|
183
|
+
input.cssOutput !== undefined &&
|
|
184
|
+
input.cssOutput !== 'atomic' &&
|
|
185
|
+
input.cssOutput !== 'grouped'
|
|
186
|
+
)
|
|
187
|
+
throw new InvalidError('cssOutput must be atomic or grouped.')
|
|
188
|
+
|
|
189
|
+
if (input.theme !== undefined && input.themes !== undefined)
|
|
190
|
+
throw new InvalidError('Use either theme or themes, not both.')
|
|
191
|
+
|
|
192
|
+
if (
|
|
193
|
+
input.defaultLayer !== undefined &&
|
|
194
|
+
typeof input.defaultLayer !== 'string'
|
|
195
|
+
)
|
|
196
|
+
throw new InvalidError('defaultLayer must be a CSS layer name.')
|
|
197
|
+
|
|
198
|
+
if (input.layers !== undefined || input.defaultLayer !== undefined) {
|
|
199
|
+
if (input.layers !== undefined && !Array.isArray(input.layers))
|
|
200
|
+
throw new InvalidError('layers must be an array.')
|
|
201
|
+
|
|
202
|
+
const layers = (input.layers ?? []) as readonly unknown[]
|
|
203
|
+
const seen = new Set<string>()
|
|
204
|
+
|
|
205
|
+
for (const layer of [
|
|
206
|
+
...layers,
|
|
207
|
+
...(input.defaultLayer === undefined ||
|
|
208
|
+
layers.includes(input.defaultLayer)
|
|
209
|
+
? []
|
|
210
|
+
: [input.defaultLayer]),
|
|
211
|
+
]) {
|
|
212
|
+
if (
|
|
213
|
+
typeof layer !== 'string' ||
|
|
214
|
+
!/^(?:--|-?[_a-zA-Z])[\w-]*(?:\.(?:--|-?[_a-zA-Z])[\w-]*)*$/.test(
|
|
215
|
+
layer,
|
|
216
|
+
) ||
|
|
217
|
+
layer
|
|
218
|
+
.split('.')
|
|
219
|
+
.some((part) =>
|
|
220
|
+
[
|
|
221
|
+
'default',
|
|
222
|
+
'inherit',
|
|
223
|
+
'initial',
|
|
224
|
+
'revert',
|
|
225
|
+
'revert-layer',
|
|
226
|
+
'unset',
|
|
227
|
+
].includes(part.toLowerCase()),
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
throw new InvalidError(
|
|
231
|
+
'Layer names must be plain CSS identifiers, optionally dotted.',
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if (seen.has(layer)) throw new InvalidError(`Duplicate layer: ${layer}`)
|
|
235
|
+
|
|
236
|
+
seen.add(layer)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const shorthands = (() => {
|
|
241
|
+
if (input.shorthands === undefined) return undefined
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
return Shorthands.read(input.shorthands)
|
|
245
|
+
} catch (error) {
|
|
246
|
+
throw new InvalidError((error as Error).message)
|
|
247
|
+
}
|
|
248
|
+
})()
|
|
249
|
+
|
|
250
|
+
const contract = Object.freeze({
|
|
251
|
+
...(variableMode ? { variableSet: true, mappings: variableMappings } : {}),
|
|
252
|
+
...(input.defaultLayer !== undefined
|
|
253
|
+
? { defaultLayer: input.defaultLayer as string }
|
|
254
|
+
: {}),
|
|
255
|
+
cssOutput:
|
|
256
|
+
(input.cssOutput as 'atomic' | 'grouped' | undefined) ?? 'atomic',
|
|
257
|
+
...(shorthands ? { shorthands } : {}),
|
|
258
|
+
...(typeof input.id === 'string'
|
|
259
|
+
? {
|
|
260
|
+
[Token.identity]: Identity.requireId(input.id, 'Config.create'),
|
|
261
|
+
[Token.complete]: true,
|
|
262
|
+
}
|
|
263
|
+
: {}),
|
|
264
|
+
})
|
|
265
|
+
function boundStyle(theme?: Theme.Definition) {
|
|
266
|
+
return (styles?: unknown, options: style.DefinitionOptions = {}) => {
|
|
267
|
+
if (
|
|
268
|
+
theme &&
|
|
269
|
+
Object.keys(theme[Token.definition].values).length &&
|
|
270
|
+
!contract[Token.identity]
|
|
271
|
+
)
|
|
272
|
+
Identity.requireId(undefined, 'Config.create')
|
|
273
|
+
return Authoring.create(styles, {
|
|
274
|
+
...options,
|
|
275
|
+
theme,
|
|
276
|
+
output: input.output as style.Output | undefined,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function boundVariants(
|
|
281
|
+
definition: Record<string, unknown>,
|
|
282
|
+
options: style.DefinitionOptions = {},
|
|
283
|
+
) {
|
|
284
|
+
return Authoring.variants(definition, {
|
|
285
|
+
...options,
|
|
286
|
+
output: input.output as style.Output | undefined,
|
|
287
|
+
})
|
|
288
|
+
}
|
|
289
|
+
function handle(theme: Theme.Definition, name: string) {
|
|
290
|
+
const original = Token.bind(theme, contract)
|
|
291
|
+
const className = () =>
|
|
292
|
+
`z_theme-${Identity.requireId(typeof input.id === 'string' ? input.id : undefined, 'Config.create')}-${name.replace(/[^a-zA-Z0-9-]/g, (character) => `_${character.charCodeAt(0).toString(16)}_`)}`
|
|
293
|
+
const select = (options: { colorScheme?: string } = {}) => {
|
|
294
|
+
const scheme = options.colorScheme
|
|
295
|
+
const selection =
|
|
296
|
+
scheme !== undefined && Object.hasOwn(Scheme.classes, scheme)
|
|
297
|
+
? ` ${Scheme.classes[scheme as Scheme.Name]}`
|
|
298
|
+
: ''
|
|
299
|
+
const result = {
|
|
300
|
+
className: `${className()}${selection}`,
|
|
301
|
+
...(scheme ? { style: { colorScheme: scheme } } : {}),
|
|
302
|
+
}
|
|
303
|
+
return input.output === 'html' ? Html.from(result) : result
|
|
304
|
+
}
|
|
305
|
+
const descriptors = Object.getOwnPropertyDescriptors(original)
|
|
306
|
+
delete (descriptors as Record<string, unknown>).className
|
|
307
|
+
delete (descriptors as Record<string, unknown>).style
|
|
308
|
+
delete (descriptors as Record<string, unknown>).variants
|
|
309
|
+
Object.defineProperties(select, descriptors)
|
|
310
|
+
Object.defineProperty(select, 'className', {
|
|
311
|
+
get: className,
|
|
312
|
+
configurable: true,
|
|
313
|
+
})
|
|
314
|
+
Object.assign(select, {
|
|
315
|
+
style: boundStyle(original),
|
|
316
|
+
variants: boundVariants,
|
|
317
|
+
})
|
|
318
|
+
return select
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (input.themes !== undefined) {
|
|
322
|
+
const catalog = record(input.themes)
|
|
323
|
+
if (
|
|
324
|
+
typeof input.defaultTheme !== 'string' ||
|
|
325
|
+
!Object.hasOwn(catalog, input.defaultTheme)
|
|
326
|
+
)
|
|
327
|
+
throw new InvalidError('defaultTheme must name a theme in the catalog.')
|
|
328
|
+
|
|
329
|
+
const defaultTheme = input.defaultTheme
|
|
330
|
+
const definitions = Object.fromEntries(
|
|
331
|
+
Object.entries(catalog).map(([name, value]) => {
|
|
332
|
+
if (!name) throw new InvalidError('Theme names must be nonempty.')
|
|
333
|
+
|
|
334
|
+
return [name, definition(value)]
|
|
335
|
+
}),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
const base = definitions[input.defaultTheme]!
|
|
339
|
+
const paths = Object.keys(base[Token.definition].values).sort()
|
|
340
|
+
|
|
341
|
+
function queries(theme: Theme.Definition) {
|
|
342
|
+
const data = theme[Token.definition].queries
|
|
343
|
+
|
|
344
|
+
return JSON.stringify({
|
|
345
|
+
breakpoints: Object.keys(data?.breakpoints ?? {}).sort(),
|
|
346
|
+
containers: Object.keys(data?.containers ?? {}).sort(),
|
|
347
|
+
containerNames: [...(data?.containerNames ?? [])].sort(),
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for (const [name, value] of Object.entries(definitions)) {
|
|
352
|
+
const candidate = Object.keys(value[Token.definition].values).sort()
|
|
353
|
+
if (
|
|
354
|
+
queries(base) !== queries(value) ||
|
|
355
|
+
paths.length !== candidate.length ||
|
|
356
|
+
paths.some(
|
|
357
|
+
(path, index) =>
|
|
358
|
+
path !== candidate[index] ||
|
|
359
|
+
(variableMode &&
|
|
360
|
+
VariableSets.domain(base[Token.definition].values[path]!) !==
|
|
361
|
+
VariableSets.domain(value[Token.definition].values[path]!)),
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
throw new InvalidError(
|
|
365
|
+
`Theme ${JSON.stringify(name)} must have the default theme's complete token paths and domains.`,
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const bound = Object.fromEntries(
|
|
370
|
+
Object.entries(definitions).map(([name, value]) => [
|
|
371
|
+
name,
|
|
372
|
+
handle(value, name),
|
|
373
|
+
]),
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
const select = (options: { theme: string; colorScheme?: string }) =>
|
|
377
|
+
(bound[options.theme] as (options: { colorScheme?: string }) => unknown)(
|
|
378
|
+
options,
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
Object.defineProperties(select, Object.getOwnPropertyDescriptors(bound))
|
|
382
|
+
|
|
383
|
+
const entries = () =>
|
|
384
|
+
Object.entries(bound).map(([name, value]): readonly [string, string] => [
|
|
385
|
+
name,
|
|
386
|
+
(value as unknown as Theme.Definition).className,
|
|
387
|
+
])
|
|
388
|
+
|
|
389
|
+
// Class names require an identity, so the compiler's collection pass must
|
|
390
|
+
// not read them; root controls resolve their entries on first use.
|
|
391
|
+
const appearance = ((): Appearance.Root<string> => {
|
|
392
|
+
let root: Appearance.Root<string> | undefined
|
|
393
|
+
const resolve = () =>
|
|
394
|
+
(root ??= Appearance.root(entries(), {
|
|
395
|
+
defaultVars: defaultTheme,
|
|
396
|
+
storageKey,
|
|
397
|
+
}))
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
get: () => resolve().get(),
|
|
401
|
+
set: (selection) => resolve().set(selection),
|
|
402
|
+
}
|
|
403
|
+
})()
|
|
404
|
+
|
|
405
|
+
return finish({
|
|
406
|
+
appearance,
|
|
407
|
+
script: () => Appearance.create(entries(), { storageKey })(),
|
|
408
|
+
style: boundStyle(
|
|
409
|
+
bound[input.defaultTheme] as unknown as Theme.Definition,
|
|
410
|
+
),
|
|
411
|
+
theme: bound[input.defaultTheme],
|
|
412
|
+
themes: Object.freeze(select),
|
|
413
|
+
variants: boundVariants,
|
|
414
|
+
})
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (input.defaultTheme !== undefined)
|
|
418
|
+
throw new InvalidError('defaultTheme requires a named themes catalog.')
|
|
419
|
+
|
|
420
|
+
if (input.theme !== undefined) {
|
|
421
|
+
const theme = handle(definition(input.theme), 'theme')
|
|
422
|
+
return finish({
|
|
423
|
+
appearance: Appearance.root([], { storageKey }),
|
|
424
|
+
script: Appearance.create([], { storageKey }),
|
|
425
|
+
style: boundStyle(theme as unknown as Theme.Definition),
|
|
426
|
+
theme,
|
|
427
|
+
variants: boundVariants,
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const theme =
|
|
432
|
+
shorthands || input.defaultLayer !== undefined
|
|
433
|
+
? Token.bind(Theme.define({}), contract)
|
|
434
|
+
: undefined
|
|
435
|
+
return finish({
|
|
436
|
+
appearance: Appearance.root([], { storageKey }),
|
|
437
|
+
script: Appearance.create([], { storageKey }),
|
|
438
|
+
style: boundStyle(theme),
|
|
439
|
+
variants: boundVariants,
|
|
440
|
+
})
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Configuration inputs and inferred results. */
|
|
444
|
+
export declare namespace create {
|
|
445
|
+
/** Optional layer names and mutually exclusive theme modes. */
|
|
446
|
+
type Options = {
|
|
447
|
+
/** CSS representation inherited by bound helpers; atomic by default. */
|
|
448
|
+
readonly cssOutput?: 'atomic' | 'grouped' | undefined
|
|
449
|
+
/** Fallback CSS layer for bound styles and variants; unlayered when omitted. */
|
|
450
|
+
readonly defaultLayer?: string | undefined
|
|
451
|
+
/** Stable theme identity required without source rewriting. */
|
|
452
|
+
readonly id?: string | undefined
|
|
453
|
+
/** Explicit ordered property aliases; none are installed by default. */
|
|
454
|
+
readonly shorthands?: Shorthands.Map | undefined
|
|
455
|
+
/** Renderer props format; React is the default. */
|
|
456
|
+
readonly output?: style.Output | undefined
|
|
457
|
+
/** Ordered plain or dotted CSS layer names; emission follows source integration. */
|
|
458
|
+
readonly layers?: readonly string[] | undefined
|
|
459
|
+
/** localStorage key shared by `script()` and `appearance`; zyzz by default. */
|
|
460
|
+
readonly storageKey?: string | undefined
|
|
461
|
+
} & (
|
|
462
|
+
| {
|
|
463
|
+
/** Single inline or reusable theme. */ readonly theme: Input
|
|
464
|
+
/** Named mode is excluded. */ readonly themes?: never
|
|
465
|
+
/** Only named catalogs use defaults. */ readonly defaultTheme?: never
|
|
466
|
+
}
|
|
467
|
+
| {
|
|
468
|
+
/** Explicit catalog key. */ readonly defaultTheme: string
|
|
469
|
+
/** Single mode is excluded. */ readonly theme?: never
|
|
470
|
+
/** Complete named theme alternatives. */ readonly themes: Readonly<
|
|
471
|
+
Record<string, Input>
|
|
472
|
+
>
|
|
473
|
+
}
|
|
474
|
+
| {
|
|
475
|
+
/** Only named catalogs use defaults. */ readonly defaultTheme?: never
|
|
476
|
+
/** Omit themes for token-free authoring. */ readonly theme?: never
|
|
477
|
+
/** Omit themes for token-free authoring. */ readonly themes?: never
|
|
478
|
+
}
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
/** Bound authoring and the handles corresponding to the selected theme mode. */
|
|
482
|
+
type ReturnType<options extends Options = Options> = {
|
|
483
|
+
/** Reads and persists the root theme and scheme selection on the document element. */
|
|
484
|
+
readonly appearance: Appearance.Root<
|
|
485
|
+
options extends { themes: infer catalog } ? keyof catalog & string : never
|
|
486
|
+
>
|
|
487
|
+
/** Generates synchronous HTML-safe root preference restoration. */
|
|
488
|
+
readonly script: () => string
|
|
489
|
+
/** Inferred callable authoring; execution requires a source transform. */
|
|
490
|
+
readonly style: StyleFactory<
|
|
491
|
+
Tokens<options>,
|
|
492
|
+
options extends { layers: readonly (infer name extends string)[] }
|
|
493
|
+
? name
|
|
494
|
+
: never,
|
|
495
|
+
options extends { output: infer output extends style.Output }
|
|
496
|
+
? output
|
|
497
|
+
: 'react',
|
|
498
|
+
Mappings<options>
|
|
499
|
+
>
|
|
500
|
+
/** Theme-, layer-, and mapping-aware recipe authoring. */
|
|
501
|
+
readonly variants: variants.Bound<
|
|
502
|
+
Tokens<options>,
|
|
503
|
+
options extends { output: infer output extends style.Output }
|
|
504
|
+
? output
|
|
505
|
+
: 'react',
|
|
506
|
+
options extends { layers: readonly (infer name extends string)[] }
|
|
507
|
+
? name
|
|
508
|
+
: never,
|
|
509
|
+
Mappings<options>
|
|
510
|
+
>
|
|
511
|
+
} & (options extends { theme: infer input }
|
|
512
|
+
? {
|
|
513
|
+
/** Isolated single-theme contract. */ readonly theme: Handle<
|
|
514
|
+
ExtractTokens<input>,
|
|
515
|
+
Mappings<options>,
|
|
516
|
+
options extends { output: infer output extends style.Output }
|
|
517
|
+
? output
|
|
518
|
+
: 'react'
|
|
519
|
+
>
|
|
520
|
+
}
|
|
521
|
+
: options extends { themes: infer catalog }
|
|
522
|
+
? {
|
|
523
|
+
/** Shared default token and variable contract. */ readonly theme: Handle<
|
|
524
|
+
Tokens<options>,
|
|
525
|
+
Mappings<options>,
|
|
526
|
+
options extends { output: infer output extends style.Output }
|
|
527
|
+
? output
|
|
528
|
+
: 'react'
|
|
529
|
+
>
|
|
530
|
+
/** Selects a compiled named scope; catalog members retain compatibility. */ readonly themes: (<
|
|
531
|
+
const selection extends {
|
|
532
|
+
readonly colorScheme?: 'dark' | 'light' | 'light dark' | undefined
|
|
533
|
+
readonly theme: keyof catalog & string
|
|
534
|
+
},
|
|
535
|
+
>(
|
|
536
|
+
options: selection &
|
|
537
|
+
Record<Exclude<keyof selection, 'theme' | 'colorScheme'>, never>,
|
|
538
|
+
) => style.Props<
|
|
539
|
+
options extends { output: infer output extends style.Output }
|
|
540
|
+
? output
|
|
541
|
+
: 'react'
|
|
542
|
+
>) & {
|
|
543
|
+
readonly [name in keyof catalog]: Handle<
|
|
544
|
+
ExtractTokens<catalog[name]>,
|
|
545
|
+
Mappings<options>,
|
|
546
|
+
options extends { output: infer output extends style.Output }
|
|
547
|
+
? output
|
|
548
|
+
: 'react'
|
|
549
|
+
>
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
: {})
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
type Mappings<options> = options extends {
|
|
556
|
+
shorthands: infer map extends Shorthands.Map
|
|
557
|
+
}
|
|
558
|
+
? string extends keyof map
|
|
559
|
+
? {}
|
|
560
|
+
: map
|
|
561
|
+
: {}
|
|
562
|
+
|
|
563
|
+
/** Theme handle retaining configured shorthands and renderer output in emitted declarations. */
|
|
564
|
+
export type Handle<
|
|
565
|
+
tokens extends Theme.Tokens,
|
|
566
|
+
mappings extends Shorthands.Map,
|
|
567
|
+
output extends style.Output,
|
|
568
|
+
> = Omit<Theme.Definition<tokens>, 'style' | 'variants'> & {
|
|
569
|
+
/** Style factory bound to this handle. */
|
|
570
|
+
readonly style: StyleFactory<tokens, never, output, mappings>
|
|
571
|
+
/** Variant factory bound to this handle. */
|
|
572
|
+
readonly variants: variants.Bound<tokens, output, never, mappings>
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** Configured style factory with a portable name for library declaration emission. */
|
|
576
|
+
export type StyleFactory<
|
|
577
|
+
tokens extends Theme.Tokens,
|
|
578
|
+
layers extends string,
|
|
579
|
+
output extends style.Output,
|
|
580
|
+
mappings extends Shorthands.Map,
|
|
581
|
+
> = {
|
|
582
|
+
(): style.ReturnType<output>
|
|
583
|
+
<
|
|
584
|
+
const values extends Record<string, string | number>,
|
|
585
|
+
const styles extends Record<string, unknown>,
|
|
586
|
+
const callback extends (...args: never[]) => unknown,
|
|
587
|
+
>(
|
|
588
|
+
styles: callback &
|
|
589
|
+
((
|
|
590
|
+
values: values,
|
|
591
|
+
) => styles &
|
|
592
|
+
NoInfer<
|
|
593
|
+
Body<styles, tokens, layers, mappings> & Binding.Checked<styles>
|
|
594
|
+
>) &
|
|
595
|
+
Completion.Properties<tokens, NoInfer<styles>>,
|
|
596
|
+
...options: Parameters<callback> extends [Record<string, string | number>]
|
|
597
|
+
? values extends Binding.Inputs<values>
|
|
598
|
+
? [options?: style.DefinitionOptions]
|
|
599
|
+
: [invalid: never, unavailable: never]
|
|
600
|
+
: [invalid: never, unavailable: never]
|
|
601
|
+
): style.Dynamic<values, output>
|
|
602
|
+
<const styles extends Record<string, unknown>>(
|
|
603
|
+
styles: styles &
|
|
604
|
+
NoInfer<Body<styles, tokens, layers, mappings>> &
|
|
605
|
+
Completion.Properties<tokens>,
|
|
606
|
+
options?: style.DefinitionOptions,
|
|
607
|
+
): style.ReturnType<output>
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
type Keys<styles> = styles extends unknown ? keyof styles : never
|
|
611
|
+
|
|
612
|
+
/** Checked declaration body shared by configured styles and recipe choices. */
|
|
613
|
+
export type Body<
|
|
614
|
+
styles,
|
|
615
|
+
tokens extends Theme.Tokens,
|
|
616
|
+
layers extends string,
|
|
617
|
+
mappings extends Shorthands.Map,
|
|
618
|
+
targets extends boolean = true,
|
|
619
|
+
> = Record<
|
|
620
|
+
Exclude<
|
|
621
|
+
Keys<styles>,
|
|
622
|
+
| keyof mappings
|
|
623
|
+
| 'selectors'
|
|
624
|
+
| 'targets'
|
|
625
|
+
| 'typography'
|
|
626
|
+
| 'vars'
|
|
627
|
+
| keyof Style.DeclarationProperties
|
|
628
|
+
| Exclude<Condition.Keys<tokens, Keys<styles>>, `@layer${string}`>
|
|
629
|
+
| '@layer'
|
|
630
|
+
| `@layer ${layers}`
|
|
631
|
+
>,
|
|
632
|
+
never
|
|
633
|
+
> &
|
|
634
|
+
(styles extends unknown
|
|
635
|
+
? {
|
|
636
|
+
[key in keyof styles]: key extends 'targets'
|
|
637
|
+
? targets extends false
|
|
638
|
+
? never
|
|
639
|
+
: styles[key] extends undefined
|
|
640
|
+
? undefined
|
|
641
|
+
: {
|
|
642
|
+
[target in keyof styles[key]]: target extends 'web'
|
|
643
|
+
?
|
|
644
|
+
| Body<
|
|
645
|
+
NonNullable<styles[key][target]>,
|
|
646
|
+
tokens,
|
|
647
|
+
layers,
|
|
648
|
+
mappings,
|
|
649
|
+
false
|
|
650
|
+
>
|
|
651
|
+
| Extract<styles[key][target], undefined>
|
|
652
|
+
: Style.Accepted<
|
|
653
|
+
{ targets: Pick<styles[key], target> },
|
|
654
|
+
tokens
|
|
655
|
+
>['targets'][target]
|
|
656
|
+
}
|
|
657
|
+
: key extends 'selectors'
|
|
658
|
+
? styles[key] extends Record<string, unknown>
|
|
659
|
+
? {
|
|
660
|
+
[selector in keyof styles[key]]: styles[key][selector] extends Record<
|
|
661
|
+
string,
|
|
662
|
+
unknown
|
|
663
|
+
>
|
|
664
|
+
? Body<
|
|
665
|
+
styles[key][selector],
|
|
666
|
+
tokens,
|
|
667
|
+
layers,
|
|
668
|
+
mappings,
|
|
669
|
+
targets
|
|
670
|
+
>
|
|
671
|
+
: never
|
|
672
|
+
}
|
|
673
|
+
: never
|
|
674
|
+
: key extends
|
|
675
|
+
| Exclude<Condition.Keys<tokens, key>, `@layer${string}`>
|
|
676
|
+
| '@layer'
|
|
677
|
+
| `@layer ${layers}`
|
|
678
|
+
? styles[key] extends Record<string, unknown>
|
|
679
|
+
? Body<styles[key], tokens, layers, mappings, targets>
|
|
680
|
+
: never
|
|
681
|
+
: key extends keyof mappings
|
|
682
|
+
? {
|
|
683
|
+
[target in mappings[key][number]]: styles[key] extends Style.Accepted<
|
|
684
|
+
Record<target, styles[key]>,
|
|
685
|
+
tokens
|
|
686
|
+
>[target] &
|
|
687
|
+
Binding.Checked<Record<target, styles[key]>>[target]
|
|
688
|
+
? never
|
|
689
|
+
: target
|
|
690
|
+
}[mappings[key][number]] extends never
|
|
691
|
+
? styles[key]
|
|
692
|
+
: never
|
|
693
|
+
: Style.Accepted<Pick<styles, key>, tokens>[key]
|
|
694
|
+
}
|
|
695
|
+
: never)
|
|
696
|
+
|
|
697
|
+
function definition(value: unknown): Theme.Definition {
|
|
698
|
+
try {
|
|
699
|
+
if (
|
|
700
|
+
value &&
|
|
701
|
+
typeof value === 'object' &&
|
|
702
|
+
Object.getOwnPropertyDescriptor(value, Token.definition)?.value
|
|
703
|
+
)
|
|
704
|
+
return value as Theme.Definition
|
|
705
|
+
|
|
706
|
+
return Theme.define(record(value) as Theme.Tokens)
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (error instanceof InvalidError) throw error
|
|
709
|
+
|
|
710
|
+
throw new InvalidError((error as Error).message)
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
type ExtractTokens<input> =
|
|
715
|
+
input extends Theme.Definition<infer tokens>
|
|
716
|
+
? tokens
|
|
717
|
+
: input extends Theme.Tokens
|
|
718
|
+
? input
|
|
719
|
+
: never
|
|
720
|
+
|
|
721
|
+
type Input = Theme.Definition | Theme.Tokens
|
|
722
|
+
|
|
723
|
+
/** Invalid options or incompatible named themes. */
|
|
724
|
+
export class InvalidError extends Error {
|
|
725
|
+
/** Creates a configuration diagnostic without generating styles. */
|
|
726
|
+
constructor(message: string) {
|
|
727
|
+
super(message)
|
|
728
|
+
}
|
|
729
|
+
/** Stable namespaced error identifier. */
|
|
730
|
+
override name = 'Config.InvalidError'
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function record(value: unknown): Record<string, unknown> {
|
|
734
|
+
if (
|
|
735
|
+
!value ||
|
|
736
|
+
typeof value !== 'object' ||
|
|
737
|
+
Array.isArray(value) ||
|
|
738
|
+
![null, Object.prototype].includes(Object.getPrototypeOf(value))
|
|
739
|
+
)
|
|
740
|
+
throw new InvalidError('Expected a plain configuration record.')
|
|
741
|
+
|
|
742
|
+
const result: Record<string, unknown> = Object.create(null)
|
|
743
|
+
|
|
744
|
+
for (const [key, descriptor] of Object.entries(
|
|
745
|
+
Object.getOwnPropertyDescriptors(value),
|
|
746
|
+
)) {
|
|
747
|
+
if (!('value' in descriptor))
|
|
748
|
+
throw new InvalidError('Configuration records cannot contain accessors.')
|
|
749
|
+
|
|
750
|
+
result[key] = descriptor.value
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return result
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
type Tokens<options> = options extends { theme: infer input }
|
|
757
|
+
? ExtractTokens<input>
|
|
758
|
+
: options extends { themes: infer catalog; defaultTheme: infer key }
|
|
759
|
+
? key extends keyof catalog
|
|
760
|
+
? ExtractTokens<catalog[key]>
|
|
761
|
+
: never
|
|
762
|
+
: {}
|
|
763
|
+
|
|
764
|
+
type ValidInput<input> = input extends Theme.Definition
|
|
765
|
+
? input
|
|
766
|
+
: input extends Theme.Tokens
|
|
767
|
+
? Parameters<typeof Theme.define<input>>[0]
|
|
768
|
+
: never
|
|
769
|
+
|
|
770
|
+
type Match<input, base> = base extends
|
|
771
|
+
| string
|
|
772
|
+
| number
|
|
773
|
+
| { light: string; dark: string }
|
|
774
|
+
? input
|
|
775
|
+
: {
|
|
776
|
+
[key in keyof input | keyof base]: key extends keyof input
|
|
777
|
+
? key extends keyof base
|
|
778
|
+
? key extends 'containerNames'
|
|
779
|
+
? input[key] extends readonly string[]
|
|
780
|
+
? base[key] extends readonly string[]
|
|
781
|
+
?
|
|
782
|
+
| Exclude<input[key][number], base[key][number]>
|
|
783
|
+
| Exclude<
|
|
784
|
+
base[key][number],
|
|
785
|
+
input[key][number]
|
|
786
|
+
> extends never
|
|
787
|
+
? input[key]
|
|
788
|
+
: never
|
|
789
|
+
: never
|
|
790
|
+
: never
|
|
791
|
+
: Match<input[key], base[key]>
|
|
792
|
+
: never
|
|
793
|
+
: never
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
type Validated<options> = Record<
|
|
797
|
+
Exclude<keyof options, keyof create.Options>,
|
|
798
|
+
never
|
|
799
|
+
> &
|
|
800
|
+
(options extends { shorthands: infer mappings extends Shorthands.Map }
|
|
801
|
+
? { shorthands: Shorthands.Validated<mappings> }
|
|
802
|
+
: {}) &
|
|
803
|
+
(options extends { theme: infer input }
|
|
804
|
+
? { theme: ValidInput<input> }
|
|
805
|
+
: options extends { themes: infer catalog; defaultTheme: infer key }
|
|
806
|
+
? {
|
|
807
|
+
defaultTheme: keyof catalog
|
|
808
|
+
themes: {
|
|
809
|
+
[name in keyof catalog]: ValidInput<catalog[name]> &
|
|
810
|
+
(key extends keyof catalog
|
|
811
|
+
? catalog[name] extends Theme.Definition
|
|
812
|
+
? Theme.Definition<
|
|
813
|
+
ExtractTokens<catalog[name]> &
|
|
814
|
+
Match<
|
|
815
|
+
ExtractTokens<catalog[name]>,
|
|
816
|
+
ExtractTokens<catalog[key]>
|
|
817
|
+
>
|
|
818
|
+
>
|
|
819
|
+
: Match<
|
|
820
|
+
ExtractTokens<catalog[name]>,
|
|
821
|
+
ExtractTokens<catalog[key]>
|
|
822
|
+
>
|
|
823
|
+
: never)
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
: {})
|
|
827
|
+
|
|
828
|
+
/** Vars, optional named alternatives, and category-to-property mappings. */
|
|
829
|
+
export type VariableOptions = {
|
|
830
|
+
/** CSS representation inherited by bound helpers; atomic by default. */
|
|
831
|
+
readonly cssOutput?: 'atomic' | 'grouped' | undefined
|
|
832
|
+
/** Fallback CSS layer for bound styles and variants; unlayered when omitted. */
|
|
833
|
+
readonly defaultLayer?: string | undefined
|
|
834
|
+
/** Required default key when vars contains named sets. */
|
|
835
|
+
readonly defaultVars?: string | undefined
|
|
836
|
+
/** Stable identity required without source rewriting. */
|
|
837
|
+
readonly id?: string | undefined
|
|
838
|
+
/** Ordered CSS layer names. */
|
|
839
|
+
readonly layers?: readonly string[] | undefined
|
|
840
|
+
/** Category mappings; false enables full paths in every compatible property. */
|
|
841
|
+
readonly mappings?: Vars.Mappings | false | undefined
|
|
842
|
+
/** Styling props format; React by default. */
|
|
843
|
+
readonly output?: style.Output | undefined
|
|
844
|
+
/** Explicit local property aliases. */
|
|
845
|
+
readonly shorthands?: Shorthands.Map | undefined
|
|
846
|
+
/** Preference storage key; zyzz by default. */
|
|
847
|
+
readonly storageKey?: string | undefined
|
|
848
|
+
/** One inline or reusable set, or a catalog with defaultVars. */
|
|
849
|
+
readonly vars:
|
|
850
|
+
| Vars.Values
|
|
851
|
+
| Vars.Definition
|
|
852
|
+
| Readonly<Record<string, Vars.Definition | Vars.Values>>
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
type VariableValues<options extends VariableOptions> = options extends {
|
|
856
|
+
defaultVars: infer key
|
|
857
|
+
}
|
|
858
|
+
? key extends keyof options['vars']
|
|
859
|
+
? Vars.Extract<options['vars'][key]>
|
|
860
|
+
: never
|
|
861
|
+
: Vars.Extract<options['vars']>
|
|
862
|
+
|
|
863
|
+
type VariableTokens<options extends VariableOptions> = Vars.Mapped<
|
|
864
|
+
VariableValues<options>,
|
|
865
|
+
options extends { mappings: infer mappings } ? mappings : {}
|
|
866
|
+
>
|
|
867
|
+
|
|
868
|
+
type VariableInput<input> = input extends {
|
|
869
|
+
readonly [Token.definition]: Token.Metadata
|
|
870
|
+
}
|
|
871
|
+
? input
|
|
872
|
+
: input extends Vars.Values
|
|
873
|
+
? Parameters<typeof Vars.define<input>>[0]
|
|
874
|
+
: never
|
|
875
|
+
|
|
876
|
+
type VariableMatch<input, base> = base extends readonly string[]
|
|
877
|
+
? input extends readonly string[]
|
|
878
|
+
?
|
|
879
|
+
| Exclude<input[number], base[number]>
|
|
880
|
+
| Exclude<base[number], input[number]> extends never
|
|
881
|
+
? input
|
|
882
|
+
: never
|
|
883
|
+
: never
|
|
884
|
+
: base extends Vars.Value
|
|
885
|
+
? input extends Vars.Value
|
|
886
|
+
? Vars.Domain<Vars.Scalar<input>> extends Vars.Domain<Vars.Scalar<base>>
|
|
887
|
+
? input
|
|
888
|
+
: never
|
|
889
|
+
: never
|
|
890
|
+
: {
|
|
891
|
+
readonly [key in keyof input | keyof base]: key extends keyof base
|
|
892
|
+
? key extends keyof input
|
|
893
|
+
? VariableMatch<input[key], base[key]>
|
|
894
|
+
: never
|
|
895
|
+
: never
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
export type VariableValidation<options extends VariableOptions> =
|
|
899
|
+
VariableOptions extends options
|
|
900
|
+
? unknown
|
|
901
|
+
: Record<Exclude<keyof options, keyof VariableOptions>, never> &
|
|
902
|
+
(options extends { shorthands: infer map extends Shorthands.Map }
|
|
903
|
+
? { shorthands: Shorthands.Validated<map> }
|
|
904
|
+
: {}) &
|
|
905
|
+
(options extends { defaultVars: infer selected }
|
|
906
|
+
? {
|
|
907
|
+
readonly defaultVars: keyof options['vars']
|
|
908
|
+
readonly vars: {
|
|
909
|
+
readonly [key in keyof options['vars']]: VariableInput<
|
|
910
|
+
options['vars'][key]
|
|
911
|
+
> &
|
|
912
|
+
(selected extends keyof options['vars']
|
|
913
|
+
? Vars.Extract<options['vars'][key]> extends VariableMatch<
|
|
914
|
+
Vars.Extract<options['vars'][key]>,
|
|
915
|
+
Vars.Extract<options['vars'][selected]>
|
|
916
|
+
>
|
|
917
|
+
? unknown
|
|
918
|
+
: never
|
|
919
|
+
: never)
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
: { readonly vars: VariableInput<options['vars']> })
|
|
923
|
+
|
|
924
|
+
/** Configured authoring, portable references, and scoped set selection. */
|
|
925
|
+
export type VariableConfig<options extends VariableOptions> = {
|
|
926
|
+
/** Reads and persists the root set and color scheme. */
|
|
927
|
+
readonly appearance: Appearance.Root<
|
|
928
|
+
options extends { defaultVars: unknown }
|
|
929
|
+
? keyof options['vars'] & string
|
|
930
|
+
: never
|
|
931
|
+
>
|
|
932
|
+
/** Generates the root preference restoration script. */
|
|
933
|
+
readonly script: () => string
|
|
934
|
+
/** Authors styles with configured tokens and property aliases. */
|
|
935
|
+
readonly style: StyleFactory<
|
|
936
|
+
VariableTokens<options>,
|
|
937
|
+
options extends { layers: readonly (infer layer extends string)[] }
|
|
938
|
+
? layer
|
|
939
|
+
: never,
|
|
940
|
+
options extends { output: infer output extends style.Output }
|
|
941
|
+
? output
|
|
942
|
+
: 'react',
|
|
943
|
+
Mappings<options>
|
|
944
|
+
>
|
|
945
|
+
/** Authors recipes with the same variable and mapping contract. */
|
|
946
|
+
readonly variants: variants.Bound<
|
|
947
|
+
VariableTokens<options>,
|
|
948
|
+
options extends { output: infer output extends style.Output }
|
|
949
|
+
? output
|
|
950
|
+
: 'react',
|
|
951
|
+
options extends { layers: readonly (infer layer extends string)[] }
|
|
952
|
+
? layer
|
|
953
|
+
: never,
|
|
954
|
+
Mappings<options>
|
|
955
|
+
>
|
|
956
|
+
/** Explicit portable references, independent of shorthand mappings. */
|
|
957
|
+
/** Applies a named or default set and optional color scheme to a scope. */
|
|
958
|
+
readonly vars: VariableScope<options>
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/** References and scoped selection inferred from a configuration. */
|
|
962
|
+
export type VariableScope<options extends VariableOptions> = Vars.Bound<
|
|
963
|
+
VariableValues<options> extends Vars.Values ? VariableValues<options> : {},
|
|
964
|
+
options extends { defaultVars: unknown }
|
|
965
|
+
? keyof options['vars'] & string
|
|
966
|
+
: never,
|
|
967
|
+
options extends { output: infer output extends style.Output }
|
|
968
|
+
? output
|
|
969
|
+
: 'react'
|
|
970
|
+
>
|