zyzz 0.0.20 → 0.0.22

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 (298) hide show
  1. package/Zyzz.podspec +16 -0
  2. package/dist/Style.d.ts +10 -1
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js +1 -5
  5. package/dist/Style.js.map +1 -1
  6. package/dist/babel/NativeJsx.d.ts.map +1 -1
  7. package/dist/babel/NativeJsx.js +204 -12
  8. package/dist/babel/NativeJsx.js.map +1 -1
  9. package/dist/babel/index.d.ts +6 -2
  10. package/dist/babel/index.d.ts.map +1 -1
  11. package/dist/babel/index.js +10 -1
  12. package/dist/babel/index.js.map +1 -1
  13. package/dist/compiler/Graph.d.ts +3 -0
  14. package/dist/compiler/Graph.d.ts.map +1 -1
  15. package/dist/compiler/Graph.js +108 -37
  16. package/dist/compiler/Graph.js.map +1 -1
  17. package/dist/compiler/Native.d.ts +6 -3
  18. package/dist/compiler/Native.d.ts.map +1 -1
  19. package/dist/compiler/Native.js +142 -24
  20. package/dist/compiler/Native.js.map +1 -1
  21. package/dist/compiler/Source.d.ts +6 -0
  22. package/dist/compiler/Source.d.ts.map +1 -1
  23. package/dist/compiler/Source.js +80 -11
  24. package/dist/compiler/Source.js.map +1 -1
  25. package/dist/compiler/Transform.d.ts +4 -1
  26. package/dist/compiler/Transform.d.ts.map +1 -1
  27. package/dist/compiler/Transform.js +32 -46
  28. package/dist/compiler/Transform.js.map +1 -1
  29. package/dist/compiler/internal/Catalogs.d.ts.map +1 -1
  30. package/dist/compiler/internal/Catalogs.js +6 -1
  31. package/dist/compiler/internal/Catalogs.js.map +1 -1
  32. package/dist/compiler/internal/Compositions.d.ts.map +1 -1
  33. package/dist/compiler/internal/Compositions.js +5 -0
  34. package/dist/compiler/internal/Compositions.js.map +1 -1
  35. package/dist/compiler/internal/Configurations.d.ts +2 -0
  36. package/dist/compiler/internal/Configurations.d.ts.map +1 -1
  37. package/dist/compiler/internal/Configurations.js +23 -4
  38. package/dist/compiler/internal/Configurations.js.map +1 -1
  39. package/dist/compiler/internal/Contract.d.ts +4 -1
  40. package/dist/compiler/internal/Contract.d.ts.map +1 -1
  41. package/dist/compiler/internal/Contract.js +97 -151
  42. package/dist/compiler/internal/Contract.js.map +1 -1
  43. package/dist/compiler/internal/Contributions.d.ts.map +1 -1
  44. package/dist/compiler/internal/Contributions.js +8 -4
  45. package/dist/compiler/internal/Contributions.js.map +1 -1
  46. package/dist/compiler/internal/Descriptors.d.ts.map +1 -1
  47. package/dist/compiler/internal/Descriptors.js +11 -2
  48. package/dist/compiler/internal/Descriptors.js.map +1 -1
  49. package/dist/compiler/internal/Dynamic.d.ts +1 -1
  50. package/dist/compiler/internal/Dynamic.d.ts.map +1 -1
  51. package/dist/compiler/internal/Dynamic.js +5 -5
  52. package/dist/compiler/internal/Dynamic.js.map +1 -1
  53. package/dist/compiler/internal/Names.d.ts +5 -0
  54. package/dist/compiler/internal/Names.d.ts.map +1 -0
  55. package/dist/compiler/internal/Names.js +37 -0
  56. package/dist/compiler/internal/Names.js.map +1 -0
  57. package/dist/compiler/internal/NativeBindings.d.ts +24 -26
  58. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  59. package/dist/compiler/internal/NativeBindings.js +61 -8
  60. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  61. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  62. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  63. package/dist/compiler/internal/NativeMedia.js +287 -0
  64. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  65. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  66. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  67. package/dist/compiler/internal/NativeVars.js +157 -0
  68. package/dist/compiler/internal/NativeVars.js.map +1 -0
  69. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  70. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  71. package/dist/compiler/internal/PackedStyles.js +2 -0
  72. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  73. package/dist/compiler/internal/Selectors.d.ts +1 -1
  74. package/dist/compiler/internal/Selectors.d.ts.map +1 -1
  75. package/dist/compiler/internal/Selectors.js +5 -2
  76. package/dist/compiler/internal/Selectors.js.map +1 -1
  77. package/dist/compiler/internal/Stylesheets.js +2 -2
  78. package/dist/compiler/internal/Stylesheets.js.map +1 -1
  79. package/dist/compiler/internal/Themes.d.ts +22 -1
  80. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  81. package/dist/compiler/internal/Themes.js +120 -4
  82. package/dist/compiler/internal/Themes.js.map +1 -1
  83. package/dist/compiler/internal/Vars.d.ts +3 -1
  84. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  85. package/dist/compiler/internal/Vars.js +14 -3
  86. package/dist/compiler/internal/Vars.js.map +1 -1
  87. package/dist/default.d.ts +4 -4
  88. package/dist/default.d.ts.map +1 -1
  89. package/dist/default.js +9 -9
  90. package/dist/default.js.map +2 -2
  91. package/dist/default.js.zyzz.json +1 -1
  92. package/dist/internal/Authoring.d.ts.map +1 -1
  93. package/dist/internal/Authoring.js +8 -3
  94. package/dist/internal/Authoring.js.map +1 -1
  95. package/dist/internal/Binding.d.ts +4 -0
  96. package/dist/internal/Binding.d.ts.map +1 -1
  97. package/dist/internal/Binding.js +2 -0
  98. package/dist/internal/Binding.js.map +1 -1
  99. package/dist/internal/Completion.d.ts +1 -1
  100. package/dist/internal/Completion.d.ts.map +1 -1
  101. package/dist/internal/Configuration.d.ts +2 -2
  102. package/dist/internal/Configuration.d.ts.map +1 -1
  103. package/dist/internal/Configuration.js +4 -2
  104. package/dist/internal/Configuration.js.map +1 -1
  105. package/dist/internal/Identity.d.ts +3 -3
  106. package/dist/internal/Identity.d.ts.map +1 -1
  107. package/dist/internal/Identity.js +11 -9
  108. package/dist/internal/Identity.js.map +1 -1
  109. package/dist/internal/Targets.d.ts.map +1 -1
  110. package/dist/internal/Targets.js +2 -0
  111. package/dist/internal/Targets.js.map +1 -1
  112. package/dist/internal/Theme.js +2 -2
  113. package/dist/internal/Theme.js.map +1 -1
  114. package/dist/internal/Token.d.ts +3 -1
  115. package/dist/internal/Token.d.ts.map +1 -1
  116. package/dist/internal/Token.js +9 -2
  117. package/dist/internal/Token.js.map +1 -1
  118. package/dist/internal/Value.d.ts +7 -0
  119. package/dist/internal/Value.d.ts.map +1 -1
  120. package/dist/internal/Value.js.map +1 -1
  121. package/dist/internal/VariableSets.js +1 -1
  122. package/dist/internal/VariableSets.js.map +1 -1
  123. package/dist/metro/Graph.d.ts +22 -7
  124. package/dist/metro/Graph.d.ts.map +1 -1
  125. package/dist/metro/Graph.js +179 -42
  126. package/dist/metro/Graph.js.map +1 -1
  127. package/dist/metro/Watch.d.ts +28 -4
  128. package/dist/metro/Watch.d.ts.map +1 -1
  129. package/dist/metro/Watch.js +39 -7
  130. package/dist/metro/Watch.js.map +1 -1
  131. package/dist/metro/index.d.ts +15 -4
  132. package/dist/metro/index.d.ts.map +1 -1
  133. package/dist/metro/index.js +27 -0
  134. package/dist/metro/index.js.map +1 -1
  135. package/dist/metro/transformer.d.ts +5 -1
  136. package/dist/metro/transformer.d.ts.map +1 -1
  137. package/dist/metro/transformer.js +5 -3
  138. package/dist/metro/transformer.js.map +1 -1
  139. package/dist/node/Host.d.ts.map +1 -1
  140. package/dist/node/Host.js +2 -0
  141. package/dist/node/Host.js.map +1 -1
  142. package/dist/react-native/StyleSheet.d.ts +3 -2
  143. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  144. package/dist/react-native/StyleSheet.js +17 -34
  145. package/dist/react-native/StyleSheet.js.map +1 -1
  146. package/dist/react-native/index.d.ts +1 -1
  147. package/dist/react-native/index.d.ts.map +1 -1
  148. package/dist/react-native/index.js +1 -1
  149. package/dist/react-native/index.js.map +1 -1
  150. package/dist/react-native/index.native.d.ts +5 -0
  151. package/dist/react-native/index.native.js +20 -0
  152. package/dist/react-native/index.native.js.map +7 -0
  153. package/dist/react-native/internal/Calculation.d.ts +7 -0
  154. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  155. package/dist/react-native/internal/Calculation.js +134 -0
  156. package/dist/react-native/internal/Calculation.js.map +1 -0
  157. package/dist/react-native/internal/Device.js +191 -0
  158. package/dist/react-native/internal/Device.js.map +7 -0
  159. package/dist/react-native/internal/NativeZyzz.js +7 -0
  160. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  161. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  162. package/dist/react-native/internal/Scalar.js +9 -2
  163. package/dist/react-native/internal/Scalar.js.map +1 -1
  164. package/dist/react-native/internal/Store.d.ts +32 -0
  165. package/dist/react-native/internal/Store.d.ts.map +1 -0
  166. package/dist/react-native/internal/Store.js +57 -0
  167. package/dist/react-native/internal/Store.js.map +1 -0
  168. package/dist/react-native/internal/Tokens.d.ts +21 -0
  169. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  170. package/dist/react-native/internal/Tokens.js +58 -0
  171. package/dist/react-native/internal/Tokens.js.map +1 -0
  172. package/dist/react-native/internal/Viewport.d.ts +6 -0
  173. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  174. package/dist/react-native/internal/Viewport.js +4 -0
  175. package/dist/react-native/internal/Viewport.js.map +1 -0
  176. package/dist/react-native/react.d.ts +74 -11
  177. package/dist/react-native/react.d.ts.map +1 -1
  178. package/dist/react-native/react.js +111 -12
  179. package/dist/react-native/react.js.map +1 -1
  180. package/dist/react-native/reanimated.d.ts +39 -0
  181. package/dist/react-native/reanimated.d.ts.map +1 -0
  182. package/dist/react-native/reanimated.js +76 -0
  183. package/dist/react-native/reanimated.js.map +1 -0
  184. package/dist/runtime/NativeContext.d.ts +10 -1
  185. package/dist/runtime/NativeContext.d.ts.map +1 -1
  186. package/dist/runtime/NativeContext.js +67 -14
  187. package/dist/runtime/NativeContext.js.map +1 -1
  188. package/dist/runtime/NativeDynamic.d.ts +3 -2
  189. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  190. package/dist/runtime/NativeDynamic.js +32 -7
  191. package/dist/runtime/NativeDynamic.js.map +1 -1
  192. package/dist/runtime/NativeStatic.d.ts +23 -0
  193. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  194. package/dist/runtime/NativeStatic.js +106 -0
  195. package/dist/runtime/NativeStatic.js.map +1 -0
  196. package/dist/runtime/NativeVars.d.ts +53 -0
  197. package/dist/runtime/NativeVars.d.ts.map +1 -0
  198. package/dist/runtime/NativeVars.js +77 -0
  199. package/dist/runtime/NativeVars.js.map +1 -0
  200. package/dist/runtime/index.d.ts +4 -0
  201. package/dist/runtime/index.d.ts.map +1 -1
  202. package/dist/runtime/index.js +4 -0
  203. package/dist/runtime/index.js.map +1 -1
  204. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  205. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  206. package/dist/runtime/internal/NativeMedia.js +36 -0
  207. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  208. package/dist/variable.js +1 -1
  209. package/dist/variable.js.map +1 -1
  210. package/dist/vite/index.d.ts.map +1 -1
  211. package/dist/vite/index.js +43 -47
  212. package/dist/vite/index.js.map +1 -1
  213. package/dist/web/Css.d.ts +7 -2
  214. package/dist/web/Css.d.ts.map +1 -1
  215. package/dist/web/Css.js +76 -57
  216. package/dist/web/Css.js.map +1 -1
  217. package/dist/web/internal/ClassName.d.ts +13 -2
  218. package/dist/web/internal/ClassName.d.ts.map +1 -1
  219. package/dist/web/internal/ClassName.js +17 -30
  220. package/dist/web/internal/ClassName.js.map +1 -1
  221. package/dist/web/internal/Themes.d.ts +3 -2
  222. package/dist/web/internal/Themes.d.ts.map +1 -1
  223. package/dist/web/internal/Themes.js +54 -28
  224. package/dist/web/internal/Themes.js.map +1 -1
  225. package/dist/web/page.d.ts +2 -0
  226. package/dist/web/page.d.ts.map +1 -1
  227. package/native/NativeZyzz.cpp +215 -0
  228. package/native/NativeZyzz.h +52 -0
  229. package/native/ZyzzSpecJSI.h +65 -0
  230. package/native/android/CMakeLists.txt +8 -0
  231. package/native/android/ZyzzSpec-generated.cpp +50 -0
  232. package/native/android/ZyzzSpec.h +31 -0
  233. package/native/ios/ZyzzModuleProvider.mm +13 -0
  234. package/package.json +39 -1
  235. package/react-native.config.ts +14 -0
  236. package/src/Style.ts +15 -9
  237. package/src/babel/NativeJsx.ts +305 -28
  238. package/src/babel/index.ts +21 -3
  239. package/src/compiler/Graph.ts +135 -46
  240. package/src/compiler/Native.ts +158 -26
  241. package/src/compiler/Source.ts +115 -10
  242. package/src/compiler/Transform.ts +42 -60
  243. package/src/compiler/internal/Catalogs.ts +9 -1
  244. package/src/compiler/internal/Compositions.ts +7 -0
  245. package/src/compiler/internal/Configurations.ts +27 -4
  246. package/src/compiler/internal/Contract.ts +130 -260
  247. package/src/compiler/internal/Contributions.ts +8 -6
  248. package/src/compiler/internal/Descriptors.ts +12 -8
  249. package/src/compiler/internal/Dynamic.ts +14 -9
  250. package/src/compiler/internal/Names.ts +48 -0
  251. package/src/compiler/internal/NativeBindings.ts +98 -15
  252. package/src/compiler/internal/NativeMedia.ts +342 -0
  253. package/src/compiler/internal/NativeVars.ts +228 -0
  254. package/src/compiler/internal/PackedStyles.ts +4 -0
  255. package/src/compiler/internal/Selectors.ts +6 -1
  256. package/src/compiler/internal/Stylesheets.ts +2 -2
  257. package/src/compiler/internal/Themes.ts +193 -3
  258. package/src/compiler/internal/Vars.ts +21 -2
  259. package/src/default.ts +9 -10
  260. package/src/internal/Authoring.ts +8 -2
  261. package/src/internal/Binding.ts +5 -0
  262. package/src/internal/Completion.ts +6 -3
  263. package/src/internal/Configuration.ts +7 -4
  264. package/src/internal/Identity.ts +14 -12
  265. package/src/internal/Targets.ts +2 -0
  266. package/src/internal/Theme.ts +2 -2
  267. package/src/internal/Token.ts +11 -1
  268. package/src/internal/Value.ts +20 -0
  269. package/src/internal/VariableSets.ts +1 -1
  270. package/src/metro/Graph.ts +235 -50
  271. package/src/metro/Watch.ts +95 -10
  272. package/src/metro/index.ts +62 -4
  273. package/src/metro/transformer.ts +21 -11
  274. package/src/node/Host.ts +3 -0
  275. package/src/react-native/StyleSheet.ts +28 -44
  276. package/src/react-native/index.native.ts +10 -0
  277. package/src/react-native/index.ts +1 -1
  278. package/src/react-native/internal/Calculation.ts +159 -0
  279. package/src/react-native/internal/Device.ts +231 -0
  280. package/src/react-native/internal/NativeZyzz.ts +20 -0
  281. package/src/react-native/internal/Scalar.ts +13 -2
  282. package/src/react-native/internal/Store.ts +78 -0
  283. package/src/react-native/internal/Tokens.ts +85 -0
  284. package/src/react-native/internal/Viewport.ts +8 -0
  285. package/src/react-native/react.ts +211 -19
  286. package/src/react-native/reanimated.ts +162 -0
  287. package/src/runtime/NativeContext.ts +88 -17
  288. package/src/runtime/NativeDynamic.ts +35 -10
  289. package/src/runtime/NativeStatic.ts +145 -0
  290. package/src/runtime/NativeVars.ts +189 -0
  291. package/src/runtime/index.ts +6 -0
  292. package/src/runtime/internal/NativeMedia.ts +63 -0
  293. package/src/variable.ts +1 -1
  294. package/src/vite/index.ts +52 -60
  295. package/src/web/Css.ts +123 -74
  296. package/src/web/internal/ClassName.ts +31 -36
  297. package/src/web/internal/Themes.ts +62 -39
  298. package/src/web/page.ts +2 -0
