@tamagui/static 1.132.16 → 1.132.18
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/extractor/concatClassName.js +69 -0
- package/dist/extractor/concatClassName.js.map +6 -0
- package/dist/extractor/concatClassName.native.js +69 -0
- package/dist/extractor/concatClassName.native.js.map +6 -0
- package/dist/extractor/createEvaluator.js +13 -1
- package/dist/extractor/createEvaluator.js.map +1 -1
- package/dist/extractor/createEvaluator.native.js +6 -1
- package/dist/extractor/createEvaluator.native.js.map +2 -2
- package/dist/extractor/createExtractor.js +60 -114
- package/dist/extractor/createExtractor.js.map +1 -1
- package/dist/extractor/createExtractor.native.js +70 -123
- package/dist/extractor/createExtractor.native.js.map +2 -2
- package/dist/extractor/errors.js +22 -0
- package/dist/extractor/errors.js.map +6 -0
- package/dist/extractor/errors.native.js +119 -0
- package/dist/extractor/errors.native.js.map +6 -0
- package/dist/extractor/extractMediaStyle.js +1 -1
- package/dist/extractor/extractMediaStyle.js.map +1 -1
- package/dist/extractor/extractMediaStyle.native.js +1 -1
- package/dist/extractor/extractMediaStyle.native.js.map +2 -2
- package/dist/extractor/extractToClassNames.js +212 -172
- package/dist/extractor/extractToClassNames.js.map +2 -2
- package/dist/extractor/extractToClassNames.native.js +212 -188
- package/dist/extractor/extractToClassNames.native.js.map +2 -2
- package/dist/extractor/extractToNative.js +47 -78
- package/dist/extractor/extractToNative.js.map +1 -1
- package/dist/extractor/extractToNative.native.js +23 -39
- package/dist/extractor/extractToNative.native.js.map +2 -2
- package/dist/extractor/normalizeTernaries.js +5 -3
- package/dist/extractor/normalizeTernaries.js.map +1 -1
- package/dist/extractor/normalizeTernaries.native.js +5 -3
- package/dist/extractor/normalizeTernaries.native.js.map +2 -2
- package/dist/extractor/propsToFontFamilyCache.js +7 -8
- package/dist/extractor/propsToFontFamilyCache.js.map +1 -1
- package/dist/extractor/propsToFontFamilyCache.native.js +9 -10
- package/dist/extractor/propsToFontFamilyCache.native.js.map +2 -2
- package/dist/registerRequire.js +1 -1
- package/dist/registerRequire.js.map +1 -1
- package/dist/registerRequire.native.js +1 -1
- package/dist/registerRequire.native.js.map +1 -1
- package/dist/types.native.js.map +1 -1
- package/package.json +15 -15
- package/src/extractor/concatClassName.ts +100 -0
- package/src/extractor/createEvaluator.ts +26 -1
- package/src/extractor/createExtractor.ts +108 -194
- package/src/extractor/errors.ts +1 -0
- package/src/extractor/extractMediaStyle.ts +1 -1
- package/src/extractor/extractToClassNames.ts +377 -264
- package/src/extractor/extractToNative.ts +68 -111
- package/src/extractor/normalizeTernaries.ts +10 -3
- package/src/extractor/propsToFontFamilyCache.ts +5 -5
- package/src/registerRequire.ts +1 -1
- package/src/types.ts +10 -13
- package/types/extractor/concatClassName.d.ts +8 -0
- package/types/extractor/concatClassName.d.ts.map +1 -0
- package/types/extractor/createEvaluator.d.ts.map +1 -1
- package/types/extractor/createExtractor.d.ts.map +1 -1
- package/types/extractor/errors.d.ts +3 -0
- package/types/extractor/errors.d.ts.map +1 -0
- package/types/extractor/extractToClassNames.d.ts.map +1 -1
- package/types/extractor/extractToNative.d.ts.map +1 -1
- package/types/extractor/normalizeTernaries.d.ts.map +1 -1
- package/types/extractor/propsToFontFamilyCache.d.ts +2 -2
- package/types/extractor/propsToFontFamilyCache.d.ts.map +1 -1
- package/types/types.d.ts +9 -10
- package/types/types.d.ts.map +1 -1
- package/dist/extractor/buildClassName.js +0 -72
- package/dist/extractor/buildClassName.js.map +0 -6
- package/dist/extractor/buildClassName.native.js +0 -67
- package/dist/extractor/buildClassName.native.js.map +0 -6
- package/dist/extractor/ensureImportingConcat.js +0 -50
- package/dist/extractor/ensureImportingConcat.js.map +0 -6
- package/dist/extractor/ensureImportingConcat.native.js +0 -49
- package/dist/extractor/ensureImportingConcat.native.js.map +0 -6
- package/dist/extractor/hoistClassNames.js +0 -63
- package/dist/extractor/hoistClassNames.js.map +0 -6
- package/dist/extractor/hoistClassNames.native.js +0 -66
- package/dist/extractor/hoistClassNames.native.js.map +0 -6
- package/src/extractor/buildClassName.ts +0 -76
- package/src/extractor/ensureImportingConcat.ts +0 -36
- package/src/extractor/hoistClassNames.ts +0 -52
- package/types/extractor/buildClassName.d.ts +0 -7
- package/types/extractor/buildClassName.d.ts.map +0 -1
- package/types/extractor/ensureImportingConcat.d.ts +0 -4
- package/types/extractor/ensureImportingConcat.d.ts.map +0 -1
- package/types/extractor/hoistClassNames.d.ts +0 -6
- package/types/extractor/hoistClassNames.d.ts.map +0 -1
@@ -1,30 +1,23 @@
|
|
1
|
-
import * as path from 'node:path'
|
2
|
-
import * as util from 'node:util'
|
3
|
-
|
4
1
|
import generate from '@babel/generator'
|
2
|
+
import type { NodePath } from '@babel/traverse'
|
5
3
|
import * as t from '@babel/types'
|
6
|
-
import
|
7
|
-
import
|
8
|
-
|
4
|
+
import { mergeProps, StyleObjectIdentifier, StyleObjectRules } from '@tamagui/web'
|
5
|
+
import * as path from 'node:path'
|
6
|
+
import * as util from 'node:util'
|
9
7
|
import { requireTamaguiCore } from '../helpers/requireTamaguiCore'
|
10
|
-
import type {
|
8
|
+
import type { StyleObject, TamaguiOptions, Ternary } from '../types'
|
11
9
|
import { babelParse } from './babelParse'
|
12
|
-
import { buildClassName } from './buildClassName'
|
13
10
|
import type { Extractor } from './createExtractor'
|
14
11
|
import { createLogger } from './createLogger'
|
15
|
-
import { ensureImportingConcat } from './ensureImportingConcat'
|
16
|
-
import { isSimpleSpread } from './extractHelpers'
|
17
12
|
import { extractMediaStyle } from './extractMediaStyle'
|
18
|
-
import {
|
19
|
-
import {
|
13
|
+
import { normalizeTernaries } from './normalizeTernaries'
|
14
|
+
import {
|
15
|
+
forwardFontFamilyName,
|
16
|
+
getFontFamilyNameFromProps,
|
17
|
+
} from './propsToFontFamilyCache'
|
20
18
|
import { timer } from './timer'
|
21
|
-
|
22
|
-
|
23
|
-
shadowOpacity: true,
|
24
|
-
shadowRadius: true,
|
25
|
-
shadowColor: true,
|
26
|
-
shadowOffset: true,
|
27
|
-
}
|
19
|
+
import { BailOptimizationError } from './errors'
|
20
|
+
import { concatClassName } from './concatClassName'
|
28
21
|
|
29
22
|
export type ExtractedResponse = {
|
30
23
|
js: string | Buffer
|
@@ -42,6 +35,12 @@ export type ExtractToClassNamesProps = {
|
|
42
35
|
shouldPrintDebug: boolean | 'verbose'
|
43
36
|
}
|
44
37
|
|
38
|
+
// we only expand into ternaries or plain attr, all style is turned into a always-true ternary
|
39
|
+
// this lets us more easily combine everything easily
|
40
|
+
// all ternaries in this array ONLY have consequent, they are normalized
|
41
|
+
const remove = () => {} // we dont remove after this step
|
42
|
+
const spaceString = t.stringLiteral(' ')
|
43
|
+
|
45
44
|
export async function extractToClassNames({
|
46
45
|
extractor,
|
47
46
|
source,
|
@@ -50,7 +49,7 @@ export async function extractToClassNames({
|
|
50
49
|
shouldPrintDebug,
|
51
50
|
}: ExtractToClassNamesProps): Promise<ExtractedResponse | null> {
|
52
51
|
const tm = timer()
|
53
|
-
const { getCSSStylesAtomic } = requireTamaguiCore('web')
|
52
|
+
const { getCSSStylesAtomic, createMediaStyle } = requireTamaguiCore('web')
|
54
53
|
|
55
54
|
if (sourcePath.includes('node_modules')) {
|
56
55
|
return null
|
@@ -93,9 +92,7 @@ export async function extractToClassNames({
|
|
93
92
|
tm.mark(`babel-parse`, shouldPrintDebug === 'verbose')
|
94
93
|
|
95
94
|
const cssMap = new Map<string, { css: string; commentTexts: string[] }>()
|
96
|
-
const
|
97
|
-
|
98
|
-
let hasFlattened = false
|
95
|
+
const tamaguiConfig = extractor.getTamagui()!
|
99
96
|
|
100
97
|
const res = await extractor.parse(ast, {
|
101
98
|
shouldPrintDebug,
|
@@ -103,7 +100,7 @@ export async function extractToClassNames({
|
|
103
100
|
platform: 'web',
|
104
101
|
sourcePath,
|
105
102
|
extractStyledDefinitions: true,
|
106
|
-
|
103
|
+
onStyledDefinitionRule(identifier, rules) {
|
107
104
|
const css = rules.join(';')
|
108
105
|
if (shouldPrintDebug) {
|
109
106
|
console.info(`adding styled() rule: .${identifier} ${css}`)
|
@@ -111,7 +108,6 @@ export async function extractToClassNames({
|
|
111
108
|
cssMap.set(`.${identifier}`, { css, commentTexts: [] })
|
112
109
|
},
|
113
110
|
getFlattenedNode: ({ tag }) => {
|
114
|
-
hasFlattened = true
|
115
111
|
return tag
|
116
112
|
},
|
117
113
|
onExtractTag: ({
|
@@ -123,300 +119,364 @@ export async function extractToClassNames({
|
|
123
119
|
originalNodeName,
|
124
120
|
filePath,
|
125
121
|
lineNumbers,
|
126
|
-
programPath,
|
127
|
-
isFlattened,
|
128
122
|
staticConfig,
|
129
123
|
}) => {
|
130
124
|
// bail out of views that don't accept className (falls back to runtime + style={})
|
131
125
|
if (staticConfig.acceptsClassName === false) {
|
132
|
-
|
133
|
-
console.info(`bail, acceptsClassName is false`)
|
134
|
-
}
|
135
|
-
return
|
126
|
+
throw new BailOptimizationError()
|
136
127
|
}
|
137
128
|
|
138
|
-
//
|
139
|
-
|
140
|
-
|
129
|
+
// re-worked how we do this
|
130
|
+
// merging ternaries on top of base styles is not simple, because we need to ensure the final
|
131
|
+
// className has no duplicate style props and selector order is preserved
|
132
|
+
// before we tried to be smart and build a big binary expression
|
133
|
+
// instead, what we'll do now is pre-calculate the entire className for every possible path
|
134
|
+
// for super complex components that means we *will* output a lot of bigger classNames
|
135
|
+
// but its so much simpler than trying to implement a multi-stage solver here
|
136
|
+
// and in the end its just strings that gzip very well
|
137
|
+
// its also much easier to intuit/debug for end users and ourselves
|
138
|
+
|
139
|
+
// example:
|
140
|
+
// a ? 'a' : 'b'
|
141
|
+
// b ? 'c' : 'd'
|
142
|
+
// we want:
|
143
|
+
// a && b ? 'a c' : ''
|
144
|
+
// !a && b ? 'b c' : ''
|
145
|
+
// a && !b ? 'a d' : ''
|
146
|
+
// !a && !b ? 'b d' : ''
|
147
|
+
|
148
|
+
// we also simplified the compiler to only handle views that can be fully flattened
|
149
|
+
// this means we don't need to account for strange in-between spreads, so we can merge things
|
150
|
+
// fairly simply. first, we just merge forward all the non-ternary styles into ternaries.
|
151
|
+
|
152
|
+
// save for the end
|
153
|
+
const finalAttrs: t.JSXAttribute[] = []
|
154
|
+
|
155
|
+
let mergeForwardBaseStyle: Object | null = null
|
156
|
+
let attrClassName: t.Expression | null = null
|
157
|
+
let baseFontFamily = ''
|
158
|
+
let mediaStylesSeen = 1
|
141
159
|
|
142
|
-
|
143
|
-
|
144
|
-
|
160
|
+
const comment = util.format(
|
161
|
+
'/* %s:%s (%s) */',
|
162
|
+
filePath,
|
163
|
+
lineNumbers,
|
164
|
+
originalNodeName
|
165
|
+
)
|
145
166
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
167
|
+
function addStyle(style: StyleObject) {
|
168
|
+
const identifier = style[StyleObjectIdentifier]
|
169
|
+
const rules = style[StyleObjectRules]
|
170
|
+
const selector = `.${identifier}`
|
171
|
+
if (cssMap.has(selector)) {
|
172
|
+
const val = cssMap.get(selector)!
|
173
|
+
val.commentTexts.push(comment)
|
174
|
+
} else if (rules.length) {
|
175
|
+
cssMap.set(selector, {
|
176
|
+
css: rules.join('\n'),
|
177
|
+
commentTexts: [comment],
|
178
|
+
})
|
153
179
|
}
|
180
|
+
return identifier
|
154
181
|
}
|
155
182
|
|
156
|
-
|
157
|
-
|
158
|
-
const
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
183
|
+
function addStyles(style: Object) {
|
184
|
+
const cssStyles = getCSSStylesAtomic(style as any)
|
185
|
+
const classNames: string[] = []
|
186
|
+
|
187
|
+
for (const style of cssStyles) {
|
188
|
+
const mediaName = style[0].slice(1)
|
189
|
+
if (tamaguiConfig.media[mediaName]) {
|
190
|
+
const mediaStyle = createMediaStyle(
|
191
|
+
style,
|
192
|
+
mediaName,
|
193
|
+
extractor.getTamagui()!.media,
|
194
|
+
true,
|
195
|
+
false,
|
196
|
+
mediaStylesSeen
|
197
|
+
)
|
198
|
+
const identifier = addStyle(mediaStyle)
|
199
|
+
classNames.push(identifier)
|
200
|
+
continue
|
165
201
|
}
|
166
|
-
}
|
167
|
-
return style
|
168
|
-
}
|
169
202
|
|
170
|
-
|
171
|
-
|
172
|
-
const styleWithPrev = ensureNeededPrevStyle(style)
|
173
|
-
const res = getCSSStylesAtomic(styleWithPrev as any)
|
174
|
-
if (res.length) {
|
175
|
-
finalStyles = [...finalStyles, ...res]
|
203
|
+
const identifier = addStyle(style)
|
204
|
+
classNames.push(identifier)
|
176
205
|
}
|
177
|
-
|
206
|
+
|
207
|
+
return classNames
|
178
208
|
}
|
179
209
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
switch (attr.type) {
|
184
|
-
case 'style': {
|
185
|
-
if (!isFlattened) {
|
186
|
-
const styles = getCSSStylesAtomic(attr.value as any)
|
187
|
-
|
188
|
-
finalStyles = [...finalStyles, ...styles]
|
189
|
-
|
190
|
-
for (const style of styles) {
|
191
|
-
// leave them as attributes
|
192
|
-
const prop = style[helpers.StyleObjectPseudo]
|
193
|
-
? `${style[helpers.StyleObjectProperty]}-${
|
194
|
-
style[helpers.StyleObjectPseudo]
|
195
|
-
}`
|
196
|
-
: style[helpers.StyleObjectProperty]
|
197
|
-
finalAttrs.push(
|
198
|
-
t.jsxAttribute(
|
199
|
-
t.jsxIdentifier(prop),
|
200
|
-
t.stringLiteral(style[helpers.StyleObjectIdentifier])
|
201
|
-
)
|
202
|
-
)
|
203
|
-
}
|
204
|
-
} else {
|
205
|
-
const styles = addStyles(attr.value)
|
206
|
-
const newFontFamily = getFontFamilyClassNameFromProps(attr.value) || ''
|
207
|
-
const newClassNames = helpers.concatClassName(
|
208
|
-
styles.map((x) => x[helpers.StyleObjectIdentifier]).join(' ') +
|
209
|
-
newFontFamily
|
210
|
-
)
|
211
|
-
const existing = finalClassNames.find(
|
212
|
-
(x) => x.type == 'StringLiteral'
|
213
|
-
) as t.StringLiteral | null
|
214
|
-
|
215
|
-
if (existing) {
|
216
|
-
let previous = existing.value
|
217
|
-
// replace existing font_ with new one
|
218
|
-
if (newFontFamily) {
|
219
|
-
if (shouldPrintDebug) {
|
220
|
-
console.info(` newFontFamily: ${newFontFamily}`)
|
221
|
-
}
|
222
|
-
previous = previous.replace(/font_[a-z]+/i, '')
|
223
|
-
}
|
224
|
-
existing.value = `${previous} ${newClassNames}`
|
225
|
-
} else {
|
226
|
-
finalClassNames = [...finalClassNames, t.stringLiteral(newClassNames)]
|
227
|
-
}
|
228
|
-
}
|
210
|
+
const onlyTernaries: Ternary[] = attrs.flatMap((attr) => {
|
211
|
+
if (attr.type === 'attr') {
|
212
|
+
const value = attr.value
|
229
213
|
|
230
|
-
|
214
|
+
if (t.isJSXSpreadAttribute(value)) {
|
215
|
+
// we only handle flattened stuff now so skip this
|
216
|
+
console.error(`Should never happen`)
|
217
|
+
return []
|
231
218
|
}
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
t.logicalExpression(
|
238
|
-
'&&',
|
239
|
-
val.argument,
|
240
|
-
t.memberExpression(val.argument, t.identifier('className'))
|
241
|
-
)
|
242
|
-
)
|
243
|
-
}
|
244
|
-
} else if (val.name.name === 'className') {
|
245
|
-
const value = val.value
|
246
|
-
if (value) {
|
247
|
-
try {
|
248
|
-
const evaluatedValue = attemptEval(value)
|
249
|
-
finalClassNames.push(t.stringLiteral(evaluatedValue))
|
250
|
-
} catch (e) {
|
251
|
-
finalClassNames.push(value['expression'])
|
252
|
-
}
|
253
|
-
}
|
254
|
-
continue
|
219
|
+
|
220
|
+
if (value.name.name === 'className') {
|
221
|
+
let inner: any = value.value
|
222
|
+
if (t.isJSXExpressionContainer(inner)) {
|
223
|
+
inner = inner.expression
|
255
224
|
}
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
const mediaExtraction = extractMediaStyle(
|
261
|
-
parserProps,
|
262
|
-
attr.value,
|
263
|
-
jsxPath,
|
264
|
-
extractor.getTamagui()!,
|
265
|
-
sourcePath || '',
|
266
|
-
lastMediaImportance,
|
267
|
-
shouldPrintDebug
|
268
|
-
)
|
269
|
-
if (shouldPrintDebug) {
|
270
|
-
if (mediaExtraction) {
|
271
|
-
console.info(
|
272
|
-
'ternary (mediaStyles)',
|
273
|
-
mediaExtraction.ternaryWithoutMedia?.inlineMediaQuery ?? '',
|
274
|
-
mediaExtraction.mediaStyles
|
275
|
-
.map((x) => x[helpers.StyleObjectIdentifier])
|
276
|
-
.join('.')
|
277
|
-
)
|
225
|
+
try {
|
226
|
+
const evaluatedValue = inner ? attemptEval(inner) : null
|
227
|
+
if (typeof evaluatedValue === 'string') {
|
228
|
+
attrClassName = t.stringLiteral(evaluatedValue)
|
278
229
|
}
|
279
|
-
}
|
280
|
-
|
281
|
-
|
282
|
-
if (mediaExtraction) {
|
283
|
-
console.info('add ternary')
|
284
|
-
}
|
230
|
+
} catch (e) {
|
231
|
+
if (inner) {
|
232
|
+
attrClassName ||= inner
|
285
233
|
}
|
286
|
-
addTernaryStyle(
|
287
|
-
attr.value,
|
288
|
-
addStyles(attr.value.consequent),
|
289
|
-
addStyles(attr.value.alternate)
|
290
|
-
)
|
291
|
-
continue
|
292
234
|
}
|
293
|
-
|
235
|
+
return []
|
236
|
+
}
|
237
|
+
|
238
|
+
finalAttrs.push(value)
|
239
|
+
return []
|
240
|
+
}
|
241
|
+
|
242
|
+
if (attr.type === 'style') {
|
243
|
+
mergeForwardBaseStyle = mergeProps(mergeForwardBaseStyle || {}, attr.value)
|
244
|
+
baseFontFamily = getFontFamilyNameFromProps(attr.value) || ''
|
245
|
+
return []
|
246
|
+
}
|
247
|
+
|
248
|
+
let ternary = attr.value
|
249
|
+
|
250
|
+
if (ternary.inlineMediaQuery) {
|
251
|
+
const mediaExtraction = extractMediaStyle(
|
252
|
+
parserProps,
|
253
|
+
attr.value,
|
254
|
+
jsxPath,
|
255
|
+
extractor.getTamagui()!,
|
256
|
+
sourcePath || '',
|
257
|
+
mediaStylesSeen++,
|
258
|
+
shouldPrintDebug
|
259
|
+
)
|
260
|
+
|
261
|
+
if (mediaExtraction) {
|
294
262
|
if (mediaExtraction.mediaStyles) {
|
295
|
-
|
263
|
+
mergeForwardBaseStyle = mergeProps(mergeForwardBaseStyle || {}, {
|
264
|
+
[`$${ternary.inlineMediaQuery}`]: attr.value.consequent!,
|
265
|
+
})
|
296
266
|
}
|
297
267
|
if (mediaExtraction.ternaryWithoutMedia) {
|
298
|
-
|
299
|
-
mediaExtraction.ternaryWithoutMedia,
|
300
|
-
mediaExtraction.mediaStyles,
|
301
|
-
[]
|
302
|
-
)
|
268
|
+
ternary = mediaExtraction.ternaryWithoutMedia
|
303
269
|
} else {
|
304
|
-
|
305
|
-
...finalClassNames,
|
306
|
-
...mediaExtraction.mediaStyles.map((x) =>
|
307
|
-
t.stringLiteral(x[helpers.StyleObjectIdentifier])
|
308
|
-
),
|
309
|
-
]
|
270
|
+
return []
|
310
271
|
}
|
311
|
-
break
|
312
272
|
}
|
313
273
|
}
|
314
|
-
}
|
315
274
|
|
316
|
-
|
317
|
-
|
318
|
-
const aCN = b.map((x) => x[helpers.StyleObjectIdentifier]).join(' ')
|
275
|
+
let mergedAlternate
|
276
|
+
let mergedConsequent
|
319
277
|
|
320
|
-
if (
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
t.stringLiteral(cCN),
|
325
|
-
t.stringLiteral(aCN)
|
326
|
-
)
|
278
|
+
if (ternary.alternate && Object.keys(ternary.alternate).length) {
|
279
|
+
mergedAlternate = mergeProps(
|
280
|
+
mergeForwardBaseStyle || {},
|
281
|
+
ternary.alternate || {}
|
327
282
|
)
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
283
|
+
forwardFontFamilyName(ternary.alternate, mergedAlternate, baseFontFamily)
|
284
|
+
}
|
285
|
+
|
286
|
+
if (ternary.consequent && Object.keys(ternary.consequent).length) {
|
287
|
+
mergedConsequent = mergeProps(
|
288
|
+
mergeForwardBaseStyle || {},
|
289
|
+
ternary.consequent || {}
|
335
290
|
)
|
291
|
+
forwardFontFamilyName(ternary.consequent, mergedConsequent, baseFontFamily)
|
336
292
|
}
|
337
|
-
}
|
338
293
|
|
339
|
-
|
340
|
-
|
294
|
+
// merge the base style forward into both sides
|
295
|
+
return {
|
296
|
+
...ternary,
|
297
|
+
alternate: mergedAlternate,
|
298
|
+
consequent: mergedConsequent,
|
299
|
+
}
|
300
|
+
})
|
301
|
+
|
302
|
+
const hasTernaries = Boolean(onlyTernaries.length)
|
303
|
+
|
304
|
+
const baseClassNames = mergeForwardBaseStyle
|
305
|
+
? addStyles(mergeForwardBaseStyle)
|
306
|
+
: null
|
307
|
+
|
308
|
+
let baseClassNameStr = !baseClassNames ? '' : baseClassNames.join(' ')
|
309
|
+
|
310
|
+
if (!hasTernaries && baseFontFamily) {
|
311
|
+
baseClassNameStr = `font_${baseFontFamily}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
341
312
|
}
|
342
313
|
|
343
|
-
|
314
|
+
let base = staticConfig.componentName
|
315
|
+
? t.stringLiteral(
|
316
|
+
`is_${staticConfig.componentName}${baseClassNameStr ? ` ${baseClassNameStr}` : ''}`
|
317
|
+
)
|
318
|
+
: t.stringLiteral(baseClassNameStr || '')
|
344
319
|
|
345
|
-
|
346
|
-
const extraClassNames = (() => {
|
347
|
-
let value = ''
|
348
|
-
if (!isFlattened) {
|
349
|
-
return value
|
350
|
-
}
|
320
|
+
attrClassName = attrClassName as t.Expression | null // actual typescript bug, flatMap doesn't update from never
|
351
321
|
|
352
|
-
|
353
|
-
|
354
|
-
|
322
|
+
const baseClassNameExpression: t.Expression = (() => {
|
323
|
+
if (attrClassName) {
|
324
|
+
if (t.isStringLiteral(attrClassName)) {
|
325
|
+
return t.stringLiteral(
|
326
|
+
base.value ? `${base.value} ${attrClassName.value}` : attrClassName.value
|
327
|
+
)
|
328
|
+
} else {
|
329
|
+
// space after to ensure its a string and its spaced
|
330
|
+
return t.conditionalExpression(
|
331
|
+
attrClassName,
|
332
|
+
t.binaryExpression('+', attrClassName, spaceString),
|
333
|
+
base
|
334
|
+
)
|
355
335
|
}
|
336
|
+
}
|
337
|
+
return base
|
338
|
+
})()
|
356
339
|
|
357
|
-
|
358
|
-
if (staticConfig.componentName) {
|
359
|
-
value += ` is_${staticConfig.componentName}`
|
360
|
-
}
|
340
|
+
const expandedTernaries: Ternary[] = []
|
361
341
|
|
362
|
-
|
363
|
-
|
342
|
+
if (onlyTernaries.length) {
|
343
|
+
// normalize tests to reduce duplicates
|
344
|
+
const normalizedTernaries = normalizeTernaries(onlyTernaries)
|
364
345
|
|
365
|
-
|
366
|
-
|
346
|
+
for (const ternary of normalizedTernaries) {
|
347
|
+
if (!expandedTernaries.length) {
|
348
|
+
expandTernary(ternary)
|
349
|
+
continue
|
350
|
+
}
|
351
|
+
for (const prev of [...expandedTernaries]) {
|
352
|
+
expandTernary(ternary, prev)
|
353
|
+
}
|
354
|
+
}
|
355
|
+
}
|
367
356
|
|
368
|
-
|
369
|
-
|
357
|
+
function expandTernary(ternary: Ternary, prev?: Ternary) {
|
358
|
+
// need to diverge into two (or four if alternate)
|
359
|
+
if (ternary.consequent && Object.keys(ternary.consequent).length) {
|
360
|
+
const fontFamily = getFontFamilyNameFromProps(ternary.consequent)
|
361
|
+
|
362
|
+
expandedTernaries.push({
|
363
|
+
fontFamily,
|
364
|
+
// prevTest && test: merge consequent
|
365
|
+
test: prev
|
366
|
+
? t.logicalExpression('&&', prev.test, ternary.test)
|
367
|
+
: ternary.test,
|
368
|
+
consequent: prev
|
369
|
+
? mergeProps(prev.consequent!, ternary.consequent)
|
370
|
+
: ternary.consequent,
|
371
|
+
remove,
|
372
|
+
alternate: null,
|
373
|
+
})
|
370
374
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
375
|
+
if (prev) {
|
376
|
+
expandedTernaries.push({
|
377
|
+
fontFamily,
|
378
|
+
// !prevTest && test: just consequent
|
379
|
+
test: t.logicalExpression(
|
380
|
+
'&&',
|
381
|
+
t.unaryExpression('!', prev.test),
|
382
|
+
ternary.test
|
383
|
+
),
|
384
|
+
consequent: ternary.consequent,
|
385
|
+
alternate: null,
|
386
|
+
remove,
|
383
387
|
})
|
384
|
-
expr = t.callExpression(t.identifier('concatClassName'), [
|
385
|
-
expr,
|
386
|
-
...simpleSpreads.map((val) => val.value['argument']),
|
387
|
-
])
|
388
388
|
}
|
389
389
|
}
|
390
390
|
|
391
|
-
|
392
|
-
|
393
|
-
|
391
|
+
if (ternary.alternate && Object.keys(ternary.alternate).length) {
|
392
|
+
const fontFamily = getFontFamilyNameFromProps(ternary.alternate)
|
393
|
+
const negated = t.unaryExpression('!', ternary.test)
|
394
|
+
expandedTernaries.push({
|
395
|
+
fontFamily,
|
396
|
+
// prevTest && !test: merge alternate
|
397
|
+
test: prev ? t.logicalExpression('&&', prev.test, negated) : negated,
|
398
|
+
consequent: prev
|
399
|
+
? mergeProps(prev.alternate!, ternary.alternate)
|
400
|
+
: ternary.alternate,
|
401
|
+
remove,
|
402
|
+
alternate: null,
|
403
|
+
})
|
404
|
+
|
405
|
+
if (prev) {
|
406
|
+
expandedTernaries.push({
|
407
|
+
fontFamily,
|
408
|
+
test: t.logicalExpression(
|
409
|
+
'&&',
|
410
|
+
t.unaryExpression('!', prev.test),
|
411
|
+
ternary.test
|
412
|
+
),
|
413
|
+
consequent: ternary.alternate,
|
414
|
+
remove,
|
415
|
+
alternate: null,
|
416
|
+
})
|
417
|
+
}
|
418
|
+
}
|
394
419
|
}
|
395
420
|
|
396
|
-
|
397
|
-
'/* %s:%s (%s) */',
|
398
|
-
filePath,
|
399
|
-
lineNumbers,
|
400
|
-
originalNodeName
|
401
|
-
)
|
421
|
+
let ternaryClassNameExpr: t.Expression | null = null
|
402
422
|
|
403
|
-
|
404
|
-
|
405
|
-
const
|
406
|
-
|
407
|
-
|
408
|
-
if (
|
409
|
-
|
410
|
-
|
411
|
-
|
423
|
+
// next: create all CSS, build className strings and hoist, and create final node with props
|
424
|
+
if (hasTernaries) {
|
425
|
+
for (const ternary of expandedTernaries) {
|
426
|
+
if (!ternary.consequent) continue
|
427
|
+
const classNames = addStyles(ternary.consequent)
|
428
|
+
if (ternary.fontFamily) {
|
429
|
+
classNames.unshift(`font_${ternary.fontFamily}`)
|
430
|
+
}
|
431
|
+
const baseString = t.isStringLiteral(baseClassNameExpression)
|
432
|
+
? baseClassNameExpression.value
|
433
|
+
: ''
|
434
|
+
|
435
|
+
// we concat here as the base could be conditionally overriden by our classNames
|
436
|
+
const fullClassName = concatClassName(
|
437
|
+
(baseString ? `${baseString} ` : '') + classNames.join(' ')
|
438
|
+
)
|
439
|
+
|
440
|
+
const classNameLiteral = t.stringLiteral(fullClassName)
|
441
|
+
|
442
|
+
if (!ternaryClassNameExpr) {
|
443
|
+
ternaryClassNameExpr = t.conditionalExpression(
|
444
|
+
ternary.test,
|
445
|
+
classNameLiteral,
|
446
|
+
baseClassNameExpression
|
447
|
+
)
|
448
|
+
} else {
|
449
|
+
ternaryClassNameExpr = t.conditionalExpression(
|
450
|
+
ternary.test,
|
451
|
+
classNameLiteral,
|
452
|
+
ternaryClassNameExpr
|
453
|
+
)
|
412
454
|
}
|
413
|
-
} else if (rules.length) {
|
414
|
-
cssMap.set(className, {
|
415
|
-
css: rules.join('\n'),
|
416
|
-
commentTexts: [comment],
|
417
|
-
})
|
418
455
|
}
|
419
456
|
}
|
457
|
+
|
458
|
+
let finalExpression: t.Expression | null =
|
459
|
+
ternaryClassNameExpr || baseClassNameExpression || null
|
460
|
+
|
461
|
+
// console.info('attrs', JSON.stringify(attrs, null, 2))
|
462
|
+
// console.info('expandedTernaries', JSON.stringify(expandedTernaries, null, 2))
|
463
|
+
// console.info('finalExpression', JSON.stringify(finalExpression, null, 2))
|
464
|
+
// console.info({ baseClassNameExpression })
|
465
|
+
|
466
|
+
if (finalExpression) {
|
467
|
+
// hoist to global variables
|
468
|
+
finalExpression = hoistClassNames(jsxPath, finalExpression)
|
469
|
+
|
470
|
+
// console.log('finalExpression', finalExpression)
|
471
|
+
|
472
|
+
const classNameProp = t.jsxAttribute(
|
473
|
+
t.jsxIdentifier('className'),
|
474
|
+
t.jsxExpressionContainer(finalExpression!)
|
475
|
+
)
|
476
|
+
finalAttrs.unshift(classNameProp)
|
477
|
+
}
|
478
|
+
|
479
|
+
node.attributes = finalAttrs
|
420
480
|
},
|
421
481
|
})
|
422
482
|
|
@@ -466,3 +526,56 @@ export async function extractToClassNames({
|
|
466
526
|
map: result.map,
|
467
527
|
}
|
468
528
|
}
|
529
|
+
|
530
|
+
function hoistClassNames(path: NodePath<t.JSXElement>, expr: t.Expression) {
|
531
|
+
if (t.isStringLiteral(expr)) {
|
532
|
+
return hoistClassName(path, expr.value)
|
533
|
+
}
|
534
|
+
|
535
|
+
if (t.isLogicalExpression(expr)) {
|
536
|
+
const left = t.isStringLiteral(expr.left)
|
537
|
+
? hoistClassName(path, expr.left.value)
|
538
|
+
: expr.left
|
539
|
+
const right = t.isStringLiteral(expr.right)
|
540
|
+
? hoistClassName(path, expr.right.value)
|
541
|
+
: hoistClassNames(path, expr.right)
|
542
|
+
return t.logicalExpression(expr.operator, left, right)
|
543
|
+
}
|
544
|
+
|
545
|
+
if (t.isConditionalExpression(expr)) {
|
546
|
+
const cons = t.isStringLiteral(expr.consequent)
|
547
|
+
? hoistClassName(path, expr.consequent.value)
|
548
|
+
: hoistClassNames(path, expr.consequent)
|
549
|
+
|
550
|
+
const alt = t.isStringLiteral(expr.alternate)
|
551
|
+
? hoistClassName(path, expr.alternate.value)
|
552
|
+
: hoistClassNames(path, expr.alternate)
|
553
|
+
|
554
|
+
return t.conditionalExpression(expr.test, cons, alt)
|
555
|
+
}
|
556
|
+
|
557
|
+
return expr
|
558
|
+
}
|
559
|
+
|
560
|
+
function hoistClassName(path: NodePath<t.JSXElement>, str: string) {
|
561
|
+
const uid = path.scope.generateUidIdentifier('cn')
|
562
|
+
const parent = path.findParent((path) => path.isProgram())
|
563
|
+
if (!parent) throw new Error(`no program?`)
|
564
|
+
const variable = t.variableDeclaration('const', [
|
565
|
+
t.variableDeclarator(uid, t.stringLiteral(cleanupClassName(str))),
|
566
|
+
])
|
567
|
+
// @ts-ignore
|
568
|
+
parent.unshiftContainer('body', variable)
|
569
|
+
return uid
|
570
|
+
}
|
571
|
+
|
572
|
+
function cleanupClassName(inStr: string) {
|
573
|
+
const out = new Set<string>()
|
574
|
+
for (const part of inStr.split(' ')) {
|
575
|
+
if (part === ' ') continue
|
576
|
+
if (part === 'font_') continue
|
577
|
+
out.add(part)
|
578
|
+
}
|
579
|
+
// always a space after for joining
|
580
|
+
return [...out].join(' ') + ' '
|
581
|
+
}
|