zyzz 0.0.21 → 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 (220) 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 +204 -12
  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 +64 -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/NativeBindings.d.ts +24 -26
  37. package/dist/compiler/internal/NativeBindings.d.ts.map +1 -1
  38. package/dist/compiler/internal/NativeBindings.js +61 -8
  39. package/dist/compiler/internal/NativeBindings.js.map +1 -1
  40. package/dist/compiler/internal/NativeMedia.d.ts +14 -0
  41. package/dist/compiler/internal/NativeMedia.d.ts.map +1 -0
  42. package/dist/compiler/internal/NativeMedia.js +287 -0
  43. package/dist/compiler/internal/NativeMedia.js.map +1 -0
  44. package/dist/compiler/internal/NativeVars.d.ts +28 -0
  45. package/dist/compiler/internal/NativeVars.d.ts.map +1 -0
  46. package/dist/compiler/internal/NativeVars.js +157 -0
  47. package/dist/compiler/internal/NativeVars.js.map +1 -0
  48. package/dist/compiler/internal/PackedStyles.d.ts +2 -0
  49. package/dist/compiler/internal/PackedStyles.d.ts.map +1 -1
  50. package/dist/compiler/internal/PackedStyles.js +2 -0
  51. package/dist/compiler/internal/PackedStyles.js.map +1 -1
  52. package/dist/compiler/internal/Themes.d.ts +22 -1
  53. package/dist/compiler/internal/Themes.d.ts.map +1 -1
  54. package/dist/compiler/internal/Themes.js +118 -3
  55. package/dist/compiler/internal/Themes.js.map +1 -1
  56. package/dist/compiler/internal/Vars.d.ts +1 -1
  57. package/dist/compiler/internal/Vars.d.ts.map +1 -1
  58. package/dist/compiler/internal/Vars.js +7 -2
  59. package/dist/compiler/internal/Vars.js.map +1 -1
  60. package/dist/default.js.zyzz.json +1 -1
  61. package/dist/internal/Completion.d.ts +1 -1
  62. package/dist/internal/Completion.d.ts.map +1 -1
  63. package/dist/internal/Targets.d.ts.map +1 -1
  64. package/dist/internal/Targets.js +2 -0
  65. package/dist/internal/Targets.js.map +1 -1
  66. package/dist/internal/Token.d.ts +0 -2
  67. package/dist/internal/Token.d.ts.map +1 -1
  68. package/dist/internal/Token.js.map +1 -1
  69. package/dist/internal/Value.d.ts +7 -0
  70. package/dist/internal/Value.d.ts.map +1 -1
  71. package/dist/internal/Value.js.map +1 -1
  72. package/dist/metro/Graph.d.ts +22 -7
  73. package/dist/metro/Graph.d.ts.map +1 -1
  74. package/dist/metro/Graph.js +179 -42
  75. package/dist/metro/Graph.js.map +1 -1
  76. package/dist/metro/Watch.d.ts +28 -4
  77. package/dist/metro/Watch.d.ts.map +1 -1
  78. package/dist/metro/Watch.js +39 -7
  79. package/dist/metro/Watch.js.map +1 -1
  80. package/dist/metro/index.d.ts +15 -4
  81. package/dist/metro/index.d.ts.map +1 -1
  82. package/dist/metro/index.js +27 -0
  83. package/dist/metro/index.js.map +1 -1
  84. package/dist/metro/transformer.d.ts +5 -1
  85. package/dist/metro/transformer.d.ts.map +1 -1
  86. package/dist/metro/transformer.js +5 -3
  87. package/dist/metro/transformer.js.map +1 -1
  88. package/dist/react-native/StyleSheet.d.ts +3 -2
  89. package/dist/react-native/StyleSheet.d.ts.map +1 -1
  90. package/dist/react-native/StyleSheet.js +17 -34
  91. package/dist/react-native/StyleSheet.js.map +1 -1
  92. package/dist/react-native/index.d.ts +1 -1
  93. package/dist/react-native/index.d.ts.map +1 -1
  94. package/dist/react-native/index.js +1 -1
  95. package/dist/react-native/index.js.map +1 -1
  96. package/dist/react-native/index.native.d.ts +5 -0
  97. package/dist/react-native/index.native.js +20 -0
  98. package/dist/react-native/index.native.js.map +7 -0
  99. package/dist/react-native/internal/Calculation.d.ts +7 -0
  100. package/dist/react-native/internal/Calculation.d.ts.map +1 -0
  101. package/dist/react-native/internal/Calculation.js +134 -0
  102. package/dist/react-native/internal/Calculation.js.map +1 -0
  103. package/dist/react-native/internal/Device.js +191 -0
  104. package/dist/react-native/internal/Device.js.map +7 -0
  105. package/dist/react-native/internal/NativeZyzz.js +7 -0
  106. package/dist/react-native/internal/NativeZyzz.js.map +7 -0
  107. package/dist/react-native/internal/Scalar.d.ts.map +1 -1
  108. package/dist/react-native/internal/Scalar.js +9 -2
  109. package/dist/react-native/internal/Scalar.js.map +1 -1
  110. package/dist/react-native/internal/Store.d.ts +32 -0
  111. package/dist/react-native/internal/Store.d.ts.map +1 -0
  112. package/dist/react-native/internal/Store.js +57 -0
  113. package/dist/react-native/internal/Store.js.map +1 -0
  114. package/dist/react-native/internal/Tokens.d.ts +21 -0
  115. package/dist/react-native/internal/Tokens.d.ts.map +1 -0
  116. package/dist/react-native/internal/Tokens.js +58 -0
  117. package/dist/react-native/internal/Tokens.js.map +1 -0
  118. package/dist/react-native/internal/Viewport.d.ts +6 -0
  119. package/dist/react-native/internal/Viewport.d.ts.map +1 -0
  120. package/dist/react-native/internal/Viewport.js +4 -0
  121. package/dist/react-native/internal/Viewport.js.map +1 -0
  122. package/dist/react-native/react.d.ts +74 -11
  123. package/dist/react-native/react.d.ts.map +1 -1
  124. package/dist/react-native/react.js +111 -12
  125. package/dist/react-native/react.js.map +1 -1
  126. package/dist/react-native/reanimated.d.ts +39 -0
  127. package/dist/react-native/reanimated.d.ts.map +1 -0
  128. package/dist/react-native/reanimated.js +76 -0
  129. package/dist/react-native/reanimated.js.map +1 -0
  130. package/dist/runtime/NativeContext.d.ts +10 -1
  131. package/dist/runtime/NativeContext.d.ts.map +1 -1
  132. package/dist/runtime/NativeContext.js +67 -14
  133. package/dist/runtime/NativeContext.js.map +1 -1
  134. package/dist/runtime/NativeDynamic.d.ts +3 -2
  135. package/dist/runtime/NativeDynamic.d.ts.map +1 -1
  136. package/dist/runtime/NativeDynamic.js +32 -7
  137. package/dist/runtime/NativeDynamic.js.map +1 -1
  138. package/dist/runtime/NativeStatic.d.ts +23 -0
  139. package/dist/runtime/NativeStatic.d.ts.map +1 -0
  140. package/dist/runtime/NativeStatic.js +106 -0
  141. package/dist/runtime/NativeStatic.js.map +1 -0
  142. package/dist/runtime/NativeVars.d.ts +53 -0
  143. package/dist/runtime/NativeVars.d.ts.map +1 -0
  144. package/dist/runtime/NativeVars.js +77 -0
  145. package/dist/runtime/NativeVars.js.map +1 -0
  146. package/dist/runtime/index.d.ts +4 -0
  147. package/dist/runtime/index.d.ts.map +1 -1
  148. package/dist/runtime/index.js +4 -0
  149. package/dist/runtime/index.js.map +1 -1
  150. package/dist/runtime/internal/NativeMedia.d.ts +25 -0
  151. package/dist/runtime/internal/NativeMedia.d.ts.map +1 -0
  152. package/dist/runtime/internal/NativeMedia.js +36 -0
  153. package/dist/runtime/internal/NativeMedia.js.map +1 -0
  154. package/dist/vite/index.d.ts.map +1 -1
  155. package/dist/vite/index.js +7 -35
  156. package/dist/vite/index.js.map +1 -1
  157. package/dist/web/Css.d.ts.map +1 -1
  158. package/dist/web/Css.js +31 -19
  159. package/dist/web/Css.js.map +1 -1
  160. package/dist/web/internal/Themes.d.ts +0 -2
  161. package/dist/web/internal/Themes.d.ts.map +1 -1
  162. package/dist/web/internal/Themes.js +30 -28
  163. package/dist/web/internal/Themes.js.map +1 -1
  164. package/dist/web/page.d.ts +2 -0
  165. package/dist/web/page.d.ts.map +1 -1
  166. package/native/NativeZyzz.cpp +215 -0
  167. package/native/NativeZyzz.h +52 -0
  168. package/native/ZyzzSpecJSI.h +65 -0
  169. package/native/android/CMakeLists.txt +8 -0
  170. package/native/android/ZyzzSpec-generated.cpp +50 -0
  171. package/native/android/ZyzzSpec.h +31 -0
  172. package/native/ios/ZyzzModuleProvider.mm +13 -0
  173. package/package.json +39 -1
  174. package/react-native.config.ts +14 -0
  175. package/src/Style.ts +11 -8
  176. package/src/babel/NativeJsx.ts +305 -28
  177. package/src/babel/index.ts +21 -3
  178. package/src/compiler/Graph.ts +75 -0
  179. package/src/compiler/Native.ts +158 -26
  180. package/src/compiler/Source.ts +32 -5
  181. package/src/compiler/internal/Configurations.ts +25 -3
  182. package/src/compiler/internal/Contract.ts +98 -50
  183. package/src/compiler/internal/Contributions.ts +7 -1
  184. package/src/compiler/internal/Descriptors.ts +12 -8
  185. package/src/compiler/internal/NativeBindings.ts +98 -15
  186. package/src/compiler/internal/NativeMedia.ts +342 -0
  187. package/src/compiler/internal/NativeVars.ts +228 -0
  188. package/src/compiler/internal/PackedStyles.ts +4 -0
  189. package/src/compiler/internal/Themes.ts +187 -2
  190. package/src/compiler/internal/Vars.ts +12 -1
  191. package/src/internal/Completion.ts +6 -3
  192. package/src/internal/Targets.ts +2 -0
  193. package/src/internal/Token.ts +0 -2
  194. package/src/internal/Value.ts +20 -0
  195. package/src/metro/Graph.ts +235 -50
  196. package/src/metro/Watch.ts +95 -10
  197. package/src/metro/index.ts +62 -4
  198. package/src/metro/transformer.ts +21 -11
  199. package/src/react-native/StyleSheet.ts +28 -44
  200. package/src/react-native/index.native.ts +10 -0
  201. package/src/react-native/index.ts +1 -1
  202. package/src/react-native/internal/Calculation.ts +159 -0
  203. package/src/react-native/internal/Device.ts +231 -0
  204. package/src/react-native/internal/NativeZyzz.ts +20 -0
  205. package/src/react-native/internal/Scalar.ts +13 -2
  206. package/src/react-native/internal/Store.ts +78 -0
  207. package/src/react-native/internal/Tokens.ts +85 -0
  208. package/src/react-native/internal/Viewport.ts +8 -0
  209. package/src/react-native/react.ts +211 -19
  210. package/src/react-native/reanimated.ts +162 -0
  211. package/src/runtime/NativeContext.ts +88 -17
  212. package/src/runtime/NativeDynamic.ts +35 -10
  213. package/src/runtime/NativeStatic.ts +145 -0
  214. package/src/runtime/NativeVars.ts +189 -0
  215. package/src/runtime/index.ts +6 -0
  216. package/src/runtime/internal/NativeMedia.ts +63 -0
  217. package/src/vite/index.ts +9 -42
  218. package/src/web/Css.ts +42 -27
  219. package/src/web/internal/Themes.ts +34 -57
  220. package/src/web/page.ts +2 -0
