@tamagui/static 1.0.1-beta.140 → 1.0.1-beta.143

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