@tamagui/static 3.0.0-beta.924.1 → 3.0.1
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/compilerHost.cjs +241 -75
- package/dist/cjs/exports.cjs +5 -1
- package/dist/cjs/extractor/bundle.cjs +14 -9
- package/dist/cjs/extractor/bundleConfig.cjs +0 -1
- package/dist/cjs/extractor/esbuildTsconfigPaths.cjs +81 -45
- package/dist/cjs/extractor/generateConfigMarkdown.cjs +492 -0
- package/dist/cjs/extractor/loadTamagui.cjs +28 -20
- package/dist/cjs/extractor/regenerateConfig.cjs +30 -20
- package/dist/cjs/zero/islands.cjs +2 -1
- package/dist/cjs/zero/theme.cjs +6 -3
- package/dist/esm/compilerHost.mjs +242 -76
- package/dist/esm/compilerHost.mjs.map +1 -1
- package/dist/esm/exports.mjs +3 -1
- package/dist/esm/extractor/bundle.mjs +14 -9
- package/dist/esm/extractor/bundle.mjs.map +1 -1
- package/dist/esm/extractor/bundleConfig.mjs +0 -1
- package/dist/esm/extractor/bundleConfig.mjs.map +1 -1
- package/dist/esm/extractor/esbuildTsconfigPaths.mjs +80 -45
- package/dist/esm/extractor/esbuildTsconfigPaths.mjs.map +1 -1
- package/dist/esm/extractor/generateConfigMarkdown.mjs +472 -0
- package/dist/esm/extractor/generateConfigMarkdown.mjs.map +1 -0
- package/dist/esm/extractor/loadTamagui.mjs +28 -20
- package/dist/esm/extractor/loadTamagui.mjs.map +1 -1
- package/dist/esm/extractor/regenerateConfig.mjs +30 -20
- package/dist/esm/extractor/regenerateConfig.mjs.map +1 -1
- package/dist/esm/zero/islands.mjs +2 -1
- package/dist/esm/zero/islands.mjs.map +1 -1
- package/dist/esm/zero/theme.mjs +7 -4
- package/dist/esm/zero/theme.mjs.map +1 -1
- package/package.json +23 -20
- package/src/compilerHost.ts +387 -160
- package/src/exports.ts +3 -0
- package/src/extractor/bundle.ts +19 -7
- package/src/extractor/bundleConfig.ts +0 -1
- package/src/extractor/esbuildTsconfigPaths.ts +106 -78
- package/src/extractor/generateConfigMarkdown.ts +610 -0
- package/src/extractor/loadTamagui.ts +55 -24
- package/src/extractor/regenerateConfig.ts +36 -35
- package/src/zero/islands.ts +2 -1
- package/src/zero/options.ts +8 -3
- package/src/zero/theme.ts +29 -4
- package/types/compilerHost.d.ts.map +1 -1
- package/types/exports.d.ts +2 -0
- package/types/exports.d.ts.map +1 -1
- package/types/extractor/bundle.d.ts.map +1 -1
- package/types/extractor/bundleConfig.d.ts.map +1 -1
- package/types/extractor/esbuildTsconfigPaths.d.ts +5 -7
- package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -1
- package/types/extractor/generateConfigMarkdown.d.ts +5 -0
- package/types/extractor/generateConfigMarkdown.d.ts.map +1 -0
- package/types/extractor/loadTamagui.d.ts +13 -1
- package/types/extractor/loadTamagui.d.ts.map +1 -1
- package/types/extractor/regenerateConfig.d.ts.map +1 -1
- package/types/zero/identity.d.ts +1 -1
- package/types/zero/islands.d.ts.map +1 -1
- package/types/zero/options.d.ts +8 -3
- package/types/zero/options.d.ts.map +1 -1
- package/types/zero/theme.d.ts +2 -0
- package/types/zero/theme.d.ts.map +1 -1
package/src/compilerHost.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
BranchDecisionNode,
|
|
3
3
|
CompilerLoweringHost,
|
|
4
4
|
CompilerTarget,
|
|
5
|
+
DynamicEvaluation,
|
|
5
6
|
LoweringCandidateInput,
|
|
6
7
|
LoweringCandidateResult,
|
|
7
8
|
LoweringComponent,
|
|
@@ -27,7 +28,6 @@ import {
|
|
|
27
28
|
} from '@tamagui/helpers'
|
|
28
29
|
import {
|
|
29
30
|
ATTRIBUTES,
|
|
30
|
-
DISPLAY_WEB_RESET,
|
|
31
31
|
EVENTS,
|
|
32
32
|
NATIVE_BACKING,
|
|
33
33
|
NATIVE_BLOCK_DEFAULTS,
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
TAG_WEB_DEFAULTS,
|
|
40
40
|
type TagName,
|
|
41
41
|
} from '@tamagui/dom'
|
|
42
|
+
import { resolveTransition, toCSSTransition } from '@tamagui/animation-helpers'
|
|
42
43
|
import {
|
|
43
44
|
createModifierRegistry,
|
|
44
45
|
parseTransition,
|
|
@@ -139,14 +140,6 @@ function componentKey(resolvedId: string, exportName: string): string {
|
|
|
139
140
|
return `${resolvedId}#${exportName}`
|
|
140
141
|
}
|
|
141
142
|
|
|
142
|
-
function cssFromRules(rules: Record<string, any>): string[] {
|
|
143
|
-
return Object.values(rules).flatMap((styleObject: any) => {
|
|
144
|
-
const identifier = styleObject?.[StyleObjectIdentifier]
|
|
145
|
-
const styleRules = styleObject?.[StyleObjectRules]
|
|
146
|
-
return identifier && Array.isArray(styleRules) ? styleRules : []
|
|
147
|
-
})
|
|
148
|
-
}
|
|
149
|
-
|
|
150
143
|
function jsxClassName(value: string): string {
|
|
151
144
|
return `className=${JSON.stringify(value)}`
|
|
152
145
|
}
|
|
@@ -214,7 +207,16 @@ function extractedStyleArtifacts(
|
|
|
214
207
|
? viewClassName.slice(0, -callerClassName.length).trimEnd()
|
|
215
208
|
: viewClassName
|
|
216
209
|
const rules = split.rulesToInsert ?? {}
|
|
217
|
-
const mediaNames =
|
|
210
|
+
const mediaNames = Object.keys(config.media ?? {})
|
|
211
|
+
const rulesByIdentifier = new Map<string, string[]>()
|
|
212
|
+
for (const styleObject of Object.values(rules) as any[]) {
|
|
213
|
+
const identifier = styleObject?.[StyleObjectIdentifier]
|
|
214
|
+
const styleRules = styleObject?.[StyleObjectRules]
|
|
215
|
+
if (typeof identifier !== 'string' || !Array.isArray(styleRules)) continue
|
|
216
|
+
const existing = rulesByIdentifier.get(identifier)
|
|
217
|
+
if (existing) existing.push(...styleRules)
|
|
218
|
+
else rulesByIdentifier.set(identifier, [...styleRules])
|
|
219
|
+
}
|
|
218
220
|
const pseudoNames = [
|
|
219
221
|
'hover',
|
|
220
222
|
'press',
|
|
@@ -243,21 +245,14 @@ function extractedStyleArtifacts(
|
|
|
243
245
|
])
|
|
244
246
|
for (const identifier of identifiers) {
|
|
245
247
|
const key = classKeys.get(identifier) ?? ''
|
|
246
|
-
const css =
|
|
247
|
-
Object.fromEntries(
|
|
248
|
-
Object.entries(rules).filter(
|
|
249
|
-
([, styleObject]: [string, any]) =>
|
|
250
|
-
styleObject?.[StyleObjectIdentifier] === identifier
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
).join('')
|
|
248
|
+
const css = (rulesByIdentifier.get(identifier) ?? []).join('')
|
|
254
249
|
if (identifier.startsWith('t_group_')) {
|
|
255
250
|
buckets.group.push(identifier)
|
|
256
251
|
} else if (pseudoNames.some((name) => key.endsWith(`-${name}`))) {
|
|
257
252
|
buckets.pseudo.push(identifier)
|
|
258
253
|
} else if (css.includes('.t_')) {
|
|
259
254
|
buckets.theme.push(identifier)
|
|
260
|
-
} else if (
|
|
255
|
+
} else if (mediaNames.some((name) => key.endsWith(`-${name}`))) {
|
|
261
256
|
buckets.media.push(identifier)
|
|
262
257
|
} else {
|
|
263
258
|
buckets.normal.push(identifier)
|
|
@@ -278,14 +273,7 @@ function extractedStyleArtifacts(
|
|
|
278
273
|
.join(' ')
|
|
279
274
|
: ''
|
|
280
275
|
const css = orderedIdentifiers.flatMap((identifier) => {
|
|
281
|
-
const identifierRules =
|
|
282
|
-
Object.fromEntries(
|
|
283
|
-
Object.entries(rules).filter(
|
|
284
|
-
([, styleObject]: [string, any]) =>
|
|
285
|
-
styleObject?.[StyleObjectIdentifier] === identifier
|
|
286
|
-
)
|
|
287
|
-
)
|
|
288
|
-
)
|
|
276
|
+
const identifierRules = rulesByIdentifier.get(identifier) ?? []
|
|
289
277
|
// The legacy static theme-block path adds one root specificity level after
|
|
290
278
|
// resolving tokens. Keep that output contract until the legacy oracle is removed.
|
|
291
279
|
return buckets.theme.includes(identifier)
|
|
@@ -403,7 +391,6 @@ const compilerStyleProps = new Set([
|
|
|
403
391
|
'group',
|
|
404
392
|
'transition',
|
|
405
393
|
'animation',
|
|
406
|
-
'animateOnly',
|
|
407
394
|
'animatePresence',
|
|
408
395
|
'animatedBy',
|
|
409
396
|
'fontFamily',
|
|
@@ -413,7 +400,6 @@ const compilerStyleProps = new Set([
|
|
|
413
400
|
const runtimeAnimationProps = new Set([
|
|
414
401
|
'transition',
|
|
415
402
|
'animation',
|
|
416
|
-
'animateOnly',
|
|
417
403
|
'animatePresence',
|
|
418
404
|
'animatedBy',
|
|
419
405
|
])
|
|
@@ -701,6 +687,61 @@ const nativePointerEventProps = new Set([
|
|
|
701
687
|
'onPointerUp',
|
|
702
688
|
])
|
|
703
689
|
|
|
690
|
+
// expandStyle writes every shorthand as four longhands, and fabric pays per prop
|
|
691
|
+
// on mount (200 views: 8.8ms as longhands, 5.7ms as shorthands). when a hoisted
|
|
692
|
+
// static style holds all four sides equal and no other key of that family, the
|
|
693
|
+
// shorthand resolves identically in yoga, and any longhand in a later style
|
|
694
|
+
// object still overrides it
|
|
695
|
+
const nativeShorthandFamilies: [string, string[], RegExp][] = [
|
|
696
|
+
['padding', ['paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft'], /^padding/],
|
|
697
|
+
['margin', ['marginTop', 'marginRight', 'marginBottom', 'marginLeft'], /^margin/],
|
|
698
|
+
[
|
|
699
|
+
'borderWidth',
|
|
700
|
+
['borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth'],
|
|
701
|
+
/^border.*Width$/,
|
|
702
|
+
],
|
|
703
|
+
[
|
|
704
|
+
'borderColor',
|
|
705
|
+
['borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor'],
|
|
706
|
+
/^border.*Color$/,
|
|
707
|
+
],
|
|
708
|
+
[
|
|
709
|
+
'borderRadius',
|
|
710
|
+
[
|
|
711
|
+
'borderTopLeftRadius',
|
|
712
|
+
'borderTopRightRadius',
|
|
713
|
+
'borderBottomRightRadius',
|
|
714
|
+
'borderBottomLeftRadius',
|
|
715
|
+
],
|
|
716
|
+
/^border.*Radius$/,
|
|
717
|
+
],
|
|
718
|
+
]
|
|
719
|
+
|
|
720
|
+
function collapseNativeShorthands(style: unknown) {
|
|
721
|
+
if (!staticObject(style)) return style
|
|
722
|
+
const keys = Object.keys(style)
|
|
723
|
+
const collapsed = new Map<string, string>()
|
|
724
|
+
for (const [shorthand, sides, family] of nativeShorthandFamilies) {
|
|
725
|
+
const value = style[sides[0]]
|
|
726
|
+
if (
|
|
727
|
+
value !== undefined &&
|
|
728
|
+
!(shorthand in style) &&
|
|
729
|
+
sides.every((side) => style[side] === value) &&
|
|
730
|
+
keys.filter((key) => family.test(key)).length === sides.length
|
|
731
|
+
) {
|
|
732
|
+
for (const side of sides) collapsed.set(side, shorthand)
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (!collapsed.size) return style
|
|
736
|
+
const out: Record<string, unknown> = {}
|
|
737
|
+
for (const key of keys) {
|
|
738
|
+
const shorthand = collapsed.get(key)
|
|
739
|
+
if (!shorthand) out[key] = style[key]
|
|
740
|
+
else if (!(shorthand in out)) out[shorthand] = style[key]
|
|
741
|
+
}
|
|
742
|
+
return out
|
|
743
|
+
}
|
|
744
|
+
|
|
704
745
|
function isSerializableNativeStyle(value: unknown): boolean {
|
|
705
746
|
if (value == null || typeof value === 'number' || typeof value === 'boolean') {
|
|
706
747
|
return true
|
|
@@ -713,6 +754,61 @@ function isSerializableNativeStyle(value: unknown): boolean {
|
|
|
713
754
|
return Object.values(value).every(isSerializableNativeStyle)
|
|
714
755
|
}
|
|
715
756
|
|
|
757
|
+
/**
|
|
758
|
+
* native host styles contain only completed pixel metrics. this compact record
|
|
759
|
+
* retains the authored text metric long enough for a DOM primitive or runtime
|
|
760
|
+
* Text descendant to apply it against its effective font size.
|
|
761
|
+
*/
|
|
762
|
+
type NativeTextMetrics = {
|
|
763
|
+
fontSize?: number
|
|
764
|
+
lineHeight?: number | `${number}px` | 'normal'
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function nativeTextMetrics(result: unknown): NativeTextMetrics | null {
|
|
768
|
+
const value = (result as { nativeTextMetrics?: unknown } | null)?.nativeTextMetrics
|
|
769
|
+
if (!staticObject(value)) return null
|
|
770
|
+
const { fontSize, lineHeight } = value
|
|
771
|
+
if (
|
|
772
|
+
(fontSize !== undefined && typeof fontSize !== 'number') ||
|
|
773
|
+
(lineHeight !== undefined &&
|
|
774
|
+
typeof lineHeight !== 'number' &&
|
|
775
|
+
typeof lineHeight !== 'string')
|
|
776
|
+
) {
|
|
777
|
+
return null
|
|
778
|
+
}
|
|
779
|
+
return fontSize === undefined && lineHeight === undefined
|
|
780
|
+
? null
|
|
781
|
+
: (value as NativeTextMetrics)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function mergeNativeTextMetrics(
|
|
785
|
+
current: NativeTextMetrics | null,
|
|
786
|
+
next: NativeTextMetrics | null
|
|
787
|
+
): NativeTextMetrics | null {
|
|
788
|
+
if (!current) return next
|
|
789
|
+
if (!next) return current
|
|
790
|
+
return {
|
|
791
|
+
fontSize: next.fontSize ?? current.fontSize,
|
|
792
|
+
lineHeight: next.lineHeight ?? current.lineHeight,
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function hasChildThatMayRenderReactNode(element: MaterializedElement): boolean {
|
|
797
|
+
return element.entries.some((entry) => {
|
|
798
|
+
if (entry.kind !== 'child' || entry.value.kind === 'empty') return false
|
|
799
|
+
if (entry.value.kind === 'element') return true
|
|
800
|
+
if (entry.value.kind === 'bailout') return entry.value.dynamic === null
|
|
801
|
+
if (entry.value.kind === 'conditional') {
|
|
802
|
+
return collectLeaves(entry.value.tree).some(
|
|
803
|
+
({ value }) =>
|
|
804
|
+
value === undefined || (value !== null && typeof value === 'object')
|
|
805
|
+
)
|
|
806
|
+
}
|
|
807
|
+
if (entry.value.kind !== 'static') return true
|
|
808
|
+
return entry.value.value !== null && typeof entry.value.value === 'object'
|
|
809
|
+
})
|
|
810
|
+
}
|
|
811
|
+
|
|
716
812
|
function unusedIdentifier(source: string, base: string): string {
|
|
717
813
|
let candidate = base
|
|
718
814
|
let suffix = 0
|
|
@@ -1051,6 +1147,7 @@ export function createTamaguiCompilerHost(
|
|
|
1051
1147
|
!options.tamaguiConfig.animationDrivers
|
|
1052
1148
|
? configuredAnimationDriver
|
|
1053
1149
|
: null
|
|
1150
|
+
const nativeTextMetricsByElement = new WeakMap<MaterializedElement, NativeTextMetrics>()
|
|
1054
1151
|
const resolveStaticCssTransition = (
|
|
1055
1152
|
value: unknown,
|
|
1056
1153
|
transitionPresets: Record<string, unknown>
|
|
@@ -1068,32 +1165,23 @@ export function createTamaguiCompilerHost(
|
|
|
1068
1165
|
for (const payload of payloads) {
|
|
1069
1166
|
const transition = parseTransition(payload, transitionPresetNames)
|
|
1070
1167
|
if (!transition.ok) return null
|
|
1071
|
-
if (
|
|
1072
|
-
transition.value.kind === 'global' ||
|
|
1073
|
-
transition.value.entries.every((entry) => entry.timing.type === 'css')
|
|
1074
|
-
) {
|
|
1168
|
+
if (transition.value.kind === 'global') {
|
|
1075
1169
|
resolvedPayloads.push(payload)
|
|
1076
1170
|
continue
|
|
1077
1171
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
if (
|
|
1088
|
-
!parsedPreset.ok ||
|
|
1089
|
-
parsedPreset.value.kind !== 'transition' ||
|
|
1090
|
-
parsedPreset.value.entries.length !== 1 ||
|
|
1091
|
-
parsedPreset.value.entries[0]!.property !== 'all' ||
|
|
1092
|
-
parsedPreset.value.entries[0]!.timing.type !== 'css'
|
|
1093
|
-
) {
|
|
1094
|
-
return null
|
|
1172
|
+
// the runtime's own resolver, so a preset lowers to exactly the css the
|
|
1173
|
+
// css driver would have emitted. a preset of any shape works, springs
|
|
1174
|
+
// included: they become a `linear()` easing here rather than forcing the
|
|
1175
|
+
// element onto the runtime path.
|
|
1176
|
+
const resolved = resolveTransition(payload, { animations: transitionPresets })
|
|
1177
|
+
if (resolved.diagnostics.length > 0) return null
|
|
1178
|
+
if (!resolved.fused) {
|
|
1179
|
+
resolvedPayloads.push(payload)
|
|
1180
|
+
continue
|
|
1095
1181
|
}
|
|
1096
|
-
|
|
1182
|
+
const css = toCSSTransition(resolved)
|
|
1183
|
+
if (css === undefined) return null
|
|
1184
|
+
resolvedPayloads.push(css)
|
|
1097
1185
|
}
|
|
1098
1186
|
let payloadIndex = 0
|
|
1099
1187
|
const resolved: string[] = []
|
|
@@ -1171,10 +1259,13 @@ export function createTamaguiCompilerHost(
|
|
|
1171
1259
|
| StaticConfig
|
|
1172
1260
|
| undefined
|
|
1173
1261
|
if (!base) return null
|
|
1262
|
+
// Web keeps only the semantic defaults as atomic defaults. Undoing the
|
|
1263
|
+
// browser stylesheet is the `:where(.is_DOM)` host reset, and the
|
|
1264
|
+
// extracted className below keeps the runtime base so compiled hosts
|
|
1265
|
+
// carry `is_DOM` for it to match.
|
|
1174
1266
|
const platformDefaults =
|
|
1175
1267
|
platform === 'web'
|
|
1176
1268
|
? {
|
|
1177
|
-
...DISPLAY_WEB_RESET[row.display],
|
|
1178
1269
|
...row.defaults,
|
|
1179
1270
|
...TAG_WEB_DEFAULTS[tag],
|
|
1180
1271
|
}
|
|
@@ -1190,6 +1281,7 @@ export function createTamaguiCompilerHost(
|
|
|
1190
1281
|
displayName: tag,
|
|
1191
1282
|
staticConfig: normalizeStaticConfig({
|
|
1192
1283
|
...base,
|
|
1284
|
+
isDOM: true,
|
|
1193
1285
|
isInput: row.backing === 'textinput',
|
|
1194
1286
|
// CSS text properties may be authored on any element and inherited by
|
|
1195
1287
|
// descendant text, including from a View-backed tag on native.
|
|
@@ -1284,8 +1376,11 @@ export function createTamaguiCompilerHost(
|
|
|
1284
1376
|
const defaultProps = resolved.staticConfig.defaultProps ?? {}
|
|
1285
1377
|
return {
|
|
1286
1378
|
key: resolved.key,
|
|
1379
|
+
// a HOC (themed icons, createStyledHOC) renders a real component, so
|
|
1380
|
+
// flattening it to a host element drops everything it renders
|
|
1287
1381
|
canFlatten:
|
|
1288
1382
|
resolved.staticConfig.acceptsClassName !== false &&
|
|
1383
|
+
!resolved.staticConfig.isHOC &&
|
|
1289
1384
|
!resolved.staticConfig.neverFlatten &&
|
|
1290
1385
|
!resolved.staticConfig.context,
|
|
1291
1386
|
staticConfig: resolved.staticConfig,
|
|
@@ -1324,7 +1419,6 @@ export function createTamaguiCompilerHost(
|
|
|
1324
1419
|
'group',
|
|
1325
1420
|
'transition',
|
|
1326
1421
|
'animation',
|
|
1327
|
-
'animateOnly',
|
|
1328
1422
|
'animatePresence',
|
|
1329
1423
|
'animatedBy',
|
|
1330
1424
|
'render',
|
|
@@ -1357,6 +1451,21 @@ export function createTamaguiCompilerHost(
|
|
|
1357
1451
|
if (dynamic?.type !== 'string' || !dynamic.values?.length) return null
|
|
1358
1452
|
return dynamic.values.map(String)
|
|
1359
1453
|
}
|
|
1454
|
+
// an opaque native value the compiler can still lower: a finite domain
|
|
1455
|
+
// (a static array member, a template over one) resolves each value like a
|
|
1456
|
+
// conditional branch and selects it by the runtime value; an unbounded
|
|
1457
|
+
// number passes straight into the style key once probing proves the prop
|
|
1458
|
+
// maps numbers to that one key unchanged
|
|
1459
|
+
const nativeDynamicShape = (
|
|
1460
|
+
name: string,
|
|
1461
|
+
component: LoweringComponent,
|
|
1462
|
+
dynamic: DynamicEvaluation | null | undefined
|
|
1463
|
+
): 'domain' | 'number' | null => {
|
|
1464
|
+
if (!dynamic || !canLowerConditionalStyleProp(name, component)) return null
|
|
1465
|
+
if (dynamic.values?.length) return 'domain'
|
|
1466
|
+
if (dynamic.type === 'number' && directStyleName(name, component)) return 'number'
|
|
1467
|
+
return null
|
|
1468
|
+
}
|
|
1360
1469
|
type DynamicStyleEntry = Extract<
|
|
1361
1470
|
MaterializedElement['entries'][number],
|
|
1362
1471
|
{ kind: 'prop' }
|
|
@@ -1373,11 +1482,17 @@ export function createTamaguiCompilerHost(
|
|
|
1373
1482
|
canLowerConditionalStyleProp(entry.name, component)) ||
|
|
1374
1483
|
frontendClassDomain(entry, component) !== null
|
|
1375
1484
|
|
|
1485
|
+
const expandStyleShorthand = (name: string): string =>
|
|
1486
|
+
options.tamaguiConfig.shorthands?.[name] ?? name
|
|
1487
|
+
|
|
1376
1488
|
const isInvalidHostStyleProp = (
|
|
1377
1489
|
name: string,
|
|
1378
1490
|
component: LoweringComponent
|
|
1379
1491
|
): boolean => {
|
|
1492
|
+
const expanded = expandStyleShorthand(name)
|
|
1380
1493
|
const staticConfig = component.staticConfig as StaticConfig
|
|
1494
|
+
// explicit variants and inline props retain their component owners.
|
|
1495
|
+
if (staticConfig.inlineProps?.has(name) || staticConfig.variants?.[name]) return false
|
|
1381
1496
|
const validStyles =
|
|
1382
1497
|
staticConfig.validStyles ||
|
|
1383
1498
|
(staticConfig.isInput
|
|
@@ -1385,7 +1500,7 @@ export function createTamaguiCompilerHost(
|
|
|
1385
1500
|
: staticConfig.isText
|
|
1386
1501
|
? stylePropsText
|
|
1387
1502
|
: validStylesView)
|
|
1388
|
-
return
|
|
1503
|
+
return expanded in stylePropsAll && !isValidStyleKey(expanded, validStyles)
|
|
1389
1504
|
}
|
|
1390
1505
|
|
|
1391
1506
|
const directStyleName = (name: string, component: LoweringComponent): string | null => {
|
|
@@ -1394,7 +1509,7 @@ export function createTamaguiCompilerHost(
|
|
|
1394
1509
|
}
|
|
1395
1510
|
const staticConfig = component.staticConfig as StaticConfig
|
|
1396
1511
|
if (staticConfig.variants?.[name]) return null
|
|
1397
|
-
const expanded =
|
|
1512
|
+
const expanded = expandStyleShorthand(name)
|
|
1398
1513
|
return staticConfig.validStyles?.[expanded] ? expanded : null
|
|
1399
1514
|
}
|
|
1400
1515
|
|
|
@@ -1620,7 +1735,7 @@ export function createTamaguiCompilerHost(
|
|
|
1620
1735
|
return {
|
|
1621
1736
|
resolveComponent: resolve,
|
|
1622
1737
|
isStyleProp,
|
|
1623
|
-
canLowerDynamicStyleProp(name, component, valueKind) {
|
|
1738
|
+
canLowerDynamicStyleProp(name, component, valueKind, dynamic) {
|
|
1624
1739
|
// a conditional with static branches lowers per-branch on both
|
|
1625
1740
|
// platforms: each side resolves at compile time, only the test survives
|
|
1626
1741
|
if (
|
|
@@ -1646,7 +1761,10 @@ export function createTamaguiCompilerHost(
|
|
|
1646
1761
|
return (
|
|
1647
1762
|
!options.disablePartialExtraction &&
|
|
1648
1763
|
((platform === 'web' && !!directStyleName(name, component)) ||
|
|
1649
|
-
(platform === 'native' &&
|
|
1764
|
+
(platform === 'native' &&
|
|
1765
|
+
(directStyleName(name, component) === 'opacity' ||
|
|
1766
|
+
(valueKind === 'bailout' &&
|
|
1767
|
+
nativeDynamicShape(name, component, dynamic) !== null))))
|
|
1650
1768
|
)
|
|
1651
1769
|
},
|
|
1652
1770
|
developmentDebugInstrumentation,
|
|
@@ -1825,8 +1943,12 @@ export function createTamaguiCompilerHost(
|
|
|
1825
1943
|
// DOM primitives (html.*) keep their own event contract (see
|
|
1826
1944
|
// @tamagui/dom events table) — only bail for tamagui components,
|
|
1827
1945
|
// whose pointer handlers exist solely via usePointerEvents.
|
|
1946
|
+
// onLayout on web exists only through the runtime's
|
|
1947
|
+
// useElementLayout; a flattened div drops it. a flattened native
|
|
1948
|
+
// view reports layout itself.
|
|
1828
1949
|
const needsRuntimeMapping = (name: string) =>
|
|
1829
1950
|
runtimeEventProps.has(name) ||
|
|
1951
|
+
(platform === 'web' && name === 'onLayout') ||
|
|
1830
1952
|
(platform === 'native' &&
|
|
1831
1953
|
!component.domTag &&
|
|
1832
1954
|
nativePointerEventProps.has(name))
|
|
@@ -1848,7 +1970,7 @@ export function createTamaguiCompilerHost(
|
|
|
1848
1970
|
// A styled() definition's animation props decide the same things the call
|
|
1849
1971
|
// site's do, so the lowering decision reads both. completeProps merges
|
|
1850
1972
|
// them far below, long after this point, and reading only the call site
|
|
1851
|
-
// here decided a definition's transition/animation
|
|
1973
|
+
// here decided a definition's transition/animation props as if they
|
|
1852
1974
|
// had never been written: the element flattened and the prop was dropped
|
|
1853
1975
|
// with no diagnostic. Same merge function and same precedence as
|
|
1854
1976
|
// completeProps, so there is one answer to "what is this prop's value".
|
|
@@ -1864,27 +1986,6 @@ export function createTamaguiCompilerHost(
|
|
|
1864
1986
|
(name) => runtimeAnimationProps.has(name) && animationProps[name] !== undefined
|
|
1865
1987
|
),
|
|
1866
1988
|
])
|
|
1867
|
-
const animateOnlyEntry = input.element.entries.find(
|
|
1868
|
-
(entry) => entry.kind === 'prop' && entry.name === 'animateOnly'
|
|
1869
|
-
)
|
|
1870
|
-
if (animationNames.has('animateOnly')) {
|
|
1871
|
-
return bailout(
|
|
1872
|
-
input,
|
|
1873
|
-
'local/unsupported-target',
|
|
1874
|
-
'Animated candidates remain on the runtime path',
|
|
1875
|
-
animateOnlyEntry?.span,
|
|
1876
|
-
{
|
|
1877
|
-
rule: 5,
|
|
1878
|
-
message: zeroRuleMessage(5, {
|
|
1879
|
-
// without the origin an author reads this against JSX that does
|
|
1880
|
-
// not carry the prop, because the styled() definition does
|
|
1881
|
-
detail: animateOnlyEntry
|
|
1882
|
-
? `animateOnly on ${input.element.component.name}`
|
|
1883
|
-
: `animateOnly in the styled() definition of ${input.element.component.name}`,
|
|
1884
|
-
}),
|
|
1885
|
-
}
|
|
1886
|
-
)
|
|
1887
|
-
}
|
|
1888
1989
|
const transitionEntry = input.element.entries.find(
|
|
1889
1990
|
(entry) => entry.kind === 'prop' && entry.name === 'transition'
|
|
1890
1991
|
)
|
|
@@ -1936,8 +2037,7 @@ export function createTamaguiCompilerHost(
|
|
|
1936
2037
|
const runtimeAnimationRequired =
|
|
1937
2038
|
(animationNames.has('transition') && resolvedCssTransition === null) ||
|
|
1938
2039
|
[...animationNames].some(
|
|
1939
|
-
(name) =>
|
|
1940
|
-
name !== 'transition' && name !== 'animatedBy' && name !== 'animateOnly'
|
|
2040
|
+
(name) => name !== 'transition' && name !== 'animatedBy'
|
|
1941
2041
|
) ||
|
|
1942
2042
|
animatedByNeedsRuntime
|
|
1943
2043
|
let dynamicHostStyleProperties: string[] | null = null
|
|
@@ -2311,7 +2411,9 @@ export function createTamaguiCompilerHost(
|
|
|
2311
2411
|
entry.kind === 'prop' &&
|
|
2312
2412
|
(directStyleName(entry.name, component) === 'opacity' ||
|
|
2313
2413
|
(entry.value.kind === 'conditional' &&
|
|
2314
|
-
canLowerConditionalStyleProp(entry.name, component))
|
|
2414
|
+
canLowerConditionalStyleProp(entry.name, component)) ||
|
|
2415
|
+
(entry.value.kind === 'bailout' &&
|
|
2416
|
+
nativeDynamicShape(entry.name, component, entry.value.dynamic) !== null))
|
|
2315
2417
|
)
|
|
2316
2418
|
if (
|
|
2317
2419
|
dynamicStyleEntries.length > 0 &&
|
|
@@ -2491,6 +2593,52 @@ export function createTamaguiCompilerHost(
|
|
|
2491
2593
|
'getSplitStyles returned no static result'
|
|
2492
2594
|
)
|
|
2493
2595
|
}
|
|
2596
|
+
const nativeMetrics = platform === 'native' ? nativeTextMetrics(split) : null
|
|
2597
|
+
if (nativeMetrics) nativeTextMetricsByElement.set(input.element, nativeMetrics)
|
|
2598
|
+
const hasDynamicFontSize = dynamicStyleEntries.some((entry) => {
|
|
2599
|
+
const name = directStyleName(entry.name, component)
|
|
2600
|
+
return name === 'fontFamily' || name === 'fontSize'
|
|
2601
|
+
})
|
|
2602
|
+
const hasUnresolvedNativeLineHeight =
|
|
2603
|
+
typeof nativeMetrics?.lineHeight === 'number' &&
|
|
2604
|
+
nativeMetrics.fontSize === undefined &&
|
|
2605
|
+
!hasDynamicFontSize
|
|
2606
|
+
const hasDynamicTextMetric = dynamicStyleEntries.some((entry) => {
|
|
2607
|
+
const name = directStyleName(entry.name, component)
|
|
2608
|
+
return (
|
|
2609
|
+
name === 'fontFamily' ||
|
|
2610
|
+
name === 'fontSize' ||
|
|
2611
|
+
name === 'lineHeight' ||
|
|
2612
|
+
(name === null && !!component.staticConfig.variants?.[entry.name])
|
|
2613
|
+
)
|
|
2614
|
+
})
|
|
2615
|
+
const changesInheritedNativeFontSize =
|
|
2616
|
+
nativeMetrics?.fontSize !== undefined && nativeMetrics.lineHeight === undefined
|
|
2617
|
+
// flattened core Text goes straight to react-native Text, which cannot
|
|
2618
|
+
// carry this semantic record to a runtime child. the component path
|
|
2619
|
+
// completes it once through the shared metric context.
|
|
2620
|
+
if (
|
|
2621
|
+
platform === 'native' &&
|
|
2622
|
+
component.staticConfig.isText &&
|
|
2623
|
+
!component.domTag &&
|
|
2624
|
+
(hasUnresolvedNativeLineHeight ||
|
|
2625
|
+
changesInheritedNativeFontSize ||
|
|
2626
|
+
(hasChildThatMayRenderReactNode(input.element) &&
|
|
2627
|
+
(nativeMetrics !== null || hasDynamicTextMetric)) ||
|
|
2628
|
+
input.module.elements.some(
|
|
2629
|
+
(ancestor) =>
|
|
2630
|
+
ancestor !== input.element &&
|
|
2631
|
+
ancestor.span.start < input.element.span.start &&
|
|
2632
|
+
ancestor.span.end > input.element.span.end &&
|
|
2633
|
+
nativeTextMetricsByElement.has(ancestor)
|
|
2634
|
+
))
|
|
2635
|
+
) {
|
|
2636
|
+
return bailout(
|
|
2637
|
+
input,
|
|
2638
|
+
'local/unsupported-target',
|
|
2639
|
+
'Native text metrics need the runtime Text provider for this subtree'
|
|
2640
|
+
)
|
|
2641
|
+
}
|
|
2494
2642
|
if (
|
|
2495
2643
|
split.programLifecycleStyleKeys?.enter?.size ||
|
|
2496
2644
|
split.programLifecycleStyleKeys?.exit?.size
|
|
@@ -2677,9 +2825,15 @@ export function createTamaguiCompilerHost(
|
|
|
2677
2825
|
input.source,
|
|
2678
2826
|
`__TamaguiNativeStyle${input.element.span.start}`
|
|
2679
2827
|
)
|
|
2828
|
+
// the native style engine pushes the runtime's exact keys, so its base keeps them
|
|
2829
|
+
const usesNativeFastPath =
|
|
2830
|
+
!!themedStyleKeys &&
|
|
2831
|
+
!!options.experimentalNativeFastPath &&
|
|
2832
|
+
dynamicStyleEntries.length === 0 &&
|
|
2833
|
+
!input.element.entries.some((entry) => entry.kind === 'spread')
|
|
2680
2834
|
const nativeStyleImports = [
|
|
2681
2835
|
{
|
|
2682
|
-
content: `\nfunction ${nativeStyleLocal}() { return ${nativeStyleLocal}._ ?? (${nativeStyleLocal}._ = ${JSON.stringify(nativeStyle ?? {})}); } ${nativeStyleLocal}();`,
|
|
2836
|
+
content: `\nfunction ${nativeStyleLocal}() { return ${nativeStyleLocal}._ ?? (${nativeStyleLocal}._ = ${JSON.stringify((usesNativeFastPath ? nativeStyle : collapseNativeShorthands(nativeStyle)) ?? {})}); } ${nativeStyleLocal}();`,
|
|
2683
2837
|
origin: input.element.component.span,
|
|
2684
2838
|
},
|
|
2685
2839
|
]
|
|
@@ -2690,12 +2844,7 @@ export function createTamaguiCompilerHost(
|
|
|
2690
2844
|
mappingLocal: string
|
|
2691
2845
|
}
|
|
2692
2846
|
| undefined
|
|
2693
|
-
if (
|
|
2694
|
-
themedStyleKeys &&
|
|
2695
|
-
options.experimentalNativeFastPath &&
|
|
2696
|
-
dynamicStyleEntries.length === 0 &&
|
|
2697
|
-
!input.element.entries.some((entry) => entry.kind === 'spread')
|
|
2698
|
-
) {
|
|
2847
|
+
if (usesNativeFastPath) {
|
|
2699
2848
|
const mappingLocal = unusedIdentifier(
|
|
2700
2849
|
input.source,
|
|
2701
2850
|
`__TamaguiNativeMapping${input.element.span.start}`
|
|
@@ -2732,6 +2881,7 @@ export function createTamaguiCompilerHost(
|
|
|
2732
2881
|
}
|
|
2733
2882
|
styleEntries = [...new Set([...styleEntries, ...propsResult.consumed])]
|
|
2734
2883
|
let nativeDOMStyleSource = nativeStyleSource
|
|
2884
|
+
let nativeDOMMetrics = nativeMetrics
|
|
2735
2885
|
let runtimeStyleSource: string | null = null
|
|
2736
2886
|
if (
|
|
2737
2887
|
domStyleProgram?.kind === 'prop' &&
|
|
@@ -2795,6 +2945,10 @@ export function createTamaguiCompilerHost(
|
|
|
2795
2945
|
item.value.span
|
|
2796
2946
|
)
|
|
2797
2947
|
}
|
|
2948
|
+
nativeDOMMetrics = mergeNativeTextMetrics(
|
|
2949
|
+
nativeDOMMetrics,
|
|
2950
|
+
nativeTextMetrics(itemSplit)
|
|
2951
|
+
)
|
|
2798
2952
|
value = JSON.stringify(itemStyle ?? {})
|
|
2799
2953
|
}
|
|
2800
2954
|
const condition = item.condition
|
|
@@ -2819,6 +2973,11 @@ export function createTamaguiCompilerHost(
|
|
|
2819
2973
|
? `__styles={${runtimeStyleSource}}`
|
|
2820
2974
|
: `__styles: ${runtimeStyleSource}`
|
|
2821
2975
|
: '',
|
|
2976
|
+
nativeDOMMetrics
|
|
2977
|
+
? serializedProps(input.element.form, [
|
|
2978
|
+
['__textMetrics', JSON.stringify(nativeDOMMetrics)],
|
|
2979
|
+
])
|
|
2980
|
+
: '',
|
|
2822
2981
|
serializedProps(input.element.form, propsResult.additions),
|
|
2823
2982
|
]
|
|
2824
2983
|
.filter(Boolean)
|
|
@@ -3061,7 +3220,77 @@ export function createTamaguiCompilerHost(
|
|
|
3061
3220
|
const baseStyleForDiff = staticObject(nativeStyleResolved)
|
|
3062
3221
|
? nativeStyleResolved
|
|
3063
3222
|
: {}
|
|
3223
|
+
// resolves the element with one value in place of the dynamic prop
|
|
3224
|
+
// and returns what that value adds over the static style
|
|
3225
|
+
const valueDiff = (
|
|
3226
|
+
entry: DynamicStyleEntry,
|
|
3227
|
+
value: unknown,
|
|
3228
|
+
what: string
|
|
3229
|
+
): Record<string, unknown> | string => {
|
|
3230
|
+
const { branchCompleteProps } = propsForConditional(entry, value)
|
|
3231
|
+
const branchSplit = resolveSplitStyles(
|
|
3232
|
+
branchCompleteProps,
|
|
3233
|
+
component.staticConfig,
|
|
3234
|
+
cssAnimationDriver,
|
|
3235
|
+
component.displayName
|
|
3236
|
+
)
|
|
3237
|
+
const branchStyle = branchSplit?.viewProps?.style ?? {}
|
|
3238
|
+
if (!staticObject(branchStyle)) {
|
|
3239
|
+
return `${what} did not resolve to a static native style`
|
|
3240
|
+
}
|
|
3241
|
+
// a value (e.g. through a variant) may change viewProps beyond
|
|
3242
|
+
// style; the style array cannot express those, so any non-style
|
|
3243
|
+
// difference sends the element to the runtime path
|
|
3244
|
+
for (const viewPropsKey of new Set([
|
|
3245
|
+
...Object.keys(branchSplit?.viewProps ?? {}),
|
|
3246
|
+
...Object.keys(split.viewProps ?? {}),
|
|
3247
|
+
])) {
|
|
3248
|
+
if (viewPropsKey === 'style') continue
|
|
3249
|
+
if (
|
|
3250
|
+
JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !==
|
|
3251
|
+
JSON.stringify((split.viewProps as any)?.[viewPropsKey])
|
|
3252
|
+
) {
|
|
3253
|
+
return `${what} changes ${viewPropsKey}, which the compiled style array cannot express`
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
for (const key of Object.keys(baseStyleForDiff)) {
|
|
3257
|
+
if (!(key in branchStyle)) {
|
|
3258
|
+
return `${what} removes ${key}, which an additive style array cannot express`
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
const diff: Record<string, unknown> = {}
|
|
3262
|
+
for (const [key, value] of Object.entries(branchStyle)) {
|
|
3263
|
+
if (
|
|
3264
|
+
JSON.stringify((baseStyleForDiff as any)[key]) !== JSON.stringify(value)
|
|
3265
|
+
) {
|
|
3266
|
+
diff[key] = value
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) {
|
|
3270
|
+
return `${what} style is not statically representable`
|
|
3271
|
+
}
|
|
3272
|
+
return diff
|
|
3273
|
+
}
|
|
3274
|
+
// each dynamic prop owns the keys it contributes; two props writing
|
|
3275
|
+
// one key would need their source order resolved at runtime
|
|
3276
|
+
const claimKeys = (entry: DynamicStyleEntry, keys: Iterable<string>) => {
|
|
3277
|
+
for (const key of keys) {
|
|
3278
|
+
if (conditionalKeys.has(key)) {
|
|
3279
|
+
return bailout(
|
|
3280
|
+
input,
|
|
3281
|
+
'local/dynamic-style-value',
|
|
3282
|
+
`Multiple dynamic props contribute ${key}; their interaction cannot be resolved at compile time`,
|
|
3283
|
+
entry.value.span
|
|
3284
|
+
)
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
return null
|
|
3288
|
+
}
|
|
3064
3289
|
for (const entry of dynamicStyleEntries) {
|
|
3290
|
+
const shape =
|
|
3291
|
+
entry.value.kind === 'bailout'
|
|
3292
|
+
? nativeDynamicShape(entry.name, component, entry.value.dynamic)
|
|
3293
|
+
: null
|
|
3065
3294
|
// opacity keeps the leaner inline-expression form even for
|
|
3066
3295
|
// ternaries; per-branch lowering is for props that form cannot carry
|
|
3067
3296
|
if (
|
|
@@ -3075,79 +3304,21 @@ export function createTamaguiCompilerHost(
|
|
|
3075
3304
|
Record<string, unknown>
|
|
3076
3305
|
>()
|
|
3077
3306
|
for (const leaf of leaves) {
|
|
3078
|
-
const
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
cssAnimationDriver,
|
|
3083
|
-
component.displayName
|
|
3307
|
+
const diff = valueDiff(
|
|
3308
|
+
entry,
|
|
3309
|
+
leaf.value,
|
|
3310
|
+
`Conditional ${entry.name} branch`
|
|
3084
3311
|
)
|
|
3085
|
-
|
|
3086
|
-
if (!staticObject(branchStyle)) {
|
|
3087
|
-
return bailout(
|
|
3088
|
-
input,
|
|
3089
|
-
'local/dynamic-style-value',
|
|
3090
|
-
`Conditional ${entry.name} branch did not resolve to a static native style`,
|
|
3091
|
-
entry.value.span
|
|
3092
|
-
)
|
|
3093
|
-
}
|
|
3094
|
-
// a branch (e.g. through a variant) may change viewProps
|
|
3095
|
-
// beyond style; the style array cannot express those, so any
|
|
3096
|
-
// non-style difference sends the element to the runtime path
|
|
3097
|
-
for (const viewPropsKey of new Set([
|
|
3098
|
-
...Object.keys(branchSplit?.viewProps ?? {}),
|
|
3099
|
-
...Object.keys(split.viewProps ?? {}),
|
|
3100
|
-
])) {
|
|
3101
|
-
if (viewPropsKey === 'style') continue
|
|
3102
|
-
if (
|
|
3103
|
-
JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !==
|
|
3104
|
-
JSON.stringify((split.viewProps as any)?.[viewPropsKey])
|
|
3105
|
-
) {
|
|
3106
|
-
return bailout(
|
|
3107
|
-
input,
|
|
3108
|
-
'local/dynamic-style-value',
|
|
3109
|
-
`Conditional ${entry.name} branch changes ${viewPropsKey}, which the compiled style array cannot express`,
|
|
3110
|
-
entry.value.span
|
|
3111
|
-
)
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
for (const key of Object.keys(baseStyleForDiff)) {
|
|
3115
|
-
if (!(key in branchStyle)) {
|
|
3116
|
-
return bailout(
|
|
3117
|
-
input,
|
|
3118
|
-
'local/dynamic-style-value',
|
|
3119
|
-
`Conditional ${entry.name} branch removes ${key}, which an additive style array cannot express`,
|
|
3120
|
-
entry.value.span
|
|
3121
|
-
)
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
|
-
const diff: Record<string, unknown> = {}
|
|
3125
|
-
for (const [key, value] of Object.entries(branchStyle)) {
|
|
3126
|
-
if (
|
|
3127
|
-
JSON.stringify((baseStyleForDiff as any)[key]) !==
|
|
3128
|
-
JSON.stringify(value)
|
|
3129
|
-
) {
|
|
3130
|
-
diff[key] = value
|
|
3131
|
-
}
|
|
3132
|
-
}
|
|
3133
|
-
if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) {
|
|
3312
|
+
if (typeof diff === 'string') {
|
|
3134
3313
|
return bailout(
|
|
3135
3314
|
input,
|
|
3136
3315
|
'local/dynamic-style-value',
|
|
3137
|
-
|
|
3316
|
+
diff,
|
|
3138
3317
|
entry.value.span
|
|
3139
3318
|
)
|
|
3140
3319
|
}
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
return bailout(
|
|
3144
|
-
input,
|
|
3145
|
-
'local/dynamic-style-value',
|
|
3146
|
-
`Multiple conditionals contribute ${key}; their interaction cannot be resolved per-branch`,
|
|
3147
|
-
entry.value.span
|
|
3148
|
-
)
|
|
3149
|
-
}
|
|
3150
|
-
}
|
|
3320
|
+
const conflict = claimKeys(entry, Object.keys(diff))
|
|
3321
|
+
if (conflict) return conflict
|
|
3151
3322
|
leafDiffs.set(leaf, diff)
|
|
3152
3323
|
}
|
|
3153
3324
|
for (const diff of leafDiffs.values()) {
|
|
@@ -3165,14 +3336,70 @@ export function createTamaguiCompilerHost(
|
|
|
3165
3336
|
}
|
|
3166
3337
|
|
|
3167
3338
|
conditionalParts.push(serializeNativeTree(tree))
|
|
3168
|
-
} else {
|
|
3339
|
+
} else if (shape === 'domain' && entry.value.kind === 'bailout') {
|
|
3340
|
+
// a finite domain is a lookup from each runtime value to its diff
|
|
3341
|
+
const lookup: string[] = []
|
|
3342
|
+
const keys = new Set<string>()
|
|
3343
|
+
for (const value of entry.value.dynamic!.values!) {
|
|
3344
|
+
const diff = valueDiff(
|
|
3345
|
+
entry,
|
|
3346
|
+
value,
|
|
3347
|
+
`${entry.name} value ${JSON.stringify(value)}`
|
|
3348
|
+
)
|
|
3349
|
+
if (typeof diff === 'string') {
|
|
3350
|
+
return bailout(
|
|
3351
|
+
input,
|
|
3352
|
+
'local/dynamic-style-value',
|
|
3353
|
+
diff,
|
|
3354
|
+
entry.value.span
|
|
3355
|
+
)
|
|
3356
|
+
}
|
|
3357
|
+
for (const key of Object.keys(diff)) keys.add(key)
|
|
3358
|
+
lookup.push(`${JSON.stringify(String(value))}: ${JSON.stringify(diff)}`)
|
|
3359
|
+
}
|
|
3360
|
+
const conflict = claimKeys(entry, keys)
|
|
3361
|
+
if (conflict) return conflict
|
|
3362
|
+
for (const key of keys) conditionalKeys.add(key)
|
|
3169
3363
|
const index = expressions.length
|
|
3170
3364
|
expressions.push(
|
|
3171
3365
|
input.source.slice(entry.value.span.start, entry.value.span.end)
|
|
3172
3366
|
)
|
|
3173
|
-
|
|
3174
|
-
|
|
3367
|
+
conditionalParts.push(`({ ${lookup.join(', ')} })[expressions[${index}]]`)
|
|
3368
|
+
} else {
|
|
3369
|
+
const key = directStyleName(entry.name, component)!
|
|
3370
|
+
if (shape === 'number') {
|
|
3371
|
+
// two probes prove a number lands unchanged in exactly this key
|
|
3372
|
+
for (const probe of [7.25, 1031]) {
|
|
3373
|
+
const diff = valueDiff(entry, probe, `${entry.name} number`)
|
|
3374
|
+
if (
|
|
3375
|
+
typeof diff === 'string' ||
|
|
3376
|
+
JSON.stringify(diff) !== JSON.stringify({ [key]: probe })
|
|
3377
|
+
) {
|
|
3378
|
+
return bailout(
|
|
3379
|
+
input,
|
|
3380
|
+
'local/dynamic-style-value',
|
|
3381
|
+
`Style prop ${entry.name} transforms numbers on native, so its runtime value cannot pass through`,
|
|
3382
|
+
entry.value.span
|
|
3383
|
+
)
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
if (key in baseStyleForDiff) {
|
|
3387
|
+
return bailout(
|
|
3388
|
+
input,
|
|
3389
|
+
'local/dynamic-style-value',
|
|
3390
|
+
`Style prop ${entry.name} overlaps a static ${key}`,
|
|
3391
|
+
entry.value.span
|
|
3392
|
+
)
|
|
3393
|
+
}
|
|
3394
|
+
const conflict = claimKeys(entry, [key])
|
|
3395
|
+
if (conflict) return conflict
|
|
3396
|
+
conditionalKeys.add(key)
|
|
3397
|
+
}
|
|
3398
|
+
const index = expressions.length
|
|
3399
|
+
expressions.push(
|
|
3400
|
+
input.source.slice(entry.value.span.start, entry.value.span.end)
|
|
3175
3401
|
)
|
|
3402
|
+
plainDynamicParts.push(`${JSON.stringify(key)}: expressions[${index}]`)
|
|
3176
3403
|
}
|
|
3177
3404
|
}
|
|
3178
3405
|
const dynamicStyle = plainDynamicParts.join(', ')
|
|
@@ -3362,7 +3589,7 @@ export function createTamaguiCompilerHost(
|
|
|
3362
3589
|
split,
|
|
3363
3590
|
props,
|
|
3364
3591
|
options.tamaguiConfig,
|
|
3365
|
-
|
|
3592
|
+
true,
|
|
3366
3593
|
Boolean(component.staticConfig.styleFrontend)
|
|
3367
3594
|
)
|
|
3368
3595
|
const className = artifacts.className
|
|
@@ -3564,7 +3791,7 @@ export function createTamaguiCompilerHost(
|
|
|
3564
3791
|
branchSplit,
|
|
3565
3792
|
branchProps,
|
|
3566
3793
|
options.tamaguiConfig,
|
|
3567
|
-
|
|
3794
|
+
true,
|
|
3568
3795
|
Boolean(component.staticConfig.styleFrontend)
|
|
3569
3796
|
)
|
|
3570
3797
|
leafArtifactsMap.set(leaf, {
|