@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.
Files changed (59) hide show
  1. package/dist/cjs/compilerHost.cjs +241 -75
  2. package/dist/cjs/exports.cjs +5 -1
  3. package/dist/cjs/extractor/bundle.cjs +14 -9
  4. package/dist/cjs/extractor/bundleConfig.cjs +0 -1
  5. package/dist/cjs/extractor/esbuildTsconfigPaths.cjs +81 -45
  6. package/dist/cjs/extractor/generateConfigMarkdown.cjs +492 -0
  7. package/dist/cjs/extractor/loadTamagui.cjs +28 -20
  8. package/dist/cjs/extractor/regenerateConfig.cjs +30 -20
  9. package/dist/cjs/zero/islands.cjs +2 -1
  10. package/dist/cjs/zero/theme.cjs +6 -3
  11. package/dist/esm/compilerHost.mjs +242 -76
  12. package/dist/esm/compilerHost.mjs.map +1 -1
  13. package/dist/esm/exports.mjs +3 -1
  14. package/dist/esm/extractor/bundle.mjs +14 -9
  15. package/dist/esm/extractor/bundle.mjs.map +1 -1
  16. package/dist/esm/extractor/bundleConfig.mjs +0 -1
  17. package/dist/esm/extractor/bundleConfig.mjs.map +1 -1
  18. package/dist/esm/extractor/esbuildTsconfigPaths.mjs +80 -45
  19. package/dist/esm/extractor/esbuildTsconfigPaths.mjs.map +1 -1
  20. package/dist/esm/extractor/generateConfigMarkdown.mjs +472 -0
  21. package/dist/esm/extractor/generateConfigMarkdown.mjs.map +1 -0
  22. package/dist/esm/extractor/loadTamagui.mjs +28 -20
  23. package/dist/esm/extractor/loadTamagui.mjs.map +1 -1
  24. package/dist/esm/extractor/regenerateConfig.mjs +30 -20
  25. package/dist/esm/extractor/regenerateConfig.mjs.map +1 -1
  26. package/dist/esm/zero/islands.mjs +2 -1
  27. package/dist/esm/zero/islands.mjs.map +1 -1
  28. package/dist/esm/zero/theme.mjs +7 -4
  29. package/dist/esm/zero/theme.mjs.map +1 -1
  30. package/package.json +23 -20
  31. package/src/compilerHost.ts +387 -160
  32. package/src/exports.ts +3 -0
  33. package/src/extractor/bundle.ts +19 -7
  34. package/src/extractor/bundleConfig.ts +0 -1
  35. package/src/extractor/esbuildTsconfigPaths.ts +106 -78
  36. package/src/extractor/generateConfigMarkdown.ts +610 -0
  37. package/src/extractor/loadTamagui.ts +55 -24
  38. package/src/extractor/regenerateConfig.ts +36 -35
  39. package/src/zero/islands.ts +2 -1
  40. package/src/zero/options.ts +8 -3
  41. package/src/zero/theme.ts +29 -4
  42. package/types/compilerHost.d.ts.map +1 -1
  43. package/types/exports.d.ts +2 -0
  44. package/types/exports.d.ts.map +1 -1
  45. package/types/extractor/bundle.d.ts.map +1 -1
  46. package/types/extractor/bundleConfig.d.ts.map +1 -1
  47. package/types/extractor/esbuildTsconfigPaths.d.ts +5 -7
  48. package/types/extractor/esbuildTsconfigPaths.d.ts.map +1 -1
  49. package/types/extractor/generateConfigMarkdown.d.ts +5 -0
  50. package/types/extractor/generateConfigMarkdown.d.ts.map +1 -0
  51. package/types/extractor/loadTamagui.d.ts +13 -1
  52. package/types/extractor/loadTamagui.d.ts.map +1 -1
  53. package/types/extractor/regenerateConfig.d.ts.map +1 -1
  54. package/types/zero/identity.d.ts +1 -1
  55. package/types/zero/islands.d.ts.map +1 -1
  56. package/types/zero/options.d.ts +8 -3
  57. package/types/zero/options.d.ts.map +1 -1
  58. package/types/zero/theme.d.ts +2 -0
  59. package/types/zero/theme.d.ts.map +1 -1
@@ -1,6 +1,7 @@
1
1
  import { collectLeaves, zeroRuleMessage, zeroThemeBoundaryMessage } from "@tamagui/compiler-core";
2
2
  import { StyleObjectIdentifier, StyleObjectProperty, StyleObjectRules, StyleObjectValue, stylePropsAll, stylePropsInput, stylePropsText, validStyles } from "@tamagui/helpers";
3
- import { ATTRIBUTES, DISPLAY_WEB_RESET, EVENTS, NATIVE_BACKING, NATIVE_BLOCK_DEFAULTS, NATIVE_ELEMENT_DEFAULTS, NATIVE_FLEX_DEFAULTS, NATIVE_INPUT_TYPES, NATIVE_PRIMITIVE_MODULE, TAGS, TAG_WEB_DEFAULTS } from "@tamagui/dom";
3
+ import { ATTRIBUTES, EVENTS, NATIVE_BACKING, NATIVE_BLOCK_DEFAULTS, NATIVE_ELEMENT_DEFAULTS, NATIVE_FLEX_DEFAULTS, NATIVE_INPUT_TYPES, NATIVE_PRIMITIVE_MODULE, TAGS, TAG_WEB_DEFAULTS } from "@tamagui/dom";
4
+ import { resolveTransition, toCSSTransition } from "@tamagui/animation-helpers";
4
5
  import { createModifierRegistry, parseTransition, parseValue } from "@tamagui/style-grammar/tooling";
