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.
Files changed (247) hide show
  1. package/README.md +34 -19
  2. package/dist/Config.d.ts +16 -167
  3. package/dist/Config.d.ts.map +1 -1
  4. package/dist/Config.js +7 -246
  5. package/dist/Config.js.map +1 -1
  6. package/dist/Style.d.ts +15 -12
  7. package/dist/Style.d.ts.map +1 -1
  8. package/dist/Style.js +51 -26
  9. package/dist/Style.js.map +1 -1
  10. package/dist/Vars.d.ts +114 -0
  11. package/dist/Vars.d.ts.map +1 -0
  12. package/dist/Vars.js +42 -0
  13. package/dist/Vars.js.map +1 -0
  14. package/dist/compiler/Graph.js +11 -8
  15. package/dist/compiler/Graph.js.map +1 -1
  16. package/dist/compiler/Native.d.ts +5 -5
  17. package/dist/compiler/Native.d.ts.map +1 -1
  18. package/dist/compiler/Native.js +21 -21
  19. package/dist/compiler/Native.js.map +1 -1
  20. package/dist/compiler/Source.d.ts +8 -6
  21. package/dist/compiler/Source.d.ts.map +1 -1
  22. package/dist/compiler/Source.js +47 -14
  23. package/dist/compiler/Source.js.map +1 -1
  24. package/dist/compiler/Transform.d.ts +1 -1
  25. package/dist/compiler/Transform.d.ts.map +1 -1
  26. package/dist/compiler/Transform.js +58 -22
  27. package/dist/compiler/Transform.js.map +1 -1
  28. package/dist/compiler/internal/Compositions.d.ts +5 -0
  29. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  30. package/dist/compiler/internal/Compositions.js +22 -3
  31. package/dist/compiler/internal/Compositions.js.map +1 -1
  32. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  33. package/dist/compiler/internal/Configurations.js +73 -12
  34. package/dist/compiler/internal/Configurations.js.map +1 -1
  35. package/dist/compiler/internal/Contract.d.ts +1 -1
  36. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  37. package/dist/compiler/internal/Contract.js +155 -13
  38. package/dist/compiler/internal/Contract.js.map +1 -1
  39. package/dist/compiler/internal/Contributions.js +1 -1
  40. package/dist/compiler/internal/Contributions.js.map +1 -1
  41. package/dist/compiler/internal/Dynamic.js +1 -1
  42. package/dist/compiler/internal/Dynamic.js.map +1 -1
  43. package/dist/compiler/internal/Expression.d.ts +3 -0
  44. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  45. package/dist/compiler/internal/Expression.js +21 -0
  46. package/dist/compiler/internal/Expression.js.map +1 -1
  47. package/dist/compiler/internal/Identifiers.d.ts +1 -1
  48. package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
  49. package/dist/compiler/internal/Identifiers.js +2 -2
  50. package/dist/compiler/internal/Identifiers.js.map +1 -1
  51. package/dist/compiler/internal/NativeBindings.d.ts +1 -1
  52. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  53. package/dist/compiler/internal/NativeBindings.js +3 -3
  54. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  55. package/dist/compiler/internal/PackedStyles.d.ts +1 -1
  56. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  57. package/dist/compiler/internal/PackedStyles.js +15 -25
  58. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  59. package/dist/compiler/internal/Recipes.js +1 -1
  60. package/dist/compiler/internal/Recipes.js.map +1 -1
  61. package/dist/compiler/internal/Selectors.js +1 -1
  62. package/dist/compiler/internal/Selectors.js.map +1 -1
  63. package/dist/compiler/internal/Themes.d.ts +12 -3
  64. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  65. package/dist/compiler/internal/Themes.js +192 -52
  66. package/dist/compiler/internal/Themes.js.map +1 -1
  67. package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
  68. package/dist/compiler/internal/Vars.d.ts.map +1 -0
  69. package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
  70. package/dist/compiler/internal/Vars.js.map +1 -0
  71. package/dist/default.d.ts +147 -2562
  72. package/dist/default.d.ts.map +1 -1
  73. package/dist/default.js +85 -80
  74. package/dist/default.js.map +2 -2
  75. package/dist/default.js.zyzz.json +1 -1
  76. package/dist/esbuild/index.d.ts +3 -0
  77. package/dist/esbuild/index.d.ts.map +1 -0
  78. package/dist/esbuild/index.js +5 -0
  79. package/dist/esbuild/index.js.map +1 -0
  80. package/dist/index.d.ts +4 -4
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +4 -4
  83. package/dist/index.js.map +1 -1
  84. package/dist/internal/Authoring.d.ts +1 -1
  85. package/dist/internal/Authoring.d.ts.map +1 -1
  86. package/dist/internal/Authoring.js +4 -4
  87. package/dist/internal/Authoring.js.map +1 -1
  88. package/dist/internal/Binding.d.ts +1 -1
  89. package/dist/internal/Binding.d.ts.map +1 -1
  90. package/dist/internal/Completion.d.ts +19 -0
  91. package/dist/internal/Completion.d.ts.map +1 -0
  92. package/dist/internal/Completion.js +2 -0
  93. package/dist/internal/Completion.js.map +1 -0
  94. package/dist/internal/Condition.d.ts +3 -1
  95. package/dist/internal/Condition.d.ts.map +1 -1
  96. package/dist/internal/Condition.js.map +1 -1
  97. package/dist/internal/Configuration.d.ts +248 -0
  98. package/dist/internal/Configuration.d.ts.map +1 -0
  99. package/dist/internal/Configuration.js +343 -0
  100. package/dist/internal/Configuration.js.map +1 -0
  101. package/dist/{Theme.d.ts → internal/Theme.d.ts} +15 -13
  102. package/dist/internal/Theme.d.ts.map +1 -0
  103. package/dist/{Theme.js → internal/Theme.js} +39 -12
  104. package/dist/internal/Theme.js.map +1 -0
  105. package/dist/internal/Token.d.ts +46 -16
  106. package/dist/internal/Token.d.ts.map +1 -1
  107. package/dist/internal/Token.js +89 -5
  108. package/dist/internal/Token.js.map +1 -1
  109. package/dist/internal/Typography.d.ts +13 -5
  110. package/dist/internal/Typography.d.ts.map +1 -1
  111. package/dist/internal/Typography.js +42 -6
  112. package/dist/internal/Typography.js.map +1 -1
  113. package/dist/internal/Value.d.ts +6 -1
  114. package/dist/internal/Value.d.ts.map +1 -1
  115. package/dist/internal/Value.js.map +1 -1
  116. package/dist/internal/VariableSets.d.ts +20 -0
  117. package/dist/internal/VariableSets.d.ts.map +1 -0
  118. package/dist/internal/VariableSets.js +317 -0
  119. package/dist/internal/VariableSets.js.map +1 -0
  120. package/dist/next/loader.js +15 -5
  121. package/dist/next/loader.js.map +1 -1
  122. package/dist/node/Host.d.ts.map +1 -1
  123. package/dist/node/Host.js +1 -1
  124. package/dist/node/Host.js.map +1 -1
  125. package/dist/react-native/Host.d.ts +2 -2
  126. package/dist/react-native/Host.d.ts.map +1 -1
  127. package/dist/react-native/Host.js +4 -4
  128. package/dist/react-native/Host.js.map +1 -1
  129. package/dist/react-native/StyleSheet.d.ts +9 -8
  130. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  131. package/dist/react-native/StyleSheet.js +29 -23
  132. package/dist/react-native/StyleSheet.js.map +1 -1
  133. package/dist/react-native/Variants.d.ts +3 -3
  134. package/dist/react-native/Variants.d.ts.map +1 -1
  135. package/dist/react-native/Variants.js +2 -2
  136. package/dist/react-native/react.d.ts +1 -1
  137. package/dist/react-native/react.d.ts.map +1 -1
  138. package/dist/react-native/react.js +5 -5
  139. package/dist/react-native/react.js.map +1 -1
  140. package/dist/rollup/index.d.ts +3 -0
  141. package/dist/rollup/index.d.ts.map +1 -0
  142. package/dist/rollup/index.js +5 -0
  143. package/dist/rollup/index.js.map +1 -0
  144. package/dist/runtime/Appearance.d.ts +7 -7
  145. package/dist/runtime/Appearance.d.ts.map +1 -1
  146. package/dist/runtime/Appearance.js +15 -15
  147. package/dist/runtime/Appearance.js.map +1 -1
  148. package/dist/runtime/Dynamic.d.ts.map +1 -1
  149. package/dist/runtime/Dynamic.js +4 -1
  150. package/dist/runtime/Dynamic.js.map +1 -1
  151. package/dist/runtime/NativeContext.d.ts +4 -4
  152. package/dist/runtime/NativeContext.d.ts.map +1 -1
  153. package/dist/runtime/NativeContext.js +5 -5
  154. package/dist/runtime/NativeContext.js.map +1 -1
  155. package/dist/runtime/NativeDynamic.d.ts +1 -1
  156. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  157. package/dist/runtime/NativeDynamic.js.map +1 -1
  158. package/dist/runtime/Props.d.ts +1 -1
  159. package/dist/runtime/Props.js +3 -3
  160. package/dist/runtime/Props.js.map +1 -1
  161. package/dist/runtime/Selection.d.ts +14 -10
  162. package/dist/runtime/Selection.d.ts.map +1 -1
  163. package/dist/runtime/Selection.js +19 -18
  164. package/dist/runtime/Selection.js.map +1 -1
  165. package/dist/styleFunction.d.ts +14 -14
  166. package/dist/styleFunction.d.ts.map +1 -1
  167. package/dist/styleFunction.js.map +1 -1
  168. package/dist/unplugin/index.d.ts +20 -0
  169. package/dist/unplugin/index.d.ts.map +1 -0
  170. package/dist/unplugin/index.js +498 -0
  171. package/dist/unplugin/index.js.map +1 -0
  172. package/dist/variants.d.ts +3 -3
  173. package/dist/variants.d.ts.map +1 -1
  174. package/dist/variants.js.map +1 -1
  175. package/dist/vite/index.d.ts.map +1 -1
  176. package/dist/vite/index.js +6 -3
  177. package/dist/vite/index.js.map +1 -1
  178. package/dist/web/Css.d.ts +4 -3
  179. package/dist/web/Css.d.ts.map +1 -1
  180. package/dist/web/Css.js +4 -4
  181. package/dist/web/Css.js.map +1 -1
  182. package/dist/web/internal/Themes.d.ts +3 -2
  183. package/dist/web/internal/Themes.d.ts.map +1 -1
  184. package/dist/web/internal/Themes.js +42 -6
  185. package/dist/web/internal/Themes.js.map +1 -1
  186. package/dist/webpack/index.d.ts +3 -0
  187. package/dist/webpack/index.d.ts.map +1 -0
  188. package/dist/webpack/index.js +5 -0
  189. package/dist/webpack/index.js.map +1 -0
  190. package/package.json +23 -2
  191. package/src/Config.ts +43 -685
  192. package/src/Style.ts +97 -34
  193. package/src/Vars.ts +299 -0
  194. package/src/compiler/Graph.ts +14 -9
  195. package/src/compiler/Native.ts +29 -32
  196. package/src/compiler/Source.ts +59 -21
  197. package/src/compiler/Transform.ts +73 -23
  198. package/src/compiler/internal/Compositions.ts +36 -3
  199. package/src/compiler/internal/Configurations.ts +83 -11
  200. package/src/compiler/internal/Contract.ts +207 -15
  201. package/src/compiler/internal/Contributions.ts +1 -1
  202. package/src/compiler/internal/Dynamic.ts +1 -1
  203. package/src/compiler/internal/Expression.ts +26 -0
  204. package/src/compiler/internal/Identifiers.ts +2 -1
  205. package/src/compiler/internal/NativeBindings.ts +3 -3
  206. package/src/compiler/internal/PackedStyles.ts +22 -26
  207. package/src/compiler/internal/Recipes.ts +1 -1
  208. package/src/compiler/internal/Selectors.ts +1 -1
  209. package/src/compiler/internal/Themes.ts +266 -57
  210. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  211. package/src/default.ts +173 -154
  212. package/src/esbuild/index.ts +5 -0
  213. package/src/index.ts +4 -4
  214. package/src/internal/Authoring.ts +5 -7
  215. package/src/internal/Binding.ts +1 -1
  216. package/src/internal/Completion.ts +42 -0
  217. package/src/internal/Condition.ts +31 -1
  218. package/src/internal/Configuration.ts +970 -0
  219. package/src/{Theme.ts → internal/Theme.ts} +75 -20
  220. package/src/internal/Token.ts +258 -85
  221. package/src/internal/Typography.ts +71 -16
  222. package/src/internal/Value.ts +45 -26
  223. package/src/internal/VariableSets.ts +458 -0
  224. package/src/next/loader.ts +19 -5
  225. package/src/node/Host.ts +1 -2
  226. package/src/react-native/Host.ts +6 -6
  227. package/src/react-native/StyleSheet.ts +49 -42
  228. package/src/react-native/Variants.ts +3 -3
  229. package/src/react-native/react.ts +6 -6
  230. package/src/rollup/index.ts +5 -0
  231. package/src/runtime/Appearance.ts +20 -20
  232. package/src/runtime/Dynamic.ts +4 -1
  233. package/src/runtime/NativeContext.ts +8 -8
  234. package/src/runtime/NativeDynamic.ts +2 -4
  235. package/src/runtime/Props.ts +3 -3
  236. package/src/runtime/Selection.ts +44 -33
  237. package/src/styleFunction.ts +22 -19
  238. package/src/unplugin/index.ts +600 -0
  239. package/src/variants.ts +7 -5
  240. package/src/vite/index.ts +5 -4
  241. package/src/web/Css.ts +10 -7
  242. package/src/web/internal/Themes.ts +52 -9
  243. package/src/webpack/index.ts +5 -0
  244. package/dist/Theme.d.ts.map +0 -1
  245. package/dist/Theme.js.map +0 -1
  246. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  247. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -193,6 +193,7 @@ export function collect(options: collect.Options) {
193
193
  )) {
194
194
  const selected: Source.Call[] = []
195
195
  const inputs: NonNullable<Source.Call['runtimeComposition']>[number][] = []
196
+ const outputs = new Set<'html' | undefined>()
196
197
  let runtime = false
197
198
  let conditions = 0
198
199
  const attributes = new Map<string, string>()
@@ -256,7 +257,6 @@ export function collect(options: collect.Options) {
256
257
  const resolved = bindings.resolve(expression, nodes)
257
258
  const conditional =
258
259
  resolved.type === 'LogicalExpression' && resolved.operator === '&&'
259
- const condition = conditional ? conditions++ : undefined
260
260
  const value = conditional
261
261
  ? bindings.resolve(resolved.right, nodes)
262
262
  : resolved
@@ -305,6 +305,19 @@ export function collect(options: collect.Options) {
305
305
  'Composition requires statically known local style applications.',
306
306
  value,
307
307
  )
308
+ const scope = options.scopes?.get(value.start)
309
+ if (scope?.end === value.end) {
310
+ outputs.add(scope.output)
311
+ inputs.push({
312
+ start: expression.start,
313
+ end: expression.end,
314
+ name: '',
315
+ owners: [],
316
+ })
317
+ runtime = true
318
+ continue
319
+ }
320
+ const condition = conditional ? conditions++ : undefined
308
321
  const nested = calls.get(value.start)
309
322
  if (nested?.compositionCases && resolved !== expression)
310
323
  throw new Themes.InvalidError(
@@ -379,7 +392,8 @@ export function collect(options: collect.Options) {
379
392
  options.source.slice(application.start, application.calleeEnd),
380
393
  )
381
394
  }
382
- if (selected.some((call) => call.output !== selected[0]?.output))
395
+ for (const call of selected) outputs.add(call.output)
396
+ if (outputs.size > 1)
383
397
  throw new Themes.InvalidError(
384
398
  'Composition cannot mix HTML and React props.',
385
399
  node,
@@ -497,6 +511,12 @@ export function collect(options: collect.Options) {
497
511
  ),
498
512
  }
499
513
  : {}),
514
+ typography: new Map(
515
+ selected.flatMap((call) => [...(call.typography ?? [])]),
516
+ ),
517
+ typographyQueries: new Map(
518
+ selected.flatMap((call) => [...(call.typographyQueries ?? [])]),
519
+ ),
500
520
  name,
501
521
  start: node.start,
502
522
  end: node.end,
@@ -507,7 +527,7 @@ export function collect(options: collect.Options) {
507
527
  end: node.end,
508
528
  properties: selected.flatMap((call) => properties(call)),
509
529
  },
510
- ...(selected[0]?.output ? { output: selected[0].output } : {}),
530
+ ...(outputs.has('html') ? { output: 'html' as const } : {}),
511
531
  ...(identities.length ? { identity: identities.join(' ') } : {}),
512
532
  }
