zyzz 0.0.3 → 0.0.6

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 (273) 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/babel/index.d.ts +2 -0
  15. package/dist/babel/index.d.ts.map +1 -1
  16. package/dist/babel/index.js +10 -1
  17. package/dist/babel/index.js.map +1 -1
  18. package/dist/compiler/Graph.d.ts +2 -0
  19. package/dist/compiler/Graph.d.ts.map +1 -1
  20. package/dist/compiler/Graph.js +30 -9
  21. package/dist/compiler/Graph.js.map +1 -1
  22. package/dist/compiler/Native.d.ts +5 -5
  23. package/dist/compiler/Native.d.ts.map +1 -1
  24. package/dist/compiler/Native.js +21 -21
  25. package/dist/compiler/Native.js.map +1 -1
  26. package/dist/compiler/Source.d.ts +8 -6
  27. package/dist/compiler/Source.d.ts.map +1 -1
  28. package/dist/compiler/Source.js +52 -16
  29. package/dist/compiler/Source.js.map +1 -1
  30. package/dist/compiler/Transform.d.ts +1 -1
  31. package/dist/compiler/Transform.d.ts.map +1 -1
  32. package/dist/compiler/Transform.js +58 -22
  33. package/dist/compiler/Transform.js.map +1 -1
  34. package/dist/compiler/internal/Compositions.d.ts +5 -0
  35. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  36. package/dist/compiler/internal/Compositions.js +22 -3
  37. package/dist/compiler/internal/Compositions.js.map +1 -1
  38. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  39. package/dist/compiler/internal/Configurations.js +73 -12
  40. package/dist/compiler/internal/Configurations.js.map +1 -1
  41. package/dist/compiler/internal/Contract.d.ts +1 -1
  42. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  43. package/dist/compiler/internal/Contract.js +155 -13
  44. package/dist/compiler/internal/Contract.js.map +1 -1
  45. package/dist/compiler/internal/Contributions.d.ts +1 -1
  46. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  47. package/dist/compiler/internal/Contributions.js +6 -4
  48. package/dist/compiler/internal/Contributions.js.map +1 -1
  49. package/dist/compiler/internal/Dynamic.js +1 -1
  50. package/dist/compiler/internal/Dynamic.js.map +1 -1
  51. package/dist/compiler/internal/Expression.d.ts +3 -0
  52. package/dist/compiler/internal/Expression.d.ts.map +1 -1
  53. package/dist/compiler/internal/Expression.js +21 -0
  54. package/dist/compiler/internal/Expression.js.map +1 -1
  55. package/dist/compiler/internal/Identifiers.d.ts +1 -1
  56. package/dist/compiler/internal/Identifiers.d.ts.map +1 -1
  57. package/dist/compiler/internal/Identifiers.js +2 -2
  58. package/dist/compiler/internal/Identifiers.js.map +1 -1
  59. package/dist/compiler/internal/NativeBindings.d.ts +1 -1
  60. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  61. package/dist/compiler/internal/NativeBindings.js +3 -3
  62. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  63. package/dist/compiler/internal/PackedStyles.d.ts +1 -1
  64. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  65. package/dist/compiler/internal/PackedStyles.js +15 -25
  66. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  67. package/dist/compiler/internal/Recipes.js +1 -1
  68. package/dist/compiler/internal/Recipes.js.map +1 -1
  69. package/dist/compiler/internal/Selectors.js +1 -1
  70. package/dist/compiler/internal/Selectors.js.map +1 -1
  71. package/dist/compiler/internal/Static.d.ts.map +1 -1
  72. package/dist/compiler/internal/Static.js +2 -0
  73. package/dist/compiler/internal/Static.js.map +1 -1
  74. package/dist/compiler/internal/Themes.d.ts +12 -3
  75. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  76. package/dist/compiler/internal/Themes.js +192 -52
  77. package/dist/compiler/internal/Themes.js.map +1 -1
  78. package/dist/compiler/internal/{Variables.d.ts → Vars.d.ts} +1 -1
  79. package/dist/compiler/internal/Vars.d.ts.map +1 -0
  80. package/dist/compiler/internal/{Variables.js → Vars.js} +3 -3
  81. package/dist/compiler/internal/Vars.js.map +1 -0
  82. package/dist/default.d.ts +147 -2562
  83. package/dist/default.d.ts.map +1 -1
  84. package/dist/default.js +85 -80
  85. package/dist/default.js.map +2 -2
  86. package/dist/default.js.zyzz.json +1 -1
  87. package/dist/esbuild/index.d.ts +3 -0
  88. package/dist/esbuild/index.d.ts.map +1 -0
  89. package/dist/esbuild/index.js +5 -0
  90. package/dist/esbuild/index.js.map +1 -0
  91. package/dist/index.d.ts +4 -4
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +4 -4
  94. package/dist/index.js.map +1 -1
  95. package/dist/internal/Authoring.d.ts +1 -1
  96. package/dist/internal/Authoring.d.ts.map +1 -1
  97. package/dist/internal/Authoring.js +4 -4
  98. package/dist/internal/Authoring.js.map +1 -1
  99. package/dist/internal/Binding.d.ts +1 -1
  100. package/dist/internal/Binding.d.ts.map +1 -1
  101. package/dist/internal/Completion.d.ts +19 -0
  102. package/dist/internal/Completion.d.ts.map +1 -0
  103. package/dist/internal/Completion.js +2 -0
  104. package/dist/internal/Completion.js.map +1 -0
  105. package/dist/internal/Condition.d.ts +3 -1
  106. package/dist/internal/Condition.d.ts.map +1 -1
  107. package/dist/internal/Condition.js.map +1 -1
  108. package/dist/internal/Configuration.d.ts +248 -0
  109. package/dist/internal/Configuration.d.ts.map +1 -0
  110. package/dist/internal/Configuration.js +343 -0
  111. package/dist/internal/Configuration.js.map +1 -0
  112. package/dist/{Theme.d.ts → internal/Theme.d.ts} +15 -13
  113. package/dist/internal/Theme.d.ts.map +1 -0
  114. package/dist/{Theme.js → internal/Theme.js} +39 -12
  115. package/dist/internal/Theme.js.map +1 -0
  116. package/dist/internal/Token.d.ts +46 -16
  117. package/dist/internal/Token.d.ts.map +1 -1
  118. package/dist/internal/Token.js +89 -5
  119. package/dist/internal/Token.js.map +1 -1
  120. package/dist/internal/Typography.d.ts +13 -5
  121. package/dist/internal/Typography.d.ts.map +1 -1
  122. package/dist/internal/Typography.js +42 -6
  123. package/dist/internal/Typography.js.map +1 -1
  124. package/dist/internal/Value.d.ts +6 -1
  125. package/dist/internal/Value.d.ts.map +1 -1
  126. package/dist/internal/Value.js.map +1 -1
  127. package/dist/internal/VariableSets.d.ts +20 -0
  128. package/dist/internal/VariableSets.d.ts.map +1 -0
  129. package/dist/internal/VariableSets.js +317 -0
  130. package/dist/internal/VariableSets.js.map +1 -0
  131. package/dist/next/index.d.ts +8 -3
  132. package/dist/next/index.d.ts.map +1 -1
  133. package/dist/next/index.js +21 -10
  134. package/dist/next/index.js.map +1 -1
  135. package/dist/next/loader.d.ts +1 -0
  136. package/dist/next/loader.d.ts.map +1 -1
  137. package/dist/next/loader.js +25 -6
  138. package/dist/next/loader.js.map +1 -1
  139. package/dist/node/Host.d.ts.map +1 -1
  140. package/dist/node/Host.js +1 -1
  141. package/dist/node/Host.js.map +1 -1
  142. package/dist/node/Reset.d.ts +3 -0
  143. package/dist/node/Reset.d.ts.map +1 -0
  144. package/dist/node/Reset.js +7 -0
  145. package/dist/node/Reset.js.map +1 -0
  146. package/dist/react-native/Host.d.ts +2 -2
  147. package/dist/react-native/Host.d.ts.map +1 -1
  148. package/dist/react-native/Host.js +4 -4
  149. package/dist/react-native/Host.js.map +1 -1
  150. package/dist/react-native/StyleSheet.d.ts +9 -8
  151. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  152. package/dist/react-native/StyleSheet.js +29 -23
  153. package/dist/react-native/StyleSheet.js.map +1 -1
  154. package/dist/react-native/Variants.d.ts +3 -3
  155. package/dist/react-native/Variants.d.ts.map +1 -1
  156. package/dist/react-native/Variants.js +2 -2
  157. package/dist/react-native/react.d.ts +1 -1
  158. package/dist/react-native/react.d.ts.map +1 -1
  159. package/dist/react-native/react.js +5 -5
  160. package/dist/react-native/react.js.map +1 -1
  161. package/dist/rollup/index.d.ts +3 -0
  162. package/dist/rollup/index.d.ts.map +1 -0
  163. package/dist/rollup/index.js +5 -0
  164. package/dist/rollup/index.js.map +1 -0
  165. package/dist/runtime/Appearance.d.ts +7 -7
  166. package/dist/runtime/Appearance.d.ts.map +1 -1
  167. package/dist/runtime/Appearance.js +15 -15
  168. package/dist/runtime/Appearance.js.map +1 -1
  169. package/dist/runtime/Dynamic.d.ts.map +1 -1
  170. package/dist/runtime/Dynamic.js +4 -1
  171. package/dist/runtime/Dynamic.js.map +1 -1
  172. package/dist/runtime/NativeContext.d.ts +4 -4
  173. package/dist/runtime/NativeContext.d.ts.map +1 -1
  174. package/dist/runtime/NativeContext.js +5 -5
  175. package/dist/runtime/NativeContext.js.map +1 -1
  176. package/dist/runtime/NativeDynamic.d.ts +1 -1
  177. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  178. package/dist/runtime/NativeDynamic.js.map +1 -1
  179. package/dist/runtime/Props.d.ts +1 -1
  180. package/dist/runtime/Props.js +3 -3
  181. package/dist/runtime/Props.js.map +1 -1
  182. package/dist/runtime/Selection.d.ts +14 -10
  183. package/dist/runtime/Selection.d.ts.map +1 -1
  184. package/dist/runtime/Selection.js +19 -18
  185. package/dist/runtime/Selection.js.map +1 -1
  186. package/dist/styleFunction.d.ts +14 -14
  187. package/dist/styleFunction.d.ts.map +1 -1
  188. package/dist/styleFunction.js.map +1 -1
  189. package/dist/unplugin/index.d.ts +22 -0
  190. package/dist/unplugin/index.d.ts.map +1 -0
  191. package/dist/unplugin/index.js +500 -0
  192. package/dist/unplugin/index.js.map +1 -0
  193. package/dist/variants.d.ts +3 -3
  194. package/dist/variants.d.ts.map +1 -1
  195. package/dist/variants.js.map +1 -1
  196. package/dist/vite/index.d.ts +2 -0
  197. package/dist/vite/index.d.ts.map +1 -1
  198. package/dist/vite/index.js +44 -12
  199. package/dist/vite/index.js.map +1 -1
  200. package/dist/web/Css.d.ts +4 -3
  201. package/dist/web/Css.d.ts.map +1 -1
  202. package/dist/web/Css.js +4 -4
  203. package/dist/web/Css.js.map +1 -1
  204. package/dist/web/internal/Themes.d.ts +3 -2
  205. package/dist/web/internal/Themes.d.ts.map +1 -1
  206. package/dist/web/internal/Themes.js +42 -6
  207. package/dist/web/internal/Themes.js.map +1 -1
  208. package/dist/webpack/index.d.ts +3 -0
  209. package/dist/webpack/index.d.ts.map +1 -0
  210. package/dist/webpack/index.js +5 -0
  211. package/dist/webpack/index.js.map +1 -0
  212. package/package.json +23 -2
  213. package/src/Config.ts +43 -685
  214. package/src/Style.ts +97 -34
  215. package/src/Vars.ts +299 -0
  216. package/src/babel/index.ts +23 -1
  217. package/src/compiler/Graph.ts +36 -9
  218. package/src/compiler/Native.ts +29 -32
  219. package/src/compiler/Source.ts +64 -22
  220. package/src/compiler/Transform.ts +73 -23
  221. package/src/compiler/internal/Compositions.ts +36 -3
  222. package/src/compiler/internal/Configurations.ts +83 -11
  223. package/src/compiler/internal/Contract.ts +207 -15
  224. package/src/compiler/internal/Contributions.ts +6 -3
  225. package/src/compiler/internal/Dynamic.ts +1 -1
  226. package/src/compiler/internal/Expression.ts +26 -0
  227. package/src/compiler/internal/Identifiers.ts +2 -1
  228. package/src/compiler/internal/NativeBindings.ts +3 -3
  229. package/src/compiler/internal/PackedStyles.ts +22 -26
  230. package/src/compiler/internal/Recipes.ts +1 -1
  231. package/src/compiler/internal/Selectors.ts +1 -1
  232. package/src/compiler/internal/Static.ts +1 -0
  233. package/src/compiler/internal/Themes.ts +266 -57
  234. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  235. package/src/default.ts +173 -154
  236. package/src/esbuild/index.ts +5 -0
  237. package/src/index.ts +4 -4
  238. package/src/internal/Authoring.ts +5 -7
  239. package/src/internal/Binding.ts +1 -1
  240. package/src/internal/Completion.ts +42 -0
  241. package/src/internal/Condition.ts +31 -1
  242. package/src/internal/Configuration.ts +970 -0
  243. package/src/{Theme.ts → internal/Theme.ts} +75 -20
  244. package/src/internal/Token.ts +258 -85
  245. package/src/internal/Typography.ts +71 -16
  246. package/src/internal/Value.ts +45 -26
  247. package/src/internal/VariableSets.ts +458 -0
  248. package/src/next/index.ts +34 -12
  249. package/src/next/loader.ts +35 -6
  250. package/src/node/Host.ts +1 -2
  251. package/src/node/Reset.ts +7 -0
  252. package/src/react-native/Host.ts +6 -6
  253. package/src/react-native/StyleSheet.ts +49 -42
  254. package/src/react-native/Variants.ts +3 -3
  255. package/src/react-native/react.ts +6 -6
  256. package/src/rollup/index.ts +5 -0
  257. package/src/runtime/Appearance.ts +20 -20
  258. package/src/runtime/Dynamic.ts +4 -1
  259. package/src/runtime/NativeContext.ts +8 -8
  260. package/src/runtime/NativeDynamic.ts +2 -4
  261. package/src/runtime/Props.ts +3 -3
  262. package/src/runtime/Selection.ts +44 -33
  263. package/src/styleFunction.ts +22 -19
  264. package/src/unplugin/index.ts +604 -0
  265. package/src/variants.ts +7 -5
  266. package/src/vite/index.ts +48 -18
  267. package/src/web/Css.ts +10 -7
  268. package/src/web/internal/Themes.ts +52 -9
  269. package/src/webpack/index.ts +5 -0
  270. package/dist/Theme.d.ts.map +0 -1
  271. package/dist/Theme.js.map +0 -1
  272. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  273. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -2,21 +2,69 @@
