@tamagui/static 1.0.0-beta.8 → 1.0.1-beta.100

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 (244) hide show
  1. package/dist/cjs/extractor/accessSafe.js +0 -2
  2. package/dist/cjs/extractor/accessSafe.js.map +1 -1
  3. package/dist/cjs/extractor/babelParse.js +15 -16
  4. package/dist/cjs/extractor/babelParse.js.map +2 -2
  5. package/dist/cjs/extractor/buildClassName.js +0 -2
  6. package/dist/cjs/extractor/buildClassName.js.map +1 -1
  7. package/dist/cjs/extractor/createEvaluator.js +9 -7
  8. package/dist/cjs/extractor/createEvaluator.js.map +2 -2
  9. package/dist/cjs/extractor/createExtractor.js +318 -145
  10. package/dist/cjs/extractor/createExtractor.js.map +2 -2
  11. package/dist/cjs/extractor/ensureImportingConcat.js +0 -2
  12. package/dist/cjs/extractor/ensureImportingConcat.js.map +1 -1
  13. package/dist/cjs/extractor/evaluateAstNode.js +0 -2
  14. package/dist/cjs/extractor/evaluateAstNode.js.map +1 -1
  15. package/dist/cjs/extractor/extractHelpers.js +11 -16
  16. package/dist/cjs/extractor/extractHelpers.js.map +1 -1
  17. package/dist/cjs/extractor/extractMediaStyle.js +3 -8
  18. package/dist/cjs/extractor/extractMediaStyle.js.map +2 -2
  19. package/dist/cjs/extractor/extractToClassNames.js +27 -42
  20. package/dist/cjs/extractor/extractToClassNames.js.map +2 -2
  21. package/dist/cjs/extractor/findTopmostFunction.js +1 -3
  22. package/dist/cjs/extractor/findTopmostFunction.js.map +1 -1
  23. package/dist/cjs/extractor/generatedUid.js +0 -2
  24. package/dist/cjs/extractor/generatedUid.js.map +1 -1
  25. package/dist/cjs/extractor/getPrefixLogs.js +0 -2
  26. package/dist/cjs/extractor/getPrefixLogs.js.map +1 -1
  27. package/dist/cjs/extractor/getPropValueFromAttributes.js +0 -2
  28. package/dist/cjs/extractor/getPropValueFromAttributes.js.map +1 -1
  29. package/dist/cjs/extractor/getSourceModule.js +0 -2
  30. package/dist/cjs/extractor/getSourceModule.js.map +1 -1
  31. package/dist/cjs/extractor/getStaticBindingsForScope.js +1 -5
  32. package/dist/cjs/extractor/getStaticBindingsForScope.js.map +1 -1
  33. package/dist/cjs/extractor/hoistClassNames.js +0 -3
  34. package/dist/cjs/extractor/hoistClassNames.js.map +1 -1
  35. package/dist/cjs/extractor/literalToAst.js +0 -4
  36. package/dist/cjs/extractor/literalToAst.js.map +1 -1
  37. package/dist/cjs/extractor/loadTamagui.js +43 -10
  38. package/dist/cjs/extractor/loadTamagui.js.map +2 -2
  39. package/dist/cjs/extractor/logLines.js +2 -3
  40. package/dist/cjs/extractor/logLines.js.map +1 -1
  41. package/dist/cjs/extractor/normalizeTernaries.js +0 -2
  42. package/dist/cjs/extractor/normalizeTernaries.js.map +1 -1
  43. package/dist/cjs/extractor/removeUnusedHooks.js +2 -4
  44. package/dist/cjs/extractor/removeUnusedHooks.js.map +1 -1
  45. package/dist/cjs/extractor/timer.js +2 -3
  46. package/dist/cjs/extractor/timer.js.map +1 -1
  47. package/dist/cjs/extractor/validHTMLAttributes.js +51 -0
  48. package/dist/cjs/extractor/validHTMLAttributes.js.map +2 -2
  49. package/dist/cjs/index.js +2 -1
  50. package/dist/cjs/index.js.map +2 -2
  51. package/dist/cjs/types.js.map +1 -1
  52. package/dist/esm/extractor/accessSafe.js +0 -3
  53. package/dist/esm/extractor/accessSafe.js.map +1 -1
  54. package/dist/esm/extractor/babelParse.js +15 -17
  55. package/dist/esm/extractor/babelParse.js.map +2 -2
  56. package/dist/esm/extractor/buildClassName.js +0 -3
  57. package/dist/esm/extractor/buildClassName.js.map +1 -1
  58. package/dist/esm/extractor/createEvaluator.js +9 -8
  59. package/dist/esm/extractor/createEvaluator.js.map +2 -2
  60. package/dist/esm/extractor/createExtractor.js +405 -195
  61. package/dist/esm/extractor/createExtractor.js.map +2 -2
  62. package/dist/esm/extractor/ensureImportingConcat.js +0 -3
  63. package/dist/esm/extractor/ensureImportingConcat.js.map +1 -1
  64. package/dist/esm/extractor/evaluateAstNode.js +0 -3
  65. package/dist/esm/extractor/evaluateAstNode.js.map +1 -1
  66. package/dist/esm/extractor/extractHelpers.js +11 -17
  67. package/dist/esm/extractor/extractHelpers.js.map +1 -1
  68. package/dist/esm/extractor/extractMediaStyle.js +29 -20
  69. package/dist/esm/extractor/extractMediaStyle.js.map +2 -2
  70. package/dist/esm/extractor/extractToClassNames.js +53 -52
  71. package/dist/esm/extractor/extractToClassNames.js.map +2 -2
  72. package/dist/esm/extractor/findTopmostFunction.js +1 -4
  73. package/dist/esm/extractor/findTopmostFunction.js.map +1 -1
  74. package/dist/esm/extractor/generatedUid.js +0 -3
  75. package/dist/esm/extractor/generatedUid.js.map +1 -1
  76. package/dist/esm/extractor/getPrefixLogs.js +1 -5
  77. package/dist/esm/extractor/getPrefixLogs.js.map +1 -1
  78. package/dist/esm/extractor/getPropValueFromAttributes.js +0 -3
  79. package/dist/esm/extractor/getPropValueFromAttributes.js.map +1 -1
  80. package/dist/esm/extractor/getSourceModule.js +0 -3
  81. package/dist/esm/extractor/getSourceModule.js.map +1 -1
  82. package/dist/esm/extractor/getStaticBindingsForScope.js +1 -6
  83. package/dist/esm/extractor/getStaticBindingsForScope.js.map +1 -1
  84. package/dist/esm/extractor/hoistClassNames.js +0 -4
  85. package/dist/esm/extractor/hoistClassNames.js.map +1 -1
  86. package/dist/esm/extractor/literalToAst.js +21 -11
  87. package/dist/esm/extractor/literalToAst.js.map +1 -1
  88. package/dist/esm/extractor/loadTamagui.js +44 -11
  89. package/dist/esm/extractor/loadTamagui.js.map +2 -2
  90. package/dist/esm/extractor/logLines.js +2 -4
  91. package/dist/esm/extractor/logLines.js.map +1 -1
  92. package/dist/esm/extractor/normalizeTernaries.js +35 -9
  93. package/dist/esm/extractor/normalizeTernaries.js.map +1 -1
  94. package/dist/esm/extractor/removeUnusedHooks.js +5 -9
  95. package/dist/esm/extractor/removeUnusedHooks.js.map +1 -1
  96. package/dist/esm/extractor/timer.js +2 -4
  97. package/dist/esm/extractor/timer.js.map +1 -1
  98. package/dist/esm/extractor/validHTMLAttributes.js +50 -0
  99. package/dist/esm/extractor/validHTMLAttributes.js.map +2 -2
  100. package/dist/esm/index.js +2 -1
  101. package/dist/esm/index.js.map +2 -2
  102. package/dist/jsx/constants.js +1 -0
  103. package/dist/jsx/constants.js.map +7 -0
  104. package/dist/jsx/extractor/accessSafe.js +1 -3
  105. package/dist/jsx/extractor/accessSafe.js.map +7 -0
  106. package/dist/jsx/extractor/babelParse.js +16 -17
  107. package/dist/jsx/extractor/babelParse.js.map +7 -0
  108. package/dist/jsx/extractor/buildClassName.js +1 -3
  109. package/dist/jsx/extractor/buildClassName.js.map +7 -0
  110. package/dist/jsx/extractor/createEvaluator.js +10 -8
  111. package/dist/jsx/extractor/createEvaluator.js.map +7 -0
  112. package/dist/jsx/extractor/createExtractor.js +325 -150
  113. package/dist/jsx/extractor/createExtractor.js.map +7 -0
  114. package/dist/jsx/extractor/ensureImportingConcat.js +1 -3
  115. package/dist/jsx/extractor/ensureImportingConcat.js.map +7 -0
  116. package/dist/jsx/extractor/evaluateAstNode.js +1 -3
  117. package/dist/jsx/extractor/evaluateAstNode.js.map +7 -0
  118. package/dist/jsx/extractor/extractHelpers.js +12 -17
  119. package/dist/jsx/extractor/extractHelpers.js.map +7 -0
  120. package/dist/jsx/extractor/extractMediaStyle.js +4 -9
  121. package/dist/jsx/extractor/extractMediaStyle.js.map +7 -0
  122. package/dist/jsx/extractor/extractToClassNames.js +31 -43
  123. package/dist/jsx/extractor/extractToClassNames.js.map +7 -0
  124. package/dist/jsx/extractor/findTopmostFunction.js +2 -4
  125. package/dist/jsx/extractor/findTopmostFunction.js.map +7 -0
  126. package/dist/jsx/extractor/generatedUid.js +1 -3
  127. package/dist/jsx/extractor/generatedUid.js.map +7 -0
  128. package/dist/jsx/extractor/getPrefixLogs.js +1 -3
  129. package/dist/jsx/extractor/getPrefixLogs.js.map +7 -0
  130. package/dist/jsx/extractor/getPropValueFromAttributes.js +1 -3
  131. package/dist/jsx/extractor/getPropValueFromAttributes.js.map +7 -0
  132. package/dist/jsx/extractor/getSourceModule.js +1 -3
  133. package/dist/jsx/extractor/getSourceModule.js.map +7 -0
  134. package/dist/jsx/extractor/getStaticBindingsForScope.js +2 -6
  135. package/dist/jsx/extractor/getStaticBindingsForScope.js.map +7 -0
  136. package/dist/jsx/extractor/hoistClassNames.js +1 -4
  137. package/dist/jsx/extractor/hoistClassNames.js.map +7 -0
  138. package/dist/jsx/extractor/literalToAst.js +1 -5
  139. package/dist/jsx/extractor/literalToAst.js.map +7 -0
  140. package/dist/jsx/extractor/loadTamagui.js +30 -11
  141. package/dist/jsx/extractor/loadTamagui.js.map +7 -0
  142. package/dist/jsx/extractor/logLines.js +3 -4
  143. package/dist/jsx/extractor/logLines.js.map +7 -0
  144. package/dist/jsx/extractor/normalizeTernaries.js +1 -3
  145. package/dist/jsx/extractor/normalizeTernaries.js.map +7 -0
  146. package/dist/jsx/extractor/removeUnusedHooks.js +3 -5
  147. package/dist/jsx/extractor/removeUnusedHooks.js.map +7 -0
  148. package/dist/jsx/extractor/timer.js +3 -4
  149. package/dist/jsx/extractor/timer.js.map +7 -0
  150. package/dist/jsx/extractor/validHTMLAttributes.js +51 -0
  151. package/dist/jsx/extractor/validHTMLAttributes.js.map +7 -0
  152. package/dist/jsx/index.js +3 -1
  153. package/dist/jsx/index.js.map +7 -0
  154. package/dist/jsx/types.js +1 -0
  155. package/dist/jsx/types.js.map +7 -0
  156. package/package.json +27 -19
  157. package/src/constants.ts +13 -0
  158. package/src/extractor/accessSafe.ts +18 -0
  159. package/src/extractor/babelParse.ts +27 -0
  160. package/src/extractor/buildClassName.ts +61 -0
  161. package/src/extractor/createEvaluator.ts +75 -0
  162. package/src/extractor/createExtractor.ts +1928 -0
  163. package/src/extractor/ensureImportingConcat.ts +39 -0
  164. package/src/extractor/evaluateAstNode.ts +121 -0
  165. package/src/extractor/extractHelpers.ts +119 -0
  166. package/src/extractor/extractMediaStyle.ts +192 -0
  167. package/src/extractor/extractToClassNames.ts +393 -0
  168. package/src/extractor/findTopmostFunction.ts +22 -0
  169. package/src/extractor/generatedUid.ts +43 -0
  170. package/src/extractor/getPrefixLogs.ts +6 -0
  171. package/src/extractor/getPropValueFromAttributes.ts +92 -0
  172. package/src/extractor/getSourceModule.ts +101 -0
  173. package/src/extractor/getStaticBindingsForScope.ts +183 -0
  174. package/src/extractor/hoistClassNames.ts +45 -0
  175. package/src/extractor/literalToAst.ts +84 -0
  176. package/src/extractor/loadTamagui.ts +145 -0
  177. package/src/extractor/logLines.ts +16 -0
  178. package/src/extractor/normalizeTernaries.ts +63 -0
  179. package/src/extractor/removeUnusedHooks.ts +76 -0
  180. package/src/extractor/timer.ts +18 -0
  181. package/src/extractor/validHTMLAttributes.ts +99 -0
  182. package/src/index.ts +10 -0
  183. package/src/types.ts +96 -0
  184. package/types/extractor/buildClassName.d.ts +0 -0
  185. package/types/extractor/buildClassName.d.ts.map +0 -0
  186. package/types/extractor/createEvaluator.d.ts +1 -1
  187. package/types/extractor/createEvaluator.d.ts.map +1 -1
  188. package/types/extractor/createExtractor.d.ts +3 -4
  189. package/types/extractor/createExtractor.d.ts.map +1 -1
  190. package/types/extractor/extractHelpers.d.ts +1 -1
  191. package/types/extractor/extractHelpers.d.ts.map +1 -1
  192. package/types/extractor/extractMediaStyle.d.ts +0 -0
  193. package/types/extractor/extractMediaStyle.d.ts.map +1 -1
  194. package/types/extractor/extractToClassNames.d.ts +1 -6
  195. package/types/extractor/extractToClassNames.d.ts.map +1 -1
  196. package/types/extractor/getPrefixLogs.d.ts +0 -0
  197. package/types/extractor/getPrefixLogs.d.ts.map +0 -0
  198. package/types/extractor/loadTamagui.d.ts +10 -4
  199. package/types/extractor/loadTamagui.d.ts.map +1 -1
  200. package/types/extractor/normalizeTernaries.d.ts +0 -0
  201. package/types/extractor/normalizeTernaries.d.ts.map +0 -0
  202. package/types/index.d.ts +2 -1
  203. package/types/index.d.ts.map +1 -1
  204. package/types/types.d.ts +6 -7
  205. package/types/types.d.ts.map +1 -1
  206. package/dist/cjs/patchReactNativeWeb.js +0 -167
  207. package/dist/cjs/patchReactNativeWeb.js.map +0 -7
  208. package/dist/esm/patchReactNativeWeb.js +0 -142
  209. package/dist/esm/patchReactNativeWeb.js.map +0 -7
  210. package/dist/jsx/patchReactNativeWeb.js +0 -141
  211. package/types/constants.d.ts +0 -6
  212. package/types/constants.d.ts.map +0 -1
  213. package/types/extractor/accessSafe.d.ts +0 -3
  214. package/types/extractor/accessSafe.d.ts.map +0 -1
  215. package/types/extractor/babelParse.d.ts +0 -5
  216. package/types/extractor/babelParse.d.ts.map +0 -1
  217. package/types/extractor/ensureImportingConcat.d.ts +0 -4
  218. package/types/extractor/ensureImportingConcat.d.ts.map +0 -1
  219. package/types/extractor/evaluateAstNode.d.ts +0 -3
  220. package/types/extractor/evaluateAstNode.d.ts.map +0 -1
  221. package/types/extractor/findTopmostFunction.d.ts +0 -4
  222. package/types/extractor/findTopmostFunction.d.ts.map +0 -1
  223. package/types/extractor/generatedUid.d.ts +0 -5
  224. package/types/extractor/generatedUid.d.ts.map +0 -1
  225. package/types/extractor/getPropValueFromAttributes.d.ts +0 -3
  226. package/types/extractor/getPropValueFromAttributes.d.ts.map +0 -1
  227. package/types/extractor/getSourceModule.d.ts +0 -16
  228. package/types/extractor/getSourceModule.d.ts.map +0 -1
  229. package/types/extractor/getStaticBindingsForScope.d.ts +0 -4
  230. package/types/extractor/getStaticBindingsForScope.d.ts.map +0 -1
  231. package/types/extractor/hoistClassNames.d.ts +0 -6
  232. package/types/extractor/hoistClassNames.d.ts.map +0 -1
  233. package/types/extractor/literalToAst.d.ts +0 -4
  234. package/types/extractor/literalToAst.d.ts.map +0 -1
  235. package/types/extractor/logLines.d.ts +0 -2
  236. package/types/extractor/logLines.d.ts.map +0 -1
  237. package/types/extractor/removeUnusedHooks.d.ts +0 -3
  238. package/types/extractor/removeUnusedHooks.d.ts.map +0 -1
  239. package/types/extractor/timer.d.ts +0 -5
  240. package/types/extractor/timer.d.ts.map +0 -1
  241. package/types/extractor/validHTMLAttributes.d.ts +0 -49
  242. package/types/extractor/validHTMLAttributes.d.ts.map +0 -1
  243. package/types/patchReactNativeWeb.d.ts +0 -2
  244. package/types/patchReactNativeWeb.d.ts.map +0 -1
