@tamagui/static 1.0.1-beta.99 → 1.0.1-rc.0

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 (269) hide show
  1. package/dist/{cjs/constants.js → constants.js} +9 -4
  2. package/dist/constants.js.map +7 -0
  3. package/dist/{cjs/extractor → extractor}/accessSafe.js +14 -2
  4. package/dist/{esm/extractor → extractor}/accessSafe.js.map +2 -2
  5. package/dist/{cjs/extractor → extractor}/babelParse.js +5 -1
  6. package/dist/extractor/babelParse.js.map +7 -0
  7. package/dist/{cjs/extractor → extractor}/buildClassName.js +40 -9
  8. package/dist/extractor/buildClassName.js.map +7 -0
  9. package/dist/extractor/bundle.js +117 -0
  10. package/dist/extractor/bundle.js.map +7 -0
  11. package/dist/{cjs/extractor → extractor}/createEvaluator.js +11 -8
  12. package/dist/extractor/createEvaluator.js.map +7 -0
  13. package/dist/extractor/createExtractor.js +1623 -0
  14. package/dist/extractor/createExtractor.js.map +7 -0
  15. package/dist/{cjs/extractor → extractor}/ensureImportingConcat.js +18 -6
  16. package/dist/extractor/ensureImportingConcat.js.map +7 -0
  17. package/dist/{cjs/extractor → extractor}/evaluateAstNode.js +14 -4
  18. package/dist/extractor/evaluateAstNode.js.map +7 -0
  19. package/dist/{cjs/extractor → extractor}/extractHelpers.js +83 -12
  20. package/dist/extractor/extractHelpers.js.map +7 -0
  21. package/dist/{cjs/extractor → extractor}/extractMediaStyle.js +46 -42
  22. package/dist/extractor/extractMediaStyle.js.map +7 -0
  23. package/dist/{cjs/extractor → extractor}/extractToClassNames.js +144 -73
  24. package/dist/extractor/extractToClassNames.js.map +7 -0
  25. package/dist/{cjs/extractor → extractor}/findTopmostFunction.js +1 -0
  26. package/dist/{cjs/extractor → extractor}/findTopmostFunction.js.map +2 -2
  27. package/dist/{cjs/extractor → extractor}/generatedUid.js +9 -2
  28. package/dist/{cjs/extractor → extractor}/generatedUid.js.map +3 -3
  29. package/dist/{cjs/extractor → extractor}/getPrefixLogs.js +1 -0
  30. package/dist/extractor/getPrefixLogs.js.map +7 -0
  31. package/dist/{cjs/extractor → extractor}/getPropValueFromAttributes.js +24 -13
  32. package/dist/extractor/getPropValueFromAttributes.js.map +7 -0
  33. package/dist/{cjs/extractor → extractor}/getSourceModule.js +5 -1
  34. package/dist/{esm/extractor → extractor}/getSourceModule.js.map +3 -3
  35. package/dist/{cjs/extractor → extractor}/getStaticBindingsForScope.js +89 -35
  36. package/dist/extractor/getStaticBindingsForScope.js.map +7 -0
  37. package/dist/{cjs/extractor → extractor}/hoistClassNames.js +5 -1
  38. package/dist/{jsx/extractor → extractor}/hoistClassNames.js.map +3 -3
  39. package/dist/{cjs/extractor → extractor}/literalToAst.js +26 -30
  40. package/dist/{jsx/extractor → extractor}/literalToAst.js.map +2 -2
  41. package/dist/extractor/loadFile.js +21 -0
  42. package/dist/extractor/loadFile.js.map +7 -0
  43. package/dist/extractor/loadTamagui.js +427 -0
  44. package/dist/extractor/loadTamagui.js.map +7 -0
  45. package/dist/{cjs/extractor → extractor}/logLines.js +2 -1
  46. package/dist/extractor/logLines.js.map +7 -0
  47. package/dist/{cjs/extractor → extractor}/normalizeTernaries.js +13 -34
  48. package/dist/extractor/normalizeTernaries.js.map +7 -0
  49. package/dist/{cjs/extractor → extractor}/removeUnusedHooks.js +5 -1
  50. package/dist/extractor/removeUnusedHooks.js.map +7 -0
  51. package/dist/{cjs/extractor → extractor}/timer.js +9 -1
  52. package/dist/extractor/timer.js.map +7 -0
  53. package/dist/{cjs/extractor → extractor}/validHTMLAttributes.js +1 -0
  54. package/dist/{jsx/extractor → extractor}/validHTMLAttributes.js.map +2 -2
  55. package/dist/getPragmaOptions.js +68 -0
  56. package/dist/getPragmaOptions.js.map +7 -0
  57. package/dist/helpers/memoize.js +46 -0
  58. package/dist/helpers/memoize.js.map +7 -0
  59. package/dist/{cjs/index.js → index.js} +10 -8
  60. package/dist/index.js.map +7 -0
  61. package/dist/require.js +86 -0
  62. package/dist/require.js.map +7 -0
  63. package/dist/{cjs/types.js → types.js} +1 -0
  64. package/dist/types.js.map +7 -0
  65. package/dist/webpackPlugin.js +35 -0
  66. package/dist/webpackPlugin.js.map +7 -0
  67. package/package.json +40 -36
  68. package/src/constants.ts +2 -1
  69. package/src/extractor/babelParse.ts +3 -2
  70. package/src/extractor/buildClassName.ts +21 -5
  71. package/src/extractor/bundle.ts +110 -0
  72. package/src/extractor/createEvaluator.ts +10 -12
  73. package/src/extractor/createExtractor.ts +1702 -1523
  74. package/src/extractor/ensureImportingConcat.ts +4 -11
  75. package/src/extractor/evaluateAstNode.ts +1 -1
  76. package/src/extractor/extractHelpers.ts +93 -10
  77. package/src/extractor/extractMediaStyle.ts +18 -11
  78. package/src/extractor/extractToClassNames.ts +104 -47
  79. package/src/extractor/getPrefixLogs.ts +1 -1
  80. package/src/extractor/getPropValueFromAttributes.ts +4 -3
  81. package/src/extractor/getSourceModule.ts +2 -0
  82. package/src/extractor/getStaticBindingsForScope.ts +90 -46
  83. package/src/extractor/loadFile.ts +17 -0
  84. package/src/extractor/loadTamagui.ts +448 -103
  85. package/src/extractor/logLines.ts +1 -1
  86. package/src/extractor/normalizeTernaries.ts +2 -2
  87. package/src/extractor/removeUnusedHooks.ts +2 -0
  88. package/src/extractor/timer.ts +12 -1
  89. package/src/getPragmaOptions.ts +53 -0
  90. package/src/helpers/memoize.ts +21 -0
  91. package/src/index.ts +9 -8
  92. package/src/require.ts +103 -0
  93. package/src/types.ts +33 -31
  94. package/src/webpackPlugin.ts +9 -0
  95. package/tamagui.tsconfig.json +5 -0
  96. package/types/constants.d.ts +6 -0
  97. package/types/extractor/accessSafe.d.ts +3 -0
  98. package/types/extractor/babelParse.d.ts +6 -0
  99. package/types/extractor/buildClassName.d.ts +5 -2
  100. package/types/extractor/bundle.d.ts +13 -0
  101. package/types/extractor/createEvaluator.d.ts +3 -3
  102. package/types/extractor/createExtractor.d.ts +25 -9
  103. package/types/extractor/ensureImportingConcat.d.ts +4 -0
  104. package/types/extractor/evaluateAstNode.d.ts +3 -0
  105. package/types/extractor/extractHelpers.d.ts +15 -3
  106. package/types/extractor/extractMediaStyle.d.ts +3 -3
  107. package/types/extractor/extractToClassNames.d.ts +5 -4
  108. package/types/extractor/findTopmostFunction.d.ts +4 -0
  109. package/types/extractor/generatedUid.d.ts +5 -0
  110. package/types/extractor/getPrefixLogs.d.ts +1 -1
  111. package/types/extractor/getPropValueFromAttributes.d.ts +19 -0
  112. package/types/extractor/getSourceModule.d.ts +16 -0
  113. package/types/extractor/getStaticBindingsForScope.d.ts +5 -0
  114. package/types/extractor/hoistClassNames.d.ts +6 -0
  115. package/types/extractor/literalToAst.d.ts +4 -0
  116. package/types/extractor/loadFile.d.ts +1 -0
  117. package/types/extractor/loadTamagui.d.ts +15 -5
  118. package/types/extractor/logLines.d.ts +2 -0
  119. package/types/extractor/normalizeTernaries.d.ts +1 -1
  120. package/types/extractor/removeUnusedHooks.d.ts +3 -0
  121. package/types/extractor/timer.d.ts +5 -0
  122. package/types/extractor/validHTMLAttributes.d.ts +98 -0
  123. package/types/getPragmaOptions.d.ts +9 -0
  124. package/types/helpers/memoize.d.ts +8 -0
  125. package/types/index.d.ts +9 -8
  126. package/types/require.d.ts +4 -0
  127. package/types/types.d.ts +28 -27
  128. package/types/webpackPlugin.d.ts +4 -0
  129. package/dist/cjs/constants.js.map +0 -7
  130. package/dist/cjs/extractor/accessSafe.js.map +0 -7
  131. package/dist/cjs/extractor/babelParse.js.map +0 -7
  132. package/dist/cjs/extractor/buildClassName.js.map +0 -7
  133. package/dist/cjs/extractor/createEvaluator.js.map +0 -7
  134. package/dist/cjs/extractor/createExtractor.js +0 -1394
  135. package/dist/cjs/extractor/createExtractor.js.map +0 -7
  136. package/dist/cjs/extractor/ensureImportingConcat.js.map +0 -7
  137. package/dist/cjs/extractor/evaluateAstNode.js.map +0 -7
  138. package/dist/cjs/extractor/extractHelpers.js.map +0 -7
  139. package/dist/cjs/extractor/extractMediaStyle.js.map +0 -7
  140. package/dist/cjs/extractor/extractToClassNames.js.map +0 -7
  141. package/dist/cjs/extractor/getPrefixLogs.js.map +0 -7
  142. package/dist/cjs/extractor/getPropValueFromAttributes.js.map +0 -7
  143. package/dist/cjs/extractor/getSourceModule.js.map +0 -7
  144. package/dist/cjs/extractor/getStaticBindingsForScope.js.map +0 -7
  145. package/dist/cjs/extractor/hoistClassNames.js.map +0 -7
  146. package/dist/cjs/extractor/literalToAst.js.map +0 -7
  147. package/dist/cjs/extractor/loadTamagui.js +0 -139
  148. package/dist/cjs/extractor/loadTamagui.js.map +0 -7
  149. package/dist/cjs/extractor/logLines.js.map +0 -7
  150. package/dist/cjs/extractor/normalizeTernaries.js.map +0 -7
  151. package/dist/cjs/extractor/removeUnusedHooks.js.map +0 -7
  152. package/dist/cjs/extractor/timer.js.map +0 -7
  153. package/dist/cjs/extractor/validHTMLAttributes.js.map +0 -7
  154. package/dist/cjs/index.js.map +0 -7
  155. package/dist/cjs/types.js.map +0 -7
  156. package/dist/esm/constants.js +0 -14
  157. package/dist/esm/constants.js.map +0 -7
  158. package/dist/esm/extractor/accessSafe.js +0 -8
  159. package/dist/esm/extractor/babelParse.js +0 -28
  160. package/dist/esm/extractor/babelParse.js.map +0 -7
  161. package/dist/esm/extractor/buildClassName.js +0 -37
  162. package/dist/esm/extractor/buildClassName.js.map +0 -7
  163. package/dist/esm/extractor/createEvaluator.js +0 -53
  164. package/dist/esm/extractor/createEvaluator.js.map +0 -7
  165. package/dist/esm/extractor/createExtractor.js +0 -1379
  166. package/dist/esm/extractor/createExtractor.js.map +0 -7
  167. package/dist/esm/extractor/ensureImportingConcat.js +0 -21
  168. package/dist/esm/extractor/ensureImportingConcat.js.map +0 -7
  169. package/dist/esm/extractor/evaluateAstNode.js +0 -91
  170. package/dist/esm/extractor/evaluateAstNode.js.map +0 -7
  171. package/dist/esm/extractor/extractHelpers.js +0 -97
  172. package/dist/esm/extractor/extractHelpers.js.map +0 -7
  173. package/dist/esm/extractor/extractMediaStyle.js +0 -161
  174. package/dist/esm/extractor/extractMediaStyle.js.map +0 -7
  175. package/dist/esm/extractor/extractToClassNames.js +0 -294
  176. package/dist/esm/extractor/extractToClassNames.js.map +0 -7
  177. package/dist/esm/extractor/findTopmostFunction.js +0 -20
  178. package/dist/esm/extractor/findTopmostFunction.js.map +0 -7
  179. package/dist/esm/extractor/generatedUid.js +0 -25
  180. package/dist/esm/extractor/generatedUid.js.map +0 -7
  181. package/dist/esm/extractor/getPrefixLogs.js +0 -8
  182. package/dist/esm/extractor/getPrefixLogs.js.map +0 -7
  183. package/dist/esm/extractor/getPropValueFromAttributes.js +0 -46
  184. package/dist/esm/extractor/getPropValueFromAttributes.js.map +0 -7
  185. package/dist/esm/extractor/getSourceModule.js +0 -66
  186. package/dist/esm/extractor/getStaticBindingsForScope.js +0 -132
  187. package/dist/esm/extractor/getStaticBindingsForScope.js.map +0 -7
  188. package/dist/esm/extractor/hoistClassNames.js +0 -40
  189. package/dist/esm/extractor/hoistClassNames.js.map +0 -7
  190. package/dist/esm/extractor/literalToAst.js +0 -90
  191. package/dist/esm/extractor/literalToAst.js.map +0 -7
  192. package/dist/esm/extractor/loadTamagui.js +0 -117
  193. package/dist/esm/extractor/loadTamagui.js.map +0 -7
  194. package/dist/esm/extractor/logLines.js +0 -20
  195. package/dist/esm/extractor/logLines.js.map +0 -7
  196. package/dist/esm/extractor/normalizeTernaries.js +0 -79
  197. package/dist/esm/extractor/normalizeTernaries.js.map +0 -7
  198. package/dist/esm/extractor/removeUnusedHooks.js +0 -76
  199. package/dist/esm/extractor/removeUnusedHooks.js.map +0 -7
  200. package/dist/esm/extractor/timer.js +0 -22
  201. package/dist/esm/extractor/timer.js.map +0 -7
  202. package/dist/esm/extractor/validHTMLAttributes.js +0 -102
  203. package/dist/esm/extractor/validHTMLAttributes.js.map +0 -7
  204. package/dist/esm/index.js +0 -15
  205. package/dist/esm/index.js.map +0 -7
  206. package/dist/esm/types.js +0 -1
  207. package/dist/esm/types.js.map +0 -7
  208. package/dist/jsx/constants.js +0 -14
  209. package/dist/jsx/constants.js.map +0 -7
  210. package/dist/jsx/extractor/accessSafe.js +0 -8
  211. package/dist/jsx/extractor/accessSafe.js.map +0 -7
  212. package/dist/jsx/extractor/babelParse.js +0 -28
  213. package/dist/jsx/extractor/babelParse.js.map +0 -7
  214. package/dist/jsx/extractor/buildClassName.js +0 -37
  215. package/dist/jsx/extractor/buildClassName.js.map +0 -7
  216. package/dist/jsx/extractor/createEvaluator.js +0 -53
  217. package/dist/jsx/extractor/createEvaluator.js.map +0 -7
  218. package/dist/jsx/extractor/createExtractor.js +0 -1343
  219. package/dist/jsx/extractor/createExtractor.js.map +0 -7
  220. package/dist/jsx/extractor/ensureImportingConcat.js +0 -21
  221. package/dist/jsx/extractor/ensureImportingConcat.js.map +0 -7
  222. package/dist/jsx/extractor/evaluateAstNode.js +0 -91
  223. package/dist/jsx/extractor/evaluateAstNode.js.map +0 -7
  224. package/dist/jsx/extractor/extractHelpers.js +0 -97
  225. package/dist/jsx/extractor/extractHelpers.js.map +0 -7
  226. package/dist/jsx/extractor/extractMediaStyle.js +0 -146
  227. package/dist/jsx/extractor/extractMediaStyle.js.map +0 -7
  228. package/dist/jsx/extractor/extractToClassNames.js +0 -280
  229. package/dist/jsx/extractor/extractToClassNames.js.map +0 -7
  230. package/dist/jsx/extractor/findTopmostFunction.js +0 -20
  231. package/dist/jsx/extractor/findTopmostFunction.js.map +0 -7
  232. package/dist/jsx/extractor/generatedUid.js +0 -25
  233. package/dist/jsx/extractor/generatedUid.js.map +0 -7
  234. package/dist/jsx/extractor/getPrefixLogs.js +0 -9
  235. package/dist/jsx/extractor/getPrefixLogs.js.map +0 -7
  236. package/dist/jsx/extractor/getPropValueFromAttributes.js +0 -46
  237. package/dist/jsx/extractor/getPropValueFromAttributes.js.map +0 -7
  238. package/dist/jsx/extractor/getSourceModule.js +0 -66
  239. package/dist/jsx/extractor/getSourceModule.js.map +0 -7
  240. package/dist/jsx/extractor/getStaticBindingsForScope.js +0 -132
  241. package/dist/jsx/extractor/getStaticBindingsForScope.js.map +0 -7
  242. package/dist/jsx/extractor/hoistClassNames.js +0 -40
  243. package/dist/jsx/extractor/literalToAst.js +0 -75
  244. package/dist/jsx/extractor/loadTamagui.js +0 -102
  245. package/dist/jsx/extractor/loadTamagui.js.map +0 -7
  246. package/dist/jsx/extractor/logLines.js +0 -20
  247. package/dist/jsx/extractor/logLines.js.map +0 -7
  248. package/dist/jsx/extractor/normalizeTernaries.js +0 -50
  249. package/dist/jsx/extractor/normalizeTernaries.js.map +0 -7
  250. package/dist/jsx/extractor/removeUnusedHooks.js +0 -77
  251. package/dist/jsx/extractor/removeUnusedHooks.js.map +0 -7
  252. package/dist/jsx/extractor/timer.js +0 -22
  253. package/dist/jsx/extractor/timer.js.map +0 -7
  254. package/dist/jsx/extractor/validHTMLAttributes.js +0 -102
  255. package/dist/jsx/index.js +0 -15
  256. package/dist/jsx/index.js.map +0 -7
  257. package/dist/jsx/types.js +0 -1
  258. package/dist/jsx/types.js.map +0 -7
  259. package/types/extractor/buildClassName.d.ts.map +0 -1
  260. package/types/extractor/createEvaluator.d.ts.map +0 -1
  261. package/types/extractor/createExtractor.d.ts.map +0 -1
  262. package/types/extractor/extractHelpers.d.ts.map +0 -1
  263. package/types/extractor/extractMediaStyle.d.ts.map +0 -1
  264. package/types/extractor/extractToClassNames.d.ts.map +0 -1
  265. package/types/extractor/getPrefixLogs.d.ts.map +0 -1
  266. package/types/extractor/loadTamagui.d.ts.map +0 -1
  267. package/types/extractor/normalizeTernaries.d.ts.map +0 -1
  268. package/types/index.d.ts.map +0 -1
  269. package/types/types.d.ts.map +0 -1
@@ -1,43 +1,53 @@
1
- import { relative } from 'path'
1
+ /* eslint-disable no-console */
2
+ import { basename, relative } from 'path'
2
3
 
3
- import traverse, { NodePath, Visitor } from '@babel/traverse'
4
+ import traverse, { NodePath, TraverseOptions } from '@babel/traverse'
4
5
  import * as t from '@babel/types'