@@ -1,13 +1,32 @@
1
1
  /** Lowers target-neutral callback declarations to ordered native binding data. @module */
2
- import type * as Recipe from '../../internal/Recipe.js'
3
2
  import * as Binding from '../../internal/Binding.js'
4
- import * as Token from '../../internal/Token.js'
5
- import type * as Style from '../../Style.js'
6
- import * as StyleSheet from '../../react-native/StyleSheet.js'
7
- import * as Scalar from '../../react-native/internal/Scalar.js'
8
- import type * as Runtime from '../../runtime/NativeDynamic.js'
9
3
  import type * as Native from '../Native.js'
4
+ import type * as Recipe from '../../internal/Recipe.js'
5
+ import type * as Runtime from '../../runtime/NativeDynamic.js'
6
+ import * as Scalar from '../../react-native/internal/Scalar.js'
10
7
  import type * as Source from '../Source.js'
8
+ import type * as Static from '../../runtime/NativeStatic.js'
9
+ import type * as Style from '../../Style.js'
10
+ import * as StyleSheet from '../../react-native/StyleSheet.js'
11
+ import * as Token from '../../internal/Token.js'
12
+ import * as Tokens from '../../react-native/internal/Tokens.js'
13
+
14
+ type Step = Runtime.Program['rules'][number]['steps'][number]
15
+ type PreparedStep =
16
+ | Extract<Step, { readonly style: string }>
17
+ | {
18
+ readonly property: keyof typeof Scalar.properties
19
+ readonly parts: readonly (
20
+ | string
21
+ | number
22
+ | Token.Reference
23
+ | { readonly slot: string; readonly number?: true }
24
+ )[]
25
+ }
26
+ type PreparedRule = {
27
+ readonly matches: Runtime.Program['rules'][number]['matches']
28
+ readonly steps: readonly PreparedStep[]
29
+ }
11
30
 
