@mgcrea/react-native-tailwind 0.8.1 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +152 -0
  2. package/dist/babel/config-loader.ts +2 -0
  3. package/dist/babel/index.cjs +205 -17
  4. package/dist/babel/plugin.d.ts +4 -1
  5. package/dist/babel/plugin.test.ts +327 -0
  6. package/dist/babel/plugin.ts +194 -14
  7. package/dist/babel/utils/platformModifierProcessing.d.ts +30 -0
  8. package/dist/babel/utils/platformModifierProcessing.ts +80 -0
  9. package/dist/babel/utils/styleInjection.d.ts +5 -1
  10. package/dist/babel/utils/styleInjection.ts +52 -7
  11. package/dist/babel/utils/styleTransforms.ts +1 -0
  12. package/dist/parser/aspectRatio.js +1 -1
  13. package/dist/parser/aspectRatio.test.js +1 -1
  14. package/dist/parser/index.d.ts +2 -2
  15. package/dist/parser/index.js +1 -1
  16. package/dist/parser/modifiers.d.ts +20 -2
  17. package/dist/parser/modifiers.js +1 -1
  18. package/dist/parser/spacing.d.ts +1 -1
  19. package/dist/parser/spacing.js +1 -1
  20. package/dist/parser/spacing.test.js +1 -1
  21. package/dist/runtime.cjs +1 -1
  22. package/dist/runtime.cjs.map +4 -4
  23. package/dist/runtime.js +1 -1
  24. package/dist/runtime.js.map +4 -4
  25. package/dist/runtime.test.js +1 -1
  26. package/dist/stubs/tw.test.js +1 -0
  27. package/package.json +7 -7
  28. package/src/babel/config-loader.ts +2 -0
  29. package/src/babel/plugin.test.ts +327 -0
  30. package/src/babel/plugin.ts +194 -14
  31. package/src/babel/utils/platformModifierProcessing.ts +80 -0
  32. package/src/babel/utils/styleInjection.ts +52 -7
  33. package/src/babel/utils/styleTransforms.ts +1 -0
  34. package/src/parser/aspectRatio.test.ts +25 -2
  35. package/src/parser/aspectRatio.ts +4 -3
  36. package/src/parser/borders.ts +2 -0
  37. package/src/parser/colors.ts +2 -0
  38. package/src/parser/index.ts +9 -2
  39. package/src/parser/layout.ts +2 -0
  40. package/src/parser/modifiers.ts +38 -4
  41. package/src/parser/placeholder.ts +1 -0
  42. package/src/parser/sizing.ts +1 -0
  43. package/src/parser/spacing.test.ts +63 -0
  44. package/src/parser/spacing.ts +11 -6
  45. package/src/parser/transforms.ts +5 -0
  46. package/src/parser/typography.ts +2 -0
  47. package/src/runtime.test.ts +27 -0
  48. package/src/runtime.ts +2 -1
  49. package/src/stubs/tw.test.ts +27 -0
  50. package/dist/babel/index.test.ts +0 -481
  51. package/dist/config/palettes.d.ts +0 -302
  52. package/dist/config/palettes.js +0 -1
  53. package/dist/parser/__snapshots__/aspectRatio.test.js.snap +0 -9
  54. package/dist/parser/__snapshots__/borders.test.js.snap +0 -23
  55. package/dist/parser/__snapshots__/colors.test.js.snap +0 -251
  56. package/dist/parser/__snapshots__/shadows.test.js.snap +0 -76
  57. package/dist/parser/__snapshots__/sizing.test.js.snap +0 -61
  58. package/dist/parser/__snapshots__/spacing.test.js.snap +0 -40
  59. package/dist/parser/__snapshots__/transforms.test.js.snap +0 -58
  60. package/dist/parser/__snapshots__/typography.test.js.snap +0 -30
  61. package/dist/parser/aspectRatio.test.d.ts +0 -1
  62. package/dist/parser/borders.test.d.ts +0 -1
  63. package/dist/parser/colors.test.d.ts +0 -1
  64. package/dist/parser/layout.test.d.ts +0 -1
  65. package/dist/parser/modifiers.test.d.ts +0 -1
  66. package/dist/parser/shadows.test.d.ts +0 -1
  67. package/dist/parser/sizing.test.d.ts +0 -1
  68. package/dist/parser/spacing.test.d.ts +0 -1
  69. package/dist/parser/typography.test.d.ts +0 -1
  70. package/dist/types.d.ts +0 -42
  71. package/dist/types.js +0 -1