5
6
  import {
6
7
  PseudoStyles,
7
8
  StaticConfigParsed,
8
- TamaguiInternalConfig,
9
- createDOMProps,
10
9
  expandStyles,
11
10
  getSplitStyles,
12
11
  getStylesAtomic,
13
12
  mediaQueryConfig,
14
13
  proxyThemeVariables,
15
14
  pseudoDescriptors,
16
- stylePropsTransform,
17
15
  } from '@tamagui/core-node'
18
- import { difference, pick } from 'lodash'
19
16
  import type { ViewStyle } from 'react-native'
17
+ import { createDOMProps } from 'react-native-web-internals'
20
18
 
21
- import { FAILED_EVAL } from '../constants'
22
- import {
19
+ import { FAILED_EVAL } from '../constants.js'
20
+ import type {
23
21
  ExtractedAttr,
24
22
  ExtractedAttrAttr,
25
23
  ExtractedAttrStyle,
24
+ ExtractorOptions,
26
25
  ExtractorParseProps,
26
+ TamaguiOptions,
27
+ TamaguiOptionsWithFileInfo,
27
28
  Ternary,
28
- } from '../types'
29
- import { createEvaluator, createSafeEvaluator } from './createEvaluator'
30
- import { evaluateAstNode } from './evaluateAstNode'
31
- import { attrStr, findComponentName, isInsideTamagui, isPresent, objToStr } from './extractHelpers'
32
- import { findTopmostFunction } from './findTopmostFunction'
33
- import { getStaticBindingsForScope } from './getStaticBindingsForScope'
34
- import { literalToAst } from './literalToAst'
35
- import { loadTamagui } from './loadTamagui'
36
- import { logLines } from './logLines'
37
- import { normalizeTernaries } from './normalizeTernaries'
38
- import { removeUnusedHooks } from './removeUnusedHooks'
39
- import { timer } from './timer'
40
- import { validHTMLAttributes } from './validHTMLAttributes'
29
+ } from '../types.js'
30
+ import { createEvaluator, createSafeEvaluator } from './createEvaluator.js'
31
+ import { evaluateAstNode } from './evaluateAstNode.js'
32
+ import {
33
+ attrStr,
34
+ findComponentName,
35
+ getValidComponent,
36
+ getValidImport,
37
+ isPresent,
38
+ isValidImport,
39
+ objToStr,
40
+ } from './extractHelpers.js'
41
+ import { findTopmostFunction } from './findTopmostFunction.js'
42
+ import { getPrefixLogs } from './getPrefixLogs.js'
43
+ import { cleanupBeforeExit, getStaticBindingsForScope } from './getStaticBindingsForScope.js'
44
+ import { literalToAst } from './literalToAst.js'
45
+ import { TamaguiProjectInfo, loadTamagui, loadTamaguiSync } from './loadTamagui.js'
46
+ import { logLines } from './logLines.js'
47
+ import { normalizeTernaries } from './normalizeTernaries.js'
48
+ import { removeUnusedHooks } from './removeUnusedHooks.js'
49
+ import { timer } from './timer.js'
50
+ import { validHTMLAttributes } from './validHTMLAttributes.js'
41
51
 
