zyzz 0.0.21 → 0.0.23

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 (233) hide show
  1. package/Zyzz.podspec +16 -0
  2. package/dist/Style.d.ts +3 -1
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js.map +1 -1
  5. package/dist/babel/NativeJsx.d.ts.map +1 -1
  6. package/dist/babel/NativeJsx.js +217 -15
  7. package/dist/babel/NativeJsx.js.map +1 -1
  8. package/dist/babel/index.d.ts +6 -2
  9. package/dist/babel/index.d.ts.map +1 -1
  10. package/dist/babel/index.js +10 -1
  11. package/dist/babel/index.js.map +1 -1
  12. package/dist/compiler/Graph.d.ts.map +1 -1
  13. package/dist/compiler/Graph.js +107 -0
  14. package/dist/compiler/Graph.js.map +1 -1
  15. package/dist/compiler/Native.d.ts +6 -3
  16. package/dist/compiler/Native.d.ts.map +1 -1
  17. package/dist/compiler/Native.js +142 -24
  18. package/dist/compiler/Native.js.map +1 -1
  19. package/dist/compiler/Source.d.ts +2 -0
  20. package/dist/compiler/Source.d.ts.map +1 -1
  21. package/dist/compiler/Source.js +17 -5
  22. package/dist/compiler/Source.js.map +1 -1
  23. package/dist/compiler/internal/Configurations.d.ts +2 -0
  24. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  25. package/dist/compiler/internal/Configurations.js +21 -3
  26. package/dist/compiler/internal/Configurations.js.map +1 -1
  27. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  28. package/dist/compiler/internal/Contract.js +75 -38
  29. package/dist/compiler/internal/Contract.js.map +1 -1
  30. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  31. package/dist/compiler/internal/Contributions.js +7 -1
  32. package/dist/compiler/internal/Contributions.js.map +1 -1
  33. package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
  34. package/dist/compiler/internal/Descriptors.js +11 -2
  35. package/dist/compiler/internal/Descriptors.js.map +1 -1
  36. package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
  37. package/dist/compiler/internal/Dynamic.js +3 -0
  38. package/dist/compiler/internal/Dynamic.js.map +1 -1
  39. package/dist/compiler/internal/Expression.js +2 -2
  40. package/dist/compiler/internal/Expression.js.map +1 -1
  41. package/dist/compiler/internal/NativeBindings.d.ts +24 -26
  42. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  43. package/dist/compiler/internal/NativeBindings.js +62 -8
  44. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  45. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  46. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  47. package/dist/compiler/internal/NativeMedia.js +287 -0
  48. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  49. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  50. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  51. package/dist/compiler/internal/NativeVars.js +160 -0
  52. package/dist/compiler/internal/NativeVars.js.map +1 -0
  53. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  54. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  55. package/dist/compiler/internal/PackedStyles.js +2 -0
  56. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  57. package/dist/compiler/internal/Themes.d.ts +24 -1
  58. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  59. package/dist/compiler/internal/Themes.js +120 -3
  60. package/dist/compiler/internal/Themes.js.map +1 -1
  61. package/dist/compiler/internal/Vars.d.ts +1 -1
  62. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  63. package/dist/compiler/internal/Vars.js +7 -2
  64. package/dist/compiler/internal/Vars.js.map +1 -1
  65. package/dist/default.js.zyzz.json +1 -1
  66. package/dist/internal/Completion.d.ts +1 -1
  67. package/dist/internal/Completion.d.ts.map +1 -1
  68. package/dist/internal/Targets.d.ts.map +1 -1
  69. package/dist/internal/Targets.js +2 -0
  70. package/dist/internal/Targets.js.map +1 -1
  71. package/dist/internal/Token.d.ts +0 -2
  72. package/dist/internal/Token.d.ts.map +1 -1
  73. package/dist/internal/Token.js.map +1 -1
  74. package/dist/internal/Value.d.ts +7 -0
  75. package/dist/internal/Value.d.ts.map +1 -1
  76. package/dist/internal/Value.js.map +1 -1
  77. package/dist/metro/Graph.d.ts +22 -7
  78. package/dist/metro/Graph.d.ts.map +1 -1
  79. package/dist/metro/Graph.js +188 -42
  80. package/dist/metro/Graph.js.map +1 -1
  81. package/dist/metro/Watch.d.ts +28 -4
  82. package/dist/metro/Watch.d.ts.map +1 -1
  83. package/dist/metro/Watch.js +39 -7
  84. package/dist/metro/Watch.js.map +1 -1
  85. package/dist/metro/index.d.ts +15 -4
  86. package/dist/metro/index.d.ts.map +1 -1
  87. package/dist/metro/index.js +27 -0
  88. package/dist/metro/index.js.map +1 -1
  89. package/dist/metro/transformer.d.ts +5 -1
  90. package/dist/metro/transformer.d.ts.map +1 -1
  91. package/dist/metro/transformer.js +5 -3
  92. package/dist/metro/transformer.js.map +1 -1
  93. package/dist/react-native/StyleSheet.d.ts +8 -3
  94. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  95. package/dist/react-native/StyleSheet.js +25 -34
  96. package/dist/react-native/StyleSheet.js.map +1 -1
  97. package/dist/react-native/index.d.ts +1 -1
  98. package/dist/react-native/index.d.ts.map +1 -1
  99. package/dist/react-native/index.js +1 -1
  100. package/dist/react-native/index.js.map +1 -1
  101. package/dist/react-native/index.native.d.ts +5 -0
  102. package/dist/react-native/index.native.js +20 -0
  103. package/dist/react-native/index.native.js.map +7 -0
  104. package/dist/react-native/internal/Calculation.d.ts +7 -0
  105. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  106. package/dist/react-native/internal/Calculation.js +134 -0
  107. package/dist/react-native/internal/Calculation.js.map +1 -0
  108. package/dist/react-native/internal/Device.js +218 -0
  109. package/dist/react-native/internal/Device.js.map +7 -0
  110. package/dist/react-native/internal/NativeZyzz.js +7 -0
  111. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  112. package/dist/react-native/internal/Scalar.d.ts +16 -0
  113. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  114. package/dist/react-native/internal/Scalar.js +62 -4
  115. package/dist/react-native/internal/Scalar.js.map +1 -1
  116. package/dist/react-native/internal/Store.d.ts +32 -0
  117. package/dist/react-native/internal/Store.d.ts.map +1 -0
  118. package/dist/react-native/internal/Store.js +57 -0
  119. package/dist/react-native/internal/Store.js.map +1 -0
  120. package/dist/react-native/internal/Styles.d.ts +19 -0
  121. package/dist/react-native/internal/Styles.d.ts.map +1 -0
  122. package/dist/react-native/internal/Styles.js +35 -0
  123. package/dist/react-native/internal/Styles.js.map +1 -0
  124. package/dist/react-native/internal/Tokens.d.ts +21 -0
  125. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  126. package/dist/react-native/internal/Tokens.js +58 -0
  127. package/dist/react-native/internal/Tokens.js.map +1 -0
  128. package/dist/react-native/internal/Viewport.d.ts +6 -0
  129. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  130. package/dist/react-native/internal/Viewport.js +4 -0
  131. package/dist/react-native/internal/Viewport.js.map +1 -0
  132. package/dist/react-native/react.d.ts +102 -14
  133. package/dist/react-native/react.d.ts.map +1 -1
  134. package/dist/react-native/react.js +86 -18
  135. package/dist/react-native/react.js.map +1 -1
  136. package/dist/react-native/reanimated.d.ts +39 -0
  137. package/dist/react-native/reanimated.d.ts.map +1 -0
  138. package/dist/react-native/reanimated.js +76 -0
  139. package/dist/react-native/reanimated.js.map +1 -0
  140. package/dist/runtime/NativeContext.d.ts +10 -1
  141. package/dist/runtime/NativeContext.d.ts.map +1 -1
  142. package/dist/runtime/NativeContext.js +67 -14
  143. package/dist/runtime/NativeContext.js.map +1 -1
  144. package/dist/runtime/NativeDynamic.d.ts +3 -2
  145. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  146. package/dist/runtime/NativeDynamic.js +37 -7
  147. package/dist/runtime/NativeDynamic.js.map +1 -1
  148. package/dist/runtime/NativeStatic.d.ts +23 -0
  149. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  150. package/dist/runtime/NativeStatic.js +106 -0
  151. package/dist/runtime/NativeStatic.js.map +1 -0
  152. package/dist/runtime/NativeVars.d.ts +53 -0
  153. package/dist/runtime/NativeVars.d.ts.map +1 -0
  154. package/dist/runtime/NativeVars.js +77 -0
  155. package/dist/runtime/NativeVars.js.map +1 -0
  156. package/dist/runtime/index.d.ts +4 -0
  157. package/dist/runtime/index.d.ts.map +1 -1
  158. package/dist/runtime/index.js +4 -0
  159. package/dist/runtime/index.js.map +1 -1
  160. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  161. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  162. package/dist/runtime/internal/NativeMedia.js +36 -0
  163. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  164. package/dist/vite/index.d.ts.map +1 -1
  165. package/dist/vite/index.js +7 -35
  166. package/dist/vite/index.js.map +1 -1
  167. package/dist/web/Css.d.ts.map +1 -1
  168. package/dist/web/Css.js +31 -19
  169. package/dist/web/Css.js.map +1 -1
  170. package/dist/web/internal/Themes.d.ts +0 -2
  171. package/dist/web/internal/Themes.d.ts.map +1 -1
  172. package/dist/web/internal/Themes.js +30 -28
  173. package/dist/web/internal/Themes.js.map +1 -1
  174. package/dist/web/page.d.ts +2 -0
  175. package/dist/web/page.d.ts.map +1 -1
  176. package/native/NativeZyzz.cpp +215 -0
  177. package/native/NativeZyzz.h +52 -0
  178. package/native/ZyzzSpecJSI.h +65 -0
  179. package/native/android/CMakeLists.txt +8 -0
  180. package/native/android/ZyzzSpec-generated.cpp +50 -0
  181. package/native/android/ZyzzSpec.h +31 -0
  182. package/native/ios/ZyzzModuleProvider.mm +13 -0
  183. package/package.json +39 -1
  184. package/react-native.config.ts +14 -0
  185. package/src/Style.ts +11 -8
  186. package/src/babel/NativeJsx.ts +319 -33
  187. package/src/babel/index.ts +21 -3
  188. package/src/compiler/Graph.ts +126 -0
  189. package/src/compiler/Native.ts +158 -26
  190. package/src/compiler/Source.ts +32 -5
  191. package/src/compiler/internal/Configurations.ts +25 -3
  192. package/src/compiler/internal/Contract.ts +98 -50
  193. package/src/compiler/internal/Contributions.ts +7 -1
  194. package/src/compiler/internal/Descriptors.ts +12 -8
  195. package/src/compiler/internal/Dynamic.ts +4 -0
  196. package/src/compiler/internal/Expression.ts +2 -2
  197. package/src/compiler/internal/NativeBindings.ts +99 -15
  198. package/src/compiler/internal/NativeMedia.ts +342 -0
  199. package/src/compiler/internal/NativeVars.ts +231 -0
  200. package/src/compiler/internal/PackedStyles.ts +4 -0
  201. package/src/compiler/internal/Themes.ts +193 -2
  202. package/src/compiler/internal/Vars.ts +12 -1
  203. package/src/internal/Completion.ts +6 -3
  204. package/src/internal/Targets.ts +2 -0
  205. package/src/internal/Token.ts +0 -2
  206. package/src/internal/Value.ts +20 -0
  207. package/src/metro/Graph.ts +250 -50
  208. package/src/metro/Watch.ts +95 -10
  209. package/src/metro/index.ts +62 -4
  210. package/src/metro/transformer.ts +21 -11
  211. package/src/react-native/StyleSheet.ts +56 -49
  212. package/src/react-native/index.native.ts +10 -0
  213. package/src/react-native/index.ts +1 -1
  214. package/src/react-native/internal/Calculation.ts +159 -0
  215. package/src/react-native/internal/Device.ts +297 -0
  216. package/src/react-native/internal/NativeZyzz.ts +20 -0
  217. package/src/react-native/internal/Scalar.ts +86 -4
  218. package/src/react-native/internal/Store.ts +78 -0
  219. package/src/react-native/internal/Styles.ts +77 -0
  220. package/src/react-native/internal/Tokens.ts +85 -0
  221. package/src/react-native/internal/Viewport.ts +8 -0
  222. package/src/react-native/react.ts +218 -26
  223. package/src/react-native/reanimated.ts +162 -0
  224. package/src/runtime/NativeContext.ts +88 -17
  225. package/src/runtime/NativeDynamic.ts +42 -10
  226. package/src/runtime/NativeStatic.ts +145 -0
  227. package/src/runtime/NativeVars.ts +189 -0
  228. package/src/runtime/index.ts +6 -0
  229. package/src/runtime/internal/NativeMedia.ts +63 -0
  230. package/src/vite/index.ts +9 -42
  231. package/src/web/Css.ts +42 -27
  232. package/src/web/internal/Themes.ts +34 -57
  233. package/src/web/page.ts +2 -0
