@tamagui/static 3.0.0-beta.765.1 → 3.0.0-beta.804.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.
@@ -1,4 +1,4 @@
1
- import { zeroRuleMessage, zeroThemeBoundaryMessage } from "@tamagui/compiler-core";
1
+ import { collectLeaves, zeroRuleMessage, zeroThemeBoundaryMessage } from "@tamagui/compiler-core";
2
2
  import { StyleObjectIdentifier, StyleObjectProperty, StyleObjectRules, StyleObjectValue, stylePropsAll, stylePropsText, validStyles } from "@tamagui/helpers";
3
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";
4
4
  import { createModifierRegistry, parseTransition, parseValue } from "@tamagui/style-grammar/tooling";
@@ -80,7 +80,9 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
80
80
  media: []
81
81
  };
82
82
  const classKeys = /* @__PURE__ */ new Map();
83
- for (const [key, identifier] of Object.entries(split.classNames ?? {})) if (typeof identifier === "string") classKeys.set(identifier, key);
83
+ for (const [key, identifier] of Object.entries(split.classNames ?? {})) {
84
+ if (typeof identifier === "string") classKeys.set(identifier, key);
85
+ }
84
86
  const identifiers = /* @__PURE__ */ new Set([...classKeys.keys(), ...Object.values(rules).flatMap((styleObject) => {
85
87
  const identifier = styleObject?.[StyleObjectIdentifier];
86
88
  return typeof identifier === "string" ? [identifier] : [];
@@ -88,11 +90,17 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
88
90
  for (const identifier of identifiers) {
89
91
  const key = classKeys.get(identifier) ?? "";
90
92
  const css2 = cssFromRules(Object.fromEntries(Object.entries(rules).filter(([, styleObject]) => styleObject?.[StyleObjectIdentifier] === identifier))).join("");
91
- if (identifier.startsWith("t_group_")) buckets.group.push(identifier);
92
- else if (pseudoNames.some((name) => key.endsWith(`-${name}`))) buckets.pseudo.push(identifier);
93
- else if (css2.includes(".t_")) buckets.theme.push(identifier);
94
- else if ([...mediaNames].some((name) => key.endsWith(`-${name}`))) buckets.media.push(identifier);
95
- else buckets.normal.push(identifier);
93
+ if (identifier.startsWith("t_group_")) {
94
+ buckets.group.push(identifier);
95
+ } else if (pseudoNames.some((name) => key.endsWith(`-${name}`))) {
96
+ buckets.pseudo.push(identifier);
97
+ } else if (css2.includes(".t_")) {
98
+ buckets.theme.push(identifier);
99
+ } else if ([...mediaNames].some((name) => key.endsWith(`-${name}`))) {
100
+ buckets.media.push(identifier);
101
+ } else {
102
+ buckets.normal.push(identifier);
103
+ }
96
104
  }
97
105
  const orderedIdentifiers = [
98
106
  ...buckets.group,
@@ -112,17 +120,28 @@ function extractedStyleArtifacts(split, props, config, includeRuntimeBase = true
112
120
  css
113
121
  };
114
122
  }
115
- function compiledPropsEdits(input, styleEntries, replacement) {
123
+ function spreadNonStyleReplacement(form, entry, isPropIgnored, rewriteProp) {
124
+ if (entry.kind !== "spread" || entry.value.kind !== "static" || !staticObject(entry.value.value)) {
125
+ return "";
126
+ }
127
+ const nonStyleEntries = Object.entries(entry.value.value).filter(([key]) => !isPropIgnored(key)).map(([key, value]) => rewriteProp(key, value));
128
+ if (nonStyleEntries.length === 0) return "";
129
+ const objectSource = `{ ${nonStyleEntries.map(([key, value]) => `${JSON.stringify(key)}: ${JSON.stringify(value)}`).join(", ")} }`;
130
+ return form === "jsx" ? `{...${objectSource}}` : `...${objectSource}`;
131
+ }
132
+ function compiledPropsEdits(input, styleEntries, replacement, spreadReplacement) {
116
133
  const propsSpan = input.element.propsSpan;
117
134
  if (!propsSpan) return null;
118
135
  const original = input.source.slice(propsSpan.start, propsSpan.end);
119
136
  const trimmed = original.trim();
120
- if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return [{
121
- start: propsSpan.start,
122
- end: propsSpan.end,
123
- content: `{ ${replacement} }`,
124
- origin: propsSpan
125
- }];
137
+ if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
138
+ return [{
139
+ start: propsSpan.start,
140
+ end: propsSpan.end,
141
+ content: `{ ${replacement} }`,
142
+ origin: propsSpan
143
+ }];
144
+ }
126
145
  if (styleEntries.length === 0) {
127
146
  const close = original.lastIndexOf("}");
128
147
  const separator = original.slice(1, close).trim() ? ", " : " ";
@@ -138,19 +157,22 @@ function compiledPropsEdits(input, styleEntries, replacement) {
138
157
  for (const [index, entry] of styleEntries.entries()) {
139
158
  let start = entry.span.start;
140
159
  let end = entry.span.end;
160
+ const nonStyle = spreadReplacement?.(entry) ?? "";
141
161
  if (index === 0) {
162
+ const content = [replacement, nonStyle].filter(Boolean).join(", ");
142
163
  edits.push({
143
164
  start,
144
165
  end,
145
- content: replacement,
166
+ content,
146
167
  origin: entry.span
147
168
  });
148
169
  continue;
149
170
  }
150
171
  let cursor = end - propsSpan.start;
151
172
  while (cursor < original.length - 1 && /\s/.test(original[cursor])) cursor++;
152
- if (original[cursor] === ",") end = propsSpan.start + cursor + 1;
153
- else {
173
+ if (original[cursor] === ",") {
174
+ end = propsSpan.start + cursor + 1;
175
+ } else {
154
176
  cursor = start - propsSpan.start - 1;
155
177
  while (cursor > 0 && /\s/.test(original[cursor])) cursor--;
156
178
  if (original[cursor] === ",") start = propsSpan.start + cursor;
@@ -158,15 +180,18 @@ function compiledPropsEdits(input, styleEntries, replacement) {
158
180
  edits.push({
159
181
  start,
160
182
  end,
161
- content: "",
183
+ content: nonStyle,
162
184
  origin: entry.span
163
185
  });
164
186
  }
165
187
  const merged = [];
166
188
  for (const edit of edits.sort((left, right) => left.start - right.start)) {
167
189
  const previous = merged.at(-1);
168
- if (previous && edit.start <= previous.end && !previous.content && !edit.content) previous.end = Math.max(previous.end, edit.end);
169
- else merged.push(edit);
190
+ if (previous && edit.start <= previous.end && !previous.content && !edit.content) {
191
+ previous.end = Math.max(previous.end, edit.end);
192
+ } else {
193
+ merged.push(edit);
194
+ }
170
195
  }
171
196
  return merged;
172
197
  }
@@ -421,7 +446,13 @@ const cssConflictFamilies = [
421
446
  ])
422
447
  ];
423
448
  function cssOwnersConflict(left, right) {
424
- for (const leftOwner of left) for (const rightOwner of right) if (leftOwner === rightOwner || cssShorthandConflicts[leftOwner]?.includes(rightOwner) || cssShorthandConflicts[rightOwner]?.includes(leftOwner) || cssConflictFamilies.some((family) => family.has(leftOwner) && family.has(rightOwner))) return true;
449
+ for (const leftOwner of left) {
450
+ for (const rightOwner of right) {
451
+ if (leftOwner === rightOwner || cssShorthandConflicts[leftOwner]?.includes(rightOwner) || cssShorthandConflicts[rightOwner]?.includes(leftOwner) || cssConflictFamilies.some((family) => family.has(leftOwner) && family.has(rightOwner))) {
452
+ return true;
453
+ }
454
+ }
455
+ }
425
456
  return false;
426
457
  }
427
458
  const runtimeEventProps = /* @__PURE__ */ new Set([
@@ -442,7 +473,9 @@ const nativePointerEventProps = /* @__PURE__ */ new Set([
442
473
  "onPointerUp"
443
474
  ]);
444
475
  function isSerializableNativeStyle(value) {
445
- if (value == null || typeof value === "number" || typeof value === "boolean") return true;
476
+ if (value == null || typeof value === "number" || typeof value === "boolean") {
477
+ return true;
478
+ }
446
479
  if (typeof value === "string") return true;
447
480
  if (Array.isArray(value)) return value.every(isSerializableNativeStyle);
448
481
  if (!staticObject(value)) return false;
@@ -453,7 +486,9 @@ function isSerializableNativeStyle(value) {
453
486
  function unusedIdentifier(source, base) {
454
487
  let candidate = base;
455
488
  let suffix = 0;
456
- while (new RegExp(`\\b${candidate}\\b`).test(source)) candidate = `${base}${++suffix}`;
489
+ while (new RegExp(`\\b${candidate}\\b`).test(source)) {
490
+ candidate = `${base}${++suffix}`;
491
+ }
457
492
  return candidate;
458
493
  }
459
494
  const VIEW_WRAPPER_PROPS = /^(aria-|accessibilityState$|accessibilityValue$|id$|tabIndex$|nativeID$)/;
@@ -487,7 +522,9 @@ function nativeDOMProps(input, tag) {
487
522
  const entries = input.element.entries.filter((entry) => entry.kind === "prop");
488
523
  const names = new Set(entries.map((entry) => entry.name));
489
524
  if (names.has("ref") || tag === "br") additions.push(["__tag", JSON.stringify(tag)]);
490
- if (TAGS[tag].backing === "text" || TAGS[tag].backing === "textinput") additions.push(["__inherit", "true"]);
525
+ if (TAGS[tag].backing === "text" || TAGS[tag].backing === "textinput") {
526
+ additions.push(["__inherit", "true"]);
527
+ }
491
528
  const add = (name, value) => additions.push([name, value]);
492
529
  const consume = (entry) => consumed.push(entry);
493
530
  for (const entry of entries) {
@@ -495,20 +532,24 @@ function nativeDOMProps(input, tag) {
495
532
  const attribute = (Object.hasOwn(ATTRIBUTES, entry.name) ? ATTRIBUTES[entry.name] : void 0) ?? (entry.name.startsWith("data-") ? ATTRIBUTES["data-*"] : void 0);
496
533
  const event = Object.hasOwn(EVENTS, entry.name) ? EVENTS[entry.name] : void 0;
497
534
  if (event) {
498
- if (event.native === "none") return {
499
- consumed,
500
- edits,
501
- additions,
502
- diagnostic: `${entry.name} has no native DOM event equivalent`,
503
- diagnosticSpan: entry.span
504
- };
505
- if (entry.name === "onKeyDown" && tag !== "input" && tag !== "textarea") return {
506
- consumed,
507
- edits,
508
- additions,
509
- diagnostic: `onKeyDown requires a native text-entry control`,
510
- diagnosticSpan: entry.span
511
- };
535
+ if (event.native === "none") {
536
+ return {
537
+ consumed,
538
+ edits,
539
+ additions,
540
+ diagnostic: `${entry.name} has no native DOM event equivalent`,
541
+ diagnosticSpan: entry.span
542
+ };
543
+ }
544
+ if (entry.name === "onKeyDown" && tag !== "input" && tag !== "textarea") {
545
+ return {
546
+ consumed,
547
+ edits,
548
+ additions,
549
+ diagnostic: `onKeyDown requires a native text-entry control`,
550
+ diagnosticSpan: entry.span
551
+ };
552
+ }
512
553
  if ([
513
554
  "onClick",
514
555
  "onLoad",
@@ -516,7 +557,9 @@ function nativeDOMProps(input, tag) {
516
557
  "onChange",
517
558
  "onInput",
518
559
  "onKeyDown"
519
- ].includes(entry.name)) continue;
560
+ ].includes(entry.name)) {
561
+ continue;
562
+ }
520
563
  if (event.nativeProp) {
521
564
  const edit2 = renamedPropEdit(input, entry, event.nativeProp);
522
565
  if (edit2) edits.push(edit2);
@@ -525,24 +568,28 @@ function nativeDOMProps(input, tag) {
525
568
  }
526
569
  if (!attribute || attribute.native === "none" || entry.name === "style") continue;
527
570
  if (DOM_STYLE_ATTRIBUTES.has(entry.name)) {
528
- if (entry.value.kind !== "static") return {
529
- consumed,
530
- edits,
531
- additions,
532
- diagnostic: `html.${tag} ${entry.name} must be statically known for native lowering`,
533
- diagnosticSpan: entry.span
534
- };
571
+ if (entry.value.kind !== "static") {
572
+ return {
573
+ consumed,
574
+ edits,
575
+ additions,
576
+ diagnostic: `html.${tag} ${entry.name} must be statically known for native lowering`,
577
+ diagnosticSpan: entry.span
578
+ };
579
+ }
535
580
  consume(entry);
536
581
  continue;
537
582
  }
538
583
  if (entry.name === "hidden") {
539
- if (entry.value.kind !== "static") return {
540
- consumed,
541
- edits,
542
- additions,
543
- diagnostic: `html.${tag} hidden must be statically known for native lowering`,
544
- diagnosticSpan: entry.span
545
- };
584
+ if (entry.value.kind !== "static") {
585
+ return {
586
+ consumed,
587
+ edits,
588
+ additions,
589
+ diagnostic: `html.${tag} hidden must be statically known for native lowering`,
590
+ diagnosticSpan: entry.span
591
+ };
592
+ }
546
593
  consume(entry);
547
594
  continue;
548
595
  }
@@ -568,18 +615,22 @@ function nativeDOMProps(input, tag) {
568
615
  }
569
616
  if (entry.name === "type") {
570
617
  consume(entry);
571
- if (tag === "input" && entry.value.kind !== "static") return {
572
- consumed,
573
- edits,
574
- additions,
575
- diagnostic: `html.input type must be statically known for native lowering`,
576
- diagnosticSpan: entry.span
577
- };
618
+ if (tag === "input" && entry.value.kind !== "static") {
619
+ return {
620
+ consumed,
621
+ edits,
622
+ additions,
623
+ diagnostic: `html.input type must be statically known for native lowering`,
624
+ diagnosticSpan: entry.span
625
+ };
626
+ }
578
627
  if (tag === "input" && entry.value.kind === "static") {
579
628
  const type = entry.value.value;
580
629
  if (type === "password") add("secureTextEntry", "true");
581
630
  else if (typeof type === "string" && type !== "text" && NATIVE_INPUT_TYPES.includes(type)) {
582
- if (!names.has("inputMode")) add("inputMode", JSON.stringify(type === "number" ? "numeric" : type));
631
+ if (!names.has("inputMode")) {
632
+ add("inputMode", JSON.stringify(type === "number" ? "numeric" : type));
633
+ }
583
634
  }
584
635
  }
585
636
  continue;
@@ -608,7 +659,9 @@ function nativeDOMProps(input, tag) {
608
659
  const edit = renamedPropEdit(input, entry, nativeProp);
609
660
  if (edit) edits.push(edit);
610
661
  }
611
- for (const [name, values] of nested) add(name, `{ ${values.map(([key, value]) => `${key}: ${value}`).join(", ")} }`);
662
+ for (const [name, values] of nested) {
663
+ add(name, `{ ${values.map(([key, value]) => `${key}: ${value}`).join(", ")} }`);
664
+ }
612
665
  if (!names.has("role") && TAGS[tag].role) add("role", JSON.stringify(TAGS[tag].role));
613
666
  if (tag === "textarea") add("multiline", "true");
614
667
  return {
@@ -627,15 +680,19 @@ function webDOMProps(input, tag) {
627
680
  const edit = renamedPropEdit(input, entry, "htmlFor");
628
681
  if (edit) edits.push(edit);
629
682
  }
630
- if (entry.name === "role" && entry.value.kind === "static" && entry.value.value === "none") edits.push({
631
- start: entry.value.span.start,
632
- end: entry.value.span.end,
633
- content: JSON.stringify("presentation"),
634
- origin: entry.value.span
635
- });
683
+ if (entry.name === "role" && entry.value.kind === "static" && entry.value.value === "none") {
684
+ edits.push({
685
+ start: entry.value.span.start,
686
+ end: entry.value.span.end,
687
+ content: JSON.stringify("presentation"),
688
+ origin: entry.value.span
689
+ });
690
+ }
636
691
  }
637
692
  if (tag === "button" && !names.has("type")) additions.push(["type", "\"button\""]);
638
- if ((tag === "input" || tag === "textarea") && !names.has("dir")) additions.push(["dir", "\"auto\""]);
693
+ if ((tag === "input" || tag === "textarea") && !names.has("dir")) {
694
+ additions.push(["dir", "\"auto\""]);
695
+ }
639
696
  return {
640
697
  edits,
641
698
  additions
@@ -645,7 +702,8 @@ function createTamaguiCompilerHost(options) {
645
702
  const platform = options.target === "native" ? "native" : "web";
646
703
  const core = requireTamaguiCore(platform);
647
704
  const firstThemeName = Object.keys(options.tamaguiConfig.themes ?? {})[0] ?? "";
648
- const theme = options.tamaguiConfig.themes?.[firstThemeName] ?? {};
705
+ const firstTheme = options.tamaguiConfig.themes?.[firstThemeName] ?? {};
706
+ const theme = firstTheme;
649
707
  const modifierRegistry = createModifierRegistry({
650
708
  mediaNames: options.tamaguiConfig.media ?? {},
651
709
  themeNames: options.tamaguiConfig.themes ?? {}
@@ -682,17 +740,25 @@ function createTamaguiCompilerHost(options) {
682
740
  resolvedPayloads.push(payload);
683
741
  continue;
684
742
  }
685
- if (transition.value.entries.length !== 1 || transition.value.entries[0].timing.type !== "preset") return null;
743
+ if (transition.value.entries.length !== 1 || transition.value.entries[0].timing.type !== "preset") {
744
+ return null;
745
+ }
686
746
  const preset = transitionPresets[transition.value.entries[0].timing.name];
687
747
  if (typeof preset !== "string") return null;
688
748
  const parsedPreset = parseTransition(preset);
689
- 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") return null;
749
+ 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") {
750
+ return null;
751
+ }
690
752
  resolvedPayloads.push(`all ${preset}`);
691
753
  }
692
754
  let payloadIndex = 0;
693
755
  const resolved = [];
694
- if (program.value.base !== null) resolved.push(resolvedPayloads[payloadIndex++]);
695
- for (const clause of program.value.clauses) resolved.push(`${clause.modifiers.join(":")}:${resolvedPayloads[payloadIndex++]}`);
756
+ if (program.value.base !== null) {
757
+ resolved.push(resolvedPayloads[payloadIndex++]);
758
+ }
759
+ for (const clause of program.value.clauses) {
760
+ resolved.push(`${clause.modifiers.join(":")}:${resolvedPayloads[payloadIndex++]}`);
761
+ }
696
762
  return resolved.join(" ");
697
763
  };
698
764
  const modulesById = new Map(options.componentModules.map((module) => [module.resolvedId, module.moduleName]));
@@ -702,7 +768,8 @@ function createTamaguiCompilerHost(options) {
702
768
  const identity = element.component.provenance;
703
769
  if (!identity) return null;
704
770
  const moduleName = modulesById.get(identity.resolvedId);
705
- const info = (moduleName ? componentsByModule.get(moduleName) : void 0)?.nameToInfo[identity.importedName];
771
+ const component = moduleName ? componentsByModule.get(moduleName) : void 0;
772
+ const info = component?.nameToInfo[identity.importedName];
706
773
  return info ? {
707
774
  key: componentKey(identity.resolvedId, identity.importedName),
708
775
  staticConfig: normalizeStaticConfig(info.staticConfig),
@@ -712,9 +779,12 @@ function createTamaguiCompilerHost(options) {
712
779
  const domStaticConfig = (element) => {
713
780
  const identity = element.component.provenance;
714
781
  const tag = element.component.name;
715
- if (identity?.importedName !== "html" || !DOM_FRONTENDS.has(identity.specifier) || !Object.hasOwn(TAGS, tag)) return null;
782
+ if (identity?.importedName !== "html" || !DOM_FRONTENDS.has(identity.specifier) || !Object.hasOwn(TAGS, tag)) {
783
+ return null;
784
+ }
716
785
  const row = TAGS[tag];
717
- const base = (row.backing === "text" || row.backing === "textinput" ? core.Text : core.View)?.staticConfig;
786
+ const textLike = row.backing === "text" || row.backing === "textinput";
787
+ const base = (textLike ? core.Text : core.View)?.staticConfig;
718
788
  if (!base) return null;
719
789
  const platformDefaults = platform === "web" ? {
720
790
  ...DISPLAY_WEB_RESET[row.display],
@@ -818,7 +888,9 @@ function createTamaguiCompilerHost(options) {
818
888
  return name in stylePropsAll && !isValidStyleKey(name, validStyles$1, staticConfig.accept);
819
889
  };
820
890
  const directStyleName = (name, component) => {
821
- if (compilerStyleProps.has(name) || name === "style") return null;
891
+ if (compilerStyleProps.has(name) || name === "style") {
892
+ return null;
893
+ }
822
894
  const staticConfig = component.staticConfig;
823
895
  if (staticConfig.variants?.[name]) return null;
824
896
  const expanded = options.tamaguiConfig.shorthands?.[name] ?? name;
@@ -826,11 +898,15 @@ function createTamaguiCompilerHost(options) {
826
898
  };
827
899
  const resolveSplitStyles = (props, staticConfig, animationDriver, displayName) => {
828
900
  const previousStatic = process.env.IS_STATIC;
829
- const previousTarget = "web";
830
- if (platform === "native") process.env.IS_STATIC = "is_static";
831
- else delete process.env.IS_STATIC;
901
+ const previousTarget = process.env.TAMAGUI_TARGET;
902
+ if (platform === "native") {
903
+ process.env.IS_STATIC = "is_static";
904
+ } else {
905
+ delete process.env.IS_STATIC;
906
+ }
832
907
  process.env.TAMAGUI_TARGET = platform;
833
908
  try {
909
+ core.prepareStyleStaticConfig(staticConfig);
834
910
  return core.getSplitStyles(props, staticConfig, theme, firstThemeName, componentState, {
835
911
  resolveValues: platform === "native" ? "except-theme" : "variable",
836
912
  noClass: platform === "native",
@@ -840,7 +916,8 @@ function createTamaguiCompilerHost(options) {
840
916
  } finally {
841
917
  if (previousStatic === void 0) delete process.env.IS_STATIC;
842
918
  else process.env.IS_STATIC = previousStatic;
843
- process.env.TAMAGUI_TARGET = previousTarget;
919
+ if (previousTarget === void 0) delete process.env.TAMAGUI_TARGET;
920
+ else process.env.TAMAGUI_TARGET = previousTarget;
844
921
  }
845
922
  };
846
923
  const partialStaticConfig = (staticConfig) => ({
@@ -855,7 +932,9 @@ function createTamaguiCompilerHost(options) {
855
932
  const split = resolveSplitStyles({ [name]: value }, partialStaticConfig(staticConfig));
856
933
  if (!split) return null;
857
934
  const inlineStyle = split.viewProps?.style;
858
- if (staticObject(inlineStyle) && !inlineStyle["$$css"] && Object.keys(inlineStyle).length > 0) return null;
935
+ if (staticObject(inlineStyle) && Object.keys(inlineStyle).length > 0) {
936
+ return null;
937
+ }
859
938
  const owners = new Set(Object.keys(split.classNames ?? {}));
860
939
  for (const styleObject of Object.values(split.rulesToInsert ?? {})) {
861
940
  const property = styleObject?.[StyleObjectProperty];
@@ -864,7 +943,7 @@ function createTamaguiCompilerHost(options) {
864
943
  return owners.size > 0 ? owners : null;
865
944
  };
866
945
  const dynamicStyleOwners = (name, staticConfig) => {
867
- const owners = styleOwners(name, name === "transform" ? [{ scale: 1 }] : name === "transformMatrix" ? [
946
+ const probeValue = name === "transform" ? [{ scale: 1 }] : name === "transformMatrix" ? [
868
947
  1,
869
948
  0,
870
949
  0,
@@ -874,7 +953,8 @@ function createTamaguiCompilerHost(options) {
874
953
  ] : name === "shadowOffset" ? {
875
954
  width: 0,
876
955
  height: 0
877
- } : name === "shadowColor" ? "black" : name === "border" || name === "outline" ? "0 solid transparent" : name === "position" ? "relative" : name === "objectFit" ? "contain" : 0, staticConfig);
956
+ } : name === "shadowColor" ? "black" : name === "border" || name === "outline" ? "0 solid transparent" : name === "position" ? "relative" : name === "objectFit" ? "contain" : 0;
957
+ const owners = styleOwners(name, probeValue, staticConfig);
878
958
  if (!owners || name !== "flex") return owners;
879
959
  const shorthandOwners = styleOwners(name, "0 1 auto", staticConfig);
880
960
  if (!shorthandOwners) return null;
@@ -895,28 +975,35 @@ function createTamaguiCompilerHost(options) {
895
975
  ] : ["bailed"];
896
976
  const why = result.ok ? flattened ? "Static styles were lowered and the component was flattened to a host element." : "Static styles were lowered while the Tamagui component remained at runtime." : result.bailout.message;
897
977
  const styles = styleEntries.map((entry) => {
898
- if (!result.ok) return {
899
- prop: entry.name,
900
- tier: "bailed",
901
- runtime: true,
902
- why: `${result.bailout.message}. The Tamagui runtime resolved this prop.`
903
- };
978
+ if (!result.ok) {
979
+ return {
980
+ prop: entry.name,
981
+ tier: "bailed",
982
+ runtime: true,
983
+ why: `${result.bailout.message}. The Tamagui runtime resolved this prop.`
984
+ };
985
+ }
904
986
  const edited = result.edits.some((edit) => edit.start < entry.span.end && edit.end > entry.span.start);
905
- if (!flattened && !edited) return {
906
- prop: entry.name,
907
- tier: "bailed",
908
- runtime: true,
909
- why: "This prop remained for Tamagui runtime resolution."
910
- };
911
- if (entry.value.kind === "static") {
912
- const split = resolveSplitStyles({ [entry.name]: entry.value.value }, partialStaticConfig(component.staticConfig));
913
- if (!Boolean(split && (Object.keys(split.classNames ?? {}).length > 0 || Object.keys(split.rulesToInsert ?? {}).length > 0 || staticObject(split.style) && Object.keys(split.style).length > 0 || staticObject(split.viewProps?.style) && Object.keys(split.viewProps.style).length > 0))) return {
987
+ if (!flattened && !edited) {
988
+ return {
914
989
  prop: entry.name,
915
- tier: flattened ? "flattened" : "lowered",
916
- dropped: true,
917
- why: `No ${platform} style output was produced for this prop.`
990
+ tier: "bailed",
991
+ runtime: true,
992
+ why: "This prop remained for Tamagui runtime resolution."
918
993
  };
919
994
  }
995
+ if (entry.value.kind === "static") {
996
+ const split = resolveSplitStyles({ [entry.name]: entry.value.value }, partialStaticConfig(component.staticConfig));
997
+ const hasOutput = Boolean(split && (Object.keys(split.classNames ?? {}).length > 0 || Object.keys(split.rulesToInsert ?? {}).length > 0 || staticObject(split.style) && Object.keys(split.style).length > 0 || staticObject(split.viewProps?.style) && Object.keys(split.viewProps.style).length > 0));
998
+ if (!hasOutput) {
999
+ return {
1000
+ prop: entry.name,
1001
+ tier: flattened ? "flattened" : "lowered",
1002
+ dropped: true,
1003
+ why: `No ${platform} style output was produced for this prop.`
1004
+ };
1005
+ }
1006
+ }
920
1007
  return {
921
1008
  prop: entry.name,
922
1009
  tier: flattened ? "flattened" : "lowered",
@@ -941,68 +1028,94 @@ function createTamaguiCompilerHost(options) {
941
1028
  resolveComponent: resolve,
942
1029
  isStyleProp,
943
1030
  canLowerDynamicStyleProp(name, component, valueKind) {
944
- if (!options.disablePartialExtraction && valueKind === "conditional" && canLowerConditionalStyleProp(name, component)) return true;
1031
+ if (!options.disablePartialExtraction && valueKind === "conditional" && canLowerConditionalStyleProp(name, component)) {
1032
+ return true;
1033
+ }
945
1034
  return !options.disablePartialExtraction && (platform === "web" && !!directStyleName(name, component) || platform === "native" && directStyleName(name, component) === "opacity");
946
1035
  },
947
1036
  developmentDebugInstrumentation,
948
1037
  lowerCandidate(input) {
949
1038
  const component = input.component;
950
- if (!component.canFlatten) return bailout(input, "local/unsupported-target", component.staticConfig.acceptsClassName === false ? `${component.key} does not accept className` : component.staticConfig.neverFlatten ? `${component.key} is never flattened (behavior HOC)` : `${component.key} provides a styled context`, input.element.span, { rule: 6 });
1039
+ if (!component.canFlatten) {
1040
+ const reason = component.staticConfig.acceptsClassName === false ? `${component.key} does not accept className` : component.staticConfig.neverFlatten ? `${component.key} is never flattened (behavior HOC)` : `${component.key} provides a styled context`;
1041
+ return bailout(input, "local/unsupported-target", reason, input.element.span, { rule: 6 });
1042
+ }
951
1043
  if (options.zeroRuntime) {
952
1044
  const spread = input.element.entries.find((entry) => entry.kind === "spread");
953
- if (spread) return bailout(input, "local/unsafe-style-spread", "Zero-runtime rejects prop spreads", spread.span, {
954
- rule: 1,
955
- message: zeroRuleMessage(1, { component: input.element.component.name })
956
- });
1045
+ if (spread) {
1046
+ return bailout(input, "local/unsafe-style-spread", "Zero-runtime rejects prop spreads", spread.span, {
1047
+ rule: 1,
1048
+ message: zeroRuleMessage(1, { component: input.element.component.name })
1049
+ });
1050
+ }
957
1051
  }
958
1052
  const props = {};
959
1053
  for (const entry of input.element.entries) {
960
1054
  if (entry.kind === "child" || entry.value.kind !== "static") continue;
961
1055
  if (entry.kind === "spread") {
962
- if (!staticObject(entry.value.value)) return bailout(input, "local/unsafe-style-spread", "Static spread did not materialize to an object", entry.span);
1056
+ if (!staticObject(entry.value.value)) {
1057
+ return bailout(input, "local/unsafe-style-spread", "Static spread did not materialize to an object", entry.span);
1058
+ }
963
1059
  Object.assign(props, entry.value.value);
964
- } else props[entry.name] = entry.value.value;
1060
+ } else {
1061
+ props[entry.name] = entry.value.value;
1062
+ }
965
1063
  }
966
1064
  const disableOptimizationEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "disableOptimization");
967
- if (disableOptimizationEntry || Object.hasOwn(props, "disableOptimization")) return bailout(input, "local/unsupported-target", "disableOptimization keeps the component on the runtime path", disableOptimizationEntry?.span);
1065
+ if (disableOptimizationEntry || Object.hasOwn(props, "disableOptimization")) {
1066
+ return bailout(input, "local/unsupported-target", "disableOptimization keeps the component on the runtime path", disableOptimizationEntry?.span);
1067
+ }
968
1068
  if (component.domTag && props.hidden) props.display = "none";
969
1069
  if (component.domTag && platform === "native") {
970
- for (const [name, styleKey] of DOM_STYLE_ATTRIBUTES) if (name in props) {
971
- props[styleKey] = props[name];
972
- delete props[name];
1070
+ for (const [name, styleKey] of DOM_STYLE_ATTRIBUTES) {
1071
+ if (name in props) {
1072
+ props[styleKey] = props[name];
1073
+ delete props[name];
1074
+ }
973
1075
  }
974
1076
  }
975
- if (platform === "native" && component.domTag === "input" && typeof props.type === "string" && !NATIVE_INPUT_TYPES.includes(props.type)) return bailout(input, "local/unsupported-target", `input type ${props.type} has no native text-entry control`);
1077
+ if (platform === "native" && component.domTag === "input" && typeof props.type === "string" && !NATIVE_INPUT_TYPES.includes(props.type)) {
1078
+ return bailout(input, "local/unsupported-target", `input type ${props.type} has no native text-entry control`);
1079
+ }
976
1080
  const dynamicStyleEntries = input.element.entries.filter((entry) => entry.kind === "prop" && (entry.value.kind === "bailout" || entry.value.kind === "conditional") && isStyleProp(entry.name, component));
977
1081
  {
978
1082
  const needsRuntimeMapping = (name) => runtimeEventProps.has(name) || platform === "native" && !component.domTag && nativePointerEventProps.has(name);
979
1083
  const directRuntimeEvent = input.element.entries.find((entry) => entry.kind === "prop" && needsRuntimeMapping(entry.name));
980
1084
  const runtimeEvent = directRuntimeEvent?.kind === "prop" ? directRuntimeEvent.name : Object.keys(props).find(needsRuntimeMapping);
981
- if (runtimeEvent) return bailout(input, "local/unsupported-target", `${runtimeEvent} requires Tamagui runtime event mapping`);
1085
+ if (runtimeEvent) {
1086
+ return bailout(input, "local/unsupported-target", `${runtimeEvent} requires Tamagui runtime event mapping`);
1087
+ }
982
1088
  }
983
1089
  const animationDefaultProps = core.getDefaultProps(component.staticConfig) ?? {};
984
1090
  const animationProps = core.mergeProps(animationDefaultProps, props);
985
1091
  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)]);
986
1092
  const animateOnlyEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "animateOnly");
987
- if (animationNames.has("animateOnly")) return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", animateOnlyEntry?.span, {
988
- rule: 5,
989
- message: zeroRuleMessage(5, { detail: animateOnlyEntry ? `animateOnly on ${input.element.component.name}` : `animateOnly in the styled() definition of ${input.element.component.name}` })
990
- });
1093
+ if (animationNames.has("animateOnly")) {
1094
+ return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", animateOnlyEntry?.span, {
1095
+ rule: 5,
1096
+ message: zeroRuleMessage(5, { detail: animateOnlyEntry ? `animateOnly on ${input.element.component.name}` : `animateOnly in the styled() definition of ${input.element.component.name}` })
1097
+ });
1098
+ }
991
1099
  const transitionEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "transition");
992
1100
  const animatedBy = typeof animationProps.animatedBy === "string" ? animationProps.animatedBy.trim() : null;
993
1101
  const namedAnimationDriver = animatedBy === null ? null : options.tamaguiConfig.animationDrivers?.[animatedBy];
994
- const cssAnimationDriver = (platform === "web" && namedAnimationDriver?.outputStyle === "css" ? namedAnimationDriver : null) ?? (animatedBy === null || animatedBy === "default" ? configuredCssAnimationDriver : null);
1102
+ const namedCssAnimationDriver = platform === "web" && namedAnimationDriver && !namedAnimationDriver.isStub && namedAnimationDriver.outputStyle === "css" ? namedAnimationDriver : null;
1103
+ const cssAnimationDriver = namedCssAnimationDriver ?? (animatedBy === null || animatedBy === "default" ? configuredCssAnimationDriver : null);
995
1104
  const resolvedCssTransition = animationNames.has("transition") && cssAnimationDriver ? resolveStaticCssTransition(animationProps.transition, cssAnimationDriver.animations ?? {}) : null;
996
- if (resolvedCssTransition !== null) props.transition = resolvedCssTransition;
1105
+ if (resolvedCssTransition !== null) {
1106
+ props.transition = resolvedCssTransition;
1107
+ }
997
1108
  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")));
998
1109
  const runtimeAnimationRequired = animationNames.has("transition") && resolvedCssTransition === null || [...animationNames].some((name) => name !== "transition" && name !== "animatedBy" && name !== "animateOnly") || animatedByNeedsRuntime;
999
1110
  let dynamicHostStyleProperties = null;
1000
- if (platform === "web" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicStyleEntries.length > 0 && !input.element.entries.some((entry) => entry.kind === "spread")) {
1111
+ if (platform === "web" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicStyleEntries.length > 0 && !input.element.entries.some((entry) => entry.kind === "spread" && entry.value.kind !== "static")) {
1001
1112
  const seen = /* @__PURE__ */ new Set();
1002
1113
  const properties = [];
1003
1114
  const dynamicOwners = /* @__PURE__ */ new Set();
1004
1115
  for (const entry of dynamicStyleEntries) {
1005
- if (entry.kind !== "prop" || entry.value.kind !== "bailout" && entry.value.kind !== "conditional") continue;
1116
+ if (entry.kind !== "prop" || entry.value.kind !== "bailout" && entry.value.kind !== "conditional") {
1117
+ continue;
1118
+ }
1006
1119
  const name = directStyleName(entry.name, component);
1007
1120
  if (!name || seen.has(name)) {
1008
1121
  properties.length = 0;
@@ -1015,11 +1128,13 @@ function createTamaguiCompilerHost(options) {
1015
1128
  }
1016
1129
  let property = null;
1017
1130
  const expression = input.source.slice(entry.value.span.start, entry.value.span.end);
1018
- if (resolvedCssTransition !== null && (name === "opacity" || name === "scale")) property = name === "opacity" ? `opacity: (${expression})` : `transform: "scale(" + (${expression}) + ")"`;
1019
- else if (entry.value.kind === "bailout" && owners.size === 1 && owners.has(name)) {
1131
+ if (resolvedCssTransition !== null && (name === "opacity" || name === "scale")) {
1132
+ property = name === "opacity" ? `opacity: (${expression})` : `transform: "scale(" + (${expression}) + ")"`;
1133
+ } else if (entry.value.kind === "bailout" && owners.size === 1 && owners.has(name)) {
1020
1134
  const dynamic = entry.value.dynamic;
1021
- if (dynamic?.type === "number") property = `${JSON.stringify(name)}: (${expression})`;
1022
- else if (dynamic?.type === "string" && dynamic.values?.length) {
1135
+ if (dynamic?.type === "number") {
1136
+ property = `${JSON.stringify(name)}: (${expression})`;
1137
+ } else if (dynamic?.type === "string" && dynamic.values?.length) {
1023
1138
  let valuesStayLiteral = true;
1024
1139
  for (const value of dynamic.values) {
1025
1140
  const split2 = resolveSplitStyles({ [entry.name]: value }, partialStaticConfig(component.staticConfig));
@@ -1029,7 +1144,9 @@ function createTamaguiCompilerHost(options) {
1029
1144
  break;
1030
1145
  }
1031
1146
  }
1032
- if (valuesStayLiteral) property = `${JSON.stringify(name)}: (${expression})`;
1147
+ if (valuesStayLiteral) {
1148
+ property = `${JSON.stringify(name)}: (${expression})`;
1149
+ }
1033
1150
  }
1034
1151
  }
1035
1152
  if (!property) {
@@ -1046,9 +1163,11 @@ function createTamaguiCompilerHost(options) {
1046
1163
  if (!name) return false;
1047
1164
  const owners = styleOwners(name, entry.value.value, component.staticConfig);
1048
1165
  return !!owners && cssOwnersConflict(owners, dynamicOwners);
1049
- })) dynamicHostStyleProperties = properties;
1166
+ })) {
1167
+ dynamicHostStyleProperties = properties;
1168
+ }
1050
1169
  }
1051
- const supportsWebConditionalClasses = platform === "web" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicHostStyleProperties === null && dynamicStyleEntries.length === 1 && dynamicStyleEntries.every((entry) => entry.kind === "prop" && entry.value.kind === "conditional" && canLowerConditionalStyleProp(entry.name, component));
1170
+ const supportsWebConditionalClasses = platform === "web" && !options.disablePartialExtraction && (input.element.form === "jsx" || input.element.propsSpan !== null) && dynamicHostStyleProperties === null && dynamicStyleEntries.length > 0 && dynamicStyleEntries.every((entry) => entry.kind === "prop" && entry.value.kind === "conditional" && canLowerConditionalStyleProp(entry.name, component));
1052
1171
  if ("theme" in props || "themeInverse" in props) {
1053
1172
  const themeProp = "theme" in props ? "theme" : "themeInverse";
1054
1173
  return bailout(input, "local/unsupported-target", "Theme boundary candidates remain on the runtime path", input.element.span, {
@@ -1057,14 +1176,20 @@ function createTamaguiCompilerHost(options) {
1057
1176
  });
1058
1177
  }
1059
1178
  const asChildEntry = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "asChild");
1060
- if (asChildEntry || Object.hasOwn(props, "asChild")) return bailout(input, "local/unsupported-target", "asChild renders a Slot, not a host view", asChildEntry?.span);
1061
- if (platform === "native" && ("group" in props || "container" in props || "containerName" in props || "containerType" in props)) return bailout(input, "local/unsupported-target", "Native group containers remain on the runtime path");
1062
- if (runtimeAnimationRequired && !cssAnimationDriver) return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", transitionEntry?.span, {
1063
- rule: 5,
1064
- message: zeroRuleMessage(5, { detail: `the animation configured on ${input.element.component.name}` })
1065
- });
1179
+ if (asChildEntry || Object.hasOwn(props, "asChild")) {
1180
+ return bailout(input, "local/unsupported-target", "asChild renders a Slot, not a host view", asChildEntry?.span);
1181
+ }
1182
+ if (platform === "native" && ("group" in props || "container" in props || "containerName" in props || "containerType" in props)) {
1183
+ return bailout(input, "local/unsupported-target", "Native group and container providers remain on the runtime path");
1184
+ }
1185
+ if (runtimeAnimationRequired && !cssAnimationDriver) {
1186
+ return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", transitionEntry?.span, {
1187
+ rule: 5,
1188
+ message: zeroRuleMessage(5, { detail: `the animation configured on ${input.element.component.name}` })
1189
+ });
1190
+ }
1066
1191
  if (platform === "web" && (dynamicStyleEntries.length > 0 || runtimeAnimationRequired) && dynamicHostStyleProperties === null && !supportsWebConditionalClasses && component.partialRuntimeSafe) {
1067
- const hasSpread = input.element.entries.some((entry) => entry.kind === "spread");
1192
+ const hasSpread = input.element.entries.some((entry) => entry.kind === "spread" && entry.value.kind !== "static");
1068
1193
  const unsupportedRuntimeStyle = input.element.entries.find((entry) => entry.kind === "prop" && isStyleProp(entry.name, component) && !runtimeAnimationProps.has(entry.name) && !directStyleName(entry.name, component));
1069
1194
  if (!hasSpread && !unsupportedRuntimeStyle) {
1070
1195
  const dynamicOwners = /* @__PURE__ */ new Set();
@@ -1093,49 +1218,59 @@ function createTamaguiCompilerHost(options) {
1093
1218
  }) : [];
1094
1219
  if (staticStyleEntries.length > 0) {
1095
1220
  const partialProps = {};
1096
- for (const entry of staticStyleEntries) if (entry.kind === "prop" && entry.value.kind === "static") partialProps[entry.name] = entry.value.value;
1221
+ for (const entry of staticStyleEntries) {
1222
+ if (entry.kind === "prop" && entry.value.kind === "static") {
1223
+ partialProps[entry.name] = entry.value.value;
1224
+ }
1225
+ }
1097
1226
  const partialSplit = resolveSplitStyles(partialProps, partialStaticConfig(component.staticConfig));
1098
1227
  const partialInlineStyle = partialSplit?.viewProps?.style;
1099
- const hasPartialInlineStyle = staticObject(partialInlineStyle) && !partialInlineStyle["$$css"] && Object.keys(partialInlineStyle).length > 0;
1228
+ const hasPartialInlineStyle = staticObject(partialInlineStyle) && Object.keys(partialInlineStyle).length > 0;
1100
1229
  if (partialSplit && !hasPartialInlineStyle) {
1101
1230
  const artifacts2 = extractedStyleArtifacts(partialSplit, partialProps, options.tamaguiConfig, false);
1102
- if (artifacts2.className) if (input.element.form !== "jsx") {
1103
- const propsEdits = compiledPropsEdits(input, staticStyleEntries, objectClassName(artifacts2.className));
1104
- if (propsEdits) return {
1105
- ok: true,
1106
- edits: propsEdits,
1107
- css: artifacts2.css,
1108
- imports: [],
1109
- flattened: false
1110
- };
1111
- } else {
1112
- const [first2, ...rest2] = staticStyleEntries;
1113
- return {
1114
- ok: true,
1115
- edits: [{
1116
- start: first2.span.start,
1117
- end: first2.span.end,
1118
- content: jsxClassName(artifacts2.className),
1119
- origin: first2.span
1120
- }, ...rest2.map((entry) => ({
1121
- start: entry.span.start,
1122
- end: entry.span.end,
1123
- content: "",
1124
- origin: entry.span
1125
- }))],
1126
- css: artifacts2.css,
1127
- imports: [],
1128
- flattened: false
1129
- };
1231
+ if (artifacts2.className) {
1232
+ if (input.element.form !== "jsx") {
1233
+ const propsEdits = compiledPropsEdits(input, staticStyleEntries, objectClassName(artifacts2.className));
1234
+ if (propsEdits) {
1235
+ return {
1236
+ ok: true,
1237
+ edits: propsEdits,
1238
+ css: artifacts2.css,
1239
+ imports: [],
1240
+ flattened: false
1241
+ };
1242
+ }
1243
+ } else {
1244
+ const [first2, ...rest2] = staticStyleEntries;
1245
+ return {
1246
+ ok: true,
1247
+ edits: [{
1248
+ start: first2.span.start,
1249
+ end: first2.span.end,
1250
+ content: jsxClassName(artifacts2.className),
1251
+ origin: first2.span
1252
+ }, ...rest2.map((entry) => ({
1253
+ start: entry.span.start,
1254
+ end: entry.span.end,
1255
+ content: "",
1256
+ origin: entry.span
1257
+ }))],
1258
+ css: artifacts2.css,
1259
+ imports: [],
1260
+ flattened: false
1261
+ };
1262
+ }
1130
1263
  }
1131
1264
  }
1132
1265
  }
1133
1266
  }
1134
1267
  }
1135
- if (runtimeAnimationRequired) return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", transitionEntry?.span, {
1136
- rule: 5,
1137
- message: zeroRuleMessage(5, { detail: `the animation configured on ${input.element.component.name}` })
1138
- });
1268
+ if (runtimeAnimationRequired) {
1269
+ return bailout(input, "local/unsupported-target", "Animated candidates remain on the runtime path", transitionEntry?.span, {
1270
+ rule: 5,
1271
+ message: zeroRuleMessage(5, { detail: `the animation configured on ${input.element.component.name}` })
1272
+ });
1273
+ }
1139
1274
  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)));
1140
1275
  if (dynamicStyleEntries.length > 0 && dynamicHostStyleProperties === null && !supportsNativeDynamicStyles && !supportsWebConditionalClasses) {
1141
1276
  const entry = dynamicStyleEntries[0];
@@ -1144,18 +1279,59 @@ function createTamaguiCompilerHost(options) {
1144
1279
  const staticDefaultProps = core.getDefaultProps(component.staticConfig) ?? {};
1145
1280
  const defaultProps = platform === "web" && !component.staticConfig.isText && options.tamaguiConfig.settings.defaultPosition === "relative" && staticDefaultProps.position === void 0 ? core.mergeProps({ position: "relative" }, staticDefaultProps) : staticDefaultProps;
1146
1281
  let completeProps = core.mergeProps(defaultProps, props);
1147
- if (platform === "native" && component.domTag && props.display === "flex") completeProps = core.mergeProps(core.mergeProps(defaultProps, NATIVE_FLEX_DEFAULTS), props);
1282
+ if (platform === "native" && component.domTag && props.display === "flex") {
1283
+ completeProps = core.mergeProps(core.mergeProps(defaultProps, NATIVE_FLEX_DEFAULTS), props);
1284
+ }
1285
+ const propsForConditional = (target, value) => {
1286
+ const branchProps = {};
1287
+ for (const entry of input.element.entries) {
1288
+ if (entry === target) {
1289
+ branchProps[target.name] = value;
1290
+ continue;
1291
+ }
1292
+ if (entry.kind === "child" || entry.value.kind !== "static") continue;
1293
+ if (entry.kind === "spread") {
1294
+ if (staticObject(entry.value.value)) Object.assign(branchProps, entry.value.value);
1295
+ } else {
1296
+ branchProps[entry.name] = entry.value.value;
1297
+ }
1298
+ }
1299
+ if (component.domTag && branchProps.hidden) branchProps.display = "none";
1300
+ if (resolvedCssTransition !== null) {
1301
+ branchProps.transition = resolvedCssTransition;
1302
+ }
1303
+ if (component.domTag && platform === "native") {
1304
+ for (const [name, styleKey] of DOM_STYLE_ATTRIBUTES) {
1305
+ if (name in branchProps) {
1306
+ branchProps[styleKey] = branchProps[name];
1307
+ delete branchProps[name];
1308
+ }
1309
+ }
1310
+ }
1311
+ const branchCompleteProps = platform === "native" && component.domTag && branchProps.display === "flex" ? core.mergeProps(core.mergeProps(defaultProps, NATIVE_FLEX_DEFAULTS), branchProps) : core.mergeProps(defaultProps, branchProps);
1312
+ return {
1313
+ branchProps,
1314
+ branchCompleteProps
1315
+ };
1316
+ };
1148
1317
  if (platform === "native") {
1149
1318
  const isClauseValue = (name, value) => isStyleProp(name, component) && (typeof value === "string" && flatClausePattern.test(value) || isClauseObjectValue(value));
1150
1319
  const defaultVariants = component.staticConfig.defaultVariants ?? {};
1151
- if (Object.entries(completeProps).some(([name, value]) => isClauseValue(name, value)) || Object.entries(component.staticConfig.variants ?? {}).some(([variantName, definitions]) => (completeProps[variantName] !== void 0 || defaultVariants[variantName] !== void 0) && staticObject(definitions) && Object.values(definitions).some((definition) => staticObject(definition) && Object.entries(definition).some(([name, value]) => isClauseValue(name, value)))) || (component.staticConfig.compoundVariants ?? []).some((compound) => staticObject(compound) && staticObject(compound.style) && Object.entries(compound.style).some(([name, value]) => isClauseValue(name, value)))) return bailout(input, "local/unsupported-target", "Native conditional value programs remain on the runtime path");
1320
+ const carriesClause = Object.entries(completeProps).some(([name, value]) => isClauseValue(name, value)) || Object.entries(component.staticConfig.variants ?? {}).some(([variantName, definitions]) => (completeProps[variantName] !== void 0 || defaultVariants[variantName] !== void 0) && staticObject(definitions) && Object.values(definitions).some((definition) => staticObject(definition) && Object.entries(definition).some(([name, value]) => isClauseValue(name, value)))) || (component.staticConfig.compoundVariants ?? []).some((compound) => staticObject(compound) && staticObject(compound.style) && Object.entries(compound.style).some(([name, value]) => isClauseValue(name, value)));
1321
+ if (carriesClause) {
1322
+ return bailout(input, "local/unsupported-target", "Native conditional value programs remain on the runtime path");
1323
+ }
1152
1324
  }
1153
1325
  const split = resolveSplitStyles(completeProps, component.staticConfig, cssAnimationDriver, component.displayName);
1154
- if (!split) return bailout(input, "local/style-resolution-failed", "getSplitStyles returned no static result");
1155
- if (split.programLifecycleStyleKeys?.enter?.size || split.programLifecycleStyleKeys?.exit?.size) return bailout(input, "local/unsupported-target", "Lifecycle value programs remain on the runtime path", input.element.span, {
1156
- rule: 5,
1157
- message: zeroRuleMessage(5, { detail: `an enter or exit style program on ${input.element.component.name}` })
1158
- });
1326
+ if (!split) {
1327
+ return bailout(input, "local/style-resolution-failed", "getSplitStyles returned no static result");
1328
+ }
1329
+ if (split.programLifecycleStyleKeys?.enter?.size || split.programLifecycleStyleKeys?.exit?.size) {
1330
+ return bailout(input, "local/unsupported-target", "Lifecycle value programs remain on the runtime path", input.element.span, {
1331
+ rule: 5,
1332
+ message: zeroRuleMessage(5, { detail: `an enter or exit style program on ${input.element.component.name}` })
1333
+ });
1334
+ }
1159
1335
  const domStyleProgram = input.element.entries.find((entry) => entry.kind === "prop" && entry.name === "style" && entry.value.kind === "dom-style");
1160
1336
  const flatTag = component.domTag ?? (typeof props.render === "string" ? props.render : typeof defaultProps.render === "string" ? defaultProps.render : component.staticConfig.isText ? "span" : "div");
1161
1337
  const tagEdits = [input.element.component.span, input.element.component.closingSpan].filter((span) => !!span).map((span) => ({
@@ -1164,7 +1340,17 @@ function createTamaguiCompilerHost(options) {
1164
1340
  content: input.element.form === "jsx" ? flatTag : JSON.stringify(flatTag),
1165
1341
  origin: span
1166
1342
  }));
1167
- let styleEntries = input.element.entries.filter((entry) => entry.kind === "prop" && (isStyleProp(entry.name, component) || isInvalidHostStyleProp(entry.name, component)) || entry.kind === "spread" && entry.value.kind === "static" && staticObject(entry.value.value) && Object.keys(entry.value.value).every((name) => isStyleProp(name, component) || isInvalidHostStyleProp(name, component)));
1343
+ const isPropIgnored = (name) => isStyleProp(name, component) || isInvalidHostStyleProp(name, component);
1344
+ const spreadReplacement = (form, entry) => spreadNonStyleReplacement(form, entry, isPropIgnored, (name, value) => {
1345
+ if (platform !== "web") return [name, value];
1346
+ if (name === "testID") return ["data-testid", value];
1347
+ if (component.domTag && name === "for") return ["htmlFor", value];
1348
+ if (component.domTag && name === "role" && value === "none") {
1349
+ return ["role", "presentation"];
1350
+ }
1351
+ return [name, value];
1352
+ });
1353
+ let styleEntries = input.element.entries.filter((entry) => entry.kind === "prop" && (isStyleProp(entry.name, component) || isInvalidHostStyleProp(entry.name, component)) || entry.kind === "spread" && entry.value.kind === "static" && staticObject(entry.value.value) && Object.keys(entry.value.value).some((name) => isStyleProp(name, component) || isInvalidHostStyleProp(name, component)));
1168
1354
  let invalidHostStyle;
1169
1355
  for (const entry of input.element.entries) {
1170
1356
  if (entry.kind === "prop") {
@@ -1188,7 +1374,15 @@ function createTamaguiCompilerHost(options) {
1188
1374
  }
1189
1375
  }
1190
1376
  }
1191
- if (invalidHostStyle) return bailout(input, "local/unsupported-target", `"${invalidHostStyle.name}" is a text style prop and this component is not text. Use a Text-based component, or html.* for raw web elements.`, invalidHostStyle.entry.span);
1377
+ if (invalidHostStyle) {
1378
+ return bailout(input, "local/unsupported-target", `"${invalidHostStyle.name}" is a text style prop and this component is not text. Use a Text-based component, or html.* for raw web elements.`, invalidHostStyle.entry.span);
1379
+ }
1380
+ if (platform === "native" && component.domTag) {
1381
+ const mixedSpread = styleEntries.find((entry) => entry.kind === "spread" && entry.value.kind === "static" && staticObject(entry.value.value) && Object.keys(entry.value.value).some((name) => !isPropIgnored(name)));
1382
+ if (mixedSpread) {
1383
+ return bailout(input, "local/unsafe-style-spread", "Native DOM prop mapping requires non-style spread props to remain on the runtime path", mixedSpread.span);
1384
+ }
1385
+ }
1192
1386
  const webPropEdits = platform === "web" ? input.element.entries.flatMap((entry) => {
1193
1387
  if (entry.kind !== "prop" || entry.name !== "testID") return [];
1194
1388
  const content = input.source.slice(entry.span.start, entry.span.end);
@@ -1197,7 +1391,7 @@ function createTamaguiCompilerHost(options) {
1197
1391
  const alreadyQuoted = content[nameOffset - 1] === "\"" || content[nameOffset - 1] === "'";
1198
1392
  return [{
1199
1393
  start: entry.span.start + nameOffset,
1200
- end: entry.span.start + nameOffset + 6,
1394
+ end: entry.span.start + nameOffset + "testID".length,
1201
1395
  content: input.element.form === "jsx" || alreadyQuoted ? "data-testid" : `'data-testid'`,
1202
1396
  origin: entry.span
1203
1397
  }];
@@ -1207,8 +1401,6 @@ function createTamaguiCompilerHost(options) {
1207
1401
  additions: []
1208
1402
  };
1209
1403
  webPropEdits.push(...webDOMResult.edits);
1210
- const unsafeSpread = input.element.entries.find((entry) => entry.kind === "spread" && !styleEntries.includes(entry) && entry.value.kind === "static" && staticObject(entry.value.value) && Object.keys(entry.value.value).some((name) => isStyleProp(name, component)));
1211
- if (unsafeSpread) return bailout(input, "local/unsafe-style-spread", "A mixed style/non-style spread cannot be removed transactionally", unsafeSpread.span);
1212
1404
  if (platform === "native") {
1213
1405
  const nativeStyleResolved = split.viewProps?.style;
1214
1406
  let themedStyleKeys = null;
@@ -1217,16 +1409,25 @@ function createTamaguiCompilerHost(options) {
1217
1409
  for (const [styleKey, styleValue] of Object.entries(nativeStyleResolved)) {
1218
1410
  if (!core.containsThemeRef(styleValue)) continue;
1219
1411
  const themeKey = core.themeRefKey(styleValue);
1220
- if (!themeKey) return bailout(input, "local/dynamic-style-value", `Style ${styleKey} uses a theme value in a compound or modified position that compiled output cannot represent`);
1412
+ if (!themeKey) {
1413
+ return bailout(input, "local/dynamic-style-value", `Style ${styleKey} uses a theme value in a compound or modified position that compiled output cannot represent`);
1414
+ }
1415
+ ;
1221
1416
  (themedStyleKeys ||= {})[styleKey] = themeKey;
1222
1417
  }
1223
1418
  if (themedStyleKeys) {
1224
1419
  const plain = {};
1225
- for (const [styleKey, styleValue] of Object.entries(nativeStyleResolved)) if (!(styleKey in themedStyleKeys)) plain[styleKey] = styleValue;
1420
+ for (const [styleKey, styleValue] of Object.entries(nativeStyleResolved)) {
1421
+ if (!(styleKey in themedStyleKeys)) plain[styleKey] = styleValue;
1422
+ }
1226
1423
  nativeStyle = plain;
1227
1424
  }
1228
- } else if (core.containsThemeRef(nativeStyleResolved)) return bailout(input, "local/dynamic-style-value", "Theme values inside non-object native style output stay on the runtime path");
1229
- if (!isSerializableNativeStyle(nativeStyle)) return bailout(input, "local/unsupported-target", "Native style output is not a static serializable value");
1425
+ } else if (core.containsThemeRef(nativeStyleResolved)) {
1426
+ return bailout(input, "local/dynamic-style-value", "Theme values inside non-object native style output stay on the runtime path");
1427
+ }
1428
+ if (!isSerializableNativeStyle(nativeStyle)) {
1429
+ return bailout(input, "local/unsupported-target", "Native style output is not a static serializable value");
1430
+ }
1230
1431
  const nativeStyleLocal = unusedIdentifier(input.source, `__TamaguiNativeStyle${input.element.span.start}`);
1231
1432
  const nativeStyleImports = [{
1232
1433
  content: `
@@ -1247,12 +1448,16 @@ const ${mappingLocal} = ${JSON.stringify(themedStyleKeys)};`,
1247
1448
  };
1248
1449
  }
1249
1450
  if (component.domTag) {
1250
- if (themedStyleKeys) return bailout(input, "local/unsupported-target", "Theme values on DOM-tag native output stay on the runtime path");
1451
+ if (themedStyleKeys) {
1452
+ return bailout(input, "local/unsupported-target", "Theme values on DOM-tag native output stay on the runtime path");
1453
+ }
1251
1454
  const row = TAGS[component.domTag];
1252
1455
  const basePrimitive = NATIVE_BACKING[row.backing].primitive;
1253
1456
  let primitive = basePrimitive;
1254
1457
  const propsResult = nativeDOMProps(input, component.domTag);
1255
- if (propsResult.diagnostic) return bailout(input, "local/unsupported-target", propsResult.diagnostic, propsResult.diagnosticSpan);
1458
+ if (propsResult.diagnostic) {
1459
+ return bailout(input, "local/unsupported-target", propsResult.diagnostic, propsResult.diagnosticSpan);
1460
+ }
1256
1461
  styleEntries = [.../* @__PURE__ */ new Set([...styleEntries, ...propsResult.consumed])];
1257
1462
  let nativeDOMStyleSource = nativeStyleSource;
1258
1463
  let runtimeStyleSource = null;
@@ -1260,22 +1465,32 @@ const ${mappingLocal} = ${JSON.stringify(themedStyleKeys)};`,
1260
1465
  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))));
1261
1466
  const itemSources = [];
1262
1467
  for (const item of domStyleProgram.value.items) {
1263
- if (item.value.kind !== "static" || !staticObject(item.value.value)) return bailout(input, "local/dynamic-style-value", "Every style() handle in a style array must be statically evaluable", item.value.span);
1264
- if (Object.values(item.value.value).some((value2) => typeof value2 === "string" && nativeInitialKeywordPattern.test(value2))) return bailout(input, "local/unsupported-target", "Native DOM style() does not support the CSS initial keyword, matching the pinned upstream limitation", item.value.span);
1468
+ if (item.value.kind !== "static" || !staticObject(item.value.value)) {
1469
+ return bailout(input, "local/dynamic-style-value", "Every style() handle in a style array must be statically evaluable", item.value.span);
1470
+ }
1471
+ if (Object.values(item.value.value).some((value2) => typeof value2 === "string" && nativeInitialKeywordPattern.test(value2))) {
1472
+ return bailout(input, "local/unsupported-target", "Native DOM style() does not support the CSS initial keyword, matching the pinned upstream limitation", item.value.span);
1473
+ }
1265
1474
  let value;
1266
- if (needsRuntime) value = JSON.stringify(item.value.value);
1267
- else {
1268
- const itemStyle = resolveSplitStyles(item.value.value, partialStaticConfig(component.staticConfig))?.viewProps?.style;
1269
- if (!isSerializableNativeStyle(itemStyle) || core.containsThemeRef(itemStyle)) return bailout(input, "local/unsupported-target", "Native style() output is not serializable", item.value.span);
1475
+ if (needsRuntime) {
1476
+ value = JSON.stringify(item.value.value);
1477
+ } else {
1478
+ const itemSplit = resolveSplitStyles(item.value.value, partialStaticConfig(component.staticConfig));
1479
+ const itemStyle = itemSplit?.viewProps?.style;
1480
+ if (!isSerializableNativeStyle(itemStyle) || core.containsThemeRef(itemStyle)) {
1481
+ return bailout(input, "local/unsupported-target", "Native style() output is not serializable", item.value.span);
1482
+ }
1270
1483
  value = JSON.stringify(itemStyle ?? {});
1271
1484
  }
1272
1485
  const condition = item.condition ? input.source.slice(item.condition.start, item.condition.end) : null;
1273
1486
  itemSources.push(condition ? `(${condition}) && ${value}` : value);
1274
1487
  }
1275
1488
  if (needsRuntime) {
1276
- primitive = `DOMRuntime${basePrimitive.slice(3)}`;
1489
+ primitive = `DOMRuntime${basePrimitive.slice("DOM".length)}`;
1277
1490
  runtimeStyleSource = `[${itemSources.join(", ")}]`;
1278
- } else nativeDOMStyleSource = `[${[nativeDOMStyleSource, ...itemSources].join(", ")}]`;
1491
+ } else {
1492
+ nativeDOMStyleSource = `[${[nativeDOMStyleSource, ...itemSources].join(", ")}]`;
1493
+ }
1279
1494
  }
1280
1495
  const nativeLocal2 = unusedIdentifier(input.source, `__Tamagui${primitive}`);
1281
1496
  const propertyContent = [
@@ -1303,17 +1518,22 @@ import { ${primitive} as ${nativeLocal2} } from ${JSON.stringify(NATIVE_PRIMITIV
1303
1518
  let needsCreateElement = false;
1304
1519
  for (const entry of input.element.entries) {
1305
1520
  if (entry.kind !== "child") continue;
1306
- if (entry.value.kind === "empty" || entry.value.kind === "element" || entry.value.kind === "static" && (entry.value.value === null || typeof entry.value.value === "boolean")) continue;
1307
- if (entry.value.kind !== "static" || entry.value.literalOrigin !== true || typeof entry.value.value !== "string" && typeof entry.value.value !== "number") return bailout(input, "local/unsupported-child", `html.${component.domTag} has a direct child that may render unwrapped native text; write a literal as JSX text or wrap the child in html.span`, entry.span);
1521
+ if (entry.value.kind === "empty" || entry.value.kind === "element" || entry.value.kind === "static" && (entry.value.value === null || typeof entry.value.value === "boolean")) {
1522
+ continue;
1523
+ }
1524
+ if (entry.value.kind !== "static" || entry.value.literalOrigin !== true || typeof entry.value.value !== "string" && typeof entry.value.value !== "number") {
1525
+ return bailout(input, "local/unsupported-child", `html.${component.domTag} has a direct child that may render unwrapped native text; write a literal as JSX text or wrap the child in html.span`, entry.span);
1526
+ }
1308
1527
  needsText = true;
1309
1528
  const child = input.source.slice(entry.span.start, entry.span.end);
1310
- if (input.element.form === "jsx") literalEdits.push({
1311
- start: entry.span.start,
1312
- end: entry.span.end,
1313
- content: `<${textLocal} __inherit>${child}</${textLocal}>`,
1314
- origin: entry.span
1315
- });
1316
- else {
1529
+ if (input.element.form === "jsx") {
1530
+ literalEdits.push({
1531
+ start: entry.span.start,
1532
+ end: entry.span.end,
1533
+ content: `<${textLocal} __inherit>${child}</${textLocal}>`,
1534
+ origin: entry.span
1535
+ });
1536
+ } else {
1317
1537
  needsCreateElement = true;
1318
1538
  literalEdits.push({
1319
1539
  start: entry.span.start,
@@ -1323,36 +1543,44 @@ import { ${primitive} as ${nativeLocal2} } from ${JSON.stringify(NATIVE_PRIMITIV
1323
1543
  });
1324
1544
  }
1325
1545
  }
1326
- if (needsText) imports.push({
1327
- content: `
1546
+ if (needsText) {
1547
+ imports.push({
1548
+ content: `
1328
1549
  import { DOMText as ${textLocal} } from ${JSON.stringify(NATIVE_PRIMITIVE_MODULE)}
1329
1550
  `,
1330
- origin: input.element.component.span
1331
- });
1332
- if (needsCreateElement) imports.push({
1333
- content: `
1551
+ origin: input.element.component.span
1552
+ });
1553
+ }
1554
+ if (needsCreateElement) {
1555
+ imports.push({
1556
+ content: `
1334
1557
  import { createElement as ${createElementLocal} } from "react"
1335
1558
  `,
1336
- origin: input.element.component.span
1337
- });
1559
+ origin: input.element.component.span
1560
+ });
1561
+ }
1338
1562
  }
1563
+ const [first3, ...rest3] = styleEntries;
1564
+ const firstNonStyle3 = first3 ? spreadReplacement("jsx", first3) : "";
1339
1565
  const propsEdits = input.element.form === "jsx" ? styleEntries.length === 0 ? [{
1340
1566
  start: input.element.component.span.end,
1341
1567
  end: input.element.component.span.end,
1342
1568
  content: ` ${propertyContent}`,
1343
1569
  origin: input.element.component.span
1344
1570
  }] : [{
1345
- start: styleEntries[0].span.start,
1346
- end: styleEntries[0].span.end,
1347
- content: propertyContent,
1348
- origin: styleEntries[0].span
1349
- }, ...styleEntries.slice(1).map((entry) => ({
1571
+ start: first3.span.start,
1572
+ end: first3.span.end,
1573
+ content: [propertyContent, firstNonStyle3].filter(Boolean).join(" "),
1574
+ origin: first3.span
1575
+ }, ...rest3.map((entry) => ({
1350
1576
  start: entry.span.start,
1351
1577
  end: entry.span.end,
1352
- content: "",
1578
+ content: spreadReplacement("jsx", entry),
1353
1579
  origin: entry.span
1354
- }))] : compiledPropsEdits(input, styleEntries, propertyContent);
1355
- if (!propsEdits) return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1580
+ }))] : compiledPropsEdits(input, styleEntries, propertyContent, (entry) => spreadReplacement(input.element.form, entry));
1581
+ if (!propsEdits) {
1582
+ return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1583
+ }
1356
1584
  return {
1357
1585
  ok: true,
1358
1586
  edits: [
@@ -1370,7 +1598,9 @@ import { createElement as ${createElementLocal} } from "react"
1370
1598
  const useHostView = options.experimentalNativeFastPath && nativeName === "View" && isBareHostView(input.element);
1371
1599
  const nativeExport = useHostView ? "unstable_NativeView" : nativeName;
1372
1600
  const nativeLocal = unusedIdentifier(input.source, `__TamaguiNative${useHostView ? "HostView" : nativeName}`);
1373
- if (themedStyleKeys && options.disablePartialExtraction) return bailout(input, "local/dynamic-style-value", "Theme values require partial extraction, which is disabled");
1601
+ if (themedStyleKeys && options.disablePartialExtraction) {
1602
+ return bailout(input, "local/dynamic-style-value", "Theme values require partial extraction, which is disabled");
1603
+ }
1374
1604
  if (dynamicStyleEntries.length > 0 || themedStyleKeys) {
1375
1605
  if (nativeFastPath) {
1376
1606
  const fastLocal = `__TamaguiNativeFast${nativeName}${input.element.span.start}`;
@@ -1392,7 +1622,9 @@ import { createElement as ${createElementLocal} } from "react"
1392
1622
  content: "",
1393
1623
  origin: entry.span
1394
1624
  }))] : compiledPropsEdits(input, styleEntries, `_expressions: []`);
1395
- if (!styleEdits) return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1625
+ if (!styleEdits) {
1626
+ return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1627
+ }
1396
1628
  return {
1397
1629
  ok: true,
1398
1630
  edits: [...tagEdits4, ...styleEdits],
@@ -1420,34 +1652,63 @@ const ${fastLocal} = require('@tamagui/core')._withNativeStyle(${nativeLocal}, $
1420
1652
  const conditionalParts = [];
1421
1653
  const conditionalKeys = /* @__PURE__ */ new Set();
1422
1654
  const baseStyleForDiff = staticObject(nativeStyleResolved) ? nativeStyleResolved : {};
1423
- for (const entry of dynamicStyleEntries) if (entry.value.kind === "conditional" && directStyleName(entry.name, component) !== "opacity") {
1424
- const branchDiffs = [];
1425
- for (const branchValue of [entry.value.whenTrue, entry.value.whenFalse]) {
1426
- const branchSplit = resolveSplitStyles({
1427
- ...completeProps,
1428
- [entry.name]: branchValue
1429
- }, component.staticConfig, cssAnimationDriver, component.displayName);
1430
- const branchStyle = branchSplit?.viewProps?.style;
1431
- if (!staticObject(branchStyle)) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch did not resolve to a static native style`, entry.value.span);
1432
- for (const viewPropsKey of /* @__PURE__ */ new Set([...Object.keys(branchSplit?.viewProps ?? {}), ...Object.keys(split.viewProps ?? {})])) {
1433
- if (viewPropsKey === "style") continue;
1434
- if (JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch changes ${viewPropsKey}, which the compiled style array cannot express`, entry.value.span);
1655
+ for (const entry of dynamicStyleEntries) {
1656
+ if (entry.value.kind === "conditional" && directStyleName(entry.name, component) !== "opacity") {
1657
+ let serializeNativeTree = function(node) {
1658
+ if (node.kind === "leaf") {
1659
+ const diff = leafDiffs.get(node) ?? {};
1660
+ return JSON.stringify(diff);
1661
+ }
1662
+ const index = expressions.length;
1663
+ expressions.push(input.source.slice(node.test.start, node.test.end));
1664
+ return `expressions[${index}] ? ${serializeNativeTree(node.whenTrue)} : ${serializeNativeTree(node.whenFalse)}`;
1665
+ };
1666
+ const tree = entry.value.tree;
1667
+ const leaves = collectLeaves(tree);
1668
+ const leafDiffs = /* @__PURE__ */ new Map();
1669
+ for (const leaf of leaves) {
1670
+ const { branchCompleteProps } = propsForConditional(entry, leaf.value);
1671
+ const branchSplit = resolveSplitStyles(branchCompleteProps, component.staticConfig, cssAnimationDriver, component.displayName);
1672
+ const branchStyle = branchSplit?.viewProps?.style ?? {};
1673
+ if (!staticObject(branchStyle)) {
1674
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch did not resolve to a static native style`, entry.value.span);
1675
+ }
1676
+ for (const viewPropsKey of /* @__PURE__ */ new Set([...Object.keys(branchSplit?.viewProps ?? {}), ...Object.keys(split.viewProps ?? {})])) {
1677
+ if (viewPropsKey === "style") continue;
1678
+ if (JSON.stringify(branchSplit?.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) {
1679
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch changes ${viewPropsKey}, which the compiled style array cannot express`, entry.value.span);
1680
+ }
1681
+ }
1682
+ for (const key of Object.keys(baseStyleForDiff)) {
1683
+ if (!(key in branchStyle)) {
1684
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch removes ${key}, which an additive style array cannot express`, entry.value.span);
1685
+ }
1686
+ }
1687
+ const diff = {};
1688
+ for (const [key, value] of Object.entries(branchStyle)) {
1689
+ if (JSON.stringify(baseStyleForDiff[key]) !== JSON.stringify(value)) {
1690
+ diff[key] = value;
1691
+ }
1692
+ }
1693
+ if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) {
1694
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch style is not statically representable`, entry.value.span);
1695
+ }
1696
+ for (const key of Object.keys(diff)) {
1697
+ if (conditionalKeys.has(key)) {
1698
+ return bailout(input, "local/dynamic-style-value", `Multiple conditionals contribute ${key}; their interaction cannot be resolved per-branch`, entry.value.span);
1699
+ }
1700
+ }
1701
+ leafDiffs.set(leaf, diff);
1702
+ }
1703
+ for (const diff of leafDiffs.values()) {
1704
+ for (const key of Object.keys(diff)) conditionalKeys.add(key);
1435
1705
  }
1436
- for (const key of Object.keys(baseStyleForDiff)) if (!(key in branchStyle)) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch removes ${key}, which an additive style array cannot express`, entry.value.span);
1437
- const diff = {};
1438
- for (const [key, value] of Object.entries(branchStyle)) if (JSON.stringify(baseStyleForDiff[key]) !== JSON.stringify(value)) diff[key] = value;
1439
- if (!isSerializableNativeStyle(diff) || core.containsThemeRef(diff)) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch style is not statically representable`, entry.value.span);
1440
- for (const key of Object.keys(diff)) if (conditionalKeys.has(key)) return bailout(input, "local/dynamic-style-value", `Multiple conditionals contribute ${key}; their interaction cannot be resolved per-branch`, entry.value.span);
1441
- branchDiffs.push(diff);
1706
+ conditionalParts.push(serializeNativeTree(tree));
1707
+ } else {
1708
+ const index = expressions.length;
1709
+ expressions.push(input.source.slice(entry.value.span.start, entry.value.span.end));
1710
+ plainDynamicParts.push(`${JSON.stringify(directStyleName(entry.name, component))}: expressions[${index}]`);
1442
1711
  }
1443
- for (const diff of branchDiffs) for (const key of Object.keys(diff)) conditionalKeys.add(key);
1444
- const index = expressions.length;
1445
- expressions.push(input.source.slice(entry.value.test.start, entry.value.test.end));
1446
- conditionalParts.push(`expressions[${index}] ? ${JSON.stringify(branchDiffs[0])} : ${JSON.stringify(branchDiffs[1])}`);
1447
- } else {
1448
- const index = expressions.length;
1449
- expressions.push(input.source.slice(entry.value.span.start, entry.value.span.end));
1450
- plainDynamicParts.push(`${JSON.stringify(directStyleName(entry.name, component))}: expressions[${index}]`);
1451
1712
  }
1452
1713
  const dynamicStyle = plainDynamicParts.join(", ");
1453
1714
  const themedStyle = themedStyleKeys ? Object.entries(themedStyleKeys).map(([styleKey, themeKey]) => `${JSON.stringify(styleKey)}: _theme[${JSON.stringify(themeKey)}]?.get()`).join(", ") : null;
@@ -1464,18 +1725,21 @@ const ${fastLocal} = require('@tamagui/core')._withNativeStyle(${nativeLocal}, $
1464
1725
  origin: span
1465
1726
  }));
1466
1727
  const [first3, ...rest3] = styleEntries;
1728
+ const firstNonStyle3 = first3 ? spreadReplacement("jsx", first3) : "";
1467
1729
  const expressionEdits = styleEntries.length === 0 ? [] : input.element.form === "jsx" ? [{
1468
1730
  start: first3.span.start,
1469
1731
  end: first3.span.end,
1470
- content: expressions.length > 0 ? `_expressions={[${expressions.join(", ")}]}` : "",
1732
+ content: [expressions.length > 0 ? `_expressions={[${expressions.join(", ")}]}` : "", firstNonStyle3].filter(Boolean).join(" "),
1471
1733
  origin: first3.span
1472
1734
  }, ...rest3.map((entry) => ({
1473
1735
  start: entry.span.start,
1474
1736
  end: entry.span.end,
1475
- content: "",
1737
+ content: spreadReplacement("jsx", entry),
1476
1738
  origin: entry.span
1477
- }))] : compiledPropsEdits(input, styleEntries, `_expressions: [${expressions.join(", ")}]`);
1478
- if (!expressionEdits) return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1739
+ }))] : compiledPropsEdits(input, styleEntries, `_expressions: [${expressions.join(", ")}]`, (entry) => spreadReplacement(input.element.form, entry));
1740
+ if (!expressionEdits) {
1741
+ return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1742
+ }
1479
1743
  return {
1480
1744
  ok: true,
1481
1745
  edits: [...tagEdits3, ...expressionEdits],
@@ -1504,8 +1768,10 @@ const ${stableLocal} = require('@tamagui/core')._withStableStyle(${nativeLocal},
1504
1768
  origin: span
1505
1769
  }));
1506
1770
  if (input.element.form !== "jsx") {
1507
- const propsEdits = compiledPropsEdits(input, styleEntries, styleContent);
1508
- if (!propsEdits) return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1771
+ const propsEdits = compiledPropsEdits(input, styleEntries, styleContent, (entry) => spreadReplacement(input.element.form, entry));
1772
+ if (!propsEdits) {
1773
+ return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1774
+ }
1509
1775
  return {
1510
1776
  ok: true,
1511
1777
  edits: [...tagEdits2, ...propsEdits],
@@ -1518,23 +1784,26 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1518
1784
  flattened: true
1519
1785
  };
1520
1786
  }
1521
- if (styleEntries.length === 0) return {
1522
- ok: true,
1523
- edits: [...tagEdits2, {
1524
- start: input.element.component.span.end,
1525
- end: input.element.component.span.end,
1526
- content: ` style={${nativeStyleSource}}`,
1527
- origin: input.element.component.span
1528
- }],
1529
- css: [],
1530
- imports: [...nativeStyleImports, {
1531
- content: `
1787
+ if (styleEntries.length === 0) {
1788
+ return {
1789
+ ok: true,
1790
+ edits: [...tagEdits2, {
1791
+ start: input.element.component.span.end,
1792
+ end: input.element.component.span.end,
1793
+ content: ` style={${nativeStyleSource}}`,
1794
+ origin: input.element.component.span
1795
+ }],
1796
+ css: [],
1797
+ imports: [...nativeStyleImports, {
1798
+ content: `
1532
1799
  const ${nativeLocal} = require('react-native').${nativeExport};`,
1533
- origin: input.element.component.span
1534
- }],
1535
- flattened: true
1536
- };
1800
+ origin: input.element.component.span
1801
+ }],
1802
+ flattened: true
1803
+ };
1804
+ }
1537
1805
  const [first2, ...rest2] = styleEntries;
1806
+ const firstNonStyle2 = first2 ? spreadReplacement("jsx", first2) : "";
1538
1807
  return {
1539
1808
  ok: true,
1540
1809
  edits: [
@@ -1543,13 +1812,13 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1543
1812
  {
1544
1813
  start: first2.span.start,
1545
1814
  end: first2.span.end,
1546
- content: `style={${nativeStyleSource}}`,
1815
+ content: [`style={${nativeStyleSource}}`, firstNonStyle2].filter(Boolean).join(" "),
1547
1816
  origin: first2.span
1548
1817
  },
1549
1818
  ...rest2.map((entry) => ({
1550
1819
  start: entry.span.start,
1551
1820
  end: entry.span.end,
1552
- content: "",
1821
+ content: spreadReplacement("jsx", entry),
1553
1822
  origin: entry.span
1554
1823
  }))
1555
1824
  ],
@@ -1565,60 +1834,112 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1565
1834
  const artifacts = extractedStyleArtifacts(split, props, options.tamaguiConfig, !component.domTag, Boolean(component.staticConfig.styleFrontend));
1566
1835
  const className = artifacts.className;
1567
1836
  const rawInlineStyle = split.viewProps?.style;
1568
- const inlineStyle = staticObject(rawInlineStyle) && !rawInlineStyle["$$css"] && Object.keys(rawInlineStyle).length > 0 ? rawInlineStyle : null;
1569
- if (rawInlineStyle && !inlineStyle && !isSerializableNativeStyle(rawInlineStyle)) return bailout(input, "local/unsupported-target", "Web inline style output is not a static serializable value");
1837
+ const inlineStyle = staticObject(rawInlineStyle) && Object.keys(rawInlineStyle).length > 0 ? rawInlineStyle : null;
1838
+ if (rawInlineStyle && !inlineStyle && !isSerializableNativeStyle(rawInlineStyle)) {
1839
+ return bailout(input, "local/unsupported-target", "Web inline style output is not a static serializable value");
1840
+ }
1570
1841
  const programCSS = [];
1571
1842
  const programClassSources = [];
1572
- if (domStyleProgram?.kind === "prop" && domStyleProgram.value.kind === "dom-style") for (const item of domStyleProgram.value.items) {
1573
- if (item.value.kind !== "static" || !staticObject(item.value.value)) return bailout(input, "local/dynamic-style-value", "Every style() handle in a style array must be statically evaluable", item.value.span);
1574
- const itemSplit = resolveSplitStyles(item.value.value, partialStaticConfig(component.staticConfig));
1575
- if (!itemSplit) return bailout(input, "local/style-resolution-failed", "A style() handle could not be resolved", item.value.span);
1576
- const itemInline = itemSplit.viewProps?.style;
1577
- if (staticObject(itemInline) && !itemInline["$$css"] && Object.keys(itemInline).length > 0) return bailout(input, "local/unsupported-target", "Conditional web style() handles must lower to CSS classes", item.value.span);
1578
- const itemArtifacts = extractedStyleArtifacts(itemSplit, item.value.value, options.tamaguiConfig, false);
1579
- programCSS.push(...itemArtifacts.css);
1580
- if (itemArtifacts.className) {
1581
- const condition = item.condition ? input.source.slice(item.condition.start, item.condition.end) : null;
1582
- programClassSources.push(condition ? `(${condition}) && ${JSON.stringify(itemArtifacts.className)}` : JSON.stringify(itemArtifacts.className));
1843
+ if (domStyleProgram?.kind === "prop" && domStyleProgram.value.kind === "dom-style") {
1844
+ for (const item of domStyleProgram.value.items) {
1845
+ if (item.value.kind !== "static" || !staticObject(item.value.value)) {
1846
+ return bailout(input, "local/dynamic-style-value", "Every style() handle in a style array must be statically evaluable", item.value.span);
1847
+ }
1848
+ const itemSplit = resolveSplitStyles(item.value.value, partialStaticConfig(component.staticConfig));
1849
+ if (!itemSplit) {
1850
+ return bailout(input, "local/style-resolution-failed", "A style() handle could not be resolved", item.value.span);
1851
+ }
1852
+ const itemInline = itemSplit.viewProps?.style;
1853
+ if (staticObject(itemInline) && Object.keys(itemInline).length > 0) {
1854
+ return bailout(input, "local/unsupported-target", "Conditional web style() handles must lower to CSS classes", item.value.span);
1855
+ }
1856
+ const itemArtifacts = extractedStyleArtifacts(itemSplit, item.value.value, options.tamaguiConfig, false);
1857
+ programCSS.push(...itemArtifacts.css);
1858
+ if (itemArtifacts.className) {
1859
+ const condition = item.condition ? input.source.slice(item.condition.start, item.condition.end) : null;
1860
+ programClassSources.push(condition ? `(${condition}) && ${JSON.stringify(itemArtifacts.className)}` : JSON.stringify(itemArtifacts.className));
1861
+ }
1583
1862
  }
1584
1863
  }
1585
- let webClassName = className;
1864
+ const baseStaticClasses = new Set(className.split(" ").filter(Boolean));
1865
+ const staticClasses = new Set(baseStaticClasses);
1586
1866
  const webConditionalCSS = [];
1867
+ const webConditionalKeys = /* @__PURE__ */ new Set();
1587
1868
  const webConditionalEntries = supportsWebConditionalClasses ? dynamicStyleEntries.filter((entry) => entry.value.kind === "conditional") : [];
1588
1869
  for (const entry of webConditionalEntries) {
1870
+ let serializeWebTree = function(node) {
1871
+ if (node.kind === "leaf") {
1872
+ const artifacts2 = leafArtifactsMap.get(node);
1873
+ const only = artifacts2 ? artifacts2.classes.filter((item) => !sharedInConditional.has(item)) : [];
1874
+ return JSON.stringify(only.join(" "));
1875
+ }
1876
+ const test = input.source.slice(node.test.start, node.test.end);
1877
+ const truePart = serializeWebTree(node.whenTrue);
1878
+ const falsePart = serializeWebTree(node.whenFalse);
1879
+ return `(${test}) ? ${truePart} : ${falsePart}`;
1880
+ };
1589
1881
  if (entry.value.kind !== "conditional") continue;
1590
- const branches = [];
1591
- for (const branchValue of [entry.value.whenTrue, entry.value.whenFalse]) {
1592
- const branchSplit = resolveSplitStyles({
1593
- ...completeProps,
1594
- [entry.name]: branchValue
1595
- }, component.staticConfig, cssAnimationDriver, component.displayName);
1596
- if (!branchSplit) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch could not be resolved`, entry.value.span);
1882
+ const tree = entry.value.tree;
1883
+ const leaves = collectLeaves(tree);
1884
+ const leafArtifactsMap = /* @__PURE__ */ new Map();
1885
+ const entryConditionalKeys = /* @__PURE__ */ new Set();
1886
+ for (const leaf of leaves) {
1887
+ const { branchProps, branchCompleteProps } = propsForConditional(entry, leaf.value);
1888
+ const branchSplit = resolveSplitStyles(branchCompleteProps, component.staticConfig, cssAnimationDriver, component.displayName);
1889
+ if (!branchSplit) {
1890
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch could not be resolved`, entry.value.span);
1891
+ }
1597
1892
  for (const viewPropsKey of /* @__PURE__ */ new Set([...Object.keys(branchSplit.viewProps ?? {}), ...Object.keys(split.viewProps ?? {})])) {
1598
1893
  if (viewPropsKey === "className") continue;
1599
- if (JSON.stringify(branchSplit.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch changes ${viewPropsKey}, which conditional classes cannot express`, entry.value.span);
1894
+ if (JSON.stringify(branchSplit.viewProps?.[viewPropsKey]) !== JSON.stringify(split.viewProps?.[viewPropsKey])) {
1895
+ return bailout(input, "local/dynamic-style-value", `Conditional ${entry.name} branch changes ${viewPropsKey}, which conditional classes cannot express`, entry.value.span);
1896
+ }
1600
1897
  }
1601
- const branchArtifacts = extractedStyleArtifacts(branchSplit, {
1602
- ...props,
1603
- [entry.name]: branchValue
1604
- }, options.tamaguiConfig, !component.domTag, Boolean(component.staticConfig.styleFrontend));
1605
- branches.push({
1898
+ const changedKeys = /* @__PURE__ */ new Set();
1899
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(branchSplit.classNames ?? {}), ...Object.keys(split.classNames ?? {})])) {
1900
+ if (JSON.stringify(branchSplit.classNames?.[key]) !== JSON.stringify(split.classNames?.[key])) {
1901
+ changedKeys.add(key);
1902
+ }
1903
+ }
1904
+ const branchStyle = branchSplit.viewProps?.style;
1905
+ const baseStyle = split.viewProps?.style;
1906
+ if (staticObject(branchStyle) || staticObject(baseStyle)) {
1907
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(staticObject(branchStyle) ? branchStyle : {}), ...Object.keys(staticObject(baseStyle) ? baseStyle : {})])) {
1908
+ if (JSON.stringify(staticObject(branchStyle) ? branchStyle[key] : void 0) !== JSON.stringify(staticObject(baseStyle) ? baseStyle[key] : void 0)) {
1909
+ changedKeys.add(key);
1910
+ }
1911
+ }
1912
+ }
1913
+ for (const key of changedKeys) {
1914
+ if (webConditionalKeys.has(key)) {
1915
+ return bailout(input, "local/dynamic-style-value", `Multiple conditionals contribute ${key}; their interaction cannot be resolved per-branch`, entry.value.span);
1916
+ }
1917
+ entryConditionalKeys.add(key);
1918
+ }
1919
+ const branchArtifacts = extractedStyleArtifacts(branchSplit, branchProps, options.tamaguiConfig, !component.domTag, Boolean(component.staticConfig.styleFrontend));
1920
+ leafArtifactsMap.set(leaf, {
1606
1921
  classes: branchArtifacts.className.split(" ").filter(Boolean),
1607
1922
  css: branchArtifacts.css
1608
1923
  });
1609
1924
  }
1610
- const [whenTrue, whenFalse] = branches;
1611
- const shared = new Set(whenTrue.classes.filter((item) => whenFalse.classes.includes(item)));
1612
- const trueOnly = whenTrue.classes.filter((item) => !shared.has(item));
1613
- const falseOnly = whenFalse.classes.filter((item) => !shared.has(item));
1614
- webClassName = [...shared].join(" ");
1615
- webConditionalCSS.push(...whenTrue.css, ...whenFalse.css);
1616
- if (trueOnly.length > 0 || falseOnly.length > 0) {
1617
- const test = input.source.slice(entry.value.test.start, entry.value.test.end);
1618
- programClassSources.push(`(${test}) ? ${JSON.stringify(trueOnly.join(" "))} : ${JSON.stringify(falseOnly.join(" "))}`);
1925
+ for (const key of entryConditionalKeys) webConditionalKeys.add(key);
1926
+ for (const artifacts2 of leafArtifactsMap.values()) {
1927
+ webConditionalCSS.push(...artifacts2.css);
1619
1928
  }
1929
+ const allLeafArtifacts = leaves.map((l) => leafArtifactsMap.get(l));
1930
+ const sharedInConditional = new Set(allLeafArtifacts[0]?.classes.filter((c) => allLeafArtifacts.every((a) => a.classes.includes(c))) ?? []);
1931
+ for (const cls of baseStaticClasses) {
1932
+ if (!sharedInConditional.has(cls)) staticClasses.delete(cls);
1933
+ }
1934
+ for (const cls of sharedInConditional) {
1935
+ if (!baseStaticClasses.has(cls)) staticClasses.add(cls);
1936
+ }
1937
+ const classExpr = serializeWebTree(tree);
1938
+ programClassSources.push(classExpr);
1620
1939
  }
1621
- const classNameExpression = programClassSources.length > 0 ? `[${[JSON.stringify(webClassName), ...programClassSources].join(", ")}].filter(Boolean).join(" ")` : null;
1940
+ const webClassName = [...staticClasses].join(" ");
1941
+ const hasStyleProgram = programClassSources.length > 0;
1942
+ const classNameExpression = hasStyleProgram ? `[${[JSON.stringify(webClassName), ...programClassSources].join(", ")}].filter(Boolean).join(" ")` : null;
1622
1943
  const serializedInlineStyle = serializedStyle(inlineStyle, dynamicHostStyleProperties ?? []);
1623
1944
  const jsxWebStyle = classNameExpression ? [`className={${classNameExpression}}`, serializedInlineStyle ? `style={${serializedInlineStyle}}` : ""].filter(Boolean).join(" ") : jsxStyleAttributes(webClassName, inlineStyle, dynamicHostStyleProperties ?? []);
1624
1945
  const objectWebStyle = classNameExpression ? [`className: ${classNameExpression}`, serializedInlineStyle ? `style: ${serializedInlineStyle}` : ""].filter(Boolean).join(", ") : objectStyleProperties(webClassName, inlineStyle, dynamicHostStyleProperties ?? []);
@@ -1630,8 +1951,10 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1630
1951
  const webExtraProps = serializedProps(input.element.form, webDOMResult.additions);
1631
1952
  if (input.element.form !== "jsx") {
1632
1953
  const replacement = [objectWebStyle, webExtraProps].filter(Boolean).join(", ");
1633
- const propsEdits = compiledPropsEdits(input, styleEntries, replacement);
1634
- if (!propsEdits) return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1954
+ const propsEdits = compiledPropsEdits(input, styleEntries, replacement, (entry) => spreadReplacement(input.element.form, entry));
1955
+ if (!propsEdits) {
1956
+ return bailout(input, "local/unsupported-target", `Compiled ${input.element.form} call has no editable props argument`);
1957
+ }
1635
1958
  return {
1636
1959
  ok: true,
1637
1960
  edits: [
@@ -1664,7 +1987,12 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1664
1987
  };
1665
1988
  }
1666
1989
  const [first, ...rest] = styleEntries;
1667
- const attributes = [jsxWebStyle, webExtraProps].filter(Boolean).join(" ");
1990
+ const firstNonStyle = first ? spreadReplacement("jsx", first) : "";
1991
+ const attributes = [
1992
+ jsxWebStyle,
1993
+ webExtraProps,
1994
+ firstNonStyle
1995
+ ].filter(Boolean).join(" ");
1668
1996
  return {
1669
1997
  ok: true,
1670
1998
  edits: [
@@ -1679,7 +2007,7 @@ const ${nativeLocal} = require('react-native').${nativeExport};`,
1679
2007
  ...rest.map((entry) => ({
1680
2008
  start: entry.span.start,
1681
2009
  end: entry.span.end,
1682
- content: "",
2010
+ content: spreadReplacement("jsx", entry),
1683
2011
  origin: entry.span
1684
2012
  }))
1685
2013
  ],