@tamagui/static 2.7.7 → 3.0.0-beta.643.1

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 (329) hide show
  1. package/dist/cjs/check-dep-versions.cjs +355 -0
  2. package/dist/cjs/checkDeps.cjs +302 -0
  3. package/dist/cjs/compiler.cjs +286 -0
  4. package/dist/cjs/compilerHost.cjs +1695 -0
  5. package/dist/cjs/constants.cjs +33 -0
  6. package/dist/cjs/domStructuralPass.cjs +112 -0
  7. package/dist/cjs/exports.cjs +42 -0
  8. package/dist/cjs/extractor/addLocalExports.cjs +64 -0
  9. package/dist/cjs/extractor/bundle.cjs +203 -0
  10. package/dist/cjs/extractor/bundleConfig.cjs +747 -0
  11. package/dist/cjs/extractor/concatClassName.cjs +117 -0
  12. package/dist/cjs/extractor/detectModuleFormat.cjs +54 -0
  13. package/dist/cjs/extractor/esbuildAliasPlugin.cjs +47 -0
  14. package/dist/cjs/extractor/esbuildTsconfigPaths.cjs +92 -0
  15. package/dist/cjs/extractor/getTamaguiConfigPathFromOptionsConfig.cjs +38 -0
  16. package/dist/cjs/extractor/loadTamagui.cjs +344 -0
  17. package/dist/cjs/extractor/regenerateConfig.cjs +161 -0
  18. package/dist/cjs/extractor/watchTamaguiConfig.cjs +64 -0
  19. package/dist/cjs/getPragmaOptions.cjs +67 -0
  20. package/dist/cjs/helpers/memoize.cjs +42 -0
  21. package/dist/cjs/helpers/requireTamaguiCore.cjs +40 -0
  22. package/dist/cjs/index.cjs +34 -0
  23. package/dist/cjs/registerRequire.cjs +220 -0
  24. package/dist/cjs/server.cjs +66 -0
  25. package/dist/cjs/staticEvaluationIgnoredModules.cjs +86 -0
  26. package/dist/cjs/types.cjs +17 -0
  27. package/dist/cjs/zero/artifact.cjs +145 -0
  28. package/dist/cjs/zero/graph.cjs +209 -0
  29. package/dist/cjs/zero/identity.cjs +61 -0
  30. package/dist/cjs/zero/index.cjs +26 -0
  31. package/dist/cjs/zero/islands.cjs +232 -0
  32. package/dist/cjs/zero/options.cjs +163 -0
  33. package/dist/cjs/zero/ownership.cjs +79 -0
  34. package/dist/cjs/zero/theme.cjs +328 -0
  35. package/dist/cjs/zero/transform.cjs +203 -0
  36. package/dist/esm/check-dep-versions.mjs +329 -0
  37. package/dist/esm/check-dep-versions.mjs.map +1 -0
  38. package/dist/esm/checkDeps.mjs +279 -0
  39. package/dist/esm/checkDeps.mjs.map +1 -0
  40. package/dist/esm/compiler.mjs +255 -0
  41. package/dist/esm/compiler.mjs.map +1 -0
  42. package/dist/esm/compilerHost.mjs +1675 -0
  43. package/dist/esm/compilerHost.mjs.map +1 -0
  44. package/dist/esm/constants.mjs +7 -0
  45. package/dist/esm/constants.mjs.map +1 -0
  46. package/dist/esm/domStructuralPass.mjs +92 -0
  47. package/dist/esm/domStructuralPass.mjs.map +1 -0
  48. package/dist/esm/exports.mjs +26 -0
  49. package/dist/esm/extractor/addLocalExports.mjs +44 -0
  50. package/dist/esm/extractor/addLocalExports.mjs.map +1 -0
  51. package/dist/esm/extractor/bundle.mjs +173 -0
  52. package/dist/esm/extractor/bundle.mjs.map +1 -0
  53. package/dist/esm/extractor/bundleConfig.mjs +706 -0
  54. package/dist/esm/extractor/bundleConfig.mjs.map +1 -0
  55. package/dist/esm/extractor/concatClassName.mjs +96 -0
  56. package/dist/esm/extractor/concatClassName.mjs.map +1 -0
  57. package/dist/esm/extractor/detectModuleFormat.mjs +31 -0
  58. package/dist/esm/extractor/detectModuleFormat.mjs.map +1 -0
  59. package/dist/esm/extractor/esbuildAliasPlugin.mjs +26 -0
  60. package/dist/esm/extractor/esbuildAliasPlugin.mjs.map +1 -0
  61. package/dist/esm/extractor/esbuildTsconfigPaths.mjs +63 -0
  62. package/dist/esm/extractor/esbuildTsconfigPaths.mjs.map +1 -0
  63. package/dist/esm/extractor/getTamaguiConfigPathFromOptionsConfig.mjs +18 -0
  64. package/dist/esm/extractor/getTamaguiConfigPathFromOptionsConfig.mjs.map +1 -0
  65. package/dist/esm/extractor/loadTamagui.mjs +306 -0
  66. package/dist/esm/extractor/loadTamagui.mjs.map +1 -0
  67. package/dist/esm/extractor/regenerateConfig.mjs +131 -0
  68. package/dist/esm/extractor/regenerateConfig.mjs.map +1 -0
  69. package/dist/esm/extractor/watchTamaguiConfig.mjs +43 -0
  70. package/dist/esm/extractor/watchTamaguiConfig.mjs.map +1 -0
  71. package/dist/esm/getPragmaOptions.mjs +46 -0
  72. package/dist/esm/getPragmaOptions.mjs.map +1 -0
  73. package/dist/esm/helpers/memoize.mjs +21 -0
  74. package/dist/esm/helpers/memoize.mjs.map +1 -0
  75. package/dist/esm/helpers/requireTamaguiCore.mjs +19 -0
  76. package/dist/esm/helpers/requireTamaguiCore.mjs.map +1 -0
  77. package/dist/esm/index.mjs +8 -0
  78. package/dist/esm/index.mjs.map +1 -0
  79. package/dist/esm/registerRequire.mjs +195 -0
  80. package/dist/esm/registerRequire.mjs.map +1 -0
  81. package/dist/esm/server.mjs +40 -0
  82. package/dist/esm/server.mjs.map +1 -0
  83. package/dist/esm/setup.mjs +0 -0
  84. package/dist/esm/staticEvaluationIgnoredModules.mjs +61 -0
  85. package/dist/esm/staticEvaluationIgnoredModules.mjs.map +1 -0
  86. package/dist/esm/types.mjs +0 -0
  87. package/dist/esm/zero/artifact.mjs +119 -0
  88. package/dist/esm/zero/artifact.mjs.map +1 -0
  89. package/dist/esm/zero/graph.mjs +170 -0
  90. package/dist/esm/zero/graph.mjs.map +1 -0
  91. package/dist/esm/zero/identity.mjs +36 -0
  92. package/dist/esm/zero/identity.mjs.map +1 -0
  93. package/dist/esm/zero/index.mjs +15 -0
  94. package/dist/esm/zero/islands.mjs +197 -0
  95. package/dist/esm/zero/islands.mjs.map +1 -0
  96. package/dist/esm/zero/options.mjs +129 -0
  97. package/dist/esm/zero/options.mjs.map +1 -0
  98. package/dist/esm/zero/ownership.mjs +48 -0
  99. package/dist/esm/zero/ownership.mjs.map +1 -0
  100. package/dist/esm/zero/theme.mjs +303 -0
  101. package/dist/esm/zero/theme.mjs.map +1 -0
  102. package/dist/esm/zero/transform.mjs +177 -0
  103. package/dist/esm/zero/transform.mjs.map +1 -0
  104. package/package.json +33 -43
  105. package/src/check-dep-versions.ts +2 -2
  106. package/src/checkDeps.ts +3 -2
  107. package/src/compiler.ts +425 -0
  108. package/src/compilerHost.ts +3125 -0
  109. package/src/constants.ts +0 -5
  110. package/src/domStructuralPass.ts +170 -0
  111. package/src/exports.ts +3 -6
  112. package/src/extractor/addLocalExports.ts +49 -0
  113. package/src/extractor/bundle.ts +20 -35
  114. package/src/extractor/bundleConfig.ts +334 -187
  115. package/src/extractor/concatClassName.ts +13 -20
  116. package/src/extractor/getTamaguiConfigPathFromOptionsConfig.ts +5 -4
  117. package/src/extractor/loadTamagui.ts +196 -143
  118. package/src/extractor/regenerateConfig.ts +65 -13
  119. package/src/extractor/watchTamaguiConfig.ts +6 -1
  120. package/src/helpers/requireTamaguiCore.ts +6 -1
  121. package/src/registerRequire.ts +94 -132
  122. package/src/staticEvaluationIgnoredModules.ts +74 -0
  123. package/src/types.ts +2 -107
  124. package/src/zero/artifact.ts +142 -0
  125. package/src/zero/graph.ts +359 -0
  126. package/src/zero/identity.ts +73 -0
  127. package/src/zero/index.ts +8 -0
  128. package/src/zero/islands.ts +262 -0
  129. package/src/zero/options.ts +239 -0
  130. package/src/zero/ownership.ts +113 -0
  131. package/src/zero/theme.ts +478 -0
  132. package/src/zero/transform.ts +320 -0
  133. package/types/check-dep-versions.d.ts.map +1 -1
  134. package/types/checkDeps.d.ts.map +1 -1
  135. package/types/compiler.d.ts +117 -0
  136. package/types/compiler.d.ts.map +1 -0
  137. package/types/compilerHost.d.ts +25 -0
  138. package/types/compilerHost.d.ts.map +1 -0
  139. package/types/constants.d.ts +0 -1
  140. package/types/constants.d.ts.map +1 -1
  141. package/types/domStructuralPass.d.ts +3 -0
  142. package/types/domStructuralPass.d.ts.map +1 -0
  143. package/types/exports.d.ts +3 -6
  144. package/types/exports.d.ts.map +1 -1
  145. package/types/extractor/addLocalExports.d.ts +3 -0
  146. package/types/extractor/addLocalExports.d.ts.map +1 -0
  147. package/types/extractor/bundle.d.ts +66 -65
  148. package/types/extractor/bundle.d.ts.map +1 -1
  149. package/types/extractor/bundleConfig.d.ts +19 -4
  150. package/types/extractor/bundleConfig.d.ts.map +1 -1
  151. package/types/extractor/esbuildAliasPlugin.d.ts.map +1 -1
  152. package/types/extractor/getTamaguiConfigPathFromOptionsConfig.d.ts +1 -1
  153. package/types/extractor/getTamaguiConfigPathFromOptionsConfig.d.ts.map +1 -1
  154. package/types/extractor/loadTamagui.d.ts +26 -3
  155. package/types/extractor/loadTamagui.d.ts.map +1 -1
  156. package/types/extractor/regenerateConfig.d.ts +1 -1
  157. package/types/extractor/regenerateConfig.d.ts.map +1 -1
  158. package/types/extractor/watchTamaguiConfig.d.ts.map +1 -1
  159. package/types/getPragmaOptions.d.ts +1 -1
  160. package/types/helpers/memoize.d.ts +1 -1
  161. package/types/helpers/memoize.d.ts.map +1 -1
  162. package/types/helpers/requireTamaguiCore.d.ts.map +1 -1
  163. package/types/index.d.ts.map +1 -1
  164. package/types/registerRequire.d.ts +2 -2
  165. package/types/registerRequire.d.ts.map +1 -1
  166. package/types/staticEvaluationIgnoredModules.d.ts +9 -0
  167. package/types/staticEvaluationIgnoredModules.d.ts.map +1 -0
  168. package/types/types.d.ts +2 -90
  169. package/types/types.d.ts.map +1 -1
  170. package/types/zero/artifact.d.ts +44 -0
  171. package/types/zero/artifact.d.ts.map +1 -0
  172. package/types/zero/graph.d.ts +139 -0
  173. package/types/zero/graph.d.ts.map +1 -0
  174. package/types/zero/identity.d.ts +33 -0
  175. package/types/zero/identity.d.ts.map +1 -0
  176. package/types/zero/index.d.ts +9 -0
  177. package/types/zero/index.d.ts.map +1 -0
  178. package/types/zero/islands.d.ts +56 -0
  179. package/types/zero/islands.d.ts.map +1 -0
  180. package/types/zero/options.d.ts +58 -0
  181. package/types/zero/options.d.ts.map +1 -0
  182. package/types/zero/ownership.d.ts +51 -0
  183. package/types/zero/ownership.d.ts.map +1 -0
  184. package/types/zero/theme.d.ts +83 -0
  185. package/types/zero/theme.d.ts.map +1 -0
  186. package/types/zero/transform.d.ts +53 -0
  187. package/types/zero/transform.d.ts.map +1 -0
  188. package/dist/check-dep-versions.cjs +0 -372
  189. package/dist/checkDeps.cjs +0 -271
  190. package/dist/constants.cjs +0 -51
  191. package/dist/exports.cjs +0 -48
  192. package/dist/extractor/accessSafe.cjs +0 -48
  193. package/dist/extractor/babelParse.cjs +0 -56
  194. package/dist/extractor/buildClassName.cjs +0 -86
  195. package/dist/extractor/bundle.cjs +0 -234
  196. package/dist/extractor/bundleConfig.cjs +0 -729
  197. package/dist/extractor/concatClassName.cjs +0 -129
  198. package/dist/extractor/createEvaluator.cjs +0 -88
  199. package/dist/extractor/createExtractor.cjs +0 -1968
  200. package/dist/extractor/createLogger.cjs +0 -50
  201. package/dist/extractor/detectModuleFormat.cjs +0 -55
  202. package/dist/extractor/ensureImportingConcat.cjs +0 -54
  203. package/dist/extractor/errors.cjs +0 -28
  204. package/dist/extractor/esbuildAliasPlugin.cjs +0 -56
  205. package/dist/extractor/esbuildTsconfigPaths.cjs +0 -108
  206. package/dist/extractor/evaluateAstNode.cjs +0 -128
  207. package/dist/extractor/extractHelpers.cjs +0 -182
  208. package/dist/extractor/extractMediaStyle.cjs +0 -169
  209. package/dist/extractor/extractToClassNames.cjs +0 -475
  210. package/dist/extractor/extractToNative.cjs +0 -369
  211. package/dist/extractor/findTopmostFunction.cjs +0 -43
  212. package/dist/extractor/generatedUid.cjs +0 -58
  213. package/dist/extractor/getPrefixLogs.cjs +0 -31
  214. package/dist/extractor/getPropValueFromAttributes.cjs +0 -85
  215. package/dist/extractor/getSourceModule.cjs +0 -100
  216. package/dist/extractor/getStaticBindingsForScope.cjs +0 -213
  217. package/dist/extractor/getTamaguiConfigPathFromOptionsConfig.cjs +0 -41
  218. package/dist/extractor/hasTopLevelAwait.cjs +0 -62
  219. package/dist/extractor/hoistClassNames.cjs +0 -76
  220. package/dist/extractor/literalToAst.cjs +0 -109
  221. package/dist/extractor/loadFile.cjs +0 -17
  222. package/dist/extractor/loadTamagui.cjs +0 -373
  223. package/dist/extractor/logLines.cjs +0 -43
  224. package/dist/extractor/normalizeTernaries.cjs +0 -100
  225. package/dist/extractor/propsToFontFamilyCache.cjs +0 -42
  226. package/dist/extractor/regenerateConfig.cjs +0 -178
  227. package/dist/extractor/removeUnusedHooks.cjs +0 -108
  228. package/dist/extractor/timer.cjs +0 -52
  229. package/dist/extractor/validHTMLAttributes.cjs +0 -79
  230. package/dist/extractor/watchTamaguiConfig.cjs +0 -70
  231. package/dist/getPragmaOptions.cjs +0 -73
  232. package/dist/helpers/memoize.cjs +0 -45
  233. package/dist/helpers/requireTamaguiCore.cjs +0 -40
  234. package/dist/index.cjs +0 -42
  235. package/dist/registerRequire.cjs +0 -209
  236. package/dist/server.cjs +0 -82
  237. package/dist/types.cjs +0 -18
  238. package/dist/worker.cjs +0 -111
  239. package/src/extractor/accessSafe.ts +0 -22
  240. package/src/extractor/babelParse.ts +0 -38
  241. package/src/extractor/buildClassName.ts +0 -76
  242. package/src/extractor/createEvaluator.ts +0 -82
  243. package/src/extractor/createExtractor.ts +0 -2970
  244. package/src/extractor/createLogger.ts +0 -41
  245. package/src/extractor/ensureImportingConcat.ts +0 -36
  246. package/src/extractor/errors.ts +0 -1
  247. package/src/extractor/evaluateAstNode.ts +0 -136
  248. package/src/extractor/extractHelpers.ts +0 -211
  249. package/src/extractor/extractMediaStyle.ts +0 -194
  250. package/src/extractor/extractToClassNames.ts +0 -733
  251. package/src/extractor/extractToNative.ts +0 -506
  252. package/src/extractor/findTopmostFunction.ts +0 -24
  253. package/src/extractor/generatedUid.ts +0 -41
  254. package/src/extractor/getPrefixLogs.ts +0 -9
  255. package/src/extractor/getPropValueFromAttributes.ts +0 -95
  256. package/src/extractor/getSourceModule.ts +0 -96
  257. package/src/extractor/getStaticBindingsForScope.ts +0 -237
  258. package/src/extractor/hasTopLevelAwait.ts +0 -28
  259. package/src/extractor/hoistClassNames.ts +0 -52
  260. package/src/extractor/literalToAst.ts +0 -85
  261. package/src/extractor/loadFile.ts +0 -17
  262. package/src/extractor/logLines.ts +0 -16
  263. package/src/extractor/normalizeTernaries.ts +0 -89
  264. package/src/extractor/propsToFontFamilyCache.ts +0 -18
  265. package/src/extractor/removeUnusedHooks.ts +0 -83
  266. package/src/extractor/timer.ts +0 -25
  267. package/src/extractor/validHTMLAttributes.ts +0 -52
  268. package/src/worker.ts +0 -142
  269. package/types/extractor/accessSafe.d.ts +0 -3
  270. package/types/extractor/accessSafe.d.ts.map +0 -1
  271. package/types/extractor/babelParse.d.ts +0 -5
  272. package/types/extractor/babelParse.d.ts.map +0 -1
  273. package/types/extractor/buildClassName.d.ts +0 -7
  274. package/types/extractor/buildClassName.d.ts.map +0 -1
  275. package/types/extractor/createEvaluator.d.ts +0 -12
  276. package/types/extractor/createEvaluator.d.ts.map +0 -1
  277. package/types/extractor/createExtractor.d.ts +0 -32
  278. package/types/extractor/createExtractor.d.ts.map +0 -1
  279. package/types/extractor/createLogger.d.ts +0 -3
  280. package/types/extractor/createLogger.d.ts.map +0 -1
  281. package/types/extractor/ensureImportingConcat.d.ts +0 -4
  282. package/types/extractor/ensureImportingConcat.d.ts.map +0 -1
  283. package/types/extractor/errors.d.ts +0 -3
  284. package/types/extractor/errors.d.ts.map +0 -1
  285. package/types/extractor/evaluateAstNode.d.ts +0 -3
  286. package/types/extractor/evaluateAstNode.d.ts.map +0 -1
  287. package/types/extractor/extractHelpers.d.ts +0 -28
  288. package/types/extractor/extractHelpers.d.ts.map +0 -1
  289. package/types/extractor/extractMediaStyle.d.ts +0 -11
  290. package/types/extractor/extractMediaStyle.d.ts.map +0 -1
  291. package/types/extractor/extractToClassNames.d.ts +0 -25
  292. package/types/extractor/extractToClassNames.d.ts.map +0 -1
  293. package/types/extractor/extractToNative.d.ts +0 -13
  294. package/types/extractor/extractToNative.d.ts.map +0 -1
  295. package/types/extractor/findTopmostFunction.d.ts +0 -4
  296. package/types/extractor/findTopmostFunction.d.ts.map +0 -1
  297. package/types/extractor/generatedUid.d.ts +0 -5
  298. package/types/extractor/generatedUid.d.ts.map +0 -1
  299. package/types/extractor/getPrefixLogs.d.ts +0 -3
  300. package/types/extractor/getPrefixLogs.d.ts.map +0 -1
  301. package/types/extractor/getPropValueFromAttributes.d.ts +0 -19
  302. package/types/extractor/getPropValueFromAttributes.d.ts.map +0 -1
  303. package/types/extractor/getSourceModule.d.ts +0 -16
  304. package/types/extractor/getSourceModule.d.ts.map +0 -1
  305. package/types/extractor/getStaticBindingsForScope.d.ts +0 -5
  306. package/types/extractor/getStaticBindingsForScope.d.ts.map +0 -1
  307. package/types/extractor/hasTopLevelAwait.d.ts +0 -2
  308. package/types/extractor/hasTopLevelAwait.d.ts.map +0 -1
  309. package/types/extractor/hoistClassNames.d.ts +0 -6
  310. package/types/extractor/hoistClassNames.d.ts.map +0 -1
  311. package/types/extractor/literalToAst.d.ts +0 -4
  312. package/types/extractor/literalToAst.d.ts.map +0 -1
  313. package/types/extractor/loadFile.d.ts +0 -1
  314. package/types/extractor/loadFile.d.ts.map +0 -1
  315. package/types/extractor/logLines.d.ts +0 -2
  316. package/types/extractor/logLines.d.ts.map +0 -1
  317. package/types/extractor/normalizeTernaries.d.ts +0 -5
  318. package/types/extractor/normalizeTernaries.d.ts.map +0 -1
  319. package/types/extractor/propsToFontFamilyCache.d.ts +0 -4
  320. package/types/extractor/propsToFontFamilyCache.d.ts.map +0 -1
  321. package/types/extractor/removeUnusedHooks.d.ts +0 -3
  322. package/types/extractor/removeUnusedHooks.d.ts.map +0 -1
  323. package/types/extractor/timer.d.ts +0 -5
  324. package/types/extractor/timer.d.ts.map +0 -1
  325. package/types/extractor/validHTMLAttributes.d.ts +0 -52
  326. package/types/extractor/validHTMLAttributes.d.ts.map +0 -1
  327. package/types/worker.d.ts +0 -46
  328. package/types/worker.d.ts.map +0 -1
  329. /package/dist/{setup.cjs → cjs/setup.cjs} +0 -0