@@ -32,6 +32,8 @@ export type Alias = Call & {
32
32
 
33
33
  /** Theme factory span and generated scope key. */
34
34
  export type Call = {
35
+ /** Whether native defineConfig retains a typed React Provider. */
36
+ readonly nativeProvider?: boolean | undefined
35
37
  readonly variableSet?: boolean | undefined
36
38
  readonly directVariables?: boolean | undefined
37
39
  readonly variableConfig?: boolean | undefined
@@ -102,6 +104,50 @@ export type Link = {
102
104
  readonly members?: Readonly<Record<string, Link>> | undefined
103
105
  }
104
106
 
107
+ /** A readonly variable argument consumed by a native React hook. */
108
+ export type VarsRead = {
109
+ /** Owning configuration fallback. */
110
+ readonly defaultVars: string
111
+ /** Exclusive source offset. */
112
+ readonly end: number
113
+ /** Stable configuration identity for graph-owned native profiles. */
114
+ readonly owner: string
115
+ /** Inclusive source offset. */
116
+ readonly start: number
117
+ /** A standalone definition that does not select a named catalog entry. */
118
+ readonly unnamed: boolean
119
+ /** Complete compatible variable alternatives. */
120
+ readonly vars: Readonly<Record<string, Theme.Definition>>
121
+ }
122
+
123
+ /** Resolves the variable alternatives a linked configuration or variable set exposes to native hooks. */
124
+ export function varsRead(
125
+ link: Pick<Link, 'call' | 'definition' | 'members'>,
126
+ ): Omit<VarsRead, 'end' | 'start'> {
127
+ const named = link.call.options?.themes !== undefined
128
+ const vars = (() => {
129
+ if (!named) return { default: link.definition }
130
+ return Object.fromEntries(
131
+ Object.entries(link.members ?? {}).flatMap((entry) => {
132
+ const [key, member] = entry
133
+ const path = JSON.parse(key) as string[]
134
+ if (path.length === 1 && link.call.selection)
135
+ return [[path[0]!, member.definition]]
136
+ if (path.length === 2 && path[0] === 'themes')
137
+ return [[path[1]!, member.definition]]
138
+ return []
139
+ }),
140
+ )
141
+ })()
142
+
143
+ return {
144
+ defaultVars: named ? String(link.call.options!.defaultTheme) : 'default',
145
+ owner: link.call.name,
146
+ unnamed: !named,
147
+ vars,
148
+ }
149
+ }
150
+
105
151
  /** Shared graph data; no filesystem or runtime evaluation is involved. */
106
152
  export type Context = {
107
153
  /** Canonical identifier names collected during graph validation. */
@@ -137,6 +183,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
137
183
  { end: number; output: 'html' | undefined }
138
184
  >()
139
185
  const selections = new Set<string>()
186
+ const selectionUses = new Set<string>()
140
187
  const calls: Call[] = []
141
188
  const scripts = new Set<string>()
142
189
  const definitions = new Map<number, Call>()
@@ -145,10 +192,12 @@ export function collect(program: Ast.Program, options: collect.Options) {
145
192
  const variableImports = new Set<number>()
146
193
  const configImports = new Set<number>()
147
194
  const factoryImports = new Map<number, 'create' | 'define' | 'extend'>()
195
+ const nativeFactories = new Set<string>()
148
196
  const configs = new Map<string, Link>()
149
197
  const configBindings = new Map<number, Link>()
150
198
  const factoryReferences = new Set<number>()
151
199
  const references: Reference[] = []
200
+ const varsReads: VarsRead[] = []
152
201
 
153
202
  const styles = new Map<
154
203
  number,
@@ -166,12 +215,28 @@ export function collect(program: Ast.Program, options: collect.Options) {
166
215
  for (const node of program.body) {
167
216
  if (
168
217
  node.type !== 'ImportDeclaration' ||
169
- node.source.value !== 'zyzz' ||
218
+ !['zyzz', 'zyzz/react-native', 'zyzz/react-native/react'].includes(
219
+ node.source.value,
220
+ ) ||
170
221
  node.importKind === 'type'
171
222
  )
172
223
  continue
173
224
 
174
- for (const specifier of node.specifiers)
225
+ for (const specifier of node.specifiers) {
226
+ if (node.source.value !== 'zyzz') {
227
+ if (
228
+ specifier.type === 'ImportSpecifier' &&
229
+ specifier.importKind !== 'type' &&
230
+ (specifier.imported.type === 'Identifier'
231
+ ? specifier.imported.name
232
+ : specifier.imported.value) === 'defineConfig'
233
+ ) {
234
+ factoryImports.set(specifier.start, 'create')
235
+ nativeFactories.add(specifier.local.name)
236
+ }
237
+ continue
238
+ }
239
+
175
240
  if (
176
241
  specifier.type === 'ImportSpecifier' &&
177
242
  specifier.importKind !== 'type' &&
@@ -208,6 +273,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
208
273
  factoryImports.set(specifier.start, 'define')
209
274
  else factoryImports.set(specifier.start, 'extend')
210
275
  }
276
+ }
211
277
  }
212
278
 
213
279
  if (
@@ -686,11 +752,20 @@ export function collect(program: Ast.Program, options: collect.Options) {
686
752
  }
687
753
 
688
754
  if (config) {
755
+ const nativeProvider =
756
+ member.type === 'Identifier' && nativeFactories.has(member.name)
757
+ if (nativeProvider && !options.native)
758
+ fail(
759
+ 'Native defineConfig requires a native compilation target.',
760
+ expression,
761
+ )
762
+
689
763
  try {
690
764
  const link = Configurations.collect({
691
765
  data,
692
766
  expression,
693
767
  name,
768
+ nativeProvider,
694
769
  resolve: (node) => {
695
770
  const link = resolve(node)
696
771
 
@@ -717,6 +792,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
717
792
  themes[link.call.name] = link.definition
718
793
 
719
794
  for (const { key, id } of bindings) {
795
+ if (key === 'Provider' && link.call.nativeProvider) continue
796
+
720
797
  if (
721
798
  (key === 'style' || key === 'variants') &&
722
799
  !link.call.selection &&
@@ -1046,6 +1123,15 @@ export function collect(program: Ast.Program, options: collect.Options) {
1046
1123
  })
1047
1124
 
1048
1125
  for (const { key, id } of bindings) {
1126
+ if (
1127
+ key === 'Provider' &&
1128
+ link.call.nativeProvider &&
1129
+ !link.call.initialization &&
1130
+ !link.call.root &&
1131
+ !link.call.selection
1132
+ )
1133
+ continue
1134
+
1049
1135
  if (
1050
1136
  (key === 'style' || key === 'variants') &&
1051
1137
  !link.call.selection &&
@@ -1351,6 +1437,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1351
1437
  parent: Ast.Node,
1352
1438
  ancestors: readonly Ast.Node[],
1353
1439
  binding: Walker.ScopeTrackerNode | null,
1440
+ lookup: (name: string) => Walker.ScopeTrackerNode | null,
1354
1441
  ): boolean {
1355
1442
  const grandparent = ancestors.at(-3)
1356
1443
 
@@ -1438,6 +1525,13 @@ export function collect(program: Ast.Program, options: collect.Options) {
1438
1525
  )
1439
1526
  fail('Configuration references must follow their definition.', node)
1440
1527
 
1528
+ if (
1529
+ config.call.variableConfig &&
1530
+ config.call.selection &&
1531
+ readVars(node, parent, config, lookup)
1532
+ )
1533
+ return true
1534
+
1441
1535
  // Root controls are an ordinary runtime object; any read or call is valid.
1442
1536
  if (config.call.root) {
1443
1537
  appearances.add(config.call.name)
@@ -1459,6 +1553,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1459
1553
 
1460
1554
  if (config.call.selection) {
1461
1555
  selections.add(config.call.name)
1556
+ selectionUses.add(config.call.name)
1462
1557
  scopeApplications.set(parent.start, {
1463
1558
  end: parent.end,
1464
1559
  output: config.call.options?.output === 'html' ? 'html' : undefined,
@@ -1504,6 +1599,24 @@ export function collect(program: Ast.Program, options: collect.Options) {
1504
1599
  path.push(key)
1505
1600
  target = member
1506
1601
 
1602
+ if (
1603
+ path.length === 1 &&
1604
+ path[0] === 'Provider' &&
1605
+ config.call.nativeProvider &&
1606
+ !config.call.initialization &&
1607
+ !config.call.root &&
1608
+ !config.call.selection
1609
+ )
1610
+ return true
1611
+
1612
+ if (
1613
+ config.call.variableConfig &&
1614
+ path.length === 1 &&
1615
+ path[0] === 'vars' &&
1616
+ readVars(target, ancestors[index - 1]!, config, lookup)
1617
+ )
1618
+ return true
1619
+
1507
1620
  if (
1508
1621
  aliasReferences.has(target.start) ||
1509
1622
  factoryReferences.has(target.start)
@@ -1562,6 +1675,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1562
1675
 
1563
1676
  if (path[0] === 'themes' || path[0] === 'vars') {
1564
1677
  selections.add(config.call.name)
1678
+ selectionUses.add(config.call.name)
1565
1679
  const application = ancestors[index - 1]!
1566
1680
  scopeApplications.set(application.start, {
1567
1681
  end: application.end,
@@ -1623,6 +1737,7 @@ export function collect(program: Ast.Program, options: collect.Options) {
1623
1737
  // A compiled selector is an ordinary runtime function, so passing or storing it is safe.
1624
1738
  if (config.call.selection && !config.call.catalogOnly && !path.length) {
1625
1739
  selections.add(config.call.name)
1740
+ selectionUses.add(config.call.name)
1626
1741
 
1627
1742
  return true
1628
1743
  }
@@ -1640,9 +1755,79 @@ export function collect(program: Ast.Program, options: collect.Options) {
1640
1755
  if (!theme) return false
1641
1756
  if (node.start === binding!.node.start) return true
1642
1757
 
1758
+ if (
1759
+ theme.variableSet &&
1760
+ readVars(
1761
+ node,
1762
+ parent,
1763
+ {
1764
+ call: theme,
1765
+ definition: themes[theme.name]!,
1766
+ },
1767
+ lookup,
1768
+ )
1769
+ )
1770
+ return true
1771
+
1643
1772
  return themeReference(node, parent, ancestors, theme)
1644
1773
  }
1645
1774
 
1775
+ function readVars(
1776
+ node: Ast.Node,
1777
+ parent: Ast.Node,
1778
+ link: Pick<Link, 'call' | 'definition' | 'members'>,
1779
+ lookup: (name: string) => Walker.ScopeTrackerNode | null,
1780
+ ): boolean {
1781
+ if (
1782
+ !options.native ||
1783
+ parent.type !== 'CallExpression' ||
1784
+ parent.optional ||
1785
+ parent.arguments[0] !== node
1786
+ )
1787
+ return false
1788
+
1789
+ const callee = parent.callee
1790
+ const name =
1791
+ callee.type === 'Identifier'
1792
+ ? callee.name
1793
+ : callee.type === 'MemberExpression' &&
1794
+ !callee.computed &&
1795
+ !callee.optional &&
1796
+ callee.object.type === 'Identifier' &&
1797
+ callee.property.type === 'Identifier' &&
1798
+ ['useAnimatedVars', 'useVars'].includes(callee.property.name)
1799
+ ? callee.object.name
1800
+ : undefined
1801
+ const binding = name === undefined ? undefined : lookup(name)
1802
+ if (
1803
+ binding?.type !== 'Import' ||
1804
+ ![
1805
+ 'zyzz/react-native',
1806
+ 'zyzz/react-native/reanimated',
1807
+ 'zyzz/react-native/react',
1808
+ ].includes(binding.importNode.source.value) ||
1809
+ binding.importNode.importKind === 'type'
1810
+ )
1811
+ return false
1812
+
1813
+ const specifier = binding.node
1814
+ if (
1815
+ callee.type === 'Identifier'
1816
+ ? specifier.type !== 'ImportSpecifier' ||
1817
+ specifier.importKind === 'type' ||
1818
+ !['useAnimatedVars', 'useVars'].includes(
1819
+ specifier.imported.type === 'Identifier'
1820
+ ? specifier.imported.name
1821
+ : specifier.imported.value,
1822
+ )
1823
+ : specifier.type !== 'ImportNamespaceSpecifier'
1824
+ )
1825
+ return false
1826
+
1827
+ varsReads.push({ ...varsRead(link), end: node.end, start: node.start })
1828
+ return true
1829
+ }
1830
+
1646
1831
  function themeReference(
1647
1832
  node: Ast.Node,
1648
1833
  parent: Ast.Node,
@@ -2023,6 +2208,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
2023
2208
  const entries = Object.entries(config.members ?? {}).flatMap(
2024
2209
  ([key, value]) => {
2025
2210
  const path = JSON.parse(key) as string[]
2211
+ if (config.call.selection && path.length === 1)
2212
+ return [[path[0]!, value.definition] as const]
2026
2213
  return (path[0] === 'themes' || path[0] === 'vars') &&
2027
2214
  path.length === 2
2028
2215
  ? [[path[1]!, value.definition] as const]
@@ -2045,10 +2232,12 @@ export function collect(program: Ast.Program, options: collect.Options) {
2045
2232
  scopeApplications,
2046
2233
  scripts,
2047
2234
  selections,
2235
+ selectionUses,
2048
2236
  staticTokens,
2049
2237
  styles,
2050
2238
  themes: Object.freeze(themes),
2051
2239
  tokens,
2240
+ varsReads,
2052
2241
  }
2053
2242
  }
2054
2243
 
@@ -2056,6 +2245,8 @@ export function collect(program: Ast.Program, options: collect.Options) {
2056
2245
  export declare namespace collect {
2057
2246
  /** Stable module namespace, independent of token values and call offsets. */
2058
2247
  type Options = {
2248
+ /** Captures readonly variable arguments for native React hooks. */
2249
+ readonly native?: boolean | undefined
2059
2250
  /** Encoded package/module identity from the source adapter. */
2060
2251
  readonly staticBindings?: ReadonlySet<number> | undefined
2061
2252
  readonly contributionCalls?: ReadonlySet<number> | undefined
@@ -477,6 +477,7 @@ export function collect(
477
477
  function reference(
478
478
  node: Extract<Ast.Node, { type: 'Identifier' | 'JSXIdentifier' }>,
479
479
  parent: Ast.Node,
480
+ ancestors: readonly Ast.Node[],
480
481
  binding: Walker.ScopeTrackerNode | null,
481
482
  ) {
482
483
  if (binding?.type === 'Import' && imports.has(binding.node.start))
@@ -488,7 +489,17 @@ export function collect(
488
489
  : node,
489
490
  ) ?? resolve(node)
490
491
  if (!link) return false
491
- if (link.call.start >= 0 && node.start < link.call.end)
492
+ // Reads inside functions wait for a call, so only module-scope reads must follow the declaration.
493
+ if (
494
+ link.call.start >= 0 &&
495
+ node.start < link.call.end &&
496
+ !ancestors.some(
497
+ (ancestor) =>
498
+ ancestor.type === 'ArrowFunctionExpression' ||
499
+ ancestor.type === 'FunctionDeclaration' ||
500
+ ancestor.type === 'FunctionExpression',
501
+ )
502
+ )
492
503
  throw new InvalidError('Vars must be declared before use.', node)
493
504
  return true
494
505
  }
@@ -11,9 +11,12 @@ export type Properties<
11
11
  styles = Record<string, unknown>,
12
12
  > = {
13
13
  readonly [property in keyof Literal.Properties & keyof styles]?:
14
- | (string extends Literal.Properties[property]
15
- ? never
16
- : Literal.Properties[property])
14
+ // Configured tokens replace literal CSS values, so only token names are suggested.
15
+ | ([Token.Names<tokens, property>] extends [never]
16
+ ? string extends Literal.Properties[property]
17
+ ? never
18
+ : Literal.Properties[property]
19
+ : never)
17
20
  | Token.Names<tokens, property>
18
21
  | (string & {})
19
22
  | number
@@ -147,6 +147,8 @@ export function copy(
147
147
  export function web<name extends string>(
148
148
  style: Style.NamedStyle<name>,
149
149
  ): Style.NamedStyle<name> {
150
+ if (!style.rules && !style.targets) return style
151
+
150
152
  const rules = style.rules?.map((rule) => ({
151
153
  ...rule,
152
154
  style: web(rule.style),
@@ -391,8 +391,6 @@ export const complete = Symbol('zyzz.contract.complete')
391
391
  export type Contract = {
392
392
  /** Whether values belong to independent variables rather than fixed theme categories. */
393
393
  readonly variableSet?: boolean | undefined
394
- /** Custom-property spelling retained by older packed producers. */
395
- readonly variableNaming?: 'legacy' | 'hashed' | undefined
396
394
  /** Configuration-local category-to-property mappings. */
397
395
  readonly mappings?: VariableSets.Mappings | false | undefined
398
396
  /** Web emission mode retained by configuration-bound theme handles. */
@@ -190,6 +190,26 @@ type CheckedArray<values, property extends PropertyKey, tokens> = {
190
190
  >[property]
191
191
  }
192
192
 
193
+ /** Lists the values a token-constrained property accepts, so mismatch diagnostics name them. */
194
+ export type Mismatch<tokens, property extends keyof Literal.Properties> =
195
+ // Resolving through a conditional prints the expected values instead of this alias's arguments.
196
+ tokens extends unknown
197
+ ? Expected<
198
+ | Extract<Token.Names<tokens, property>, string>
199
+ | NumericNames<Token.Names<tokens, property>>
200
+ | `${string} !custom`
201
+ >
202
+ : never
203
+
204
+ // An object brand never reduces with a string literal, so the diagnostic keeps the expected values.
205
+ type Expected<values> = { readonly [expected]: values }
206
+
207
+ declare const expected: unique symbol
208
+
209
+ type NumericNames<names> = names extends `${infer value extends number}`
210
+ ? value
211
+ : never
212
+
193
213
  /** Requires tokens for properties with configured values. */
194
214
  export type Tokens<
195
215
  value,