zyzz 0.0.2 → 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 (252) 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 +19 -13
  7. package/dist/Style.d.ts.map +1 -1
  8. package/dist/Style.js +73 -7
  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 +10 -6
  21. package/dist/compiler/Source.d.ts.map +1 -1
  22. package/dist/compiler/Source.js +66 -11
  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 +59 -21
  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 +162 -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 +451 -1649
  72. package/dist/default.d.ts.map +1 -1
  73. package/dist/default.js +389 -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/internal/Shorthands.d.ts +1 -1
  102. package/dist/internal/Shorthands.d.ts.map +1 -1
  103. package/dist/internal/Shorthands.js +1 -0
  104. package/dist/internal/Shorthands.js.map +1 -1
  105. package/dist/{Theme.d.ts → internal/Theme.d.ts} +24 -19
  106. package/dist/internal/Theme.d.ts.map +1 -0
  107. package/dist/{Theme.js → internal/Theme.js} +57 -14
  108. package/dist/internal/Theme.js.map +1 -0
  109. package/dist/internal/Token.d.ts +46 -16
  110. package/dist/internal/Token.d.ts.map +1 -1
  111. package/dist/internal/Token.js +89 -5
  112. package/dist/internal/Token.js.map +1 -1
  113. package/dist/internal/Typography.d.ts +31 -0
  114. package/dist/internal/Typography.d.ts.map +1 -0
  115. package/dist/internal/Typography.js +54 -0
  116. package/dist/internal/Typography.js.map +1 -0
  117. package/dist/internal/Value.d.ts +6 -1
  118. package/dist/internal/Value.d.ts.map +1 -1
  119. package/dist/internal/Value.js.map +1 -1
  120. package/dist/internal/VariableSets.d.ts +20 -0
  121. package/dist/internal/VariableSets.d.ts.map +1 -0
  122. package/dist/internal/VariableSets.js +317 -0
  123. package/dist/internal/VariableSets.js.map +1 -0
  124. package/dist/next/loader.js +15 -5
  125. package/dist/next/loader.js.map +1 -1
  126. package/dist/node/Host.d.ts.map +1 -1
  127. package/dist/node/Host.js +1 -1
  128. package/dist/node/Host.js.map +1 -1
  129. package/dist/react-native/Host.d.ts +2 -2
  130. package/dist/react-native/Host.d.ts.map +1 -1
  131. package/dist/react-native/Host.js +4 -4
  132. package/dist/react-native/Host.js.map +1 -1
  133. package/dist/react-native/StyleSheet.d.ts +9 -8
  134. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  135. package/dist/react-native/StyleSheet.js +29 -23
  136. package/dist/react-native/StyleSheet.js.map +1 -1
  137. package/dist/react-native/Variants.d.ts +3 -3
  138. package/dist/react-native/Variants.d.ts.map +1 -1
  139. package/dist/react-native/Variants.js +2 -2
  140. package/dist/react-native/react.d.ts +1 -1
  141. package/dist/react-native/react.d.ts.map +1 -1
  142. package/dist/react-native/react.js +5 -5
  143. package/dist/react-native/react.js.map +1 -1
  144. package/dist/rollup/index.d.ts +3 -0
  145. package/dist/rollup/index.d.ts.map +1 -0
  146. package/dist/rollup/index.js +5 -0
  147. package/dist/rollup/index.js.map +1 -0
  148. package/dist/runtime/Appearance.d.ts +7 -7
  149. package/dist/runtime/Appearance.d.ts.map +1 -1
  150. package/dist/runtime/Appearance.js +15 -15
  151. package/dist/runtime/Appearance.js.map +1 -1
  152. package/dist/runtime/Dynamic.d.ts.map +1 -1
  153. package/dist/runtime/Dynamic.js +4 -1
  154. package/dist/runtime/Dynamic.js.map +1 -1
  155. package/dist/runtime/NativeContext.d.ts +4 -4
  156. package/dist/runtime/NativeContext.d.ts.map +1 -1
  157. package/dist/runtime/NativeContext.js +5 -5
  158. package/dist/runtime/NativeContext.js.map +1 -1
  159. package/dist/runtime/NativeDynamic.d.ts +1 -1
  160. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  161. package/dist/runtime/NativeDynamic.js.map +1 -1
  162. package/dist/runtime/Props.d.ts +1 -1
  163. package/dist/runtime/Props.js +3 -3
  164. package/dist/runtime/Props.js.map +1 -1
  165. package/dist/runtime/Selection.d.ts +14 -10
  166. package/dist/runtime/Selection.d.ts.map +1 -1
  167. package/dist/runtime/Selection.js +19 -18
  168. package/dist/runtime/Selection.js.map +1 -1
  169. package/dist/styleFunction.d.ts +14 -14
  170. package/dist/styleFunction.d.ts.map +1 -1
  171. package/dist/styleFunction.js.map +1 -1
  172. package/dist/unplugin/index.d.ts +20 -0
  173. package/dist/unplugin/index.d.ts.map +1 -0
  174. package/dist/unplugin/index.js +498 -0
  175. package/dist/unplugin/index.js.map +1 -0
  176. package/dist/variants.d.ts +3 -3
  177. package/dist/variants.d.ts.map +1 -1
  178. package/dist/variants.js.map +1 -1
  179. package/dist/vite/index.d.ts.map +1 -1
  180. package/dist/vite/index.js +6 -3
  181. package/dist/vite/index.js.map +1 -1
  182. package/dist/web/Css.d.ts +4 -3
  183. package/dist/web/Css.d.ts.map +1 -1
  184. package/dist/web/Css.js +4 -4
  185. package/dist/web/Css.js.map +1 -1
  186. package/dist/web/internal/Themes.d.ts +3 -2
  187. package/dist/web/internal/Themes.d.ts.map +1 -1
  188. package/dist/web/internal/Themes.js +42 -6
  189. package/dist/web/internal/Themes.js.map +1 -1
  190. package/dist/webpack/index.d.ts +3 -0
  191. package/dist/webpack/index.d.ts.map +1 -0
  192. package/dist/webpack/index.js +5 -0
  193. package/dist/webpack/index.js.map +1 -0
  194. package/package.json +23 -2
  195. package/src/Config.ts +43 -684
  196. package/src/Style.ts +167 -40
  197. package/src/Vars.ts +299 -0
  198. package/src/compiler/Graph.ts +14 -9
  199. package/src/compiler/Native.ts +29 -32
  200. package/src/compiler/Source.ts +85 -18
  201. package/src/compiler/Transform.ts +78 -22
  202. package/src/compiler/internal/Compositions.ts +36 -3
  203. package/src/compiler/internal/Configurations.ts +83 -11
  204. package/src/compiler/internal/Contract.ts +223 -17
  205. package/src/compiler/internal/Contributions.ts +1 -1
  206. package/src/compiler/internal/Dynamic.ts +1 -1
  207. package/src/compiler/internal/Expression.ts +26 -0
  208. package/src/compiler/internal/Identifiers.ts +2 -1
  209. package/src/compiler/internal/NativeBindings.ts +3 -3
  210. package/src/compiler/internal/PackedStyles.ts +22 -26
  211. package/src/compiler/internal/Recipes.ts +1 -1
  212. package/src/compiler/internal/Selectors.ts +1 -1
  213. package/src/compiler/internal/Themes.ts +266 -57
  214. package/src/compiler/internal/{Variables.ts → Vars.ts} +2 -2
  215. package/src/default.ts +868 -156
  216. package/src/esbuild/index.ts +5 -0
  217. package/src/index.ts +4 -4
  218. package/src/internal/Authoring.ts +5 -7
  219. package/src/internal/Binding.ts +1 -1
  220. package/src/internal/Completion.ts +42 -0
  221. package/src/internal/Condition.ts +31 -1
  222. package/src/internal/Configuration.ts +970 -0
  223. package/src/internal/Shorthands.ts +2 -0
  224. package/src/{Theme.ts → internal/Theme.ts} +174 -48
  225. package/src/internal/Token.ts +258 -85
  226. package/src/internal/Typography.ts +106 -0
  227. package/src/internal/Value.ts +45 -26
  228. package/src/internal/VariableSets.ts +458 -0
  229. package/src/next/loader.ts +19 -5
  230. package/src/node/Host.ts +1 -2
  231. package/src/react-native/Host.ts +6 -6
  232. package/src/react-native/StyleSheet.ts +49 -42
  233. package/src/react-native/Variants.ts +3 -3
  234. package/src/react-native/react.ts +6 -6
  235. package/src/rollup/index.ts +5 -0
  236. package/src/runtime/Appearance.ts +20 -20
  237. package/src/runtime/Dynamic.ts +4 -1
  238. package/src/runtime/NativeContext.ts +8 -8
  239. package/src/runtime/NativeDynamic.ts +2 -4
  240. package/src/runtime/Props.ts +3 -3
  241. package/src/runtime/Selection.ts +44 -33
  242. package/src/styleFunction.ts +22 -19
  243. package/src/unplugin/index.ts +600 -0
  244. package/src/variants.ts +7 -5
  245. package/src/vite/index.ts +5 -4
  246. package/src/web/Css.ts +10 -7
  247. package/src/web/internal/Themes.ts +52 -9
  248. package/src/webpack/index.ts +5 -0
  249. package/dist/Theme.d.ts.map +0 -1
  250. package/dist/Theme.js.map +0 -1
  251. package/dist/compiler/internal/Variables.d.ts.map +0 -1
  252. package/dist/compiler/internal/Variables.js.map +0 -1
