@mgcrea/react-native-tailwind 0.11.1 → 0.12.0

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.
@@ -334,6 +334,89 @@ describe("Babel plugin - className transformation (existing behavior)", () => {
334
334
  // Should not have className in output
335
335
  expect(output).not.toContain("className");
336
336
  });
337
+
338
+ it('should transform className={"..."} (string literal in expression container)', () => {
339
+ const input = `
340
+ import { View } from 'react-native';
341
+ export function Component() {
342
+ return <View className={"flex-row items-center justify-start"} />;
343
+ }
344
+ `;
345
+
346
+ const output = transform(input, undefined, true);
347
+
348
+ // Should have StyleSheet
349
+ expect(output).toContain("StyleSheet.create");
350
+ expect(output).toContain("_twStyles");
351
+
352
+ // Should replace className with style
353
+ expect(output).not.toContain("className");
354
+ expect(output).toContain("style:");
355
+
356
+ // Should have the expected style keys
357
+ expect(output).toContain("_flex_row_items_center_justify_start");
358
+ });
359
+
360
+ it('should transform className={"..."} with modifiers', () => {
361
+ const input = `
362
+ import { Pressable } from 'react-native';
363
+ export function Component() {
364
+ return <Pressable className={"bg-blue-500 active:bg-blue-700 p-4"} />;
365
+ }
366
+ `;
367
+
368
+ const output = transform(input, undefined, true);
369
+
370
+ // Should have StyleSheet with both base and active styles
371
+ expect(output).toContain("_bg_blue_500_p_4");
372
+ expect(output).toContain("_active_bg_blue_700");
373
+
374
+ // Should have style function for active modifier (Pressable uses 'pressed' parameter)
375
+ expect(output).toMatch(/(pressed|_state)/);
376
+
377
+ // Should not have className in output
378
+ expect(output).not.toContain("className");
379
+ });
380
+
381
+ it('should transform className={"..."} with platform modifiers', () => {
382
+ const input = `
383
+ import { View } from 'react-native';
384
+ export function Component() {
385
+ return <View className={"p-4 ios:p-6 android:p-8"} />;
386
+ }
387
+ `;
388
+
389
+ const output = transform(input, undefined, true);
390
+
391
+ // Should have Platform import
392
+ expect(output).toContain("Platform");
393
+ expect(output).toMatch(/from ['"]react-native['"]/); // Match both single and double quotes
394
+
395
+ // Should have Platform.select
396
+ expect(output).toContain("Platform.select");
397
+
398
+ // Should have platform-specific styles
399
+ expect(output).toContain("_ios_p_6");
400
+ expect(output).toContain("_android_p_8");
401
+
402
+ // Should not have className in output
403
+ expect(output).not.toContain("className");
404
+ });
405
+
406
+ it('should handle empty className={""}', () => {
407
+ const input = `
408
+ import { View } from 'react-native';
409
+ export function Component() {
410
+ return <View className={""} />;
411
+ }
412
+ `;
413
+
414
+ const output = transform(input, undefined, true);
415
+
416
+ // Should remove empty className attribute entirely
417
+ expect(output).not.toContain("className");
418
+ expect(output).not.toContain("style=");
419
+ });
337
420
  });
338
421
 
