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
@@ -7,6 +7,8 @@ import type * as Native from '../internal/NativeProperties.js'
7
7
  import * as Values from './internal/Values.js'
8
8
  import * as Color from './internal/Color.js'
9
9
  import * as Scalar from './internal/Scalar.js'
10
+ import * as Calculation from './internal/Calculation.js'
11
+ import * as Tokens from './internal/Tokens.js'
10
12
 
11
13
  const properties = Scalar.properties
12
14
 
@@ -207,6 +209,7 @@ export function compile<
207
209
  scheme,
208
210
  path,
209
211
  resolution,
212
+ options,
210
213
  )
211
214
  if (property === 'lineHeight') {
212
215
  lineHeight = value
@@ -266,9 +269,7 @@ export function compile<
266
269
  const converted = convert(kind, value, options, path)
267
270
  if (property === 'padding' || property === 'margin') {
268
271
  const values =
269
- typeof value === 'string'
270
- ? value.trim().split(/\s+/)
271
- : [value]
272
+ typeof value === 'string' ? Calculation.parts(value) : [value]
272
273
  const [top, right = top, bottom = top, left = right] =
273
274
  values.map((value) =>
274
275
  length(value, options, property === 'margin', path),
@@ -277,6 +278,14 @@ export function compile<
277
278
  output[`${property}Right`] = right!
278
279
  output[`${property}Bottom`] = bottom!
279
280
  output[`${property}Left`] = left!
281
+ } else if (property === 'flex') {
282
+ Object.assign(output, Scalar.flex(value, options, path))
283
+ } else if (property === 'inset') {
284
+ const values =
285
+ typeof value === 'string' ? Calculation.parts(value) : [value]
286
+ const [top, right = top, bottom = top, left = right] =
287
+ values.map((value) => convert('offset', value, options, path))
288
+ Object.assign(output, { top, right, bottom, left })
280
289
  } else if (
281
290
  property === 'borderColor' ||
282
291
  property === 'borderWidth'
@@ -606,12 +615,32 @@ type Merged<style> = {
606
615
  type StyleArray<style extends object> = Array<
607
616
  style | Falsy | readonly (style | Falsy)[] | StyleArray<style>
608
617
  >
609
- type Length = 0 | '0' | `${number}px` | `${number}rem`
610
- type Box =
611
- | Length
612
- | `${Length} ${Length}`
613
- | `${Length} ${Length} ${Length}`
614
- | `${Length} ${Length} ${Length} ${Length}`
618
+ type Length =
619
+ | 0
620
+ | '0'
621
+ | `${number}px`
622
+ | `${number}rem`
623
+ | `${'c' | 'C'}${'a' | 'A'}${'l' | 'L'}${'c' | 'C'}(${string})`
624
+ type Box<item extends string | number = Length> =
625
+ | item
626
+ | `${item} ${item}`
627
+ | `${item} ${item} ${item}`
628
+ | Box4<item>
629
+ // Distribute the first item to keep case-insensitive calculations below the template expansion limit.
630
+ type Box4<
631
+ item extends string | number,
632
+ first extends string | number = item,
633
+ > = first extends unknown ? `${first} ${item} ${item} ${item}` : never
634
+ type FlexBasis = Length | `${number}%` | 'auto'
635
+ type Flex =
636
+ | number
637
+ | 'initial'
638
+ | 'none'
639
+ | FlexBasis
640
+ | `${number} ${number | FlexBasis}`
641
+ | `${FlexBasis} ${number}`
642
+ | `${number} ${number} ${FlexBasis}`
643
+ | `${FlexBasis} ${number} ${number}`
615
644
  type Weight =
616
645
  | 'bold'
617
646
  | 'normal'
@@ -656,7 +685,11 @@ type Atom<kind> = kind extends readonly string[]
656
685
  ? Length | number
657
686
  : kind extends 'box' | 'boxSigned'
658
687
  ? Box
659
- : Length
688
+ : kind extends 'boxOffset'
689
+ ? Box<Length | `${number}%`>
690
+ : kind extends 'flex'
691
+ ? Flex
692
+ : Length
660
693
 
661
694
  type Reference<property extends keyof typeof properties> = {
662
695
  [group in Token.Group]: property extends Token.Properties<group>
@@ -757,50 +790,24 @@ function resolve(
757
790
  scheme: ColorScheme,
758
791
  path: readonly string[],
759
792
  resolution: { schemeIndependent: boolean },
793
+ options: compile.Options,
760
794
  ): number | string {
761
- if (typeof value === 'number' || typeof value === 'string') return value
762
- if (
763
- !Token.is(value) ||
764
- Object.getOwnPropertyDescriptor(value, Token.web)?.value
765
- )
795
+ try {
796
+ return Tokens.resolve(value, {
797
+ ...options,
798
+ metadata,
799
+ colorScheme: scheme,
800
+ resolution,
801
+ })
802
+ } catch (error) {
766
803
  fail(
767
- 'unsupported_feature',
768
- 'Web variables, expressions, and dynamic bindings are not native scalar tokens.',
804
+ error instanceof Tokens.UnsupportedError
805
+ ? 'unsupported_feature'
806
+ : 'unsupported_value',
807
+ (error as Error).message,
769
808
  path,
770
809
  )
771
- const shared =
772
- metadata &&
773
- (metadata.contract === value.contract ||
774
- (metadata.contract[Token.identity] !== undefined &&
775
- metadata.contract[Token.identity] === value.contract[Token.identity]))
776
- let resolved = shared
777
- ? Object.hasOwn(metadata.values, value.path)
778
- ? metadata.values[value.path]
779
- : undefined
780
- : value.value
781
- if (resolved === undefined)
782
- fail('unsupported_value', 'Theme is missing a live token.', path)
783
- while (typeof resolved === 'object') {
784
- if (Token.is(resolved)) {
785
- resolved = resolved.value
786
- continue
787
- }
788
- if (Token.isExpression(resolved))
789
- fail(
790
- 'unsupported_feature',
791
- 'Composed variables require a web target.',
792
- path,
793
- )
794
- if ('default' in resolved)
795
- fail(
796
- 'unsupported_feature',
797
- 'Media-conditioned variables require a web target.',
798
- path,
799
- )
800
- if (resolved.light !== resolved.dark) resolution.schemeIndependent = false
801
- resolved = resolved[scheme]
802
810
  }
803
- return resolved
804
811
  }
805
812
 
806
813
  type TransformValues = {
@@ -0,0 +1,10 @@
1
+ /** Native authoring and React subscriptions with automatic device dimensions. @module */
2
+ export * as Host from './Host.js'
3
+ export * as StyleSheet from './StyleSheet.js'
4
+ export * as Variants from './Variants.js'
5
+ export {
6
+ defineConfig,
7
+ useStyles,
8
+ useVars,
9
+ withStyles,
10
+ } from './internal/Device.js'
@@ -1,4 +1,4 @@
1
- /** Native compilation, precompiled selection, and explicit host lifecycle. @module */
1
+ /** Native compilation and explicit host lifecycle. @module */
2
2
  /** Explicit native context and adapter lifecycle. */
3
3
  export * as Host from './Host.js'
4
4
  /** Native compilation and table selection. */
@@ -0,0 +1,159 @@
1
+ /** Evaluates finite calc lengths using explicit native unit mappings. @module */
2
+ import type * as Scalar from './Scalar.js'
3
+
4
+ type Value = { amount: number; length: boolean }
5
+
6
+ /** Resolves number/length arithmetic, rejecting relative units and incompatible dimensions. */
7
+ export function length(input: string, options: Scalar.Options): number {
8
+ const pattern =
9
+ /\s*(?:(\d+(?:\.\d+)?|\.\d+)(?:[eE]([+-]?\d+))?(px|rem)?|(calc)|([()+*/-]))/giy
10
+ const positions: number[] = []
11
+ const tokens: (string | Value)[] = []
12
+ let offset = 0
13
+ while (offset < input.trimEnd().length) {
14
+ pattern.lastIndex = offset
15
+ const match = pattern.exec(input)
16
+ if (!match) throw new Error('Unsupported native calc expression.')
17
+
18
+ offset = pattern.lastIndex
19
+ positions.push(match.index + match[0].search(/\S/))
20
+ if (positions.length > 512)
21
+ throw new Error('Native calc exceeds 512 tokens.')
22
+ if (match[1] === undefined) {
23
+ tokens.push(match[4]?.toLowerCase() ?? match[5]!)
24
+ continue
25
+ }
26
+
27
+ const unit = match[3]?.toLowerCase()
28
+ const scale =
29
+ unit === 'rem'
30
+ ? options.units?.rem
31
+ : unit === 'px'
32
+ ? (options.units?.px ?? 1)
33
+ : 1
34
+ if (scale === undefined)
35
+ throw new Error('Provide units.rem for rem lengths.')
36
+
37
+ tokens.push({
38
+ amount:
39
+ Number(`${match[1]}${match[2] === undefined ? '' : `e${match[2]}`}`) *
40
+ scale,
41
+ length: unit !== undefined,
42
+ })
43
+ }
44
+
45
+ let cursor = 0
46
+ let depth = 0
47
+ function atom(): Value {
48
+ if (++depth > 64)
49
+ throw new Error('Native calc exceeds 64 nested operations.')
50
+ try {
51
+ return readAtom()
52
+ } finally {
53
+ depth--
54
+ }
55
+ }
56
+ function readAtom(): Value {
57
+ const token = tokens[cursor++]
58
+ if (token === '+' || token === '-') {
59
+ const value = atom()
60
+ return { ...value, amount: token === '-' ? -value.amount : value.amount }
61
+ }
62
+ if (token === 'calc' || token === '(') {
63
+ if (token === 'calc' && tokens[cursor++] !== '(')
64
+ throw new Error('Unsupported native calc expression.')
65
+ const value = sum()
66
+ if (tokens[cursor++] !== ')')
67
+ throw new Error('Unsupported native calc expression.')
68
+ return value
69
+ }
70
+ if (!token || typeof token === 'string')
71
+ throw new Error('Unsupported native calc expression.')
72
+
73
+ return token
74
+ }
75
+
76
+ function product(): Value {
77
+ let value = atom()
78
+ while (tokens[cursor] === '*' || tokens[cursor] === '/') {
79
+ const operator = tokens[cursor++]
80
+ const right = atom()
81
+ if (
82
+ (operator === '*' && value.length && right.length) ||
83
+ (operator === '/' && (right.length || right.amount === 0))
84
+ )
85
+ throw new Error(
86
+ 'Native calc requires compatible number and length operands.',
87
+ )
88
+
89
+ value = {
90
+ amount:
91
+ operator === '*'
92
+ ? value.amount * right.amount
93
+ : value.amount / right.amount,
94
+ length: value.length || right.length,
95
+ }
96
+ }
97
+ return value
98
+ }
99
+
100
+ function sum(): Value {
101
+ let value = product()
102
+ while (tokens[cursor] === '+' || tokens[cursor] === '-') {
103
+ const position = positions[cursor]!
104
+ if (
105
+ !/\s/.test(input[position - 1] ?? '') ||
106
+ !/\s/.test(input[position + 1] ?? '')
107
+ )
108
+ throw new Error('Unsupported native calc expression.')
109
+
110
+ const operator = tokens[cursor++]
111
+ const right = product()
112
+ if (value.length !== right.length)
113
+ throw new Error(
114
+ 'Native calc requires compatible number and length operands.',
115
+ )
116
+
117
+ value = {
118
+ amount:
119
+ operator === '+'
120
+ ? value.amount + right.amount
121
+ : value.amount - right.amount,
122
+ length: value.length,
123
+ }
124
+ }
125
+ return value
126
+ }
127
+
128
+ const value = atom()
129
+ if (
130
+ cursor !== tokens.length ||
131
+ !value.length ||
132
+ !Number.isFinite(value.amount)
133
+ )
134
+ throw new Error('Native calc must resolve to one finite px or rem length.')
135
+
136
+ return value.amount
137
+ }
138
+
139
+ /** Splits shorthand lengths while preserving spaces within calculations. */
140
+ export function parts(input: string): readonly string[] {
141
+ const result: string[] = []
142
+ let depth = 0
143
+ let start = 0
144
+ const text = input.trim()
145
+ for (let index = 0; index < text.length; index++) {
146
+ const character = text[index]
147
+ if (character === '(') depth++
148
+ if (character === ')' && --depth < 0)
149
+ throw new Error('Unbalanced native calculation.')
150
+ if (depth > 64) throw new Error('Native calc exceeds 64 nested operations.')
151
+ if (depth === 0 && /\s/.test(character!)) {
152
+ if (index > start) result.push(text.slice(start, index))
153
+ start = index + 1
154
+ }
155
+ }
156
+ if (depth !== 0) throw new Error('Unbalanced native calculation.')
157
+ if (start < text.length) result.push(text.slice(start))
158
+ return result
159
+ }
@@ -0,0 +1,297 @@
1
+ /** Supplies automatic window measurements for native React consumers. @module */
2
+ import type * as Config from '../../Config.js'
3
+ import * as NativeContext from '../../runtime/NativeContext.js'
4
+ import NativeZyzz from './NativeZyzz.js'
5
+ import * as React from 'react'
6
+ import * as ReactNative from 'react-native'
7
+ import * as Store from './Store.js'
8
+ import * as Styles from './Styles.js'
9
+ import * as Subscription from '../react.js'
10
+ import * as Viewport from './Viewport.js'
11
+
12
+ /** Creates native authoring helpers and a Provider with automatic window subscriptions. */
13
+ export function defineConfig<const options extends Config.create.Options = {}>(
14
+ options: options & Parameters<typeof Config.create<options>>[0] = {} as never,
15
+ ): Subscription.defineConfig.ReturnType<options> {
16
+ const config = Subscription.defineConfig<options>(options)
17
+ return Object.freeze({
18
+ ...config,
19
+ Provider: dimensions(config.Provider),
20
+ }) as Subscription.defineConfig.ReturnType<options>
21
+ }
22
+
23
+ /** Provides selected variables, appearance, and automatic native dimensions. */
24
+ export const Provider = dimensions(Subscription.Provider)
25
+
26
+ /**
27
+ * Compiler-inserted subscription for resolving native style applications.
28
+ * Outside a Provider, selections use default variables, the light scheme, and window dimensions.
29
+ */
30
+ export function useStyles(): Subscription.useStyles.ReturnType {
31
+ const owner = React.useContext(Store.context)
32
+ const [fallback] = React.useState(() =>
33
+ Store.create({
34
+ colorScheme: 'light',
35
+ viewport: ReactNative.Dimensions.get('window'),
36
+ }),
37
+ )
38
+ React.useLayoutEffect(() => {
39
+ if (owner) return
40
+ const update = () =>
41
+ fallback.update({
42
+ colorScheme: 'light',
43
+ viewport: ReactNative.Dimensions.get('window'),
44
+ })
45
+ // Dimensions can change between render and commit.
46
+ update()
47
+ const subscription = ReactNative.Dimensions.addEventListener(
48
+ 'change',
49
+ update,
50
+ )
51
+ return () => subscription.remove()
52
+ }, [fallback, owner])
53
+
54
+ return Styles.use(owner ?? fallback)
55
+ }
56
+
57
+ export { useVars } from '../react.js'
58
+
59
+ /**
60
+ * Resolves compiled style props for function components using React updates.
61
+ * @param Component - Function, memo, or ref-forwarding component receiving native styles.
62
+ * @param options - Additional style-bearing prop names.
63
+ * @returns A component with the original call signature and ref. Type parameters remain when `styleProps` is omitted.
64
+ * @throws For invalid style prop names.
65
+ */
66
+ export function withStyles<
67
+ props extends object,
68
+ result extends React.ReactNode,
69
+ >(
70
+ Component: (props: props) => result,
71
+ options?: NoInfer<Subscription.withStyles.Options<(props: props) => result>>,
72
+ ): (props: props) => result
73
+ /**
74
+ * Resolves compiled style props for class and host components using React updates.
75
+ * @param Component - Class component or host element type receiving native styles.
76
+ * @param options - Additional style-bearing prop names.
77
+ * @returns A component preserving the original props and instance ref.
78
+ * @throws For invalid style prop names.
79
+ */
80
+ export function withStyles<const component extends React.ElementType>(
81
+ Component: component,
82
+ options?: Subscription.withStyles.Options<component>,
83
+ ): React.ForwardRefExoticComponent<React.ComponentPropsWithRef<component>>
84
+ export function withStyles(
85
+ Component: React.ElementType,
86
+ options: Subscription.withStyles.Options<
87
+ (props: Readonly<Record<string, unknown>>) => React.ReactNode
88
+ > = {},
89
+ ): React.ElementType {
90
+ return Styles.wrap(Component, options, useStyles)
91
+ }
92
+
93
+ function dimensions<props extends Subscription.Provider.Props>(
94
+ Component: React.FunctionComponent<props>,
95
+ ): React.FunctionComponent<props> {
96
+ return function WindowProvider(props: props) {
97
+ const window = ReactNative.useWindowDimensions()
98
+ const viewport = React.useMemo(
99
+ () => ({ height: window.height, width: window.width }),
100
+ [window.height, window.width],
101
+ )
102
+ return React.createElement(
103
+ Viewport.context.Provider,
104
+ { value: viewport },
105
+ React.createElement(Component, props),
106
+ )
107
+ }
108
+ }
109
+
110
+ /** Compiler-only binding path, with React subscriptions for Expo Go. */
111
+ export const useNativeStyles = useBindings
112
+
113
+ type Instance = {
114
+ readonly __internalInstanceHandle?:
115
+ | {
116
+ readonly stateNode?: { readonly node?: object | undefined } | undefined
117
+ }
118
+ | undefined
119
+ readonly getNativeScrollRef?: (() => Instance) | undefined
120
+ }
121
+
122
+ function useBindings() {
123
+ const owner = React.useContext(Store.context)
124
+ const [fallback] = React.useState(() =>
125
+ Store.create({
126
+ colorScheme: 'light',
127
+ viewport: ReactNative.Dimensions.get('window'),
128
+ }),
129
+ )
130
+ const store = owner ?? fallback
131
+ const [, render] = React.useReducer((count: number) => count + 1, 0)
132
+ const [refs] = React.useState(
133
+ () =>
134
+ new Map<
135
+ Instance,
136
+ {
137
+ active: boolean
138
+ ref: React.Ref<Instance> | undefined
139
+ release: () => void
140
+ }
141
+ >(),
142
+ )
143
+ React.useLayoutEffect(() => {
144
+ for (const [instance, entry] of refs) {
145
+ if (entry.active) continue
146
+ entry.release()
147
+ refs.delete(instance)
148
+ }
149
+ })
150
+ React.useLayoutEffect(
151
+ () => () => {
152
+ for (const entry of refs.values()) entry.release()
153
+ refs.clear()
154
+ },
155
+ [refs],
156
+ )
157
+ return {
158
+ style: NativeContext.application,
159
+ view: (props: Record<string, unknown>) => {
160
+ const snapshot = store.getSnapshot()
161
+ const selected = NativeContext.resolve(props.style, snapshot)
162
+ const initial = NativeContext.key(props.style, snapshot)
163
+
164
+ return {
165
+ ...props,
166
+ ref: (value: Instance | null) => {
167
+ const ref = props.ref as React.Ref<Instance> | undefined
168
+ if (!value) return
169
+ let entry = refs.get(value)
170
+ if (entry && entry.ref !== ref) {
171
+ entry.release()
172
+ refs.delete(value)
173
+ entry = undefined
174
+ }
175
+ if (!entry) {
176
+ const release = typeof ref === 'function' ? ref(value) : undefined
177
+ if (ref && typeof ref !== 'function') ref.current = value
178
+ entry = {
179
+ active: true,
180
+ ref,
181
+ release: () => {
182
+ if (typeof release === 'function') release()
183
+ else if (typeof ref === 'function') ref(null)
184
+ else if (ref) ref.current = null
185
+ },
186
+ }
187
+ refs.set(value, entry)
188
+ }
189
+ entry.active = true
190
+ const attached = entry
191
+
192
+ const instance = value.getNativeScrollRef?.() ?? value
193
+ const node = instance.__internalInstanceHandle?.stateNode?.node
194
+ const detach = (() => {
195
+ if (!initial.length && typeof selected !== 'function')
196
+ return () => {}
197
+ if (!NativeZyzz || !node || !selective(selected)) {
198
+ const unsubscribe = store.subscribe(render)
199
+ if (store.getSnapshot() !== snapshot) render()
200
+ return unsubscribe
201
+ }
202
+ const id = NativeZyzz!.attach(node, nativeProps(selected))
203
+ let previous = initial
204
+ const read = (context: NativeContext.Context) => {
205
+ const keys = NativeContext.key(props.style, context)
206
+ if (
207
+ keys.length === previous.length &&
208
+ keys.every((key, index) => Object.is(key, previous[index]))
209
+ )
210
+ return { commit: () => {} }
211
+ const selected = NativeContext.resolve(props.style, context)
212
+ if (!selective(selected)) return { commit: render }
213
+ return {
214
+ commit: () => {
215
+ previous = keys
216
+ },
217
+ patch: {
218
+ id,
219
+ props: nativeProps(selected),
220
+ },
221
+ }
222
+ }
223
+ // Context can change between render and committing the host ref.
224
+ const current = read(store.getSnapshot())
225
+ if (current.patch) write([current.patch])
226
+ current.commit()
227
+ const unbind = store.bind(read, write)
228
+ return () => {
229
+ unbind()
230
+ NativeZyzz!.detach(id)
231
+ }
232
+ })()
233
+ const update = () =>
234
+ fallback.update({
235
+ colorScheme: 'light',
236
+ viewport: ReactNative.Dimensions.get('window'),
237
+ })
238
+ const contextual =
239
+ initial.length > 0 || typeof selected === 'function'
240
+ const subscription =
241
+ owner || !contextual
242
+ ? undefined
243
+ : ReactNative.Dimensions.addEventListener('change', update)
244
+ if (!owner && contextual) update()
245
+ return () => {
246
+ detach()
247
+ subscription?.remove()
248
+ attached.active = false
249
+ }
250
+ },
251
+ style: selected,
252
+ }
253
+ },
254
+ }
255
+ }
256
+
257
+ function selective(style: unknown): boolean {
258
+ if (typeof style === 'function') return false
259
+ const value = ReactNative.StyleSheet.flatten(
260
+ style as ReactNative.StyleProp<ReactNative.ViewStyle>,
261
+ )
262
+ // Let React Native apply its processors to caller-owned representations.
263
+ // Only color properties are processed by the selective writer below.
264
+ return (
265
+ !value ||
266
+ ![
267
+ 'aspectRatio',
268
+ 'backgroundImage',
269
+ 'backgroundPosition',
270
+ 'backgroundRepeat',
271
+ 'backgroundSize',
272
+ 'boxShadow',
273
+ 'experimental_backgroundImage',
274
+ 'filter',
275
+ 'fontVariant',
276
+ 'transform',
277
+ 'transformOrigin',
278
+ ].some((property) => property in value)
279
+ )
280
+ }
281
+
282
+ function nativeProps(style: unknown): Record<string, unknown> {
283
+ const flattened =
284
+ ReactNative.StyleSheet.flatten(
285
+ style as ReactNative.StyleProp<ReactNative.ViewStyle>,
286
+ ) ?? {}
287
+ const result: Record<string, unknown> = { ...flattened }
288
+ for (const [name, value] of Object.entries(result)) {
289
+ if (name === 'color' || name.endsWith('Color'))
290
+ result[name] = ReactNative.processColor(value as ReactNative.ColorValue)
291
+ }
292
+ return result
293
+ }
294
+
295
+ function write(patches: readonly Store.Patch[]) {
296
+ NativeZyzz!.update(patches)
297
+ }
@@ -0,0 +1,20 @@
1
+ /** Private Fabric bridge for committed view bindings. @module */
2
+ import type { CodegenTypes, TurboModule } from 'react-native'
3
+ import { TurboModuleRegistry } from 'react-native'
4
+
5
+ /** Codegen contract, consumed only by the native adapter. */
6
+ export interface Spec extends TurboModule {
7
+ /** Links a mounted Fabric node and its rendered style. */
8
+ readonly attach: (
9
+ node: CodegenTypes.UnsafeObject,
10
+ props: CodegenTypes.UnsafeObject,
11
+ ) => number
12
+ /** Releases a view binding. */
13
+ readonly detach: (id: number) => void
14
+ /** Reports live bindings and applied update batches. */
15
+ readonly inspect: () => CodegenTypes.UnsafeObject
16
+ /** Applies one batch of changed native styles. */
17
+ readonly update: (updates: ReadonlyArray<CodegenTypes.UnsafeObject>) => void
18
+ }
19
+
20
+ export default TurboModuleRegistry.get<Spec>('NativeZyzz')