@@ -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
+ }
@@ -90,10 +90,11 @@ async function compile(context: Context, source: string) {
90
90
 
91
91
  const specifier = node.source.value
92
92
  links[specifier] = null
93
+ // Next owns this virtual module, which has no physical stylesheet contract.
94
+ if (specifier === 'next/root-params') continue
93
95
  if (
94
96
  specifier === 'zyzz' ||
95
- (specifier.startsWith('zyzz/') &&
96
- specifier !== 'zyzz/default') ||
97
+ (specifier.startsWith('zyzz/') && specifier !== 'zyzz/default') ||
97
98
  specifier.startsWith('node:')
98
99
  )
99
100
  continue
@@ -288,15 +289,28 @@ async function compile(context: Context, source: string) {
288
289
  )
289
290
  .split(Path.sep)
290
291
  .join('/')
292
+ // Turbopack appends the output extension to loader-transformed resource paths.
293
+ const resource = await Fs.stat(context.resourcePath).then(
294
+ () => context.resourcePath,
295
+ async (error: NodeJS.ErrnoException) => {
296
+ if (error.code !== 'ENOENT') throw error
297
+ const original = context.resourcePath.slice(
298
+ 0,
299
+ -Path.extname(context.resourcePath).length,
300
+ )
301
+ await Fs.access(original)
302
+ return original
303
+ },
304
+ )
291
305
  const requests = [
292
306
  ...(graph.sharedCss
293
307
  ? [
294
- `import ${JSON.stringify(sharedFile.startsWith('.') ? sharedFile : `./${sharedFile}`)};`,
308
+ `import ${JSON.stringify(sharedFile.startsWith('../') ? sharedFile : `./${sharedFile}`)};`,
295
309
  ]
296
310
  : []),
297
311
  ...(output.css
298
312
  ? [
299
- `import ${JSON.stringify(`./${Path.basename(context.resourcePath)}?zyzz-style`)};`,
313
+ `import ${JSON.stringify(`./${Path.basename(resource)}?zyzz-style`)};`,
300
314
  ]
301
315
  : []),
302
316
  ]
@@ -326,7 +340,7 @@ async function compile(context: Context, source: string) {
326
340
  .split(Path.sep)
327
341
  .join('/')
328
342
  requests.push(
329
- `import ${JSON.stringify(relative.startsWith('.') ? relative : `./${relative}`)};`,
343
+ `import ${JSON.stringify(relative.startsWith('../') ? relative : `./${relative}`)};`,
330
344
  )
331
345
  }
332
346
 
package/src/node/Host.ts CHANGED
@@ -54,8 +54,7 @@ export async function create(options: create.Options): Promise<Runtime> {
54
54
  fonts:
55
55
  options.native.fonts && Object.freeze({ ...options.native.fonts }),
56
56
  // Theme definitions are immutable. Only the caller-owned catalog needs copying.
57
- themes:
58
- options.native.themes && Object.freeze({ ...options.native.themes }),
57
+ vars: options.native.vars && Object.freeze({ ...options.native.vars }),
59
58
  units:
60
59
  options.native.units && Object.freeze({ ...options.native.units }),
61
60
  })
