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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyzz",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "license": "MIT",
5
5
  "description": "Modern, universal, simple styling library for the Web and React Native",
6
6
  "repository": {
@@ -16,6 +16,9 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "src",
19
+ "native",
20
+ "react-native.config.ts",
21
+ "Zyzz.podspec",
19
22
  "!src/**/*.bench-d.ts",
20
23
  "!src/**/*.bench.ts",
21
24
  "!src/**/*.test-d.ts",
@@ -25,6 +28,20 @@
25
28
  "./src/reset.css",
26
29
  "./dist/reset.css"
27
30
  ],
31
+ "codegenConfig": {
32
+ "name": "ZyzzSpec",
33
+ "type": "modules",
34
+ "jsSrcsDir": "src/react-native/internal",
35
+ "android": {
36
+ "javaPackageName": "xyz.wevm.zyzz"
37
+ },
38
+ "ios": {
39
+ "modulesProvider": {
40
+ "NativeZyzz": "ZyzzModuleProvider"
41
+ }
42
+ },
43
+ "includesGeneratedCode": true
44
+ },
28
45
  "module": "./dist/index.js",
29
46
  "types": "./dist/index.d.ts",
30
47
  "exports": {
@@ -84,11 +101,24 @@
84
101
  "default": "./dist/oxlint/index.js"
85
102
  },
86
103
  "./react-native": {
104
+ "react-native": {
105
+ "types": "./dist/react-native/index.native.d.ts",
106
+ "default": "./dist/react-native/index.native.js"
107
+ },
87
108
  "src": "./src/react-native/index.ts",
88
109
  "types": "./dist/react-native/index.d.ts",
89
110
  "default": "./dist/react-native/index.js"
90
111
  },
112
+ "./react-native/reanimated": {
113
+ "src": "./src/react-native/reanimated.ts",
114
+ "types": "./dist/react-native/reanimated.d.ts",
115
+ "default": "./dist/react-native/reanimated.js"
116
+ },
91
117
  "./react-native/react": {
118
+ "react-native": {
119
+ "types": "./dist/react-native/react.d.ts",
120
+ "default": "./dist/react-native/internal/Device.js"
121
+ },
92
122
  "src": "./src/react-native/react.ts",
93
123
  "types": "./dist/react-native/react.d.ts",
94
124
  "default": "./dist/react-native/react.js"
@@ -146,6 +176,8 @@
146
176
  "@babel/core": "^7.29.7",
147
177
  "next": "^16.3.5",
148
178
  "react": "^19.0.0",
179
+ "react-native": ">=0.86.0 <1.0.0",
180
+ "react-native-reanimated": ">=4.5.3 <5.0.0",
149
181
  "vite": "^8.0.0"
150
182
  },
151
183
  "peerDependenciesMeta": {
@@ -158,6 +190,12 @@
158
190
  "react": {
159
191
  "optional": true
160
192
  },
193
+ "react-native": {
194
+ "optional": true
195
+ },
196
+ "react-native-reanimated": {
197
+ "optional": true
198
+ },
161
199
  "vite": {
162
200
  "optional": true
163
201
  }
@@ -0,0 +1,14 @@
1
+ /** Autolinks the shared native adapter without application registration. @module */
2
+ export default {
3
+ dependency: {
4
+ platforms: {
5
+ android: {
6
+ cxxModuleCMakeListsModuleName: 'react_codegen_ZyzzSpec',
7
+ cxxModuleCMakeListsPath: 'CMakeLists.txt',
8
+ cxxModuleHeaderName: 'NativeZyzz',
9
+ sourceDir: 'native/android',
10
+ },
11
+ ios: {},
12
+ },
13
+ },
14
+ }
package/src/Style.ts CHANGED
@@ -75,14 +75,17 @@ export type Accepted<
75
75
  ? LiteralDeclarations
76
76
  : DeclarationProperties<tokens>)[key] extends style[key]
77
77
  ? style[key]