5
6
  import { isValidStyleKey } from "@tamagui/web";
6
7
  import { concatClassName } from "./extractor/concatClassName.mjs";
@@ -39,13 +40,6 @@ const nativeRuntimeOnlyStyleProperties = /* @__PURE__ */ new Set(["textIndent"])
39
40
  function componentKey(resolvedId, exportName) {
40
41
  return `${resolvedId}#${exportName}`;
41
42
  }
42
- function cssFromRules(rules) {
43
- return Object.values(rules).flatMap((styleObject) => {
44
- const identifier = styleObject?.[StyleObjectIdentifier];
45
- const styleRules = styleObject?.[StyleObjectRules];
46
- return identifier && Array.isArray(styleRules) ? styleRules : [];
47
- });
48
- }
49
43
  function jsxClassName(value) {
50
44
  return `className=${JSON.stringify(value)}`;
51
45
  }
@@ -69,7 +63,16 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
69
63
  const viewClassName = typeof split.viewProps?.className === "string" ? split.viewProps.className : "";
70
64
  const classNameWithoutCaller = callerClassName && viewClassName.endsWith(callerClassName) ? viewClassName.slice(0, -callerClassName.length).trimEnd() : viewClassName;
71
65
  const rules = split.rulesToInsert ?? {};
72
- const mediaNames = new Set(Object.keys(config.media ?? {}));
66
+ const mediaNames = Object.keys(config.media ?? {});
67
+ const rulesByIdentifier = /* @__PURE__ */ new Map();
68
+ for (const styleObject of Object.values(rules)) {
69
+ const identifier = styleObject?.[StyleObjectIdentifier];
70
+ const styleRules = styleObject?.[StyleObjectRules];
71
+ if (typeof identifier !== "string" || !Array.isArray(styleRules)) continue;
72
+ const existing = rulesByIdentifier.get(identifier);
73
+ if (existing) existing.push(...styleRules);
74
+ else rulesByIdentifier.set(identifier, [...styleRules]);
75
+ }
73
76
  const pseudoNames = [
74
77
  "hover",
75
78
  "press",
@@ -95,14 +98,14 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
95
98
  })]);