@@ -1,2970 +0,0 @@
1
- import type { NodePath, TraverseOptions } from '@babel/traverse'
2
- import traverse from '@babel/traverse'
3
- import * as t from '@babel/types'
4
- import { Color, colorLog } from '@tamagui/cli-color'
5
- import * as reactNativeWebInternals from '@tamagui/react-native-web-internals'
6
- import {
7
- StyleObjectIdentifier,
8
- StyleObjectRules,
9
- type GetStyleState,
10
- type PseudoStyles,
11
- type SplitStyleProps,
12
- type StaticConfig,
13
- type TamaguiComponentState,
14
- } from '@tamagui/web'
15
- import { existsSync, readFileSync } from 'node:fs'
16
- import { basename, dirname, resolve, relative } from 'node:path'
17
- import { nodeModuleNameResolver, sys } from 'typescript'
18
- import type { ViewStyle } from 'react-native'
19
-
20
- import { FAILED_EVAL } from '../constants'
21
- import { requireTamaguiCore } from '../helpers/requireTamaguiCore'
22
- import type {
23
- ExtractedAttr,
24
- ExtractedAttrStyle,
25
- ExtractorOptions,
26
- ExtractorParseProps,
27
- TamaguiOptions,
28
- TamaguiOptionsWithFileInfo,
29
- Ternary,
30
- } from '../types'
31
- import type { LoadedComponents, TamaguiProjectInfo } from './bundleConfig'
32
- import { createEvaluator, createSafeEvaluator } from './createEvaluator'
33
- import { evaluateAstNode } from './evaluateAstNode'
34
- import {
35
- attrStr,
36
- findComponentName,
37
- getValidComponent,
38
- getValidComponentsPaths,
39
- getValidImport,
40
- isPresent,
41
- isValidImport,
42
- objToStr,
43
- } from './extractHelpers'
44
- import { findTopmostFunction } from './findTopmostFunction'
45
- import { cleanupBeforeExit, getStaticBindingsForScope } from './getStaticBindingsForScope'
46
- import { literalToAst } from './literalToAst'
47
- import { loadTamagui, loadTamaguiSync } from './loadTamagui'
48
- import { logLines } from './logLines'
49
- import { getTernaryKey, normalizeTernaries } from './normalizeTernaries'
50
- import { setPropsToFontFamily } from './propsToFontFamilyCache'
51
- import { timer } from './timer'
52
- import { validHTMLAttributes } from './validHTMLAttributes'
53
- import { BailOptimizationError } from './errors'
54
- import { loadCompilerOptionsFromTsconfig } from './esbuildTsconfigPaths'
55
-
56
- const UNTOUCHED_PROPS = {
57
- key: true,
58
- style: true,
59
- className: true,
60
- }
61
-
62
- // Platform variants that can't be resolved at compile time on native builds.
63
- // Defined at module level (not inside the loop) to avoid repeated Set allocations during compilation.
64
- // (requires runtime Platform.OS + Platform.isTV checks via react-native-tvos)
65
- const nativeOnlyPlatforms = new Set(['android', 'ios', 'tv', 'androidtv', 'tvos'])
66
-
67
- const createTernary = (x: Ternary) => x
68
-
69
- export type Extractor = ReturnType<typeof createExtractor>
70
-
71
- type FileOrPath = NodePath<t.Program> | t.File
72
-
73
- let hasLoggedBaseInfo = false
74
-
75
- function isFullyDisabled(props: TamaguiOptions) {
76
- return props.disableExtraction && props.disableDebugAttr
77
- }
78
-
79
- // Walk up JSX ancestors looking for one that declares `group="<groupName>"` together
80
- // with the `untilMeasured` prop. Used to deopt children whose styles depend on a
81
- // parent that the runtime measures before emitting child styles (can't be modeled
82
- // in static CSS).
83
- function hasUntilMeasuredAncestor(path: NodePath<any>, groupName: string): boolean {
84
- let current: NodePath<any> | null = path.parentPath
85
- while (current) {
86
- if (current.isJSXElement()) {
87
- const opening = current.node.openingElement
88
- let foundGroup = false
89
- let foundUntilMeasured = false
90
- for (const attr of opening.attributes) {
91
- if (!t.isJSXAttribute(attr)) continue
92
- if (!t.isJSXIdentifier(attr.name)) continue
93
- const aName = attr.name.name
94
- if (aName === 'group') {
95
- // only literal string equality counts — dynamic group= is left to runtime
96
- if (t.isStringLiteral(attr.value) && attr.value.value === groupName) {
97
- foundGroup = true
98
- } else if (
99
- t.isJSXExpressionContainer(attr.value) &&
100
- t.isStringLiteral(attr.value.expression) &&
101
- attr.value.expression.value === groupName
102
- ) {
103
- foundGroup = true
104
- }
105
- } else if (aName === 'untilMeasured') {
106
- foundUntilMeasured = true
107
- }
108
- }
109
- if (foundGroup && foundUntilMeasured) return true
110
- }
111
- current = current.parentPath
112
- }
113
- return false
114
- }
115
-
116
- export function createExtractor(
117
- { logger = console, platform = 'web' }: ExtractorOptions = { logger: console }
118
- ) {
119
- const INLINE_EXTRACTABLE = {
120
- ref: 'ref',
121
- key: 'key',
122
- ...(platform === 'web' && {
123
- onPress: 'onClick',
124
- onHoverIn: 'onMouseEnter',
125
- onHoverOut: 'onMouseLeave',
126
- onPressIn: 'onMouseDown',
127
- onPressOut: 'onMouseUp',
128
- }),
129
- ...(platform === 'native' && {
130
- // native view props that should pass through without preventing flattening
131
- testID: 'testID',
132
- nativeID: 'nativeID',
133
- accessibilityLabel: 'accessibilityLabel',
134
- accessibilityHint: 'accessibilityHint',
135
- accessibilityRole: 'accessibilityRole',
136
- accessibilityState: 'accessibilityState',
137
- accessibilityValue: 'accessibilityValue',
138
- accessibilityActions: 'accessibilityActions',
139
- accessibilityLabelledBy: 'accessibilityLabelledBy',
140
- accessibilityLiveRegion: 'accessibilityLiveRegion',
141
- accessibilityElementsHidden: 'accessibilityElementsHidden',
142
- accessibilityViewIsModal: 'accessibilityViewIsModal',
143
- importantForAccessibility: 'importantForAccessibility',
144
- collapsable: 'collapsable',
145
- needsOffscreenAlphaCompositing: 'needsOffscreenAlphaCompositing',
146
- removeClippedSubviews: 'removeClippedSubviews',
147
- renderToHardwareTextureAndroid: 'renderToHardwareTextureAndroid',
148
- shouldRasterizeIOS: 'shouldRasterizeIOS',
149
- hitSlop: 'hitSlop',
150
- pointerEvents: 'pointerEvents',
151
- }),
152
- }
153
-
154
- const componentState: TamaguiComponentState = {
155
- focus: false,
156
- focusVisible: false,
157
- focusWithin: false,
158
- hover: false,
159
- unmounted: true,
160
- press: false,
161
- pressIn: false,
162
- disabled: false,
163
- } as const
164
-
165
- const styleProps: SplitStyleProps = {
166
- resolveValues: platform === 'native' ? 'value' : 'variable',
167
- noClass: false,
168
- isAnimated: false,
169
- }
170
-
171
- const shouldAddDebugProp =
172
- // really basic disable this for next.js because it messes with ssr
173
- !process.env.npm_package_dependencies_next &&
174
- platform !== 'native' &&
175
- process.env.IDENTIFY_TAGS !== 'false' &&
176
- (process.env.NODE_ENV === 'development' || process.env.IDENTIFY_TAGS)
177
-
178
- let projectInfo: TamaguiProjectInfo | null = null
179
-
180
- // cache of dynamically discovered styled components, keyed by absolute file path
181
- // persists across files within the same worker/extractor instance
182
- const dynamicComponentCache = new Map<string, LoadedComponents>()
183
- const dynamicLoadingInProgress = new Set<string>()
184
-
185
- // lazily loaded tsconfig compiler options for path alias resolution
186
- let _compilerOptions: any = null
187
- function getCompilerOptions() {
188
- if (!_compilerOptions) {
189
- try {
190
- _compilerOptions = loadCompilerOptionsFromTsconfig()
191
- } catch {
192
- _compilerOptions = {}
193
- }
194
- }
195
- return _compilerOptions
196
- }
197
-
198
- function resolveImportPath(fromFile: string, importPath: string): string | null {
199
- if (importPath.startsWith('.')) {
200
- // relative path resolution
201
- const dir = dirname(fromFile)
202
- const base = resolve(dir, importPath)
203
- const extensions = ['.tsx', '.ts', '.jsx', '.js']
204
- for (const ext of extensions) {
205
- const full = base + ext
206
- if (existsSync(full)) return full
207
- }
208
- // try index files
209
- for (const ext of extensions) {
210
- const full = resolve(base, `index${ext}`)
211
- if (existsSync(full)) return full
212
- }
213
- return null
214
- }
215
-
216
- // tsconfig path alias resolution (e.g. ~/foo, @/bar)
217
- const compilerOptions = getCompilerOptions()
218
- if (compilerOptions.paths) {
219
- try {
220
- const { resolvedModule } = nodeModuleNameResolver(
221
- importPath,
222
- fromFile,
223
- compilerOptions,
224
- sys
225
- )
226
- if (
227
- resolvedModule &&
228
- !resolvedModule.resolvedFileName.endsWith('.d.ts') &&
229
- !resolvedModule.isExternalLibraryImport
230
- ) {
231
- return resolvedModule.resolvedFileName
232
- }
233
- } catch {
234
- // fallback - tsconfig resolution failed
235
- }
236
- }
237
-
238
- return null
239
- }
240
-
241
- const styledCheckCache = new Map<string, boolean>()
242
-
243
- function mightHaveStyledComponents(filePath: string): boolean {
244
- const cached = styledCheckCache.get(filePath)
245
- if (cached !== undefined) return cached
246
- try {
247
- const content = readFileSync(filePath, 'utf-8')
248
- const result = content.includes('styled(')
249
- styledCheckCache.set(filePath, result)
250
- return result
251
- } catch {
252
- styledCheckCache.set(filePath, false)
253
- return false
254
- }
255
- }
256
-
257
- // we load tamagui delayed because we need to set some global/env stuff before importing
258
- // otherwise we'd import `rnw` and cause it to evaluate react-native-web which causes errors
259
-
260
- function loadSync(props: TamaguiOptions) {
261
- if (isFullyDisabled(props)) {
262
- return null
263
- }
264
- return (projectInfo ||= loadTamaguiSync(props))
265
- }
266
-
267
- async function load(props: TamaguiOptions) {
268
- if (isFullyDisabled(props)) {
269
- return null
270
- }
271
- return (projectInfo ||= await loadTamagui(props))
272
- }
273
-
274
- return {
275
- options: {
276
- logger,
277
- },
278
- cleanupBeforeExit,
279
- loadTamagui: load,
280
- loadTamaguiSync: loadSync,
281
- getTamagui() {
282
- return projectInfo?.tamaguiConfig
283
- },
284
- parseSync: (f: FileOrPath, props: ExtractorParseProps) => {
285
- globalThis.expo ||= {} // expo-modules-core checks this and avoids loading "native" modules if exists
286
- const projectInfo = loadSync(props)
287
- return parseWithConfig(projectInfo || {}, f, props)
288
- },
289
- parse: async (f: FileOrPath, props: ExtractorParseProps) => {
290
- globalThis.expo ||= {} // expo-modules-core checks this and avoids loading "native" modules if exists
291
- const projectInfo = await load(props)
292
- return parseWithConfig(projectInfo || {}, f, props)
293
- },
294
- }
295
-
296
- function parseWithConfig(
297
- { components, tamaguiConfig }: TamaguiProjectInfo,
298
- fileOrPath: FileOrPath,
299
- options: ExtractorParseProps
300
- ) {
301
- const {
302
- config = 'tamagui.config.ts',
303
- importsWhitelist = ['constants.js'],
304
- evaluateVars = true,
305
- sourcePath = '',
306
- onExtractTag,
307
- onStyledDefinitionRule,
308
- getFlattenedNode,
309
- disable,
310
- disableExtraction,
311
- disableExtractVariables,
312
- disableDebugAttr,
313
- enableDynamicEvaluation = false,
314
- includeExtensions = ['.ts', '.tsx', '.jsx'],
315
- extractStyledDefinitions = false,
316
- prefixLogs,
317
- excludeProps,
318
- platform,
319
- ...restProps
320
- } = options
321
-
322
- // invalidate dynamic cache for this file on re-parse (HMR)
323
- if (sourcePath && dynamicComponentCache.has(sourcePath)) {
324
- dynamicComponentCache.delete(sourcePath)
325
- styledCheckCache.delete(sourcePath)
326
- }
327
-
328
- if (sourcePath.includes('.tamagui-dynamic-eval')) {
329
- return null
330
- }
331
-
332
- const {
333
- normalizeStyle,
334
- getSplitStyles,
335
- mediaQueryConfig,
336
- propMapper,
337
- proxyThemeVariables,
338
- getDefaultProps,
339
- pseudoDescriptors,
340
- } = requireTamaguiCore(platform)
341
-
342
- let shouldPrintDebug = options.shouldPrintDebug || false
343
-
344
- if (disable === true || (Array.isArray(disable) && disable.includes(sourcePath))) {
345
- return null
346
- }
347
-
348
- if (!isFullyDisabled(options)) {
349
- if (!components) {
350
- throw new Error(`Must provide components`)
351
- }
352
- }
353
-
354
- if (
355
- sourcePath &&
356
- includeExtensions &&
357
- !includeExtensions.some((ext) => sourcePath.endsWith(ext))
358
- ) {
359
- if (shouldPrintDebug) {
360
- logger.info(
361
- `Ignoring file due to includeExtensions: ${sourcePath}, includeExtensions: ${includeExtensions.join(
362
- ', '
363
- )}`
364
- )
365
- }
366
- return null
367
- }
368
-
369
- function isValidStyleKey(name: string, staticConfig: StaticConfig) {
370
- if (!projectInfo) {
371
- throw new Error(`Tamagui extractor not loaded yet`)
372
- }
373
- if (platform === 'native' && name[0] === '$' && mediaQueryConfig[name.slice(1)]) {
374
- return false
375
- }
376
- // Check for $theme-, $platform-, $group- prefixed keys
377
- if (name[0] === '$') {
378
- const mediaName = name.slice(1)
379
- if (
380
- mediaName.startsWith('theme-') ||
381
- mediaName.startsWith('platform-') ||
382
- mediaName.startsWith('group-')
383
- ) {
384
- return true
385
- }
386
- if (mediaQueryConfig[mediaName]) {
387
- return true
388
- }
389
- }
390
- return !!(
391
- staticConfig.validStyles?.[name] ||
392
- pseudoDescriptors[name] ||
393
- // don't disable variants or else you lose many things flattening
394
- staticConfig.variants?.[name] ||
395
- projectInfo?.tamaguiConfig?.shorthands[name]
396
- )
397
- }
398
-
399
- function getGroupPseudo(name: string) {
400
- const [_, groupName, a, b, c] = name.split('-')
401
- if (!groupName) return
402
- const m2 = a && b ? `${a}-${b}` : ''
403
- const media = (m2 && mediaQueryConfig[m2] && m2) || (a && mediaQueryConfig[a] && a)
404
- return media
405
- ? media === m2
406
- ? c
407
- : b
408
- ? `${b}${c ? `-${c}` : ''}`
409
- : undefined
410
- : a
411
- ? `${a}${b ? `-${b}` : ''}${c ? `-${c}` : ''}`
412
- : undefined
413
- }
414
-
415
- /**
416
- * Step 1: Determine if importing any statically extractable components
417
- */
418
-
419
- const isTargetingHTML = platform === 'web'
420
- const ogDebug = shouldPrintDebug
421
- const tm = timer()
422
- const propsWithFileInfo: TamaguiOptionsWithFileInfo = {
423
- ...options,
424
- sourcePath,
425
- allLoadedComponents: components ? [...components] : [],
426
- }
427
-
428
- if (!hasLoggedBaseInfo) {
429
- hasLoggedBaseInfo = true
430
- if (shouldPrintDebug) {
431
- logger.info(
432
- [
433
- 'loaded components:',
434
- propsWithFileInfo.allLoadedComponents
435
- .map((comp) => Object.keys(comp.nameToInfo).join(', '))
436
- .join(', '),
437
- ].join(' ')
438
- )
439
- }
440
- if (process.env.DEBUG?.startsWith('tamagui')) {
441
- logger.info(
442
- [
443
- 'loaded:',
444
- propsWithFileInfo.allLoadedComponents.map((x) => x.moduleName),
445
- ].join('\n')
446
- )
447
- }
448
- }
449
-
450
- tm.mark('load-tamagui', !!shouldPrintDebug)
451
-
452
- if (!isFullyDisabled(options)) {
453
- if (!tamaguiConfig?.themes) {
454
- console.error(
455
- `⛔️ Error: Missing "themes" in your tamagui.config file:
456
-
457
- You may not need the compiler! Remember you can run Tamagui with no configuration at all.
458
-
459
- You may have not "export default" your config (you can also "export const config").
460
-
461
- Or this may be due to duplicated dependency versions:
462
- - try out https://github.com/bmish/check-dependency-version-consistency to see if there are mis-matches.
463
- - or search your lockfile for mis-matches.
464
- `
465
- )
466
- console.info(` Got config:`, tamaguiConfig)
467
- process.exit(0)
468
- }
469
- }
470
-
471
- const firstThemeName = Object.keys(tamaguiConfig?.themes || {})[0]
472
- const firstTheme = tamaguiConfig?.themes[firstThemeName] || {}
473
-
474
- if (!firstTheme || typeof firstTheme !== 'object') {
475
- const err = `Missing theme ${firstThemeName}, an error occurred when importing your config`
476
- console.info(err, `Got config:`, tamaguiConfig)
477
- console.info(`Looking for theme:`, firstThemeName)
478
- throw new Error(err)
479
- }
480
-
481
- const proxiedTheme = proxyThemeVariables(firstTheme)
482
- type AccessListener = (key: string) => void
483
- const themeAccessListeners = new Set<AccessListener>()
484
- const defaultTheme = new Proxy(proxiedTheme, {
485
- get(target, key) {
486
- if (Reflect.has(target, key)) {
487
- themeAccessListeners.forEach((cb) => cb(String(key)))
488
- }
489
- return Reflect.get(target, key)
490
- },
491
- })
492
-
493
- const body: t.Statement[] | NodePath<t.Statement>[] =
494
- fileOrPath.type === 'Program' ? fileOrPath.get('body') : fileOrPath.program.body
495
-
496
- if (!isFullyDisabled(options)) {
497
- if (Object.keys(components || []).length === 0) {
498
- console.warn(
499
- `Warning: Tamagui didn't find any valid components (DEBUG=tamagui for more)`
500
- )
501
- if (process.env.DEBUG === 'tamagui') {
502
- console.info(`components`, Object.keys(components || []), components)
503
- }
504
- }
505
- }
506
-
507
- if (shouldPrintDebug === 'verbose') {
508
- logger.info(
509
- `allLoadedComponent modules ${propsWithFileInfo.allLoadedComponents
510
- .map((k) => k.moduleName)
511
- .join(', ')}`
512
- )
513
- logger.info(
514
- `valid import paths: ${JSON.stringify(
515
- getValidComponentsPaths(propsWithFileInfo)
516
- )}`
517
- )
518
- }
519
-
520
- let doesUseValidImport = false
521
- let hasImportedTheme = false
522
-
523
- const importDeclarations: t.ImportDeclaration[] = []
524
-
525
- for (const bodyPath of body) {
526
- if (bodyPath.type !== 'ImportDeclaration') continue
527
- const node = ('node' in bodyPath ? bodyPath.node : bodyPath) as t.ImportDeclaration
528
- const moduleName = node.source.value
529
-
530
- // if importing valid module
531
- const valid = isValidImport(propsWithFileInfo, moduleName)
532
-
533
- if (valid) {
534
- importDeclarations.push(node)
535
- }
536
-
537
- if (shouldPrintDebug === 'verbose') {
538
- logger.info(` - import via ${moduleName} ${valid}`)
539
- }
540
-
541
- if (extractStyledDefinitions && enableDynamicEvaluation) {
542
- // check all imports for `styled`, not just valid packages
543
- // styled( is basically guaranteed to be tamagui regardless of source
544
- if (node.specifiers.some((specifier) => specifier.local.name === 'styled')) {
545
- doesUseValidImport = true
546
- // don't break - need to collect all import declarations for the styled() handler
547
- }
548
- }
549
-
550
- if (valid) {
551
- const names = node.specifiers.map((specifier) => specifier.local.name)
552
- const isValidComponent = names.some((name) =>
553
- Boolean(isValidImport(propsWithFileInfo, moduleName, name))
554
- )
555
- if (shouldPrintDebug === 'verbose') {
556
- logger.info(
557
- ` - import ${isValidComponent ? '✅' : '⇣'} - ${names.join(
558
- ', '
559
- )} via package '${moduleName}' - (valid: ${JSON.stringify(
560
- getValidComponentsPaths(propsWithFileInfo)
561
- )})`
562
- )
563
- }
564
- if (isValidComponent) {
565
- doesUseValidImport = true
566
- if (!(extractStyledDefinitions && enableDynamicEvaluation)) break
567
- }
568
- }
569
- }
570
-
571
- if (shouldPrintDebug) {
572
- logger.info(
573
- `${JSON.stringify({ doesUseValidImport, hasImportedTheme }, null, 2)}\n`
574
- )
575
- }
576
-
577
- if (
578
- !doesUseValidImport &&
579
- extractStyledDefinitions &&
580
- enableDynamicEvaluation &&
581
- sourcePath
582
- ) {
583
- // check if any local import is in the dynamic cache or has styled components
584
- for (const bodyPath of body) {
585
- if (bodyPath.type !== 'ImportDeclaration') continue
586
- const node = (
587
- 'node' in bodyPath ? bodyPath.node : bodyPath
588
- ) as t.ImportDeclaration
589
- const moduleName = node.source.value
590
-
591
- const resolved = resolveImportPath(sourcePath, moduleName)
592
- if (!resolved) continue
593
-
594
- if (dynamicComponentCache.has(resolved)) {
595
- doesUseValidImport = true
596
- break
597
- }
598
-
599
- if (mightHaveStyledComponents(resolved)) {
600
- doesUseValidImport = true
601
- break
602
- }
603
- }
604
- }
605
-
606
- if (!doesUseValidImport) {
607
- return null
608
- }
609
-
610
- function getValidImportedComponent(componentName: string) {
611
- const importDeclaration = importDeclarations.find((dec) =>
612
- dec.specifiers.some((spec) => spec.local.name === componentName)
613
- )
614
- if (!importDeclaration) {
615
- return null
616
- }
617
- return getValidImport(
618
- propsWithFileInfo,
619
- importDeclaration.source.value,
620
- componentName
621
- )
622
- }
623
-
624
- tm.mark('import-check', !!shouldPrintDebug)
625
-
626
- let couldntParse = false
627
- // evaluating the file resolves every styled() in it at once, so once is enough
628
- let didDynamicLoad = false
629
- const modifiedComponents = new Set<NodePath<any>>()
630
-
631
- // only keeping a cache around per-file, reset it if it changes
632
- const bindingCache: Record<string, string | null> = {}
633
-
634
- const callTraverse = (a: TraverseOptions<any>) => {
635
- // @ts-ignore
636
- return fileOrPath.type === 'File' ? traverse(fileOrPath, a) : fileOrPath.traverse(a)
637
- }
638
-
639
- const shouldDisableExtraction =
640
- disableExtraction === true ||
641
- (Array.isArray(disableExtraction) && disableExtraction.includes(sourcePath))
642
-
643
- /**
644
- * Step 2: Statically extract from JSX < /> nodes
645
- */
646
- let programPath: NodePath<t.Program> | null = null
647
-
648
- const res = {
649
- styled: 0,
650
- flattened: 0,
651
- optimized: 0,
652
- modified: 0,
653
- found: 0,
654
- }
655
-
656
- const version = `${Math.random()}`
657
-
658
- callTraverse({
659
- // @ts-ignore
660
- Program: {
661
- enter(path) {
662
- programPath = path
663
- },
664
- },
665
-
666
- // styled() calls
667
- CallExpression(path) {
668
- if (disable || shouldDisableExtraction || extractStyledDefinitions === false) {
669
- return
670
- }
671
-
672
- if (!t.isIdentifier(path.node.callee) || path.node.callee.name !== 'styled') {
673
- return
674
- }
675
-
676
- const variableName =
677
- t.isVariableDeclarator(path.parent) && t.isIdentifier(path.parent.id)
678
- ? path.parent.id.name
679
- : 'unknown'
680
-
681
- if (shouldPrintDebug) {
682
- logger.info(` [styled] Found styled(${variableName})`)
683
- }
684
-
685
- const parentNode = path.node.arguments[0]
686
-
687
- if (!t.isIdentifier(parentNode)) {
688
- return
689
- }
690
- const parentName = parentNode.name
691
- const definition = path.node.arguments[1]
692
-
693
- if (!parentName || !definition || !t.isObjectExpression(definition)) {
694
- return
695
- }
696
-
697
- // look up by parent first (e.g. View in `styled(View, {...})`), then by self
698
- let Component =
699
- getValidImportedComponent(parentName) || getValidImportedComponent(variableName)
700
-
701
- // the parent's staticConfig knows nothing about the variants this call
702
- // declares, so a JSX site passing one reads as an unknown prop and
703
- // de-opts the whole element. evaluating the file gives us the real
704
- // staticConfig, variants included.
705
- const declaresVariants =
706
- !didDynamicLoad &&
707
- definition.properties.some(
708
- (prop) =>
709
- t.isObjectProperty(prop) &&
710
- t.isIdentifier(prop.key) &&
711
- prop.key.name === 'variants'
712
- )
713
-
714
- if ((!Component || declaresVariants) && enableDynamicEvaluation) {
715
- didDynamicLoad = true
716
- try {
717
- if (shouldPrintDebug) {
718
- logger.info(
719
- `Unknown component: ${variableName} = styled(${parentName}) attempting dynamic load: ${sourcePath}`
720
- )
721
- }
722
-
723
- const out = loadTamaguiSync({
724
- forceExports: true,
725
- components: [sourcePath],
726
- cacheKey: version,
727
- })
728
-
729
- if (out?.components) {
730
- propsWithFileInfo.allLoadedComponents = [
731
- ...propsWithFileInfo.allLoadedComponents,
732
- ...out.components,
733
- ]
734
-
735
- Component =
736
- out.components.flatMap((x) => x.nameToInfo[variableName] ?? [])[0] ||
737
- Component
738
-
739
- if (!out.cached) {
740
- const foundNames = out.components
741
- ?.map((x) => Object.keys(x.nameToInfo).join(', '))
742
- .join(', ')
743
- .trim()
744
-
745
- if (foundNames) {
746
- colorLog(
747
- Color.FgYellow,
748
- ` | Tamagui found dynamic components: ${foundNames}`
749
- )
750
- }
751
- }
752
- } else if (shouldPrintDebug) {
753
- logger.info(`Couldn't load, got ${out}`)
754
- }
755
- } catch (err: any) {
756
- if (shouldPrintDebug) {
757
- logger.info(
758
- `skip optimize styled(${variableName}), unable to pre-process (DEBUG=tamagui for more)`
759
- )
760
- }
761
- }
762
- }
763
-
764
- if (!Component) {
765
- if (shouldPrintDebug) {
766
- logger.info(` No component found`)
767
- }
768
-
769
- /**
770
- * We could/should still extract CSS just limited to validStyleProps
771
- */
772
- return
773
- }
774
-
775
- const componentSkipProps = new Set([
776
- ...(Component.staticConfig.inlineWhenUnflattened || []),
777
- ...(Component.staticConfig.inlineProps || []),
778
- // for now skip variants, will return to them
779
- 'variants',
780
- 'defaultVariants',
781
- // skip fontFamily its basically a "variant", important for theme use to be value always
782
- 'fontFamily',
783
- 'name',
784
- 'focusStyle',
785
- 'focusVisibleStyle',
786
- 'focusWithinStyle',
787
- 'disabledStyle',
788
- 'hoverStyle',
789
- 'pressStyle',
790
- ])
791
-
792
- // for now dont parse variants, spreads, etc
793
- const skipped = new Set<t.ObjectProperty | t.SpreadElement | t.ObjectMethod>()
794
- const styles = {}
795
- const staticDefaultProps = {}
796
-
797
- // Generate scope object at this level
798
- const staticNamespace = getStaticBindingsForScope(
799
- path.scope,
800
- importsWhitelist,
801
- sourcePath,
802
- bindingCache,
803
- shouldPrintDebug
804
- )
805
-
806
- const attemptEval = !evaluateVars
807
- ? evaluateAstNode
808
- : createEvaluator({
809
- props: propsWithFileInfo,
810
- staticNamespace,
811
- sourcePath,
812
- shouldPrintDebug,
813
- })
814
- const attemptEvalSafe = createSafeEvaluator(attemptEval)
815
-
816
- for (const property of definition.properties) {
817
- if (
818
- t.isObjectProperty(property) &&
819
- (t.isIdentifier(property.key) || t.isStringLiteral(property.key))
820
- ) {
821
- const key = t.isIdentifier(property.key)
822
- ? property.key.name
823
- : property.key.value
824
- const defaultPropValue = attemptEvalSafe(property.value)
825
- if (defaultPropValue !== FAILED_EVAL) {
826
- staticDefaultProps[key] = defaultPropValue
827
- }
828
- }
829
-
830
- if (
831
- !t.isObjectProperty(property) ||
832
- !t.isIdentifier(property.key) ||
833
- !isValidStyleKey(property.key.name, Component.staticConfig) ||
834
- // TODO make pseudos and variants work
835
- // skip pseudos
836
- pseudoDescriptors[property.key.name] ||
837
- // skip variants
838
- Component.staticConfig.variants?.[property.key.name] ||
839
- componentSkipProps.has(property.key.name)
840
- ) {
841
- skipped.add(property)
842
- continue
843
- }
844
- // attempt eval
845
- const out = attemptEvalSafe(property.value)
846
- if (out === FAILED_EVAL) {
847
- skipped.add(property)
848
- } else {
849
- styles[property.key.name] = out
850
- }
851
- }
852
-
853
- const out = getSplitStyles(
854
- styles,
855
- Component.staticConfig,
856
- defaultTheme,
857
- '',
858
- componentState,
859
- styleProps,
860
- undefined,
861
- undefined,
862
- undefined,
863
- undefined,
864
- false,
865
- shouldPrintDebug
866
- )!
867
-
868
- const classNames = {
869
- ...out.classNames,
870
- }
871
-
872
- // // add in the style object as classnames
873
- // const atomics = getPropsAtomic(out.style)
874
- // for (const atomic of atomics) {
875
- // out.rulesToInsert = out.rulesToInsert || []
876
- // out.rulesToInsert.push(atomic)
877
- // classNames[atomic.property] = atomic.identifier
878
- // }
879
-
880
- if (shouldPrintDebug) {
881
- logger.info(
882
- [
883
- `Extracted styled(${variableName})\n`,
884
- JSON.stringify(styles, null, 2),
885
- '\n classNames:',
886
- JSON.stringify(classNames, null, 2),
887
- '\n rulesToInsert:',
888
- out.rulesToInsert,
889
- ].join(' ')
890
- )
891
- }
892
-
893
- // don't replace definition values with class name strings -
894
- // the runtime needs real values for animations, context, and group styles.
895
- // we only emit the CSS rules so they're available if the runtime uses classNames.
896
-
897
- if (out.rulesToInsert) {
898
- for (const key in out.rulesToInsert) {
899
- const styleObject = out.rulesToInsert[key]
900
- onStyledDefinitionRule?.(
901
- styleObject[StyleObjectIdentifier],
902
- styleObject[StyleObjectRules]
903
- )
904
- }
905
- }
906
-
907
- res.styled++
908
-
909
- // register so JSX handler can find this component (same-file and cross-file)
910
- if (extractStyledDefinitions && enableDynamicEvaluation && Component) {
911
- const dynamicStaticConfig = {
912
- ...Component.staticConfig,
913
- defaultProps: {
914
- ...Component.staticConfig.defaultProps,
915
- ...staticDefaultProps,
916
- },
917
- }
918
-
919
- // add to allLoadedComponents with '' so getValidComponent matches when moduleName is ''
920
- // (same-file styled components have '' as moduleName in JSX handler)
921
- propsWithFileInfo.allLoadedComponents.push({
922
- moduleName: '',
923
- nameToInfo: { [variableName]: { staticConfig: dynamicStaticConfig } },
924
- })
925
-
926
- // also cache by file path so other files importing from this path can find it
927
- if (sourcePath) {
928
- let existing = dynamicComponentCache.get(sourcePath)
929
- if (!existing) {
930
- existing = { moduleName: sourcePath, nameToInfo: {} }
931
- dynamicComponentCache.set(sourcePath, existing)
932
- }
933
- existing.nameToInfo[variableName] = { staticConfig: dynamicStaticConfig }
934
- }
935
- }
936
-
937
- if (shouldPrintDebug) {
938
- logger.info(`Extracted styled(${variableName})`)
939
- }
940
- },
941
-
942
- JSXElement(traversePath) {
943
- tm.mark('jsx-element', !!shouldPrintDebug)
944
-
945
- const node = traversePath.node.openingElement
946
- const ogAttributes = node.attributes.map((attr) => ({ ...attr }))
947
- const componentName = findComponentName(traversePath.scope)
948
- const closingElement = traversePath.node.closingElement
949
-
950
- // skip non-identifier opening elements (member expressions, etc.)
951
- if (
952
- (closingElement && t.isJSXMemberExpression(closingElement?.name)) ||
953
- !t.isJSXIdentifier(node.name)
954
- ) {
955
- if (shouldPrintDebug) {
956
- logger.info(` skip non-identifier element`)
957
- }
958
-
959
- return
960
- }
961
-
962
- // validate its a proper import from tamagui (or internally inside tamagui)
963
- const binding = traversePath.scope.getBinding(node.name.name)
964
- let moduleName = ''
965
- let dynamicComponent: { staticConfig: any } | null = null
966
-
967
- if (binding) {
968
- if (t.isImportDeclaration(binding.path.parent)) {
969
- moduleName = binding.path.parent.source.value
970
- if (!isValidImport(propsWithFileInfo, moduleName, binding.identifier.name)) {
971
- // fallback: try dynamic component cache for local imports (relative or tsconfig alias)
972
- if (enableDynamicEvaluation && sourcePath) {
973
- const resolved = resolveImportPath(sourcePath, moduleName)
974
- if (resolved) {
975
- // check cache first
976
- const cached = dynamicComponentCache.get(resolved)
977
- if (cached?.nameToInfo[binding.identifier.name]) {
978
- dynamicComponent = cached.nameToInfo[binding.identifier.name]
979
- } else if (
980
- !dynamicLoadingInProgress.has(resolved) &&
981
- mightHaveStyledComponents(resolved)
982
- ) {
983
- // proactively load the file
984
- dynamicLoadingInProgress.add(resolved)
985
- try {
986
- const out = loadTamaguiSync({
987
- forceExports: true,
988
- components: [resolved],
989
- })
990
- if (out?.components) {
991
- for (const comp of out.components) {
992
- // merge into cache
993
- let existing = dynamicComponentCache.get(resolved)
994
- if (!existing) {
995
- existing = { moduleName: resolved, nameToInfo: {} }
996
- dynamicComponentCache.set(resolved, existing)
997
- }
998
- Object.assign(existing.nameToInfo, comp.nameToInfo)
999
- // also add to allLoadedComponents so getValidComponent works
1000
- propsWithFileInfo.allLoadedComponents.push({
1001
- moduleName: resolved,
1002
- nameToInfo: comp.nameToInfo,
1003
- })
1004
- }
1005
- const cachedNow = dynamicComponentCache.get(resolved)
1006
- if (cachedNow?.nameToInfo[binding.identifier.name]) {
1007
- dynamicComponent = cachedNow.nameToInfo[binding.identifier.name]
1008
- }
1009
- }
1010
- } catch (err) {
1011
- if (shouldPrintDebug) {
1012
- logger.info(` - Failed to dynamically load ${resolved}: ${err}`)
1013
- }
1014
- } finally {
1015
- dynamicLoadingInProgress.delete(resolved)
1016
- }
1017
- }
1018
- }
1019
- }
1020
-
1021
- if (!dynamicComponent) {
1022
- if (shouldPrintDebug) {
1023
- logger.info(
1024
- ` - Binding in component ${componentName} not valid import: "${binding.identifier.name}" isn't in ${moduleName}\n`
1025
- )
1026
- }
1027
- return
1028
- }
1029
- }
1030
- }
1031
- }
1032
-
1033
- const component =
1034
- dynamicComponent ||
1035
- getValidComponent(propsWithFileInfo, moduleName, node.name.name)
1036
- if (!component || !component.staticConfig) {
1037
- if (shouldPrintDebug) {
1038
- logger.info(`\n - No Tamagui conf for: ${node.name.name}\n`)
1039
- }
1040
- return
1041
- }
1042
-
1043
- const originalNodeName = node.name.name
1044
-
1045
- // found a valid tag
1046
- res.found++
1047
-
1048
- const filePath = `./${relative(process.cwd(), sourcePath)}`
1049
- const lineNumbers = node.loc
1050
- ? node.loc.start.line +
1051
- (node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '')
1052
- : ''
1053
-
1054
- const codePosition = `${filePath}:${lineNumbers}`
1055
-
1056
- // debug just one
1057
- const debugPropValue = node.attributes
1058
- .filter(
1059
- (n) =>
1060
- t.isJSXAttribute(n) && t.isJSXIdentifier(n.name) && n.name.name === 'debug'
1061
- )
1062
- // @ts-ignore
1063
- .map((n: t.JSXAttribute) => {
1064
- if (n.value === null) return true
1065
- if (t.isStringLiteral(n.value)) return n.value.value as 'verbose'
1066
- return false
1067
- })[0] as boolean | 'verbose' | undefined
1068
-
1069
- if (debugPropValue) {
1070
- shouldPrintDebug = debugPropValue
1071
- }
1072
-
1073
- if (shouldPrintDebug) {
1074
- logger.info(
1075
- `\x1b[33m\x1b[0m ` + `${componentName} | ${codePosition} -------------------`
1076
- )
1077
- // prettier-ignore
1078
- logger.info(
1079
- [
1080
- '\x1b[1m',
1081
- '\x1b[32m',
1082
- `<${originalNodeName} />`,
1083
- disableDebugAttr ? '' : '🐛',
1084
- ].join(' ')
1085
- )
1086
- }
1087
-
1088
- // add data-* debug attributes
1089
- if (platform !== 'native') {
1090
- if (shouldAddDebugProp && !disableDebugAttr) {
1091
- res.modified++
1092
- node.attributes.unshift(
1093
- t.jsxAttribute(t.jsxIdentifier('data-is'), t.stringLiteral(node.name.name))
1094
- )
1095
- if (componentName) {
1096
- node.attributes.unshift(
1097
- t.jsxAttribute(t.jsxIdentifier('data-in'), t.stringLiteral(componentName))
1098
- )
1099
- }
1100
-
1101
- node.attributes.unshift(
1102
- t.jsxAttribute(
1103
- t.jsxIdentifier('data-at'),
1104
- t.stringLiteral(`${basename(filePath)}:${lineNumbers}`)
1105
- )
1106
- )
1107
- }
1108
- }
1109
-
1110
- if (shouldDisableExtraction) {
1111
- if (shouldPrintDebug === 'verbose') {
1112
- logger.info(` ❌ Extraction disabled: ${JSON.stringify(disableExtraction)}\n`)
1113
- }
1114
- return
1115
- }
1116
-
1117
- try {
1118
- const { staticConfig } = component
1119
-
1120
- const defaultProps = {
1121
- ...getDefaultProps(staticConfig),
1122
- }
1123
- const variants = staticConfig.variants || {}
1124
- const isTextView = staticConfig.isText || false
1125
- const validStyles = staticConfig?.validStyles ?? {}
1126
-
1127
- // find render="a" render="main" etc dom indicators
1128
- let tagName = defaultProps.render ?? (isTextView ? 'span' : 'div')
1129
- traversePath
1130
- .get('openingElement')
1131
- .get('attributes')
1132
- .forEach((path) => {
1133
- const attr = path.node
1134
- if (t.isJSXSpreadAttribute(attr)) return
1135
- if (attr.name.name !== 'render') return
1136
- const val = attr.value
1137
- if (!t.isStringLiteral(val)) return
1138
- tagName = val.value
1139
- })
1140
-
1141
- if (shouldPrintDebug === 'verbose') {
1142
- console.info(` Start tag ${tagName}`)
1143
- }
1144
-
1145
- const flatNodeName = getFlattenedNode?.({ isTextView, tag: tagName })
1146
-
1147
- const inlineProps = new Set([
1148
- // adding some always inline props
1149
- ...(restProps.inlineProps || []),
1150
- ...(staticConfig.inlineProps || []),
1151
- ])
1152
-
1153
- const canFlattenTransition =
1154
- isTargetingHTML &&
1155
- tamaguiConfig?.animations.outputStyle === 'css' &&
1156
- !tamaguiConfig.animationDrivers
1157
-
1158
- const deoptProps = new Set([
1159
- // css-only transitions lower to ordinary css; every runtime driver
1160
- // needs the component preserved so it can respond to prop changes.
1161
- 'animation',
1162
- ...(canFlattenTransition ? [] : ['transition']),
1163
- 'animateOnly',
1164
- 'animatePresence',
1165
- 'disableOptimization',
1166
-
1167
- ...(!isTargetingHTML
1168
- ? [
1169
- // native has no css pseudo selectors; these are runtime-only on
1170
- // rn (event listeners + style merge in createComponent), and if
1171
- // we let them flatten into the stylesheet they get written
1172
- // under a literal key that rn treats as an unknown style prop.
1173
- // hover is intentionally excluded: it is no-op on native and
1174
- // should be dropped rather than preserved as runtime work.
1175
- 'pressStyle',
1176
- 'focusStyle',
1177
- 'focusVisibleStyle',
1178
- 'focusWithinStyle',
1179
- 'disabledStyle',
1180
- ]
1181
- : []),
1182
-
1183
- // when using a non-CSS driver, de-opt on enterStyle/exitStyle
1184
- ...(tamaguiConfig?.animations.isReactNative
1185
- ? ['enterStyle', 'exitStyle']
1186
- : []),
1187
- ])
1188
-
1189
- const inlineWhenUnflattened = new Set(staticConfig.inlineWhenUnflattened || [])
1190
-
1191
- // Generate scope object at this level
1192
- const staticNamespace = getStaticBindingsForScope(
1193
- traversePath.scope,
1194
- importsWhitelist,
1195
- sourcePath,
1196
- bindingCache,
1197
- shouldPrintDebug
1198
- )
1199
-
1200
- const attemptEval = !evaluateVars
1201
- ? evaluateAstNode
1202
- : createEvaluator({
1203
- props: propsWithFileInfo,
1204
- staticNamespace,
1205
- sourcePath,
1206
- traversePath,
1207
- shouldPrintDebug,
1208
- })
1209
- const attemptEvalSafe = createSafeEvaluator(attemptEval)
1210
-
1211
- if (shouldPrintDebug) {
1212
- logger.info(` staticNamespace ${Object.keys(staticNamespace).join(', ')}`)
1213
- }
1214
-
1215
- //
1216
- // SPREADS SETUP
1217
- //
1218
-
1219
- if (couldntParse) {
1220
- return
1221
- }
1222
-
1223
- tm.mark('jsx-element-flattened', !!shouldPrintDebug)
1224
-
1225
- let attrs: ExtractedAttr[] = []
1226
- let shouldDeopt = false
1227
- const inlined = new Map<string, any>()
1228
- const variantValues = new Map<string, any>()
1229
- let hasSetOptimized = false
1230
- const inlineWhenUnflattenedOGVals = {}
1231
-
1232
- // RUN first pass
1233
-
1234
- // normalize all conditionals so we can evaluate away easier later
1235
- // at the same time lets normalize shorthand media queries into spreads:
1236
- // that way we can parse them with the same logic later on
1237
- //
1238
- // {...media.sm && { color: x ? 'red' : 'blue' }}
1239
- // => {...media.sm && x && { color: 'red' }}
1240
- // => {...media.sm && !x && { color: 'blue' }}
1241
- //
1242
- // $sm={{ color: 'red' }}
1243
- // => {...media.sm && { color: 'red' }}
1244
- //
1245
- // $sm={{ color: x ? 'red' : 'blue' }}
1246
- // => {...media.sm && x && { color: 'red' }}
1247
- // => {...media.sm && !x && { color: 'blue' }}
1248
-
1249
- const propMapperStyleState: GetStyleState = {
1250
- staticConfig,
1251
- usedKeys: {},
1252
- classNames: {},
1253
- style: {},
1254
- theme: defaultTheme,
1255
- viewProps: defaultProps,
1256
- conf: tamaguiConfig!,
1257
- props: defaultProps,
1258
- componentState,
1259
- styleProps: {
1260
- ...styleProps,
1261
- resolveValues: 'auto',
1262
- },
1263
- debug: shouldPrintDebug,
1264
- }
1265
-
1266
- attrs = traversePath
1267
- .get('openingElement')
1268
- .get('attributes')
1269
- .flatMap((path) => {
1270
- // avoid work
1271
- if (shouldDeopt) {
1272
- return
1273
- }
1274
-
1275
- try {
1276
- const res = evaluateAttribute(path)
1277
- if (!res) {
1278
- path.remove()
1279
- }
1280
-
1281
- return res
1282
- } catch (err: any) {
1283
- if (shouldPrintDebug) {
1284
- logger.info(
1285
- [
1286
- 'Recoverable error extracting attribute',
1287
- err.message,
1288
- shouldPrintDebug === 'verbose' ? err.stack : '',
1289
- ].join(' ')
1290
- )
1291
- if (shouldPrintDebug === 'verbose') {
1292
- logger.info(`node ${path.node?.type}`)
1293
- }
1294
- }
1295
- // dont flatten if we run into error
1296
- inlined.set(`${Math.random()}`, 'spread')
1297
- return {
1298
- type: 'attr',
1299
- value: path.node,
1300
- } as const
1301
- }
1302
- })
1303
- .flat(4)
1304
- .filter(isPresent)
1305
-
1306
- if (shouldPrintDebug) {
1307
- logger.info(
1308
- [' - attrs (before):\n', logLines(attrs.map(attrStr).join(', '))].join(' ')
1309
- )
1310
- }
1311
-
1312
- // START function evaluateAttribute
1313
- function evaluateAttribute(
1314
- path: NodePath<t.JSXAttribute | t.JSXSpreadAttribute>
1315
- ): ExtractedAttr | ExtractedAttr[] | null {
1316
- const attribute = path.node
1317
- const attr: ExtractedAttr = { type: 'attr', value: attribute }
1318
- // ...spreads
1319
- if (t.isJSXSpreadAttribute(attribute)) {
1320
- const arg = attribute.argument
1321
- const conditional = t.isConditionalExpression(arg)
1322
- ? // <YStack {...isSmall ? { color: 'red } : { color: 'blue }}
1323
- ([arg.test, arg.consequent, arg.alternate] as const)
1324
- : t.isLogicalExpression(arg) && arg.operator === '&&'
1325
- ? // <YStack {...isSmall && { color: 'red }}
1326
- ([arg.left, arg.right, null] as const)
1327
- : null
1328
-
1329
- if (conditional) {
1330
- const [test, alt, cons] = conditional
1331
- if (!test) throw new Error(`no test`)
1332
- if ([alt, cons].some((side) => side && !isStaticObject(side))) {
1333
- if (shouldPrintDebug) {
1334
- logger.info(`not extractable ${alt} ${cons}`)
1335
- }
1336
- return attr
1337
- }
1338
- // split into individual ternaries per object property
1339
- return [
1340
- ...(flattenNestedTernaries(test, alt) || []),
1341
- ...((cons &&
1342
- flattenNestedTernaries(t.unaryExpression('!', test), cons)) ||
1343
- []),
1344
- ].map((ternary) => ({
1345
- type: 'ternary',
1346
- value: ternary,
1347
- }))
1348
- }
1349
- }
1350
- // END ...spreads
1351
-
1352
- // directly keep these
1353
- // couldn't evaluate spread, undefined name, or name is not string
1354
- if (
1355
- t.isJSXSpreadAttribute(attribute) ||
1356
- !attribute.name ||
1357
- typeof attribute.name.name !== 'string'
1358
- ) {
1359
- if (shouldPrintDebug) {
1360
- logger.info(' ! inlining, spread attr')
1361
- }
1362
- inlined.set(`${Math.random()}`, 'spread')
1363
- return attr
1364
- }
1365
-
1366
- const name = attribute.name.name
1367
-
1368
- // in tamagui style is handled at the end of the style loop so its not as simple as just
1369
- // adding this as a "style" property
1370
- // its not used often when using tamagui so not optimizing it for now
1371
- if (name === 'style') {
1372
- shouldDeopt = true
1373
- return null
1374
- }
1375
-
1376
- if (excludeProps?.has(name)) {
1377
- if (shouldPrintDebug) {
1378
- logger.info([' excluding prop', name].join(' '))
1379
- }
1380
- return null
1381
- }
1382
-
1383
- if (inlineProps.has(name)) {
1384
- inlined.set(name, name)
1385
- if (shouldPrintDebug) {
1386
- logger.info([' ! inlining, inline prop', name].join(' '))
1387
- }
1388
- return attr
1389
- }
1390
-
1391
- // pass className, key, and style props through untouched
1392
- if (UNTOUCHED_PROPS[name]) {
1393
- return attr
1394
- }
1395
-
1396
- if (INLINE_EXTRACTABLE[name]) {
1397
- inlined.set(name, INLINE_EXTRACTABLE[name])
1398
- return attr
1399
- }
1400
-
1401
- if (
1402
- name.startsWith('data-') ||
1403
- name.startsWith('aria-') ||
1404
- validHTMLAttributes[name]
1405
- ) {
1406
- return attr
1407
- }
1408
-
1409
- // de-opt on enterStyle={expression}
1410
- if (
1411
- (name === 'enterStyle' || name === 'exitStyle') &&
1412
- t.isJSXExpressionContainer(attribute?.value)
1413
- ) {
1414
- shouldDeopt = true
1415
- return attr
1416
- }
1417
-
1418
- // shorthand media queries
1419
- if (name[0] === '$' && t.isJSXExpressionContainer(attribute?.value)) {
1420
- const shortname = name.slice(1)
1421
- if (mediaQueryConfig[shortname]) {
1422
- const expression = attribute.value.expression
1423
- if (!t.isJSXEmptyExpression(expression)) {
1424
- const ternaries = flattenNestedTernaries(
1425
- t.stringLiteral(shortname),
1426
- expression,
1427
- {
1428
- inlineMediaQuery: shortname,
1429
- }
1430
- )
1431
- if (ternaries) {
1432
- return ternaries.map((value) => ({
1433
- type: 'ternary',
1434
- value,
1435
- }))
1436
- }
1437
- }
1438
- }
1439
- }
1440
-
1441
- const [value, valuePath] = (() => {
1442
- if (t.isJSXExpressionContainer(attribute?.value)) {
1443
- return [attribute.value.expression!, path.get('value')!] as const
1444
- }
1445
- return [attribute.value!, path.get('value')!] as const
1446
- })()
1447
-
1448
- // these props have runtime-only meaning on native. decide from the
1449
- // original jsx attr, before getSplitStyles has a chance to drop
1450
- // native-dead work like hoverStyle from its static output.
1451
- if (
1452
- deoptProps.has(name) ||
1453
- (platform === 'native' &&
1454
- name[0] === '$' &&
1455
- (name.startsWith('$theme-') ||
1456
- (name.startsWith('$group-') && getGroupPseudo(name) !== 'hover')))
1457
- ) {
1458
- inlined.set(name, true)
1459
- return attr
1460
- }
1461
-
1462
- const remove = () => {
1463
- Array.isArray(valuePath)
1464
- ? valuePath.map((p) => p.remove())
1465
- : valuePath.remove()
1466
- }
1467
-
1468
- if (name === 'ref') {
1469
- if (shouldPrintDebug) {
1470
- logger.info([' ! inlining, ref', name].join(' '))
1471
- }
1472
- inlined.set('ref', 'ref')
1473
- return attr
1474
- }
1475
-
1476
- if (name === 'render') {
1477
- // Only optimize string literal render props
1478
- // JSX elements and functions should deopt
1479
- if (!value || value.type !== 'StringLiteral') {
1480
- if (shouldPrintDebug) {
1481
- logger.info(` ! deopt on render prop (not a string literal)`)
1482
- }
1483
- shouldDeopt = true
1484
- }
1485
- return {
1486
- type: 'attr',
1487
- value: path.node,
1488
- }
1489
- }
1490
-
1491
- // native shouldn't extract variables
1492
- if (disableExtractVariables === true) {
1493
- if (value) {
1494
- if (value.type === 'StringLiteral' && value.value[0] === '$') {
1495
- if (shouldPrintDebug) {
1496
- logger.info(
1497
- [
1498
- ` ! inlining, native disable extract: ${name} =`,
1499
- value.value,
1500
- ].join(' ')
1501
- )
1502
- }
1503
- inlined.set(name, true)
1504
- return attr
1505
- }
1506
- }
1507
- }
1508
-
1509
- if (name === 'theme') {
1510
- inlined.set('theme', attr.value)
1511
- return attr
1512
- }
1513
-
1514
- // static `group="<literal>"` is handled at compile-time in
1515
- // extractToClassNames (emits container CSS + adds `t_group_<name>`
1516
- // className), so we drop the JSX attribute here without bailing
1517
- // flattening. dynamic `group={expr}` falls through to runtime.
1518
- if (isTargetingHTML && name === 'group' && t.isStringLiteral(value)) {
1519
- return []
1520
- }
1521
-
1522
- // if value can be evaluated, extract it and filter it out
1523
- const styleValue = attemptEvalSafe(value)
1524
-
1525
- // media-like blocks with nested $-keys (eg $theme-dark={{ $sm: {…} }})
1526
- // can't resolve to static CSS in one pass — keep them on the runtime path
1527
- if (
1528
- name[0] === '$' &&
1529
- (name.startsWith('$theme-') || name.startsWith('$group-')) &&
1530
- styleValue &&
1531
- typeof styleValue === 'object' &&
1532
- Object.keys(styleValue).some((k) => k[0] === '$')
1533
- ) {
1534
- if (shouldPrintDebug) {
1535
- logger.info(` ! nested media-like key inside ${name}, deopt to runtime`)
1536
- }
1537
- inlined.set(name, true)
1538
- return attr
1539
- }
1540
-
1541
- // never flatten if a prop isn't a valid static attribute
1542
- // only post prop-mapping
1543
- if (!variants[name] && !isValidStyleKey(name, staticConfig)) {
1544
- let out: any = null
1545
-
1546
- // for now passing empty props {}, a bit odd, need to at least document
1547
- // for now we don't expose custom components so just noting behavior
1548
- propMapper(name, styleValue, propMapperStyleState, false, (key, val) => {
1549
- out ||= {}
1550
- out[key] = val
1551
- })
1552
-
1553
- if (out) {
1554
- if (isTargetingHTML) {
1555
- // translate to DOM-compat
1556
- out = reactNativeWebInternals.createDOMProps(
1557
- isTextView ? 'span' : 'div',
1558
- out
1559
- )
1560
- // remove className - we dont use rnw styling
1561
- delete out.className
1562
- // remove style - rnw createDOMProps unconditionally emits a
1563
- // (possibly empty) style key, but we passed it a single
1564
- // non-style prop. Leaving it in causes Object.keys(out) to
1565
- // iterate twice and emit the original JSXAttribute twice
1566
- // (e.g. duplicate testID), breaking the DOM output.
1567
- delete out.style
1568
- }
1569
- }
1570
-
1571
- let didInline = false
1572
- const attributes = Object.keys(out).map((key) => {
1573
- const val = out[key]
1574
- const isStyle = isValidStyleKey(key, staticConfig)
1575
- if (isStyle) {
1576
- return {
1577
- type: 'style',
1578
- value: { [key]: styleValue },
1579
- name: key,
1580
- attr: path.node,
1581
- } as const
1582
- }
1583
- if (
1584
- validHTMLAttributes[key] ||
1585
- key.startsWith('aria-') ||
1586
- key.startsWith('data-') ||
1587
- // this is debug stuff added by vite / new jsx transform
1588
- key === '__source' ||
1589
- key === '__self'
1590
- ) {
1591
- if (
1592
- styleValue === FAILED_EVAL &&
1593
- key !== name &&
1594
- t.isJSXAttribute(attr.value)
1595
- ) {
1596
- // createDOMProps renamed the prop (e.g. testID -> data-testid).
1597
- // preserve the original expression value but use the new
1598
- // attribute name. restricted to FAILED_EVAL because the
1599
- // later `case 'attr'` rename pass only runs on
1600
- // statically-evaluable values; for static values that pass
1601
- // intentionally preserves some prop names (e.g. focusable
1602
- // in v2) instead of doing the createDOMProps rename.
1603
- return {
1604
- type: 'attr',
1605
- value: t.jsxAttribute(t.jsxIdentifier(key), attr.value.value),
1606
- } as const
1607
- }
1608
- return attr
1609
- }
1610
- if (shouldPrintDebug) {
1611
- logger.info(' ! inlining, non-static ' + key)
1612
- }
1613
- didInline = true
1614
- inlined.set(key, val)
1615
- return val
1616
- })
1617
-
1618
- // weird logic whats going on here
1619
- if (didInline) {
1620
- if (shouldPrintDebug) {
1621
- logger.info(
1622
- ` bailing flattening due to attributes ${attributes.map((x) =>
1623
- x.toString()
1624
- )}`
1625
- )
1626
- }
1627
- // bail
1628
- return attr
1629
- }
1630
-
1631
- // return evaluated attributes
1632
- return attributes
1633
- }
1634
-
1635
- // FAILED = dynamic or ternary, keep going
1636
- if (styleValue !== FAILED_EVAL) {
1637
- if (inlineWhenUnflattened.has(name)) {
1638
- // preserve original value for restoration
1639
- inlineWhenUnflattenedOGVals[name] = { styleValue, attr }
1640
- }
1641
-
1642
- if (isValidStyleKey(name, staticConfig)) {
1643
- // $theme- / $group- styles extract through the atomic-CSS pipeline
1644
- // (extractToClassNames → createMediaStyle), so they fall through to
1645
- // the normal style return below. The one case we still bail is
1646
- // $group-<name>-* when an ancestor element declares `group="<name>"`
1647
- // together with `untilMeasured` — the runtime measures the parent
1648
- // and only then emits child styles, which can't be modeled in CSS.
1649
- // $platform- can be flattened if the platform matches.
1650
- if (name[0] === '$') {
1651
- if (name.startsWith('$group-')) {
1652
- const groupName = name.slice('$group-'.length).split('-')[0]
1653
- if (groupName && hasUntilMeasuredAncestor(path, groupName)) {
1654
- if (shouldPrintDebug) {
1655
- logger.info(
1656
- ` ! group="${groupName}" ancestor has untilMeasured, not flattening: ${name}`
1657
- )
1658
- }
1659
- inlined.set(name, true)
1660
- return attr
1661
- }
1662
- }
1663
-
1664
- // $platform-web, $platform-native, $platform-ios, $platform-android, $platform-tv, $platform-androidtv, $platform-tvos
1665
- if (name.startsWith('$platform-')) {
1666
- const platformName = name.slice(10) // remove '$platform-'
1667
- const isMatchingPlatform =
1668
- platformName === platform ||
1669
- (platformName === 'native' && platform === 'native') ||
1670
- (platformName === 'web' && platform === 'web')
1671
-
1672
- if (isMatchingPlatform && typeof styleValue === 'object') {
1673
- // Flatten the inner styles directly
1674
- if (shouldPrintDebug) {
1675
- logger.info(
1676
- ` flattening $platform-${platformName}: ${JSON.stringify(styleValue)}`
1677
- )
1678
- }
1679
- return Object.entries(styleValue).map(([key, val]) => ({
1680
- type: 'style' as const,
1681
- value: { [key]: val },
1682
- name: key,
1683
- attr: path.node,
1684
- }))
1685
- } else {
1686
- // On native builds, sub-platform variants (android, ios, tv, androidtv, tvos)
1687
- // can't be resolved at compile time - leave for runtime evaluation
1688
- if (
1689
- platform === 'native' &&
1690
- nativeOnlyPlatforms.has(platformName)
1691
- ) {
1692
- if (shouldPrintDebug) {
1693
- logger.info(
1694
- ` ! keeping platform-specific style for runtime evaluation: ${name}`
1695
- )
1696
- }
1697
- inlined.set(name, true)
1698
- return attr
1699
- }
1700
- // Platform doesn't match, skip these styles entirely
1701
- if (shouldPrintDebug) {
1702
- logger.info(` ! skipping non-matching platform style: ${name}`)
1703
- }
1704
- return []
1705
- }
1706
- }
1707
- }
1708
- if (shouldPrintDebug) {
1709
- logger.info(` style: ${name} = ${JSON.stringify(styleValue)}`)
1710
- }
1711
- if (!(name in defaultProps)) {
1712
- if (!hasSetOptimized) {
1713
- res.optimized++
1714
- hasSetOptimized = true
1715
- }
1716
- }
1717
- return {
1718
- type: 'style',
1719
- value: { [name]: styleValue },
1720
- name,
1721
- attr: path.node,
1722
- }
1723
- }
1724
- if (variants[name]) {
1725
- variantValues.set(name, styleValue)
1726
- }
1727
- inlined.set(name, true)
1728
- return attr
1729
- }
1730
-
1731
- // ternaries!
1732
-
1733
- // binary ternary, we can eventually make this smarter but step 1
1734
- // basically for the common use case of:
1735
- // opacity={(conditional ? 0 : 1) * scale}
1736
- if (t.isBinaryExpression(value)) {
1737
- if (shouldPrintDebug) {
1738
- logger.info(` binary expression ${name} = ${value}`)
1739
- }
1740
- const { operator, left, right } = value
1741
- // if one side is a ternary, and the other side is evaluatable, we can maybe extract
1742
- const lVal = attemptEvalSafe(left)
1743
- const rVal = attemptEvalSafe(right)
1744
- if (shouldPrintDebug) {
1745
- logger.info(
1746
- ` evalBinaryExpression lVal ${String(lVal)}, rVal ${String(rVal)}`
1747
- )
1748
- }
1749
- if (lVal !== FAILED_EVAL && t.isConditionalExpression(right)) {
1750
- const ternary = addBinaryConditional(operator, left, right)
1751
- if (ternary) return ternary
1752
- }
1753
- if (rVal !== FAILED_EVAL && t.isConditionalExpression(left)) {
1754
- const ternary = addBinaryConditional(operator, right, left)
1755
- if (ternary) return ternary
1756
- }
1757
- if (shouldPrintDebug) {
1758
- logger.info(` evalBinaryExpression cant extract`)
1759
- }
1760
- inlined.set(name, true)
1761
- return attr
1762
- }
1763
-
1764
- const staticConditional = getStaticConditional(value)
1765
- if (staticConditional) {
1766
- if (shouldPrintDebug === 'verbose') {
1767
- logger.info(` static conditional ${name} ${value}`)
1768
- }
1769
- return { type: 'ternary', value: staticConditional }
1770
- }
1771
-
1772
- const staticLogical = getStaticLogical(value)
1773
- if (staticLogical) {
1774
- if (shouldPrintDebug === 'verbose') {
1775
- logger.info(` static ternary ${name} = ${value}`)
1776
- }
1777
- return { type: 'ternary', value: staticLogical }
1778
- }
1779
-
1780
- // if we've made it this far, the prop stays inline
1781
- inlined.set(name, true)
1782
- if (shouldPrintDebug) {
1783
- logger.info(` ! inline no match ${name} ${value}`)
1784
- }
1785
-
1786
- //
1787
- // RETURN ATTR
1788
- //
1789
- return attr
1790
-
1791
- // attr helpers:
1792
- function addBinaryConditional(
1793
- operator: any,
1794
- staticExpr: any,
1795
- cond: t.ConditionalExpression
1796
- ): ExtractedAttr | null {
1797
- if (getStaticConditional(cond)) {
1798
- const alt = attemptEval(
1799
- t.binaryExpression(operator, staticExpr, cond.alternate)
1800
- )
1801
- const cons = attemptEval(
1802
- t.binaryExpression(operator, staticExpr, cond.consequent)
1803
- )
1804
- if (shouldPrintDebug) {
1805
- logger.info([' binaryConditional', cond.test, cons, alt].join(' '))
1806
- }
1807
- return {
1808
- type: 'ternary',
1809
- value: {
1810
- test: cond.test,
1811
- remove,
1812
- alternate: { [name]: alt },
1813
- consequent: { [name]: cons },
1814
- },
1815
- }
1816
- }
1817
- return null
1818
- }
1819
-
1820
- function getStaticConditional(value: t.Node): Ternary | null {
1821
- if (t.isConditionalExpression(value)) {
1822
- try {
1823
- const aVal = attemptEval(value.alternate)
1824
- const cVal = attemptEval(value.consequent)
1825
- if (shouldPrintDebug) {
1826
- const type = value.test.type
1827
- logger.info([' static ternary', type, cVal, aVal].join(' '))
1828
- }
1829
- return {
1830
- test: value.test,
1831
- remove,
1832
- consequent: { [name]: cVal },
1833
- alternate: { [name]: aVal },
1834
- }
1835
- } catch (err: any) {
1836
- if (shouldPrintDebug) {
1837
- logger.info([' cant eval ternary', err.message].join(' '))
1838
- }
1839
- }
1840
- }
1841
- return null
1842
- }
1843
-
1844
- function getStaticLogical(value: t.Node): Ternary | null {
1845
- if (t.isLogicalExpression(value)) {
1846
- if (value.operator === '&&') {
1847
- try {
1848
- const val = attemptEval(value.right)
1849
- if (shouldPrintDebug) {
1850
- logger.info([' staticLogical', value.left, name, val].join(' '))
1851
- }
1852
- return {
1853
- test: value.left,
1854
- remove,
1855
- consequent: { [name]: val },
1856
- alternate: null,
1857
- }
1858
- } catch (err) {
1859
- if (shouldPrintDebug) {
1860
- logger.info([' cant static eval logical', err].join(' '))
1861
- }
1862
- }
1863
- }
1864
- }
1865
- return null
1866
- }
1867
- } // END function evaluateAttribute
1868
-
1869
- function isStaticObject(obj: t.Node): obj is t.ObjectExpression {
1870
- return (
1871
- t.isObjectExpression(obj) &&
1872
- obj.properties.every((prop) => {
1873
- if (!t.isObjectProperty(prop)) {
1874
- // console.warn('not an object prop?', prop)
1875
- return false
1876
- }
1877
- const propName = prop.key['name']
1878
- if (!isValidStyleKey(propName, staticConfig) && propName !== 'render') {
1879
- if (shouldPrintDebug) {
1880
- logger.info([' not a valid style prop!', propName].join(' '))
1881
- }
1882
- return false
1883
- }
1884
- return true
1885
- })
1886
- )
1887
- }
1888
-
1889
- // side = {
1890
- // color: 'red',
1891
- // background: x ? 'red' : 'green',
1892
- // $gtSm: { color: 'green' }
1893
- // }
1894
- // => Ternary<test, { color: 'red' }, null>
1895
- // => Ternary<test && x, { background: 'red' }, null>
1896
- // => Ternary<test && !x, { background: 'green' }, null>
1897
- // => Ternary<test && '$gtSm', { color: 'green' }, null>
1898
- function flattenNestedTernaries(
1899
- test: t.Expression,
1900
- side: t.Expression | null,
1901
- ternaryPartial: Partial<Ternary> = {}
1902
- ): null | Ternary[] {
1903
- if (!side) {
1904
- return null
1905
- }
1906
- if (!isStaticObject(side)) {
1907
- throw new Error('not extractable')
1908
- }
1909
- return side.properties.flatMap((property) => {
1910
- if (!t.isObjectProperty(property)) {
1911
- throw new Error('expected object property')
1912
- }
1913
- // this could be a recurse here if we want to get fancy
1914
- if (t.isConditionalExpression(property.value)) {
1915
- // merge up into the parent conditional, split into two
1916
- const [truthy, falsy] = [
1917
- t.objectExpression([
1918
- t.objectProperty(property.key, property.value.consequent),
1919
- ]),
1920
- t.objectExpression([
1921
- t.objectProperty(property.key, property.value.alternate),
1922
- ]),
1923
- ].map((x) => attemptEval(x))
1924
- return [
1925
- createTernary({
1926
- remove() {},
1927
- ...ternaryPartial,
1928
- test: t.logicalExpression('&&', test, property.value.test),
1929
- consequent: truthy,
1930
- alternate: null,
1931
- }),
1932
- createTernary({
1933
- ...ternaryPartial,
1934
- test: t.logicalExpression(
1935
- '&&',
1936
- test,
1937
- t.unaryExpression('!', property.value.test)
1938
- ),
1939
- consequent: falsy,
1940
- alternate: null,
1941
- remove() {},
1942
- }),
1943
- ]
1944
- }
1945
- const obj = t.objectExpression([
1946
- t.objectProperty(property.key, property.value),
1947
- ])
1948
- const consequent = attemptEval(obj)
1949
- return createTernary({
1950
- remove() {},
1951
- ...ternaryPartial,
1952
- test,
1953
- consequent,
1954
- alternate: null,
1955
- })
1956
- })
1957
- }
1958
-
1959
- if (couldntParse || shouldDeopt) {
1960
- if (shouldPrintDebug) {
1961
- logger.info(
1962
- [` avoid optimizing:`, { couldntParse, shouldDeopt }].join(' ')
1963
- )
1964
- }
1965
- node.attributes = ogAttributes
1966
- return
1967
- }
1968
-
1969
- // before deopt, can still optimize
1970
- const parentFn = findTopmostFunction(traversePath)
1971
- if (parentFn) {
1972
- modifiedComponents.add(parentFn)
1973
- }
1974
-
1975
- // flatten logic!
1976
- // fairly simple check to see if all children are text
1977
- const hasSpread = attrs.some(
1978
- (x) => x.type === 'attr' && t.isJSXSpreadAttribute(x.value)
1979
- )
1980
-
1981
- const hasOnlyStringChildren =
1982
- !hasSpread &&
1983
- (node.selfClosing ||
1984
- (traversePath.node.children &&
1985
- traversePath.node.children.every((x) => x.type === 'JSXText')))
1986
-
1987
- let themeVal = inlined.get('theme')
1988
-
1989
- // on native we can't flatten when theme prop is set
1990
- if (platform !== 'native') {
1991
- inlined.delete('theme')
1992
- }
1993
-
1994
- for (const [key] of inlined) {
1995
- const isStaticObjectVariant =
1996
- staticConfig.variants?.[key] && variantValues.has(key)
1997
- if (INLINE_EXTRACTABLE[key] || isStaticObjectVariant) {
1998
- inlined.delete(key)
1999
- }
2000
- }
2001
-
2002
- const canFlattenProps = inlined.size === 0
2003
-
2004
- let shouldFlatten = Boolean(
2005
- flatNodeName &&
2006
- !shouldDeopt &&
2007
- canFlattenProps &&
2008
- !hasSpread &&
2009
- !staticConfig.isStyledHOC &&
2010
- !staticConfig.isHOC &&
2011
- !staticConfig.isReactNative &&
2012
- staticConfig.neverFlatten !== true &&
2013
- (staticConfig.neverFlatten === 'jsx' ? hasOnlyStringChildren : true)
2014
- )
2015
-
2016
- const usedThemeKeys = new Set<string>()
2017
- // if it accesses any theme values during evaluation
2018
- themeAccessListeners.add((key) => {
2019
- if (disableExtractVariables) {
2020
- usedThemeKeys.add(key)
2021
- shouldFlatten = false
2022
- if (shouldPrintDebug === 'verbose') {
2023
- logger.info([' ! accessing theme key, avoid flatten', key].join(' '))
2024
- }
2025
- }
2026
- })
2027
-
2028
- if (!shouldFlatten) {
2029
- if (shouldPrintDebug) {
2030
- logger.info(
2031
- `Deopting ${JSON.stringify({
2032
- shouldFlatten,
2033
- shouldDeopt,
2034
- canFlattenProps,
2035
- hasSpread,
2036
- neverFlatten: staticConfig.neverFlatten,
2037
- })}`
2038
- )
2039
- }
2040
- node.attributes = ogAttributes
2041
- return
2042
- }
2043
-
2044
- // ensure the default styles are there
2045
- let skipMap = false
2046
- const defaultStyleAttrs = Object.keys(defaultProps).flatMap((key) => {
2047
- if (skipMap) return []
2048
- const value = defaultProps[key]
2049
- if (key === 'theme' && !themeVal) {
2050
- if (platform === 'native') {
2051
- shouldFlatten = false
2052
- skipMap = true
2053
- inlined.set('theme', { value: t.stringLiteral(value) })
2054
- }
2055
- themeVal = { value: t.stringLiteral(value) }
2056
- return []
2057
- }
2058
- if (!isValidStyleKey(key, staticConfig)) {
2059
- return []
2060
- }
2061
- const name = tamaguiConfig?.shorthands[key] || key
2062
- if (value === undefined) {
2063
- logger.warn(
2064
- `⚠️ Error evaluating default style for component, prop ${key} ${value}`
2065
- )
2066
- shouldDeopt = true
2067
- return
2068
- }
2069
- if (name[0] === '$' && mediaQueryConfig[name.slice(1)]) {
2070
- defaultProps[key] = undefined
2071
- return evaluateAttribute({
2072
- node: t.jsxAttribute(
2073
- t.jsxIdentifier(name),
2074
- t.jsxExpressionContainer(
2075
- t.objectExpression(
2076
- Object.keys(value)
2077
- .filter((k) => {
2078
- return typeof value[k] !== 'undefined'
2079
- })
2080
- .map((k) => {
2081
- return t.objectProperty(t.identifier(k), literalToAst(value[k]))
2082
- })
2083
- )
2084
- )
2085
- ),
2086
- } as any)
2087
- }
2088
- const attr: ExtractedAttrStyle = {
2089
- type: 'style',
2090
- name,
2091
- value: { [name]: value },
2092
- }
2093
- return attr
2094
- }) as ExtractedAttr[]
2095
-
2096
- if (!skipMap) {
2097
- if (defaultStyleAttrs.length) {
2098
- attrs = [...defaultStyleAttrs, ...attrs]
2099
- }
2100
- }
2101
-
2102
- // combine ternaries
2103
- let ternaries: Ternary[] = []
2104
- attrs = attrs
2105
- .reduce<(ExtractedAttr | ExtractedAttr[])[]>((out, cur) => {
2106
- const next = attrs[attrs.indexOf(cur) + 1]
2107
- if (cur.type === 'ternary') {
2108
- ternaries.push(cur.value)
2109
- }
2110
- if ((!next || next.type !== 'ternary') && ternaries.length) {
2111
- // finish, process
2112
- const normalized = normalizeTernaries(ternaries).map(
2113
- ({ alternate, consequent, ...rest }) => {
2114
- return {
2115
- type: 'ternary' as const,
2116
- value: {
2117
- ...rest,
2118
- alternate: alternate || null,
2119
- consequent: consequent || null,
2120
- },
2121
- }
2122
- }
2123
- )
2124
- try {
2125
- return [...out, ...normalized]
2126
- } finally {
2127
- if (shouldPrintDebug) {
2128
- logger.info(
2129
- ` normalizeTernaries (${ternaries.length} => ${normalized.length})`
2130
- )
2131
- }
2132
- ternaries = []
2133
- }
2134
- }
2135
- if (cur.type === 'ternary') {
2136
- return out
2137
- }
2138
- out.push(cur)
2139
- return out
2140
- }, [])
2141
- .flat()
2142
-
2143
- const ternaryBranchProps = new Map<string, Ternary>()
2144
- const allTernaries = attrs.flatMap((attr) =>
2145
- attr.type === 'ternary' ? attr.value : []
2146
- )
2147
- if (allTernaries.length > 1) {
2148
- for (const ternary of normalizeTernaries(allTernaries)) {
2149
- ternaryBranchProps.set(getTernaryKey(ternary.test), ternary)
2150
- }
2151
- }
2152
-
2153
- // wrap theme around children on flatten
2154
- // account for shouldFlatten could change w the above block "if (disableExtractVariables)"
2155
- if (themeVal) {
2156
- if (!programPath) {
2157
- console.warn(
2158
- `No program path found, avoiding importing flattening / importing theme in ${sourcePath}`
2159
- )
2160
- } else {
2161
- if (shouldPrintDebug) {
2162
- logger.info([' - wrapping theme', themeVal].join(' '))
2163
- }
2164
-
2165
- // remove theme attribute from flattened node
2166
- attrs = attrs.filter(
2167
- (x) =>
2168
- !(
2169
- x.type === 'attr' &&
2170
- t.isJSXAttribute(x.value) &&
2171
- x.value.name.name === 'theme'
2172
- )
2173
- )
2174
-
2175
- // add import
2176
- if (!hasImportedTheme) {
2177
- hasImportedTheme = true
2178
- programPath.node.body.push(
2179
- t.importDeclaration(
2180
- [
2181
- t.importSpecifier(
2182
- t.identifier('_TamaguiTheme'),
2183
- t.identifier('Theme')
2184
- ),
2185
- ],
2186
- t.stringLiteral('@tamagui/web')
2187
- )
2188
- )
2189
- }
2190
-
2191
- traversePath.replaceWith(
2192
- t.jsxElement(
2193
- t.jsxOpeningElement(t.jsxIdentifier('_TamaguiTheme'), [
2194
- t.jsxAttribute(t.jsxIdentifier('name'), themeVal.value),
2195
- ]),
2196
- t.jsxClosingElement(t.jsxIdentifier('_TamaguiTheme')),
2197
- [traversePath.node]
2198
- )
2199
- )
2200
- }
2201
- }
2202
-
2203
- if (shouldPrintDebug) {
2204
- logger.info(
2205
- [' - attrs (flattened): \n', logLines(attrs.map(attrStr).join(', '))].join(
2206
- ' '
2207
- )
2208
- )
2209
- }
2210
-
2211
- function mergeToEnd(obj: object, key: string, val: any) {
2212
- if (key in obj) {
2213
- delete obj[key]
2214
- }
2215
- obj[key] = val
2216
- }
2217
-
2218
- // preserves order
2219
- function normalizeStyleWithoutVariants(style: any) {
2220
- let res = {}
2221
- for (const key in style) {
2222
- if (staticConfig.variants && key in staticConfig.variants) {
2223
- mergeToEnd(res, key, style[key])
2224
- } else {
2225
- const expanded = normalizeStyle({ [key]: style[key] }, true)
2226
- for (const key in expanded) {
2227
- mergeToEnd(res, key, expanded[key])
2228
- }
2229
- }
2230
- }
2231
- return res
2232
- }
2233
-
2234
- // evaluates all static attributes into a simple object
2235
- let foundStaticProps = {}
2236
-
2237
- for (const key in attrs) {
2238
- const cur = attrs[key]
2239
- if (cur.type === 'style') {
2240
- // remove variants because they are processed later, and can lead to invalid values here
2241
- // see <Spacer flex /> where flex looks like a valid style, but is a variant
2242
- const expanded = normalizeStyleWithoutVariants(cur.value)
2243
- // preserve order
2244
- for (const key in expanded) {
2245
- mergeToEnd(foundStaticProps, key, expanded[key])
2246
- }
2247
- continue
2248
- }
2249
- if (cur.type === 'attr') {
2250
- if (t.isJSXSpreadAttribute(cur.value)) {
2251
- continue
2252
- }
2253
- if (!t.isJSXIdentifier(cur.value.name)) {
2254
- continue
2255
- }
2256
- const key = cur.value.name.name
2257
- // undefined = boolean true
2258
- const value = attemptEvalSafe(cur.value.value || t.booleanLiteral(true))
2259
- if (value !== FAILED_EVAL) {
2260
- mergeToEnd(foundStaticProps, key, value)
2261
- }
2262
- }
2263
- }
2264
-
2265
- // must preserve exact order
2266
- const completeProps = {}
2267
- for (const key in defaultProps) {
2268
- if (!(key in foundStaticProps)) {
2269
- completeProps[key] = defaultProps[key]
2270
- }
2271
- }
2272
- for (const key in foundStaticProps) {
2273
- completeProps[key] = foundStaticProps[key]
2274
- }
2275
-
2276
- // expand shorthands, de-opt variables
2277
- attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
2278
- if (!cur) return acc
2279
- if (cur.type === 'attr' && !t.isJSXSpreadAttribute(cur.value)) {
2280
- if (shouldFlatten) {
2281
- const name = cur.value.name.name
2282
- if (typeof name === 'string') {
2283
- if (name === 'render') {
2284
- // remove render=""
2285
- return acc
2286
- }
2287
-
2288
- // if flattening, expand variants
2289
- if (variants[name] && variantValues.has(name)) {
2290
- const styleState = {
2291
- ...propMapperStyleState,
2292
- props: completeProps,
2293
- }
2294
-
2295
- let out: Record<string, any> = {}
2296
- propMapper(
2297
- name,
2298
- variantValues.get(name),
2299
- styleState,
2300
- false,
2301
- (key, val) => {
2302
- out[key] = val
2303
- }
2304
- )
2305
-
2306
- if (out && isTargetingHTML) {
2307
- const cn = out.className
2308
- // translate to DOM-compat
2309
- out = reactNativeWebInternals.createDOMProps(
2310
- isTextView ? 'span' : 'div',
2311
- out
2312
- )
2313
- // remove rnw className use ours
2314
- out.className = cn
2315
- // see note in single-prop branch above; createDOMProps
2316
- // also emits a stray style key here that would duplicate
2317
- // emitted JSXAttributes downstream.
2318
- delete out.style
2319
- }
2320
- if (shouldPrintDebug) {
2321
- logger.info([' - expanded variant', name, out].join(' '))
2322
- }
2323
- for (const key in out) {
2324
- const value = out[key]
2325
- if (isValidStyleKey(key, staticConfig)) {
2326
- acc.push({
2327
- type: 'style',
2328
- value: { [key]: value },
2329
- name: key,
2330
- attr: cur.value,
2331
- } as const)
2332
- } else {
2333
- acc.push({
2334
- type: 'attr',
2335
- value: t.jsxAttribute(
2336
- t.jsxIdentifier(key),
2337
- t.jsxExpressionContainer(
2338
- typeof value === 'string'
2339
- ? t.stringLiteral(value)
2340
- : literalToAst(value)
2341
- )
2342
- ),
2343
- })
2344
- }
2345
- }
2346
- }
2347
- }
2348
- }
2349
- }
2350
-
2351
- if (cur.type !== 'style') {
2352
- acc.push(cur)
2353
- return acc
2354
- }
2355
-
2356
- let key = Object.keys(cur.value)[0]
2357
- const value = cur.value[key]
2358
- const fullKey = tamaguiConfig?.shorthands[key]
2359
- // expand shorthands
2360
- if (fullKey) {
2361
- cur.value = { [fullKey]: value }
2362
- key = fullKey
2363
- }
2364
-
2365
- // finally we have all styles + expansions, lets see if we need to skip
2366
- // any and keep them as attrs
2367
- if (disableExtractVariables) {
2368
- if (
2369
- value[0] === '$' &&
2370
- (usedThemeKeys.has(key) || usedThemeKeys.has(fullKey))
2371
- ) {
2372
- if (shouldPrintDebug) {
2373
- logger.info([` keeping variable inline: ${key} =`, value].join(' '))
2374
- }
2375
- acc.push({
2376
- type: 'attr',
2377
- value: t.jsxAttribute(
2378
- t.jsxIdentifier(key),
2379
- t.jsxExpressionContainer(t.stringLiteral(value))
2380
- ),
2381
- })
2382
- return acc
2383
- }
2384
- }
2385
-
2386
- acc.push(cur)
2387
- return acc
2388
- }, [])
2389
-
2390
- tm.mark('jsx-element-expanded', !!shouldPrintDebug)
2391
- if (shouldPrintDebug) {
2392
- logger.info(
2393
- [' - attrs (expanded): \n', logLines(attrs.map(attrStr).join(', '))].join(
2394
- ' '
2395
- )
2396
- )
2397
- }
2398
-
2399
- // merge styles, leave undefined values
2400
- let prev: ExtractedAttr | null = null
2401
-
2402
- function mergeStyles(
2403
- prev: ViewStyle & PseudoStyles,
2404
- next: ViewStyle & PseudoStyles
2405
- ) {
2406
- for (const key in next) {
2407
- // merge pseudos
2408
- if (pseudoDescriptors[key]) {
2409
- prev[key] = prev[key] || {}
2410
- Object.assign(prev[key], next[key])
2411
- } else {
2412
- mergeToEnd(prev, key, next[key])
2413
- }
2414
- }
2415
- }
2416
-
2417
- // post process
2418
- const getProps = (
2419
- props: object | null,
2420
- includeProps = false,
2421
- debugName = '',
2422
- branchProps?: object | null
2423
- ) => {
2424
- if (!props) {
2425
- if (shouldPrintDebug) logger.info([' getProps() no props'].join(' '))
2426
- return {}
2427
- }
2428
- if (excludeProps?.size) {
2429
- for (const key in props) {
2430
- if (excludeProps.has(key)) {
2431
- if (shouldPrintDebug) logger.info([' delete excluded', key].join(' '))
2432
- delete props[key]
2433
- }
2434
- }
2435
- }
2436
-
2437
- const before = process.env.IS_STATIC
2438
- process.env.IS_STATIC = 'is_static'
2439
- try {
2440
- const fallbackProps = branchProps
2441
- ? { ...completeProps, ...branchProps }
2442
- : completeProps
2443
-
2444
- // $group-* / $theme-* keys carry block-form style objects that
2445
- // getSplitStyles drops in static mode (no parent group context,
2446
- // no theme value to read). Pluck them out so the atomic-CSS
2447
- // pipeline in extractToClassNames can emit @container / theme
2448
- // rules for them directly.
2449
- let extractedMediaLikeProps: Record<string, any> | null = null
2450
- let propsForSplit: any = props
2451
- for (const k in props) {
2452
- if (
2453
- k[0] === '$' &&
2454
- (k.startsWith('$group-') || k.startsWith('$theme-'))
2455
- ) {
2456
- if (propsForSplit === props) {
2457
- propsForSplit = { ...props }
2458
- }
2459
- if (
2460
- platform === 'native' &&
2461
- k.startsWith('$group-') &&
2462
- getGroupPseudo(k) === 'hover'
2463
- ) {
2464
- delete propsForSplit[k]
2465
- continue
2466
- }
2467
- extractedMediaLikeProps ||= {}
2468
- extractedMediaLikeProps[k] = propsForSplit[k]
2469
- delete propsForSplit[k]
2470
- }
2471
- }
2472
-
2473
- const out = getSplitStyles(
2474
- propsForSplit,
2475
- staticConfig,
2476
- defaultTheme,
2477
- '',
2478
- componentState,
2479
- {
2480
- ...styleProps,
2481
- noClass: true,
2482
- fallbackProps,
2483
- ...(platform === 'native' && {
2484
- resolveValues: 'except-theme',
2485
- }),
2486
- },
2487
- undefined,
2488
- undefined,
2489
- undefined,
2490
- undefined,
2491
- false,
2492
- debugPropValue || shouldPrintDebug
2493
- )!
2494
-
2495
- // resolve tokens inside the plucked blocks: they skipped the main
2496
- // split, so values like "$color5" would flow raw into the emitted
2497
- // CSS where browsers reject the declaration. run each block through
2498
- // its own static split so tokens become var(--x) references.
2499
- // (native never extracts these — it deopts below — so web only.)
2500
- if (extractedMediaLikeProps && platform !== 'native') {
2501
- for (const k in extractedMediaLikeProps) {
2502
- const block = extractedMediaLikeProps[k]
2503
- if (!block || typeof block !== 'object') continue
2504
- const blockOut = getSplitStyles(
2505
- block,
2506
- staticConfig,
2507
- defaultTheme,
2508
- '',
2509
- componentState,
2510
- {
2511
- ...styleProps,
2512
- noClass: true,
2513
- fallbackProps,
2514
- },
2515
- undefined,
2516
- undefined,
2517
- undefined,
2518
- undefined,
2519
- false,
2520
- debugPropValue || shouldPrintDebug
2521
- )
2522
- if (blockOut) {
2523
- extractedMediaLikeProps[k] = {
2524
- ...blockOut.style,
2525
- ...blockOut.pseudos,
2526
- }
2527
- }
2528
- }
2529
- }
2530
-
2531
- let outProps = {
2532
- ...(includeProps ? out.viewProps : {}),
2533
- ...out.style,
2534
- ...out.pseudos,
2535
- ...extractedMediaLikeProps,
2536
- }
2537
-
2538
- // check de-opt props again
2539
- for (const key in outProps) {
2540
- if (deoptProps.has(key)) {
2541
- shouldFlatten = false
2542
- }
2543
- // native has no atomic-CSS sink for theme- / group- pseudo
2544
- // blocks; if they flatten they get serialized under the literal
2545
- // `$theme-…` / `$group-…` key into the RN StyleSheet, where the
2546
- // runtime's @container / theme-name matching never runs. de-opt
2547
- // → preserve as inline prop so getSplitStyles handles them at
2548
- // render time.
2549
- if (
2550
- platform === 'native' &&
2551
- key[0] === '$' &&
2552
- (key.startsWith('$theme-') ||
2553
- (key.startsWith('$group-') && getGroupPseudo(key) !== 'hover') ||
2554
- // same story for a breakpoint a variant brought with it:
2555
- // native matches media at render time, so there is nothing
2556
- // to write it to here
2557
- mediaQueryConfig[key.slice(1)])
2558
- ) {
2559
- shouldFlatten = false
2560
- }
2561
- }
2562
-
2563
- if (shouldPrintDebug) {
2564
- logger.info(`(${debugName})`)
2565
- // prettier-ignore
2566
- logger.info(`\n getProps (props in): ${logLines(objToStr(props))}`)
2567
- // prettier-ignore
2568
- logger.info(
2569
- `\n getProps (outProps): ${logLines(objToStr(outProps))}`
2570
- )
2571
- }
2572
-
2573
- if (out.fontFamily) {
2574
- setPropsToFontFamily(outProps, out.fontFamily)
2575
- if (shouldPrintDebug) {
2576
- logger.info(`\n 💬 new font fam: ${out.fontFamily}`)
2577
- }
2578
- }
2579
-
2580
- return outProps
2581
- } catch (err: any) {
2582
- logger.info(['error', err.message, err.stack].join(' '))
2583
- return {}
2584
- } finally {
2585
- process.env.IS_STATIC = before
2586
- }
2587
- }
2588
-
2589
- // add default props
2590
- attrs.unshift({
2591
- type: 'style',
2592
- value: defaultProps,
2593
- })
2594
-
2595
- attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
2596
- if (cur.type === 'style') {
2597
- const keys = Object.keys(cur.value || {})
2598
- if (!keys.length) {
2599
- return acc
2600
- }
2601
- const key = keys[0]
2602
- const value = cur.value[key]
2603
-
2604
- // Check if this is a media-like key ($theme-, $platform-, $group-, or $mediaQuery)
2605
- const isMediaLikeKey =
2606
- key[0] === '$' &&
2607
- (key.startsWith('$theme-') ||
2608
- key.startsWith('$platform-') ||
2609
- key.startsWith('$group-') ||
2610
- mediaQueryConfig[key.slice(1)])
2611
-
2612
- const shouldKeepOriginalAttr =
2613
- // !isStyleAndAttr[key] &&
2614
- !shouldFlatten &&
2615
- // de-opt if non-style
2616
- !validStyles[key] &&
2617
- !pseudoDescriptors[key] &&
2618
- !isMediaLikeKey &&
2619
- !(key.startsWith('data-') || key.startsWith('aria-'))
2620
-
2621
- if (shouldKeepOriginalAttr) {
2622
- if (shouldPrintDebug) {
2623
- logger.info([' - keeping as non-style', key].join(' '))
2624
- }
2625
- prev = cur
2626
- acc.push({
2627
- type: 'attr',
2628
- value: t.jsxAttribute(
2629
- t.jsxIdentifier(key),
2630
- t.jsxExpressionContainer(
2631
- typeof value === 'string'
2632
- ? t.stringLiteral(value)
2633
- : literalToAst(value)
2634
- )
2635
- ),
2636
- })
2637
- acc.push(cur)
2638
- return acc
2639
- }
2640
-
2641
- if (prev?.type === 'style') {
2642
- mergeStyles(prev.value, cur.value)
2643
- return acc
2644
- }
2645
- }
2646
-
2647
- if (cur.type === 'style') {
2648
- prev = cur
2649
- }
2650
- acc.push(cur)
2651
- return acc
2652
- }, [])
2653
-
2654
- if (shouldPrintDebug) {
2655
- logger.info(
2656
- [
2657
- ' - attrs (combined 🔀): \n',
2658
- logLines(attrs.map(attrStr).join(', ')),
2659
- ].join(' ')
2660
- )
2661
- }
2662
-
2663
- let getStyleError: any = null
2664
-
2665
- // a variant can carry its own media blocks ($md/$lg inside a variant
2666
- // value). getSplitStyles leaves those nested rather than folding them
2667
- // into the flat style, so hand them to the same media path a $md/$lg
2668
- // prop takes. they go on the end because a variant is a prop, and so
2669
- // outranks the media the styled() frame declared in its defaults.
2670
- const mediaFromVariants: ExtractedAttr[] = []
2671
-
2672
- // fix up ternaries, combine final style values
2673
- for (const attr of attrs) {
2674
- try {
2675
- if (shouldPrintDebug) {
2676
- console.info(` Processing ${attr.type}:`)
2677
- }
2678
-
2679
- switch (attr.type) {
2680
- case 'ternary': {
2681
- const branchProps = ternaryBranchProps.get(
2682
- getTernaryKey(attr.value.test)
2683
- )
2684
- const a = getProps(
2685
- attr.value.alternate,
2686
- false,
2687
- 'ternary.alternate',
2688
- branchProps?.alternate
2689
- )
2690
- const c = getProps(
2691
- attr.value.consequent,
2692
- false,
2693
- 'ternary.consequent',
2694
- branchProps?.consequent
2695
- )
2696
- if (a) attr.value.alternate = a
2697
- if (c) attr.value.consequent = c
2698
- if (shouldPrintDebug)
2699
- logger.info([' => tern ', attrStr(attr)].join(' '))
2700
- continue
2701
- }
2702
- case 'style': {
2703
- // expand variants and such
2704
- const styles = getProps(attr.value, false, 'style')
2705
- if (styles) {
2706
- for (const key in styles) {
2707
- if (key[0] !== '$') continue
2708
- const mediaKey = key.slice(1)
2709
- if (!mediaQueryConfig[mediaKey]) continue
2710
- mediaFromVariants.push({
2711
- type: 'ternary',
2712
- value: {
2713
- inlineMediaQuery: mediaKey,
2714
- test: t.stringLiteral(mediaKey),
2715
- consequent: styles[key],
2716
- alternate: null,
2717
- remove() {},
2718
- },
2719
- })
2720
- delete styles[key]
2721
- }
2722
- // @ts-ignore
2723
- attr.value = styles
2724
- }
2725
- // prettier-ignore
2726
- if (shouldPrintDebug)
2727
- logger.info(
2728
- [' * styles (in)', logLines(objToStr(attr.value))].join(' ')
2729
- )
2730
- // prettier-ignore
2731
- if (shouldPrintDebug)
2732
- logger.info(
2733
- [' * styles (out)', logLines(objToStr(styles))].join(' ')
2734
- )
2735
- continue
2736
- }
2737
- case 'attr': {
2738
- if (shouldFlatten && t.isJSXAttribute(attr.value)) {
2739
- // we know all attributes are static
2740
- // this only does one at a time but it should really do the whole group together...
2741
- // also awkward to be doing it using jsxAttributes...
2742
- const key = attr.value.name.name as string
2743
-
2744
- // dont process style/className can just stay attrs
2745
- if (key === 'style' || key === 'className' || key === 'render') {
2746
- continue
2747
- }
2748
-
2749
- // undefined = boolean true
2750
- const value = attemptEvalSafe(
2751
- attr.value.value || t.booleanLiteral(true)
2752
- )
2753
- if (value !== FAILED_EVAL) {
2754
- const outProps = getProps({ [key]: value }, true, `attr.${key}`)
2755
- const outKey = Object.keys(outProps)[0]
2756
- if (outKey) {
2757
- const outVal = outProps[outKey]
2758
- attr.value = t.jsxAttribute(
2759
- t.jsxIdentifier(outKey),
2760
- t.jsxExpressionContainer(
2761
- typeof outVal === 'string'
2762
- ? t.stringLiteral(outVal)
2763
- : literalToAst(outVal)
2764
- )
2765
- )
2766
- }
2767
- }
2768
- }
2769
- }
2770
- }
2771
- } catch (err) {
2772
- // any error de-opt
2773
- getStyleError = err
2774
- }
2775
- }
2776
-
2777
- if (mediaFromVariants.length) {
2778
- attrs.push(...mediaFromVariants)
2779
- }
2780
-
2781
- if (shouldPrintDebug) {
2782
- // prettier-ignore
2783
- logger.info(
2784
- [
2785
- ' - attrs (ternaries/combined):\n',
2786
- logLines(attrs.map(attrStr).join(', ')),
2787
- ].join(' ')
2788
- )
2789
- }
2790
-
2791
- tm.mark('jsx-element-styles', !!shouldPrintDebug)
2792
-
2793
- if (getStyleError) {
2794
- logger.info([' ⚠️ postprocessing error, deopt', getStyleError].join(' '))
2795
- node.attributes = ogAttributes
2796
- return null
2797
- }
2798
-
2799
- // final lazy extra loop:
2800
- const existingStyleKeys = new Set()
2801
- for (let i = attrs.length - 1; i >= 0; i--) {
2802
- const attr = attrs[i]
2803
-
2804
- // if flattening map inline props to proper flattened names
2805
- if (shouldFlatten) {
2806
- if (attr.type === 'attr') {
2807
- if (t.isJSXAttribute(attr.value)) {
2808
- if (t.isJSXIdentifier(attr.value.name)) {
2809
- const name = attr.value.name.name
2810
- if (INLINE_EXTRACTABLE[name]) {
2811
- // map to HTML only name
2812
- attr.value.name.name = INLINE_EXTRACTABLE[name]
2813
- }
2814
- }
2815
- }
2816
- }
2817
- }
2818
-
2819
- // remove duplicate styles
2820
- // so if you have:
2821
- // style({ color: 'red' }), ...someProps, style({ color: 'green' })
2822
- // this will mutate:
2823
- // style({}), ...someProps, style({ color: 'green' })
2824
- if (attr.type === 'style') {
2825
- for (const key in attr.value) {
2826
- if (existingStyleKeys.has(key)) {
2827
- if (shouldPrintDebug) {
2828
- logger.info([` >> delete existing ${key}`].join(' '))
2829
- }
2830
- delete attr.value[key]
2831
- } else {
2832
- existingStyleKeys.add(key)
2833
- }
2834
- }
2835
- }
2836
- }
2837
-
2838
- attrs = attrs.filter(Boolean)
2839
-
2840
- // inlineWhenUnflattened
2841
- if (!shouldFlatten) {
2842
- if (inlineWhenUnflattened.size) {
2843
- for (const [index, attr] of attrs.entries()) {
2844
- if (attr.type === 'style') {
2845
- for (const key in attr.value) {
2846
- if (!inlineWhenUnflattened.has(key)) continue
2847
- const val = inlineWhenUnflattenedOGVals[key]
2848
- if (val) {
2849
- // delete the style
2850
- delete attr.value[key]
2851
-
2852
- // and insert it before
2853
- attrs.splice(index - 1, 0, val.attr)
2854
- } else {
2855
- // just delete it, it was added during expansion but should be left inline
2856
- delete attr.value[key]
2857
- }
2858
- }
2859
- }
2860
- }
2861
- }
2862
- }
2863
-
2864
- // delete empty styles:
2865
- attrs = attrs.filter((x) => {
2866
- if (x.type === 'style' && Object.keys(x.value).length === 0) {
2867
- return false
2868
- }
2869
- return true
2870
- })
2871
-
2872
- const isNativeNotFlat = !shouldFlatten && platform === 'native'
2873
- if (isNativeNotFlat) {
2874
- if (shouldPrintDebug) {
2875
- logger.info(
2876
- `Disabled flattening except for simple cases on native for now: ${JSON.stringify(
2877
- {
2878
- flatNode: flatNodeName,
2879
- shouldDeopt,
2880
- canFlattenProps,
2881
- hasSpread,
2882
- 'staticConfig.isStyledHOC': staticConfig.isStyledHOC,
2883
- '!staticConfig.isHOC': !staticConfig.isHOC,
2884
- 'staticConfig.isReactNative': staticConfig.isReactNative,
2885
- 'staticConfig.neverFlatten': staticConfig.neverFlatten,
2886
- },
2887
- null,
2888
- 2
2889
- )}`
2890
- )
2891
- }
2892
- node.attributes = ogAttributes
2893
- return null
2894
- }
2895
-
2896
- if (shouldPrintDebug) {
2897
- // prettier-ignore
2898
- logger.info(
2899
- [
2900
- ` - inlined props (${inlined.size}):`,
2901
- shouldDeopt ? ' deopted' : '',
2902
- hasSpread ? ' has spread' : '',
2903
- staticConfig.neverFlatten ? 'neverFlatten' : '',
2904
- ].join(' ')
2905
- )
2906
- logger.info(` - attrs (end):\n ${logLines(attrs.map(attrStr).join(', '))}`)
2907
- }
2908
-
2909
- onExtractTag({
2910
- parserProps: propsWithFileInfo,
2911
- attrs,
2912
- node,
2913
- lineNumbers,
2914
- filePath,
2915
- config: tamaguiConfig!,
2916
- flatNodeName,
2917
- attemptEval,
2918
- jsxPath: traversePath,
2919
- originalNodeName,
2920
- programPath: programPath!,
2921
- completeProps,
2922
- staticConfig,
2923
- })
2924
-
2925
- if (shouldFlatten) {
2926
- if (shouldPrintDebug) {
2927
- logger.info([' [✅] flattened', originalNodeName, flatNodeName].join(' '))
2928
- }
2929
- // Only rename if onExtractTag hasn't already renamed to a custom wrapper
2930
- // @ts-ignore - check if already renamed by callback (e.g., to a styled wrapper)
2931
- const currentName = node.name?.name
2932
- if (
2933
- !currentName ||
2934
- currentName === originalNodeName ||
2935
- currentName.startsWith('__ReactNative')
2936
- ) {
2937
- // @ts-ignore
2938
- node.name.name = flatNodeName
2939
- if (closingElement) {
2940
- // @ts-ignore
2941
- closingElement.name.name = flatNodeName
2942
- }
2943
- }
2944
- res.flattened++
2945
- }
2946
- } catch (err: any) {
2947
- node.attributes = ogAttributes
2948
-
2949
- if (!(err instanceof BailOptimizationError)) {
2950
- console.error(
2951
- `@tamagui/static error, reverting optimization. In ${filePath} ${lineNumbers} on ${originalNodeName}: ${err.message}. For stack trace set environment TAMAGUI_DEBUG=1`
2952
- )
2953
- if (process.env.TAMAGUI_DEBUG === '1') {
2954
- console.error(err.stack)
2955
- }
2956
- }
2957
- } finally {
2958
- if (debugPropValue) {
2959
- shouldPrintDebug = ogDebug
2960
- }
2961
- }
2962
- },
2963
- })
2964
-
2965
- tm.mark('jsx-done', !!shouldPrintDebug)
2966
- tm.done(shouldPrintDebug === 'verbose')
2967
-
2968
- return res
2969
- }
2970
- }