@@ -1 +1 @@
1
- var _vitest=require("vitest");var _runtime=require("./runtime");(0,_vitest.describe)("runtime",function(){(0,_vitest.beforeEach)(function(){(0,_runtime.clearCache)();(0,_runtime.setConfig)({});});(0,_vitest.describe)("tw template tag",function(){(0,_vitest.it)("should parse static classes",function(){var result=(0,_runtime.tw)`m-4 p-2 bg-blue-500`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should handle interpolated values",function(){var isActive=true;var result=(0,_runtime.tw)`m-4 ${isActive&&"bg-blue-500"}`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,backgroundColor:"#2b7fff"});});(0,_vitest.it)("should handle conditional classes",function(){var isLarge=true;var result=(0,_runtime.tw)`p-4 ${isLarge?"text-xl":"text-sm"}`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({padding:16,fontSize:20});});(0,_vitest.it)("should handle falsy values",function(){var result=(0,_runtime.tw)`m-4 ${false} ${null} ${undefined} p-2`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8});});(0,_vitest.it)("should return empty style object for empty className",function(){var result=(0,_runtime.tw)``;(0,_vitest.expect)(result).toEqual({style:{}});});(0,_vitest.it)("should normalize whitespace",function(){var result=(0,_runtime.tw)`m-4 p-2 bg-blue-500`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});});});(0,_vitest.describe)("twStyle function",function(){(0,_vitest.it)("should parse className string",function(){var result=(0,_runtime.twStyle)("m-4 p-2 bg-blue-500");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();});(0,_vitest.it)("should return undefined for empty string",function(){var result=(0,_runtime.twStyle)("");(0,_vitest.expect)(result).toBeUndefined();});(0,_vitest.it)("should normalize whitespace",function(){var result=(0,_runtime.twStyle)("m-4 p-2 bg-blue-500");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});});});(0,_vitest.describe)("setConfig",function(){(0,_vitest.it)("should set custom colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF",secondary:"#5856D6"}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({primary:"#007AFF",secondary:"#5856D6"});});(0,_vitest.it)("should flatten nested colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{brand:{light:"#FF6B6B",dark:"#CC0000"}}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({"brand-light":"#FF6B6B","brand-dark":"#CC0000"});});(0,_vitest.it)("should handle mixed flat and nested colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF",brand:{light:"#FF6B6B",dark:"#CC0000"}}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({primary:"#007AFF","brand-light":"#FF6B6B","brand-dark":"#CC0000"});});(0,_vitest.it)("should clear cache when config changes",function(){var style=(0,_runtime.tw)`bg-blue-500`;(0,_vitest.expect)(style).toBeDefined();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(1);(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF"}}}});(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(0);});(0,_vitest.it)("should use custom colors in parsing",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF"}}}});var result=(0,_runtime.tw)`bg-primary`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#007AFF"});});});(0,_vitest.describe)("cache",function(){(0,_vitest.it)("should cache parsed styles",function(){var result1=(0,_runtime.tw)`m-4 p-2`;var result2=(0,_runtime.tw)`m-4 p-2`;(0,_vitest.expect)(result1).toBe(result2);});(0,_vitest.it)("should track cache stats",function(){var style1=(0,_runtime.tw)`m-4`;var style2=(0,_runtime.tw)`p-2`;var style3=(0,_runtime.tw)`bg-blue-500`;(0,_vitest.expect)(style1).toBeDefined();(0,_vitest.expect)(style2).toBeDefined();(0,_vitest.expect)(style3).toBeDefined();var stats=(0,_runtime.getCacheStats)();(0,_vitest.expect)(stats.size).toBe(3);(0,_vitest.expect)(stats.keys).toContain("m-4");(0,_vitest.expect)(stats.keys).toContain("p-2");(0,_vitest.expect)(stats.keys).toContain("bg-blue-500");});(0,_vitest.it)("should clear cache",function(){var style1=(0,_runtime.tw)`m-4`;var style2=(0,_runtime.tw)`p-2`;(0,_vitest.expect)(style1).toBeDefined();(0,_vitest.expect)(style2).toBeDefined();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(2);(0,_runtime.clearCache)();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(0);});});(0,_vitest.describe)("state modifiers",function(){(0,_vitest.it)("should return activeStyle when active: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should return disabledStyle when disabled: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();});(0,_vitest.it)("should return both activeStyle and disabledStyle when both modifiers are used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});});(0,_vitest.it)("should merge base and active styles with multiple properties",function(){var result=(0,_runtime.tw)`p-4 m-2 bg-blue-500 active:bg-blue-700 active:p-6`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({padding:16,margin:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6",padding:24});});(0,_vitest.it)("should handle only modifier classes (no base)",function(){var result=(0,_runtime.tw)`active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});});(0,_vitest.it)("should work with twStyle function",function(){var result=(0,_runtime.twStyle)("bg-blue-500 active:bg-blue-700");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});});(0,_vitest.it)("should provide raw hex values for animations",function(){var _result$activeStyle;var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style.backgroundColor).toBe("#2b7fff");(0,_vitest.expect)(result==null||(_result$activeStyle=result.activeStyle)==null?void 0:_result$activeStyle.backgroundColor).toBe("#1447e6");});(0,_vitest.it)("should return focusStyle when focus: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 focus:bg-blue-800`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.focusStyle).toEqual({backgroundColor:"#193cb8"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should return all three modifier styles when all are used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700 focus:bg-blue-800 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.focusStyle).toEqual({backgroundColor:"#193cb8"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});});});});
1
+ var _vitest=require("vitest");var _runtime=require("./runtime");(0,_vitest.describe)("runtime",function(){(0,_vitest.beforeEach)(function(){(0,_runtime.clearCache)();(0,_runtime.setConfig)({});});(0,_vitest.describe)("tw template tag",function(){(0,_vitest.it)("should parse static classes",function(){var result=(0,_runtime.tw)`m-4 p-2 bg-blue-500`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should handle interpolated values",function(){var isActive=true;var result=(0,_runtime.tw)`m-4 ${isActive&&"bg-blue-500"}`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,backgroundColor:"#2b7fff"});});(0,_vitest.it)("should handle conditional classes",function(){var isLarge=true;var result=(0,_runtime.tw)`p-4 ${isLarge?"text-xl":"text-sm"}`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({padding:16,fontSize:20});});(0,_vitest.it)("should handle falsy values",function(){var result=(0,_runtime.tw)`m-4 ${false} ${null} ${undefined} p-2`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8});});(0,_vitest.it)("should preserve zero values in template literals",function(){var result=(0,_runtime.tw)`opacity-${0} m-4`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({opacity:0,margin:16});});(0,_vitest.it)("should preserve empty string values in template literals",function(){var result=(0,_runtime.tw)`m-4 ${""}p-2`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8});});(0,_vitest.it)("should handle mixed falsy and truthy numeric values",function(){var spacing=0;var result=(0,_runtime.tw)`m-${spacing} p-4`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:0,padding:16});});(0,_vitest.it)("should return empty style object for empty className",function(){var result=(0,_runtime.tw)``;(0,_vitest.expect)(result).toEqual({style:{}});});(0,_vitest.it)("should normalize whitespace",function(){var result=(0,_runtime.tw)`m-4 p-2 bg-blue-500`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});});});(0,_vitest.describe)("twStyle function",function(){(0,_vitest.it)("should parse className string",function(){var result=(0,_runtime.twStyle)("m-4 p-2 bg-blue-500");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();});(0,_vitest.it)("should return undefined for empty string",function(){var result=(0,_runtime.twStyle)("");(0,_vitest.expect)(result).toBeUndefined();});(0,_vitest.it)("should normalize whitespace",function(){var result=(0,_runtime.twStyle)("m-4 p-2 bg-blue-500");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:16,padding:8,backgroundColor:"#2b7fff"});});});(0,_vitest.describe)("setConfig",function(){(0,_vitest.it)("should set custom colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF",secondary:"#5856D6"}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({primary:"#007AFF",secondary:"#5856D6"});});(0,_vitest.it)("should flatten nested colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{brand:{light:"#FF6B6B",dark:"#CC0000"}}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({"brand-light":"#FF6B6B","brand-dark":"#CC0000"});});(0,_vitest.it)("should handle mixed flat and nested colors",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF",brand:{light:"#FF6B6B",dark:"#CC0000"}}}}});var colors=(0,_runtime.getCustomColors)();(0,_vitest.expect)(colors).toEqual({primary:"#007AFF","brand-light":"#FF6B6B","brand-dark":"#CC0000"});});(0,_vitest.it)("should clear cache when config changes",function(){var style=(0,_runtime.tw)`bg-blue-500`;(0,_vitest.expect)(style).toBeDefined();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(1);(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF"}}}});(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(0);});(0,_vitest.it)("should use custom colors in parsing",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF"}}}});var result=(0,_runtime.tw)`bg-primary`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#007AFF"});});});(0,_vitest.describe)("cache",function(){(0,_vitest.it)("should cache parsed styles",function(){var result1=(0,_runtime.tw)`m-4 p-2`;var result2=(0,_runtime.tw)`m-4 p-2`;(0,_vitest.expect)(result1).toBe(result2);});(0,_vitest.it)("should track cache stats",function(){var style1=(0,_runtime.tw)`m-4`;var style2=(0,_runtime.tw)`p-2`;var style3=(0,_runtime.tw)`bg-blue-500`;(0,_vitest.expect)(style1).toBeDefined();(0,_vitest.expect)(style2).toBeDefined();(0,_vitest.expect)(style3).toBeDefined();var stats=(0,_runtime.getCacheStats)();(0,_vitest.expect)(stats.size).toBe(3);(0,_vitest.expect)(stats.keys).toContain("m-4");(0,_vitest.expect)(stats.keys).toContain("p-2");(0,_vitest.expect)(stats.keys).toContain("bg-blue-500");});(0,_vitest.it)("should clear cache",function(){var style1=(0,_runtime.tw)`m-4`;var style2=(0,_runtime.tw)`p-2`;(0,_vitest.expect)(style1).toBeDefined();(0,_vitest.expect)(style2).toBeDefined();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(2);(0,_runtime.clearCache)();(0,_vitest.expect)((0,_runtime.getCacheStats)().size).toBe(0);});});(0,_vitest.describe)("state modifiers",function(){(0,_vitest.it)("should return activeStyle when active: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should return disabledStyle when disabled: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();});(0,_vitest.it)("should return both activeStyle and disabledStyle when both modifiers are used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});});(0,_vitest.it)("should merge base and active styles with multiple properties",function(){var result=(0,_runtime.tw)`p-4 m-2 bg-blue-500 active:bg-blue-700 active:p-6`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({padding:16,margin:8,backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6",padding:24});});(0,_vitest.it)("should handle only modifier classes (no base)",function(){var result=(0,_runtime.tw)`active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});});(0,_vitest.it)("should work with twStyle function",function(){var result=(0,_runtime.twStyle)("bg-blue-500 active:bg-blue-700");(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});});(0,_vitest.it)("should provide raw hex values for animations",function(){var _result$activeStyle;var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700`;(0,_vitest.expect)(result==null?void 0:result.style.backgroundColor).toBe("#2b7fff");(0,_vitest.expect)(result==null||(_result$activeStyle=result.activeStyle)==null?void 0:_result$activeStyle.backgroundColor).toBe("#1447e6");});(0,_vitest.it)("should return focusStyle when focus: modifier is used",function(){var result=(0,_runtime.tw)`bg-blue-500 focus:bg-blue-800`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.focusStyle).toEqual({backgroundColor:"#193cb8"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toBeUndefined();(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toBeUndefined();});(0,_vitest.it)("should return all three modifier styles when all are used",function(){var result=(0,_runtime.tw)`bg-blue-500 active:bg-blue-700 focus:bg-blue-800 disabled:bg-gray-300`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#2b7fff"});(0,_vitest.expect)(result==null?void 0:result.activeStyle).toEqual({backgroundColor:"#1447e6"});(0,_vitest.expect)(result==null?void 0:result.focusStyle).toEqual({backgroundColor:"#193cb8"});(0,_vitest.expect)(result==null?void 0:result.disabledStyle).toEqual({backgroundColor:"#d1d5dc"});});});});
@@ -0,0 +1 @@
1
+ var _vitest=require("vitest");var _tw=require("./tw");(0,_vitest.describe)("tw stub",function(){(0,_vitest.it)("should throw error when tw() is called without Babel transformation",function(){(0,_vitest.expect)(function(){return(0,_tw.tw)`bg-blue-500`;}).toThrow("tw() must be transformed by the Babel plugin. "+"Ensure @mgcrea/react-native-tailwind/babel is configured in your babel.config.js. "+"For runtime parsing, use: import { tw } from '@mgcrea/react-native-tailwind/runtime'");});(0,_vitest.it)("should throw error when twStyle() is called without Babel transformation",function(){(0,_vitest.expect)(function(){return(0,_tw.twStyle)("bg-blue-500");}).toThrow("twStyle() must be transformed by the Babel plugin. "+"Ensure @mgcrea/react-native-tailwind/babel is configured in your babel.config.js. "+"For runtime parsing, use: import { twStyle } from '@mgcrea/react-native-tailwind/runtime'");});(0,_vitest.it)("should throw error with template literal interpolation",function(){var dynamic="active";(0,_vitest.expect)(function(){return(0,_tw.tw)`bg-blue-500 ${dynamic}:bg-blue-700`;}).toThrow("tw() must be transformed by the Babel plugin");});});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgcrea/react-native-tailwind",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "description": "Compile-time Tailwind CSS for React Native with zero runtime overhead",
5
5
  "author": "Olivier Louvignes <olivier@mgcrea.io> (https://github.com/mgcrea)",
6
6
  "homepage": "https://github.com/mgcrea/react-native-tailwind#readme",
@@ -29,10 +29,9 @@
29
29
  }
30
30
  },
31
31
  "files": [
32
- "*.podspec",
33
32
  "src",
34
33
  "dist",
35
- "ios"
34
+ "README.md"
36
35
  ],
37
36
  "scripts": {
38
37
  "dev": "cd example; npm run dev",
@@ -46,7 +45,7 @@
46
45
  "lint": "eslint src/",
47
46
  "prettify": "prettier --write src/",
48
47
  "check": "tsc --noEmit",
49
- "spec": "vitest --run",
48
+ "spec": "vitest --run --coverage",
50
49
  "test": "npm run lint && npm run check && npm run spec",
51
50
  "prepare": "npm run build"
52
51
  },
@@ -69,7 +68,8 @@
69
68
  "@testing-library/react-native": "^13.3.3",
70
69
  "@types/babel__core": "^7.20.5",
71
70
  "@types/babel__traverse": "^7.28.0",
72
- "@types/react": "^19.2.5",
71
+ "@types/react": "^19.2.6",
72
+ "@vitest/coverage-v8": "^4.0.13",
73
73
  "babel-plugin-module-resolver": "^5.0.2",
74
74
  "esbuild": "^0.27.0",
75
75
  "eslint": "^9.39.1",
@@ -79,12 +79,12 @@
79
79
  "react": "^19.2.0",
80
80
  "react-native": "0.82.1",
81
81
  "typescript": "^5.9.3",
82
- "vitest": "^4.0.10"
82
+ "vitest": "^4.0.13"
83
83
  },
84
84
  "engines": {
85
85
  "node": ">=18"
86
86
  },
87
- "packageManager": "pnpm@10.22.0",
87
+ "packageManager": "pnpm@10.23.0",
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
@@ -72,6 +72,7 @@ export function loadTailwindConfig(configPath: string): TailwindConfig | null {
72
72
  configCache.set(configPath, resolved);
73
73
  return resolved;
74
74
  } catch (error) {
75
+ /* v8 ignore next 3 */
75
76
  if (process.env.NODE_ENV !== "production") {
76
77
  console.warn(`[react-native-tailwind] Failed to load config from ${configPath}:`, error);
77
78
  }
@@ -98,6 +99,7 @@ export function extractCustomColors(filename: string): Record<string, string> {
98
99
  }
99
100
 
100
101
  // Warn if using theme.colors instead of theme.extend.colors
102
+ /* v8 ignore next 5 */
101
103
  if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
102
104
  console.warn(
103
105
  "[react-native-tailwind] Using theme.colors will override all default colors. " +
@@ -480,3 +480,330 @@ describe("Babel plugin - placeholder: modifier transformation", () => {
480
480
  consoleSpy.mockRestore();
481
481
  });
482
482
  });
483
+
484
+ describe("Babel plugin - platform modifier transformation", () => {
485
+ it("should transform platform modifiers to Platform.select()", () => {
486
+ const input = `
487
+ import React from 'react';
488
+ import { View } from 'react-native';
489
+
490
+ export function Component() {
491
+ return (
492
+ <View className="p-4 ios:p-6 android:p-8" />
493
+ );
494
+ }
495
+ `;
496
+
497
+ const output = transform(input, undefined, true);
498
+
499
+ // Should import Platform from react-native
500
+ expect(output).toContain("Platform");
501
+ expect(output).toMatch(/import.*Platform.*from ['"]react-native['"]/);
502
+
503
+ // Should generate Platform.select()
504
+ expect(output).toContain("Platform.select");
505
+
506
+ // Should have base padding style
507
+ expect(output).toContain("_p_4");
508
+
509
+ // Should have iOS and Android specific styles
510
+ expect(output).toContain("_ios_p_6");
511
+ expect(output).toContain("_android_p_8");
512
+
513
+ // Should have correct style values in StyleSheet.create
514
+ expect(output).toMatch(/padding:\s*16/); // p-4
515
+ expect(output).toMatch(/padding:\s*24/); // p-6 (ios)
516
+ expect(output).toMatch(/padding:\s*32/); // p-8 (android)
517
+ });
518
+
519
+ it("should support multiple platform modifiers on same element", () => {
520
+ const input = `
521
+ import React from 'react';
522
+ import { View } from 'react-native';
523
+
524
+ export function Component() {
525
+ return (
526
+ <View className="bg-white ios:bg-blue-50 android:bg-green-50 p-4 ios:p-6 android:p-8" />
527
+ );
528
+ }
529
+ `;
530
+
531
+ const output = transform(input, undefined, true);
532
+
533
+ // Should have Platform import
534
+ expect(output).toContain("Platform");
535
+
536
+ // Should have base styles (combined key)
537
+ expect(output).toContain("_bg_white_p_4");
538
+
539
+ // Should have iOS specific styles (combined key for multiple ios: modifiers)
540
+ expect(output).toContain("_ios_bg_blue_50_p_6");
541
+
542
+ // Should have Android specific styles (combined key for multiple android: modifiers)
543
+ expect(output).toContain("_android_bg_green_50_p_8");
544
+
545
+ // Should contain Platform.select with both platforms
546
+ expect(output).toMatch(/Platform\.select\s*\(\s*\{[\s\S]*ios:/);
547
+ expect(output).toMatch(/Platform\.select\s*\(\s*\{[\s\S]*android:/);
548
+ });
549
+
550
+ it("should support web platform modifier", () => {
551
+ const input = `
552
+ import React from 'react';
553
+ import { View } from 'react-native';
554
+
555
+ export function Component() {
556
+ return (
557
+ <View className="p-4 web:p-2" />
558
+ );
559
+ }
560
+ `;
561
+
562
+ const output = transform(input, undefined, true);
563
+
564
+ // Should have Platform.select with web
565
+ expect(output).toContain("Platform.select");
566
+ expect(output).toContain("web:");
567
+ expect(output).toContain("_web_p_2");
568
+ });
569
+
570
+ it("should work with platform modifiers on all components", () => {
571
+ const input = `
572
+ import React from 'react';
573
+ import { View, Text, ScrollView } from 'react-native';
574
+
575
+ export function Component() {
576
+ return (
577
+ <View className="ios:bg-blue-500 android:bg-green-500">
578
+ <Text className="ios:text-lg android:text-xl">Platform text</Text>
579
+ <ScrollView contentContainerClassName="ios:p-4 android:p-8" />
580
+ </View>
581
+ );
582
+ }
583
+ `;
584
+
585
+ const output = transform(input, undefined, true);
586
+
587
+ // Should work on View - check for Platform.select separately (not checking style= format)
588
+ expect(output).toContain("Platform.select");
589
+
590
+ // Should work on Text
591
+ expect(output).toContain("_ios_text_lg");
592
+ expect(output).toContain("_android_text_xl");
593
+
594
+ // Should work on ScrollView contentContainerStyle
595
+ expect(output).toContain("contentContainerStyle");
596
+ });
597
+
598
+ it("should combine platform modifiers with state modifiers", () => {
599
+ const input = `
600
+ import React from 'react';
601
+ import { Pressable, Text } from 'react-native';
602
+
603
+ export function Component() {
604
+ return (
605
+ <Pressable className="bg-blue-500 active:bg-blue-700 ios:shadow-md android:shadow-sm p-4">
606
+ <Text className="text-white">Button</Text>
607
+ </Pressable>
608
+ );
609
+ }
610
+ `;
611
+
612
+ const output = transform(input, undefined, true);
613
+
614
+ // Should have Platform.select for platform modifiers
615
+ expect(output).toContain("Platform.select");
616
+ expect(output).toContain("_ios_shadow_md");
617
+ expect(output).toContain("_android_shadow_sm");
618
+
619
+ // Should have state modifier function for active
620
+ expect(output).toMatch(/\(\s*\{\s*pressed\s*\}\s*\)\s*=>/);
621
+ expect(output).toContain("pressed");
622
+ expect(output).toContain("_active_bg_blue_700");
623
+
624
+ // Should have base styles
625
+ expect(output).toContain("_bg_blue_500");
626
+ expect(output).toContain("_p_4");
627
+ });
628
+
629
+ it("should handle platform-specific colors", () => {
630
+ const input = `
631
+ import React from 'react';
632
+ import { View, Text } from 'react-native';
633
+
634
+ export function Component() {
635
+ return (
636
+ <View className="bg-gray-100 ios:bg-blue-50 android:bg-green-50">
637
+ <Text className="text-gray-900 ios:text-blue-900 android:text-green-900">
638
+ Platform colors
639
+ </Text>
640
+ </View>
641
+ );
642
+ }
643
+ `;
644
+
645
+ const output = transform(input, undefined, true);
646
+
647
+ // Should have color values in StyleSheet
648
+ expect(output).toMatch(/#[0-9A-F]{6}/i); // Hex color format
649
+
650
+ // Should have platform-specific color classes
651
+ expect(output).toContain("_ios_text_blue_900");
652
+ expect(output).toContain("_android_text_green_900");
653
+ });
654
+
655
+ it("should only add Platform import once when needed", () => {
656
+ const input = `
657
+ import React from 'react';
658
+ import { View } from 'react-native';
659
+
660
+ export function Component() {
661
+ return (
662
+ <>
663
+ <View className="ios:p-4" />
664
+ <View className="android:p-8" />
665
+ <View className="ios:bg-blue-500" />
666
+ </>
667
+ );
668
+ }
669
+ `;
670
+
671
+ const output = transform(input, undefined, true);
672
+
673
+ // Should have Platform import
674
+ expect(output).toContain("Platform");
675
+
676
+ // Count how many times Platform is imported (should be once)
677
+ const platformImports = output.match(/import.*Platform.*from ['"]react-native['"]/g);
678
+ expect(platformImports).toHaveLength(1);
679
+ });
680
+
681
+ it("should merge with existing Platform import", () => {
682
+ const input = `
683
+ import React from 'react';
684
+ import { View, Platform } from 'react-native';
685
+
686
+ export function Component() {
687
+ return <View className="ios:p-4 android:p-8" />;
688
+ }
689
+ `;
690
+
691
+ const output = transform(input, undefined, true);
692
+
693
+ // Should still use Platform.select
694
+ expect(output).toContain("Platform.select");
695
+
696
+ // Should not duplicate Platform import - Platform appears in import and Platform.select calls
697
+ expect(output).toMatch(/Platform.*react-native/);
698
+ });
699
+
700
+ it("should handle platform modifiers without base classes", () => {
701
+ const input = `
702
+ import React from 'react';
703
+ import { View } from 'react-native';
704
+
705
+ export function Component() {
706
+ return <View className="ios:p-6 android:p-8" />;
707
+ }
708
+ `;
709
+
710
+ const output = transform(input, undefined, true);
711
+
712
+ // Should only have Platform.select, no base style
713
+ expect(output).toContain("Platform.select");
714
+ expect(output).toContain("_ios_p_6");
715
+ expect(output).toContain("_android_p_8");
716
+
717
+ // Should not have generic padding without platform prefix
718
+ // Check that non-platform-prefixed style keys don't exist
719
+ expect(output).not.toMatch(/(?<!_ios|_android|_web)_p_4:/);
720
+ expect(output).not.toMatch(/(?<!_ios|_android|_web)_p_6:/);
721
+ expect(output).not.toMatch(/(?<!_ios|_android|_web)_p_8:/);
722
+ });
723
+ });
724
+
725
+ describe("Babel plugin - import injection", () => {
726
+ it("should not add StyleSheet import to files without className usage", () => {
727
+ const input = `
728
+ import { View, Text } from 'react-native';
729
+
730
+ function MyComponent() {
731
+ return <View><Text>Hello</Text></View>;
732
+ }
733
+ `;
734
+
735
+ const output = transform(input, undefined, true);
736
+
737
+ // Should not mutate the import by adding StyleSheet
738
+ // Count occurrences of "StyleSheet" in output
739
+ const styleSheetCount = (output.match(/StyleSheet/g) ?? []).length;
740
+ expect(styleSheetCount).toBe(0);
741
+
742
+ // Should not have _twStyles definition
743
+ expect(output).not.toContain("_twStyles");
744
+ expect(output).not.toContain("StyleSheet.create");
745
+
746
+ // Original imports should remain unchanged
747
+ expect(output).toContain("View");
748
+ expect(output).toContain("Text");
749
+ });
750
+
751
+ it("should add StyleSheet import only when className is used", () => {
752
+ const input = `
753
+ import { View } from 'react-native';
754
+
755
+ function MyComponent() {
756
+ return <View className="m-4 p-2" />;
757
+ }
758
+ `;
759
+
760
+ const output = transform(input, undefined, true);
761
+
762
+ // Should have StyleSheet import (both single and double quotes)
763
+ expect(output).toMatch(/import.*StyleSheet.*from ['"]react-native['"]|require\(['"]react-native['"]\)/);
764
+
765
+ // Should have _twStyles definition
766
+ expect(output).toContain("_twStyles");
767
+ expect(output).toContain("StyleSheet.create");
768
+ });
769
+
770
+ it("should add Platform import only when platform modifiers are used", () => {
771
+ const input = `
772
+ import { View } from 'react-native';
773
+
774
+ function MyComponent() {
775
+ return <View className="ios:m-4 android:m-2" />;
776
+ }
777
+ `;
778
+
779
+ const output = transform(input, undefined, true);
780
+
781
+ // Should have Platform import
782
+ expect(output).toContain("Platform");
783
+
784
+ // Should have StyleSheet import too
785
+ expect(output).toContain("StyleSheet");
786
+
787
+ // Should use Platform.select
788
+ expect(output).toContain("Platform.select");
789
+ });
790
+
791
+ it("should not add Platform import without platform modifiers", () => {
792
+ const input = `
793
+ import { View } from 'react-native';
794
+
795
+ function MyComponent() {
796
+ return <View className="m-4 p-2" />;
797
+ }
798
+ `;
799
+
800
+ const output = transform(input, undefined, true);
801
+
802
+ // Should not have Platform import
803
+ const platformCount = (output.match(/Platform/g) ?? []).length;
804
+ expect(platformCount).toBe(0);
805
+
806
+ // Should still have StyleSheet
807
+ expect(output).toContain("StyleSheet");
808
+ });
809
+ });