42
52
  const UNTOUCHED_PROPS = {
43
53
  key: true,
@@ -48,25 +58,30 @@ const UNTOUCHED_PROPS = {
48
58
  const INLINE_EXTRACTABLE = {
49
59
  ref: 'ref',
50
60
  key: 'key',
51
- onPress: 'onClick',
52
- onHoverIn: 'onMouseEnter',
53
- onHoverOut: 'onMouseLeave',
54
- onPressIn: 'onMouseDown',
55
- onPressOut: 'onMouseUp',
61
+ ...(process.env.TAMAGUI_TARGET === 'web' && {
62
+ onPress: 'onClick',
63
+ onHoverIn: 'onMouseEnter',
64
+ onHoverOut: 'onMouseLeave',
65
+ onPressIn: 'onMouseDown',
66
+ onPressOut: 'onMouseUp',
67
+ }),
56
68
  }
57
69
 
58
- const isAttr = (x: ExtractedAttr): x is ExtractedAttrAttr => x.type === 'attr'
59
-
60
70
  const validHooks = {
61
71
  useMedia: true,
62
72
  useTheme: true,
63
73
  }
64
74
 
75
+ const isAttr = (x: ExtractedAttr): x is ExtractedAttrAttr => x.type === 'attr'
76
+ const createTernary = (x: Ternary) => x
77
+
65
78
  export type Extractor = ReturnType<typeof createExtractor>
66
79
 
67
- const createTernary = (x: Ternary) => x
80
+ type FileOrPath = NodePath<t.Program> | t.File
68
81
 
69
- export function createExtractor() {
82
+ let hasLoggedBaseInfo = false
83
+
84
+ export function createExtractor({ logger = console }: ExtractorOptions = { logger: console }) {
70
85
  if (!process.env.TAMAGUI_TARGET) {
71
86
  console.log('⚠️ Please set process.env.TAMAGUI_TARGET to either "web" or "native"')
72
87
  process.exit(1)
@@ -79,1248 +94,1451 @@ export function createExtractor() {
79
94
  process.env.IDENTIFY_TAGS !== 'false' &&
80
95
  (process.env.NODE_ENV === 'development' || process.env.DEBUG || process.env.IDENTIFY_TAGS)
81
96
 
82
- let loadedTamaguiConfig: TamaguiInternalConfig
83
- let hasLogged = false
84
-
85
- function isValidStyleKey(name: string, staticConfig: StaticConfigParsed) {
86
- return !!(
87
- !!staticConfig.validStyles?.[name] ||
88
- !!pseudoDescriptors[name] ||
89
- // dont disable variants or else you lose many things flattening
90
- staticConfig.variants?.[name] ||
91
- loadedTamaguiConfig.shorthands[name] ||
92
- (name[0] === '$' ? !!mediaQueryConfig[name.slice(1)] : false)
93
- )
97
+ let projectInfo: TamaguiProjectInfo | null = null
98
+
99
+ // we load tamagui delayed because we need to set some global/env stuff before importing
100
+ // otherwise we'd import `rnw` and cause it to evaluate react-native-web which causes errors
101
+
102
+ function loadSync(props: TamaguiOptions) {
103
+ return (projectInfo ||= loadTamaguiSync({
104
+ config: props.config || 'tamagui.config.ts',
105
+ components: props.components || ['tamagui'],
106
+ }))
107
+ }
108
+
109
+ async function load(props: TamaguiOptions) {
110
+ return (projectInfo ||= await loadTamagui({
111
+ config: props.config || 'tamagui.config.ts',
112
+ components: props.components || ['tamagui'],
113
+ }))
94
114
  }
95
115
 
96
116
  return {
117
+ options: {
118
+ logger,
119
+ },
120
+ cleanupBeforeExit,
121
+ loadTamagui: load,
122
+ loadTamaguiSync: loadSync,
97
123
  getTamagui() {
98
- return loadedTamaguiConfig
124
+ return projectInfo?.tamaguiConfig
99
125
  },
100
- parse: (
101
- fileOrPath: NodePath<t.Program> | t.File,
102
- {
103
- config = 'tamagui.config.ts',
104
- importsWhitelist = ['constants.js'],
105
- evaluateVars = true,
106
- shouldPrintDebug = false,
107
- sourcePath = '',
108
- onExtractTag,
109
- onStyleRule,
110
- getFlattenedNode,
111
- disable,
112
- disableExtraction,
113
- disableExtractInlineMedia,
114
- disableExtractVariables,
115
- disableDebugAttr,
116
- extractStyledDefinitions = false,
117
- prefixLogs,
118
- excludeProps,
119
- target,
120
- ...props
121
- }: ExtractorParseProps
122
- ) => {
123
- if (disable) {
124
- return null
126
+ parseSync: (f: FileOrPath, props: ExtractorParseProps) => {
127
+ const projectInfo = loadSync(props)
128
+ return parseWithConfig(projectInfo, f, props)
129
+ },
130
+ parse: async (f: FileOrPath, props: ExtractorParseProps) => {
131
+ const projectInfo = await load(props)
132
+ return parseWithConfig(projectInfo, f, props)
133
+ },
134
+ }
135
+
136
+ function parseWithConfig(
137
+ { components, tamaguiConfig }: TamaguiProjectInfo,
138
+ fileOrPath: FileOrPath,
139
+ options: ExtractorParseProps
140
+ ) {
141
+ const {
142
+ config = 'tamagui.config.ts',
143
+ importsWhitelist = ['constants.js'],
144
+ evaluateVars = true,
145
+ sourcePath = '',
146
+ onExtractTag,
147
+ onStyleRule,
148
+ getFlattenedNode,
149
+ disable,
150
+ disableExtraction,
151
+ disableExtractInlineMedia,
152
+ disableExtractVariables,
153
+ disableDebugAttr,
154
+ disableExtractFoundComponents,
155
+ includeExtensions = ['.tsx', '.jsx', '.js'],
156
+ extractStyledDefinitions = false,
157
+ prefixLogs,
158
+ excludeProps,
159
+ target,
160
+ ...restProps
161
+ } = options
162
+
163
+ let shouldPrintDebug = options.shouldPrintDebug || false
164
+
165
+ if (disable === true || (Array.isArray(disable) && disable.includes(sourcePath))) {
166
+ return null
167
+ }
168
+ if (sourcePath === '') {
169
+ throw new Error(`Must provide a source file name`)
170
+ }
171
+ if (!components) {
172
+ throw new Error(`Must provide components`)
173
+ }
174
+ if (includeExtensions && !includeExtensions.some((ext) => sourcePath.endsWith(ext))) {
175
+ if (shouldPrintDebug) {
176
+ logger.info(
177
+ `Ignoring file due to includeExtensions: ${sourcePath}, includeExtensions: ${includeExtensions.join(
178
+ ', '
179
+ )}`
180
+ )
125
181
  }
126
- if (sourcePath === '') {
127
- throw new Error(`Must provide a source file name`)
182
+ return null
183
+ }
184
+
185
+ function isValidStyleKey(name: string, staticConfig: StaticConfigParsed) {
186
+ if (!projectInfo) {
187
+ throw new Error(`Tamagui extractor not loaded yet`)
128
188
  }
129
- if (!Array.isArray(props.components)) {
130
- throw new Error(`Must provide components array with list of Tamagui component modules`)
189
+ if (target === 'native' && name[0] === '$' && mediaQueryConfig[name.slice(1)]) {
190
+ return false
131
191
  }
132
-
133
- const isTargetingHTML = target === 'html'
134
- const ogDebug = shouldPrintDebug
135
- const tm = timer()
136
-
137
- // we require it after parse because we need to set some global/env stuff before importing
138
- // otherwise we'd import `rnw` and cause it to evaluate react-native-web which causes errors
139
- const { components, tamaguiConfig } = loadTamagui({
140
- config,
141
- components: props.components || ['tamagui'],
142
- })
143
-
144
- if (shouldPrintDebug === 'verbose') {
145
- console.log('tamagui.config.ts:', { components, config })
192
+ return !!(
193
+ !!staticConfig.validStyles?.[name] ||
194
+ !!pseudoDescriptors[name] ||
195
+ // dont disable variants or else you lose many things flattening
196
+ staticConfig.variants?.[name] ||
197
+ projectInfo?.tamaguiConfig.shorthands[name] ||
198
+ (name[0] === '$' ? !!mediaQueryConfig[name.slice(1)] : false)
199
+ )
200
+ }
201
+
202
+ /**
203
+ * Step 1: Determine if importing any statically extractable components
204
+ */
205
+
206
+ const isTargetingHTML = target === 'html'
207
+ const ogDebug = shouldPrintDebug
208
+ const tm = timer()
209
+ const propsWithFileInfo: TamaguiOptionsWithFileInfo = {
210
+ ...options,
211
+ sourcePath,
212
+ allLoadedComponents: [...components],
213
+ }
214
+
215
+ if (!hasLoggedBaseInfo) {
216
+ hasLoggedBaseInfo = true
217
+ if (shouldPrintDebug) {
218
+ logger.info(
219
+ [
220
+ 'loaded components:',
221
+ propsWithFileInfo.allLoadedComponents
222
+ .map((comp) => Object.keys(comp.nameToInfo).join(', '))
223
+ .join(', '),
224
+ ].join(' ')
225
+ )
146
226
  }
227
+ if (process.env.DEBUG?.startsWith('tamagui')) {
228
+ const next = [...propsWithFileInfo.allLoadedComponents].map((info) => {
229
+ const nameToInfo = { ...info.nameToInfo }
230
+ for (const key in nameToInfo) {
231
+ delete nameToInfo[key].staticConfig.validStyles
232
+ }
233
+ return { ...info, nameToInfo }
234
+ })
235
+ logger.info(['loaded:', JSON.stringify(next, null, 2)].join('\n'))
236
+ }
237
+ }
238
+
239
+ tm.mark('load-tamagui', !!shouldPrintDebug)
240
+
241
+ const firstThemeName = Object.keys(tamaguiConfig.themes)[0]
242
+ const firstTheme = tamaguiConfig.themes[firstThemeName]
243
+
244
+ if (!firstTheme || typeof firstTheme !== 'object') {
245
+ console.error(`Missing theme, an error occurred when importing your config`)
246
+ console.log(`Got config:`, tamaguiConfig)
247
+ console.log(`Looking for theme:`, firstThemeName)
248
+ process.exit(0)
249
+ }
250
+
251
+ const proxiedTheme = proxyThemeVariables(firstTheme)
252
+ type AccessListener = (key: string) => void
253
+ const themeAccessListeners = new Set<AccessListener>()
254
+ const defaultTheme = new Proxy(proxiedTheme, {
255
+ get(target, key) {
256
+ if (Reflect.has(target, key)) {
257
+ themeAccessListeners.forEach((cb) => cb(String(key)))
258
+ }
259
+ return Reflect.get(target, key)
260
+ },
261
+ })
147
262
 
148
- tm.mark('load-tamagui', shouldPrintDebug === 'verbose')
263
+ // @ts-ignore
264
+ const body = fileOrPath.type === 'Program' ? fileOrPath.get('body') : fileOrPath.program.body
149
265
 
150
- loadedTamaguiConfig = tamaguiConfig as any
266
+ if (Object.keys(components).length === 0) {
267
+ console.warn(`Warning: Tamagui didn't find any valid components (DEBUG=tamagui for more)`)
268
+ if (process.env.DEBUG === 'tamagui') {
269
+ console.log(`components`, Object.keys(components), components)
270
+ }
271
+ }
151
272
 
152
- const proxiedTheme = proxyThemeVariables(
153
- tamaguiConfig.themes[Object.keys(tamaguiConfig.themes)[0]]
273
+ if (shouldPrintDebug === 'verbose') {
274
+ logger.info(
275
+ `allLoadedComponent modules ${propsWithFileInfo.allLoadedComponents
276
+ .map((k) => k.moduleName)
277
+ .join(', ')}`
154
278
  )
279
+ }
155
280
 
156
- type AccessListener = (key: string) => void
157
- const themeAccessListeners = new Set<AccessListener>()
158
- const defaultTheme = new Proxy(proxiedTheme, {
159
- get(target, key) {
160
- if (key[0] === '$') {
161
- themeAccessListeners.forEach((cb) => cb(String(key)))
162
- }
163
- return Reflect.get(target, key)
164
- },
165
- })
281
+ let doesUseValidImport = false
282
+ let hasImportedTheme = false
166
283
 
167
- const body = fileOrPath.type === 'Program' ? fileOrPath.get('body') : fileOrPath.program.body
284
+ const importDeclarations: t.ImportDeclaration[] = []
168
285
 
169
- /**
170
- * Step 1: Determine if importing any statically extractable components
171
- */
172
- const isInternalImport = (importStr: string) => {
173
- return isInsideTamagui(sourcePath) && importStr[0] === '.'
174
- }
286
+ for (const bodyPath of body) {
287
+ if (bodyPath.type !== 'ImportDeclaration') continue
288
+ const node = ('node' in bodyPath ? bodyPath.node : bodyPath) as t.ImportDeclaration
289
+ const moduleName = node.source.value
175
290
 
176
- const validComponents: { [key: string]: any } = Object.keys(components)
177
- // check if uppercase to avoid hitting media query proxy before init
178
- .filter((key) => key[0].toUpperCase() === key[0] && !!components[key]?.staticConfig)
179
- .reduce((obj, name) => {
180
- obj[name] = components[name]
181
- return obj
182
- }, {})
291
+ // if importing valid module
292
+ const valid = isValidImport(propsWithFileInfo, moduleName)
183
293
 
184
- if (shouldPrintDebug === 'verbose') {
185
- console.log('validComponents', Object.keys(validComponents))
294
+ if (valid) {
295
+ importDeclarations.push(node)
186
296
  }
187
297
 
188
- let doesUseValidImport = false
189
- let hasImportedTheme = false
190
-
191
- for (const bodyPath of body) {
192
- if (bodyPath.type !== 'ImportDeclaration') continue
193
- const node = ('node' in bodyPath ? bodyPath.node : bodyPath) as t.ImportDeclaration
194
- const from = node.source.value
195
- // if importing styled()
196
- if (extractStyledDefinitions) {
197
- if (from === '@tamagui/core' || from === 'tamagui') {
198
- if (
199
- node.specifiers.some((specifier) => {
200
- return specifier.local.name === 'styled'
201
- })
202
- ) {
203
- doesUseValidImport = true
204
- break
205
- }
206
- }
207
- }
208
- const isValidImport = props.components.includes(from) || isInternalImport(from)
209
- if (isValidImport) {
210
- const isValidComponent = node.specifiers.some((specifier) => {
211
- const name = specifier.local.name
212
- return !!(validComponents[name] || validHooks[name])
213
- })
214
- if (shouldPrintDebug === 'verbose') {
215
- console.log('import from', from, { isValidComponent })
216
- }
217
- if (isValidComponent) {
298
+ if (extractStyledDefinitions) {
299
+ if (valid) {
300
+ if (node.specifiers.some((specifier) => specifier.local.name === 'styled')) {
218
301
  doesUseValidImport = true
219
302
  break
220
303
  }
221
304
  }
222
305
  }
223
306
 
224
- if (shouldPrintDebug) {
225
- console.log(sourcePath, { doesUseValidImport })
307
+ if (valid) {
308
+ const names = node.specifiers.map((specifier) => specifier.local.name)
309
+ const isValidComponent = names.some((name) =>
310
+ Boolean(isValidImport(propsWithFileInfo, moduleName, name) || validHooks[name])
311
+ )
312
+ if (shouldPrintDebug === 'verbose') {
313
+ logger.info(
314
+ `import ${names.join(', ')} from ${moduleName} isValidComponent ${isValidComponent}`
315
+ )
316
+ }
317
+ if (isValidComponent) {
318
+ doesUseValidImport = true
319
+ break
320
+ }
226
321
  }
322
+ }
323
+
324
+ if (shouldPrintDebug) {
325
+ logger.info(`file: ${sourcePath} ${JSON.stringify({ doesUseValidImport, hasImportedTheme })}`)
326
+ }
227
327
 
228
- if (!doesUseValidImport) {
328
+ if (!doesUseValidImport) {
329
+ return null
330
+ }
331
+
332
+ function getValidImportedComponent(componentName: string) {
333
+ const importDeclaration = importDeclarations.find((dec) =>
334
+ dec.specifiers.some((spec) => spec.local.name === componentName)
335
+ )
336
+ if (!importDeclaration) {
229
337
  return null
230
338
  }
339
+ return getValidImport(propsWithFileInfo, importDeclaration.source.value, componentName)
340
+ }
341
+
342
+ tm.mark('import-check', !!shouldPrintDebug)
343
+
344
+ let couldntParse = false
345
+ const modifiedComponents = new Set<NodePath<any>>()
346
+
347
+ // only keeping a cache around per-file, reset it if it changes
348
+ const bindingCache: Record<string, string | null> = {}
349
+
350
+ const callTraverse = (a: TraverseOptions<any>) => {
351
+ return fileOrPath.type === 'File' ? traverse(fileOrPath, a) : fileOrPath.traverse(a)
352
+ }
353
+
354
+ const shouldDisableExtraction =
355
+ disableExtraction === true ||
356
+ (Array.isArray(disableExtraction) && disableExtraction.includes(sourcePath))
357
+
358
+ /**
359
+ * Step 2: Statically extract from JSX < /> nodes
360
+ */
361
+ let programPath: NodePath<t.Program> | null = null
362
+
363
+ const res = {
364
+ styled: 0,
365
+ flattened: 0,
366
+ optimized: 0,
367
+ modified: 0,
368
+ found: 0,
369
+ }
370
+
371
+ callTraverse({
372
+ // @ts-ignore
373
+ Program: {
374
+ enter(path) {
375
+ programPath = path
376
+ },
377
+ },
231
378
 
232
- tm.mark('import-check', shouldPrintDebug === 'verbose')
379
+ // styled() calls
380
+ CallExpression(path) {
381
+ if (disable || shouldDisableExtraction || extractStyledDefinitions === false) {
382
+ return
383
+ }
233
384
 
234
- let couldntParse = false
235
- const modifiedComponents = new Set<NodePath<any>>()
385
+ if (!t.isIdentifier(path.node.callee) || path.node.callee.name !== 'styled') {
386
+ return
387
+ }
236
388
 
237
- // only keeping a cache around per-file, reset it if it changes
238
- const bindingCache: Record<string, string | null> = {}
389
+ const name =
390
+ t.isVariableDeclarator(path.parent) && t.isIdentifier(path.parent.id)
391
+ ? path.parent.id.name
392
+ : 'unknown'
239
393
 
240
- const callTraverse = (a: Visitor<{}>) => {
241
- return fileOrPath.type === 'File' ? traverse(fileOrPath, a) : fileOrPath.traverse(a)
242
- }
394
+ const definition = path.node.arguments[1]
243
395
 
244
- /**
245
- * Step 2: Statically extract from JSX < /> nodes
246
- */
247
- let programPath: NodePath<t.Program>
248
-
249
- const res = {
250
- styled: 0,
251
- flattened: 0,
252
- optimized: 0,
253
- modified: 0,
254
- found: 0,
255
- }
396
+ if (!name || !definition || !t.isObjectExpression(definition)) {
397
+ return
398
+ }
256
399
 
257
- callTraverse({
258
- Program: {
259
- enter(path) {
260
- programPath = path
261
- },
262
- },
400
+ let Component = getValidImportedComponent(name)
263
401
 
264
- CallExpression(path) {
265
- if (disable || disableExtraction || extractStyledDefinitions === false) {
402
+ if (!Component) {
403
+ if (disableExtractFoundComponents === true) {
266
404
  return
267
405
  }
268
-
269
- if (!t.isIdentifier(path.node.callee) || path.node.callee.name !== 'styled') {
406
+ if (
407
+ Array.isArray(disableExtractFoundComponents) &&
408
+ disableExtractFoundComponents.includes(name)
409
+ ) {
270
410
  return
271
411
  }
272
412
 
273
- const name =
274
- t.isVariableDeclarator(path.parent) && t.isIdentifier(path.parent.id)
275
- ? path.parent.id.name
276
- : 'unknown'
277
- const definition = path.node.arguments[1]
278
-
279
- if (!name || !definition || !t.isObjectExpression(definition)) {
280
- return
281
- }
282
-
283
- const Component = validComponents[name] as
284
- | { staticConfig: StaticConfigParsed }
285
- | undefined
286
-
287
- if (!Component) {
413
+ try {
288
414
  if (shouldPrintDebug) {
289
- console.log(
290
- `Didn't recognize styled(${name}), ${name} isn't in design system provided to tamagui.config.ts`
291
- )
415
+ logger.info(`Unknown component ${name}, attempting dynamic load: ${sourcePath}`)
292
416
  }
293
- return
294
- }
295
417
 
296
- const componentSkipProps = new Set([
297
- ...(Component.staticConfig.inlineWhenUnflattened || []),
298
- ...(Component.staticConfig.inlineProps || []),
299
- ...(Component.staticConfig.deoptProps || []),
300
- // for now skip variants, will return to them
301
- 'variants',
302
- 'defaultVariants',
303
- // skip fontFamily its basically a "variant", important for theme use to be value always
304
- 'fontFamily',
305
- 'name',
306
- ])
418
+ const out = loadTamaguiSync({
419
+ forceExports: true,
420
+ components: [sourcePath],
421
+ })
307
422
 
308
- // for now dont parse variants, spreads, etc
309
- const skipped: (t.ObjectProperty | t.SpreadElement | t.ObjectMethod)[] = []
310
- const styles = {}
423
+ if (!out?.components) {
424
+ if (shouldPrintDebug) {
425
+ logger.info(`Couldn't load, got ${out}`)
426
+ }
427
+ return
428
+ }
311
429
 
312
- // Generate scope object at this level
313
- const staticNamespace = getStaticBindingsForScope(
314
- path.scope,
315
- importsWhitelist,
316
- sourcePath,
317
- bindingCache,
318
- shouldPrintDebug
319
- )
430
+ propsWithFileInfo.allLoadedComponents = [
431
+ ...propsWithFileInfo.allLoadedComponents,
432
+ ...out.components,
433
+ ]
320
434
 
321
- const attemptEval = !evaluateVars
322
- ? evaluateAstNode
323
- : createEvaluator({
324
- tamaguiConfig,
325
- staticNamespace,
326
- sourcePath,
327
- shouldPrintDebug,
328
- })
329
- const attemptEvalSafe = createSafeEvaluator(attemptEval)
435
+ Component = out.components.flatMap((x) => x.nameToInfo[name] ?? [])[0]
330
436
 
331
- for (const property of definition.properties) {
332
- if (
333
- !t.isObjectProperty(property) ||
334
- !t.isIdentifier(property.key) ||
335
- !isValidStyleKey(property.key.name, Component.staticConfig) ||
336
- // skip variants
337
- Component.staticConfig.variants?.[property.key.name] ||
338
- componentSkipProps.has(property.key.name)
339
- ) {
340
- skipped.push(property)
341
- continue
437
+ if (shouldPrintDebug === 'verbose') {
438
+ logger.info([`Tamagui Loaded`, JSON.stringify(out.components), !!Component].join(' '))
342
439
  }
343
- // attempt eval
344
- const out = attemptEvalSafe(property.value)
345
- if (out === FAILED_EVAL) {
346
- skipped.push(property)
347
- } else {
348
- styles[property.key.name] = out
440
+ } catch (err: any) {
441
+ if (shouldPrintDebug) {
442
+ logger.info(
443
+ `${getPrefixLogs(
444
+ options
445
+ )} skip optimize styled(${name}), unable to pre-process (DEBUG=tamagui for more)`
446
+ )
447
+ }
448
+ if (process.env.DEBUG === 'tamagui') {
449
+ logger.info(
450
+ ` Disable this with "disableExtractFoundComponents" in your build-time configuration. \n\n ${err.message} ${err.stack}`
451
+ )
349
452
  }
350
453
  }
454
+ }
351
455
 
352
- // turn parsed styles into CSS
353
- const out = getSplitStyles(styles, Component.staticConfig, defaultTheme, {
354
- focus: false,
355
- hover: false,
356
- mounted: false,
357
- press: false,
358
- pressIn: false,
359
- resolveVariablesAs: 'variable',
360
- })
456
+ if (!Component) {
457
+ return
458
+ }
361
459
 
362
- const classNames = {
363
- ...out.classNames,
460
+ const componentSkipProps = new Set([
461
+ ...(Component.staticConfig.inlineWhenUnflattened || []),
462
+ ...(Component.staticConfig.inlineProps || []),
463
+ ...(Component.staticConfig.deoptProps || []),
464
+ // for now skip variants, will return to them
465
+ 'variants',
466
+ 'defaultVariants',
467
+ // skip fontFamily its basically a "variant", important for theme use to be value always
468
+ 'fontFamily',
469
+ 'name',
470
+ ])
471
+
472
+ // for now dont parse variants, spreads, etc
473
+ const skipped: (t.ObjectProperty | t.SpreadElement | t.ObjectMethod)[] = []
474
+ const styles = {}
475
+
476
+ // Generate scope object at this level
477
+ const staticNamespace = getStaticBindingsForScope(
478
+ path.scope,
479
+ importsWhitelist,
480
+ sourcePath,
481
+ bindingCache,
482
+ shouldPrintDebug
483
+ )
484
+
485
+ const attemptEval = !evaluateVars
486
+ ? evaluateAstNode
487
+ : createEvaluator({
488
+ props: propsWithFileInfo,
489
+ staticNamespace,
490
+ sourcePath,
491
+ shouldPrintDebug,
492
+ })
493
+ const attemptEvalSafe = createSafeEvaluator(attemptEval)
494
+
495
+ for (const property of definition.properties) {
496
+ if (
497
+ !t.isObjectProperty(property) ||
498
+ !t.isIdentifier(property.key) ||
499
+ !isValidStyleKey(property.key.name, Component.staticConfig) ||
500
+ // skip variants
501
+ Component.staticConfig.variants?.[property.key.name] ||
502
+ componentSkipProps.has(property.key.name)
503
+ ) {
504
+ skipped.push(property)
505
+ continue
506
+ }
507
+ // attempt eval
508
+ const out = attemptEvalSafe(property.value)
509
+ if (out === FAILED_EVAL) {
510
+ skipped.push(property)
511
+ } else {
512
+ styles[property.key.name] = out
364
513
  }
514
+ }
365
515
 
366
- // add in the style object as classnames
367
- const atomics = getStylesAtomic(out.style)
516
+ const out = getSplitStyles(styles, Component.staticConfig, defaultTheme, {
517
+ focus: false,
518
+ hover: false,
519
+ unmounted: true,
520
+ press: false,
521
+ pressIn: false,
522
+ resolveVariablesAs: 'variable',
523
+ noClassNames: false,
524
+ })
525
+
526
+ const classNames = {
527
+ ...out.classNames,
528
+ }
368
529
 
369
- if (shouldPrintDebug) {
370
- console.log('Exctacting styled()', styles, out, 'to', atomics)
371
- }
530
+ // // add in the style object as classnames
531
+ // const atomics = getStylesAtomic(out.style)
532
+ // for (const atomic of atomics) {
533
+ // out.rulesToInsert = out.rulesToInsert || []
534
+ // out.rulesToInsert.push(atomic)
535
+ // classNames[atomic.property] = atomic.identifier
536
+ // }
372
537
 
373
- for (const atomic of atomics) {
374
- for (const rule of atomic.rules) {
375
- out.rulesToInsert = out.rulesToInsert || []
376
- out.rulesToInsert.push([atomic.identifier, rule])
377
- }
378
- classNames[atomic.property] = atomic.identifier
379
- }
538
+ if (shouldPrintDebug) {
539
+ // prettier-ignore
540
+ logger.info([`Extracted styled(${name})\n`, JSON.stringify(styles, null, 2), '\n rulesToInsert:', out.rulesToInsert.flatMap((rule) => rule.rules).join('\n')].join(' '))
541
+ }
380
542
 
381
- // leave only un-parsed props...
382
- definition.properties = skipped
543
+ // leave only un-parsed props...
544
+ definition.properties = skipped
383
545
 
384
- // ... + key: className
385
- for (const cn in classNames) {
386
- if (componentSkipProps.has(cn)) {
387
- continue
388
- }
389
- const val = classNames[cn]
390
- definition.properties.push(t.objectProperty(t.stringLiteral(cn), t.stringLiteral(val)))
546
+ // ... + key: className
547
+ for (const cn in classNames) {
548
+ if (componentSkipProps.has(cn)) {
549
+ continue
391
550
  }
551
+ const val = classNames[cn]
552
+ definition.properties.push(t.objectProperty(t.stringLiteral(cn), t.stringLiteral(val)))
553
+ }
392
554
 
393
- if (out.rulesToInsert) {
394
- for (const [identifier, rule] of out.rulesToInsert) {
395
- onStyleRule?.(identifier, [rule])
396
- }
555
+ if (out.rulesToInsert) {
556
+ for (const { identifier, rules } of out.rulesToInsert) {
557
+ onStyleRule?.(identifier, rules)
397
558
  }
559
+ }
398
560
 
399
- res.styled++
561
+ res.styled++
400
562
 
401
- if (shouldPrintDebug) {
402
- console.log(`Extracted styled(${name}) props:`, styles)
403
- }
404
- },
563
+ if (shouldPrintDebug) {
564
+ logger.info(`Extracted styled(${name})`)
565
+ }
566
+ },
405
567
 
406
- JSXElement(traversePath) {
407
- tm.mark('jsx-element', shouldPrintDebug === 'verbose')
568
+ JSXElement(traversePath) {
569
+ if (shouldDisableExtraction) {
570
+ return
571
+ }
408
572
 
409
- const node = traversePath.node.openingElement
410
- const ogAttributes = node.attributes
411
- const componentName = findComponentName(traversePath.scope)
412
- const closingElement = traversePath.node.closingElement
573
+ tm.mark('jsx-element', !!shouldPrintDebug)
413
574
 
414
- // skip non-identifier opening elements (member expressions, etc.)
415
- if (t.isJSXMemberExpression(closingElement?.name) || !t.isJSXIdentifier(node.name)) {
416
- return
417
- }
575
+ const node = traversePath.node.openingElement
576
+ const ogAttributes = node.attributes.map((attr) => ({ ...attr }))
577
+ const componentName = findComponentName(traversePath.scope)
578
+ const closingElement = traversePath.node.closingElement
418
579
 
419
- // validate its a proper import from tamagui (or internally inside tamagui)
420
- const binding = traversePath.scope.getBinding(node.name.name)
580
+ // skip non-identifier opening elements (member expressions, etc.)
581
+ if (t.isJSXMemberExpression(closingElement?.name) || !t.isJSXIdentifier(node.name)) {
582
+ return
583
+ }
421
584
 
422
- if (binding) {
423
- if (!t.isImportDeclaration(binding.path.parent)) {
424
- return
425
- }
426
- const source = binding.path.parent.source
427
- if (!props.components.includes(source.value) && !isInternalImport(source.value)) {
428
- return
585
+ // validate its a proper import from tamagui (or internally inside tamagui)
586
+ const binding = traversePath.scope.getBinding(node.name.name)
587
+ let modulePath = ''
588
+
589
+ if (binding) {
590
+ if (!t.isImportDeclaration(binding.path.parent)) {
591
+ if (shouldPrintDebug) {
592
+ logger.info(` - Binding not import declaration, skip`)
429
593
  }
430
- if (!validComponents[binding.identifier.name]) {
431
- return
594
+ return
595
+ }
596
+ modulePath = binding.path.parent.source.value
597
+ if (!isValidImport(propsWithFileInfo, modulePath, binding.identifier.name)) {
598
+ if (shouldPrintDebug) {
599
+ logger.info(` - Binding not internal import or from components ${modulePath}`)
432
600
  }
601
+ return
433
602
  }
603
+ }
434
604
 
435
- const component = validComponents[node.name.name] as { staticConfig?: StaticConfigParsed }
436
- if (!component || !component.staticConfig) {
437
- return
605
+ const component = getValidComponent(propsWithFileInfo, modulePath, node.name.name)
606
+ if (!component || !component.staticConfig) {
607
+ if (shouldPrintDebug) {
608
+ logger.info(` - No Tamagui conf on this: ${node.name.name}`)
438
609
  }
610
+ return
611
+ }
612
+
613
+ const originalNodeName = node.name.name
439
614
 
440
- const originalNodeName = node.name.name
615
+ // found a valid tag
616
+ res.found++
441
617
 
442
- // found a valid tag
443
- res.found++
618
+ const filePath = `./${relative(process.cwd(), sourcePath)}`
619
+ const lineNumbers = node.loc
620
+ ? node.loc.start.line +
621
+ (node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '')
622
+ : ''
444
623
 
445
- const filePath = `./${relative(process.cwd(), sourcePath)}`
446
- const lineNumbers = node.loc
447
- ? node.loc.start.line +
448
- (node.loc.start.line !== node.loc.end.line ? `-${node.loc.end.line}` : '')
449
- : ''
450
- const tagId = [componentName, `${node.name.name}`, `${filePath}:${lineNumbers}`].filter(
451
- Boolean
624
+ const codePosition = `${filePath}:${lineNumbers}`
625
+
626
+ // debug just one
627
+ const debugPropValue = node.attributes
628
+ .filter(
629
+ (n) => t.isJSXAttribute(n) && t.isJSXIdentifier(n.name) && n.name.name === 'debug'
452
630
  )
631
+ // @ts-ignore
632
+ .map((n: t.JSXAttribute) => {
633
+ if (n.value === null) return true
634
+ if (t.isStringLiteral(n.value)) return n.value.value as 'verbose'
635
+ return false
636
+ })[0] as boolean | 'verbose' | undefined
637
+
638
+ if (debugPropValue) {
639
+ shouldPrintDebug = debugPropValue
640
+ }
453
641
 
454
- // debug just one
455
- const debugPropValue = node.attributes
456
- .filter<t.JSXAttribute>(
457
- // @ts-ignore
458
- (n) => t.isJSXAttribute(n) && t.isJSXIdentifier(n.name) && n.name.name === 'debug'
459
- )
460
- .map((n) => {
461
- if (n.value === null) return true
462
- if (t.isStringLiteral(n.value)) return n.value.value as 'verbose'
463
- return false
464
- })[0]
642
+ if (shouldPrintDebug) {
643
+ logger.info('\n')
644
+ logger.info(
645
+ `\x1b[33m%s\x1b[0m ` + `${componentName} | ${codePosition} -------------------`
646
+ )
647
+ // prettier-ignore
648
+ logger.info(['\x1b[1m', '\x1b[32m', `<${originalNodeName} />`, disableDebugAttr ? '' : '🐛'].join(' '))
649
+ }
465
650
 
466
- if (debugPropValue) {
467
- shouldPrintDebug = debugPropValue
651
+ // add data-* debug attributes
652
+ if (shouldAddDebugProp && !disableDebugAttr) {
653
+ res.modified++
654
+ node.attributes.unshift(
655
+ t.jsxAttribute(t.jsxIdentifier('data-is'), t.stringLiteral(node.name.name))
656
+ )
657
+ if (componentName) {
658
+ node.attributes.unshift(
659
+ t.jsxAttribute(t.jsxIdentifier('data-in'), t.stringLiteral(componentName))
660
+ )
468
661
  }
469
662
 
470
- try {
471
- node.attributes.find(
472
- (n) =>
473
- t.isJSXAttribute(n) &&
474
- t.isJSXIdentifier(n.name) &&
475
- n.name.name === 'debug' &&
476
- n.value === null
663
+ node.attributes.unshift(
664
+ t.jsxAttribute(
665
+ t.jsxIdentifier('data-at'),
666
+ t.stringLiteral(`${basename(filePath)}:${lineNumbers}`)
477
667
  )
668
+ )
669
+ }
478
670
 
479
- if (shouldPrintDebug) {
480
- console.log('\n')
481
- console.log('\x1b[33m%s\x1b[0m', `${tagId[0]} | ${tagId[2]} -------------------`)
482
- console.log('\x1b[1m', '\x1b[32m', `<${originalNodeName} />`)
483
- }
484
-
485
- // add data-is
486
- if (shouldAddDebugProp && !disableDebugAttr) {
487
- res.modified++
488
- node.attributes.unshift(
489
- t.jsxAttribute(t.jsxIdentifier('data-is'), t.stringLiteral(tagId.join(' ')))
490
- )
491
- }
492
-
493
- const shouldLog = !hasLogged
494
- if (shouldLog) {
495
- console.log(` 1️⃣ Inline optimized 2️⃣ Inline flattened 3️⃣ styled() extracted`)
496
- const prefix = ' |'
497
- // prettier-ignore
498
- console.log(prefixLogs || prefix, ' total · 1️⃣ · 2️⃣ · 3️⃣')
499
- hasLogged = true
500
- }
501
- if (disableExtraction) {
502
- return
503
- }
504
-
505
- const { staticConfig } = component
506
- const variants = staticConfig.variants || {}
507
- const isTextView = staticConfig.isText || false
508
- const validStyles = staticConfig?.validStyles ?? {}
509
-
510
- // find tag="a" tag="main" etc dom indicators
511
- let tagName = staticConfig.defaultProps.tag ?? (isTextView ? 'span' : 'div')
512
- traversePath
513
- .get('openingElement')
514
- .get('attributes')
515
- .forEach((path) => {
516
- const attr = path.node
517
- if (t.isJSXSpreadAttribute(attr)) return
518
- if (attr.name.name !== 'tag') return
519
- const val = attr.value
520
- if (!t.isStringLiteral(val)) return
521
- tagName = val.value
522
- })
671
+ // disable as it gets messy
672
+ // const shouldLog = !hasLogged
673
+ // if (shouldLog) {
674
+ // logger.info(` 1️⃣ Inline optimized 2️⃣ Inline flattened 3️⃣ styled() extracted`)
675
+ // const prefix = ' |'
676
+ // // prettier-ignore
677
+ // logger.info([prefixLogs || prefix, ' total · 1️⃣ · 2️⃣ · 3️⃣'].join(' '))
678
+ // hasLogged = true
679
+ // }
680
+
681
+ if (disableExtraction) {
682
+ return
683
+ }
523
684
 
524
- const flatNode = getFlattenedNode({ isTextView, tag: tagName })
685
+ try {
686
+ const { staticConfig } = component
687
+ const variants = staticConfig.variants || {}
688
+ const isTextView = staticConfig.isText || false
689
+ const validStyles = staticConfig?.validStyles ?? {}
690
+
691
+ // find tag="a" tag="main" etc dom indicators
692
+ let tagName = staticConfig.defaultProps.tag ?? (isTextView ? 'span' : 'div')
693
+ traversePath
694
+ .get('openingElement')
695
+ .get('attributes')
696
+ .forEach((path) => {
697
+ const attr = path.node
698
+ if (t.isJSXSpreadAttribute(attr)) return
699
+ if (attr.name.name !== 'tag') return
700
+ const val = attr.value
701
+ if (!t.isStringLiteral(val)) return
702
+ tagName = val.value
703
+ })
525
704
 
526
- const inlineProps = new Set([
527
- ...(props.inlineProps || []),
528
- ...(staticConfig.inlineProps || []),
529
- ])
705
+ const flatNode = getFlattenedNode?.({ isTextView, tag: tagName })
530
706
 
531
- const deoptProps = new Set([
532
- // always de-opt animation
533
- 'animation',
534
- ...(props.deoptProps || []),
535
- ...(staticConfig.deoptProps || []),
536
- ])
707
+ const inlineProps = new Set([
708
+ ...(restProps.inlineProps || []),
709
+ ...(staticConfig.inlineProps || []),
710
+ ])
537
711
 
538
- const inlineWhenUnflattened = new Set([...(staticConfig.inlineWhenUnflattened || [])])
712
+ const deoptProps = new Set([
713
+ // always de-opt animation
714
+ 'animation',
715
+ ...(restProps.deoptProps || []),
716
+ ...(staticConfig.deoptProps || []),
717
+ ])
539
718
 
540
- // Generate scope object at this level
541
- const staticNamespace = getStaticBindingsForScope(
542
- traversePath.scope,
543
- importsWhitelist,
544
- sourcePath,
545
- bindingCache,
546
- shouldPrintDebug
547
- )
719
+ const inlineWhenUnflattened = new Set([...(staticConfig.inlineWhenUnflattened || [])])
548
720
 
549
- const attemptEval = !evaluateVars
550
- ? evaluateAstNode
551
- : createEvaluator({
552
- tamaguiConfig,
553
- staticNamespace,
554
- sourcePath,
555
- traversePath,
556
- shouldPrintDebug,
557
- })
558
- const attemptEvalSafe = createSafeEvaluator(attemptEval)
721
+ // Generate scope object at this level
722
+ const staticNamespace = getStaticBindingsForScope(
723
+ traversePath.scope,
724
+ importsWhitelist,
725
+ sourcePath,
726
+ bindingCache,
727
+ shouldPrintDebug
728
+ )
559
729
 
560
- if (shouldPrintDebug) {
561
- console.log(' staticNamespace', Object.keys(staticNamespace).join(', '))
562
- }
730
+ const attemptEval = !evaluateVars
731
+ ? evaluateAstNode
732
+ : createEvaluator({
733
+ props: propsWithFileInfo,
734
+ staticNamespace,
735
+ sourcePath,
736
+ traversePath,
737
+ shouldPrintDebug,
738
+ })
739
+ const attemptEvalSafe = createSafeEvaluator(attemptEval)
563
740
 
564
- //
565
- // SPREADS SETUP
566
- //
741
+ if (shouldPrintDebug) {
742
+ logger.info(` staticNamespace ${Object.keys(staticNamespace).join(', ')}`)
743
+ }
567
744
 
568
- // TODO restore
569
- // const hasDeopt = (obj: Object) => {
570
- // return Object.keys(obj).some(isDeoptedProp)
571
- // }
572
-
573
- // flatten any easily evaluatable spreads
574
- const flattenedAttrs: (t.JSXAttribute | t.JSXSpreadAttribute)[] = []
575
- traversePath
576
- .get('openingElement')
577
- .get('attributes')
578
- .forEach((path) => {
579
- const attr = path.node
580
- if (!t.isJSXSpreadAttribute(attr)) {
581
- flattenedAttrs.push(attr)
582
- return
745
+ //
746
+ // SPREADS SETUP
747
+ //
748
+
749
+ // TODO restore
750
+ // const hasDeopt = (obj: Object) => {
751
+ // return Object.keys(obj).some(isDeoptedProp)
752
+ // }
753
+
754
+ // flatten any easily evaluatable spreads
755
+ const flattenedAttrs: (t.JSXAttribute | t.JSXSpreadAttribute)[] = []
756
+ traversePath
757
+ .get('openingElement')
758
+ .get('attributes')
759
+ .forEach((path) => {
760
+ const attr = path.node
761
+ if (!t.isJSXSpreadAttribute(attr)) {
762
+ flattenedAttrs.push(attr)
763
+ return
764
+ }
765
+ let arg: any
766
+ try {
767
+ arg = attemptEval(attr.argument)
768
+ } catch (e: any) {
769
+ if (shouldPrintDebug) {
770
+ logger.info([' couldnt parse spread', e.message].join(' '))
583
771
  }
584
- let arg: any
772
+ flattenedAttrs.push(attr)
773
+ return
774
+ }
775
+ if (arg !== undefined) {
585
776
  try {
586
- arg = attemptEval(attr.argument)
587
- } catch (e: any) {
588
- if (shouldPrintDebug) {
589
- console.log(' couldnt parse spread', e.message)
590
- }
591
- flattenedAttrs.push(attr)
592
- return
593
- }
594
- if (arg !== undefined) {
595
- try {
596
- if (typeof arg !== 'object' || arg == null) {
597
- if (shouldPrintDebug) {
598
- console.log(' non object or null arg', arg)
777
+ if (typeof arg !== 'object' || arg == null) {
778
+ if (shouldPrintDebug) {
779
+ logger.info([' non object or null arg', arg].join(' '))
780
+ }
781
+ flattenedAttrs.push(attr)
782
+ } else {
783
+ for (const k in arg) {
784
+ const value = arg[k]
785
+ // this is a null prop:
786
+ if (!value && typeof value === 'object') {
787
+ logger.error(['Unhandled null prop', k, value, arg].join(' '))
788
+ continue
599
789
  }
600
- flattenedAttrs.push(attr)
601
- } else {
602
- for (const k in arg) {
603
- const value = arg[k]
604
- // this is a null prop:
605
- if (!value && typeof value === 'object') {
606
- console.log('shouldnt we handle this?', k, value, arg)
607
- continue
608
- }
609
- flattenedAttrs.push(
610
- t.jsxAttribute(
611
- t.jsxIdentifier(k),
612
- t.jsxExpressionContainer(literalToAst(value))
613
- )
790
+ flattenedAttrs.push(
791
+ t.jsxAttribute(
792
+ t.jsxIdentifier(k),
793
+ t.jsxExpressionContainer(literalToAst(value))
614
794
  )
615
- }
795
+ )
616
796
  }
617
- } catch (err) {
618
- console.warn('cant parse spread, caught err', err)
619
- couldntParse = true
620
797
  }
798
+ } catch (err) {
799
+ logger.warn(`cant parse spread, caught err ${err}`)
800
+ couldntParse = true
621
801
  }
622
- })
623
-
624
- if (couldntParse) {
625
- return
626
- }
627
-
628
- tm.mark('jsx-element-flattened', shouldPrintDebug === 'verbose')
629
-
630
- // set flattened
631
- node.attributes = flattenedAttrs
632
-
633
- let attrs: ExtractedAttr[] = []
634
- let shouldDeopt = false
635
- const inlined = new Map<string, any>()
636
- const variantValues = new Map<string, any>()
637
- let hasSetOptimized = false
638
- const inlineWhenUnflattenedOGVals = {}
639
-
640
- // RUN first pass
641
-
642
- // normalize all conditionals so we can evaluate away easier later
643
- // at the same time lets normalize shorthand media queries into spreads:
644
- // that way we can parse them with the same logic later on
645
- //
646
- // {...media.sm && { color: x ? 'red' : 'blue' }}
647
- // => {...media.sm && x && { color: 'red' }}
648
- // => {...media.sm && !x && { color: 'blue' }}
649
- //
650
- // $sm={{ color: 'red' }}
651
- // => {...media.sm && { color: 'red' }}
652
- //
653
- // $sm={{ color: x ? 'red' : 'blue' }}
654
- // => {...media.sm && x && { color: 'red' }}
655
- // => {...media.sm && !x && { color: 'blue' }}
656
-
657
- attrs = traversePath
658
- .get('openingElement')
659
- .get('attributes')
660
- .flatMap((path) => {
661
- try {
662
- const res = evaluateAttribute(path)
663
- tm.mark('jsx-element-evaluate-attr', shouldPrintDebug === 'verbose')
664
- if (!res) {
665
- path.remove()
666
- }
667
- return res
668
- } catch (err: any) {
669
- if (shouldPrintDebug) {
670
- console.log('Error extracting attribute', err.message, err.stack)
671
- console.log('node', path.node)
672
- }
673
- // dont flatten if we run into error
674
- inlined.set(`${Math.random()}`, 'spread')
675
- return {
676
- type: 'attr',
677
- value: path.node,
678
- } as const
679
- }
680
- })
681
- .flat(4)
682
- .filter(isPresent)
802
+ }
803
+ })
683
804
 
684
- if (shouldPrintDebug) {
685
- console.log(' - attrs (before):\n', logLines(attrs.map(attrStr).join(', ')))
686
- }
805
+ if (couldntParse) {
806
+ return
807
+ }
687
808
 
688
- // START function evaluateAttribute
689
- function evaluateAttribute(
690
- path: NodePath<t.JSXAttribute | t.JSXSpreadAttribute>
691
- ): ExtractedAttr | ExtractedAttr[] | null {
692
- const attribute = path.node
693
- const attr: ExtractedAttr = { type: 'attr', value: attribute }
694
- // ...spreads
695
- if (t.isJSXSpreadAttribute(attribute)) {
696
- const arg = attribute.argument
697
- const conditional = t.isConditionalExpression(arg)
698
- ? // <YStack {...isSmall ? { color: 'red } : { color: 'blue }}
699
- ([arg.test, arg.consequent, arg.alternate] as const)
700
- : t.isLogicalExpression(arg) && arg.operator === '&&'
701
- ? // <YStack {...isSmall && { color: 'red }}
702
- ([arg.left, arg.right, null] as const)
703
- : null
704
-
705
- if (conditional) {
706
- const [test, alt, cons] = conditional
707
- if (!test) throw new Error(`no test`)
708
- if ([alt, cons].some((side) => side && !isExtractable(side))) {
709
- if (shouldPrintDebug) {
710
- console.log('not extractable', alt, cons)
711
- }
712
- return attr
713
- }
714
- // split into individual ternaries per object property
715
- return [
716
- ...(createTernariesFromObjectProperties(test, alt) || []),
717
- ...((cons &&
718
- createTernariesFromObjectProperties(t.unaryExpression('!', test), cons)) ||
719
- []),
720
- ].map((ternary) => ({
721
- type: 'ternary',
722
- value: ternary,
723
- }))
809
+ tm.mark('jsx-element-flattened', !!shouldPrintDebug)
810
+
811
+ // set flattened
812
+ node.attributes = flattenedAttrs
813
+
814
+ let attrs: ExtractedAttr[] = []
815
+ let shouldDeopt = false
816
+ const inlined = new Map<string, any>()
817
+ const variantValues = new Map<string, any>()
818
+ let hasSetOptimized = false
819
+ const inlineWhenUnflattenedOGVals = {}
820
+
821
+ // RUN first pass
822
+
823
+ // normalize all conditionals so we can evaluate away easier later
824
+ // at the same time lets normalize shorthand media queries into spreads:
825
+ // that way we can parse them with the same logic later on
826
+ //
827
+ // {...media.sm && { color: x ? 'red' : 'blue' }}
828
+ // => {...media.sm && x && { color: 'red' }}
829
+ // => {...media.sm && !x && { color: 'blue' }}
830
+ //
831
+ // $sm={{ color: 'red' }}
832
+ // => {...media.sm && { color: 'red' }}
833
+ //
834
+ // $sm={{ color: x ? 'red' : 'blue' }}
835
+ // => {...media.sm && x && { color: 'red' }}
836
+ // => {...media.sm && !x && { color: 'blue' }}
837
+
838
+ attrs = traversePath
839
+ .get('openingElement')
840
+ .get('attributes')
841
+ .flatMap((path) => {
842
+ try {
843
+ const res = evaluateAttribute(path)
844
+ tm.mark('jsx-element-evaluate-attr', !!shouldPrintDebug)
845
+ if (!res) {
846
+ path.remove()
724
847
  }
725
- }
726
- // END ...spreads
727
-
728
- // directly keep these
729
- // couldn't evaluate spread, undefined name, or name is not string
730
- if (
731
- t.isJSXSpreadAttribute(attribute) ||
732
- !attribute.name ||
733
- typeof attribute.name.name !== 'string'
734
- ) {
848
+ return res
849
+ } catch (err: any) {
735
850
  if (shouldPrintDebug) {
736
- console.log(' ! inlining, spread attr')
851
+ logger.info(
852
+ [
853
+ 'Recoverable error extracting attribute',
854
+ err.message,
855
+ shouldPrintDebug === 'verbose' ? err.stack : '',
856
+ ].join(' ')
857
+ )
858
+ if (shouldPrintDebug === 'verbose') {
859
+ logger.info(`node ${path.node?.type}`)
860
+ }
737
861
  }
862
+ // dont flatten if we run into error
738
863
  inlined.set(`${Math.random()}`, 'spread')
739
- return attr
864
+ return {
865
+ type: 'attr',
866
+ value: path.node,
867
+ } as const
740
868
  }
869
+ })
870
+ .flat(4)
871
+ .filter(isPresent)
741
872
 
742
- const name = attribute.name.name
873
+ if (shouldPrintDebug) {
874
+ logger.info(
875
+ [' - attrs (before):\n', logLines(attrs.map(attrStr).join(', '))].join(' ')
876
+ )
877
+ }
743
878
 
744
- if (excludeProps?.has(name)) {
745
- if (shouldPrintDebug) {
746
- console.log(' excluding prop', name)
879
+ // START function evaluateAttribute
880
+ function evaluateAttribute(
881
+ path: NodePath<t.JSXAttribute | t.JSXSpreadAttribute>
882
+ ): ExtractedAttr | ExtractedAttr[] | null {
883
+ const attribute = path.node
884
+ const attr: ExtractedAttr = { type: 'attr', value: attribute }
885
+ // ...spreads
886
+ if (t.isJSXSpreadAttribute(attribute)) {
887
+ const arg = attribute.argument
888
+ const conditional = t.isConditionalExpression(arg)
889
+ ? // <YStack {...isSmall ? { color: 'red } : { color: 'blue }}
890
+ ([arg.test, arg.consequent, arg.alternate] as const)
891
+ : t.isLogicalExpression(arg) && arg.operator === '&&'
892
+ ? // <YStack {...isSmall && { color: 'red }}
893
+ ([arg.left, arg.right, null] as const)
894
+ : null
895
+
896
+ if (conditional) {
897
+ const [test, alt, cons] = conditional
898
+ if (!test) throw new Error(`no test`)
899
+ if ([alt, cons].some((side) => side && !isStaticObject(side))) {
900
+ if (shouldPrintDebug) {
901
+ logger.info(`not extractable ${alt} ${cons}`)
902
+ }
903
+ return attr
747
904
  }
748
- return null
905
+ // split into individual ternaries per object property
906
+ return [
907
+ ...(createTernariesFromObjectProperties(test, alt) || []),
908
+ ...((cons &&
909
+ createTernariesFromObjectProperties(t.unaryExpression('!', test), cons)) ||
910
+ []),
911
+ ].map((ternary) => ({
912
+ type: 'ternary',
913
+ value: ternary,
914
+ }))
749
915
  }
916
+ }
917
+ // END ...spreads
750
918
 
751
- if (inlineProps.has(name)) {
752
- inlined.set(name, name)
753
- if (shouldPrintDebug) {
754
- console.log(' ! inlining, inline prop', name)
755
- }
756
- return attr
919
+ // directly keep these
920
+ // couldn't evaluate spread, undefined name, or name is not string
921
+ if (
922
+ t.isJSXSpreadAttribute(attribute) ||
923
+ !attribute.name ||
924
+ typeof attribute.name.name !== 'string'
925
+ ) {
926
+ if (shouldPrintDebug) {
927
+ logger.info(' ! inlining, spread attr')
757
928
  }
929
+ inlined.set(`${Math.random()}`, 'spread')
930
+ return attr
931
+ }
758
932
 
759
- // can still optimize the object... see hoverStyle on native
760
- if (deoptProps.has(name)) {
761
- shouldDeopt = true
762
- inlined.set(name, name)
763
- if (shouldPrintDebug) {
764
- console.log(' ! inlining, deopted prop', name)
765
- }
766
- return attr
767
- }
933
+ const name = attribute.name.name
768
934
 
769
- // pass className, key, and style props through untouched
770
- if (UNTOUCHED_PROPS[name]) {
771
- return attr
935
+ if (excludeProps?.has(name)) {
936
+ if (shouldPrintDebug) {
937
+ logger.info([' excluding prop', name].join(' '))
772
938
  }
939
+ return null
940
+ }
773
941
 
774
- if (INLINE_EXTRACTABLE[name]) {
775
- inlined.set(name, INLINE_EXTRACTABLE[name])
776
- return attr
942
+ if (inlineProps.has(name)) {
943
+ inlined.set(name, name)
944
+ if (shouldPrintDebug) {
945
+ logger.info([' ! inlining, inline prop', name].join(' '))
777
946
  }
947
+ return attr
948
+ }
778
949
 
779
- if (name.startsWith('data-')) {
780
- return attr
950
+ // can still optimize the object... see hoverStyle on native
951
+ if (deoptProps.has(name)) {
952
+ shouldDeopt = true
953
+ inlined.set(name, name)
954
+ if (shouldPrintDebug) {
955
+ logger.info([' ! inlining, deopted prop', name].join(' '))
781
956
  }
957
+ return attr
958
+ }
959
+
960
+ // pass className, key, and style props through untouched
961
+ if (UNTOUCHED_PROPS[name]) {
962
+ return attr
963
+ }
964
+
965
+ if (INLINE_EXTRACTABLE[name]) {
966
+ inlined.set(name, INLINE_EXTRACTABLE[name])
967
+ return attr
968
+ }
969
+
970
+ if (name.startsWith('data-')) {
971
+ return attr
972
+ }
973
+
974
+ // shorthand media queries
975
+ if (name[0] === '$' && t.isJSXExpressionContainer(attribute?.value)) {
976
+ const shortname = name.slice(1)
977
+ if (mediaQueryConfig[shortname]) {
978
+ if (target === 'native') {
979
+ shouldDeopt = true
980
+ }
782
981
 
783
- // shorthand media queries
784
- if (name[0] === '$' && t.isJSXExpressionContainer(attribute?.value)) {
785
982
  // allow disabling this extraction
786
983
  if (disableExtractInlineMedia) {
787
984
  return attr
788
985
  }
789
986
 
790
- const shortname = name.slice(1)
791
- if (mediaQueryConfig[shortname]) {
792
- const expression = attribute.value.expression
793
- if (!t.isJSXEmptyExpression(expression)) {
794
- const ternaries = createTernariesFromObjectProperties(
795
- t.stringLiteral(shortname),
796
- expression,
797
- {
798
- inlineMediaQuery: shortname,
799
- }
800
- )
801
- if (ternaries) {
802
- return ternaries.map((value) => ({
803
- type: 'ternary',
804
- value,
805
- }))
987
+ const expression = attribute.value.expression
988
+ if (!t.isJSXEmptyExpression(expression)) {
989
+ const ternaries = createTernariesFromObjectProperties(
990
+ t.stringLiteral(shortname),
991
+ expression,
992
+ {
993
+ inlineMediaQuery: shortname,
806
994
  }
995
+ )
996
+ if (ternaries) {
997
+ return ternaries.map((value) => ({
998
+ type: 'ternary',
999
+ value,
1000
+ }))
807
1001
  }
808
1002
  }
809
1003
  }
1004
+ }
810
1005
 
811
- const [value, valuePath] = (() => {
812
- if (t.isJSXExpressionContainer(attribute?.value)) {
813
- return [attribute.value.expression!, path.get('value')!] as const
814
- } else {
815
- return [attribute.value!, path.get('value')!] as const
816
- }
817
- })()
818
-
819
- const remove = () => {
820
- Array.isArray(valuePath) ? valuePath.map((p) => p.remove()) : valuePath.remove()
1006
+ const [value, valuePath] = (() => {
1007
+ if (t.isJSXExpressionContainer(attribute?.value)) {
1008
+ return [attribute.value.expression!, path.get('value')!] as const
1009
+ } else {
1010
+ return [attribute.value!, path.get('value')!] as const
821
1011
  }
1012
+ })()
822
1013
 
823
- if (name === 'ref') {
824
- if (shouldPrintDebug) {
825
- console.log(' ! inlining, ref', name)
826
- }
827
- inlined.set('ref', 'ref')
828
- return attr
1014
+ const remove = () => {
1015
+ Array.isArray(valuePath) ? valuePath.map((p) => p.remove()) : valuePath.remove()
1016
+ }
1017
+
1018
+ if (name === 'ref') {
1019
+ if (shouldPrintDebug) {
1020
+ logger.info([' ! inlining, ref', name].join(' '))
829
1021
  }
1022
+ inlined.set('ref', 'ref')
1023
+ return attr
1024
+ }
830
1025
 
831
- if (name === 'tag') {
832
- return {
833
- type: 'attr',
834
- value: path.node,
835
- }
1026
+ if (name === 'tag') {
1027
+ return {
1028
+ type: 'attr',
1029
+ value: path.node,
836
1030
  }
1031
+ }
837
1032
 
838
- // native shouldn't extract variables
839
- if (disableExtractVariables) {
840
- if (value) {
841
- if (value.type === 'StringLiteral' && value.value[0] === '$') {
842
- if (shouldPrintDebug) {
843
- console.log(` ! inlining, native disable extract: ${name} =`, value.value)
844
- }
845
- inlined.set(name, true)
846
- return attr
1033
+ // native shouldn't extract variables
1034
+ if (disableExtractVariables === true) {
1035
+ if (value) {
1036
+ if (value.type === 'StringLiteral' && value.value[0] === '$') {
1037
+ if (shouldPrintDebug) {
1038
+ logger.info(
1039
+ [` ! inlining, native disable extract: ${name} =`, value.value].join(' ')
1040
+ )
847
1041
  }
1042
+ inlined.set(name, true)
1043
+ return attr
848
1044
  }
849
1045
  }
1046
+ }
850
1047
 
851
- if (name === 'theme') {
852
- inlined.set('theme', attr.value)
853
- return attr
854
- }
855
-
856
- // if value can be evaluated, extract it and filter it out
857
- const styleValue = attemptEvalSafe(value)
858
-
859
- // never flatten if a prop isn't a valid static attribute
860
- // only post prop-mapping
861
- if (!variants[name] && !isValidStyleKey(name, staticConfig)) {
862
- let keys = [name]
863
- let out: any = null
864
-
865
- // for now passing empty props {}, a bit odd, need to at least document
866
- // for now we don't expose custom components so just noting behavior
867
- out = staticConfig.propMapper(
868
- name,
869
- styleValue,
870
- defaultTheme,
871
- staticConfig.defaultProps,
872
- { resolveVariablesAs: 'auto' },
873
- undefined,
874
- shouldPrintDebug
875
- )
1048
+ if (name === 'theme') {
1049
+ inlined.set('theme', attr.value)
1050
+ return attr
1051
+ }
876
1052
 
877
- if (out) {
878
- if (!Array.isArray(out)) {
879
- console.warn(`Error expected array but got`, out)
880
- couldntParse = true
881
- shouldDeopt = true
882
- } else {
883
- out = Object.fromEntries(out)
884
- keys = Object.keys(out)
885
- }
886
- }
887
- if (out) {
888
- if (isTargetingHTML) {
889
- // translate to DOM-compat
890
- out = createDOMProps(isTextView ? 'span' : 'div', out)
891
- // remove className - we dont use rnw styling
892
- delete out.className
893
- }
1053
+ // if value can be evaluated, extract it and filter it out
1054
+ const styleValue = attemptEvalSafe(value)
1055
+
1056
+ // never flatten if a prop isn't a valid static attribute
1057
+ // only post prop-mapping
1058
+ if (!variants[name] && !isValidStyleKey(name, staticConfig)) {
1059
+ let keys = [name]
1060
+ let out: any = null
1061
+
1062
+ // for now passing empty props {}, a bit odd, need to at least document
1063
+ // for now we don't expose custom components so just noting behavior
1064
+ out = staticConfig.propMapper(
1065
+ name,
1066
+ styleValue,
1067
+ defaultTheme,
1068
+ staticConfig.defaultProps,
1069
+ { resolveVariablesAs: 'auto' },
1070
+ undefined,
1071
+ undefined,
1072
+ shouldPrintDebug
1073
+ )
894
1074
 
1075
+ if (out) {
1076
+ if (!Array.isArray(out)) {
1077
+ logger.warn(`Error expected array but got`, out)
1078
+ couldntParse = true
1079
+ shouldDeopt = true
1080
+ } else {
1081
+ out = Object.fromEntries(out)
895
1082
  keys = Object.keys(out)
896
1083
  }
897
-
898
- let didInline = false
899
- const attributes = keys.map((key) => {
900
- const val = out[key]
901
- if (isValidStyleKey(key, staticConfig)) {
902
- return {
903
- type: 'style',
904
- value: { [key]: styleValue },
905
- name: key,
906
- attr: path.node,
907
- } as const
908
- }
909
- if (
910
- validHTMLAttributes[key] ||
911
- key.startsWith('aria-') ||
912
- key.startsWith('data-')
913
- ) {
914
- return attr
915
- }
916
- if (shouldPrintDebug) {
917
- console.log(' ! inlining, non-static', key)
918
- }
919
- didInline = true
920
- inlined.set(key, val)
921
- return val
922
- })
923
-
924
- // weird logic whats going on here
925
- if (didInline) {
926
- if (shouldPrintDebug) {
927
- console.log(' bailing flattening due to attributes', attributes)
928
- }
929
- // bail
930
- return attr
1084
+ }
1085
+ if (out) {
1086
+ if (isTargetingHTML) {
1087
+ // translate to DOM-compat
1088
+ out = createDOMProps(isTextView ? 'span' : 'div', out)
1089
+ // remove className - we dont use rnw styling
1090
+ delete out.className
931
1091
  }
932
1092
 
933
- // return evaluated attributes
934
- return attributes
1093
+ keys = Object.keys(out)
935
1094
  }
936
1095
 
937
- // FAILED = dynamic or ternary, keep going
938
- if (styleValue !== FAILED_EVAL) {
939
- if (inlineWhenUnflattened.has(name)) {
940
- // preserve original value for restoration
941
- inlineWhenUnflattenedOGVals[name] = { styleValue, attr }
942
- }
943
-
944
- if (isValidStyleKey(name, staticConfig)) {
945
- if (shouldPrintDebug) {
946
- console.log(` style: ${name} =`, styleValue)
947
- }
948
- if (!(name in staticConfig.defaultProps)) {
949
- if (!hasSetOptimized) {
950
- res.optimized++
951
- hasSetOptimized = true
952
- }
953
- }
1096
+ let didInline = false
1097
+ const attributes = keys.map((key) => {
1098
+ const val = out[key]
1099
+ if (isValidStyleKey(key, staticConfig)) {
954
1100
  return {
955
1101
  type: 'style',
956
- value: { [name]: styleValue },
957
- name,
1102
+ value: { [key]: styleValue },
1103
+ name: key,
958
1104
  attr: path.node,
959
- }
960
- } else {
961
- if (variants[name]) {
962
- variantValues.set(name, styleValue)
963
- }
964
- inlined.set(name, true)
1105
+ } as const
1106
+ }
1107
+ if (
1108
+ validHTMLAttributes[key] ||
1109
+ key.startsWith('aria-') ||
1110
+ key.startsWith('data-') ||
1111
+ // this is debug stuff added by vite / new jsx transform
1112
+ key === '__source' ||
1113
+ key === '__self'
1114
+ ) {
965
1115
  return attr
966
1116
  }
967
- }
968
-
969
- // ternaries!
1117
+ if (shouldPrintDebug) {
1118
+ logger.info(' ! inlining, non-static ' + key)
1119
+ }
1120
+ didInline = true
1121
+ inlined.set(key, val)
1122
+ return val
1123
+ })
970
1124
 
971
- // binary ternary, we can eventually make this smarter but step 1
972
- // basically for the common use case of:
973
- // opacity={(conditional ? 0 : 1) * scale}
974
- if (t.isBinaryExpression(value)) {
1125
+ // weird logic whats going on here
1126
+ if (didInline) {
975
1127
  if (shouldPrintDebug) {
976
- console.log(` binary expression ${name} = `, value)
1128
+ logger.info(` bailing flattening due to attributes ${attributes}`)
977
1129
  }
978
- const { operator, left, right } = value
979
- // if one side is a ternary, and the other side is evaluatable, we can maybe extract
980
- const lVal = attemptEvalSafe(left)
981
- const rVal = attemptEvalSafe(right)
1130
+ // bail
1131
+ return attr
1132
+ }
1133
+
1134
+ // return evaluated attributes
1135
+ return attributes
1136
+ }
1137
+
1138
+ // FAILED = dynamic or ternary, keep going
1139
+ if (styleValue !== FAILED_EVAL) {
1140
+ if (inlineWhenUnflattened.has(name)) {
1141
+ // preserve original value for restoration
1142
+ inlineWhenUnflattenedOGVals[name] = { styleValue, attr }
1143
+ }
1144
+
1145
+ if (isValidStyleKey(name, staticConfig)) {
982
1146
  if (shouldPrintDebug) {
983
- console.log(` evalBinaryExpression lVal ${String(lVal)}, rVal ${String(rVal)}`)
1147
+ logger.info(` style: ${name} = ${styleValue}`)
984
1148
  }
985
- if (lVal !== FAILED_EVAL && t.isConditionalExpression(right)) {
986
- const ternary = addBinaryConditional(operator, left, right)
987
- if (ternary) return ternary
1149
+ if (!(name in staticConfig.defaultProps)) {
1150
+ if (!hasSetOptimized) {
1151
+ res.optimized++
1152
+ hasSetOptimized = true
1153
+ }
988
1154
  }
989
- if (rVal !== FAILED_EVAL && t.isConditionalExpression(left)) {
990
- const ternary = addBinaryConditional(operator, right, left)
991
- if (ternary) return ternary
1155
+ return {
1156
+ type: 'style',
1157
+ value: { [name]: styleValue },
1158
+ name,
1159
+ attr: path.node,
992
1160
  }
993
- if (shouldPrintDebug) {
994
- console.log(` evalBinaryExpression cant extract`)
1161
+ } else {
1162
+ if (variants[name]) {
1163
+ variantValues.set(name, styleValue)
995
1164
  }
996
1165
  inlined.set(name, true)
997
1166
  return attr
998
1167
  }
1168
+ }
999
1169
 
1000
- const staticConditional = getStaticConditional(value)
1001
- if (staticConditional) {
1002
- if (shouldPrintDebug === 'verbose') {
1003
- console.log(` static conditional ${name}`, value)
1004
- }
1005
- return { type: 'ternary', value: staticConditional }
1170
+ // ternaries!
1171
+
1172
+ // binary ternary, we can eventually make this smarter but step 1
1173
+ // basically for the common use case of:
1174
+ // opacity={(conditional ? 0 : 1) * scale}
1175
+ if (t.isBinaryExpression(value)) {
1176
+ if (shouldPrintDebug) {
1177
+ logger.info(` binary expression ${name} = ${value}`)
1178
+ }
1179
+ const { operator, left, right } = value
1180
+ // if one side is a ternary, and the other side is evaluatable, we can maybe extract
1181
+ const lVal = attemptEvalSafe(left)
1182
+ const rVal = attemptEvalSafe(right)
1183
+ if (shouldPrintDebug) {
1184
+ logger.info(` evalBinaryExpression lVal ${String(lVal)}, rVal ${String(rVal)}`)
1185
+ }
1186
+ if (lVal !== FAILED_EVAL && t.isConditionalExpression(right)) {
1187
+ const ternary = addBinaryConditional(operator, left, right)
1188
+ if (ternary) return ternary
1189
+ }
1190
+ if (rVal !== FAILED_EVAL && t.isConditionalExpression(left)) {
1191
+ const ternary = addBinaryConditional(operator, right, left)
1192
+ if (ternary) return ternary
1006
1193
  }
1194
+ if (shouldPrintDebug) {
1195
+ logger.info(` evalBinaryExpression cant extract`)
1196
+ }
1197
+ inlined.set(name, true)
1198
+ return attr
1199
+ }
1007
1200
 
1008
- const staticLogical = getStaticLogical(value)
1009
- if (staticLogical) {
1010
- if (shouldPrintDebug === 'verbose') {
1011
- console.log(` static ternary ${name} = `, value)
1012
- }
1013
- return { type: 'ternary', value: staticLogical }
1201
+ const staticConditional = getStaticConditional(value)
1202
+ if (staticConditional) {
1203
+ if (shouldPrintDebug === 'verbose') {
1204
+ logger.info(` static conditional ${name} ${value}`)
1014
1205
  }
1206
+ return { type: 'ternary', value: staticConditional }
1207
+ }
1015
1208
 
1016
- // if we've made it this far, the prop stays inline
1017
- inlined.set(name, true)
1018
- if (shouldPrintDebug) {
1019
- console.log(` ! inline no match ${name}`, value)
1209
+ const staticLogical = getStaticLogical(value)
1210
+ if (staticLogical) {
1211
+ if (shouldPrintDebug === 'verbose') {
1212
+ logger.info(` static ternary ${name} = ${value}`)
1020
1213
  }
1214
+ return { type: 'ternary', value: staticLogical }
1215
+ }
1021
1216
 
1022
- //
1023
- // RETURN ATTR
1024
- //
1025
- return attr
1217
+ // if we've made it this far, the prop stays inline
1218
+ inlined.set(name, true)
1219
+ if (shouldPrintDebug) {
1220
+ logger.info(` ! inline no match ${name} ${value}`)
1221
+ }
1026
1222
 
1027
- // attr helpers:
1028
- function addBinaryConditional(
1029
- operator: any,
1030
- staticExpr: any,
1031
- cond: t.ConditionalExpression
1032
- ): ExtractedAttr | null {
1033
- if (getStaticConditional(cond)) {
1034
- const alt = attemptEval(t.binaryExpression(operator, staticExpr, cond.alternate))
1035
- const cons = attemptEval(
1036
- t.binaryExpression(operator, staticExpr, cond.consequent)
1037
- )
1223
+ //
1224
+ // RETURN ATTR
1225
+ //
1226
+ return attr
1227
+
1228
+ // attr helpers:
1229
+ function addBinaryConditional(
1230
+ operator: any,
1231
+ staticExpr: any,
1232
+ cond: t.ConditionalExpression
1233
+ ): ExtractedAttr | null {
1234
+ if (getStaticConditional(cond)) {
1235
+ const alt = attemptEval(t.binaryExpression(operator, staticExpr, cond.alternate))
1236
+ const cons = attemptEval(t.binaryExpression(operator, staticExpr, cond.consequent))
1237
+ if (shouldPrintDebug) {
1238
+ logger.info([' binaryConditional', cond.test, cons, alt].join(' '))
1239
+ }
1240
+ return {
1241
+ type: 'ternary',
1242
+ value: {
1243
+ test: cond.test,
1244
+ remove,
1245
+ alternate: { [name]: alt },
1246
+ consequent: { [name]: cons },
1247
+ },
1248
+ }
1249
+ }
1250
+ return null
1251
+ }
1252
+
1253
+ function getStaticConditional(value: t.Node): Ternary | null {
1254
+ if (t.isConditionalExpression(value)) {
1255
+ try {
1256
+ const aVal = attemptEval(value.alternate)
1257
+ const cVal = attemptEval(value.consequent)
1038
1258
  if (shouldPrintDebug) {
1039
- console.log(' binaryConditional', cond.test, cons, alt)
1259
+ const type = value.test.type
1260
+ logger.info([' static ternary', type, cVal, aVal].join(' '))
1040
1261
  }
1041
1262
  return {
1042
- type: 'ternary',
1043
- value: {
1044
- test: cond.test,
1045
- remove,
1046
- alternate: { [name]: alt },
1047
- consequent: { [name]: cons },
1048
- },
1263
+ test: value.test,
1264
+ remove,
1265
+ consequent: { [name]: cVal },
1266
+ alternate: { [name]: aVal },
1267
+ }
1268
+ } catch (err: any) {
1269
+ if (shouldPrintDebug) {
1270
+ logger.info([' cant eval ternary', err.message].join(' '))
1049
1271
  }
1050
1272
  }
1051
- return null
1052
1273
  }
1274
+ return null
1275
+ }
1053
1276
 
1054
- function getStaticConditional(value: t.Node): Ternary | null {
1055
- if (t.isConditionalExpression(value)) {
1277
+ function getStaticLogical(value: t.Node): Ternary | null {
1278
+ if (t.isLogicalExpression(value)) {
1279
+ if (value.operator === '&&') {
1056
1280
  try {
1057
- const aVal = attemptEval(value.alternate)
1058
- const cVal = attemptEval(value.consequent)
1281
+ const val = attemptEval(value.right)
1059
1282
  if (shouldPrintDebug) {
1060
- const type = value.test.type
1061
- console.log(' static ternary', type, cVal, aVal)
1283
+ logger.info([' staticLogical', value.left, name, val].join(' '))
1062
1284
  }
1063
1285
  return {
1064
- test: value.test,
1286
+ test: value.left,
1065
1287
  remove,
1066
- consequent: { [name]: cVal },
1067
- alternate: { [name]: aVal },
1288
+ consequent: { [name]: val },
1289
+ alternate: null,
1068
1290
  }
1069
- } catch (err: any) {
1291
+ } catch (err) {
1070
1292
  if (shouldPrintDebug) {
1071
- console.log(' cant eval ternary', err.message)
1293
+ logger.info([' cant static eval logical', err].join(' '))
1072
1294
  }
1073
1295
  }
1074
1296
  }
1075
- return null
1076
1297
  }
1077
-
1078
- function getStaticLogical(value: t.Node): Ternary | null {
1079
- if (t.isLogicalExpression(value)) {
1080
- if (value.operator === '&&') {
1081
- try {
1082
- const val = attemptEval(value.right)
1083
- if (shouldPrintDebug) {
1084
- console.log(' staticLogical', value.left, name, val)
1085
- }
1086
- return {
1087
- test: value.left,
1088
- remove,
1089
- consequent: { [name]: val },
1090
- alternate: null,
1091
- }
1092
- } catch (err) {
1093
- if (shouldPrintDebug) {
1094
- console.log(' cant static eval logical', err)
1095
- }
1096
- }
1097
- }
1298
+ return null
1299
+ }
1300
+ } // END function evaluateAttribute
1301
+
1302
+ function isStaticObject(obj: t.Node): obj is t.ObjectExpression {
1303
+ return (
1304
+ t.isObjectExpression(obj) &&
1305
+ obj.properties.every((prop) => {
1306
+ if (!t.isObjectProperty(prop)) {
1307
+ logger.info(['not object prop', prop].join(' '))
1308
+ return false
1098
1309
  }
1099
- return null
1100
- }
1101
- } // END function evaluateAttribute
1102
-
1103
- function isExtractable(obj: t.Node): obj is t.ObjectExpression {
1104
- return (
1105
- t.isObjectExpression(obj) &&
1106
- obj.properties.every((prop) => {
1107
- if (!t.isObjectProperty(prop)) {
1108
- console.log('not object prop', prop)
1109
- return false
1110
- }
1111
- const propName = prop.key['name']
1112
- if (!isValidStyleKey(propName, staticConfig) && propName !== 'tag') {
1113
- if (shouldPrintDebug) {
1114
- console.log(' not a valid style prop!', propName)
1115
- }
1116
- return false
1310
+ const propName = prop.key['name']
1311
+ if (!isValidStyleKey(propName, staticConfig) && propName !== 'tag') {
1312
+ if (shouldPrintDebug) {
1313
+ logger.info([' not a valid style prop!', propName].join(' '))
1117
1314
  }
1118
- return true
1119
- })
1120
- )
1121
- }
1315
+ return false
1316
+ }
1317
+ return true
1318
+ })
1319
+ )
1320
+ }
1122
1321
 
1123
- // side = {
1124
- // color: 'red',
1125
- // background: x ? 'red' : 'green',
1126
- // $gtSm: { color: 'green' }
1127
- // }
1128
- // => Ternary<test, { color: 'red' }, null>
1129
- // => Ternary<test && x, { background: 'red' }, null>
1130
- // => Ternary<test && !x, { background: 'green' }, null>
1131
- // => Ternary<test && '$gtSm', { color: 'green' }, null>
1132
- function createTernariesFromObjectProperties(
1133
- test: t.Expression,
1134
- side: t.Expression | null,
1135
- ternaryPartial: Partial<Ternary> = {}
1136
- ): null | Ternary[] {
1137
- if (!side) {
1138
- return null
1139
- }
1140
- if (!isExtractable(side)) {
1141
- throw new Error('not extractable')
1322
+ // side = {
1323
+ // color: 'red',
1324
+ // background: x ? 'red' : 'green',
1325
+ // $gtSm: { color: 'green' }
1326
+ // }
1327
+ // => Ternary<test, { color: 'red' }, null>
1328
+ // => Ternary<test && x, { background: 'red' }, null>
1329
+ // => Ternary<test && !x, { background: 'green' }, null>
1330
+ // => Ternary<test && '$gtSm', { color: 'green' }, null>
1331
+ function createTernariesFromObjectProperties(
1332
+ test: t.Expression,
1333
+ side: t.Expression | null,
1334
+ ternaryPartial: Partial<Ternary> = {}
1335
+ ): null | Ternary[] {
1336
+ if (!side) {
1337
+ return null
1338
+ }
1339
+ if (!isStaticObject(side)) {
1340
+ throw new Error('not extractable')
1341
+ }
1342
+ return side.properties.flatMap((property) => {
1343
+ if (!t.isObjectProperty(property)) {
1344
+ throw new Error('expected object property')
1142
1345
  }
1143
- return side.properties.flatMap((property) => {
1144
- if (!t.isObjectProperty(property)) {
1145
- throw new Error('expected object property')
1146
- }
1147
- // handle media queries inside spread/conditional objects
1148
- if (t.isIdentifier(property.key)) {
1149
- const key = property.key.name
1150
- const mediaQueryKey = key.slice(1)
1151
- const isMediaQuery = key[0] === '$' && mediaQueryConfig[mediaQueryKey]
1152
- if (isMediaQuery) {
1153
- if (t.isExpression(property.value)) {
1154
- const ternaries = createTernariesFromObjectProperties(
1155
- t.stringLiteral(mediaQueryKey),
1156
- property.value,
1157
- {
1158
- inlineMediaQuery: mediaQueryKey,
1159
- }
1160
- )
1161
- if (ternaries) {
1162
- return ternaries.map((value) => ({
1163
- ...ternaryPartial,
1164
- ...value,
1165
- // ensure media query test stays on left side (see getMediaQueryTernary)
1166
- test: t.logicalExpression('&&', value.test, test),
1167
- }))
1168
- } else {
1169
- console.log('⚠️ no ternaries?', property)
1346
+ // handle media queries inside spread/conditional objects
1347
+ if (t.isIdentifier(property.key)) {
1348
+ const key = property.key.name
1349
+ const mediaQueryKey = key.slice(1)
1350
+ const isMediaQuery = key[0] === '$' && mediaQueryConfig[mediaQueryKey]
1351
+ if (isMediaQuery) {
1352
+ if (t.isExpression(property.value)) {
1353
+ const ternaries = createTernariesFromObjectProperties(
1354
+ t.stringLiteral(mediaQueryKey),
1355
+ property.value,
1356
+ {
1357
+ inlineMediaQuery: mediaQueryKey,
1170
1358
  }
1359
+ )
1360
+ if (ternaries) {
1361
+ return ternaries.map((value) => ({
1362
+ ...ternaryPartial,
1363
+ ...value,
1364
+ // ensure media query test stays on left side (see getMediaQueryTernary)
1365
+ test: t.logicalExpression('&&', value.test, test),
1366
+ }))
1171
1367
  } else {
1172
- console.log('⚠️ not expression', property)
1368
+ logger.info(['⚠️ no ternaries?', property].join(' '))
1173
1369
  }
1370
+ } else {
1371
+ logger.info(['⚠️ not expression', property].join(' '))
1174
1372
  }
1175
1373
  }
1176
- // this could be a recurse here if we want to get fancy
1177
- if (t.isConditionalExpression(property.value)) {
1178
- // merge up into the parent conditional, split into two
1179
- const [truthy, falsy] = [
1180
- t.objectExpression([t.objectProperty(property.key, property.value.consequent)]),
1181
- t.objectExpression([t.objectProperty(property.key, property.value.alternate)]),
1182
- ].map((x) => attemptEval(x))
1183
- return [
1184
- createTernary({
1185
- remove() {},
1186
- ...ternaryPartial,
1187
- test: t.logicalExpression('&&', test, property.value.test),
1188
- consequent: truthy,
1189
- alternate: null,
1190
- }),
1191
- createTernary({
1192
- ...ternaryPartial,
1193
- test: t.logicalExpression(
1194
- '&&',
1195
- test,
1196
- t.unaryExpression('!', property.value.test)
1197
- ),
1198
- consequent: falsy,
1199
- alternate: null,
1200
- remove() {},
1201
- }),
1202
- ]
1203
- }
1204
- const obj = t.objectExpression([t.objectProperty(property.key, property.value)])
1205
- const consequent = attemptEval(obj)
1206
- return createTernary({
1207
- remove() {},
1208
- ...ternaryPartial,
1209
- test,
1210
- consequent,
1211
- alternate: null,
1212
- })
1213
- })
1214
- }
1215
-
1216
- // now update to new values
1217
- node.attributes = attrs.filter(isAttr).map((x) => x.value)
1218
-
1219
- if (couldntParse || shouldDeopt) {
1220
- if (shouldPrintDebug) {
1221
- console.log(` avoid optimizing:`, { couldntParse, shouldDeopt })
1222
1374
  }
1223
- node.attributes = ogAttributes
1224
- return
1225
- }
1375
+ // this could be a recurse here if we want to get fancy
1376
+ if (t.isConditionalExpression(property.value)) {
1377
+ // merge up into the parent conditional, split into two
1378
+ const [truthy, falsy] = [
1379
+ t.objectExpression([t.objectProperty(property.key, property.value.consequent)]),
1380
+ t.objectExpression([t.objectProperty(property.key, property.value.alternate)]),
1381
+ ].map((x) => attemptEval(x))
1382
+ return [
1383
+ createTernary({
1384
+ remove() {},
1385
+ ...ternaryPartial,
1386
+ test: t.logicalExpression('&&', test, property.value.test),
1387
+ consequent: truthy,
1388
+ alternate: null,
1389
+ }),
1390
+ createTernary({
1391
+ ...ternaryPartial,
1392
+ test: t.logicalExpression(
1393
+ '&&',
1394
+ test,
1395
+ t.unaryExpression('!', property.value.test)
1396
+ ),
1397
+ consequent: falsy,
1398
+ alternate: null,
1399
+ remove() {},
1400
+ }),
1401
+ ]
1402
+ }
1403
+ const obj = t.objectExpression([t.objectProperty(property.key, property.value)])
1404
+ const consequent = attemptEval(obj)
1405
+ return createTernary({
1406
+ remove() {},
1407
+ ...ternaryPartial,
1408
+ test,
1409
+ consequent,
1410
+ alternate: null,
1411
+ })
1412
+ })
1413
+ }
1226
1414
 
1227
- // before deopt, can still optimize
1228
- const parentFn = findTopmostFunction(traversePath)
1229
- if (parentFn) {
1230
- modifiedComponents.add(parentFn)
1415
+ if (couldntParse || shouldDeopt) {
1416
+ if (shouldPrintDebug) {
1417
+ logger.info([` avoid optimizing:`, { couldntParse, shouldDeopt }].join(' '))
1231
1418
  }
1419
+ node.attributes = ogAttributes
1420
+ return
1421
+ }
1232
1422
 
1233
- // combine ternaries
1234
- let ternaries: Ternary[] = []
1235
- attrs = attrs
1236
- .reduce<(ExtractedAttr | ExtractedAttr[])[]>((out, cur) => {
1237
- const next = attrs[attrs.indexOf(cur) + 1]
1238
- if (cur.type === 'ternary') {
1239
- ternaries.push(cur.value)
1240
- }
1241
- if ((!next || next.type !== 'ternary') && ternaries.length) {
1242
- // finish, process
1243
- const normalized = normalizeTernaries(ternaries).map(
1244
- ({ alternate, consequent, ...rest }) => {
1245
- return {
1246
- type: 'ternary' as const,
1247
- value: {
1248
- ...rest,
1249
- alternate: alternate || null,
1250
- consequent: consequent || null,
1251
- },
1252
- }
1253
- }
1254
- )
1255
- try {
1256
- return [...out, ...normalized]
1257
- } finally {
1258
- if (shouldPrintDebug) {
1259
- console.log(
1260
- ` normalizeTernaries (${ternaries.length} => ${normalized.length})`
1261
- )
1423
+ // now update to new values
1424
+ node.attributes = attrs.filter(isAttr).map((x) => x.value)
1425
+
1426
+ // before deopt, can still optimize
1427
+ const parentFn = findTopmostFunction(traversePath)
1428
+ if (parentFn) {
1429
+ modifiedComponents.add(parentFn)
1430
+ }
1431
+
1432
+ // combine ternaries
1433
+ let ternaries: Ternary[] = []
1434
+ attrs = attrs
1435
+ .reduce<(ExtractedAttr | ExtractedAttr[])[]>((out, cur) => {
1436
+ const next = attrs[attrs.indexOf(cur) + 1]
1437
+ if (cur.type === 'ternary') {
1438
+ ternaries.push(cur.value)
1439
+ }
1440
+ if ((!next || next.type !== 'ternary') && ternaries.length) {
1441
+ // finish, process
1442
+ const normalized = normalizeTernaries(ternaries).map(
1443
+ ({ alternate, consequent, ...rest }) => {
1444
+ return {
1445
+ type: 'ternary' as const,
1446
+ value: {
1447
+ ...rest,
1448
+ alternate: alternate || null,
1449
+ consequent: consequent || null,
1450
+ },
1262
1451
  }
1263
- ternaries = []
1264
1452
  }
1453
+ )
1454
+ try {
1455
+ return [...out, ...normalized]
1456
+ } finally {
1457
+ if (shouldPrintDebug) {
1458
+ logger.info(
1459
+ ` normalizeTernaries (${ternaries.length} => ${normalized.length})`
1460
+ )
1461
+ }
1462
+ ternaries = []
1265
1463
  }
1266
- if (cur.type === 'ternary') {
1267
- return out
1268
- }
1269
- out.push(cur)
1464
+ }
1465
+ if (cur.type === 'ternary') {
1270
1466
  return out
1271
- }, [])
1272
- .flat()
1467
+ }
1468
+ out.push(cur)
1469
+ return out
1470
+ }, [])
1471
+ .flat()
1273
1472
 
1274
- // flatten logic!
1275
- // fairly simple check to see if all children are text
1276
- const hasSpread = node.attributes.some((x) => t.isJSXSpreadAttribute(x))
1473
+ // flatten logic!
1474
+ // fairly simple check to see if all children are text
1475
+ const hasSpread = node.attributes.some((x) => t.isJSXSpreadAttribute(x))
1277
1476
 
1278
- const hasOnlyStringChildren =
1279
- !hasSpread &&
1280
- (node.selfClosing ||
1281
- (traversePath.node.children &&
1282
- traversePath.node.children.every((x) => x.type === 'JSXText')))
1477
+ const hasOnlyStringChildren =
1478
+ !hasSpread &&
1479
+ (node.selfClosing ||
1480
+ (traversePath.node.children &&
1481
+ traversePath.node.children.every((x) => x.type === 'JSXText')))
1482
+
1483
+ const themeVal = inlined.get('theme')
1283
1484
 
1284
- const themeVal = inlined.get('theme')
1485
+ // on native we can't flatten when theme prop is set
1486
+ if (target !== 'native') {
1285
1487
  inlined.delete('theme')
1488
+ }
1286
1489
 
1287
- for (const [key] of [...inlined]) {
1288
- if (INLINE_EXTRACTABLE[key] || staticConfig.variants?.[key]) {
1289
- inlined.delete(key)
1290
- }
1490
+ for (const [key] of [...inlined]) {
1491
+ const isStaticObjectVariant = staticConfig.variants?.[key] && variantValues.has(key)
1492
+ if (INLINE_EXTRACTABLE[key] || isStaticObjectVariant) {
1493
+ inlined.delete(key)
1291
1494
  }
1495
+ }
1292
1496
 
1293
- const canFlattenProps = inlined.size === 0
1497
+ const canFlattenProps = inlined.size === 0
1294
1498
 
1295
- let shouldFlatten =
1499
+ let shouldFlatten = Boolean(
1500
+ flatNode &&
1296
1501
  !shouldDeopt &&
1297
1502
  canFlattenProps &&
1298
1503
  !hasSpread &&
1299
1504
  staticConfig.neverFlatten !== true &&
1300
1505
  (staticConfig.neverFlatten === 'jsx' ? hasOnlyStringChildren : true)
1506
+ )
1301
1507
 
1302
- const shouldWrapTheme = shouldFlatten && themeVal
1508
+ const shouldWrapTheme = shouldFlatten && themeVal
1509
+ const usedThemeKeys = new Set<string>()
1303
1510
 
1304
- if (disableExtractVariables) {
1305
- themeAccessListeners.add((key) => {
1306
- shouldFlatten = false
1307
- if (shouldPrintDebug) {
1308
- console.log(' ! accessing theme key, avoid flatten', key)
1309
- }
1310
- })
1311
- }
1511
+ if (disableExtractVariables) {
1512
+ // if it accesses any theme values during evaluation
1513
+ themeAccessListeners.add((key) => {
1514
+ shouldFlatten = false
1515
+ usedThemeKeys.add(key)
1516
+ if (shouldPrintDebug === 'verbose') {
1517
+ logger.info([' ! accessing theme key, avoid flatten', key].join(' '))
1518
+ }
1519
+ })
1520
+ }
1312
1521
 
1313
- if (shouldPrintDebug) {
1522
+ if (shouldPrintDebug) {
1523
+ try {
1314
1524
  // prettier-ignore
1315
- console.log(' - flatten?', objToStr({ hasSpread, shouldDeopt, shouldFlatten, canFlattenProps, shouldWrapTheme, hasOnlyStringChildren }), 'inlined', [...inlined])
1525
+ logger.info([' flatten?', objToStr({ hasSpread, shouldDeopt, shouldFlatten, canFlattenProps, shouldWrapTheme, hasOnlyStringChildren }), 'inlined', [...inlined]].join(' '))
1526
+ } catch {
1527
+ // ok
1316
1528
  }
1529
+ }
1317
1530
 
1318
- // wrap theme around children on flatten
1319
- // TODO move this to bottom and re-check shouldFlatten
1320
- // account for shouldFlatten could change w the above block "if (disableExtractVariables)"
1321
- if (shouldFlatten && shouldWrapTheme) {
1531
+ // wrap theme around children on flatten
1532
+ // TODO move this to bottom and re-check shouldFlatten
1533
+ // account for shouldFlatten could change w the above block "if (disableExtractVariables)"
1534
+ if (shouldFlatten && shouldWrapTheme) {
1535
+ if (!programPath) {
1536
+ console.warn(
1537
+ `No program path found, avoiding importing flattening / importing theme in ${sourcePath}`
1538
+ )
1539
+ } else {
1322
1540
  if (shouldPrintDebug) {
1323
- console.log(' - wrapping theme', themeVal)
1541
+ logger.info([' - wrapping theme', themeVal].join(' '))
1324
1542
  }
1325
1543
 
1326
1544
  // remove theme attribute from flattened node
@@ -1351,578 +1569,539 @@ export function createExtractor() {
1351
1569
  )
1352
1570
  )
1353
1571
  }
1572
+ }
1354
1573
 
1355
- // only if we flatten, ensure the default styles are there
1356
- if (shouldFlatten) {
1357
- const defaultStyleAttrs = Object.keys(staticConfig.defaultProps).flatMap((key) => {
1358
- if (!isValidStyleKey(key, staticConfig)) {
1359
- return []
1360
- }
1361
- const value = staticConfig.defaultProps[key]
1362
- const name = tamaguiConfig.shorthands[key] || key
1363
- if (value === undefined) {
1364
- console.warn(
1365
- `⚠️ Error evaluating default style for component, prop ${key} ${value}`
1366
- )
1367
- shouldDeopt = true
1368
- return
1369
- }
1370
- const attr: ExtractedAttrStyle = {
1371
- type: 'style',
1372
- name,
1373
- value: { [name]: value },
1374
- }
1375
- return attr
1376
- }) as ExtractedAttr[]
1377
-
1378
- if (defaultStyleAttrs.length) {
1379
- attrs = [...defaultStyleAttrs, ...attrs]
1574
+ // only if we flatten, ensure the default styles are there
1575
+ if (shouldFlatten) {
1576
+ const defaultStyleAttrs = Object.keys(staticConfig.defaultProps).flatMap((key) => {
1577
+ if (!isValidStyleKey(key, staticConfig)) {
1578
+ return []
1380
1579
  }
1381
- }
1382
-
1383
- if (shouldDeopt) {
1384
- node.attributes = ogAttributes
1385
- return
1386
- }
1387
-
1388
- // insert overrides - this inserts null props for things that are set in classNames
1389
- // only when not flattening, so the downstream component can skip applying those styles
1390
- const ensureOverridden = {}
1391
- if (!shouldFlatten) {
1392
- for (const cur of attrs) {
1393
- if (cur.type === 'style') {
1394
- // TODO need to loop over initial props not just style props
1395
- for (const key in cur.value) {
1396
- const shouldEnsureOverridden = !!staticConfig.ensureOverriddenProp?.[key]
1397
- const isSetInAttrsAlready = attrs.some(
1398
- (x) =>
1399
- x.type === 'attr' &&
1400
- x.value.type === 'JSXAttribute' &&
1401
- x.value.name.name === key
1402
- )
1403
-
1404
- if (!isSetInAttrsAlready) {
1405
- const isVariant = !!staticConfig.variants?.[cur.name || '']
1406
- if (isVariant || shouldEnsureOverridden) {
1407
- ensureOverridden[key] = true
1408
- }
1409
- }
1410
- }
1411
- }
1580
+ const value = staticConfig.defaultProps[key]
1581
+ const name = tamaguiConfig.shorthands[key] || key
1582
+ if (value === undefined) {
1583
+ logger.warn(`⚠️ Error evaluating default style for component, prop ${key} ${value}`)
1584
+ shouldDeopt = true
1585
+ return
1412
1586
  }
1587
+ const attr: ExtractedAttrStyle = {
1588
+ type: 'style',
1589
+ name,
1590
+ value: { [name]: value },
1591
+ }
1592
+ return attr
1593
+ }) as ExtractedAttr[]
1594
+
1595
+ if (defaultStyleAttrs.length) {
1596
+ attrs = [...defaultStyleAttrs, ...attrs]
1413
1597
  }
1598
+ }
1414
1599
 
1600
+ if (shouldDeopt || !shouldFlatten) {
1415
1601
  if (shouldPrintDebug) {
1416
- console.log(' - attrs (flattened): \n', logLines(attrs.map(attrStr).join(', ')))
1417
- console.log(' - ensureOverriden:', Object.keys(ensureOverridden).join(', '))
1602
+ logger.info(`Deopting`)
1418
1603
  }
1604
+ node.attributes = ogAttributes
1605
+ return
1606
+ }
1419
1607
 
1420
- const state = {
1421
- noClassNames: false,
1422
- focus: false,
1423
- hover: false,
1424
- mounted: true, // TODO match logic in createComponent
1425
- press: false,
1426
- pressIn: false,
1427
- }
1608
+ if (shouldPrintDebug) {
1609
+ logger.info(
1610
+ [' - attrs (flattened): \n', logLines(attrs.map(attrStr).join(', '))].join(' ')
1611
+ )
1612
+ }
1428
1613
 
1429
- // evaluates all static attributes into a simple object
1430
- let foundStaticProps = {}
1431
- for (const key in attrs) {
1432
- const cur = attrs[key]
1433
- if (cur.type === 'style') {
1434
- foundStaticProps = {
1435
- ...foundStaticProps,
1436
- ...expandStyles(cur.value),
1437
- }
1614
+ const state = {
1615
+ noClassNames: false,
1616
+ focus: false,
1617
+ hover: false,
1618
+ unmounted: false, // TODO match logic in createComponent
1619
+ press: false,
1620
+ pressIn: false,
1621
+ }
1622
+
1623
+ // evaluates all static attributes into a simple object
1624
+ let foundStaticProps = {}
1625
+ for (const key in attrs) {
1626
+ const cur = attrs[key]
1627
+ if (cur.type === 'style') {
1628
+ // remove variants because they are processed later, and can lead to invalid values here
1629
+ // see <Spacer flex /> where flex looks like a valid style, but is a variant
1630
+ foundStaticProps = {
1631
+ ...foundStaticProps,
1632
+ ...expandStylesWithoutVariants(cur.value),
1633
+ }
1634
+ continue
1635
+ }
1636
+ if (cur.type === 'attr') {
1637
+ if (t.isJSXSpreadAttribute(cur.value)) {
1438
1638
  continue
1439
1639
  }
1440
- if (cur.type === 'attr') {
1441
- if (t.isJSXSpreadAttribute(cur.value)) {
1442
- continue
1443
- }
1444
- if (!t.isJSXIdentifier(cur.value.name)) {
1445
- continue
1446
- }
1447
- const key = cur.value.name.name
1448
- // undefined = boolean true
1449
- const value = attemptEvalSafe(cur.value.value || t.booleanLiteral(true))
1450
- if (value !== FAILED_EVAL) {
1451
- foundStaticProps = {
1452
- ...foundStaticProps,
1453
- [key]: value,
1454
- }
1640
+ if (!t.isJSXIdentifier(cur.value.name)) {
1641
+ continue
1642
+ }
1643
+ const key = cur.value.name.name
1644
+ // undefined = boolean true
1645
+ const value = attemptEvalSafe(cur.value.value || t.booleanLiteral(true))
1646
+ if (value !== FAILED_EVAL) {
1647
+ foundStaticProps = {
1648
+ ...foundStaticProps,
1649
+ [key]: value,
1455
1650
  }
1456
1651
  }
1457
1652
  }
1653
+ }
1458
1654
 
1459
- // must preserve exact order
1460
- const completeProps = {}
1461
- for (const key in staticConfig.defaultProps) {
1462
- if (!(key in foundStaticProps)) {
1463
- completeProps[key] = staticConfig.defaultProps[key]
1464
- }
1465
- }
1466
- for (const key in foundStaticProps) {
1467
- completeProps[key] = foundStaticProps[key]
1655
+ // must preserve exact order
1656
+ const completeProps = {}
1657
+ for (const key in staticConfig.defaultProps) {
1658
+ if (!(key in foundStaticProps)) {
1659
+ completeProps[key] = staticConfig.defaultProps[key]
1468
1660
  }
1661
+ }
1662
+ for (const key in foundStaticProps) {
1663
+ completeProps[key] = foundStaticProps[key]
1664
+ }
1469
1665
 
1470
- // expand shorthands, de-opt variables
1471
- attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
1472
- if (!cur) return acc
1473
- if (cur.type === 'attr' && !t.isJSXSpreadAttribute(cur.value)) {
1474
- if (shouldFlatten) {
1475
- const name = cur.value.name.name
1476
- if (typeof name === 'string') {
1477
- if (name === 'tag') {
1478
- // remove tag=""
1479
- return acc
1480
- }
1666
+ // expand shorthands, de-opt variables
1667
+ attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
1668
+ if (!cur) return acc
1669
+ if (cur.type === 'attr' && !t.isJSXSpreadAttribute(cur.value)) {
1670
+ if (shouldFlatten) {
1671
+ const name = cur.value.name.name
1672
+ if (typeof name === 'string') {
1673
+ if (name === 'tag') {
1674
+ // remove tag=""
1675
+ return acc
1676
+ }
1481
1677
 
1482
- // if flattening, expand variants
1483
- if (variants[name] && variantValues.has(name)) {
1484
- let out = Object.fromEntries(
1485
- staticConfig.propMapper(
1486
- name,
1487
- variantValues.get(name),
1488
- defaultTheme,
1489
- completeProps,
1490
- { ...state, resolveVariablesAs: 'auto' },
1491
- undefined,
1492
- shouldPrintDebug
1493
- ) || []
1494
- )
1495
- if (out && isTargetingHTML) {
1496
- const cn = out.className
1497
- // translate to DOM-compat
1498
- out = createDOMProps(isTextView ? 'span' : 'div', out)
1499
- // remove rnw className use ours
1500
- out.className = cn
1501
- }
1502
- if (shouldPrintDebug) {
1503
- console.log(' - expanded variant', name, out)
1504
- }
1505
- for (const key in out) {
1506
- const value = out[key]
1507
- if (isValidStyleKey(key, staticConfig)) {
1508
- acc.push({
1509
- type: 'style',
1510
- value: { [key]: value },
1511
- name: key,
1512
- attr: cur.value,
1513
- } as const)
1514
- } else {
1515
- acc.push({
1516
- type: 'attr',
1517
- value: t.jsxAttribute(
1518
- t.jsxIdentifier(key),
1519
- t.jsxExpressionContainer(
1520
- typeof value === 'string'
1521
- ? t.stringLiteral(value)
1522
- : literalToAst(value)
1523
- )
1524
- ),
1525
- })
1526
- }
1678
+ // if flattening, expand variants
1679
+ if (variants[name] && variantValues.has(name)) {
1680
+ let out = Object.fromEntries(
1681
+ staticConfig.propMapper(
1682
+ name,
1683
+ variantValues.get(name),
1684
+ defaultTheme,
1685
+ completeProps,
1686
+ { ...state, resolveVariablesAs: 'auto' },
1687
+ undefined,
1688
+ undefined,
1689
+ shouldPrintDebug
1690
+ ) || []
1691
+ )
1692
+ if (out && isTargetingHTML) {
1693
+ const cn = out.className
1694
+ // translate to DOM-compat
1695
+ out = createDOMProps(isTextView ? 'span' : 'div', out)
1696
+ // remove rnw className use ours
1697
+ out.className = cn
1698
+ }
1699
+ if (shouldPrintDebug) {
1700
+ logger.info([' - expanded variant', name, out].join(' '))
1701
+ }
1702
+ for (const key in out) {
1703
+ const value = out[key]
1704
+ if (isValidStyleKey(key, staticConfig)) {
1705
+ acc.push({
1706
+ type: 'style',
1707
+ value: { [key]: value },
1708
+ name: key,
1709
+ attr: cur.value,
1710
+ } as const)
1711
+ } else {
1712
+ acc.push({
1713
+ type: 'attr',
1714
+ value: t.jsxAttribute(
1715
+ t.jsxIdentifier(key),
1716
+ t.jsxExpressionContainer(
1717
+ typeof value === 'string'
1718
+ ? t.stringLiteral(value)
1719
+ : literalToAst(value)
1720
+ )
1721
+ ),
1722
+ })
1527
1723
  }
1528
1724
  }
1529
1725
  }
1530
1726
  }
1531
1727
  }
1728
+ }
1532
1729
 
1533
- if (cur.type !== 'style') {
1534
- acc.push(cur)
1535
- return acc
1536
- }
1537
-
1538
- let key = Object.keys(cur.value)[0]
1539
- const value = cur.value[key]
1540
- const fullKey = tamaguiConfig.shorthands[key]
1541
- // expand shorthands
1542
- if (fullKey) {
1543
- cur.value = { [fullKey]: value }
1544
- key = fullKey
1545
- }
1546
-
1547
- // finally we have all styles + expansions, lets see if we need to skip
1548
- // any and keep them as attrs
1549
- if (disableExtractVariables) {
1550
- if (value[0] === '$') {
1551
- if (shouldPrintDebug) {
1552
- console.log(` keeping variable inline: ${key} =`, value)
1553
- }
1554
- acc.push({
1555
- type: 'attr',
1556
- value: t.jsxAttribute(
1557
- t.jsxIdentifier(key),
1558
- t.jsxExpressionContainer(t.stringLiteral(value))
1559
- ),
1560
- })
1561
- return acc
1562
- }
1563
- }
1564
-
1730
+ if (cur.type !== 'style') {
1565
1731
  acc.push(cur)
1566
1732
  return acc
1567
- }, [])
1568
-
1569
- tm.mark('jsx-element-expanded', shouldPrintDebug === 'verbose')
1570
- if (shouldPrintDebug) {
1571
- console.log(' - attrs (expanded): \n', logLines(attrs.map(attrStr).join(', ')))
1572
1733
  }
1573
1734
 
1574
- // merge styles, leave undefined values
1575
- let prev: ExtractedAttr | null = null
1735
+ let key = Object.keys(cur.value)[0]
1736
+ const value = cur.value[key]
1737
+ const fullKey = tamaguiConfig.shorthands[key]
1738
+ // expand shorthands
1739
+ if (fullKey) {
1740
+ cur.value = { [fullKey]: value }
1741
+ key = fullKey
1742
+ }
1576
1743
 
1577
- function mergeStyles(prev: ViewStyle & PseudoStyles, nextIn: ViewStyle & PseudoStyles) {
1578
- const next = expandStyles(nextIn)
1579
- for (const key in next) {
1580
- // merge pseudos
1581
- if (pseudoDescriptors[key]) {
1582
- prev[key] = prev[key] || {}
1583
- if (shouldPrintDebug) {
1584
- if (!next[key] || !prev[key]) {
1585
- console.log('warn: missing', key, prev, next)
1586
- }
1587
- }
1588
- Object.assign(prev[key], next[key])
1589
- } else {
1590
- prev[key] = next[key]
1744
+ // finally we have all styles + expansions, lets see if we need to skip
1745
+ // any and keep them as attrs
1746
+ if (disableExtractVariables) {
1747
+ if (value[0] === '$' && (usedThemeKeys.has(key) || usedThemeKeys.has(fullKey))) {
1748
+ if (shouldPrintDebug) {
1749
+ logger.info([` keeping variable inline: ${key} =`, value].join(' '))
1591
1750
  }
1751
+ acc.push({
1752
+ type: 'attr',
1753
+ value: t.jsxAttribute(
1754
+ t.jsxIdentifier(key),
1755
+ t.jsxExpressionContainer(t.stringLiteral(value))
1756
+ ),
1757
+ })
1758
+ return acc
1592
1759
  }
1593
1760
  }
1594
1761
 
1595
- attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
1596
- if (cur.type === 'style') {
1597
- const key = Object.keys(cur.value)[0]
1598
- const value = cur.value[key]
1599
-
1600
- const shouldKeepOriginalAttr =
1601
- // !isStyleAndAttr[key] &&
1602
- !shouldFlatten &&
1603
- // de-opt transform styles so it merges properly if not flattened
1604
- // we handle this later on
1605
- // (stylePropsTransform[key] ||
1606
- // de-opt if non-style
1607
- !validStyles[key] &&
1608
- !pseudoDescriptors[key] &&
1609
- !key.startsWith('data-')
1610
-
1611
- if (shouldKeepOriginalAttr) {
1612
- if (shouldPrintDebug) {
1613
- console.log(' - keeping as non-style', key)
1614
- }
1615
- prev = cur
1616
- acc.push({
1617
- type: 'attr',
1618
- value: t.jsxAttribute(
1619
- t.jsxIdentifier(key),
1620
- t.jsxExpressionContainer(
1621
- typeof value === 'string' ? t.stringLiteral(value) : literalToAst(value)
1622
- )
1623
- ),
1624
- })
1625
- acc.push(cur)
1626
- return acc
1627
- }
1762
+ acc.push(cur)
1763
+ return acc
1764
+ }, [])
1628
1765
 
1629
- if (ensureOverridden[key]) {
1630
- acc.push({
1631
- type: 'attr',
1632
- value:
1633
- cur.attr ||
1634
- t.jsxAttribute(
1635
- t.jsxIdentifier(key),
1636
- t.jsxExpressionContainer(t.nullLiteral())
1637
- ),
1638
- })
1639
- }
1766
+ tm.mark('jsx-element-expanded', !!shouldPrintDebug)
1767
+ if (shouldPrintDebug) {
1768
+ logger.info(
1769
+ [' - attrs (expanded): \n', logLines(attrs.map(attrStr).join(', '))].join(' ')
1770
+ )
1771
+ }
1640
1772
 
1641
- if (prev?.type === 'style') {
1642
- mergeStyles(prev.value, cur.value)
1643
- return acc
1644
- }
1645
- }
1773
+ // merge styles, leave undefined values
1774
+ let prev: ExtractedAttr | null = null
1646
1775
 
1647
- prev = cur
1648
- acc.push(cur)
1649
- return acc
1650
- }, [])
1776
+ function splitVariants(style: any) {
1777
+ const variants = {}
1778
+ const styles = {}
1779
+ for (const key in style) {
1780
+ if (staticConfig.variants?.[key]) {
1781
+ variants[key] = style[key]
1782
+ } else {
1783
+ styles[key] = style[key]
1784
+ }
1785
+ }
1786
+ return {
1787
+ variants,
1788
+ styles,
1789
+ }
1790
+ }
1651
1791
 
1652
- if (shouldPrintDebug) {
1653
- console.log(' - attrs (combined 🔀): \n', logLines(attrs.map(attrStr).join(', ')))
1654
- console.log(' - defaultProps: \n', logLines(objToStr(staticConfig.defaultProps)))
1655
- // prettier-ignore
1656
- console.log(' - foundStaticProps: \n', logLines(objToStr(foundStaticProps)))
1657
- console.log(' - completeProps: \n', logLines(objToStr(completeProps)))
1792
+ function expandStylesWithoutVariants(style: any) {
1793
+ const { variants, styles } = splitVariants(style)
1794
+ return {
1795
+ ...expandStyles(styles),
1796
+ ...variants,
1658
1797
  }
1798
+ }
1659
1799
 
1660
- // post process
1661
- const getStyles = (props: Object | null, debugName = '') => {
1662
- if (!props || !Object.keys(props).length) {
1663
- if (shouldPrintDebug) console.log(' getStyles() no props')
1664
- return {}
1665
- }
1666
- if (excludeProps && !!excludeProps.size) {
1667
- for (const key in props) {
1668
- if (excludeProps.has(key)) {
1669
- if (shouldPrintDebug) console.log(' delete excluded', key)
1670
- delete props[key]
1800
+ function mergeStyles(prev: ViewStyle & PseudoStyles, nextIn: ViewStyle & PseudoStyles) {
1801
+ const next = expandStylesWithoutVariants(nextIn)
1802
+ for (const key in next) {
1803
+ // merge pseudos
1804
+ if (pseudoDescriptors[key]) {
1805
+ prev[key] = prev[key] || {}
1806
+ if (shouldPrintDebug) {
1807
+ if (!next[key] || !prev[key]) {
1808
+ logger.info(['warn: missing', key, prev, next].join(' '))
1671
1809
  }
1672
1810
  }
1811
+ Object.assign(prev[key], next[key])
1812
+ } else {
1813
+ prev[key] = next[key]
1673
1814
  }
1674
- try {
1675
- const out = getSplitStyles(
1676
- props,
1677
- staticConfig,
1678
- defaultTheme,
1679
- {
1680
- ...state,
1681
- fallbackProps: completeProps,
1682
- },
1683
- undefined,
1684
- props['debug']
1685
- )
1815
+ }
1816
+ }
1817
+
1818
+ attrs = attrs.reduce<ExtractedAttr[]>((acc, cur) => {
1819
+ if (cur.type === 'style') {
1820
+ const key = Object.keys(cur.value)[0]
1821
+ const value = cur.value[key]
1686
1822
 
1687
- // console.log('outout', out)
1823
+ const shouldKeepOriginalAttr =
1824
+ // !isStyleAndAttr[key] &&
1825
+ !shouldFlatten &&
1826
+ // de-opt if non-style
1827
+ !validStyles[key] &&
1828
+ !pseudoDescriptors[key] &&
1829
+ !key.startsWith('data-')
1688
1830
 
1689
- const outStyle = {
1690
- ...out.style,
1691
- ...out.pseudos,
1831
+ if (shouldKeepOriginalAttr) {
1832
+ if (shouldPrintDebug) {
1833
+ logger.info([' - keeping as non-style', key].join(' '))
1692
1834
  }
1693
- // omitInvalidStyles(outStyle)
1694
- // if (shouldPrintDebug) {
1695
- // // prettier-ignore
1696
- // console.log(` getStyles ${debugName} (props):\n`, logLines(objToStr(props)))
1697
- // // prettier-ignore
1698
- // console.log(` getStyles ${debugName} (out.viewProps):\n`, logLines(objToStr(out.viewProps)))
1699
- // // prettier-ignore
1700
- // console.log(` getStyles ${debugName} (out.style):\n`, logLines(objToStr(outStyle || {}), true))
1701
- // }
1702
- return outStyle
1703
- } catch (err: any) {
1704
- console.log('error', err.message, err.stack)
1705
- return {}
1835
+ prev = cur
1836
+ acc.push({
1837
+ type: 'attr',
1838
+ value: t.jsxAttribute(
1839
+ t.jsxIdentifier(key),
1840
+ t.jsxExpressionContainer(
1841
+ typeof value === 'string' ? t.stringLiteral(value) : literalToAst(value)
1842
+ )
1843
+ ),
1844
+ })
1845
+ acc.push(cur)
1846
+ return acc
1706
1847
  }
1707
- }
1708
1848
 
1709
- function omitInvalidStyles(style: any) {
1710
- if (staticConfig.validStyles) {
1711
- for (const key in style) {
1712
- if (
1713
- stylePropsTransform[key] ||
1714
- (!staticConfig.validStyles[key] &&
1715
- !pseudoDescriptors[key] &&
1716
- !/(hoverStyle|focusStyle|pressStyle)$/.test(key))
1717
- ) {
1718
- if (shouldPrintDebug) console.log(' delete invalid style', key)
1719
- delete style[key]
1720
- }
1721
- }
1849
+ if (prev?.type === 'style') {
1850
+ mergeStyles(prev.value, cur.value)
1851
+ return acc
1722
1852
  }
1723
1853
  }
1724
1854
 
1725
- // used to ensure we pass the entire prop bundle to getStyles
1726
- const completeStyles = getStyles(completeProps, 'completeStyles')
1727
-
1728
- if (!completeStyles) {
1729
- throw new Error(`Impossible, no styles`)
1730
- }
1855
+ prev = cur
1856
+ acc.push(cur)
1857
+ return acc
1858
+ }, [])
1731
1859
 
1732
- // any extra styles added in postprocess should be added to first group as they wont be overriden
1733
- const addInitialStyleKeys = shouldFlatten
1734
- ? difference(Object.keys(completeStyles), Object.keys(foundStaticProps))
1735
- : []
1860
+ if (shouldPrintDebug) {
1861
+ logger.info(
1862
+ [' - attrs (combined 🔀): \n', logLines(attrs.map(attrStr).join(', '))].join(' ')
1863
+ )
1864
+ logger.info(
1865
+ [' - defaultProps: \n', logLines(objToStr(staticConfig.defaultProps))].join(' ')
1866
+ )
1867
+ // prettier-ignore
1868
+ logger.info([' - foundStaticProps: \n', logLines(objToStr(foundStaticProps))].join(' '))
1869
+ logger.info([' - completeProps: \n', logLines(objToStr(completeProps))].join(' '))
1870
+ }
1736
1871
 
1737
- if (addInitialStyleKeys.length) {
1738
- const toAdd = pick(completeStyles, ...addInitialStyleKeys)
1739
- const firstGroup = attrs.find((x) => x.type === 'style')
1740
- if (shouldPrintDebug) {
1741
- console.log(' toAdd', objToStr(toAdd))
1872
+ // post process
1873
+ const getStyles = (props: Object | null, debugName = '') => {
1874
+ if (!props || !Object.keys(props).length) {
1875
+ if (shouldPrintDebug) logger.info([' getStyles() no props'].join(' '))
1876
+ return {}
1877
+ }
1878
+ if (excludeProps && !!excludeProps.size) {
1879
+ for (const key in props) {
1880
+ if (excludeProps.has(key)) {
1881
+ if (shouldPrintDebug) logger.info([' delete excluded', key].join(' '))
1882
+ delete props[key]
1883
+ }
1742
1884
  }
1743
- if (!firstGroup) {
1744
- attrs.unshift({ type: 'style', value: toAdd })
1745
- } else {
1746
- // because were adding fully processed, remove any unprocessed from first group
1747
- omitInvalidStyles(firstGroup.value)
1748
- Object.assign(firstGroup.value, toAdd)
1885
+ }
1886
+ try {
1887
+ const out = getSplitStyles(
1888
+ props,
1889
+ staticConfig,
1890
+ defaultTheme,
1891
+ {
1892
+ ...state,
1893
+ fallbackProps: completeProps,
1894
+ },
1895
+ undefined,
1896
+ undefined,
1897
+ undefined,
1898
+ debugPropValue
1899
+ )
1900
+
1901
+ const outStyle = {
1902
+ ...out.style,
1903
+ ...out.pseudos,
1749
1904
  }
1905
+ // omitInvalidStyles(outStyle)
1906
+ // if (shouldPrintDebug) {
1907
+ // // prettier-ignore
1908
+ // logger.info(` getStyles ${debugName} (props):\n`, logLines(objToStr(props)))
1909
+ // // prettier-ignore
1910
+ // logger.info(` getStyles ${debugName} (out.viewProps):\n`, logLines(objToStr(out.viewProps)))
1911
+ // // prettier-ignore
1912
+ // logger.info(` getStyles ${debugName} (out.style):\n`, logLines(objToStr(outStyle || {}), true))
1913
+ // }
1914
+ return outStyle
1915
+ } catch (err: any) {
1916
+ logger.info(['error', err.message, err.stack].join(' '))
1917
+ return {}
1750
1918
  }
1919
+ }
1920
+
1921
+ // used to ensure we pass the entire prop bundle to getStyles
1922
+ const completeStyles = getStyles(completeProps, 'completeStyles')
1923
+
1924
+ if (!completeStyles) {
1925
+ throw new Error(`Impossible, no styles`)
1926
+ }
1751
1927
 
1928
+ const isNativeNotFlat = !shouldFlatten && target === 'native'
1929
+ if (isNativeNotFlat) {
1752
1930
  if (shouldPrintDebug) {
1753
- // prettier-ignore
1754
- if (shouldFlatten) console.log(' -- addInitialStyleKeys', addInitialStyleKeys.join(', '))
1755
- // prettier-ignore
1756
- // console.log(' -- completeStyles:\n', logLines(objToStr(completeStyles)))
1931
+ logger.info(`Disabled flattening except for simple cases on native for now`)
1757
1932
  }
1933
+ node.attributes = ogAttributes
1934
+ return null
1935
+ }
1758
1936
 
1759
- let getStyleError: any = null
1760
-
1761
- // fix up ternaries, combine final style values
1762
- for (const attr of attrs) {
1763
- try {
1764
- switch (attr.type) {
1765
- case 'ternary':
1766
- const a = getStyles(attr.value.alternate, 'ternary.alternate')
1767
- const c = getStyles(attr.value.consequent, 'ternary.consequent')
1768
- if (a) attr.value.alternate = a
1769
- if (c) attr.value.consequent = c
1770
- if (shouldPrintDebug) console.log(' => tern ', attrStr(attr))
1771
- continue
1772
- case 'style':
1773
- // expand variants and such
1774
- const styles = getStyles(attr.value, 'style')
1775
- if (styles) {
1776
- attr.value = styles
1777
- }
1778
- // prettier-ignore
1779
- if (shouldPrintDebug) console.log(' * styles (in)', logLines(objToStr(attr.value)))
1780
- // prettier-ignore
1781
- if (shouldPrintDebug) console.log(' * styles (out)', logLines(objToStr(styles)))
1782
- continue
1937
+ let getStyleError: any = null
1938
+
1939
+ // fix up ternaries, combine final style values
1940
+ for (const attr of attrs) {
1941
+ try {
1942
+ switch (attr.type) {
1943
+ case 'ternary': {
1944
+ const a = getStyles(attr.value.alternate, 'ternary.alternate')
1945
+ const c = getStyles(attr.value.consequent, 'ternary.consequent')
1946
+ if (a) attr.value.alternate = a
1947
+ if (c) attr.value.consequent = c
1948
+ if (shouldPrintDebug) logger.info([' => tern ', attrStr(attr)].join(' '))
1949
+ continue
1950
+ }
1951
+ case 'style': {
1952
+ // expand variants and such
1953
+ const styles = getStyles(attr.value, 'style')
1954
+ if (styles) {
1955
+ attr.value = styles
1956
+ }
1957
+ // prettier-ignore
1958
+ if (shouldPrintDebug) logger.info([' * styles (in)', logLines(objToStr(attr.value))].join(' '))
1959
+ // prettier-ignore
1960
+ if (shouldPrintDebug) logger.info([' * styles (out)', logLines(objToStr(styles))].join(' '))
1961
+ continue
1783
1962
  }
1784
- } catch (err) {
1785
- // any error de-opt
1786
- getStyleError = err
1787
1963
  }
1964
+ } catch (err) {
1965
+ // any error de-opt
1966
+ getStyleError = err
1788
1967
  }
1968
+ }
1789
1969
 
1790
- if (shouldPrintDebug) {
1791
- // prettier-ignore
1792
- console.log(' - attrs (ternaries/combined):\n', logLines(attrs.map(attrStr).join(', ')))
1793
- }
1794
-
1795
- tm.mark('jsx-element-styles', shouldPrintDebug === 'verbose')
1970
+ if (shouldPrintDebug) {
1971
+ // prettier-ignore
1972
+ logger.info([' - attrs (ternaries/combined):\n', logLines(attrs.map(attrStr).join(', '))].join(' '))
1973
+ }
1796
1974
 
1797
- if (getStyleError) {
1798
- console.log(' ⚠️ postprocessing error, deopt', getStyleError)
1799
- node.attributes = ogAttributes
1800
- return node
1801
- }
1975
+ tm.mark('jsx-element-styles', !!shouldPrintDebug)
1802
1976
 
1803
- // final lazy extra loop:
1804
- const existingStyleKeys = new Set()
1805
- for (let i = attrs.length - 1; i >= 0; i--) {
1806
- const attr = attrs[i]
1977
+ if (getStyleError) {
1978
+ logger.info([' ⚠️ postprocessing error, deopt', getStyleError].join(' '))
1979
+ node.attributes = ogAttributes
1980
+ return null
1981
+ }
1807
1982
 
1808
- // if flattening map inline props to proper flattened names
1809
- if (shouldFlatten) {
1810
- if (attr.type === 'attr') {
1811
- if (t.isJSXAttribute(attr.value)) {
1812
- if (t.isJSXIdentifier(attr.value.name)) {
1813
- const name = attr.value.name.name
1814
- if (INLINE_EXTRACTABLE[name]) {
1815
- // map to HTML only name
1816
- attr.value.name.name = INLINE_EXTRACTABLE[name]
1817
- }
1983
+ // final lazy extra loop:
1984
+ const existingStyleKeys = new Set()
1985
+ for (let i = attrs.length - 1; i >= 0; i--) {
1986
+ const attr = attrs[i]
1818
1987
 
1819
- // if flattening expand turn variants into styles
1820
- if (staticConfig.variants?.[name]) {
1821
- const expanded = getStyles({})
1822
- // attrs[i] = {
1823
- // type: 'style',
1824
- // value:
1825
- // }
1826
- }
1988
+ // if flattening map inline props to proper flattened names
1989
+ if (shouldFlatten) {
1990
+ if (attr.type === 'attr') {
1991
+ if (t.isJSXAttribute(attr.value)) {
1992
+ if (t.isJSXIdentifier(attr.value.name)) {
1993
+ const name = attr.value.name.name
1994
+ if (INLINE_EXTRACTABLE[name]) {
1995
+ // map to HTML only name
1996
+ attr.value.name.name = INLINE_EXTRACTABLE[name]
1827
1997
  }
1828
1998
  }
1829
1999
  }
1830
2000
  }
2001
+ }
1831
2002
 
1832
- // remove duplicate styles
1833
- // so if you have:
1834
- // style({ color: 'red' }), ...someProps, style({ color: 'green' })
1835
- // this will mutate:
1836
- // style({}), ...someProps, style({ color: 'green' })
1837
- if (attr.type === 'style') {
1838
- for (const key in attr.value) {
1839
- if (existingStyleKeys.has(key)) {
1840
- if (shouldPrintDebug) {
1841
- console.log(' >> delete existing', key)
1842
- }
1843
- delete attr.value[key]
1844
- } else {
1845
- existingStyleKeys.add(key)
2003
+ // remove duplicate styles
2004
+ // so if you have:
2005
+ // style({ color: 'red' }), ...someProps, style({ color: 'green' })
2006
+ // this will mutate:
2007
+ // style({}), ...someProps, style({ color: 'green' })
2008
+ if (attr.type === 'style') {
2009
+ for (const key in attr.value) {
2010
+ if (existingStyleKeys.has(key)) {
2011
+ if (shouldPrintDebug) {
2012
+ logger.info([` >> delete existing ${key}`].join(' '))
1846
2013
  }
2014
+ delete attr.value[key]
2015
+ } else {
2016
+ existingStyleKeys.add(key)
1847
2017
  }
1848
2018
  }
1849
2019
  }
2020
+ }
1850
2021
 
1851
- // inlineWhenUnflattened
1852
- if (!shouldFlatten) {
1853
- if (Object.keys(inlineWhenUnflattenedOGVals).length) {
1854
- for (const [index, attr] of attrs.entries()) {
1855
- if (attr.type === 'style') {
1856
- for (const key in attr.value) {
1857
- const val = inlineWhenUnflattenedOGVals[key]
1858
- if (val) {
1859
- // delete the style
1860
- delete attr.value[key]
1861
-
1862
- // and insert it before
1863
- attrs.splice(index - 1, 0, val.attr)
1864
- }
2022
+ // inlineWhenUnflattened
2023
+ if (!shouldFlatten) {
2024
+ if (inlineWhenUnflattened.size) {
2025
+ for (const [index, attr] of attrs.entries()) {
2026
+ if (attr.type === 'style') {
2027
+ for (const key in attr.value) {
2028
+ if (!inlineWhenUnflattened.has(key)) continue
2029
+ const val = inlineWhenUnflattenedOGVals[key]
2030
+ if (val) {
2031
+ // delete the style
2032
+ delete attr.value[key]
2033
+
2034
+ // and insert it before
2035
+ attrs.splice(index - 1, 0, val.attr)
2036
+ } else {
2037
+ // just delete it, it was added during expansion but should be left inline
2038
+ delete attr.value[key]
1865
2039
  }
1866
2040
  }
1867
2041
  }
1868
2042
  }
1869
2043
  }
2044
+ }
1870
2045
 
1871
- if (shouldFlatten) {
1872
- // DO FLATTEN
1873
- if (shouldPrintDebug) {
1874
- console.log(' [✅] flattening', originalNodeName, flatNode)
1875
- }
1876
- node.name.name = flatNode
1877
- res.flattened++
1878
- if (closingElement) {
1879
- closingElement.name.name = flatNode
1880
- }
1881
- }
1882
-
2046
+ if (shouldFlatten) {
2047
+ // DO FLATTEN
1883
2048
  if (shouldPrintDebug) {
1884
- // prettier-ignore
1885
- console.log(` ❊❊ inline props (${inlined.size}):`, shouldDeopt ? ' deopted' : '', hasSpread ? ' has spread' : '', staticConfig.neverFlatten ? 'neverFlatten' : '')
1886
- console.log(' - attrs (end):\n', logLines(attrs.map(attrStr).join(', ')))
2049
+ logger.info([' [✅] flattening', originalNodeName, flatNode].join(' '))
1887
2050
  }
1888
-
1889
- onExtractTag({
1890
- attrs,
1891
- node,
1892
- lineNumbers,
1893
- filePath,
1894
- attemptEval,
1895
- jsxPath: traversePath,
1896
- originalNodeName,
1897
- isFlattened: shouldFlatten,
1898
- programPath,
1899
- })
1900
- } finally {
1901
- if (debugPropValue) {
1902
- shouldPrintDebug = ogDebug
2051
+ // @ts-ignore
2052
+ node.name.name = flatNode
2053
+ res.flattened++
2054
+ if (closingElement) {
2055
+ // @ts-ignore
2056
+ closingElement.name.name = flatNode
1903
2057
  }
1904
2058
  }
1905
- },
1906
- })
1907
2059
 
1908
- tm.mark('jsx-done', shouldPrintDebug === 'verbose')
2060
+ if (shouldPrintDebug) {
2061
+ // prettier-ignore
2062
+ logger.info([` ❊❊ inline props (${inlined.size}):`, shouldDeopt ? ' deopted' : '', hasSpread ? ' has spread' : '', staticConfig.neverFlatten ? 'neverFlatten' : ''].join(' '))
2063
+ logger.info(` - attrs (end):\n ${logLines(attrs.map(attrStr).join(', '))}`)
2064
+ }
1909
2065
 
1910
- /**
1911
- * Step 3: Remove dead code from removed media query / theme hooks
1912
- */
1913
- if (modifiedComponents.size) {
1914
- const all = Array.from(modifiedComponents)
1915
- if (shouldPrintDebug) {
1916
- console.log(' [🪝] hook check', all.length)
1917
- }
1918
- for (const comp of all) {
1919
- removeUnusedHooks(comp, shouldPrintDebug)
2066
+ onExtractTag({
2067
+ parserProps: propsWithFileInfo,
2068
+ attrs,
2069
+ node,
2070
+ lineNumbers,
2071
+ filePath,
2072
+ attemptEval,
2073
+ jsxPath: traversePath,
2074
+ originalNodeName,
2075
+ isFlattened: shouldFlatten,
2076
+ programPath: programPath!,
2077
+ completeProps,
2078
+ staticConfig,
2079
+ })
2080
+ } finally {
2081
+ if (debugPropValue) {
2082
+ shouldPrintDebug = ogDebug
2083
+ }
1920
2084
  }
2085
+ },
2086
+ })
2087
+
2088
+ tm.mark('jsx-done', !!shouldPrintDebug)
2089
+
2090
+ /**
2091
+ * Step 3: Remove dead code from removed media query / theme hooks
2092
+ */
2093
+ if (modifiedComponents.size) {
2094
+ const all = Array.from(modifiedComponents)
2095
+ if (shouldPrintDebug) {
2096
+ logger.info(` [🪝] hook check ${all.length}`)
1921
2097
  }
2098
+ for (const comp of all) {
2099
+ removeUnusedHooks(comp, shouldPrintDebug)
2100
+ }
2101
+ }
1922
2102
 
1923
- tm.done(shouldPrintDebug === 'verbose')
2103
+ tm.done(shouldPrintDebug === 'verbose')
1924
2104
 
1925
- return res
1926
- },
2105
+ return res
1927
2106
  }
1928
2107
  }