78
- : Value.Accepted<
79
- Pick<style, key>,
80
- literal extends true
81
- ? LiteralDeclarations
82
- : DeclarationProperties<tokens>
83
- >[key] &
84
- Value.Checked<Pick<style, key>, tokens>[key] &
85
- Value.Tokens<style[key], tokens, key>
78
+ : // Naming the expected tokens avoids a `never` diagnostic and keeps completions to accepted values.
79
+ [Value.Tokens<style[key], tokens, key>] extends [never]
80
+ ? Value.Mismatch<tokens, key>
81
+ : Value.Accepted<
82
+ Pick<style, key>,
83
+ literal extends true
84
+ ? LiteralDeclarations
85
+ : DeclarationProperties<tokens>
86
+ >[key] &
87
+ Value.Checked<Pick<style, key>, tokens>[key] &
88
+ Value.Tokens<style[key], tokens, key>
86
89
  : key extends Condition.Keys<tokens, key>
87
90
  ? [style[key]] extends [undefined]
88
91
  ? never
@@ -112,9 +112,13 @@ export function visitor(
112
112
  callables: WeakSet<Babel.types.Node>,
113
113
  ): Babel.Visitor<Babel.PluginPass> {
114
114
  const t = api.types
115
- type Bindings = Map<'props' | 'style', Babel.types.Identifier>
115
+ type Method = 'nativeStyle' | 'props' | 'style' | 'view'
116
+ type Bindings = Map<Method, Babel.types.Identifier>
116
117
  type Owners = Map<Babel.NodePath<Babel.types.Function>, Bindings>
117
118
  const files = new WeakMap<Babel.BabelFile, Owners>()
119
+ const native = new WeakSet<Babel.types.JSXOpeningElement>()
120
+ const animated = new WeakSet<Babel.types.JSXOpeningElement>()
121
+ const wrapped = new WeakSet<Babel.types.JSXOpeningElement>()
118
122
  return {
119
123
  Program: {
120
124
  enter(_, state) {
@@ -123,50 +127,275 @@ export function visitor(
123
127
  exit(path, state) {
124
128
  const owners = files.get(state.file)!
125
129
  if (!owners.size) return
126
- const hook = path.scope.generateUidIdentifier('useZyzzStyles')
127
- path.unshiftContainer(
128
- 'body',
129
- t.importDeclaration(
130
- [t.importSpecifier(hook, t.identifier('useStyles'))],
131
- t.stringLiteral('zyzz/react-native/react'),
132
- ),
133
- )
130
+ const hooks = new Map<boolean, Babel.types.Identifier>()
131
+ for (const native of [false, true]) {
132
+ if (
133
+ ![...owners.values()].some((bindings) =>
134
+ [...bindings.keys()].some(
135
+ (method) =>
136
+ (method === 'nativeStyle' || method === 'view') === native,
137
+ ),
138
+ )
139
+ )
140
+ continue
141
+ const hook = path.scope.generateUidIdentifier(
142
+ native ? 'useZyzzNativeStyles' : 'useZyzzStyles',
143
+ )
144
+ hooks.set(native, hook)
145
+ path.unshiftContainer(
146
+ 'body',
147
+ t.importDeclaration(
148
+ [
149
+ t.importSpecifier(
150
+ t.cloneNode(hook),
151
+ t.identifier(native ? 'useNativeStyles' : 'useStyles'),
152
+ ),
153
+ ],
154
+ t.stringLiteral('zyzz/react-native/react'),
155
+ ),
156
+ )
157
+ }
134
158
  for (const [owner, bindings] of owners) {
135
159
  if (!t.isBlockStatement(owner.node.body))
136
160
  owner.node.body = t.blockStatement([
137
161
  t.returnStatement(owner.node.body),
138
162
  ])
139
- const context =
140
- bindings.size > 1
141
- ? owner.scope.generateUidIdentifier('zyzzStyles')
142
- : undefined
143
- const declarations = context
144
- ? [t.variableDeclarator(context, t.callExpression(hook, []))]
145
- : []
146
- for (const [method, binding] of bindings)
147
- declarations.push(
148
- t.variableDeclarator(
149
- binding,
150
- t.memberExpression(
151
- context ?? t.callExpression(hook, []),
152
- t.identifier(method),
153
- ),
154
- ),
163
+ const declarations: Babel.types.VariableDeclarator[] = []
164
+ for (const [native, hook] of hooks) {
165
+ const entries = [...bindings].filter(
166
+ ([method]) =>
167
+ (method === 'nativeStyle' || method === 'view') === native,
155
168
  )
169
+ const context =
170
+ entries.length > 1
171
+ ? owner.scope.generateUidIdentifier('zyzzStyles')
172
+ : undefined
173
+ if (context)
174
+ declarations.push(
175
+ t.variableDeclarator(
176
+ t.cloneNode(context),
177
+ t.callExpression(t.cloneNode(hook), []),
178
+ ),
179
+ )
180
+ for (const [method, binding] of entries)
181
+ declarations.push(
182
+ t.variableDeclarator(
183
+ t.cloneNode(binding),
184
+ t.memberExpression(
185
+ context
186
+ ? t.cloneNode(context)
187
+ : t.callExpression(t.cloneNode(hook), []),
188
+ t.identifier(method === 'nativeStyle' ? 'style' : method),
189
+ ),
190
+ ),
191
+ )
192
+ }
156
193
  owner.node.body.body.unshift(
157
194
  t.variableDeclaration('var', declarations),
158
195
  )
159
196
  }
160
197
  },
161
198
  },
199
+ JSXOpeningElement: {
200
+ enter(path, state) {
201
+ const name = path.node.name
202
+ const root = (() => {
203
+ if (t.isJSXIdentifier(name)) return name
204
+ if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object))
205
+ return name.object
206
+ return undefined
207
+ })()
208
+ if (!root) return
209
+ const binding = path.scope.getBinding(root.name)
210
+ if (
211
+ binding?.path.parentPath?.isImportDeclaration() &&
212
+ binding.path.parentPath.node.source.value ===
213
+ 'react-native-reanimated'
214
+ ) {
215
+ animated.add(path.node)
216
+ return
217
+ }
218
+ if (binding?.constant && binding.path.isVariableDeclarator()) {
219
+ const call = binding.path.node.init
220
+ if (t.isCallExpression(call)) {
221
+ const callee = call.callee
222
+ const name = t.isIdentifier(callee)
223
+ ? callee
224
+ : t.isMemberExpression(callee) &&
225
+ !callee.computed &&
226
+ t.isIdentifier(callee.object) &&
227
+ t.isIdentifier(callee.property, { name: 'withStyles' })
228
+ ? callee.object
229
+ : undefined
230
+ const factory = name && binding.path.scope.getBinding(name.name)
231
+ if (
232
+ factory?.path.parentPath?.isImportDeclaration() &&
233
+ ['zyzz/react-native', 'zyzz/react-native/react'].includes(
234
+ factory.path.parentPath.node.source.value,
235
+ ) &&
236
+ (t.isIdentifier(callee)
237
+ ? factory.path.isImportSpecifier() &&
238
+ t.isIdentifier(factory.path.node.imported, {
239
+ name: 'withStyles',
240
+ })
241
+ : factory.path.isImportNamespaceSpecifier())
242
+ ) {
243
+ wrapped.add(path.node)
244
+ return
245
+ }
246
+ }
247
+ }
248
+ if (
249
+ !binding?.path.parentPath?.isImportDeclaration() ||
250
+ binding.path.parentPath.node.source.value !== 'react-native'
251
+ )
252
+ return
253
+ const imported = (() => {
254
+ if (
255
+ binding.path.isImportSpecifier() &&
256
+ t.isIdentifier(binding.path.node.imported)
257
+ )
258
+ return binding.path.node.imported.name
259
+ if (
260
+ t.isJSXMemberExpression(name) &&
261
+ binding.path.isImportNamespaceSpecifier()
262
+ )
263
+ return name.property.name
264
+ return undefined
265
+ })()
266
+ if (
267
+ !imported ||
268
+ !['Image', 'Pressable', 'Text', 'TextInput', 'View'].includes(
269
+ imported,
270
+ )
271
+ )
272
+ return
273
+ if (
274
+ !path.node.attributes.some(
275
+ (attribute) =>
276
+ t.isJSXSpreadAttribute(attribute) ||
277
+ (t.isJSXAttribute(attribute) &&
278
+ t.isJSXIdentifier(attribute.name, { name: 'style' })),
279
+ )
280
+ )
281
+ return
282
+ native.add(path.node)
283
+ owner(path, files.get(state.file)!, 'view')
284
+ },
285
+ exit(path, state) {
286
+ if (!native.has(path.node)) return
287
+ const properties: (
288
+ | Babel.types.ObjectProperty
289
+ | Babel.types.SpreadElement
290
+ )[] = []
291
+ for (const attribute of path.node.attributes) {
292
+ if (t.isJSXSpreadAttribute(attribute))
293
+ properties.push(t.spreadElement(attribute.argument))
294
+ else if (t.isJSXIdentifier(attribute.name)) {
295
+ const value = attribute.value
296
+ const expression = (() => {
297
+ if (value === null) return t.booleanLiteral(true)
298
+ if (
299
+ t.isJSXExpressionContainer(value) &&
300
+ !t.isJSXEmptyExpression(value.expression)
301
+ )
302
+ return value.expression
303
+ if (t.isStringLiteral(value))
304
+ return t.stringLiteral(value.value.replace(/\n\s+/g, ' '))
305
+ if (t.isJSXElement(value) || t.isJSXFragment(value)) return value
306
+ return undefined
307
+ })()
308
+ if (!expression)
309
+ throw path.buildCodeFrameError(
310
+ 'Unsupported native JSX attribute.',
311
+ )
312
+ properties.push(
313
+ t.objectProperty(
314
+ t.stringLiteral(attribute.name.name),
315
+ expression,
316
+ ),
317
+ )
318
+ } else
319
+ throw path.buildCodeFrameError('Unsupported native JSX attribute.')
320
+ }
321
+ const binding = owner(path, files.get(state.file)!, 'view')
322
+ const props = t.objectExpression(properties)
323
+ const key = path.node.attributes.some(
324
+ (attribute) =>
325
+ t.isJSXAttribute(attribute) &&
326
+ t.isJSXIdentifier(attribute.name, { name: 'key' }),
327
+ )
328
+ const temporary = key
329
+ ? path.scope.generateUidIdentifier('zyzzProps')
330
+ : undefined
331
+ if (temporary)
332
+ path.scope.push({ id: t.cloneNode(temporary), kind: 'var' })
333
+ path.node.attributes = [
334
+ t.jsxSpreadAttribute(
335
+ t.callExpression(binding, [
336
+ temporary
337
+ ? t.assignmentExpression('=', t.cloneNode(temporary), props)
338
+ : props,
339
+ ]),
340
+ ),
341
+ ...(temporary
342
+ ? [
343
+ t.jsxAttribute(
344
+ t.jsxIdentifier('key'),
345
+ t.jsxExpressionContainer(
346
+ t.memberExpression(
347
+ t.cloneNode(temporary),
348
+ t.identifier('key'),
349
+ ),
350
+ ),
351
+ ),
352
+ ]
353
+ : []),
354
+ ]
355
+ },
356
+ },
162
357
  JSXAttribute(path, state) {
358
+ const name = path.node.name
163
359
  if (
164
- !t.isJSXIdentifier(path.node.name, { name: 'style' }) ||
360
+ !t.isJSXIdentifier(name) ||
361
+ (name.name !== 'style' && !name.name.endsWith('Style')) ||
165
362
  !t.isJSXExpressionContainer(path.node.value) ||
166
363
  t.isJSXEmptyExpression(path.node.value.expression)
167
364
  )
168
365
  return
169
- const binding = owner(path, files.get(state.file)!, 'style')
366
+ if (
367
+ animated.has(path.parentPath.node as Babel.types.JSXOpeningElement) &&
368
+ !requiresResolution(path, path.node.value.expression)
369
+ )
370
+ return
371
+ if (wrapped.has(path.parentPath.node as Babel.types.JSXOpeningElement)) {
372
+ const value = path.node.value.expression
373
+ if (t.isIdentifier(value) && callables.has(value))
374
+ path.node.value.expression = t.memberExpression(
375
+ t.callExpression(value, []),
376
+ t.identifier('style'),
377
+ )
378
+ else if (
379
+ t.isCallExpression(value) &&
380
+ t.isIdentifier(value.callee) &&
381
+ callables.has(value.callee)
382
+ )
383
+ path.node.value.expression = t.memberExpression(
384
+ value,
385
+ t.identifier('style'),
386
+ )
387
+ return
388
+ }
389
+ // Unlike style, other style props stay untouched outside components, so class and module-scope JSX keep compiling.
390
+ if (name.name !== 'style' && !component(path)) return
391
+ const binding = owner(
392
+ path,
393
+ files.get(state.file)!,
394
+ name.name === 'style' &&
395
+ native.has(path.parentPath.node as Babel.types.JSXOpeningElement)
396
+ ? 'nativeStyle'
397
+ : 'style',
398
+ )
170
399
  let inputs = [path.node.value.expression]
171
400
  let value = path.node.value.expression
172
401
  while (
@@ -216,15 +445,64 @@ export function visitor(
216
445
  path.node.value.expression = t.callExpression(binding, inputs)
217
446
  },
218
447
  JSXSpreadAttribute(path, state) {
448
+ if (
449
+ (animated.has(path.parentPath.node as Babel.types.JSXOpeningElement) &&
450
+ !requiresResolution(path, path.node.argument)) ||
451
+ native.has(path.parentPath.node as Babel.types.JSXOpeningElement) ||
452
+ wrapped.has(path.parentPath.node as Babel.types.JSXOpeningElement)
453
+ )
454
+ return
219
455
  const binding = owner(path, files.get(state.file)!, 'props')
220
456
  path.node.argument = t.callExpression(binding, [path.node.argument])
221
457
  },
222
458
  }
223
- function owner(
459
+ function requiresResolution(
224
460
  path: Babel.NodePath,
225
- owners: Owners,
226
- method: 'props' | 'style',
227
- ): Babel.types.Identifier {
461
+ expression: Babel.types.Node,
462
+ ): boolean {
463
+ const pending = [{ expression, scope: path.scope }]
464
+ const seen = new Set<Babel.types.VariableDeclarator>()
465
+ for (const entry of pending) {
466
+ let required = false
467
+ t.traverseFast(entry.expression, (node) => {
468
+ if (
469
+ callables.has(node) ||
470
+ (entry.expression === expression && t.isCallExpression(node)) ||
471
+ (t.isMemberExpression(node) &&
472
+ !node.computed &&
473
+ t.isIdentifier(node.property, { name: 'style' }))
474
+ ) {
475
+ required = true
476
+ return t.traverseFast.stop
477
+ }
478
+ // Alias factories stay opaque so worklet inputs do not become React subscriptions.
479
+ if (
480
+ t.isCallExpression(node) ||
481
+ t.isFunction(node) ||
482
+ t.isNewExpression(node)
483
+ )
484
+ return t.traverseFast.skip
485
+ if (!t.isIdentifier(node)) return
486
+ const binding = entry.scope.getBinding(node.name)
487
+ if (
488
+ binding?.constant &&
489
+ binding.path.isVariableDeclarator() &&
490
+ binding.path.node.init &&
491
+ !seen.has(binding.path.node)
492
+ ) {
493
+ seen.add(binding.path.node)
494
+ pending.push({
495
+ expression: binding.path.node.init,
496
+ scope: binding.path.scope,
497
+ })
498
+ }
499
+ })
500
+ if (required) return true
501
+ }
502
+
503
+ return false
504
+ }
505
+ function component(path: Babel.NodePath) {
228
506
  const parent = path.findParent((candidate) => {
229
507
  if (!candidate.isFunction()) return false
230
508
  let declaration = candidate.parentPath
@@ -241,7 +519,15 @@ export function visitor(
241
519
  candidate.parentPath.isExportDefaultDeclaration()
242
520
  )
243
521
  })
244
- if (!parent?.isFunction() || parent.isClassMethod())
522
+ return parent?.isFunction() && !parent.isClassMethod() ? parent : undefined
523
+ }
524
+ function owner(
525
+ path: Babel.NodePath,
526
+ owners: Owners,
527
+ method: Method,
528
+ ): Babel.types.Identifier {
529
+ const parent = component(path)
530
+ if (!parent)
245
531
  throw path.buildCodeFrameError(
246
532
  'Native style props must be rendered in a function component or custom hook.',
247
533
  )
@@ -255,6 +541,6 @@ export function visitor(
255
541
  binding = parent.scope.generateUidIdentifier(`zyzz${method}`)
256
542
  bindings.set(method, binding)
257
543
  }
258
- return binding
544
+ return t.cloneNode(binding)
259
545
  }
260
546
  }
@@ -17,12 +17,16 @@ import * as Transform from '../compiler/Transform.js'
17
17
  export type NativeOptions = {
18
18
  /** Fixed build-time scheme. Omit for runtime selection through React. */
19
19
  readonly colorScheme?: 'dark' | 'light' | undefined
20
- /** Closed source graph supplied by the bundler for imported authoring. */
21
- readonly modules?: Readonly<Record<string, string>> | undefined
20
+ /** Packed authoring contracts supplied by the bundler. */
21
+ readonly contracts?: Graph.compile.Options['contracts']
22
+ /** Authored font-family text mapped to registered native families. */
23
+ readonly fonts?: Native.compile.Options['fonts']
22
24
  /** Host-resolved graph imports. */
23
25
  readonly imports?: Graph.compile.Options['imports']
24
26
  /** Portable source identity. Defaults to a native module-local identity. */
25
27
  readonly moduleId?: string | undefined
28
+ /** Closed source graph supplied by the bundler for imported authoring. */
29
+ readonly modules?: Readonly<Record<string, string>> | undefined
26
30
  /** Native destination selected by the bundler. */
27
31
  readonly platform: 'android' | 'ios'
28
32
  /** Native compiler selection. Omission preserves existing native configurations. */
@@ -188,9 +192,11 @@ export function zyzz(
188
192
  options[Compilation.key]?.programs ??
189
193
  (parsed ? new Map([[moduleId, parsed]]) : undefined),
190
194
  modules: { ...options.modules, [moduleId]: file.code },
195
+ contracts: options.contracts,
191
196
  imports: options.imports,
192
197
  native: {
193
198
  [Edits.runtime]: true,
199
+ fonts: options.fonts,
194
200
  platform: options.platform,
195
201
  units: options.units,
196
202
  colorScheme: options.colorScheme ?? 'light',
@@ -201,6 +207,7 @@ export function zyzz(
201
207
  return Native.compile({
202
208
  [Themes.context]: parsed ? { parsed, links: {} } : undefined,
203
209
  [Edits.runtime]: true,
210
+ fonts: options.fonts,
204
211
  platform: options.platform,
205
212
  units: options.units,
206
213
  colorScheme: options.colorScheme ?? 'light',
@@ -333,7 +340,18 @@ export function zyzz(
333
340
  },
334
341
  visitor:
335
342
  options.target !== 'web' && options.colorScheme === undefined
336
- ? NativeJsx.visitor(api, callables)
343
+ ? {
344
+ Program(path, state) {
345
+ // Bind before framework transforms clone components or rewrite imports.
346
+ api.traverse(
347
+ state.file.ast,
348
+ NativeJsx.visitor(api, callables),
349
+ path.scope,
350
+ state,
351
+ )
352
+ path.scope.crawl()
353
+ },
354
+ }
337
355
  : {
338
356
  Program(path) {
339
357
  if (options.target !== 'web' || !options.reset) return