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
@@ -10,11 +10,13 @@ import type * as RuleReference from '../../internal/RuleReference.js'
10
10
  import type * as Ast from '@oxc-project/types'
11
11
  import type * as Walker from 'oxc-walker'
12
12
  import type * as Binding from '../../internal/Binding.js'
13
- import * as Config from '../../Config.js'
13
+ import * as Config from '../../internal/Configuration.js'
14
14
  import * as Expression from './Expression.js'
15
15
  import * as Configurations from './Configurations.js'
16
16
  import * as Token from '../../internal/Token.js'
17
- import * as Theme from '../../Theme.js'
17
+ import * as Theme from '../../internal/Theme.js'
18
+ import * as VariableSets from '../../internal/VariableSets.js'
19
+ import * as Vars from '../../Vars.js'
18
20
  import type * as Source from '../Source.js'
19
21
  import type * as PackedStyles from './PackedStyles.js'
20
22
 
@@ -30,6 +32,10 @@ export type Alias = Call & {
30
32
 
31
33
  /** Theme factory span and generated scope key. */
32
34
  export type Call = {
35
+ readonly variableSet?: boolean | undefined
36
+ readonly directVariables?: boolean | undefined
37
+ readonly variableConfig?: boolean | undefined
38
+ readonly variableMappings?: Vars.Mappings | false | undefined
33
39
  /** Whether this bound authoring alias declares recipes. */
34
40
  readonly recipe?: boolean | undefined
35
41
  /** Static CSS function signature shared during extraction and packed serialization. */
@@ -126,12 +132,17 @@ export function collect(program: Ast.Program, options: collect.Options) {
126
132
  const aliasBindings = new Map<number, Alias>()
127
133
  const aliasReferences = new Set<number>()
128
134
  const appearances = new Set<string>()
135
+ const scopeApplications = new Map<
136
+ number,
137
+ { end: number; output: 'html' | undefined }
138
+ >()
129
139
  const selections = new Set<string>()
130
140
  const calls: Call[] = []
131
141
  const scripts = new Set<string>()
132
142
  const definitions = new Map<number, Call>()
133
143
  const factories = new Set<number>()
134
144
  const imports = new Set<number>()
145
+ const variableImports = new Set<number>()
135
146
  const configImports = new Set<number>()
136
147
  const configs = new Map<string, Link>()
137
148
  const configBindings = new Map<number, Link>()
@@ -165,10 +176,11 @@ export function collect(program: Ast.Program, options: collect.Options) {
165
176
  specifier.importKind !== 'type' &&
166
177
  (specifier.imported.type === 'Identifier'
167
178
  ? specifier.imported.name
168
- : specifier.imported.value) === 'Theme'
169
- )
179
+ : specifier.imported.value) === 'Vars'
180
+ ) {
170
181
  imports.add(specifier.start)
171
- else if (
182
+ variableImports.add(specifier.start)
183
+ } else if (
172
184
  specifier.type === 'ImportSpecifier' &&
173
185
  specifier.importKind !== 'type' &&
174
186
  (specifier.imported.type === 'Identifier'
@@ -239,6 +251,12 @@ export function collect(program: Ast.Program, options: collect.Options) {
239
251
 
240
252
  const namespaces = new Set<string>()
241
253
  const configNamespaces = new Set<string>()
254
+ const variableNamespaces = new Set<string>()
255
+ for (const node of program.body)
256
+ if (node.type === 'ImportDeclaration')
257
+ for (const specifier of node.specifiers)
258
+ if (variableImports.has(specifier.start))
259
+ variableNamespaces.add(specifier.local.name)
242
260
 
243
261
  for (const node of program.body)
244
262
  if (node.type === 'ImportDeclaration')
@@ -301,15 +319,16 @@ export function collect(program: Ast.Program, options: collect.Options) {
301
319
  !config.call.initialization &&
302
320
  !config.call.root &&
303
321
  path.length === 1 &&
304
- ['themes', 'script', 'appearance'].includes(path[0]!)
322
+ ['themes', 'vars', 'script', 'appearance'].includes(path[0]!)
305
323
  ) {
306
324
  const key = path[0]!
325
+ if (config.call.variableConfig && key === 'themes') return undefined
307
326
 
308
327
  if (key === 'script') scripts.add(config.call.name)
309
328
  if (key === 'appearance') appearances.add(config.call.name)
310
329
 
311
330
  if (key === 'themes' && !config.call.options?.themes) return undefined
312
- if (key === 'themes') selections.add(config.call.name)
331
+ if (key === 'themes' || key === 'vars') selections.add(config.call.name)
313
332
 
314
333
  if (key === 'script' && !config.call.script)
315
334
  fail(
@@ -327,7 +346,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
327
346
  Object.entries(config.members ?? {}).flatMap(([name, member]) => {
328
347
  const parts = JSON.parse(name) as string[]
329
348
 
330
- return key === 'themes' && parts[0] === 'themes'
349
+ return (key === 'themes' || key === 'vars') && parts[0] === 'themes'
331
350
  ? [[JSON.stringify(parts.slice(1)), member]]
332
351
  : []
333
352
  }),
@@ -361,9 +380,51 @@ export function collect(program: Ast.Program, options: collect.Options) {
361
380
  return { selection: true }
362
381
  }
363
382
 
364
- function data(node: Ast.Node): unknown {
383
+ type Derived = { name: string; vars: Vars.Definition }
384
+
385
+ function readReference(
386
+ node: Ast.Node,
387
+ derived?: Derived,
388
+ ): Token.Reference | undefined {
389
+ if (node.type !== 'MemberExpression') return undefined
390
+ const path: string[] = []
391
+ let root: Ast.Node = node
392
+ while (root.type === 'MemberExpression' && !root.optional) {
393
+ const key =
394
+ !root.computed && root.property.type === 'Identifier'
395
+ ? root.property.name
396
+ : root.property.type === 'Literal'
397
+ ? String(root.property.value)
398
+ : undefined
399
+ if (key === undefined) return undefined
400
+ path.unshift(key)
401
+ root = root.object
402
+ }
403
+ if (root.type !== 'Identifier') return undefined
404
+ const local = derived?.name === root.name
405
+ const call = names.get(root.name)
406
+ if (!local && !call) return undefined
407
+ let value: unknown = local ? derived.vars : themes[call!.name]?.tokens
408
+ if (!local && !call!.variableSet && !call!.directVariables) {
409
+ if (!['vars', 'tokens'].includes(path.shift()!)) return undefined
410
+ }
411
+ for (const key of path)
412
+ value =
413
+ value && typeof value === 'object'
414
+ ? Object.getOwnPropertyDescriptor(value, key)?.value
415
+ : undefined
416
+ return Token.is(value) ? value : undefined
417
+ }
418
+
419
+ function data(node: Ast.Node, derived?: Derived): unknown {
420
+ const reference = readReference(node, derived)
421
+ if (reference) {
422
+ factoryReferences.add(node.start)
423
+ return reference
424
+ }
425
+
365
426
  if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')
366
- return data(node.expression)
427
+ return data(node.expression, derived)
367
428
 
368
429
  if (
369
430
  node.type === 'Literal' &&
@@ -384,7 +445,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
384
445
  if (!element || element.type === 'SpreadElement')
385
446
  return fail('Theme arrays require dense literal elements.', node)
386
447
 
387
- return data(element)
448
+ return data(element, derived)
388
449
  })
389
450
 
390
451
  if (node.type !== 'ObjectExpression')
@@ -426,7 +487,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
426
487
  if (key === undefined || Object.hasOwn(result, key))
427
488
  return fail('Theme data requires unique literal keys.', property)
428
489
 
429
- result[key] = data(property.value)
490
+ result[key] = data(property.value, derived)
430
491
  }
431
492
 
432
493
  return result
@@ -440,7 +501,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
440
501
  if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression')
441
502
  return type(node.expression)
442
503
 
443
- if (node.type !== 'ObjectExpression') return JSON.stringify(data(node))
504
+ if (node.type !== 'ObjectExpression') return Configurations.type(data(node))
444
505
 
445
506
  return `{${node.properties
446
507
  .map((property) => {
@@ -544,8 +605,16 @@ export function collect(program: Ast.Program, options: collect.Options) {
544
605
  !configNamespaces.has(expression.callee.object.name) &&
545
606
  member.property.name === 'define'
546
607
  ) {
547
- const id = Identifiers.explicit(expression)
548
- if (id !== undefined) name = Identity.requireId(id, 'Theme.define')
608
+ const argument = expression.arguments[1]
609
+ const callback = argument && Expression.unwrap(argument)
610
+ const id = Identifiers.explicit(
611
+ expression,
612
+ callback?.type === 'ArrowFunctionExpression' ||
613
+ callback?.type === 'FunctionExpression'
614
+ ? 2
615
+ : 1,
616
+ )
617
+ if (id !== undefined) name = Identity.requireId(id, 'Vars.define')
549
618
  }
550
619
 
551
620
  if (configNamespaces.has(expression.callee.object.name)) {
@@ -611,7 +680,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
611
680
  !link.call.root &&
612
681
  (key === 'script' ||
613
682
  key === 'appearance' ||
614
- (key === 'themes' && link.call.options?.themes))
683
+ (key === 'themes' && link.call.options?.themes) ||
684
+ (key === 'vars' && link.call.variableConfig))
615
685
  ) {
616
686
  if (
617
687
  (key === 'script' && !link.call.script) ||
@@ -626,14 +696,16 @@ export function collect(program: Ast.Program, options: collect.Options) {
626
696
 
627
697
  if (key === 'script') scripts.add(link.call.name)
628
698
  if (key === 'appearance') appearances.add(link.call.name)
629
- if (key === 'themes') selections.add(link.call.name)
699
+ if (key === 'themes' || key === 'vars')
700
+ selections.add(link.call.name)
630
701
 
631
702
  const members = Object.fromEntries(
632
703
  Object.entries(link.members ?? {}).flatMap(
633
704
  ([pathKey, member]) => {
634
705
  const path = JSON.parse(pathKey) as string[]
635
706
 
636
- return key === 'themes' && path[0] === 'themes'
707
+ return (key === 'themes' || key === 'vars') &&
708
+ (path[0] === 'themes' || path[0] === 'vars')
637
709
  ? [[JSON.stringify(path.slice(1)), member]]
638
710
  : []
639
711
  },
@@ -665,6 +737,9 @@ export function collect(program: Ast.Program, options: collect.Options) {
665
737
  continue
666
738
  }
667
739
 
740
+ if (link.call.variableConfig && ['theme', 'themes'].includes(key))
741
+ fail('Use vars for references and scope selection.', id)
742
+
668
743
  const member = link.members?.[JSON.stringify([key])]
669
744
 
670
745
  if (!member)
@@ -680,7 +755,11 @@ export function collect(program: Ast.Program, options: collect.Options) {
680
755
  exports[id.name] = member
681
756
  }
682
757
  } catch (error) {
683
- if (!(error instanceof Config.InvalidError)) throw error
758
+ if (
759
+ !(error instanceof Config.InvalidError) &&
760
+ !(error instanceof Vars.InvalidError)
761
+ )
762
+ throw error
684
763
 
685
764
  fail(error.message, expression)
686
765
  }
@@ -696,19 +775,79 @@ export function collect(program: Ast.Program, options: collect.Options) {
696
775
  if (member.property.name === 'define') {
697
776
  if (
698
777
  expression.arguments.length < 1 ||
699
- expression.arguments.length > 2
778
+ expression.arguments.length > 3
700
779
  )
701
- fail('Theme.define requires one literal token object.', expression)
780
+ fail(
781
+ 'Vars.define requires one literal variable object.',
782
+ expression,
783
+ )
702
784
 
703
- const input = data(expression.arguments[0]!)
785
+ let input = data(expression.arguments[0]!)
704
786
 
705
787
  tokenType = type(expression.arguments[0]!)
706
788
 
707
- const original = Theme.define(input as Theme.Tokens)
789
+ const variableSet = variableNamespaces.has(
790
+ expression.callee.object.name,
791
+ )
792
+ const argument = expression.arguments[1]
793
+ const callback = argument && Expression.unwrap(argument)
794
+ if (
795
+ variableSet &&
796
+ (callback?.type === 'ArrowFunctionExpression' ||
797
+ callback?.type === 'FunctionExpression')
798
+ ) {
799
+ const parameter = callback.params[0]
800
+ const body = (() => {
801
+ if (callback.body?.type !== 'BlockStatement') return callback.body
802
+ if (
803
+ callback.body.body.length === 1 &&
804
+ callback.body.body[0]?.type === 'ReturnStatement'
805
+ )
806
+ return callback.body.body[0].argument
807
+ return undefined
808
+ })()
809
+ if (
810
+ callback.async ||
811
+ callback.generator ||
812
+ callback.params.length !== 1 ||
813
+ parameter?.type !== 'Identifier' ||
814
+ !body
815
+ )
816
+ fail(
817
+ 'Derived variables require one named parameter and a literal return value.',
818
+ callback,
819
+ )
820
+
821
+ const base = VariableSets.build(
822
+ input,
823
+ Object.freeze({ variableSet: true, [Token.identity]: name }),
824
+ )
825
+ input = VariableSets.merge(
826
+ input,
827
+ data(body, { name: parameter.name, vars: base }),
828
+ )
829
+ tokenType = Configurations.type(input)
830
+ } else if (expression.arguments.length > 2)
831
+ fail(
832
+ 'Vars.define accepts a third argument only with a derived callback.',
833
+ expression,
834
+ )
835
+
836
+ const original = variableSet
837
+ ? VariableSets.theme(
838
+ VariableSets.build(
839
+ input,
840
+ Object.freeze({ variableSet: true, [Token.identity]: name }),
841
+ ),
842
+ )
843
+ : Theme.define(input as Theme.Tokens)
708
844
 
709
845
  definition = Token.bind(
710
846
  original,
711
- Object.freeze({ [Token.identity]: name }),
847
+ Object.freeze({
848
+ ...original[Token.definition].contract,
849
+ [Token.identity]: name,
850
+ }),
712
851
  )
713
852
  } else {
714
853
  const base = expression.arguments[0]
@@ -716,15 +855,22 @@ export function collect(program: Ast.Program, options: collect.Options) {
716
855
 
717
856
  if (expression.arguments.length !== 2 || !parent)
718
857
  fail(
719
- 'Theme.extend requires a preceding local theme and literal overrides.',
858
+ 'Vars.extend requires a preceding variable set and literal overrides.',
720
859
  expression,
721
860
  )
722
861
 
723
862
  factoryReferences.add(base!.start)
724
- definition = Theme.extend(
725
- themes[parent.name]!,
726
- data(expression.arguments[1]!) as Theme.Overrides<Theme.Tokens>,
727
- )
863
+ definition = variableNamespaces.has(expression.callee.object.name)
864
+ ? VariableSets.theme(
865
+ Vars.extend(
866
+ VariableSets.from(themes[parent.name]![Token.definition]),
867
+ data(expression.arguments[1]!) as Vars.Overrides<Vars.Values>,
868
+ ),
869
+ )
870
+ : Theme.extend(
871
+ themes[parent.name]!,
872
+ data(expression.arguments[1]!) as Theme.Overrides<Theme.Tokens>,
873
+ )
728
874
  if (
729
875
  definition[Token.definition].contract[Token.identity]?.startsWith(
730
876
  'id-',
@@ -736,12 +882,23 @@ export function collect(program: Ast.Program, options: collect.Options) {
736
882
  }
737
883
  } catch (error) {
738
884
  if (error instanceof InvalidError) throw error
739
- if (!(error instanceof Theme.InvalidError)) throw error
885
+ if (
886
+ !(error instanceof Theme.InvalidError) &&
887
+ !(error instanceof Vars.InvalidError)
888
+ )
889
+ throw error
740
890
 
741
891
  fail(error.message, expression)
742
892
  }
743
893
 
744
894
  const call = Object.freeze({
895
+ ...(variableNamespaces.has(expression.callee.object.name)
896
+ ? {
897
+ variableSet: true,
898
+ directVariables: true,
899
+ type: `import('zyzz').Vars.Definition<${tokenType}>`,
900
+ }
901
+ : {}),
745
902
  ...(output ? { output } : {}),
746
903
  end: expression.end,
747
904
  name,
@@ -850,7 +1007,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
850
1007
  !link.call.root &&
851
1008
  (key === 'script' ||
852
1009
  key === 'appearance' ||
853
- (key === 'themes' && link.call.options?.themes))
1010
+ (key === 'themes' && link.call.options?.themes) ||
1011
+ (key === 'vars' && link.call.variableConfig))
854
1012
  ) {
855
1013
  if (
856
1014
  (key === 'script' && !link.call.script) ||
@@ -865,13 +1023,14 @@ export function collect(program: Ast.Program, options: collect.Options) {
865
1023
 
866
1024
  if (key === 'script') scripts.add(link.call.name)
867
1025
  if (key === 'appearance') appearances.add(link.call.name)
868
- if (key === 'themes') selections.add(link.call.name)
1026
+ if (key === 'themes' || key === 'vars') selections.add(link.call.name)
869
1027
 
870
1028
  const members = Object.fromEntries(
871
1029
  Object.entries(link.members ?? {}).flatMap(([pathKey, member]) => {
872
1030
  const path = JSON.parse(pathKey) as string[]
873
1031
 
874
- return key === 'themes' && path[0] === 'themes'
1032
+ return (key === 'themes' || key === 'vars') &&
1033
+ (path[0] === 'themes' || path[0] === 'vars')
875
1034
  ? [[JSON.stringify(path.slice(1)), member]]
876
1035
  : []
877
1036
  }),
@@ -902,6 +1061,9 @@ export function collect(program: Ast.Program, options: collect.Options) {
902
1061
  continue
903
1062
  }
904
1063
 
1064
+ if (link.call.variableConfig && ['theme', 'themes'].includes(key))
1065
+ fail('Use vars for references and scope selection.', id)
1066
+
905
1067
  const member = link.members?.[JSON.stringify([key])]
906
1068
 
907
1069
  if (!member)
@@ -1213,7 +1375,13 @@ export function collect(program: Ast.Program, options: collect.Options) {
1213
1375
  node,
1214
1376
  )
1215
1377
 
1216
- if (config.call.selection) selections.add(config.call.name)
1378
+ if (config.call.selection) {
1379
+ selections.add(config.call.name)
1380
+ scopeApplications.set(parent.start, {
1381
+ end: parent.end,
1382
+ output: config.call.options?.output === 'html' ? 'html' : undefined,
1383
+ })
1384
+ }
1217
1385
  if (config.call.initialization) scripts.add(config.call.name)
1218
1386
 
1219
1387
  return true
@@ -1281,15 +1449,20 @@ export function collect(program: Ast.Program, options: collect.Options) {
1281
1449
  path.length === 1 &&
1282
1450
  !config.call.selection &&
1283
1451
  !config.call.initialization &&
1284
- ['script', 'themes'].includes(path[0]!) &&
1452
+ ['script', 'themes', 'vars'].includes(path[0]!) &&
1285
1453
  ancestors[index - 1]?.type === 'CallExpression' &&
1286
1454
  (ancestors[index - 1] as Ast.CallExpression).callee === target &&
1287
1455
  !(ancestors[index - 1] as Ast.CallExpression).optional
1288
1456
  ) {
1457
+ if (path[0] === 'vars' && !config.call.variableConfig)
1458
+ fail('This configuration has no vars.', target)
1289
1459
  if (path[0] === 'themes' && !config.call.options?.themes)
1290
1460
  fail('Theme selection requires a named catalog.', target)
1291
1461
 
1292
- if (path[0] === 'themes' && config.call.catalogOnly)
1462
+ if (
1463
+ (path[0] === 'themes' || path[0] === 'vars') &&
1464
+ config.call.catalogOnly
1465
+ )
1293
1466
  fail(
1294
1467
  'This legacy catalog is not callable; rebuild its library.',
1295
1468
  target,
@@ -1305,7 +1478,15 @@ export function collect(program: Ast.Program, options: collect.Options) {
1305
1478
  scripts.add(config.call.name)
1306
1479
  }
1307
1480
 
1308
- if (path[0] === 'themes') selections.add(config.call.name)
1481
+ if (path[0] === 'themes' || path[0] === 'vars') {
1482
+ selections.add(config.call.name)
1483
+ const application = ancestors[index - 1]!
1484
+ scopeApplications.set(application.start, {
1485
+ end: application.end,
1486
+ output:
1487
+ config.call.options?.output === 'html' ? 'html' : undefined,
1488
+ })
1489
+ }
1309
1490
 
1310
1491
  return true
1311
1492
  }
@@ -1333,7 +1514,15 @@ export function collect(program: Ast.Program, options: collect.Options) {
1333
1514
  return true
1334
1515
  }
1335
1516
 
1336
- const linked = config.members?.[JSON.stringify(path)]
1517
+ if (
1518
+ config.call.variableConfig &&
1519
+ ['theme', 'themes'].includes(path[0]!)
1520
+ )
1521
+ fail('Use vars for references and scope selection.', target)
1522
+ const linked =
1523
+ config.call.variableConfig && config.call.selection
1524
+ ? undefined
1525
+ : config.members?.[JSON.stringify(path)]
1337
1526
  if (linked)
1338
1527
  return themeReference(
1339
1528
  target,
@@ -1343,6 +1532,12 @@ export function collect(program: Ast.Program, options: collect.Options) {
1343
1532
  )
1344
1533
  }
1345
1534
 
1535
+ if (config.call.selection && config.call.variableConfig && path.length)
1536
+ return themeReference(node, parent, ancestors, {
1537
+ ...config.call,
1538
+ directVariables: true,
1539
+ })
1540
+
1346
1541
  // A compiled selector is an ordinary runtime function, so passing or storing it is safe.
1347
1542
  if (config.call.selection && !config.call.catalogOnly && !path.length) {
1348
1543
  selections.add(config.call.name)
@@ -1373,7 +1568,11 @@ export function collect(program: Ast.Program, options: collect.Options) {
1373
1568
  theme: Call,
1374
1569
  ): boolean {
1375
1570
  const grandparent = ancestors.at(-3)
1376
- if (factoryReferences.has(node.start)) return true
1571
+ if (
1572
+ factoryReferences.has(node.start) ||
1573
+ ancestors.some((ancestor) => factoryReferences.has(ancestor.start))
1574
+ )
1575
+ return true
1377
1576
  if (exportReferences.has(node.start)) return true
1378
1577
  if (aliasReferences.has(node.start)) return true
1379
1578
 
@@ -1390,9 +1589,10 @@ export function collect(program: Ast.Program, options: collect.Options) {
1390
1589
  if (
1391
1590
  parent.type === 'MemberExpression' &&
1392
1591
  parent.object === node &&
1393
- ((parent.property.type === 'Identifier' &&
1394
- !parent.computed &&
1395
- ['tokens', 'vars'].includes(parent.property.name)) ||
1592
+ (theme.directVariables ||
1593
+ (parent.property.type === 'Identifier' &&
1594
+ !parent.computed &&
1595
+ ['tokens', 'vars'].includes(parent.property.name)) ||
1396
1596
  (parent.property.type === 'Literal' &&
1397
1597
  parent.computed &&
1398
1598
  ['tokens', 'vars'].includes(String(parent.property.value))))
@@ -1401,16 +1601,18 @@ export function collect(program: Ast.Program, options: collect.Options) {
1401
1601
  fail('Token references cannot use optional access.', parent)
1402
1602
 
1403
1603
  const variable =
1404
- parent.property.type === 'Identifier'
1604
+ theme.directVariables ||
1605
+ (parent.property.type === 'Identifier'
1405
1606
  ? parent.property.name === 'vars'
1406
1607
  : parent.property.type === 'Literal' &&
1407
- parent.property.value === 'vars'
1608
+ parent.property.value === 'vars')
1408
1609
 
1409
- let value: unknown = variable
1410
- ? themes[theme.name]!.vars
1411
- : themes[theme.name]!.tokens
1412
- let target: Ast.Node = parent
1413
- let index = ancestors.length - 3
1610
+ let value: unknown =
1611
+ variable && !theme.directVariables
1612
+ ? themes[theme.name]!.vars
1613
+ : themes[theme.name]!.tokens
1614
+ let target: Ast.Node = theme.directVariables ? node : parent
1615
+ let index = ancestors.length - (theme.directVariables ? 2 : 3)
1414
1616
 
1415
1617
  for (; index >= 0; index--) {
1416
1618
  const ancestor = ancestors[index]!
@@ -1611,7 +1813,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1611
1813
  parent.property.type !== 'Identifier'
1612
1814
  )
1613
1815
  fail(
1614
- 'Use local theme.style calls, theme.className reads, or Theme.extend; other theme references require source linking.',
1816
+ 'Use Config.create or Vars.extend with variable definitions; other references require source linking.',
1615
1817
  node,
1616
1818
  )
1617
1819
 
@@ -1714,8 +1916,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
1714
1916
  )
1715
1917
  if (!alias?.options?.themes) return undefined
1716
1918
  const context = {
1717
- defaultTheme: String(alias.options.defaultTheme),
1718
- themes: Object.fromEntries(
1919
+ defaultVars: String(alias.options.defaultTheme),
1920
+ vars: Object.fromEntries(
1719
1921
  Object.entries(
1720
1922
  alias.options.themes as Record<
1721
1923
  string,
@@ -1723,25 +1925,31 @@ export function collect(program: Ast.Program, options: collect.Options) {
1723
1925
  >,
1724
1926
  ).map(([name, values]) => [
1725
1927
  name,
1726
- Token.bind(Theme.define(values), contract),
1928
+ Token.bind(
1929
+ contract.variableSet
1930
+ ? VariableSets.theme(Vars.define(values as Vars.Values))
1931
+ : Theme.define(values),
1932
+ contract,
1933
+ ),
1727
1934
  ]),
1728
1935
  ),
1729
1936
  }
1730
- Object.freeze(context.themes)
1937
+ Object.freeze(context.vars)
1731
1938
  nativeContexts.set(contract, Object.freeze(context))
1732
1939
  return context
1733
1940
  }
1734
1941
  const entries = Object.entries(config.members ?? {}).flatMap(
1735
1942
  ([key, value]) => {
1736
1943
  const path = JSON.parse(key) as string[]
1737
- return path[0] === 'themes' && path.length === 2
1944
+ return (path[0] === 'themes' || path[0] === 'vars') &&
1945
+ path.length === 2
1738
1946
  ? [[path[1]!, value.definition] as const]
1739
1947
  : []
1740
1948
  },
1741
1949
  )
1742
1950
  const context = Object.freeze({
1743
- defaultTheme: String(config.call.options.defaultTheme),
1744
- themes: Object.freeze(Object.fromEntries(entries)),
1951
+ defaultVars: String(config.call.options.defaultTheme),
1952
+ vars: Object.freeze(Object.fromEntries(entries)),
1745
1953
  })
1746
1954
  nativeContexts.set(contract, context)
1747
1955
  return context
@@ -1752,6 +1960,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1752
1960
  exports: Object.freeze(exports),
1753
1961
  reference,
1754
1962
  references,
1963
+ scopeApplications,
1755
1964
  scripts,
1756
1965
  selections,
1757
1966
  staticTokens,
@@ -2,7 +2,7 @@
2
2
  import type * as Ast from '@oxc-project/types'
3
3
  import * as Walker from 'oxc-walker'
4
4
  import type * as Binding from '../../internal/Binding.js'
5
- import * as Theme from '../../Theme.js'
5
+ import * as Theme from '../../internal/Theme.js'
6
6
  import type * as Css from '../../web/Css.js'
7
7
  import * as Identity from '../../internal/Identity.js'
8
8
  import * as Expression from './Expression.js'
@@ -481,7 +481,7 @@ export function collect(
481
481
  ) ?? resolve(node)
482
482
  if (!link) return false
483
483
  if (link.call.start >= 0 && node.start < link.call.end)
484
- throw new InvalidError('Variables must be declared before use.', node)
484
+ throw new InvalidError('Vars must be declared before use.', node)
485
485
  return true
486
486
  }
487
487