@@ -0,0 +1,1928 @@
1
+ import { relative } from 'path'
2
+
3
+ import traverse, { NodePath, Visitor } from '@babel/traverse'
4
+ import * as t from '@babel/types'
5
+ import {
6
+ PseudoStyles,
7
+ StaticConfigParsed,
8
+ TamaguiInternalConfig,
9
+ createDOMProps,
10
+ expandStyles,
11
+ getSplitStyles,
12
+ getStylesAtomic,
13
+ mediaQueryConfig,
14
+ proxyThemeVariables,
15
+ pseudoDescriptors,
16
+ stylePropsTransform,
17
+ } from '@tamagui/core-node'
18
+ import { difference, pick } from 'lodash'
19
+ import type { ViewStyle } from 'react-native'
20
+
21
+ import { FAILED_EVAL } from '../constants'
22
+ import {
23
+ ExtractedAttr,
24
+ ExtractedAttrAttr,
25
+ ExtractedAttrStyle,
26
+ ExtractorParseProps,
27
+ 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'
41
+
42
+ const UNTOUCHED_PROPS = {
43
+ key: true,
44
+ style: true,
45
+ className: true,
46
+ }
47
+
48
+ const INLINE_EXTRACTABLE = {
49
+ ref: 'ref',
50
+ key: 'key',
51
+ onPress: 'onClick',
52
+ onHoverIn: 'onMouseEnter',
53
+ onHoverOut: 'onMouseLeave',
54
+ onPressIn: 'onMouseDown',
55
+ onPressOut: 'onMouseUp',
56
+ }
57
+
58
+ const isAttr = (x: ExtractedAttr): x is ExtractedAttrAttr => x.type === 'attr'
59
+
60
+ const validHooks = {
61
+ useMedia: true,
62
+ useTheme: true,
63
+ }
64
+
65
+ export type Extractor = ReturnType<typeof createExtractor>
66
+
67
+ const createTernary = (x: Ternary) => x
68
+
69
+ export function createExtractor() {
70
+ if (!process.env.TAMAGUI_TARGET) {
71
+ console.log('⚠️ Please set process.env.TAMAGUI_TARGET to either "web" or "native"')
72
+ process.exit(1)
73
+ }
74
+
75
+ const shouldAddDebugProp =
76
+ // really basic disable this for next.js because it messes with ssr
77
+ !process.env.npm_package_dependencies_next &&
78
+ process.env.TAMAGUI_TARGET !== 'native' &&
79
+ process.env.IDENTIFY_TAGS !== 'false' &&
80
+ (process.env.NODE_ENV === 'development' || process.env.DEBUG || process.env.IDENTIFY_TAGS)
81
+
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
+ )
94
+ }
95
+
96
+ return {
97
+ getTamagui() {
98
+ return loadedTamaguiConfig
99
+ },
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
125
+ }
126
+ if (sourcePath === '') {
127
+ throw new Error(`Must provide a source file name`)
128
+ }
129
+ if (!Array.isArray(props.components)) {
130
+ throw new Error(`Must provide components array with list of Tamagui component modules`)
131
+ }
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 })
146
+ }
147
+
148
+ tm.mark('load-tamagui', shouldPrintDebug === 'verbose')
149
+
150
+ loadedTamaguiConfig = tamaguiConfig as any
151
+
152
+ const proxiedTheme = proxyThemeVariables(
153
+ tamaguiConfig.themes[Object.keys(tamaguiConfig.themes)[0]]
154
+ )
155
+
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
+ })
166
+
167
+ const body = fileOrPath.type === 'Program' ? fileOrPath.get('body') : fileOrPath.program.body
168
+
169
+ /**
170
+ * Step 1: Determine if importing any statically extractable components
171
+ */
172
+ const isInternalImport = (importStr: string) => {
173
+ return isInsideTamagui(sourcePath) && importStr[0] === '.'
174
+ }
175
+
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
+ }, {})
183
+
184
+ if (shouldPrintDebug === 'verbose') {
185
+ console.log('validComponents', Object.keys(validComponents))
186
+ }
187
+
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) {
218
+ doesUseValidImport = true
219
+ break
220
+ }
221
+ }
222
+ }
223
+
224
+ if (shouldPrintDebug) {
225
+ console.log(sourcePath, { doesUseValidImport })
226
+ }
227
+
228
+ if (!doesUseValidImport) {
229
+ return null
230
+ }
231
+
232
+ tm.mark('import-check', shouldPrintDebug === 'verbose')
233
+
234
+ let couldntParse = false
235
+ const modifiedComponents = new Set<NodePath<any>>()
236
+
237
+ // only keeping a cache around per-file, reset it if it changes
238
+ const bindingCache: Record<string, string | null> = {}
239
+
240
+ const callTraverse = (a: Visitor<{}>) => {
241
+ return fileOrPath.type === 'File' ? traverse(fileOrPath, a) : fileOrPath.traverse(a)
242
+ }
243
+
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
+ }
256
+
257
+ callTraverse({
258
+ Program: {
259
+ enter(path) {
260
+ programPath = path
261
+ },
262
+ },
263
+
264
+ CallExpression(path) {
265
+ if (disable || disableExtraction || extractStyledDefinitions === false) {
266
+ return
267
+ }
268
+
269
+ if (!t.isIdentifier(path.node.callee) || path.node.callee.name !== 'styled') {
270
+ return
271
+ }
272
+
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) {
288
+ if (shouldPrintDebug) {
289
+ console.log(
290
+ `Didn't recognize styled(${name}), ${name} isn't in design system provided to tamagui.config.ts`
291
+ )
292
+ }
293
+ return
294
+ }
295
+
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
+ ])
307
+
308
+ // for now dont parse variants, spreads, etc
309
+ const skipped: (t.ObjectProperty | t.SpreadElement | t.ObjectMethod)[] = []
310
+ const styles = {}
311
+
312
+ // Generate scope object at this level
313
+ const staticNamespace = getStaticBindingsForScope(
314
+ path.scope,
315
+ importsWhitelist,
316
+ sourcePath,
317
+ bindingCache,
318
+ shouldPrintDebug
319
+ )
320
+
321
+ const attemptEval = !evaluateVars
322
+ ? evaluateAstNode
323
+ : createEvaluator({
324
+ tamaguiConfig,
325
+ staticNamespace,
326
+ sourcePath,
327
+ shouldPrintDebug,
328
+ })
329
+ const attemptEvalSafe = createSafeEvaluator(attemptEval)
330
+
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
342
+ }
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
349
+ }
350
+ }
351
+
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
+ })
361
+
362
+ const classNames = {
363
+ ...out.classNames,
364
+ }
365
+
366
+ // add in the style object as classnames
367
+ const atomics = getStylesAtomic(out.style)
368
+
369
+ if (shouldPrintDebug) {
370
+ console.log('Exctacting styled()', styles, out, 'to', atomics)
371
+ }
372
+
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
+ }
380
+
381
+ // leave only un-parsed props...
382
+ definition.properties = skipped
383
+
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)))
391
+ }
392
+
393
+ if (out.rulesToInsert) {
394
+ for (const [identifier, rule] of out.rulesToInsert) {
395
+ onStyleRule?.(identifier, [rule])
396
+ }
397
+ }
398
+
399
+ res.styled++
400
+
401
+ if (shouldPrintDebug) {
402
+ console.log(`Extracted styled(${name}) props:`, styles)
403
+ }
404
+ },
405
+
406
+ JSXElement(traversePath) {
407
+ tm.mark('jsx-element', shouldPrintDebug === 'verbose')
408
+
409
+ const node = traversePath.node.openingElement
410
+ const ogAttributes = node.attributes
411
+ const componentName = findComponentName(traversePath.scope)
412
+ const closingElement = traversePath.node.closingElement
413
+
414
+ // skip non-identifier opening elements (member expressions, etc.)
415
+ if (t.isJSXMemberExpression(closingElement?.name) || !t.isJSXIdentifier(node.name)) {
416
+ return
417
+ }
418
+
419
+ // validate its a proper import from tamagui (or internally inside tamagui)
420
+ const binding = traversePath.scope.getBinding(node.name.name)
421
+
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
429
+ }
430
+ if (!validComponents[binding.identifier.name]) {
431
+ return
432
+ }
433
+ }
434
+
435
+ const component = validComponents[node.name.name] as { staticConfig?: StaticConfigParsed }
436
+ if (!component || !component.staticConfig) {
437
+ return
438
+ }
439
+
440
+ const originalNodeName = node.name.name
441
+
442
+ // found a valid tag
443
+ res.found++
444
+
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
452
+ )
453
+
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]
465
+
466
+ if (debugPropValue) {
467
+ shouldPrintDebug = debugPropValue
468
+ }
469
+
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
477
+ )
478
+
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
+ })
523
+
524
+ const flatNode = getFlattenedNode({ isTextView, tag: tagName })
525
+
526
+ const inlineProps = new Set([
527
+ ...(props.inlineProps || []),
528
+ ...(staticConfig.inlineProps || []),
529
+ ])
530
+
531
+ const deoptProps = new Set([
532
+ // always de-opt animation
533
+ 'animation',
534
+ ...(props.deoptProps || []),
535
+ ...(staticConfig.deoptProps || []),
536
+ ])
537
+
538
+ const inlineWhenUnflattened = new Set([...(staticConfig.inlineWhenUnflattened || [])])
539
+
540
+ // Generate scope object at this level
541
+ const staticNamespace = getStaticBindingsForScope(
542
+ traversePath.scope,
543
+ importsWhitelist,
544
+ sourcePath,
545
+ bindingCache,
546
+ shouldPrintDebug
547
+ )
548
+
549
+ const attemptEval = !evaluateVars
550
+ ? evaluateAstNode
551
+ : createEvaluator({
552
+ tamaguiConfig,
553
+ staticNamespace,
554
+ sourcePath,
555
+ traversePath,
556
+ shouldPrintDebug,
557
+ })
558
+ const attemptEvalSafe = createSafeEvaluator(attemptEval)
559
+
560
+ if (shouldPrintDebug) {
561
+ console.log(' staticNamespace', Object.keys(staticNamespace).join(', '))
562
+ }
563
+
564
+ //
565
+ // SPREADS SETUP
566
+ //
567
+
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
583
+ }
584
+ let arg: any
585
+ 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)
599
+ }
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
+ )
614
+ )
615
+ }
616
+ }
617
+ } catch (err) {
618
+ console.warn('cant parse spread, caught err', err)
619
+ couldntParse = true
620
+ }
621
+ }
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)
683
+
684
+ if (shouldPrintDebug) {
685
+ console.log(' - attrs (before):\n', logLines(attrs.map(attrStr).join(', ')))
686
+ }
687
+
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
+ }))
724
+ }
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
+ ) {
735
+ if (shouldPrintDebug) {
736
+ console.log(' ! inlining, spread attr')
737
+ }
738
+ inlined.set(`${Math.random()}`, 'spread')
739
+ return attr
740
+ }
741
+
742
+ const name = attribute.name.name
743
+
744
+ if (excludeProps?.has(name)) {
745
+ if (shouldPrintDebug) {
746
+ console.log(' excluding prop', name)
747
+ }
748
+ return null
749
+ }
750
+
751
+ if (inlineProps.has(name)) {
752
+ inlined.set(name, name)
753
+ if (shouldPrintDebug) {
754
+ console.log(' ! inlining, inline prop', name)
755
+ }
756
+ return attr
757
+ }
758
+
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
+ }
768
+
769
+ // pass className, key, and style props through untouched
770
+ if (UNTOUCHED_PROPS[name]) {
771
+ return attr
772
+ }
773
+
774
+ if (INLINE_EXTRACTABLE[name]) {
775
+ inlined.set(name, INLINE_EXTRACTABLE[name])
776
+ return attr
777
+ }
778
+
779
+ if (name.startsWith('data-')) {
780
+ return attr
781
+ }
782
+
783
+ // shorthand media queries
784
+ if (name[0] === '$' && t.isJSXExpressionContainer(attribute?.value)) {
785
+ // allow disabling this extraction
786
+ if (disableExtractInlineMedia) {
787
+ return attr
788
+ }
789
+
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
+ }))
806
+ }
807
+ }
808
+ }
809
+ }
810
+
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()
821
+ }
822
+
823
+ if (name === 'ref') {
824
+ if (shouldPrintDebug) {
825
+ console.log(' ! inlining, ref', name)
826
+ }
827
+ inlined.set('ref', 'ref')
828
+ return attr
829
+ }
830
+
831
+ if (name === 'tag') {
832
+ return {
833
+ type: 'attr',
834
+ value: path.node,
835
+ }
836
+ }
837
+
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
847
+ }
848
+ }
849
+ }
850
+
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
+ )
876
+
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
+ }
894
+
895
+ keys = Object.keys(out)
896
+ }
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
931
+ }
932
+
933
+ // return evaluated attributes
934
+ return attributes
935
+ }
936
+
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
+ }
954
+ return {
955
+ type: 'style',
956
+ value: { [name]: styleValue },
957
+ name,
958
+ attr: path.node,
959
+ }
960
+ } else {
961
+ if (variants[name]) {
962
+ variantValues.set(name, styleValue)
963
+ }
964
+ inlined.set(name, true)
965
+ return attr
966
+ }
967
+ }
968
+
969
+ // ternaries!
970
+
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)) {
975
+ if (shouldPrintDebug) {
976
+ console.log(` binary expression ${name} = `, value)
977
+ }
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)
982
+ if (shouldPrintDebug) {
983
+ console.log(` evalBinaryExpression lVal ${String(lVal)}, rVal ${String(rVal)}`)
984
+ }
985
+ if (lVal !== FAILED_EVAL && t.isConditionalExpression(right)) {
986
+ const ternary = addBinaryConditional(operator, left, right)
987
+ if (ternary) return ternary
988
+ }
989
+ if (rVal !== FAILED_EVAL && t.isConditionalExpression(left)) {
990
+ const ternary = addBinaryConditional(operator, right, left)
991
+ if (ternary) return ternary
992
+ }
993
+ if (shouldPrintDebug) {
994
+ console.log(` evalBinaryExpression cant extract`)
995
+ }
996
+ inlined.set(name, true)
997
+ return attr
998
+ }
999
+
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 }
1006
+ }
1007
+
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 }
1014
+ }
1015
+
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)
1020
+ }
1021
+
1022
+ //
1023
+ // RETURN ATTR
1024
+ //
1025
+ return attr
1026
+
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
+ )
1038
+ if (shouldPrintDebug) {
1039
+ console.log(' binaryConditional', cond.test, cons, alt)
1040
+ }
1041
+ return {
1042
+ type: 'ternary',
1043
+ value: {
1044
+ test: cond.test,
1045
+ remove,
1046
+ alternate: { [name]: alt },
1047
+ consequent: { [name]: cons },
1048
+ },
1049
+ }
1050
+ }
1051
+ return null
1052
+ }
1053
+
1054
+ function getStaticConditional(value: t.Node): Ternary | null {
1055
+ if (t.isConditionalExpression(value)) {
1056
+ try {
1057
+ const aVal = attemptEval(value.alternate)
1058
+ const cVal = attemptEval(value.consequent)
1059
+ if (shouldPrintDebug) {
1060
+ const type = value.test.type
1061
+ console.log(' static ternary', type, cVal, aVal)
1062
+ }
1063
+ return {
1064
+ test: value.test,
1065
+ remove,
1066
+ consequent: { [name]: cVal },
1067
+ alternate: { [name]: aVal },
1068
+ }
1069
+ } catch (err: any) {
1070
+ if (shouldPrintDebug) {
1071
+ console.log(' cant eval ternary', err.message)
1072
+ }
1073
+ }
1074
+ }
1075
+ return null
1076
+ }
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
+ }
1098
+ }
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
1117
+ }
1118
+ return true
1119
+ })
1120
+ )
1121
+ }
1122
+
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')
1142
+ }
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)
1170
+ }
1171
+ } else {
1172
+ console.log('⚠️ not expression', property)
1173
+ }
1174
+ }
1175
+ }
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
+ }
1223
+ node.attributes = ogAttributes
1224
+ return
1225
+ }
1226
+
1227
+ // before deopt, can still optimize
1228
+ const parentFn = findTopmostFunction(traversePath)
1229
+ if (parentFn) {
1230
+ modifiedComponents.add(parentFn)
1231
+ }
1232
+
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
+ )
1262
+ }
1263
+ ternaries = []
1264
+ }
1265
+ }
1266
+ if (cur.type === 'ternary') {
1267
+ return out
1268
+ }
1269
+ out.push(cur)
1270
+ return out
1271
+ }, [])
1272
+ .flat()
1273
+
1274
+ // flatten logic!
1275
+ // fairly simple check to see if all children are text
1276
+ const hasSpread = node.attributes.some((x) => t.isJSXSpreadAttribute(x))
1277
+
1278
+ const hasOnlyStringChildren =
1279
+ !hasSpread &&
1280
+ (node.selfClosing ||
1281
+ (traversePath.node.children &&
1282
+ traversePath.node.children.every((x) => x.type === 'JSXText')))
1283
+
1284
+ const themeVal = inlined.get('theme')
1285
+ inlined.delete('theme')
1286
+
1287
+ for (const [key] of [...inlined]) {
1288
+ if (INLINE_EXTRACTABLE[key] || staticConfig.variants?.[key]) {
1289
+ inlined.delete(key)
1290
+ }
1291
+ }
1292
+
1293
+ const canFlattenProps = inlined.size === 0
1294
+
1295
+ let shouldFlatten =
1296
+ !shouldDeopt &&
1297
+ canFlattenProps &&
1298
+ !hasSpread &&
1299
+ staticConfig.neverFlatten !== true &&
1300
+ (staticConfig.neverFlatten === 'jsx' ? hasOnlyStringChildren : true)
1301
+
1302
+ const shouldWrapTheme = shouldFlatten && themeVal
1303
+
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
+ }
1312
+
1313
+ if (shouldPrintDebug) {
1314
+ // prettier-ignore
1315
+ console.log(' - flatten?', objToStr({ hasSpread, shouldDeopt, shouldFlatten, canFlattenProps, shouldWrapTheme, hasOnlyStringChildren }), 'inlined', [...inlined])
1316
+ }
1317
+
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) {
1322
+ if (shouldPrintDebug) {
1323
+ console.log(' - wrapping theme', themeVal)
1324
+ }
1325
+
1326
+ // remove theme attribute from flattened node
1327
+ attrs = attrs.filter((x) =>
1328
+ x.type === 'attr' && t.isJSXAttribute(x.value) && x.value.name.name === 'theme'
1329
+ ? false
1330
+ : true
1331
+ )
1332
+
1333
+ // add import
1334
+ if (!hasImportedTheme) {
1335
+ hasImportedTheme = true
1336
+ programPath.node.body.push(
1337
+ t.importDeclaration(
1338
+ [t.importSpecifier(t.identifier('_TamaguiTheme'), t.identifier('Theme'))],
1339
+ t.stringLiteral('@tamagui/core')
1340
+ )
1341
+ )
1342
+ }
1343
+
1344
+ traversePath.replaceWith(
1345
+ t.jsxElement(
1346
+ t.jsxOpeningElement(t.jsxIdentifier('_TamaguiTheme'), [
1347
+ t.jsxAttribute(t.jsxIdentifier('name'), themeVal.value),
1348
+ ]),
1349
+ t.jsxClosingElement(t.jsxIdentifier('_TamaguiTheme')),
1350
+ [traversePath.node]
1351
+ )
1352
+ )
1353
+ }
1354
+
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]
1380
+ }
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
+ }
1412
+ }
1413
+ }
1414
+
1415
+ if (shouldPrintDebug) {
1416
+ console.log(' - attrs (flattened): \n', logLines(attrs.map(attrStr).join(', ')))
1417
+ console.log(' - ensureOverriden:', Object.keys(ensureOverridden).join(', '))
1418
+ }
1419
+
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
+ }
1428
+
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
+ }
1438
+ continue
1439
+ }
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
+ }
1455
+ }
1456
+ }
1457
+ }
1458
+
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]
1468
+ }
1469
+
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
+ }
1481
+
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
+ }
1527
+ }
1528
+ }
1529
+ }
1530
+ }
1531
+ }
1532
+
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
+
1565
+ acc.push(cur)
1566
+ 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
+ }
1573
+
1574
+ // merge styles, leave undefined values
1575
+ let prev: ExtractedAttr | null = null
1576
+
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]
1591
+ }
1592
+ }
1593
+ }
1594
+
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
+ }
1628
+
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
+ }
1640
+
1641
+ if (prev?.type === 'style') {
1642
+ mergeStyles(prev.value, cur.value)
1643
+ return acc
1644
+ }
1645
+ }
1646
+
1647
+ prev = cur
1648
+ acc.push(cur)
1649
+ return acc
1650
+ }, [])
1651
+
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)))
1658
+ }
1659
+
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]
1671
+ }
1672
+ }
1673
+ }
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
+ )
1686
+
1687
+ // console.log('outout', out)
1688
+
1689
+ const outStyle = {
1690
+ ...out.style,
1691
+ ...out.pseudos,
1692
+ }
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 {}
1706
+ }
1707
+ }
1708
+
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
+ }
1722
+ }
1723
+ }
1724
+
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
+ }
1731
+
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
+ : []
1736
+
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))
1742
+ }
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)
1749
+ }
1750
+ }
1751
+
1752
+ if (shouldPrintDebug) {
1753
+ // prettier-ignore
1754
+ if (shouldFlatten) console.log(' -- addInitialStyleKeys', addInitialStyleKeys.join(', '))
1755
+ // prettier-ignore
1756
+ // console.log(' -- completeStyles:\n', logLines(objToStr(completeStyles)))
1757
+ }
1758
+
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
1783
+ }
1784
+ } catch (err) {
1785
+ // any error de-opt
1786
+ getStyleError = err
1787
+ }
1788
+ }
1789
+
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')
1796
+
1797
+ if (getStyleError) {
1798
+ console.log(' ⚠️ postprocessing error, deopt', getStyleError)
1799
+ node.attributes = ogAttributes
1800
+ return node
1801
+ }
1802
+
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]
1807
+
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
+ }
1818
+
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
+ }
1827
+ }
1828
+ }
1829
+ }
1830
+ }
1831
+
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)
1846
+ }
1847
+ }
1848
+ }
1849
+ }
1850
+
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
+ }
1865
+ }
1866
+ }
1867
+ }
1868
+ }
1869
+ }
1870
+
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
+
1883
+ 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(', ')))
1887
+ }
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
1903
+ }
1904
+ }
1905
+ },
1906
+ })
1907
+
1908
+ tm.mark('jsx-done', shouldPrintDebug === 'verbose')
1909
+
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)
1920
+ }
1921
+ }
1922
+
1923
+ tm.done(shouldPrintDebug === 'verbose')
1924
+
1925
+ return res
1926
+ },
1927
+ }
1928
+ }