2
2
  import type * as Literal from './Literal.js'
3
3
  import * as Token from './Token.js'
4
4
 
5
- /** Resolves the scalar fields owned by one dotted set path. */
6
- export function fields(
5
+ /** Responsive query blocks accepted within a typography set. */
6
+ export type Condition = `@media ${string}` | `@container ${string}`
7
+
8
+ /** Recognizes responsive blocks without accepting selectors or unrelated at-rules. */
9
+ export function condition(key: string): key is Condition {
10
+ return /^@(media|container)\s+\S/.test(key)
11
+ }
12
+
13
+ /** Expands one named set into portable field references and ordered query blocks. */
14
+ export function entries(
7
15
  theme: object | undefined,
8
16
  name: unknown,
9
- ): readonly Property[] {
10
- const data =
11
- theme &&
12
- (Object.getOwnPropertyDescriptor(theme, Token.definition)?.value as
13
- | Token.Metadata
14
- | undefined)
15
- if (!data || typeof name !== 'string') return []
16
-
17
- return properties.filter((property) =>
18
- Object.hasOwn(data.values, `typography.${name}.${property}`),
17
+ explicit: ReadonlySet<string> = new Set(),
18
+ important = false,
19
+ ): readonly (readonly [string, unknown])[] {
20
+ if (
21
+ !theme ||
22
+ typeof name !== 'string' ||
23
+ name.split('.').some((part) => part.startsWith('@'))
19
24
  )
25
+ return []
26
+ let tree: unknown = Object.getOwnPropertyDescriptor(theme, 'tokens')?.value
27
+ for (const part of ['typography', ...name.split('.')]) {
28
+ if (!tree || typeof tree !== 'object') return []
29
+ tree = Object.getOwnPropertyDescriptor(tree, part)?.value
30
+ }
31
+ function expand(value: unknown): readonly (readonly [string, unknown])[] {
32
+ if (!value || typeof value !== 'object') return []
33
+ return Object.entries(value).flatMap(
34
+ ([key, child]): (readonly [string, unknown])[] => {
35
+ if (
36
+ properties.includes(key as Property) &&
37
+ Token.is(child) &&
38
+ !explicit.has(key)
39
+ )
40
+ return [
41
+ [key, important ? Token.compose([child, ' !important']) : child],
42
+ ]
43
+ if (condition(key)) {
44
+ const nested = expand(child)
45
+ return nested.length ? [[key, Object.fromEntries(nested)]] : []
46
+ }
47
+ return []
48
+ },
49
+ )
50
+ }
51
+ return expand(tree)
52
+ }
53
+
54
+ /** Lists expanded fields and queries in source-map traversal order. */
55
+ export function keys(
56
+ theme: object | undefined,
57
+ name: unknown,
58
+ explicit: ReadonlySet<string>,
59
+ ): readonly string[] {
60
+ function collect(values: readonly (readonly [string, unknown])[]): string[] {
61
+ return values.flatMap(([key, value]) =>
62
+ condition(key)
63
+ ? [key, ...collect(Object.entries(value as object))]
64
+ : [key],
65
+ )
66
+ }
67
+ return collect(entries(theme, name, explicit))
20
68
  }
21
69
 
22
70
  /** Names of sets containing at least one typography declaration. */
@@ -27,10 +75,15 @@ export type Names<tokens> = tokens extends { typography: infer tree }
27
75
  type Paths<tree> = string extends keyof tree
28
76
  ? string
29
77
  : {
30
- [key in Exclude<Extract<keyof tree, string | number>, Property>]:
31
- | (Extract<keyof tree[key], Property> extends never ? never : `${key}`)
78
+ [key in Exclude<
79
+ Extract<keyof tree, string | number>,
80
+ Property | Condition
81
+ >]:
82
+ | (Extract<keyof tree[key], Property | Condition> extends never
83
+ ? never
84
+ : `${key}`)
32
85
  | `${key}.${Paths<tree[key]>}`
33
- }[Exclude<Extract<keyof tree, string | number>, Property>]
86
+ }[Exclude<Extract<keyof tree, string | number>, Property | Condition>]
34
87
 
35
88
  /** Scalar properties applied by a typography set. */
36
89
  export const properties = [
@@ -45,7 +98,9 @@ export const properties = [
45
98
  export type Property = (typeof properties)[number]
46
99
 
47
100
  /** A set may supply any of the five typography properties. */
48
- export type Set = Pick<Literal.Properties, Property>
101
+ export type Set = Pick<Literal.Properties, Property> & {
102
+ readonly [key in Condition]?: Set
103
+ }
49
104
 
50
105
  /** Nested names may contain sets and additional named variants. */
51
106
  export type Sets = { readonly [name: string]: Set | Sets }
@@ -22,37 +22,56 @@ export type Atom<value> =
22
22
  */
23
23
  export type Accepted<style, properties> = {
24
24
  [property in keyof style]: property extends keyof properties
25
- ? style[property] extends Binding.Reference
26
- ? Binding.Reference<style[property]['type']> extends Exclude<
27
- properties[property],
28
- undefined
29
- >
30
- ? style[property]
25
+ ? style[property] extends { readonly [Token.scalar]: infer scalar }
26
+ ? property extends keyof Literal.Properties
27
+ ? [scalar] extends [Literal.Properties[property]]
28
+ ? style[property] & Checked<Record<property, scalar>>[property]
29
+ : never
31
30
  : never
32
- : FunctionValue.Is<style[property]> extends true
33
- ? FunctionValue.Accepted<
34
- style[property],
35
- Exclude<properties[property], undefined>,
36
- property
37
- >
38
- : Exclude<style[property], undefined> extends Exclude<
39
- properties[property],
40
- undefined
41
- >
42
- ? Exclude<style[property], undefined>
43
- : property extends keyof Literal.Properties
44
- ? style[property] extends string | readonly (number | string)[]
45
- ? Fold<style[property]> extends Input<
46
- | Lowercase<Extract<Literal.Properties[property], string>>
47
- | Extract<Literal.Properties[property], number>
31
+ : References<style[property], Exclude<properties[property], undefined>> &
32
+ (style[property] extends Binding.Reference
33
+ ? Binding.Reference<style[property]['type']> extends Exclude<
34
+ properties[property],
35
+ undefined
36
+ >
37
+ ? style[property]
38
+ : never
39
+ : FunctionValue.Is<style[property]> extends true
40
+ ? FunctionValue.Accepted<
41
+ style[property],
42
+ Exclude<properties[property], undefined>,
43
+ property
48
44
  >
49
- ? style[property]
50
- : Exclude<properties[property], undefined>
51
- : Exclude<properties[property], undefined>
52
- : Exclude<properties[property], undefined>
45
+ : Exclude<style[property], undefined> extends Exclude<
46
+ properties[property],
47
+ undefined
48
+ >
49
+ ? Exclude<style[property], undefined>
50
+ : property extends keyof Literal.Properties
51
+ ? style[property] extends
52
+ | string
53
+ | readonly (number | string)[]
54
+ ? Fold<style[property]> extends Input<
55
+ | Lowercase<
56
+ Extract<Literal.Properties[property], string>
57
+ >
58
+ | Extract<Literal.Properties[property], number>
59
+ >
60
+ ? style[property]
61
+ : Exclude<properties[property], undefined>
62
+ : Exclude<properties[property], undefined>
63
+ : Exclude<properties[property], undefined>)
53
64
  : never
54
65
  }
55
66
 
67
+ type References<value, allowed> = value extends Token.Reference
68
+ ? value extends Extract<allowed, Token.Reference>
69
+ ? unknown
70
+ : never
71
+ : value extends readonly unknown[]
72
+ ? { [key in keyof value]: References<value[key], allowed> }
73
+ : unknown
74
+
56
75
  type Fold<value> = value extends string
57
76
  ? Lexical.Fold<Canonical<Spaces<Normalized<Lexical.Normalized<value>>>>>
58
77
  : value extends readonly unknown[]
@@ -0,0 +1,458 @@
1
+ /** Normalizes shared variable data into compiler-owned token contracts. @module */
2
+ import * as Query from './Query.js'
3
+ import * as Typography from './Typography.js'
4
+ import * as Condition from './Condition.js'
5
+ import * as Identity from './Identity.js'
6
+ import * as Literal from './Literal.js'
7
+ import * as Token from './Token.js'
8
+ import * as Theme from './Theme.js'
9
+ import * as Vars from '../Vars.js'
10
+
11
+ /** Wraps a variable set for the shared style/compiler contract. */
12
+ export function theme(
13
+ variables: Vars.Definition,
14
+ mappings?: Vars.Mappings | false,
15
+ ): Theme.Definition {
16
+ const original = Theme.define({})
17
+ const metadata = variables[Token.definition]
18
+ if (!metadata?.contract.variableSet)
19
+ throw new Vars.InvalidError([], 'Expected a variable set.')
20
+ const contract = Object.freeze({
21
+ ...metadata.contract,
22
+ ...(mappings !== undefined ? { mappings } : {}),
23
+ })
24
+ const tokens = from({
25
+ ...metadata,
26
+ contract,
27
+ values: rebind(metadata, contract),
28
+ })
29
+ const definition = Object.create(null)
30
+ Object.defineProperties(definition, {
31
+ ...Object.getOwnPropertyDescriptors(original),
32
+ className: {
33
+ get: () => {
34
+ const identity = metadata.contract[Token.identity]
35
+ return identity
36
+ ? `z_theme-${identity}-${Identity.hash(JSON.stringify(metadata.values))}`
37
+ : original.className
38
+ },
39
+ enumerable: true,
40
+ },
41
+ tokens: { value: tokens, enumerable: true },
42
+ vars: { value: tokens, enumerable: true },
43
+ [Token.definition]: { value: tokens[Token.definition] },
44
+ })
45
+ return Object.freeze(definition) as Theme.Definition
46
+ }
47
+
48
+ /** Rebuilds a reference tree from normalized compiler metadata. */
49
+ export function from(metadata: Token.Metadata): Vars.Definition {
50
+ return buildTree(
51
+ metadata.values,
52
+ metadata.contract,
53
+ metadata.queries,
54
+ metadata.paths,
55
+ ) as Vars.Definition
56
+ }
57
+
58
+ /** Validates and freezes a complete set or compatible partial override. */
59
+ export function build(
60
+ input: unknown,
61
+ contract: Token.Contract,
62
+ base?: Token.Metadata['values'],
63
+ baseQueries?: Query.Metadata,
64
+ basePaths?: Token.Metadata['paths'],
65
+ ): Vars.Definition {
66
+ const values: Record<string, Token.Value> = Object.assign(
67
+ Object.create(null),
68
+ base,
69
+ )
70
+ const paths = { ...basePaths }
71
+ const conditions: string[][] = []
72
+ const active = new Set<object>()
73
+ function visit(input: unknown, path: string[]) {
74
+ if (
75
+ typeof input === 'string' ||
76
+ typeof input === 'number' ||
77
+ Token.is(input) ||
78
+ (path.length > 0 &&
79
+ input &&
80
+ typeof input === 'object' &&
81
+ ('default' in input ||
82
+ ('light' in input && 'dark' in input) ||
83
+ (Object.keys(input).length <= 2 &&
84
+ ('light' in input || 'dark' in input))))
85
+ ) {
86
+ if (!path.length)
87
+ throw new Vars.InvalidError(path, 'Expected a variable record.')
88
+ const value = read(input, path, queryData)
89
+ const name = path.join('.')
90
+ if (base && !Object.hasOwn(base, name))
91
+ throw new Vars.InvalidError(
92
+ path,
93
+ 'Extensions cannot add variable paths.',
94
+ )
95
+ if (base && domain(base[name]!) !== domain(value))
96
+ throw new Vars.InvalidError(
97
+ path,
98
+ 'Variable overrides must preserve their domain.',
99
+ )
100
+ if (path.some((part) => Typography.condition(part)))
101
+ paths[name] = Object.freeze([...path])
102
+ values[name] = value
103
+ return
104
+ }
105
+ const entries = record(input, path)
106
+ if (active.has(input as object))
107
+ throw new Vars.InvalidError(path, 'Cyclic variables are not supported.')
108
+ active.add(input as object)
109
+ if (!entries.length && !base)
110
+ throw new Vars.InvalidError(path, 'Variable records cannot be empty.')
111
+ for (const [key, value] of entries) {
112
+ const query = path[0] === 'typography' && Typography.condition(key)
113
+ if (query) conditions.push([...path, key])
114
+ if (!query && (!key || /[.!]/.test(key) || key.startsWith('@')))
115
+ throw new Vars.InvalidError(
116
+ [...path, key],
117
+ 'Expected a nonempty variable key without dots or conditions.',
118
+ )
119
+ visit(value, [...path, key])
120
+ }
121
+ active.delete(input as object)
122
+ }
123
+ const fields = Object.fromEntries(record(input, []))
124
+ const queries = { ...baseQueries }
125
+ for (const key of ['breakpoints', 'containers', 'containerNames'] as const) {
126
+ if (Object.hasOwn(fields, key)) {
127
+ const value = fields[key]
128
+ if (base) {
129
+ if (key === 'containerNames') {
130
+ if (
131
+ JSON.stringify(value) !==
132
+ JSON.stringify(baseQueries?.containerNames ?? [])
133
+ )
134
+ throw new Vars.InvalidError(
135
+ [key],
136
+ 'Extensions cannot change container identities.',
137
+ )
138
+ } else {
139
+ for (const [name] of record(value, [key]))
140
+ if (!Object.hasOwn(baseQueries?.[key] ?? {}, name))
141
+ throw new Vars.InvalidError(
142
+ [key, name],
143
+ 'Extensions cannot add query thresholds.',
144
+ )
145
+ }
146
+ }
147
+ Object.assign(queries, {
148
+ [key]:
149
+ key === 'containerNames'
150
+ ? value
151
+ : { ...baseQueries?.[key], ...value },
152
+ })
153
+ delete fields[key]
154
+ }
155
+ }
156
+ const queryData = Theme.define(queries as Theme.Tokens)[Token.definition]
157
+ .queries
158
+ if (Object.keys(fields).length) visit(fields, [])
159
+ for (const path of conditions) {
160
+ try {
161
+ Condition.normalize(
162
+ Query.resolve(
163
+ path.at(-1)!,
164
+ queryData ?? { breakpoints: {}, containers: {}, containerNames: [] },
165
+ ),
166
+ )
167
+ } catch (error) {
168
+ throw new Vars.InvalidError(path, (error as Error).message)
169
+ }
170
+ }
171
+ return buildTree(
172
+ Object.freeze(values),
173
+ contract,
174
+ queryData,
175
+ Object.keys(paths).length ? Object.freeze(paths) : undefined,
176
+ ) as Vars.Definition
177
+ }
178
+
179
+ function buildTree(
180
+ values: Token.Metadata['values'],
181
+ contract: Token.Contract,
182
+ queries?: Query.Metadata,
183
+ paths?: Token.Metadata['paths'],
184
+ ) {
185
+ values = rebind({ contract, values }, contract)
186
+
187
+ type Tree = { [key: string]: Tree | Token.Reference }
188
+ const tree: Tree = Object.create(null)
189
+ for (const [path, value] of Object.entries(values)) {
190
+ const parts = paths?.[path] ?? path.split('.')
191
+ let target = tree
192
+ for (const key of parts.slice(0, -1))
193
+ target = (target[key] ??= Object.create(null)) as Tree
194
+ target[parts.at(-1)!] = Token.create({
195
+ contract,
196
+ group: domain(value),
197
+ path,
198
+ value,
199
+ })
200
+ }
201
+ function freeze(tree: Tree) {
202
+ for (const value of Object.values(tree)) if (!Token.is(value)) freeze(value)
203
+ Object.freeze(tree)
204
+ }
205
+ Object.defineProperty(tree, Token.definition, {
206
+ value: Object.freeze({
207
+ contract,
208
+ values,
209
+ queries,
210
+ ...(paths ? { paths } : {}),
211
+ }),
212
+ })
213
+ freeze(tree)
214
+ return tree
215
+ }
216
+
217
+ function record(input: unknown, path: string[]) {
218
+ if (
219
+ !input ||
220
+ typeof input !== 'object' ||
221
+ ![null, Object.prototype].includes(Object.getPrototypeOf(input))
222
+ )
223
+ throw new Vars.InvalidError(path, 'Expected a plain variable record.')
224
+ return Reflect.ownKeys(input).map((key) => {
225
+ const descriptor = Object.getOwnPropertyDescriptor(input, key)!
226
+ if (
227
+ typeof key !== 'string' ||
228
+ !descriptor.enumerable ||
229
+ !('value' in descriptor)
230
+ )
231
+ throw new Vars.InvalidError(
232
+ path,
233
+ 'Vars require enumerable data properties.',
234
+ )
235
+ return [key, descriptor.value] as const
236
+ })
237
+ }
238
+
239
+ function read(
240
+ input: unknown,
241
+ path: string[],
242
+ queries: Query.Metadata | undefined,
243
+ active = new Set<object>(),
244
+ ): Token.Value {
245
+ if (Token.is(input)) return input
246
+ if (typeof input === 'number' && Number.isFinite(input)) return input
247
+ if (typeof input === 'string' && input.trim() && !/[;{}]/.test(input))
248
+ return input
249
+ if (active.has(input as object))
250
+ throw new Vars.InvalidError(path, 'Cyclic variables are not supported.')
251
+ const next = new Set(active).add(input as object)
252
+ const entries = record(input, path)
253
+ const fields = Object.fromEntries(entries)
254
+ if (Object.hasOwn(fields, 'default')) {
255
+ for (const [key] of entries)
256
+ if (key !== 'default' && (!/^@media\s+\S/.test(key) || /[;{}]/.test(key)))
257
+ throw new Vars.InvalidError(
258
+ [...path, key],
259
+ 'Expected an @media condition.',
260
+ )
261
+ const result = Object.fromEntries(
262
+ entries.map(([key, value]) => {
263
+ let condition = key
264
+ try {
265
+ if (key !== 'default')
266
+ condition = Condition.normalize(
267
+ Query.resolve(
268
+ key,
269
+ queries ?? {
270
+ breakpoints: {},
271
+ containers: {},
272
+ containerNames: [],
273
+ },
274
+ ),
275
+ )
276
+ } catch (error) {
277
+ throw new Vars.InvalidError([...path, key], (error as Error).message)
278
+ }
279
+ return [condition, read(value, [...path, key], queries, next)]
280
+ }),
281
+ ) as Token.Conditions
282
+ const expected = domain(result.default)
283
+ if (Object.values(result).some((value) => domain(value) !== expected))
284
+ throw new Vars.InvalidError(
285
+ path,
286
+ 'Conditional values must share one domain.',
287
+ )
288
+ return Object.freeze(result)
289
+ }
290
+ if (
291
+ entries.length === 2 &&
292
+ Object.hasOwn(fields, 'light') &&
293
+ Object.hasOwn(fields, 'dark')
294
+ ) {
295
+ const light = read(fields.light, [...path, 'light'], queries, next)
296
+ const dark = read(fields.dark, [...path, 'dark'], queries, next)
297
+ if (domain(light) !== 'color' || domain(dark) !== 'color')
298
+ throw new Vars.InvalidError(path, 'Color schemes require colors.')
299
+ return Object.freeze({ light, dark })
300
+ }
301
+ throw new Vars.InvalidError(
302
+ path,
303
+ 'Expected a scalar, a complete color pair, or media overrides with a default.',
304
+ )
305
+ }
306
+
307
+ /** Infers the shared declaration domain after following defaults and references. */
308
+ export function domain(value: Token.Value): Token.Group {
309
+ if (Token.is(value)) return value.group
310
+ if (typeof value === 'object')
311
+ return 'default' in value ? domain(value.default) : 'color'
312
+ if (
313
+ typeof value === 'string' &&
314
+ (/^(?:#|(?:color|color-mix|contrast-color|light-dark|rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch)\()/i.test(
315
+ value,
316
+ ) ||
317
+ (!/[0-9()#]/.test(value) && Literal.isLiteral('color', value)))
318
+ )
319
+ return 'color'
320
+ if (
321
+ value === 0 ||
322
+ value === '0' ||
323
+ (typeof value === 'string' &&
324
+ new RegExp(
325
+ `^[+-]?(?:[0-9]*\\.[0-9]+|[0-9]+)(?:${Literal.lengthUnits.join('|')})$`,
326
+ 'i',
327
+ ).test(value))
328
+ )
329
+ return 'spacing'
330
+ return typeof value === 'number' ? 'number' : 'string'
331
+ }
332
+
333
+ /** Copies and validates category mappings without retaining mutable caller input. */
334
+ export function mappings(input: unknown): Vars.Mappings | false | undefined {
335
+ if (input === undefined || input === false) return input
336
+ return Object.freeze(
337
+ Object.fromEntries(
338
+ record(input, ['mappings']).map(([category, value]) => {
339
+ if (
340
+ !category ||
341
+ !Array.isArray(value) ||
342
+ value.some(
343
+ (property) =>
344
+ typeof property !== 'string' ||
345
+ !Object.hasOwn(Literal.rules, property),
346
+ )
347
+ )
348
+ throw new Vars.InvalidError(
349
+ ['mappings', category],
350
+ 'Expected an array of CSS property names.',
351
+ )
352
+ if (new Set(value).size !== value.length)
353
+ throw new Vars.InvalidError(
354
+ ['mappings', category],
355
+ 'Property mappings cannot contain duplicates.',
356
+ )
357
+ return [category, Object.freeze([...value])]
358
+ }),
359
+ ),
360
+ )
361
+ }
362
+
363
+ /** Combines disjoint variable leaves without mutating either authored record. */
364
+ export function merge(
365
+ base: unknown,
366
+ derived: unknown,
367
+ path: string[] = [],
368
+ ): Vars.Values {
369
+ const result: Record<string, unknown> = Object.assign(
370
+ Object.create(null),
371
+ Object.fromEntries(record(base, path)),
372
+ )
373
+
374
+ for (const [key, value] of record(derived, path)) {
375
+ const next = [...path, key]
376
+ if (Object.hasOwn(result, key)) {
377
+ for (const entry of [result[key], value])
378
+ if (
379
+ !entry ||
380
+ typeof entry !== 'object' ||
381
+ Array.isArray(entry) ||
382
+ Token.is(entry) ||
383
+ 'default' in entry ||
384
+ ('light' in entry && 'dark' in entry) ||
385
+ (Object.keys(entry).length <= 2 &&
386
+ ('light' in entry || 'dark' in entry))
387
+ )
388
+ throw new Vars.InvalidError(
389
+ next,
390
+ 'Derived variables cannot replace existing paths.',
391
+ )
392
+
393
+ result[key] = merge(result[key], value, next)
394
+ } else result[key] = value
395
+ }
396
+
397
+ return result as Vars.Values
398
+ }
399
+
400
+ /** Updates references within a set while retaining references to independent sets. */
401
+ export function rebind(
402
+ metadata: Token.Metadata,
403
+ contract: Token.Contract,
404
+ ): Token.Metadata['values'] {
405
+ const active = new Set<string>()
406
+ const values: Record<string, Token.Value> = Object.create(null)
407
+
408
+ function resolve(value: Token.Value): Token.Value {
409
+ if (Token.is(value)) {
410
+ if (
411
+ value.contract !== metadata.contract &&
412
+ (metadata.contract[Token.identity] === undefined ||
413
+ value.contract[Token.identity] !== metadata.contract[Token.identity])
414
+ )
415
+ return value
416
+
417
+ return Token.create({
418
+ contract,
419
+ group: value.group,
420
+ path: value.path,
421
+ value: visit(value.path),
422
+ })
423
+ }
424
+ if (typeof value !== 'object') return value
425
+
426
+ return Object.freeze(
427
+ Object.fromEntries(
428
+ Object.entries(value).map(([key, entry]) => [key, resolve(entry)]),
429
+ ),
430
+ ) as Token.Value
431
+ }
432
+
433
+ function visit(path: string): Token.Value {
434
+ if (Object.hasOwn(values, path)) return values[path]!
435
+ if (active.has(path))
436
+ throw new Vars.InvalidError(
437
+ path.split('.'),
438
+ 'Cyclic variables are not supported.',
439
+ )
440
+ if (!Object.hasOwn(metadata.values, path))
441
+ throw new Vars.InvalidError(
442
+ path.split('.'),
443
+ 'Referenced variable path is missing.',
444
+ )
445
+
446
+ active.add(path)
447
+ values[path] = resolve(metadata.values[path]!)
448
+ active.delete(path)
449
+ return values[path]!
450
+ }
451
+
452
+ for (const path of Object.keys(metadata.values)) visit(path)
453
+ return Object.freeze(
454
+ Object.fromEntries(
455
+ Object.keys(metadata.values).map((path) => [path, values[path]!]),
456
+ ),
457
+ )
458
+ }