339
422
  describe("Babel plugin - placeholder: modifier transformation", () => {
@@ -1250,6 +1333,34 @@ describe("Babel plugin - custom color scheme hook import", () => {
1250
1333
  expect(output).not.toContain("_twColorScheme = useTheme()");
1251
1334
  });
1252
1335
 
1336
+ it("should not treat type-only imports as having the hook", () => {
1337
+ const input = `
1338
+ import React from 'react';
1339
+ import { View } from 'react-native';
1340
+ import type { useColorScheme } from 'react-native';
1341
+
1342
+ export function Component() {
1343
+ return <View className="dark:bg-gray-900" />;
1344
+ }
1345
+ `;
1346
+
1347
+ const output = transform(input, undefined, true);
1348
+
1349
+ // Should add a VALUE import for useColorScheme (type import doesn't count)
1350
+ expect(output).toMatch(/import\s+\{[^}]*useColorScheme[^}]*\}\s+from\s+['"]react-native['"]/);
1351
+
1352
+ // Should inject the hook
1353
+ expect(output).toContain("_twColorScheme = useColorScheme()");
1354
+
1355
+ // Should have both type-only and value imports in output
1356
+ // (TypeScript preset keeps type imports for type checking)
1357
+ const colorSchemeMatches = output.match(/useColorScheme/g);
1358
+ expect(colorSchemeMatches).toBeTruthy();
1359
+ if (colorSchemeMatches) {
1360
+ expect(colorSchemeMatches.length).toBeGreaterThanOrEqual(2); // At least in import and hook call
1361
+ }
1362
+ });
1363
+
1253
1364
  it("should handle both type-only and aliased imports together", () => {
1254
1365
  const input = `
1255
1366
  import React from 'react';
@@ -1412,3 +1523,390 @@ describe("Babel plugin - scheme: modifier", () => {
1412
1523
  expect(output).toContain("_twColorScheme === 'light'");
1413
1524
  });
1414
1525
  });
1526
+
1527
+ describe("Babel plugin - color scheme modifiers in tw/twStyle", () => {
1528
+ it("should transform tw with dark: modifier inside component", () => {
1529
+ const input = `
1530
+ import { tw } from '@mgcrea/react-native-tailwind';
1531
+
1532
+ function MyComponent() {
1533
+ const styles = tw\`bg-white dark:bg-gray-900\`;
1534
+ return null;
1535
+ }
1536
+ `;
1537
+
1538
+ const output = transform(input);
1539
+
1540
+ // Should inject useColorScheme hook
1541
+ expect(output).toContain("useColorScheme");
1542
+ expect(output).toContain("_twColorScheme");
1543
+
1544
+ // Should generate style array with conditionals
1545
+ expect(output).toContain("style: [");
1546
+ expect(output).toContain('_twColorScheme === "dark"');
1547
+ expect(output).toContain("_twStyles._dark_bg_gray_900");
1548
+ expect(output).toContain("_twStyles._bg_white");
1549
+
1550
+ // Should have StyleSheet.create
1551
+ expect(output).toContain("StyleSheet.create");
1552
+ });
1553
+
1554
+ it("should transform twStyle with light: modifier inside component", () => {
1555
+ const input = `
1556
+ import { twStyle } from '@mgcrea/react-native-tailwind';
1557
+
1558
+ export const MyComponent = () => {
1559
+ const buttonStyles = twStyle('text-gray-900 light:text-gray-100');
1560
+ return null;
1561
+ };
1562
+ `;
1563
+
1564
+ const output = transform(input);
1565
+
1566
+ // Should inject useColorScheme hook
1567
+ expect(output).toContain("useColorScheme");
1568
+ expect(output).toContain("_twColorScheme");
1569
+
1570
+ // Should generate style array with conditionals
1571
+ expect(output).toContain("style: [");
1572
+ expect(output).toContain('_twColorScheme === "light"');
1573
+ expect(output).toContain("_twStyles._light_text_gray_100");
1574
+ expect(output).toContain("_twStyles._text_gray_900");
1575
+ });
1576
+
1577
+ it("should transform tw with both dark: and light: modifiers", () => {
1578
+ const input = `
1579
+ import { tw } from '@mgcrea/react-native-tailwind';
1580
+
1581
+ function MyComponent() {
1582
+ const styles = tw\`bg-blue-500 dark:bg-blue-900 light:bg-blue-100\`;
1583
+ return null;
1584
+ }
1585
+ `;
1586
+
1587
+ const output = transform(input);
1588
+
1589
+ // Should have both conditionals
1590
+ expect(output).toContain('_twColorScheme === "dark"');
1591
+ expect(output).toContain('_twColorScheme === "light"');
1592
+ expect(output).toContain("_twStyles._dark_bg_blue_900");
1593
+ expect(output).toContain("_twStyles._light_bg_blue_100");
1594
+ expect(output).toContain("_twStyles._bg_blue_500");
1595
+ });
1596
+
1597
+ it("should combine color scheme modifiers with state modifiers", () => {
1598
+ const input = `
1599
+ import { tw } from '@mgcrea/react-native-tailwind';
1600
+
1601
+ function MyComponent() {
1602
+ const styles = tw\`bg-white dark:bg-gray-900 active:bg-blue-500\`;
1603
+ return null;
1604
+ }
1605
+ `;
1606
+
1607
+ const output = transform(input);
1608
+
1609
+ // Should have color scheme conditionals in style array
1610
+ expect(output).toContain("style: [");
1611
+ expect(output).toContain('_twColorScheme === "dark"');
1612
+
1613
+ // Should have activeStyle property (separate from color scheme)
1614
+ expect(output).toContain("activeStyle:");
1615
+ expect(output).toContain("_twStyles._active_bg_blue_500");
1616
+ });
1617
+
1618
+ it("should warn if tw with color scheme modifiers used outside component", () => {
1619
+ const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
1620
+
1621
+ const input = `
1622
+ import { tw } from '@mgcrea/react-native-tailwind';
1623
+
1624
+ const globalStyles = tw\`bg-white dark:bg-gray-900\`;
1625
+ `;
1626
+
1627
+ const output = transform(input);
1628
+
1629
+ // Should warn about usage outside component
1630
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
1631
+ expect.stringContaining("Color scheme modifiers (dark:, light:) in tw/twStyle calls"),
1632
+ );
1633
+
1634
+ // Should not inject hook (no component scope)
1635
+ expect(output).not.toContain("useColorScheme");
1636
+
1637
+ // Should still generate styles but without runtime conditionals
1638
+ expect(output).toContain("_twStyles");
1639
+
1640
+ consoleWarnSpy.mockRestore();
1641
+ });
1642
+
1643
+ it("should handle tw with only dark: modifier (no base class)", () => {
1644
+ const input = `
1645
+ import { tw } from '@mgcrea/react-native-tailwind';
1646
+
1647
+ function MyComponent() {
1648
+ const styles = tw\`dark:bg-gray-900\`;
1649
+ return null;
1650
+ }
1651
+ `;
1652
+
1653
+ const output = transform(input);
1654
+
1655
+ // Should still generate style array
1656
+ expect(output).toContain("style: [");
1657
+ expect(output).toContain('_twColorScheme === "dark"');
1658
+ expect(output).toContain("_twStyles._dark_bg_gray_900");
1659
+ });
1660
+
1661
+ it("should work with custom color scheme hook import", () => {
1662
+ const input = `
1663
+ import { tw } from '@mgcrea/react-native-tailwind';
1664
+ import { useTheme } from '@react-navigation/native';
1665
+
1666
+ function MyComponent() {
1667
+ const styles = tw\`bg-white dark:bg-gray-900\`;
1668
+ return null;
1669
+ }
1670
+ `;
1671
+
1672
+ const options: PluginOptions = {
1673
+ colorScheme: {
1674
+ importFrom: "@react-navigation/native",
1675
+ importName: "useTheme",
1676
+ },
1677
+ };
1678
+
1679
+ const output = transform(input, options);
1680
+
1681
+ // Should use existing import (not duplicate)
1682
+ const themeImportCount = (output.match(/useTheme/g) ?? []).length;
1683
+ // Should appear in import statement and hook call
1684
+ expect(themeImportCount).toBeGreaterThanOrEqual(2);
1685
+
1686
+ // Should call the custom hook
1687
+ expect(output).toContain("useTheme()");
1688
+ });
1689
+
1690
+ it("should generate both style array and darkStyle/lightStyle properties", () => {
1691
+ const input = `
1692
+ import { tw } from '@mgcrea/react-native-tailwind';
1693
+
1694
+ function MyComponent() {
1695
+ const styles = tw\`bg-white dark:bg-gray-900 light:bg-gray-50\`;
1696
+ return null;
1697
+ }
1698
+ `;
1699
+
1700
+ const output = transform(input);
1701
+
1702
+ // Should have runtime conditional in style array
1703
+ expect(output).toContain("style: [");
1704
+ expect(output).toContain('_twColorScheme === "dark"');
1705
+ expect(output).toContain('_twColorScheme === "light"');
1706
+
1707
+ // Should ALSO have darkStyle and lightStyle properties for manual access
1708
+ expect(output).toContain("darkStyle:");
1709
+ expect(output).toContain("lightStyle:");
1710
+ expect(output).toContain("_twStyles._dark_bg_gray_900");
1711
+ expect(output).toContain("_twStyles._light_bg_gray_50");
1712
+ });
1713
+
1714
+ it("should allow accessing raw color values from darkStyle/lightStyle", () => {
1715
+ const input = `
1716
+ import { tw } from '@mgcrea/react-native-tailwind';
1717
+
1718
+ function MyComponent() {
1719
+ const btnStyles = tw\`bg-blue-500 dark:bg-blue-900\`;
1720
+ // User can access raw hex for Reanimated
1721
+ const darkBgColor = btnStyles.darkStyle?.backgroundColor;
1722
+ return null;
1723
+ }
1724
+ `;
1725
+
1726
+ const output = transform(input);
1727
+
1728
+ // Should have darkStyle property available
1729
+ expect(output).toContain("darkStyle:");
1730
+ expect(output).toContain("_twStyles._dark_bg_blue_900");
1731
+
1732
+ // The actual usage line should be preserved (TypeScript/Babel doesn't remove it)
1733
+ expect(output).toContain("btnStyles.darkStyle");
1734
+ });
1735
+
1736
+ // Platform modifier tests for tw/twStyle
1737
+ it("should transform tw with ios: modifier", () => {
1738
+ const input = `
1739
+ import { tw } from '@mgcrea/react-native-tailwind';
1740
+
1741
+ function MyComponent() {
1742
+ const styles = tw\`bg-white ios:p-6\`;
1743
+ return null;
1744
+ }
1745
+ `;
1746
+
1747
+ const output = transform(input);
1748
+
1749
+ // Should add Platform import
1750
+ expect(output).toContain("Platform");
1751
+ expect(output).toContain('from "react-native"');
1752
+
1753
+ // Should generate style array with Platform.select()
1754
+ expect(output).toContain("style: [");
1755
+ expect(output).toContain("Platform.select");
1756
+ expect(output).toContain("ios:");
1757
+ expect(output).toContain("_twStyles._ios_p_6");
1758
+ expect(output).toContain("_twStyles._bg_white");
1759
+
1760
+ // Should have StyleSheet.create
1761
+ expect(output).toContain("StyleSheet.create");
1762
+ });
1763
+
1764
+ it("should transform twStyle with android: modifier", () => {
1765
+ const input = `
1766
+ import { twStyle } from '@mgcrea/react-native-tailwind';
1767
+
1768
+ export const MyComponent = () => {
1769
+ const buttonStyles = twStyle('bg-blue-500 android:p-8');
1770
+ return null;
1771
+ };
1772
+ `;
1773
+
1774
+ const output = transform(input);
1775
+
1776
+ // Should add Platform import
1777
+ expect(output).toContain("Platform");
1778
+
1779
+ // Should generate style array with Platform.select()
1780
+ expect(output).toContain("style: [");
1781
+ expect(output).toContain("Platform.select");
1782
+ expect(output).toContain("android:");
1783
+ expect(output).toContain("_twStyles._android_p_8");
1784
+ expect(output).toContain("_twStyles._bg_blue_500");
1785
+ });
1786
+
1787
+ it("should transform tw with multiple platform modifiers", () => {
1788
+ const input = `
1789
+ import { tw } from '@mgcrea/react-native-tailwind';
1790
+
1791
+ function MyComponent() {
1792
+ const styles = tw\`bg-white ios:p-6 android:p-8 web:p-4\`;
1793
+ return null;
1794
+ }
1795
+ `;
1796
+
1797
+ const output = transform(input);
1798
+
1799
+ // Should generate Platform.select() with all platforms
1800
+ expect(output).toContain("Platform.select");
1801
+ expect(output).toContain("ios:");
1802
+ expect(output).toContain("android:");
1803
+ expect(output).toContain("web:");
1804
+ expect(output).toContain("_twStyles._ios_p_6");
1805
+ expect(output).toContain("_twStyles._android_p_8");
1806
+ expect(output).toContain("_twStyles._web_p_4");
1807
+ });
1808
+
1809
+ it("should combine platform modifiers with color-scheme modifiers", () => {
1810
+ const input = `
1811
+ import { tw } from '@mgcrea/react-native-tailwind';
1812
+
1813
+ function MyComponent() {
1814
+ const styles = tw\`bg-white ios:p-6 dark:bg-gray-900\`;
1815
+ return null;
1816
+ }
1817
+ `;
1818
+
1819
+ const output = transform(input);
1820
+
1821
+ // Should have both Platform and useColorScheme
1822
+ expect(output).toContain("Platform");
1823
+ expect(output).toContain("useColorScheme");
1824
+ expect(output).toContain("_twColorScheme");
1825
+
1826
+ // Should have both conditionals in style array
1827
+ expect(output).toContain("Platform.select");
1828
+ expect(output).toContain('_twColorScheme === "dark"');
1829
+ });
1830
+
1831
+ it("should generate iosStyle/androidStyle/webStyle properties for manual access", () => {
1832
+ const input = `
1833
+ import { tw } from '@mgcrea/react-native-tailwind';
1834
+
1835
+ function MyComponent() {
1836
+ const styles = tw\`bg-white ios:p-6 android:p-8 web:p-4\`;
1837
+ return null;
1838
+ }
1839
+ `;
1840
+
1841
+ const output = transform(input);
1842
+
1843
+ // Should have separate platform style properties
1844
+ expect(output).toContain("iosStyle:");
1845
+ expect(output).toContain("_twStyles._ios_p_6");
1846
+ expect(output).toContain("androidStyle:");
1847
+ expect(output).toContain("_twStyles._android_p_8");
1848
+ expect(output).toContain("webStyle:");
1849
+ expect(output).toContain("_twStyles._web_p_4");
1850
+
1851
+ // Should also have runtime Platform.select() in style array
1852
+ expect(output).toContain("Platform.select");
1853
+ });
1854
+
1855
+ it("should work with only platform modifiers (no base class)", () => {
1856
+ const input = `
1857
+ import { tw } from '@mgcrea/react-native-tailwind';
1858
+
1859
+ function MyComponent() {
1860
+ const styles = tw\`ios:p-6 android:p-8\`;
1861
+ return null;
1862
+ }
1863
+ `;
1864
+
1865
+ const output = transform(input);
1866
+
1867
+ // Should generate Platform.select() even without base classes
1868
+ expect(output).toContain("Platform.select");
1869
+ expect(output).toContain("_twStyles._ios_p_6");
1870
+ expect(output).toContain("_twStyles._android_p_8");
1871
+ });
1872
+
1873
+ it("should allow accessing platform-specific styles manually", () => {
1874
+ const input = `
1875
+ import { tw } from '@mgcrea/react-native-tailwind';
1876
+
1877
+ function MyComponent() {
1878
+ const btnStyles = tw\`bg-blue-500 ios:p-6\`;
1879
+ const iosPadding = btnStyles.iosStyle;
1880
+ return null;
1881
+ }
1882
+ `;
1883
+
1884
+ const output = transform(input);
1885
+
1886
+ // Should have iosStyle property available
1887
+ expect(output).toContain("iosStyle:");
1888
+ expect(output).toContain("_twStyles._ios_p_6");
1889
+
1890
+ // The actual usage line should be preserved
1891
+ expect(output).toContain("btnStyles.iosStyle");
1892
+ });
1893
+
1894
+ it("should combine state modifiers with platform modifiers", () => {
1895
+ const input = `
1896
+ import { tw } from '@mgcrea/react-native-tailwind';
1897
+
1898
+ function MyComponent() {
1899
+ const styles = tw\`bg-white active:bg-blue-500 ios:p-6\`;
1900
+ return null;
1901
+ }
1902
+ `;
1903
+
1904
+ const output = transform(input);
1905
+
1906
+ // Should have both activeStyle and platform modifiers
1907
+ expect(output).toContain("activeStyle:");
1908
+ expect(output).toContain("_twStyles._active_bg_blue_500");
1909
+ expect(output).toContain("Platform.select");
1910
+ expect(output).toContain("_twStyles._ios_p_6");
1911
+ });
1912
+ });
@@ -276,6 +276,10 @@ export default function reactNativeTailwindBabelPlugin(
276
276
  state.twImportNames = new Set();
277
277
  state.hasTwImport = false;
278
278
  state.functionComponentsNeedingColorScheme = new Set();
279
+ state.hasColorSchemeImport = false;
280
+ state.colorSchemeLocalIdentifier = undefined;
281
+ state.needsPlatformImport = false;
282
+ state.hasPlatformImport = false;
279
283
 
280
284
  // Load custom theme from tailwind.config.*
281
285
  state.customTheme = extractCustomTheme(state.file.opts.filename ?? "");
@@ -367,7 +371,8 @@ export default function reactNativeTailwindBabelPlugin(
367
371
 
368
372
  // Track color scheme hook import from the configured source
369
373
  // (default: react-native, but can be custom like @/hooks/useColorScheme)
370
- if (node.source.value === state.colorSchemeImportSource) {
374
+ // Only track value imports (not type-only imports which get erased)
375
+ if (node.source.value === state.colorSchemeImportSource && node.importKind !== "type") {
371
376
  const specifiers = node.specifiers;
372
377
 
373
378
  for (const spec of specifiers) {
@@ -444,7 +449,16 @@ export default function reactNativeTailwindBabelPlugin(
444
449
  state.hasClassNames = true;
445
450
 
446
451
  // Process the className with modifiers
447
- processTwCall(className, path, state, parseClassName, generateStyleKey, splitModifierClasses, t);
452
+ processTwCall(
453
+ className,
454
+ path,
455
+ state,
456
+ parseClassName,
457
+ generateStyleKey,
458
+ splitModifierClasses,
459
+ findComponentScope,
460
+ t,
461
+ );
448
462
  },
449
463
 
450
464
  // Handle twStyle('...') call expressions
@@ -494,7 +508,16 @@ export default function reactNativeTailwindBabelPlugin(
494
508
  state.hasClassNames = true;
495
509
 
496
510
  // Process the className with modifiers
497
- processTwCall(className, path, state, parseClassName, generateStyleKey, splitModifierClasses, t);
511
+ processTwCall(
512
+ className,
513
+ path,
514
+ state,
515
+ parseClassName,
516
+ generateStyleKey,
517
+ splitModifierClasses,
518
+ findComponentScope,
519
+ t,
520
+ );
498
521
  },
499
522
 
500
523
  JSXAttribute(path, state) {
@@ -517,20 +540,22 @@ export default function reactNativeTailwindBabelPlugin(
517
540
  // Determine target style prop based on attribute name
518
541
  const targetStyleProp = getTargetStyleProp(attributeName);
519
542
 
520
- // Handle static string literals
521
- if (t.isStringLiteral(value)) {
522
- const className = value.value.trim();
543
+ /**
544
+ * Process static className string (handles both direct StringLiteral and StringLiteral in JSXExpressionContainer)
545
+ */
546
+ const processStaticClassName = (className: string): boolean => {
547
+ const trimmedClassName = className.trim();
523
548
 
524
549
  // Skip empty classNames
525
- if (!className) {
550
+ if (!trimmedClassName) {
526
551
  path.remove();
527
- return;
552
+ return true;
528
553
  }
529
554
 
530
555
  state.hasClassNames = true;
531
556
 
532
557
  // Check if className contains modifiers (active:, hover:, focus:, placeholder:, ios:, android:, web:, dark:, light:, scheme:)
533
- const { baseClasses, modifierClasses: rawModifierClasses } = splitModifierClasses(className);
558
+ const { baseClasses, modifierClasses: rawModifierClasses } = splitModifierClasses(trimmedClassName);
534
559
 
535
560
  // Expand scheme: modifiers into dark: and light: modifiers
536
561
  const modifierClasses: ParsedModifier[] = [];
@@ -697,7 +722,7 @@ export default function reactNativeTailwindBabelPlugin(
697
722
  } else {
698
723
  replaceWithStyleFunctionAttribute(path, styleFunctionExpression, targetStyleProp, t);
699
724
  }
700
- return;
725
+ return true;
701
726
  } else {
702
727
  // Component doesn't support state modifiers, but we can still use platform modifiers
703
728
  // Fall through to platform-only handling
@@ -771,7 +796,7 @@ export default function reactNativeTailwindBabelPlugin(
771
796
  path.node.name = t.jsxIdentifier(targetStyleProp);
772
797
  path.node.value = t.jsxExpressionContainer(styleExpression);
773
798
  }
774
- return;
799
+ return true;
775
800
  }
776
801
 
777
802
  // If there are state modifiers (and no platform modifiers), check if this component supports them
@@ -829,12 +854,12 @@ export default function reactNativeTailwindBabelPlugin(
829
854
  } else {
830
855
  replaceWithStyleFunctionAttribute(path, styleFunctionExpression, targetStyleProp, t);
831
856
  }
832
- return;
857
+ return true;
833
858
  }
834
859
  } else {
835
860
  // All modifiers are supported - process normally
836
861
  const styleExpression = processStaticClassNameWithModifiers(
837
- className,
862
+ trimmedClassName,
838
863
  state,
839
864
  parseClassName,
840
865
  generateStyleKey,
@@ -851,7 +876,7 @@ export default function reactNativeTailwindBabelPlugin(
851
876
  } else {
852
877
  replaceWithStyleFunctionAttribute(path, styleFunctionExpression, targetStyleProp, t);
853
878
  }
854
- return;
879
+ return true;
855
880
  }
856
881
  } else {
857
882
  // Component doesn't support any modifiers
@@ -871,7 +896,7 @@ export default function reactNativeTailwindBabelPlugin(
871
896
  if (!classNameForStyle) {
872
897
  // No base classes, only had placeholder modifiers - just remove className
873
898
  path.remove();
874
- return;
899
+ return true;
875
900
  }
876
901
 
877
902
  const styleObject = parseClassName(classNameForStyle, state.customTheme);
@@ -888,7 +913,14 @@ export default function reactNativeTailwindBabelPlugin(
888
913
  // Replace className with style prop
889
914
  replaceWithStyleAttribute(path, styleKey, targetStyleProp, state.stylesIdentifier, t);
890
915
  }
891
- return;
916
+ return true;
917
+ };
918
+
919
+ // Handle static string literals
920
+ if (t.isStringLiteral(value)) {
921
+ if (processStaticClassName(value.value)) {
922
+ return;
923
+ }
892
924
  }
893
925
 
894
926
  // Handle dynamic expressions (JSXExpressionContainer)
@@ -900,6 +932,13 @@ export default function reactNativeTailwindBabelPlugin(
900
932
  return;
901
933
  }
902
934
 
935
+ // Fast path: Support string literals wrapped in JSXExpressionContainer: className={"flex-row"}
936
+ if (t.isStringLiteral(expression)) {
937
+ if (processStaticClassName(expression.value)) {
938
+ return;
939
+ }
940
+ }
941
+
903
942
  try {
904
943
  // Find component scope for color scheme modifiers
905
944
  const componentScope = findComponentScope(path, t);