513
533
  calls.set(node.start, call)
@@ -538,6 +558,12 @@ export function collect(options: collect.Options) {
538
558
  start: call.start,
539
559
  end: call.end,
540
560
  compositionCase: true,
561
+ typography: new Map(
562
+ included.flatMap((call) => [...(call.typography ?? [])]),
563
+ ),
564
+ typographyQueries: new Map(
565
+ included.flatMap((call) => [...(call.typographyQueries ?? [])]),
566
+ ),
541
567
  body: {
542
568
  ...call.body!,
543
569
  properties: included.flatMap((call) => properties(call)),
@@ -604,6 +630,13 @@ export declare namespace collect {
604
630
  readonly links?: Readonly<Record<string, Themes.Link>> | undefined
605
631
  /** Parsed module with lexical binding scopes. */
606
632
  readonly program: Ast.Program
633
+ /** Validated variable scope applications preserved as runtime props. */
634
+ readonly scopes?:
635
+ | ReadonlyMap<
636
+ number,
637
+ { readonly end: number; readonly output: 'html' | undefined }
638
+ >
639
+ | undefined
607
640
  /** Source used to preserve initialization-sensitive binding reads. */
608
641
  readonly source: string
609
642
  /** Validated ordered style bodies. */
@@ -4,21 +4,32 @@
4
4
  */
5
5
  import * as Identity from '../../internal/Identity.js'
6
6
  import type * as Ast from '@oxc-project/types'
7
- import * as Config from '../../Config.js'
7
+ import * as Config from '../../internal/Configuration.js'
8
+ import * as PublicConfig from '../../Config.js'
8
9
  import * as Shorthands from '../../internal/Shorthands.js'
9
10
  import * as Token from '../../internal/Token.js'
10
- import * as Theme from '../../Theme.js'
11
+ import * as Theme from '../../internal/Theme.js'
12
+ import * as VariableSets from '../../internal/VariableSets.js'
13
+ import * as Vars from '../../Vars.js'
11
14
  import type * as Themes from './Themes.js'
12
15
 
13
16
  /** Reads a direct configuration factory without executing authored expressions. */
14
17
  export function collect(options: collect.Options): Themes.Link {
15
18
  function data(node: Ast.Node): unknown {
16
19
  const linked = options.resolve(node)
17
- if (linked?.kind === 'theme') return linked.definition
20
+ if (
21
+ linked?.kind === 'theme' ||
22
+ (linked?.call.variableConfig && linked.call.selection)
23
+ )
24
+ return linked.definition[Token.definition].contract.variableSet
25
+ ? VariableSets.from(linked.definition[Token.definition])
26
+ : linked.definition
18
27
 
19
28
  if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')
20
29
  return data(node.expression)
21
30
 
31
+ if (node.type === 'Literal' && node.value === false) return false
32
+
22
33
  if (node.type === 'ArrayExpression')
23
34
  return node.elements.map((node) => {
24
35
  if (!node || node.type === 'SpreadElement')
@@ -77,12 +88,40 @@ export function collect(options: collect.Options): Themes.Link {
77
88
  'Config.create accepts one literal options object.',
78
89
  )
79
90
 
80
- const input = (
91
+ let input = (
81
92
  options.expression.arguments.length
82
93
  ? data(options.expression.arguments[0]!)
83
94
  : {}
84
95
  ) as Config.create.Options
85
96
 
97
+ PublicConfig.create(input as unknown as PublicConfig.create.Options)
98
+
99
+ const authored = input as unknown as Config.VariableOptions
100
+ const variableMode = authored.vars !== undefined
101
+ if (variableMode) {
102
+ Config.create(authored)
103
+ const wrap = (value: Vars.Definition | Vars.Values) =>
104
+ VariableSets.theme(
105
+ Token.definition in value
106
+ ? (value as Vars.Definition)
107
+ : Vars.define(value as Vars.Values),
108
+ authored.mappings,
109
+ )
110
+ const { vars, defaultVars, mappings: _mappings, ...rest } = authored
111
+ input =
112
+ defaultVars === undefined
113
+ ? { ...rest, theme: wrap(vars as Vars.Definition) }
114
+ : {
115
+ ...rest,
116
+ defaultTheme: defaultVars,
117
+ themes: Object.fromEntries(
118
+ Object.entries(vars).map(([key, value]) => [
119
+ key,
120
+ wrap(value as Vars.Definition),
121
+ ]),
122
+ ),
123
+ }
124
+ }
86
125
  const config = Config.create(input)
87
126
 
88
127
  const catalog = (() => {
@@ -102,8 +141,17 @@ export function collect(options: collect.Options): Themes.Link {
102
141
  ? options.name
103
142
  : Identity.requireId(input.id, 'Config.create')
104
143
  const contract = Object.freeze({
144
+ ...(variableMode
145
+ ? {
146
+ variableSet: true,
147
+ mappings: VariableSets.mappings(authored.mappings),
148
+ }
149
+ : {}),
105
150
  [Token.identity]: identity,
106
151
  cssOutput: input.cssOutput ?? 'atomic',
152
+ ...(input.defaultLayer !== undefined
153
+ ? { defaultLayer: input.defaultLayer }
154
+ : {}),
107
155
  ...(input.shorthands
108
156
  ? { shorthands: Shorthands.read(input.shorthands) }
109
157
  : {}),
@@ -151,6 +199,16 @@ export function collect(options: collect.Options): Themes.Link {
151
199
  if ('themes' in config && selected)
152
200
  members[JSON.stringify(['theme'])] = selected
153
201
 
202
+ if (variableMode && selected)
203
+ members[JSON.stringify(['vars'])] = {
204
+ ...selected,
205
+ call: {
206
+ ...selected.call,
207
+ directVariables: true,
208
+ type: `import('zyzz').Vars.References<${selected.call.tokenType}>`,
209
+ },
210
+ }
211
+
154
212
  const definition =
155
213
  selected?.definition ?? Token.bind(Theme.define({}), contract)
156
214
 
@@ -160,10 +218,14 @@ export function collect(options: collect.Options): Themes.Link {
160
218
  return {
161
219
  defaultTheme: input.defaultTheme,
162
220
  themes: Object.fromEntries(
163
- Object.entries(catalog).map(([name, theme]) => [
164
- name,
165
- { ...values(theme.tokens), ...theme[Token.definition].queries },
166
- ]),
221
+ Object.keys(catalog).map((name) => {
222
+ const theme =
223
+ members[JSON.stringify(['themes', name])]!.definition!
224
+ return [
225
+ name,
226
+ { ...values(theme.tokens), ...theme[Token.definition].queries },
227
+ ]
228
+ }),
167
229
  ),
168
230
  }
169
231
  }
@@ -171,8 +233,8 @@ export function collect(options: collect.Options): Themes.Link {
171
233
  if ('theme' in config) {
172
234
  return {
173
235
  theme: {
174
- ...values(config.theme.tokens),
175
- ...config.theme[Token.definition].queries,
236
+ ...values(definition.tokens),
237
+ ...definition[Token.definition].queries,
176
238
  },
177
239
  }
178
240
  }
@@ -180,6 +242,9 @@ export function collect(options: collect.Options): Themes.Link {
180
242
  return {}
181
243
  })(),
182
244
  ...(input.cssOutput ? { cssOutput: input.cssOutput } : {}),
245
+ ...(input.defaultLayer !== undefined
246
+ ? { defaultLayer: input.defaultLayer }
247
+ : {}),
183
248
  ...(input.shorthands ? { shorthands: input.shorthands } : {}),
184
249
  ...(input.output ? { output: input.output } : {}),
185
250
  ...(input.layers ? { layers: input.layers } : {}),
@@ -189,6 +254,9 @@ export function collect(options: collect.Options): Themes.Link {
189
254
  return {
190
255
  binding: options.name,
191
256
  call: {
257
+ ...(variableMode
258
+ ? { variableConfig: true, variableMappings: authored.mappings }
259
+ : {}),
192
260
  appearance: true,
193
261
  script: true,
194
262
  end: options.expression.end,
@@ -199,7 +267,9 @@ export function collect(options: collect.Options): Themes.Link {
199
267
  options: normalized,
200
268
  start: options.expression.start,
201
269
  tokenType: selected?.call.tokenType ?? '{}',
202
- type: `import('zyzz').Config.create.ReturnType<${type(normalized)}>`,
270
+ type: variableMode
271
+ ? `import('zyzz').Config.VariableConfig<${type({ ...normalized, theme: undefined, themes: undefined, defaultTheme: undefined, vars: 'themes' in normalized ? normalized.themes : normalized.theme, ...('themes' in normalized ? { defaultVars: normalized.defaultTheme } : {}), ...(authored.mappings !== undefined ? { mappings: VariableSets.mappings(authored.mappings) } : {}) })}>`
272
+ : `import('zyzz').Config.create.ReturnType<${type(normalized)}>`,
203
273
  },
204
274
  definition,
205
275
  kind: 'config',
@@ -224,10 +294,12 @@ export declare namespace collect {
224
294
 
225
295
  /** Encodes validated literal options as a declaration type, never executable text. */
226
296
  export function type(value: unknown): string {
297
+ if (Token.is(value)) return `import('zyzz').Vars.Reference<'${value.group}'>`
227
298
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
228
299
  if (!value || typeof value !== 'object') return JSON.stringify(value)
229
300
 
230
301
  return `{${Object.entries(value)
302
+ .filter(([, value]) => value !== undefined)
231
303
  .map(([key, value]) => `readonly ${JSON.stringify(key)}:${type(value)}`)
232
304
  .join(';')}}`
233
305
  }
@@ -3,14 +3,16 @@
3
3
  * @module
4
4
  */
5
5
  import type * as Binding from '../../internal/Binding.js'
6
- import * as Config from '../../Config.js'
6
+ import * as Config from '../../internal/Configuration.js'
7
7
  import * as Configurations from './Configurations.js'
8
8
  import * as FunctionSyntax from '../../internal/FunctionSyntax.js'
9
9
  import * as Identifiers from './Identifiers.js'
10
10
  import * as PackedStyles from './PackedStyles.js'
11
11
  import * as Shorthands from '../../internal/Shorthands.js'
12
12
  import * as Stylesheets from './Stylesheets.js'
13
- import * as Theme from '../../Theme.js'
13
+ import * as Theme from '../../internal/Theme.js'
14
+ import * as VariableSets from '../../internal/VariableSets.js'
15
+ import * as Vars from '../../Vars.js'
14
16
  import type * as Themes from './Themes.js'
15
17
  import * as Token from '../../internal/Token.js'
16
18
 
@@ -24,7 +26,7 @@ export function read(
24
26
  if (
25
27
  ![
26
28
  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
27
- 22, 23, 24,
29
+ 22, 23, 24, 25, 26, 27,
28
30
  ].includes(data.version as number)
29
31
  )
30
32
  throw new Error('Unsupported Zyzz contract version.')
@@ -56,12 +58,53 @@ export function read(
56
58
  for (const entry of Object.values(record(data.exports))) collect(entry)
57
59
  }
58
60
 
61
+ function decode(value: unknown): unknown {
62
+ if (!value || typeof value !== 'object') return value
63
+ if (Array.isArray(value)) return value.map(decode)
64
+ const fields = record(value)
65
+ if (Object.hasOwn(fields, '$object'))
66
+ return Object.fromEntries(
67
+ Object.entries(record(fields.$object)).map(([key, value]) => [
68
+ key,
69
+ decode(value),
70
+ ]),
71
+ )
72
+ if (Object.hasOwn(fields, '$variable')) {
73
+ const reference = record(fields.$variable)
74
+ const identity = string(reference.identity)
75
+ let contract = identities.get(identity)
76
+ if (contract && !contract.variableSet)
77
+ throw new Error(
78
+ 'Conflicting packed variable-set modes for one identity.',
79
+ )
80
+ if (!contract) {
81
+ contract = Object.freeze({
82
+ variableSet: true,
83
+ [Token.identity]: identity,
84
+ })
85
+ identities.set(identity, contract)
86
+ }
87
+ const resolved = decode(reference.value) as Token.Value
88
+ return Token.create({
89
+ contract,
90
+ group: VariableSets.domain(resolved),
91
+ path: string(reference.path),
92
+ value: resolved,
93
+ })
94
+ }
95
+ return Object.fromEntries(
96
+ Object.entries(fields).map(([key, value]) => [key, decode(value)]),
97
+ )
98
+ }
59
99
  const themes: Record<string, Theme.Definition> = Object.create(null)
60
100
  const types: Record<string, string> = Object.create(null)
61
101
 
62
102
  for (const [name, value] of Object.entries(record(data.themes))) {
63
103
  const entry = record(value)
64
104
  const identity = string(entry.identity)
105
+ if (entry.variableSet === true && (data.version as number) < 26)
106
+ throw new Error('Vars contracts require contract version 26 or later.')
107
+ const mappings = VariableSets.mappings(entry.mappings)
65
108
  if (
66
109
  entry.cssOutput !== undefined &&
67
110
  entry.cssOutput !== 'atomic' &&
@@ -73,11 +116,28 @@ export function read(
73
116
  | 'grouped'
74
117
  | undefined
75
118
 
119
+ if (entry.defaultLayer !== undefined) {
120
+ if ((data.version as number) < 27)
121
+ throw new Error('Default layers require contract version 27 or later.')
122
+ Config.create({ defaultLayer: entry.defaultLayer as string })
123
+ }
124
+ const defaultLayer = entry.defaultLayer as string | undefined
125
+
76
126
  const shorthands =
77
127
  entry.shorthands !== undefined
78
128
  ? Shorthands.read(entry.shorthands)
79
129
  : undefined
80
130
  let contract = identities.get(identity)
131
+ if (
132
+ contract &&
133
+ Boolean(contract.variableSet) !== (entry.variableSet === true)
134
+ )
135
+ throw new Error('Conflicting packed variable-set modes for one identity.')
136
+ if (
137
+ contract &&
138
+ JSON.stringify(contract.mappings ?? {}) !== JSON.stringify(mappings ?? {})
139
+ )
140
+ throw new Error('Conflicting packed variable mappings for one identity.')
81
141
  if (
82
142
  contract &&
83
143
  Shorthands.signature(contract.shorthands) !==
@@ -95,19 +155,37 @@ export function read(
95
155
  'Conflicting packed CSS output modes for one theme identity.',
96
156
  )
97
157
 
158
+ if (contract && contract.defaultLayer !== defaultLayer)
159
+ throw new Error(
160
+ 'Conflicting packed default layers for one theme identity.',
161
+ )
162
+
98
163
  if (!contract) {
99
164
  contract = Object.freeze({
165
+ ...(entry.variableSet === true
166
+ ? {
167
+ variableSet: true,
168
+ mappings,
169
+ }
170
+ : {}),
100
171
  ...(entry.shorthands !== undefined
101
172
  ? { shorthands: Shorthands.read(entry.shorthands) }
102
173
  : {}),
103
174
  ...(cssOutput ? { cssOutput } : {}),
175
+ ...(defaultLayer !== undefined ? { defaultLayer } : {}),
104
176
  [Token.complete]: true,
105
177
  [Token.identity]: identity,
106
178
  })
107
179
  identities.set(identity, contract)
108
180
  }
109
181
 
110
- const definition = Theme.define(record(entry.tokens) as Theme.Tokens)
182
+ const definition =
183
+ entry.variableSet === true
184
+ ? VariableSets.theme(
185
+ Vars.define(decode(entry.tokens) as Vars.Values),
186
+ mappings,
187
+ )
188
+ : Theme.define(record(entry.tokens) as Theme.Tokens)
111
189
  if (
112
190
  (data.version as number) < 24 &&
113
191
  Object.hasOwn(definition.tokens, 'typography')
@@ -116,6 +194,15 @@ export function read(
116
194
  'Packed typography sets require contract version 24 or later.',
117
195
  )
118
196
 
197
+ if (
198
+ (data.version as number) < 25 &&
199
+ (definition[Token.definition].paths ||
200
+ Object.hasOwn(definition.tokens, 'borderWidth'))
201
+ )
202
+ throw new Error(
203
+ 'Packed responsive typography and border widths require contract version 25 or later.',
204
+ )
205
+
119
206
  themes[name] = Token.bind(definition, contract)
120
207
  types[name] = type(input(definition))
121
208
  }
@@ -206,7 +293,8 @@ export function read(
206
293
  const reference = string(entry.reference)
207
294
  if (
208
295
  ![
209
- 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
296
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
297
+ 27,
210
298
  ].includes(data.version as number) ||
211
299
  ![
212
300
  'cssFunction',
@@ -254,10 +342,7 @@ export function read(
254
342
  }
255
343
 
256
344
  if (entry.kind === 'style-reference') {
257
- if (
258
- entry.style !== undefined &&
259
- ![16, 17, 18, 19, 20, 21, 22, 23, 24].includes(data.version as number)
260
- )
345
+ if (entry.style !== undefined && (data.version as number) < 16)
261
346
  throw new Error(
262
347
  'Packed callable styles require contract version 16 or later.',
263
348
  )
@@ -331,10 +416,17 @@ export function read(
331
416
  : undefined
332
417
 
333
418
  const options =
334
- entry.options === undefined ? undefined : record(entry.options)
419
+ entry.options === undefined ? undefined : record(decode(entry.options))
335
420
 
336
421
  if (options) {
337
- Config.create(options as Config.create.Options)
422
+ if (entry.variableConfig === true)
423
+ Config.create(
424
+ variableOptions(
425
+ options,
426
+ entry.variableMappings as Vars.Mappings | false | undefined,
427
+ ),
428
+ )
429
+ else Config.create(options as Config.create.Options)
338
430
  if (
339
431
  (data.version as number) >= 17 &&
340
432
  (options.cssOutput ?? 'atomic') !==
@@ -344,6 +436,14 @@ export function read(
344
436
  'Configuration CSS output disagrees with linked theme metadata.',
345
437
  )
346
438
 
439
+ if (
440
+ options.defaultLayer !==
441
+ definition[Token.definition].contract.defaultLayer
442
+ )
443
+ throw new Error(
444
+ 'Configuration default layer disagrees with linked theme metadata.',
445
+ )
446
+
347
447
  if (
348
448
  Shorthands.signature(options.shorthands) !==
349
449
  Shorthands.signature(definition[Token.definition].contract.shorthands)
@@ -357,7 +457,9 @@ export function read(
357
457
  !!options?.themes &&
358
458
  ((data.version as number) < 4 || entry.catalogOnly === true)
359
459
  const fullConfigType = options
360
- ? `import('zyzz').Config.create.ReturnType<${Configurations.type(options)}>`
460
+ ? entry.variableConfig === true
461
+ ? `import('zyzz').Config.VariableConfig<${Configurations.type(variableOptions(options, entry.variableMappings as Vars.Mappings | false | undefined))}>`
462
+ : `import('zyzz').Config.create.ReturnType<${Configurations.type(options)}>`
361
463
  : ''
362
464
  // Helpers a legacy library did not compile are hidden from its consumers' types.
363
465
  const hidden = [
@@ -376,6 +478,27 @@ export function read(
376
478
  return {
377
479
  binding: string(entry.binding),
378
480
  call: {
481
+ ...(entry.variableSet === true
482
+ ? {
483
+ variableSet: true,
484
+ type: `import('zyzz').Vars.Definition<${types[theme]!}>`,
485
+ }
486
+ : {}),
487
+ ...(entry.directVariables === true
488
+ ? {
489
+ directVariables: true,
490
+ type: `import('zyzz').Vars.${entry.variableSet === true ? 'Definition' : 'References'}<${types[theme]!}>`,
491
+ }
492
+ : {}),
493
+ ...(entry.variableConfig === true
494
+ ? {
495
+ variableConfig: true,
496
+ variableMappings: entry.variableMappings as
497
+ | Vars.Mappings
498
+ | false
499
+ | undefined,
500
+ }
501
+ : {}),
379
502
  ...(entry.recipe === true ? { recipe: true } : {}),
380
503
  ...(entry.output === 'html' ? { output: 'html' as const } : {}),
381
504
  ...(catalogOnly ? { catalogOnly: true } : {}),
@@ -399,7 +522,7 @@ export function read(
399
522
  ...(options
400
523
  ? {
401
524
  options,
402
- type: `${outputType}${entry.initialization === true ? "['script']" : entry.root === true ? "['appearance']" : entry.selection === true ? "['themes']" : ''}`,
525
+ type: `${outputType}${entry.initialization === true ? "['script']" : entry.root === true ? "['appearance']" : entry.selection === true ? (entry.variableConfig === true ? "['vars']" : "['themes']") : ''}`,
403
526
  }
404
527
  : {}),
405
528
  ...(members
@@ -459,6 +582,7 @@ function tokens(tree: Theme.References<Theme.Tokens>): Record<string, unknown> {
459
582
  }
460
583
 
461
584
  function type(value: unknown): string {
585
+ if (Token.is(value)) return Configurations.type(value)
462
586
  if (Array.isArray(value)) return `readonly [${value.map(type).join(',')}]`
463
587
  if (!value || typeof value !== 'object') return JSON.stringify(value)
464
588
 
@@ -525,6 +649,11 @@ export function write(
525
649
  }
526
650
 
527
651
  return {
652
+ ...(link.call.variableSet ? { variableSet: true } : {}),
653
+ ...(link.call.directVariables ? { directVariables: true } : {}),
654
+ ...(link.call.variableConfig
655
+ ? { variableConfig: true, variableMappings: link.call.variableMappings }
656
+ : {}),
528
657
  ...(link.call.recipe ? { recipe: true } : {}),
529
658
  ...(link.call.output ? { output: link.call.output } : {}),
530
659
  ...(link.call.script &&
@@ -541,7 +670,7 @@ export function write(
541
670
  ...(link.call.selection ? { selection: true } : {}),
542
671
  ...(link.call.initialization ? { initialization: true } : {}),
543
672
  ...(link.call.root ? { root: true } : {}),
544
- ...(link.call.options ? { options: link.call.options } : {}),
673
+ ...(link.call.options ? { options: encode(link.call.options) } : {}),
545
674
  ...(link.members
546
675
  ? {
547
676
  members: Object.fromEntries(
@@ -572,15 +701,46 @@ export function write(
572
701
  ...(theme[Token.definition].contract.shorthands
573
702
  ? { shorthands: theme[Token.definition].contract.shorthands }
574
703
  : {}),
704
+ ...(theme[Token.definition].contract.defaultLayer !== undefined
705
+ ? { defaultLayer: theme[Token.definition].contract.defaultLayer }
706
+ : {}),
575
707
  ...(theme[Token.definition].contract.cssOutput
576
708
  ? { cssOutput: theme[Token.definition].contract.cssOutput }
577
709
  : {}),
710
+ ...(theme[Token.definition].contract.variableSet
711
+ ? {
712
+ variableSet: true,
713
+ mappings: theme[Token.definition].contract.mappings,
714
+ }
715
+ : {}),
578
716
  identity: theme[Token.definition].contract[Token.identity],
579
- tokens: input(theme),
717
+ tokens: encode(input(theme)),
580
718
  },
581
719
  ]),
582
720
  ),
583
721
  version: (() => {
722
+ if (
723
+ Object.values(themes).some(
724
+ (theme) =>
725
+ theme[Token.definition].contract.defaultLayer !== undefined,
726
+ )
727
+ )
728
+ return 27
729
+ if (
730
+ Object.values(themes).some(
731
+ (theme) => theme[Token.definition].contract.variableSet,
732
+ )
733
+ )
734
+ return 26
735
+ if (
736
+ Object.values(themes).some(
737
+ (theme) =>
738
+ theme[Token.definition].paths ||
739
+ Object.hasOwn(theme.tokens, 'borderWidth'),
740
+ )
741
+ )
742
+ return 25
743
+
584
744
  if (
585
745
  Object.values(themes).some((theme) =>
586
746
  Object.hasOwn(theme.tokens, 'typography'),
@@ -858,3 +1018,35 @@ function extended(signature: NonNullable<Themes.Call['function']>): boolean {
858
1018
  )
859
1019
  )
860
1020
  }
1021
+
1022
+ function encode(value: unknown): unknown {
1023
+ if (Token.is(value))
1024
+ return {
1025
+ $variable: {
1026
+ identity: value.contract[Token.identity],
1027
+ path: value.path,
1028
+ value: encode(value.value),
1029
+ },
1030
+ }
1031
+ if (Array.isArray(value)) return value.map(encode)
1032
+ if (!value || typeof value !== 'object') return value
1033
+ const fields = Object.fromEntries(
1034
+ Object.entries(value).map(([key, value]) => [key, encode(value)]),
1035
+ )
1036
+ return Object.hasOwn(fields, '$variable') || Object.hasOwn(fields, '$object')
1037
+ ? { $object: fields }
1038
+ : fields
1039
+ }
1040
+
1041
+ function variableOptions(
1042
+ options: Record<string, unknown>,
1043
+ mappings?: Vars.Mappings | false,
1044
+ ): Config.VariableOptions {
1045
+ const { theme, themes, defaultTheme, ...rest } = options
1046
+ return {
1047
+ ...rest,
1048
+ vars: (themes ?? theme) as Vars.Values,
1049
+ ...(themes ? { defaultVars: String(defaultTheme) } : {}),
1050
+ ...(mappings !== undefined ? { mappings } : {}),
1051
+ }
1052
+ }
@@ -18,7 +18,7 @@ import * as Properties from './Properties.js'
18
18
  import * as RuleReference from '../../internal/RuleReference.js'
19
19
  import type * as Scope from './Scope.js'
20
20
  import * as Style from '../../Style.js'
21
- import * as Theme from '../../Theme.js'
21
+ import * as Theme from '../../internal/Theme.js'
22
22
  import * as Themes from './Themes.js'
23
23
  import type * as Token from '../../internal/Token.js'
24
24
  import * as Walker from 'oxc-walker'
@@ -64,7 +64,7 @@ export function read(
64
64
  'key',
65
65
  'ref',
66
66
  'style',
67
- 'variables',
67
+ 'vars',
68
68
  '__proto__',
69
69
  ].includes(key) ||
70
70
  Object.hasOwn(slots, key)