@@ -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,42 +127,233 @@ 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) {
163
358
  if (
164
359
  !t.isJSXIdentifier(path.node.name, { name: 'style' }) ||
@@ -166,7 +361,36 @@ export function visitor(
166
361
  t.isJSXEmptyExpression(path.node.value.expression)
167
362
  )
168
363
  return
169
- const binding = owner(path, files.get(state.file)!, 'style')
364
+ if (
365
+ animated.has(path.parentPath.node as Babel.types.JSXOpeningElement) &&
366
+ !requiresResolution(path, path.node.value.expression)
367
+ )
368
+ return
369
+ if (wrapped.has(path.parentPath.node as Babel.types.JSXOpeningElement)) {
370
+ const value = path.node.value.expression
371
+ if (t.isIdentifier(value) && callables.has(value))
372
+ path.node.value.expression = t.memberExpression(
373
+ t.callExpression(value, []),
374
+ t.identifier('style'),
375
+ )
376
+ else if (
377
+ t.isCallExpression(value) &&
378
+ t.isIdentifier(value.callee) &&
379
+ callables.has(value.callee)
380
+ )
381
+ path.node.value.expression = t.memberExpression(
382
+ value,
383
+ t.identifier('style'),
384
+ )
385
+ return
386
+ }
387
+ const binding = owner(
388
+ path,
389
+ files.get(state.file)!,
390
+ native.has(path.parentPath.node as Babel.types.JSXOpeningElement)
391
+ ? 'nativeStyle'
392
+ : 'style',
393
+ )
170
394
  let inputs = [path.node.value.expression]
171
395
  let value = path.node.value.expression
172
396
  while (
@@ -216,14 +440,67 @@ export function visitor(
216
440
  path.node.value.expression = t.callExpression(binding, inputs)
217
441
  },
218
442
  JSXSpreadAttribute(path, state) {
443
+ if (
444
+ (animated.has(path.parentPath.node as Babel.types.JSXOpeningElement) &&
445
+ !requiresResolution(path, path.node.argument)) ||
446
+ native.has(path.parentPath.node as Babel.types.JSXOpeningElement) ||
447
+ wrapped.has(path.parentPath.node as Babel.types.JSXOpeningElement)
448
+ )
449
+ return
219
450
  const binding = owner(path, files.get(state.file)!, 'props')
220
451
  path.node.argument = t.callExpression(binding, [path.node.argument])
221
452
  },
222
453
  }
454
+ function requiresResolution(
455
+ path: Babel.NodePath,
456
+ expression: Babel.types.Node,
457
+ ): boolean {
458
+ const pending = [{ expression, scope: path.scope }]
459
+ const seen = new Set<Babel.types.VariableDeclarator>()
460
+ for (const entry of pending) {
461
+ let required = false
462
+ t.traverseFast(entry.expression, (node) => {
463
+ if (
464
+ callables.has(node) ||
465
+ (entry.expression === expression && t.isCallExpression(node)) ||
466
+ (t.isMemberExpression(node) &&
467
+ !node.computed &&
468
+ t.isIdentifier(node.property, { name: 'style' }))
469
+ ) {
470
+ required = true
471
+ return t.traverseFast.stop
472
+ }
473
+ // Alias factories stay opaque so worklet inputs do not become React subscriptions.
474
+ if (
475
+ t.isCallExpression(node) ||
476
+ t.isFunction(node) ||
477
+ t.isNewExpression(node)
478
+ )
479
+ return t.traverseFast.skip
480
+ if (!t.isIdentifier(node)) return
481
+ const binding = entry.scope.getBinding(node.name)
482
+ if (
483
+ binding?.constant &&
484
+ binding.path.isVariableDeclarator() &&
485
+ binding.path.node.init &&
486
+ !seen.has(binding.path.node)
487
+ ) {
488
+ seen.add(binding.path.node)
489
+ pending.push({
490
+ expression: binding.path.node.init,
491
+ scope: binding.path.scope,
492
+ })
493
+ }
494
+ })
495
+ if (required) return true
496
+ }
497
+
498
+ return false
499
+ }
223
500
  function owner(
224
501
  path: Babel.NodePath,
225
502
  owners: Owners,
226
- method: 'props' | 'style',
503
+ method: Method,
227
504
  ): Babel.types.Identifier {
228
505
  const parent = path.findParent((candidate) => {
229
506
  if (!candidate.isFunction()) return false
@@ -255,6 +532,6 @@ export function visitor(
255
532
  binding = parent.scope.generateUidIdentifier(`zyzz${method}`)
256
533
  bindings.set(method, binding)
257
534
  }
258
- return binding
535
+ return t.cloneNode(binding)
259
536
  }
260
537
  }
@@ -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
@@ -8,6 +8,8 @@ import type * as Mapping from '@jridgewell/gen-mapping'
8
8
  import * as Css from '../web/Css.js'
9
9
  import * as ClassName from '../web/internal/ClassName.js'
10
10
  import * as Native from './Native.js'
11
+ import * as NativeVars from './internal/NativeVars.js'
12
+ import * as NativeStyleSheet from '../react-native/StyleSheet.js'
11
13
  import * as Edits from './internal/Edits.js'
12
14
  import * as Identity from '../internal/Identity.js'
13
15
  import type * as Ast from '@oxc-project/types'
@@ -1086,10 +1088,82 @@ function build(options: compile.Options, cache?: Cache): Cache {
1086
1088
  : {}),
1087
1089
  }
1088
1090
  }
1091
+ const profiles = new Map<
1092
+ string,
1093
+ {
1094
+ definitions: Map<string, string>
1095
+ reads: Map<number, { name: string; source?: string }>
1096
+ }
1097
+ >()
1098
+ const reserved = new Set(
1099
+ [...identifiers.values()].flatMap((names) => [...names]),
1100
+ )
1101
+ function profile(moduleId: string) {
1102
+ let value = profiles.get(moduleId)
1103
+ if (!value) {
1104
+ value = { definitions: new Map(), reads: new Map() }
1105
+ profiles.set(moduleId, value)
1106
+ }
1107
+ return value
1108
+ }
1109
+ if (ids.some((id) => extracted.get(id)!.nativeVars?.length))
1110
+ NativeStyleSheet.compile({
1111
+ fonts: native.fonts,
1112
+ platform: native.platform,
1113
+ units: native.units,
1114
+ styles: { styles: [] },
1115
+ })
1116
+ for (const moduleId of ids) {
1117
+ for (const read of extracted.get(moduleId)!.nativeVars ?? []) {
1118
+ const owner = owners[read.owner]?.moduleId
1119
+ // Single-file adapters cannot publish changes to dependency modules.
1120
+ // Packed catalogs without source also retain their local fallback.
1121
+ if (
1122
+ native[Edits.runtime] ||
1123
+ !owner ||
1124
+ !Object.hasOwn(options.modules, owner)
1125
+ )
1126
+ continue
1127
+ let value: string
1128
+ try {
1129
+ value = JSON.stringify(
1130
+ NativeVars.compile({
1131
+ ...read,
1132
+ fonts: native.fonts,
1133
+ units: native.units,
1134
+ }),
1135
+ )
1136
+ } catch (error) {
1137
+ throw new Native.CompileError((error as Error).message)
1138
+ }
1139
+ const definitions = profile(owner).definitions
1140
+ let name = definitions.get(value)
1141
+ if (!name) {
1142
+ name = `__zyzzProfile${Identity.hash(value)}`
1143
+ while (reserved.has(name)) name += '_'
1144
+ reserved.add(name)
1145
+ definitions.set(value, name)
1146
+ }
1147
+ const from = moduleId.split('/').slice(0, -1)
1148
+ const to = owner.split('/')
1149
+ while (from.length && from[0] === to[0]) {
1150
+ from.shift()
1151
+ to.shift()
1152
+ }
1153
+ const source =
1154
+ (from.length ? '../'.repeat(from.length) : './') +
1155
+ to.join('/').replace(/\.([cm]?)tsx?$/, '.$1js')
1156
+ profile(moduleId).reads.set(read.start, {
1157
+ name,
1158
+ ...(owner !== moduleId ? { source } : {}),
1159
+ })
1160
+ }
1161
+ }
1089
1162
  const modules = Object.fromEntries(
1090
1163
  ids.map((moduleId) => {
1091
1164
  if (
1092
1165
  previous &&
1166
+ !profiles.size &&
1093
1167
  extracted.get(moduleId) === previous.extracted.get(moduleId)
1094
1168
  )
1095
1169
  return [moduleId, previous.result.modules[moduleId]!]
@@ -1133,6 +1207,7 @@ function build(options: compile.Options, cache?: Cache): Cache {
1133
1207
  }
1134
1208
  const output = Native.compile({
1135
1209
  ...native,
1210
+ [NativeVars.shared]: profiles.get(moduleId),
1136
1211
  moduleId,
1137
1212
  source: options.modules[moduleId]!,
1138
1213
  [Themes.context]: {