12
31
  /** Compiles static fragments and retains scalar assignments in authored order. */
13
32
  export function compile(
@@ -16,8 +35,36 @@ export function compile(
16
35
  options: Native.compile.Options,
17
36
  prepared = prepare(recipe, call, options),
18
37
  ): Runtime.create.Options {
38
+ const selected = options.vars?.[options.set ?? 'default']
39
+ const metadata =
40
+ selected &&
41
+ (Object.getOwnPropertyDescriptor(selected, Token.definition)?.value as
42
+ | Token.Metadata
43
+ | undefined)
19
44
  return {
20
45
  ...prepared,
46
+ program: {
47
+ slots: prepared.program.slots,
48
+ rules: prepared.program.rules.map((rule) => ({
49
+ ...rule,
50
+ steps: rule.steps.map((step) =>
51
+ 'style' in step
52
+ ? step
53
+ : {
54
+ ...step,
55
+ parts: step.parts.map((part) =>
56
+ Token.is(part)
57
+ ? Tokens.resolve(part, {
58
+ ...options,
59
+ metadata,
60
+ resolution: { schemeIndependent: true },
61
+ })
62
+ : part,
63
+ ),
64
+ },
65
+ ),
66
+ })),
67
+ },
21
68
  styles: StyleSheet.select(prepared.styles, {
22
69
  set: options.set ?? 'default',
23
70
  colorScheme: options.colorScheme,
@@ -25,6 +72,39 @@ export function compile(
25
72
  }
26
73
  }
27
74
 
75
+ /** Removes dynamic instructions and resolves absolute line heights before publication. */
76
+ export function finite(options: Runtime.create.Options): Static.create.Options {
77
+ const styles = { ...options.styles }
78
+ return {
79
+ axes: options.axes,
80
+ defaults: options.defaults,
81
+ rules: options.program.rules.map((rule) => ({
82
+ matches: rule.matches,
83
+ steps: rule.steps.map((step) => {
84
+ if ('style' in step) return step.style
85
+ if (
86
+ step.property !== 'lineHeight' ||
87
+ !step.parts.length ||
88
+ step.parts.some((part) => typeof part === 'object')
89
+ )
90
+ throw new Error(
91
+ 'Static native recipes cannot contain runtime bindings.',
92
+ )
93
+ const value = (
94
+ step.parts.length === 1 ? step.parts[0]! : step.parts.join('')
95
+ ) as string | number
96
+ if (typeof value === 'number') return value
97
+ const name = String(Object.keys(styles).length)
98
+ styles[name] = {
99
+ lineHeight: Scalar.length(value, options, false, ['lineHeight']),
100
+ }
101
+ return name
102
+ }),
103
+ })),
104
+ styles,
105
+ }
106
+ }
107
+
28
108
  /** Prepares immutable programs and all set/scheme tables before selection. */
29
109
  export function prepare(
30
110
  recipe: Recipe.Definition,
@@ -41,15 +121,10 @@ export function prepare(
41
121
  ),
42
122
  ])
43
123
  const fragments: Style.NamedStyle[] = []
44
- const rules: Runtime.Program['rules'][number][] = []
45
- let count = 1
46
- for (const choices of Object.values(recipe.axes)) count *= choices.length + 1
47
- if (count > 256)
48
- throw new Error(
49
- 'Native recipes support at most 256 selections, including null choices.',
50
- )
124
+ const rules: PreparedRule[] = []
125
+
51
126
  for (const rule of recipe.rules) {
52
- const steps: Runtime.Program['rules'][number]['steps'][number][] = []
127
+ const steps: PreparedStep[] = []
53
128
  function fragment(style: Style.NamedStyle) {
54
129
  const name = String(fragments.length)
55
130
  fragments.push({ ...style, name })
@@ -94,14 +169,22 @@ export function prepare(
94
169
  )
95
170
  const values = parts.map((part) => {
96
171
  if (typeof part === 'string' || typeof part === 'number') return part
172
+ if (Token.is(part)) return part
97
173
  if (!Binding.is(part) || !available.has(part.name))
98
174
  throw new Error(
99
175
  'Native bindings require declared runtime scalar fields.',
100
176
  )
101
- return { slot: part.name }
177
+ return {
178
+ slot: part.name,
179
+ ...(part.type === 'number' ? { number: true as const } : {}),
180
+ }
102
181
  })
182
+ const calculation = values.some(
183
+ (value) => typeof value === 'string' && /(?:^|\s)calc\(/i.test(value),
184
+ )
103
185
  if (
104
186
  values.length > 1 &&
187
+ !calculation &&
105
188
  !(
106
189
  values.length === 2 &&
107
190
  typeof values[0] === 'object' &&
@@ -0,0 +1,342 @@
1
+ /** Lowers native media rules and conditional tokens to bounded static alternatives. @module */
2
+ import * as CssTree from 'css-tree'
3
+ import type * as Media from '../../runtime/internal/NativeMedia.js'
4
+ import * as Scalar from '../../react-native/internal/Scalar.js'
5
+ import type * as Style from '../../Style.js'
6
+ import type * as StyleSheet from '../../react-native/StyleSheet.js'
7
+ import * as Token from '../../internal/Token.js'
8
+
9
+ /** Retains authored precedence while selecting each possible media outcome. */
10
+ export function prepare<variables extends StyleSheet.compile.Options['vars']>(
11
+ styles: readonly Style.NamedStyle[] | undefined,
12
+ vars: variables,
13
+ options: Scalar.Options,
14
+ ) {
15
+ const catalog: StyleSheet.compile.Options['vars'] = vars
16
+ const expressions = new Map<string, number>()
17
+ const names = new Map<string, number>()
18
+ const queries: Media.Query[] = []
19
+ const visited = new Set<object>()
20
+
21
+ function add(name: string) {
22
+ if (names.has(name)) return
23
+
24
+ const query = parse(name, options)
25
+ const expression = JSON.stringify(query)
26
+ let index = expressions.get(expression)
27
+ if (index === undefined) {
28
+ index = queries.length
29
+ if (index === 8)
30
+ throw new Error(
31
+ 'Native media supports at most eight distinct conditions per definition.',
32
+ )
33
+
34
+ queries.push(query)
35
+ expressions.set(expression, index)
36
+ }
37
+
38
+ names.set(name, index)
39
+ }
40
+
41
+ function values(value: unknown) {
42
+ if (!value || typeof value !== 'object' || visited.has(value)) return
43
+
44
+ visited.add(value)
45
+ if (Token.is(value)) {
46
+ values(value.value)
47
+ for (const theme of Object.values(catalog ?? {})) {
48
+ const metadata = theme[Token.definition]
49
+ if (
50
+ metadata.contract === value.contract ||
51
+ (metadata.contract[Token.identity] !== undefined &&
52
+ metadata.contract[Token.identity] ===
53
+ value.contract[Token.identity])
54
+ )
55
+ values(metadata.values[value.path])
56
+ }
57
+ } else if (Token.isExpression(value)) value.parts.forEach(values)
58
+ else if ('default' in value) {
59
+ for (const [name, entry] of Object.entries(value)) {
60
+ if (name !== 'default') {
61
+ // Unsupported variable leaves keep their deferred read diagnostic.
62
+ try {
63
+ add(name)
64
+ } catch (error) {
65
+ if (!(error instanceof UnsupportedError)) throw error
66
+ }
67
+ }
68
+ values(entry)
69
+ }
70
+ } else if ('light' in value && 'dark' in value) {
71
+ values(value.light)
72
+ values(value.dark)
73
+ }
74
+ }
75
+
76
+ function visit(style: Style.NamedStyle) {
77
+ for (const declaration of style.declarations) values(declaration.value)
78
+ for (const rule of style.rules ?? []) {
79
+ if (rule.condition?.startsWith('@media')) add(rule.condition)
80
+ visit(rule.style)
81
+ }
82
+ }
83
+
84
+ styles?.forEach(visit)
85
+ if (!styles)
86
+ for (const theme of Object.values(catalog ?? {})) {
87
+ const metadata = theme[Token.definition]
88
+ Object.values(metadata.values).forEach(values)
89
+ }
90
+ if (!queries.length) return
91
+
92
+ return {
93
+ queries,
94
+ select(selection: string) {
95
+ const active = new Set<object>()
96
+ function value<entry>(entry: entry): entry {
97
+ if (!entry || typeof entry !== 'object') return entry
98
+ if (active.has(entry))
99
+ throw new Error('Native token aliases must not form a cycle.')
100
+
101
+ active.add(entry)
102
+ try {
103
+ if (Token.is(entry))
104
+ return Token.create({
105
+ ...entry,
106
+ value: value(entry.value),
107
+ }) as entry
108
+ if (Token.isExpression(entry))
109
+ return { ...entry, parts: entry.parts.map(value) } as entry
110
+ if ('default' in entry) {
111
+ const branches = Object.entries(entry)
112
+ if (
113
+ branches.some(([name]) => name !== 'default' && !names.has(name))
114
+ )
115
+ return Object.fromEntries(
116
+ branches
117
+ .filter(([name]) => name === 'default' || !names.has(name))
118
+ .map(([name, branch]) => [name, value(branch)]),
119
+ ) as entry
120
+
121
+ let selected = entry.default
122
+ for (const [name, branch] of branches)
123
+ if (name !== 'default' && selection[names.get(name)!] === '1')
124
+ selected = branch
125
+
126
+ return value(selected) as entry
127
+ }
128
+ if ('light' in entry && 'dark' in entry)
129
+ return {
130
+ ...entry,
131
+ dark: value(entry.dark),
132
+ light: value(entry.light),
133
+ }
134
+ return entry
135
+ } finally {
136
+ active.delete(entry)
137
+ }
138
+ }
139
+
140
+ function style(entry: Style.NamedStyle): Style.NamedStyle {
141
+ return {
142
+ ...entry,
143
+ declarations: entry.declarations.map((declaration) => ({
144
+ ...declaration,
145
+ value: value(declaration.value),
146
+ })),
147
+ ...(entry.rules
148
+ ? {
149
+ rules: entry.rules.flatMap((rule) => {
150
+ const index =
151
+ rule.condition === undefined
152
+ ? undefined
153
+ : names.get(rule.condition)
154
+ if (index !== undefined && selection[index] !== '1') return []
155
+
156
+ return [
157
+ {
158
+ ...(index === undefined ? rule : {}),
159
+ style: style(rule.style),
160
+ },
161
+ ]
162
+ }),
163
+ }
164
+ : {}),
165
+ }
166
+ }
167
+
168
+ return {
169
+ style,
170
+ vars:
171
+ catalog &&
172
+ (Object.fromEntries(
173
+ Object.entries(catalog).map(([name, theme]) => {
174
+ const metadata = theme[Token.definition]
175
+ return [
176
+ name,
177
+ Object.defineProperty({}, Token.definition, {
178
+ value: {
179
+ ...metadata,
180
+ values: Object.fromEntries(
181
+ Object.entries(metadata.values).map(([path, entry]) => [
182
+ path,
183
+ value(entry),
184
+ ]),
185
+ ),
186
+ },
187
+ }) as typeof theme,
188
+ ]
189
+ }),
190
+ ) as variables),
191
+ }
192
+ },
193
+ selections: Array.from({ length: 2 ** queries.length }, (_entry, index) =>
194
+ index.toString(2).padStart(queries.length, '0'),
195
+ ),
196
+ }
197
+ }
198
+
199
+ class UnsupportedError extends Error {
200
+ override name = 'NativeMedia.UnsupportedError'
201
+ }
202
+
203
+ function parse(input: string, options: Scalar.Options): Media.Query {
204
+ if (!input.startsWith('@media'))
205
+ throw new UnsupportedError(
206
+ 'Native conditional variables require media queries.',
207
+ )
208
+ const parsed = CssTree.parse(input.slice(6), { context: 'mediaQueryList' })
209
+ let count = 0
210
+ function read(node: CssTree.CssNode): Media.Query {
211
+ if (++count > 128)
212
+ throw new Error('Native media queries exceed 128 syntax nodes.')
213
+ if (node.type === 'MediaQueryList') {
214
+ const queries = node.children.toArray().map(read)
215
+ return queries.length === 1 ? queries[0]! : { kind: 'or', queries }
216
+ }
217
+ if (node.type === 'MediaQuery') {
218
+ if (
219
+ (node.mediaType &&
220
+ !['all', 'screen'].includes(node.mediaType.toLowerCase())) ||
221
+ node.modifier?.toLowerCase() === 'not'
222
+ )
223
+ throw new UnsupportedError(
224
+ 'Native media types support all or screen without negation.',
225
+ )
226
+ return node.condition
227
+ ? read(node.condition)
228
+ : { kind: 'compare', left: 0, operator: '=', right: 0 }
229
+ }
230
+ if (node.type === 'Condition') {
231
+ const parts = node.children.toArray()
232
+ if (parts.length === 1) return read(parts[0]!)
233
+ if (
234
+ parts.length === 2 &&
235
+ parts[0]?.type === 'Identifier' &&
236
+ parts[0].name.toLowerCase() === 'not'
237
+ )
238
+ return { kind: 'not', query: read(parts[1]!) }
239
+ const operator =
240
+ parts[1]?.type === 'Identifier'
241
+ ? parts[1].name.toLowerCase()
242
+ : undefined
243
+ if (
244
+ (operator !== 'and' && operator !== 'or') ||
245
+ parts.length % 2 !== 1 ||
246
+ parts.some(
247
+ (part, index) =>
248
+ index % 2 === 1 &&
249
+ (part.type !== 'Identifier' ||
250
+ part.name.toLowerCase() !== operator),
251
+ )
252
+ )
253
+ throw new UnsupportedError('Unsupported native media condition.')
254
+ return {
255
+ kind: operator,
256
+ queries: parts.filter((_part, index) => index % 2 === 0).map(read),
257
+ }
258
+ }
259
+ function operand(entry: CssTree.CssNode): number | 'height' | 'width' {
260
+ if (
261
+ entry.type === 'Identifier' &&
262
+ (entry.name.toLowerCase() === 'height' ||
263
+ entry.name.toLowerCase() === 'width')
264
+ )
265
+ return entry.name.toLowerCase() as 'height' | 'width'
266
+ if (entry.type === 'Dimension') {
267
+ try {
268
+ return Scalar.length(
269
+ `${entry.value}${entry.unit.toLowerCase()}`,
270
+ options,
271
+ false,
272
+ [],
273
+ )
274
+ } catch (error) {
275
+ throw new UnsupportedError((error as Error).message)
276
+ }
277
+ }
278
+ if (entry.type === 'Number' && Number(entry.value) === 0) return 0
279
+ throw new UnsupportedError(
280
+ 'Native media ranges require width/height and px/rem thresholds.',
281
+ )
282
+ }
283
+ function comparison(
284
+ left: CssTree.CssNode,
285
+ operator: string | null,
286
+ right: CssTree.CssNode,
287
+ ): Media.Query {
288
+ if (!operator || !['<', '<=', '=', '>', '>='].includes(operator))
289
+ throw new UnsupportedError('Unsupported native media comparison.')
290
+ return {
291
+ kind: 'compare',
292
+ left: operand(left),
293
+ operator: operator as '<' | '<=' | '=' | '>' | '>=',
294
+ right: operand(right),
295
+ }
296
+ }
297
+ if (node.type === 'FeatureRange') {
298
+ const first = comparison(node.left, node.leftComparison, node.middle)
299
+ return node.right
300
+ ? {
301
+ kind: 'and',
302
+ queries: [
303
+ first,
304
+ comparison(node.middle, node.rightComparison, node.right),
305
+ ],
306
+ }
307
+ : first
308
+ }
309
+ if (node.type === 'Feature') {
310
+ const name = node.name.toLowerCase()
311
+ if (
312
+ name === 'orientation' &&
313
+ node.value?.type === 'Identifier' &&
314
+ ['landscape', 'portrait'].includes(node.value.name.toLowerCase())
315
+ )
316
+ return {
317
+ kind: 'compare',
318
+ left: 'width',
319
+ operator: node.value.name.toLowerCase() === 'landscape' ? '>' : '<=',
320
+ right: 'height',
321
+ }
322
+ const match = /^(?:(min|max)-)?(height|width)$/.exec(name)
323
+ if (match)
324
+ return {
325
+ kind: 'compare',
326
+ left: match[2] as 'height' | 'width',
327
+ operator:
328
+ match[1] === 'min'
329
+ ? '>='
330
+ : match[1] === 'max'
331
+ ? '<='
332
+ : node.value
333
+ ? '='
334
+ : '>',
335
+ right: node.value ? operand(node.value) : 0,
336
+ }
337
+ }
338
+ throw new UnsupportedError('Unsupported native media feature.')
339
+ }
340
+
341
+ return read(parsed)
342
+ }