@@ -28,7 +28,7 @@ export function create<
28
28
  'highContrast',
29
29
  'reducedMotion',
30
30
  'rtl',
31
- 'theme',
31
+ 'set',
32
32
  ] as const
33
33
  const bindings = new Set<(context: Snapshot) => () => void>()
34
34
  const listeners = new Set<(context: Snapshot) => void>()
@@ -45,8 +45,8 @@ export function create<
45
45
  (key) => !keys.includes(key as (typeof keys)[number]),
46
46
  ) ||
47
47
  (values.colorScheme !== 'light' && values.colorScheme !== 'dark') ||
48
- typeof values.theme !== 'string' ||
49
- !values.theme.trim() ||
48
+ typeof values.set !== 'string' ||
49
+ !values.set.trim() ||
50
50
  typeof values.density !== 'number' ||
51
51
  !Number.isFinite(values.density) ||
52
52
  values.density <= 0 ||
@@ -58,7 +58,7 @@ export function create<
58
58
  typeof values.rtl !== 'boolean'
59
59
  )
60
60
  throw new InputError(
61
- 'Provide a theme, resolved scheme, positive finite scales, and boolean accessibility inputs.',
61
+ 'Provide a set, resolved scheme, positive finite scales, and boolean accessibility inputs.',
62
62
  )
63
63
  const hairlineWidth =
64
64
  Math.round(0.4 * values.density) / values.density || 1 / values.density
@@ -265,8 +265,8 @@ export type Inputs = {
265
265
  readonly reducedMotion: boolean
266
266
  /** Whether layout follows a right-to-left direction. */
267
267
  readonly rtl: boolean
268
- /** Compiled theme label selected by binding factories. */
269
- readonly theme: string
268
+ /** Compiled set label selected by binding factories. */
269
+ readonly set: string
270
270
  }
271
271
 
272
272
  /** An operation attempted to change a disposed or currently updating host. */