@tamagui/static 1.0.1-beta.141 → 1.0.1-beta.144
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.
- package/dist/cjs/extractor/createEvaluator.js.map +2 -2
- package/dist/cjs/extractor/createExtractor.js +1138 -1106
- package/dist/cjs/extractor/createExtractor.js.map +2 -2
- package/dist/cjs/extractor/extractToClassNames.js +4 -4
- package/dist/cjs/extractor/extractToClassNames.js.map +2 -2
- package/dist/cjs/extractor/getStaticBindingsForScope.js +18 -2
- package/dist/cjs/extractor/getStaticBindingsForScope.js.map +2 -2
- package/dist/cjs/extractor/loadTamagui.js +205 -28
- package/dist/cjs/extractor/loadTamagui.js.map +2 -2
- package/dist/cjs/extractor/timer.js +8 -1
- package/dist/cjs/extractor/timer.js.map +2 -2
- package/dist/cjs/getPragmaOptions.js +68 -0
- package/dist/cjs/getPragmaOptions.js.map +7 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/require.js +1 -20
- package/dist/cjs/require.js.map +2 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/webpackPlugin.js +35 -0
- package/dist/cjs/webpackPlugin.js.map +7 -0
- package/dist/esm/extractor/createEvaluator.js.map +2 -2
- package/dist/esm/extractor/createExtractor.js +1140 -1108
- package/dist/esm/extractor/createExtractor.js.map +2 -2
- package/dist/esm/extractor/extractToClassNames.js +4 -4
- package/dist/esm/extractor/extractToClassNames.js.map +2 -2
- package/dist/esm/extractor/getStaticBindingsForScope.js +17 -2
- package/dist/esm/extractor/getStaticBindingsForScope.js.map +2 -2
- package/dist/esm/extractor/loadTamagui.js +201 -28
- package/dist/esm/extractor/loadTamagui.js.map +2 -2
- package/dist/esm/extractor/timer.js +8 -1
- package/dist/esm/extractor/timer.js.map +2 -2
- package/dist/esm/getPragmaOptions.js +44 -0
- package/dist/esm/getPragmaOptions.js.map +7 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/require.js +2 -21
- package/dist/esm/require.js.map +2 -2
- package/dist/esm/webpackPlugin.js +11 -0
- package/dist/esm/webpackPlugin.js.map +7 -0
- package/dist/jsx/extractor/createEvaluator.js.map +2 -2
- package/dist/jsx/extractor/createExtractor.js +1133 -1101
- package/dist/jsx/extractor/createExtractor.js.map +2 -2
- package/dist/jsx/extractor/extractToClassNames.js +4 -4
- package/dist/jsx/extractor/extractToClassNames.js.map +2 -2
- package/dist/jsx/extractor/getStaticBindingsForScope.js +17 -2
- package/dist/jsx/extractor/getStaticBindingsForScope.js.map +2 -2
- package/dist/jsx/extractor/loadTamagui.js +199 -28
- package/dist/jsx/extractor/loadTamagui.js.map +2 -2
- package/dist/jsx/extractor/timer.js +8 -1
- package/dist/jsx/extractor/timer.js.map +2 -2
- package/dist/jsx/getPragmaOptions.js +43 -0
- package/dist/jsx/getPragmaOptions.js.map +7 -0
- package/dist/jsx/index.js +1 -0
- package/dist/jsx/index.js.map +2 -2
- package/dist/jsx/require.js +2 -21
- package/dist/jsx/require.js.map +2 -2
- package/dist/jsx/webpackPlugin.js +11 -0
- package/dist/jsx/webpackPlugin.js.map +7 -0
- package/package.json +20 -17
- package/src/extractor/createEvaluator.ts +2 -0
- package/src/extractor/createExtractor.ts +1592 -1532
- package/src/extractor/extractToClassNames.ts +25 -10
- package/src/extractor/getStaticBindingsForScope.ts +22 -5
- package/src/extractor/loadTamagui.ts +249 -32
- package/src/extractor/timer.ts +12 -1
- package/src/getPragmaOptions.ts +52 -0
- package/src/index.ts +2 -0
- package/src/require.ts +29 -24
- package/src/types.ts +16 -0
- package/src/webpackPlugin.ts +9 -0
- package/types/extractor/createExtractor.d.ts +22 -6
- package/types/extractor/extractToClassNames.d.ts +1 -1
- package/types/extractor/getStaticBindingsForScope.d.ts +1 -0
- package/types/extractor/loadTamagui.d.ts +5 -3
- package/types/getPragmaOptions.d.ts +9 -0
- package/types/index.d.ts +1 -0
- package/types/types.d.ts +13 -0
- 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
|
-
|
|
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
|
|
83
|
-
|
|
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
|
-
|
|
133
|
+
projectInfo?.tamaguiConfig.shorthands[name] ||
|
|
92
134
|
(name[0] === '$' ? !!mediaQueryConfig[name.slice(1)] : false)
|
|
93
135
|
)
|
|
94
136
|
}
|
|
95
137
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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 (
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
-
|
|
241
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
enter(path) {
|
|
260
|
-
programPath = path
|
|
261
|
-
},
|
|
262
|
-
},
|
|
271
|
+
let couldntParse = false
|
|
272
|
+
const modifiedComponents = new Set<NodePath<any>>()
|
|
263
273
|
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
277
|
+
const callTraverse = (a: Visitor<{}>) => {
|
|
278
|
+
return fileOrPath.type === 'File' ? traverse(fileOrPath, a) : fileOrPath.traverse(a)
|
|
279
|
+
}
|
|
273
280
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
286
|
+
const res = {
|
|
287
|
+
styled: 0,
|
|
288
|
+
flattened: 0,
|
|
289
|
+
optimized: 0,
|
|
290
|
+
modified: 0,
|
|
291
|
+
found: 0,
|
|
292
|
+
}
|
|
283
293
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
294
|
+
callTraverse({
|
|
295
|
+
Program: {
|
|
296
|
+
enter(path) {
|
|
297
|
+
programPath = path
|
|
298
|
+
},
|
|
299
|
+
},
|
|
287
300
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
return
|
|
295
|
-
}
|
|
301
|
+
// styled() calls
|
|
302
|
+
CallExpression(path) {
|
|
303
|
+
if (disable || disableExtraction || extractStyledDefinitions === false) {
|
|
304
|
+
return
|
|
305
|
+
}
|
|
296
306
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
importsWhitelist,
|
|
317
|
-
sourcePath,
|
|
318
|
-
bindingCache,
|
|
319
|
-
shouldPrintDebug
|
|
320
|
-
)
|
|
317
|
+
if (!name || !definition || !t.isObjectExpression(definition)) {
|
|
318
|
+
return
|
|
319
|
+
}
|
|
321
320
|
|
|
322
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
354
|
-
|
|
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
|
-
|
|
364
|
-
|
|
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
|
-
|
|
368
|
-
const atomics = getStylesAtomic(out.style)
|
|
340
|
+
Component = validComponents[name]
|
|
369
341
|
|
|
370
342
|
if (shouldPrintDebug) {
|
|
371
|
-
|
|
343
|
+
logger.info([`Loaded`, Object.keys(out.components).join(', '), !!Component].join(' '))
|
|
372
344
|
}
|
|
345
|
+
}
|
|
373
346
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
|
|
381
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
447
|
+
if (out.rulesToInsert) {
|
|
448
|
+
for (const { identifier, rules } of out.rulesToInsert) {
|
|
449
|
+
onStyleRule?.(identifier, rules)
|
|
396
450
|
}
|
|
451
|
+
}
|
|
397
452
|
|
|
398
|
-
|
|
453
|
+
res.styled++
|
|
399
454
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
455
|
+
if (shouldPrintDebug) {
|
|
456
|
+
logger.info(`Extracted styled(${name})`)
|
|
457
|
+
}
|
|
458
|
+
},
|
|
404
459
|
|
|
405
|
-
|
|
406
|
-
|
|
460
|
+
JSXElement(traversePath) {
|
|
461
|
+
tm.mark('jsx-element', !!shouldPrintDebug)
|
|
407
462
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
419
|
-
|
|
473
|
+
// validate its a proper import from tamagui (or internally inside tamagui)
|
|
474
|
+
const binding = traversePath.scope.getBinding(node.name.name)
|
|
420
475
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
489
|
+
const component = validComponents[node.name.name] as { staticConfig?: StaticConfigParsed }
|
|
490
|
+
if (!component || !component.staticConfig) {
|
|
491
|
+
return
|
|
492
|
+
}
|
|
443
493
|
|
|
444
|
-
|
|
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
|
-
|
|
496
|
+
// found a valid tag
|
|
497
|
+
res.found++
|
|
451
498
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
464
|
-
shouldPrintDebug = debugPropValue
|
|
465
|
-
}
|
|
505
|
+
const codePosition = `${filePath}:${lineNumbers}`
|
|
466
506
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
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
|
-
|
|
536
|
-
...(props.inlineProps || []),
|
|
537
|
-
...(staticConfig.inlineProps || []),
|
|
538
|
-
])
|
|
585
|
+
const flatNode = getFlattenedNode({ isTextView, tag: tagName })
|
|
539
586
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
...(staticConfig.deoptProps || []),
|
|
545
|
-
])
|
|
587
|
+
const inlineProps = new Set([
|
|
588
|
+
...(props.inlineProps || []),
|
|
589
|
+
...(staticConfig.inlineProps || []),
|
|
590
|
+
])
|
|
546
591
|
|
|
547
|
-
|
|
592
|
+
const deoptProps = new Set([
|
|
593
|
+
// always de-opt animation
|
|
594
|
+
'animation',
|
|
595
|
+
...(props.deoptProps || []),
|
|
596
|
+
...(staticConfig.deoptProps || []),
|
|
597
|
+
])
|
|
548
598
|
|
|
549
|
-
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
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
|
-
|
|
570
|
-
|
|
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
|
-
|
|
575
|
-
|
|
621
|
+
if (shouldPrintDebug) {
|
|
622
|
+
logger.info(` staticNamespace ${Object.keys(staticNamespace).join(', ')}`)
|
|
623
|
+
}
|
|
576
624
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
-
|
|
652
|
+
flattenedAttrs.push(attr)
|
|
653
|
+
return
|
|
654
|
+
}
|
|
655
|
+
if (arg !== undefined) {
|
|
594
656
|
try {
|
|
595
|
-
arg
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
-
|
|
691
|
-
.filter(isPresent)
|
|
682
|
+
}
|
|
683
|
+
})
|
|
692
684
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
685
|
+
if (couldntParse) {
|
|
686
|
+
return
|
|
687
|
+
}
|
|
696
688
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
736
|
+
return {
|
|
737
|
+
type: 'attr',
|
|
738
|
+
value: path.node,
|
|
739
|
+
} as const
|
|
749
740
|
}
|
|
741
|
+
})
|
|
742
|
+
.flat(4)
|
|
743
|
+
.filter(isPresent)
|
|
750
744
|
|
|
751
|
-
|
|
745
|
+
if (shouldPrintDebug) {
|
|
746
|
+
logger.info(
|
|
747
|
+
[' - attrs (before):\n', logLines(attrs.map(attrStr).join(', '))].join(' ')
|
|
748
|
+
)
|
|
749
|
+
}
|
|
752
750
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
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
|
-
|
|
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
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
779
|
-
if (
|
|
780
|
-
|
|
807
|
+
if (excludeProps?.has(name)) {
|
|
808
|
+
if (shouldPrintDebug) {
|
|
809
|
+
logger.info([' excluding prop', name].join(' '))
|
|
781
810
|
}
|
|
811
|
+
return null
|
|
812
|
+
}
|
|
782
813
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
-
|
|
789
|
-
|
|
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
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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
|
-
|
|
829
|
-
|
|
830
|
-
|
|
842
|
+
if (name.startsWith('data-')) {
|
|
843
|
+
return attr
|
|
844
|
+
}
|
|
831
845
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
-
|
|
855
|
-
|
|
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
|
-
|
|
861
|
-
|
|
862
|
-
return
|
|
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
|
-
|
|
866
|
-
|
|
882
|
+
const remove = () => {
|
|
883
|
+
Array.isArray(valuePath) ? valuePath.map((p) => p.remove()) : valuePath.remove()
|
|
884
|
+
}
|
|
867
885
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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
|
-
|
|
898
|
-
|
|
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
|
-
|
|
906
|
-
|
|
916
|
+
if (name === 'theme') {
|
|
917
|
+
inlined.set('theme', attr.value)
|
|
918
|
+
return attr
|
|
919
|
+
}
|
|
907
920
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
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
|
-
|
|
935
|
-
if (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
//
|
|
951
|
-
|
|
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
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
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: { [
|
|
967
|
-
name,
|
|
970
|
+
value: { [key]: styleValue },
|
|
971
|
+
name: key,
|
|
968
972
|
attr: path.node,
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
982
|
-
|
|
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
|
-
|
|
993
|
+
logger.info(` bailing flattening due to attributes ${attributes}`)
|
|
987
994
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
-
|
|
1012
|
+
logger.info(` style: ${name} = ${styleValue}`)
|
|
994
1013
|
}
|
|
995
|
-
if (
|
|
996
|
-
|
|
997
|
-
|
|
1014
|
+
if (!(name in staticConfig.defaultProps)) {
|
|
1015
|
+
if (!hasSetOptimized) {
|
|
1016
|
+
res.optimized++
|
|
1017
|
+
hasSetOptimized = true
|
|
1018
|
+
}
|
|
998
1019
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1020
|
+
return {
|
|
1021
|
+
type: 'style',
|
|
1022
|
+
value: { [name]: styleValue },
|
|
1023
|
+
name,
|
|
1024
|
+
attr: path.node,
|
|
1002
1025
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
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
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
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
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
1028
|
-
if (shouldPrintDebug) {
|
|
1029
|
-
|
|
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
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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
|
-
|
|
1124
|
+
const type = value.test.type
|
|
1125
|
+
logger.info([' static ternary', type, cVal, aVal].join(' '))
|
|
1050
1126
|
}
|
|
1051
1127
|
return {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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
|
-
|
|
1065
|
-
|
|
1142
|
+
function getStaticLogical(value: t.Node): Ternary | null {
|
|
1143
|
+
if (t.isLogicalExpression(value)) {
|
|
1144
|
+
if (value.operator === '&&') {
|
|
1066
1145
|
try {
|
|
1067
|
-
const
|
|
1068
|
-
const cVal = attemptEval(value.consequent)
|
|
1146
|
+
const val = attemptEval(value.right)
|
|
1069
1147
|
if (shouldPrintDebug) {
|
|
1070
|
-
|
|
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.
|
|
1151
|
+
test: value.left,
|
|
1075
1152
|
remove,
|
|
1076
|
-
consequent: { [name]:
|
|
1077
|
-
alternate:
|
|
1153
|
+
consequent: { [name]: val },
|
|
1154
|
+
alternate: null,
|
|
1078
1155
|
}
|
|
1079
|
-
} catch (err
|
|
1156
|
+
} catch (err) {
|
|
1080
1157
|
if (shouldPrintDebug) {
|
|
1081
|
-
|
|
1158
|
+
logger.info([' cant static eval logical', err].join(' '))
|
|
1082
1159
|
}
|
|
1083
1160
|
}
|
|
1084
1161
|
}
|
|
1085
|
-
return null
|
|
1086
1162
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
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
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1180
|
+
return false
|
|
1181
|
+
}
|
|
1182
|
+
return true
|
|
1183
|
+
})
|
|
1184
|
+
)
|
|
1185
|
+
}
|
|
1132
1186
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
1233
|
+
logger.info(['⚠️ no ternaries?', property].join(' '))
|
|
1183
1234
|
}
|
|
1235
|
+
} else {
|
|
1236
|
+
logger.info(['⚠️ not expression', property].join(' '))
|
|
1184
1237
|
}
|
|
1185
1238
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
)
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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
|
-
|
|
1227
|
-
|
|
1280
|
+
// now update to new values
|
|
1281
|
+
node.attributes = attrs.filter(isAttr).map((x) => x.value)
|
|
1228
1282
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
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
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1291
|
+
// before deopt, can still optimize
|
|
1292
|
+
const parentFn = findTopmostFunction(traversePath)
|
|
1293
|
+
if (parentFn) {
|
|
1294
|
+
modifiedComponents.add(parentFn)
|
|
1295
|
+
}
|
|
1242
1296
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
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
|
-
|
|
1277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
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
|
-
|
|
1348
|
+
const themeVal = inlined.get('theme')
|
|
1349
|
+
inlined.delete('theme')
|
|
1314
1350
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
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
|
-
|
|
1373
|
+
logger.info([' ! accessing theme key, avoid flatten', key].join(' '))
|
|
1335
1374
|
}
|
|
1375
|
+
})
|
|
1376
|
+
}
|
|
1336
1377
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
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
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
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
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
t.
|
|
1361
|
-
|
|
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
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
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
|
-
|
|
1390
|
-
|
|
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 (
|
|
1395
|
-
|
|
1396
|
-
return
|
|
1441
|
+
if (defaultStyleAttrs.length) {
|
|
1442
|
+
attrs = [...defaultStyleAttrs, ...attrs]
|
|
1397
1443
|
}
|
|
1444
|
+
}
|
|
1398
1445
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
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
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
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
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
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.
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
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
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
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
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1589
|
-
|
|
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
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
if (
|
|
1596
|
-
|
|
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
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
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
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
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
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
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
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
!
|
|
1641
|
-
|
|
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
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
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
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
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
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
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
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
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
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
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
|
-
|
|
1748
|
-
const completeStyles = getStyles(completeProps, 'completeStyles')
|
|
1784
|
+
// logger.info('outout', out)
|
|
1749
1785
|
|
|
1750
|
-
|
|
1751
|
-
|
|
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
|
-
|
|
1806
|
+
// used to ensure we pass the entire prop bundle to getStyles
|
|
1807
|
+
const completeStyles = getStyles(completeProps, 'completeStyles')
|
|
1755
1808
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
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
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1846
|
+
if (shouldPrintDebug) {
|
|
1847
|
+
// prettier-ignore
|
|
1848
|
+
logger.info([' - attrs (ternaries/combined):\n', logLines(attrs.map(attrStr).join(', '))].join(' '))
|
|
1849
|
+
}
|
|
1789
1850
|
|
|
1790
|
-
|
|
1851
|
+
tm.mark('jsx-element-styles', !!shouldPrintDebug)
|
|
1791
1852
|
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1853
|
+
if (getStyleError) {
|
|
1854
|
+
logger.info([' ⚠️ postprocessing error, deopt', getStyleError].join(' '))
|
|
1855
|
+
node.attributes = ogAttributes
|
|
1856
|
+
return node
|
|
1857
|
+
}
|
|
1797
1858
|
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
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
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
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
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
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
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
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
|
-
|
|
1862
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
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
|
-
|
|
1976
|
+
tm.done(shouldPrintDebug === 'verbose')
|
|
1916
1977
|
|
|
1917
|
-
|
|
1918
|
-
},
|
|
1978
|
+
return res
|
|
1919
1979
|
}
|
|
1920
1980
|
}
|