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