96
99
  for (const identifier of identifiers) {
97
100
  const key = classKeys.get(identifier) ?? "";
98
- const css2 = cssFromRules(Object.fromEntries(Object.entries(rules).filter(([, styleObject]) => styleObject?.[StyleObjectIdentifier] === identifier))).join("");
101
+ const css2 = (rulesByIdentifier.get(identifier) ?? []).join("");
99
102
  if (identifier.startsWith("t_group_")) {
100
103
  buckets.group.push(identifier);
101
104
  } else if (pseudoNames.some((name) => key.endsWith(`-${name}`))) {
102
105
  buckets.pseudo.push(identifier);
103
106
  } else if (css2.includes(".t_")) {
104
107
  buckets.theme.push(identifier);
105
- } else if ([...mediaNames].some((name) => key.endsWith(`-${name}`))) {
108
+ } else if (mediaNames.some((name) => key.endsWith(`-${name}`))) {
106
109
  buckets.media.push(identifier);
107
110
  } else {
108
111
  buckets.normal.push(identifier);
@@ -118,7 +121,7 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
118
121
  const orderedSet = new Set(orderedIdentifiers);
119
122
  const baseViewClassName = includeRuntimeBase ? classNameWithoutCaller.split(/\s+/).filter((token) => token && !orderedSet.has(token)).join(" ") : "";
120
123
  const css = orderedIdentifiers.flatMap((identifier) => {
121
- const identifierRules = cssFromRules(Object.fromEntries(Object.entries(rules).filter(([, styleObject]) => styleObject?.[StyleObjectIdentifier] === identifier)));
124
+ const identifierRules = rulesByIdentifier.get(identifier) ?? [];
122
125
  return buckets.theme.includes(identifier) ? identifierRules.map((rule) => rule.replace(":root:root:root.t_", ":root:root:root:root.t_")) : identifierRules;
123
126
  });
124
127
  return {
@@ -207,7 +210,6 @@ const compilerStyleProps = /* @__PURE__ */ new Set([
207
210
  "group",
208
211
  "transition",
209
212
  "animation",
210
- "animateOnly",
211
213
  "animatePresence",
212
214
  "animatedBy",
213
215
  "fontFamily",
@@ -216,7 +218,6 @@ const compilerStyleProps = /* @__PURE__ */ new Set([
216
218
  const runtimeAnimationProps = /* @__PURE__ */ new Set([
217
219
  "transition",
218
220
  "animation",
219
- "animateOnly",
220
221
  "animatePresence",
221
222
  "animatedBy"
222
223
  ]);
@@ -489,6 +490,77 @@ const nativePointerEventProps = /* @__PURE__ */ new Set([
489
490
  "onPointerMove",
490
491
  "onPointerUp"
491
492
  ]);
493
+ const nativeShorthandFamilies = [
494
+ [
495
+ "padding",
496
+ [
497
+ "paddingTop",
498
+ "paddingRight",
499
+ "paddingBottom",
500
+ "paddingLeft"
501
+ ],
502
+ /^padding/
503
+ ],
504
+ [
505
+ "margin",
506
+ [
507
+ "marginTop",
508
+ "marginRight",
509
+ "marginBottom",
510
+ "marginLeft"
511
+ ],
512
+ /^margin/
513
+ ],
514
+ [
515
+ "borderWidth",
516
+ [
517
+ "borderTopWidth",
518
+ "borderRightWidth",
519
+ "borderBottomWidth",
520
+ "borderLeftWidth"
521
+ ],
522
+ /^border.*Width$/
523
+ ],
524
+ [
525
+ "borderColor",
526
+ [
527
+ "borderTopColor",
528
+ "borderRightColor",
529
+ "borderBottomColor",
530
+ "borderLeftColor"
531
+ ],
532
+ /^border.*Color$/
533
+ ],
534
+ [
535
+ "borderRadius",
536
+ [
537
+ "borderTopLeftRadius",
538
+ "borderTopRightRadius",
539
+ "borderBottomRightRadius",
540
+ "borderBottomLeftRadius"
541
+ ],
542
+ /^border.*Radius$/
543
+ ]
544
+ ];
545
+ function collapseNativeShorthands(style) {
546
+ if (!staticObject(style)) return style;
547
+ const keys = Object.keys(style);
548
+ const collapsed = /* @__PURE__ */ new Map();
549
+ for (const [shorthand, sides, family] of nativeShorthandFamilies) {
550
+ const value = style[sides[0]];
551
+ if (value !== void 0 && !(shorthand in style) && sides.every((side) => style[side] === value) && keys.filter((key) => family.test(key)).length === sides.length) {
552
+ for (const side of sides) collapsed.set(side, shorthand);
553
+ }
554
+ }
555
+ if (!collapsed.size) return style;
556
+ const out = {};
557
+ for (const key of keys) {
558
+ const shorthand = collapsed.get(key);
559
+ if (!shorthand) out[key] = style[key];
560
+ else if (!(shorthand in out)) out[shorthand] = style[key];
561
+ }
562
+ return out;
563
+ }
492
564
  function isSerializableNativeStyle(value) {
493
565
  if (value == null || typeof value === "number" || typeof value === "boolean") {
494
566
  return true;
@@ -500,6 +572,35 @@ function isSerializableNativeStyle(value) {
500
572
  if (prototype !== Object.prototype && prototype !== null) return false;
501
573
  return Object.values(value).every(isSerializableNativeStyle);
502
574
  }
575
+ function nativeTextMetrics(result) {
576
+ const value = result?.nativeTextMetrics;
577
+ if (!staticObject(value)) return null;
578
+ const { fontSize, lineHeight } = value;
579
+ if (fontSize !== void 0 && typeof fontSize !== "number" || lineHeight !== void 0 && typeof lineHeight !== "number" && typeof lineHeight !== "string") {
580
+ return null;
581
+ }
582
+ return fontSize === void 0 && lineHeight === void 0 ? null : value;
583
+ }
584
+ function mergeNativeTextMetrics(current, next) {
585
+ if (!current) return next;
586
+ if (!next) return current;
587
+ return {
588
+ fontSize: next.fontSize ?? current.fontSize,
589
+ lineHeight: next.lineHeight ?? current.lineHeight
590
+ };
591
+ }
592
+ function hasChildThatMayRenderReactNode(element) {
593
+ return element.entries.some((entry) => {
594
+ if (entry.kind !== "child" || entry.value.kind === "empty") return false;
595
+ if (entry.value.kind === "element") return true;
596
+ if (entry.value.kind === "bailout") return entry.value.dynamic === null;
597
+ if (entry.value.kind === "conditional") {
598
+ return collectLeaves(entry.value.tree).some(({ value }) => value === void 0 || value !== null && typeof value === "object");
599
+ }
600
+ if (entry.value.kind !== "static") return true;
601
+ return entry.value.value !== null && typeof entry.value.value === "object";
602
+ });
603
+ }
503
604
  function unusedIdentifier(source, base) {
504
605
  let candidate = base;
505
606
  let suffix = 0;
@@ -764,6 +865,7 @@ function createTamaguiCompilerHost(options) {
764
865
  };
765
866
  const configuredAnimationDriver = options.tamaguiConfig.animations;
766
867
  const configuredCssAnimationDriver = platform === "web" && configuredAnimationDriver?.outputStyle === "css" && !options.tamaguiConfig.animationDrivers ? configuredAnimationDriver : null;
868
+ const nativeTextMetricsByElement = /* @__PURE__ */ new WeakMap();
767
869
  const resolveStaticCssTransition = (value, transitionPresets) => {
768
870
  if (platform !== "web" || typeof value !== "string") return null;
769
871
  const transitionPresetNames = new Set(Object.keys(transitionPresets));
@@ -775,20 +877,19 @@ function createTamaguiCompilerHost(options) {
775
877
  for (const payload of payloads) {
776
878
  const transition = parseTransition(payload, transitionPresetNames);
777
879
  if (!transition.ok) return null;
778
- if (transition.value.kind === "global" || transition.value.entries.every((entry) => entry.timing.type === "css")) {
880
+ if (transition.value.kind === "global") {
779
881
  resolvedPayloads.push(payload);
780
882
  continue;
781
883
  }
782
- if (transition.value.entries.length !== 1 || transition.value.entries[0].timing.type !== "preset") {
783
- return null;
784
- }
785
- const preset = transitionPresets[transition.value.entries[0].timing.name];
786
- if (typeof preset !== "string") return null;
787
- const parsedPreset = parseTransition(preset);
788
- if (!parsedPreset.ok || parsedPreset.value.kind !== "transition" || parsedPreset.value.entries.length !== 1 || parsedPreset.value.entries[0].property !== "all" || parsedPreset.value.entries[0].timing.type !== "css") {
789
- return null;
884
+ const resolved2 = resolveTransition(payload, { animations: transitionPresets });
885
+ if (resolved2.diagnostics.length > 0) return null;
886
+ if (!resolved2.fused) {
887
+ resolvedPayloads.push(payload);
888
+ continue;
790
889
  }
791
- resolvedPayloads.push(`all ${preset}`);
890
+ const css = toCSSTransition(resolved2);
891
+ if (css === void 0) return null;
892
+ resolvedPayloads.push(css);
792
893
  }
793
894
  let payloadIndex = 0;
794
895
  const resolved = [];
@@ -838,7 +939,6 @@ function createTamaguiCompilerHost(options) {
838
939
  const base = (textLike ? core.Text : core.View)?.staticConfig;
839
940
  if (!base) return null;
840
941
  const platformDefaults = platform === "web" ? {
841
- ...DISPLAY_WEB_RESET[row.display],
842
942
  ...row.defaults,
843
943
  ...TAG_WEB_DEFAULTS[tag]
844
944
  } : {
@@ -853,6 +953,7 @@ function createTamaguiCompilerHost(options) {
853
953
  displayName: tag,
854
954
  staticConfig: normalizeStaticConfig({
855
955
  ...base,
956
+ isDOM: true,
856
957
  isInput: row.backing === "textinput",
857
958
  validStyles: {
858
959
  ...validStyles,
@@ -916,7 +1017,7 @@ function createTamaguiCompilerHost(options) {
916
1017
  const defaultProps = resolved.staticConfig.defaultProps ?? {};
917
1018
  return {
918
1019
  key: resolved.key,
919
- canFlatten: resolved.staticConfig.acceptsClassName !== false && !resolved.staticConfig.neverFlatten && !resolved.staticConfig.context,
1020
+ canFlatten: resolved.staticConfig.acceptsClassName !== false && !resolved.staticConfig.isHOC && !resolved.staticConfig.neverFlatten && !resolved.staticConfig.context,
920
1021
  staticConfig: resolved.staticConfig,
921
1022
  displayName: resolved.displayName,
922
1023
  ...dom && { domTag: dom.tag },
@@ -933,7 +1034,6 @@ function createTamaguiCompilerHost(options) {
933
1034
  "group",
934
1035
  "transition",
935
1036
  "animation",
936
- "animateOnly",
937
1037
  "animatePresence",
938
1038
  "animatedBy",
939
1039
  "render"
@@ -948,11 +1048,20 @@ function createTamaguiCompilerHost(options) {
948
1048
  if (dynamic?.type !== "string" || !dynamic.values?.length) return null;
949
1049
  return dynamic.values.map(String);
950
1050
  };
1051
+ const nativeDynamicShape = (name, component, dynamic) => {
1052
+ if (!dynamic || !canLowerConditionalStyleProp(name, component)) return null;
1053
+ if (dynamic.values?.length) return "domain";
1054
+ if (dynamic.type === "number" && directStyleName(name, component)) return "number";
1055
+ return null;
1056
+ };
951
1057
  const conditionalClassEntry = (entry, component) => entry.value.kind === "conditional" && canLowerConditionalStyleProp(entry.name, component) || frontendClassDomain(entry, component) !== null;
1058
+ const expandStyleShorthand = (name) => options.tamaguiConfig.shorthands?.[name] ?? name;
952
1059
  const isInvalidHostStyleProp = (name, component) => {
1060
+ const expanded = expandStyleShorthand(name);
953
1061
  const staticConfig = component.staticConfig;
1062
+ if (staticConfig.inlineProps?.has(name) || staticConfig.variants?.[name]) return false;
954
1063
  const validStyles$1 = staticConfig.validStyles || (staticConfig.isInput ? stylePropsInput : staticConfig.isText ? stylePropsText : validStyles);
955
- return name in stylePropsAll && !isValidStyleKey(name, validStyles$1);
1064
+ return expanded in stylePropsAll && !isValidStyleKey(expanded, validStyles$1);
956
1065
  };
957
1066
  const directStyleName = (name, component) => {
958
1067
  if (compilerStyleProps.has(name) || name === "style") {
@@ -960,7 +1069,7 @@ function createTamaguiCompilerHost(options) {
960
1069
  }
961
1070
  const staticConfig = component.staticConfig;
962
1071
  if (staticConfig.variants?.[name]) return null;
963
- const expanded = options.tamaguiConfig.shorthands?.[name] ?? name;
1072
+ const expanded = expandStyleShorthand(name);
964
1073
  return staticConfig.validStyles?.[expanded] ? expanded : null;
965
1074
  };
966
1075
  const resolveSplitStyles = (props, staticConfig, animationDriver, displayName) => {
@@ -1093,7 +1202,7 @@ function createTamaguiCompilerHost(options) {
1093
1202
  return {
1094
1203
  resolveComponent: resolve,
1095
1204
  isStyleProp,
1096
- canLowerDynamicStyleProp(name, component, valueKind) {
1205
+ canLowerDynamicStyleProp(name, component, valueKind, dynamic) {
1097
1206
  if (!options.disablePartialExtraction && valueKind === "conditional" && canLowerConditionalStyleProp(name, component)) {
1098
1207
  return true;
1099
1208
  }
@@ -1103,7 +1212,7 @@ function createTamaguiCompilerHost(options) {
1103
1212
  if (!options.disablePartialExtraction && platform === "web" && isFrontendClassName(name, component)) {
1104
1213
  return true;
1105
1214
  }
1106
- return !options.disablePartialExtraction && (platform === "web" && !!directStyleName(name, component) || platform === "native" && directStyleName(name, component) === "opacity");
1215
+ return !options.disablePartialExtraction && (platform === "web" && !!directStyleName(name, component) || platform === "native" && (directStyleName(name, component) === "opacity" || valueKind === "bailout" && nativeDynamicShape(name, component, dynamic) !== null));
1107
1216
  },
1108
1217
  developmentDebugInstrumentation,
1109
1218
  lowerCandidate(input) {
@@ -1198,7 +1307,7 @@ function createTamaguiCompilerHost(options) {
1198
1307
  if (styleObjectStatic) props.style = styleObjectStatic;
1199
1308
  const dynamicStyleEntries = [...input.element.entries.filter((entry) => entry.kind === "prop" && (entry.value.kind === "bailout" || entry.value.kind === "conditional") && isStyleProp(entry.name, component)), ...styleMemberEntries];
1200
1309
  {
1201
- const needsRuntimeMapping = (name) => runtimeEventProps.has(name) || platform === "native" && !component.domTag && nativePointerEventProps.has(name);
1310
+ const needsRuntimeMapping = (name) => runtimeEventProps.has(name) || platform === "web" && name === "onLayout" || platform === "native" && !component.domTag && nativePointerEventProps.has(name);
1202
1311
  const directRuntimeEvent = input.element.entries.find((entry) => entry.kind === "prop" && needsRuntimeMapping(entry.name));
1203
1312
  const runtimeEvent = directRuntimeEvent?.kind === "prop" ? directRuntimeEvent.name : Object.keys(props).find(needsRuntimeMapping);
1204
1313
  if (runtimeEvent) {
@@ -1208,13 +1317,6 @@ function createTamaguiCompilerHost(options) {
1208
1317
  const animationDefaultProps = component.staticConfig.defaultProps ?? {};
1209
1318
  const animationProps = core.mergeProps(animationDefaultProps, props);
1210
1319
  const animationNames = /* @__PURE__ */ new Set([...input.element.entries.flatMap((entry) => entry.kind === "prop" && runtimeAnimationProps.has(entry.name) ? [entry.name] : []), ...Object.keys(animationProps).filter((name) => runtimeAnimationProps.has(name) && animationProps[name] !== void 0)]);
1211
- const animateOnlyEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "animateOnly");
1212
- if (animationNames.has("animateOnly")) {
1213
- return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", animateOnlyEntry?.span, {
1214
- rule: 5,
1215
- message: zeroRuleMessage(5, { detail: animateOnlyEntry ? `animateOnly on ${input.element.component.name}` : `animateOnly in the styled() definition of ${input.element.component.name}` })
1216
- });
1217
- }
1218
1320
  const transitionEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "transition");
1219
1321
  const animatedBy = typeof animationProps.animatedBy === "string" ? animationProps.animatedBy.trim() : null;
1220
1322
  const namedAnimationDriver = animatedBy === null ? null : options.tamaguiConfig.animationDrivers?.[animatedBy];
@@ -1225,7 +1327,7 @@ function createTamaguiCompilerHost(options) {
1225
1327
  props.transition = resolvedCssTransition;
1226
1328
  }
1227
1329
  const animatedByNeedsRuntime = animationNames.has("animatedBy") && !animationNames.has("transition") && (dynamicStyleEntries.length > 0 || Object.keys(animationProps).some((name) => name !== "animatedBy" && (isStyleProp(name, component) && typeof animationProps[name] === "string" && animationProps[name].includes(":") || name === "animationConfig" || name === "forceStyle" || name === "onTransition")));
1228
- const runtimeAnimationRequired = animationNames.has("transition") && resolvedCssTransition === null || [...animationNames].some((name) => name !== "transition" && name !== "animatedBy" && name !== "animateOnly") || animatedByNeedsRuntime;
1330
+ const runtimeAnimationRequired = animationNames.has("transition") && resolvedCssTransition === null || [...animationNames].some((name) => name !== "transition" && name !== "animatedBy") || animatedByNeedsRuntime;
1229
1331
  let dynamicHostStyleProperties = null;
1230
1332
  let laneAEntries = [];
1231
1333
  const laneAOwners = /* @__PURE__ */ new Set();
@@ -1419,7 +1521,7 @@ function createTamaguiCompilerHost(options) {
1419
1521
  message: zeroRuleMessage(5, { detail: `the animation configured on ${input.element.component.name}` })
1420
1522
  });
1421
1523
  }
1422
- const supportsNativeDynamicStyles = platform === "native" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicStyleEntries.every((entry) => entry.kind === "prop" && (directStyleName(entry.name, component) === "opacity" || entry.value.kind === "conditional" && canLowerConditionalStyleProp(entry.name, component)));
1524
+ const supportsNativeDynamicStyles = platform === "native" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicStyleEntries.every((entry) => entry.kind === "prop" && (directStyleName(entry.name, component) === "opacity" || entry.value.kind === "conditional" && canLowerConditionalStyleProp(entry.name, component) || entry.value.kind === "bailout" && nativeDynamicShape(entry.name, component, entry.value.dynamic) !== null));
1423
1525
  if (dynamicStyleEntries.length > 0 && !laneACoversAll && !supportsNativeDynamicStyles && !supportsWebConditionalClasses) {
1424
1526
  const entry = dynamicStyleEntries[0];
1425
1527
  return bailout(input, "local/dynamic-style-value", `Style prop ${entry.kind === "prop" ? entry.name : "unknown"} could not be safely extracted`, entry.span);
@@ -1514,6 +1616,21 @@ function createTamaguiCompilerHost(options) {
1514
1616
  if (!split) {
1515
1617
  return bailout(input, "local/style-resolution-failed", "getSplitStyles returned no static result");
1516
1618
  }
1619
+ const nativeMetrics = platform === "native" ? nativeTextMetrics(split) : null;
1620
+ if (nativeMetrics) nativeTextMetricsByElement.set(input.element, nativeMetrics);
1621
+ const hasDynamicFontSize = dynamicStyleEntries.some((entry) => {
1622
+ const name = directStyleName(entry.name, component);
1623
+ return name === "fontFamily" || name === "fontSize";
1624
+ });
1625
+ const hasUnresolvedNativeLineHeight = typeof nativeMetrics?.lineHeight === "number" && nativeMetrics.fontSize === void 0 && !hasDynamicFontSize;
1626
+ const hasDynamicTextMetric = dynamicStyleEntries.some((entry) => {
1627
+ const name = directStyleName(entry.name, component);
1628
+ return name === "fontFamily" || name === "fontSize" || name === "lineHeight" || name === null && !!component.staticConfig.variants?.[entry.name];
1629
+ });
1630
+ const changesInheritedNativeFontSize = nativeMetrics?.fontSize !== void 0 && nativeMetrics.lineHeight === void 0;
1631
+ if (platform === "native" && component.staticConfig.isText && !component.domTag && (hasUnresolvedNativeLineHeight || changesInheritedNativeFontSize || hasChildThatMayRenderReactNode(input.element) && (nativeMetrics !== null || hasDynamicTextMetric) || input.module.elements.some((ancestor) => ancestor !== input.element && ancestor.span.start < input.element.span.start && ancestor.span.end > input.element.span.end && nativeTextMetricsByElement.has(ancestor)))) {
1632
+ return bailout(input, "local/unsupported-target", "Native text metrics need the runtime Text provider for this subtree");
1633
+ }
1517
1634
  if (split.programLifecycleStyleKeys?.enter?.size || split.programLifecycleStyleKeys?.exit?.size) {
1518
1635
  return bailout(input, "local/unsupported-target", "Lifecycle value programs remain on the runtime path", input.element.span, {
1519
1636
  rule: 5,
@@ -1616,14 +1733,15 @@ function createTamaguiCompilerHost(options) {
1616
1733
  return bailout(input, "local/unsupported-target", "Native style output is not a static serializable value");
1617
1734
  }
1618
1735
  const nativeStyleLocal = unusedIdentifier(input.source, `__TamaguiNativeStyle${input.element.span.start}`);
1736
+ const usesNativeFastPath = !!themedStyleKeys && !!options.experimentalNativeFastPath && dynamicStyleEntries.length === 0 && !input.element.entries.some((entry) => entry.kind === "spread");
1619
1737
  const nativeStyleImports = [{
1620
1738
  content: `
1621
- function ${nativeStyleLocal}() { return ${nativeStyleLocal}._ ?? (${nativeStyleLocal}._ = ${JSON.stringify(nativeStyle ?? {})}); } ${nativeStyleLocal}();`,
1739
+ function ${nativeStyleLocal}() { return ${nativeStyleLocal}._ ?? (${nativeStyleLocal}._ = ${JSON.stringify((usesNativeFastPath ? nativeStyle : collapseNativeShorthands(nativeStyle)) ?? {})}); } ${nativeStyleLocal}();`,
1622
1740
  origin: input.element.component.span
1623
1741
  }];
1624
1742
  const nativeStyleSource = `${nativeStyleLocal}._ ?? ${nativeStyleLocal}()`;
1625
1743
  let nativeFastPath;
1626
- if (themedStyleKeys && options.experimentalNativeFastPath && dynamicStyleEntries.length === 0 && !input.element.entries.some((entry) => entry.kind === "spread")) {
1744
+ if (usesNativeFastPath) {
1627
1745
  const mappingLocal = unusedIdentifier(input.source, `__TamaguiNativeMapping${input.element.span.start}`);
1628
1746
  nativeFastPath = {
1629
1747
  mappingLocal,
@@ -1647,6 +1765,7 @@ const ${mappingLocal} = ${JSON.stringify(themedStyleKeys)};`,
1647
1765
  }
1648
1766
  styleEntries = [.../* @__PURE__ */ new Set([...styleEntries, ...propsResult.consumed])];
1649
1767
  let nativeDOMStyleSource = nativeStyleSource;
1768
+ let nativeDOMMetrics = nativeMetrics;
1650
1769
  let runtimeStyleSource = null;
1651
1770
  if (domStyleProgram?.kind === "prop" && domStyleProgram.value.kind === "dom-style") {
1652
1771
  const needsRuntime = domStyleProgram.value.items.some((item) => item.value.kind === "static" && staticObject(item.value.value) && (Object.keys(item.value.value).some((property) => nativeRuntimeOnlyStyleProperties.has(property)) || Object.values(item.value.value).some((value) => typeof value === "string" && (flatClausePattern.test(value) || nativeInheritedKeywordPattern.test(value)) || isClauseObjectValue(value))));
@@ -1667,6 +1786,7 @@ const ${mappingLocal} = ${JSON.stringify(themedStyleKeys)};`,
1667
1786
  if (!isSerializableNativeStyle(itemStyle) || core.containsThemeRef(itemStyle)) {
1668
1787
  return bailout(input, "local/unsupported-target", "Native style() output is not serializable", item.value.span);
1669
1788
  }
1789
+ nativeDOMMetrics = mergeNativeTextMetrics(nativeDOMMetrics, nativeTextMetrics(itemSplit));
1670
1790
  value = JSON.stringify(itemStyle ?? {});
1671
1791
  }
1672
1792
  const condition = item.condition ? input.source.slice(item.condition.start, item.condition.end) : null;
@@ -1683,6 +1803,7 @@ const ${mappingLocal} = ${JSON.stringify(themedStyleKeys)};`,
1683
1803
  const propertyContent = [
1684
1804
  input.element.form === "jsx" ? `style={${nativeDOMStyleSource}}` : `style: ${nativeDOMStyleSource}`,
1685
1805
  runtimeStyleSource ? input.element.form === "jsx" ? `__styles={${runtimeStyleSource}}` : `__styles: ${runtimeStyleSource}` : "",
1806
+ nativeDOMMetrics ? serializedProps(input.element.form, [["__textMetrics", JSON.stringify(nativeDOMMetrics)]]) : "",
1686
1807
  serializedProps(input.element.form, propsResult.additions)
1687
1808
  ].filter(Boolean).join(input.element.form === "jsx" ? " " : ", ");
1688
1809
  const primitiveTagEdits = [input.element.component.span, input.element.component.closingSpan].filter((span) => !!span).map((span) => ({
@@ -1839,7 +1960,45 @@ const ${fastLocal} = require('@tamagui/core')._withNativeStyle(${nativeLocal}, $
1839
1960
  const conditionalParts = [];
1840
1961
  const conditionalKeys = /* @__PURE__ */ new Set();
1841
1962
  const baseStyleForDiff = staticObject(nativeStyleResolved) ? nativeStyleResolved : {};
1963
+ const valueDiff = (entry, value, what) => {
1964
+ const { branchCompleteProps } = propsForConditional(entry, value);
1965
+ const branchSplit = resolveSplitStyles(branchCompleteProps, component.staticConfig, cssAnimationDriver, component.displayName);
1966
+ const branchStyle = branchSplit?.viewProps?.style ?? {};
1967
+ if (!staticObject(branchStyle)) {
1968
+ return `${what} did not resolve to a static native style`;
1969
+ }
1970
+ for (const viewPropsKey of /* @__PURE__ */ new Set([...Object.keys(branchSplit?.viewProps ?? {}), ...Object.keys(split.viewProps ?? {})])) {
1971
+ if (viewPropsKey === "style") continue;
1972
+ if (JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) {
1973
+ return `${what} changes ${viewPropsKey}, which the compiled style array cannot express`;
1974
+ }
1975
+ }
1976
+ for (const key of Object.keys(baseStyleForDiff)) {
1977
+ if (!(key in branchStyle)) {
1978
+ return `${what} removes ${key}, which an additive style array cannot express`;
1979
+ }
1980
+ }
1981
+ const diff = {};
1982
+ for (const [key, value2] of Object.entries(branchStyle)) {
1983
+ if (JSON.stringify(baseStyleForDiff[key]) !== JSON.stringify(value2)) {
1984
+ diff[key] = value2;
1985
+ }
1986
+ }
1987
+ if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) {
1988
+ return `${what} style is not statically representable`;
1989
+ }
1990
+ return diff;
1991
+ };
1992
+ const claimKeys = (entry, keys) => {
1993
+ for (const key of keys) {
1994
+ if (conditionalKeys.has(key)) {
1995
+ return bailout(input, "local/dynamic-style-value", `Multiple dynamic props contribute ${key}; their interaction cannot be resolved at compile time`, entry.value.span);
1996
+ }
1997
+ }
1998
+ return null;
1999
+ };
1842
2000
  for (const entry of dynamicStyleEntries) {
2001
+ const shape = entry.value.kind === "bailout" ? nativeDynamicShape(entry.name, component, entry.value.dynamic) : null;
1843
2002
  if (entry.value.kind === "conditional" && directStyleName(entry.name, component) !== "opacity") {
1844
2003
  let serializeNativeTree = function(node) {
1845
2004
  if (node.kind === "leaf") {
@@ -1854,47 +2013,54 @@ const ${fastLocal} = require('@tamagui/core')._withNativeStyle(${nativeLocal}, $
1854
2013
  const leaves = collectLeaves(tree);
1855
2014
  const leafDiffs = /* @__PURE__ */ new Map();
1856
2015
  for (const leaf of leaves) {
1857
- const { branchCompleteProps } = propsForConditional(entry, leaf.value);
1858
- const branchSplit = resolveSplitStyles(branchCompleteProps, component.staticConfig, cssAnimationDriver, component.displayName);
1859
- const branchStyle = branchSplit?.viewProps?.style ?? {};
1860
- if (!staticObject(branchStyle)) {
1861
- return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch did not resolve to a static native style`, entry.value.span);
1862
- }
1863
- for (const viewPropsKey of /* @__PURE__ */ new Set([...Object.keys(branchSplit?.viewProps ?? {}), ...Object.keys(split.viewProps ?? {})])) {
1864
- if (viewPropsKey === "style") continue;
1865
- if (JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) {
1866
- return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch changes ${viewPropsKey}, which the compiled style array cannot express`, entry.value.span);
1867
- }
1868
- }
1869
- for (const key of Object.keys(baseStyleForDiff)) {
1870
- if (!(key in branchStyle)) {
1871
- return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch removes ${key}, which an additive style array cannot express`, entry.value.span);
1872
- }
1873
- }
1874
- const diff = {};
1875
- for (const [key, value] of Object.entries(branchStyle)) {
1876
- if (JSON.stringify(baseStyleForDiff[key]) !== JSON.stringify(value)) {
1877
- diff[key] = value;
1878
- }
1879
- }
1880
- if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) {
1881
- return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch style is not statically representable`, entry.value.span);
1882
- }
1883
- for (const key of Object.keys(diff)) {
1884
- if (conditionalKeys.has(key)) {
1885
- return bailout(input, "local/dynamic-style-value", `Multiple conditionals contribute ${key}; their interaction cannot be resolved per-branch`, entry.value.span);
1886
- }
2016
+ const diff = valueDiff(entry, leaf.value, `Conditional ${entry.name} branch`);
2017
+ if (typeof diff === "string") {
2018
+ return bailout(input, "local/dynamic-style-value", diff, entry.value.span);
1887
2019
  }
2020
+ const conflict = claimKeys(entry, Object.keys(diff));
2021
+ if (conflict) return conflict;
1888
2022
  leafDiffs.set(leaf, diff);
1889
2023
  }
1890
2024
  for (const diff of leafDiffs.values()) {
1891
2025
  for (const key of Object.keys(diff)) conditionalKeys.add(key);
1892
2026
  }
1893
2027
  conditionalParts.push(serializeNativeTree(tree));
2028
+ } else if (shape === "domain" && entry.value.kind === "bailout") {
2029
+ const lookup = [];
2030
+ const keys = /* @__PURE__ */ new Set();
2031
+ for (const value of entry.value.dynamic.values) {
2032
+ const diff = valueDiff(entry, value, `${entry.name} value ${JSON.stringify(value)}`);
2033
+ if (typeof diff === "string") {
2034
+ return bailout(input, "local/dynamic-style-value", diff, entry.value.span);
2035
+ }
2036
+ for (const key of Object.keys(diff)) keys.add(key);
2037
+ lookup.push(`${JSON.stringify(String(value))}: ${JSON.stringify(diff)}`);
2038
+ }
2039
+ const conflict = claimKeys(entry, keys);
2040
+ if (conflict) return conflict;
2041
+ for (const key of keys) conditionalKeys.add(key);
2042
+ const index = expressions.length;
2043
+ expressions.push(input.source.slice(entry.value.span.start, entry.value.span.end));
2044
+ conditionalParts.push(`({ ${lookup.join(", ")} })[expressions[${index}]]`);
1894
2045
  } else {
2046
+ const key = directStyleName(entry.name, component);
2047
+ if (shape === "number") {
2048
+ for (const probe of [7.25, 1031]) {
2049
+ const diff = valueDiff(entry, probe, `${entry.name} number`);
2050
+ if (typeof diff === "string" || JSON.stringify(diff) !== JSON.stringify({ [key]: probe })) {
2051
+ return bailout(input, "local/dynamic-style-value", `Style prop ${entry.name} transforms numbers on native, so its runtime value cannot pass through`, entry.value.span);
2052
+ }
2053
+ }
2054
+ if (key in baseStyleForDiff) {
2055
+ return bailout(input, "local/dynamic-style-value", `Style prop ${entry.name} overlaps a static ${key}`, entry.value.span);
2056
+ }
2057
+ const conflict = claimKeys(entry, [key]);
2058
+ if (conflict) return conflict;
2059
+ conditionalKeys.add(key);
2060
+ }
1895
2061
  const index = expressions.length;
1896
2062
  expressions.push(input.source.slice(entry.value.span.start, entry.value.span.end));
1897
- plainDynamicParts.push(`${JSON.stringify(directStyleName(entry.name, component))}: expressions[${index}]`);
2063
+ plainDynamicParts.push(`${JSON.stringify(key)}: expressions[${index}]`);
1898
2064
  }
1899
2065
  }
1900
2066
  const dynamicStyle = plainDynamicParts.join(", ");
@@ -2018,7 +2184,7 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
2018
2184
  flattened: true
2019
2185
  };
2020
2186
  }
2021
- const artifacts = extractedStyleArtifacts(split, props, options.tamaguiConfig, !component.domTag, Boolean(component.staticConfig.styleFrontend));
2187
+ const artifacts = extractedStyleArtifacts(split, props, options.tamaguiConfig, true, Boolean(component.staticConfig.styleFrontend));
2022
2188
  const className = artifacts.className;
2023
2189
  const rawInlineStyle = split.viewProps?.style;
2024
2190
  const inlineStyle = staticObject(rawInlineStyle) && Object.keys(rawInlineStyle).length > 0 ? rawInlineStyle : null;
@@ -2114,7 +2280,7 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
2114
2280
  if (conflictsWithInline(changedKeys, laneAOwners, laneAMemberOwners)) {
2115
2281
  return bailout(input, "local/dynamic-style-value", `${entry.name} and an inline dynamic style both contribute a CSS property; their precedence cannot be resolved per-branch`, entry.value.span);
2116
2282
  }
2117
- const branchArtifacts = extractedStyleArtifacts(branchSplit, branchProps, options.tamaguiConfig, !component.domTag, Boolean(component.staticConfig.styleFrontend));
2283
+ const branchArtifacts = extractedStyleArtifacts(branchSplit, branchProps, options.tamaguiConfig, true, Boolean(component.staticConfig.styleFrontend));
2118
2284
  leafArtifactsMap.set(leaf, {
2119
2285
  classes: branchArtifacts.className.split(" ").filter(Boolean),
2120
2286
  css: branchArtifacts.css