@mgcrea/react-native-tailwind 0.9.0 → 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 (48) hide show
  1. package/dist/babel/index.cjs +28 -13
  2. package/dist/babel/plugin.d.ts +2 -1
  3. package/dist/babel/plugin.test.ts +86 -0
  4. package/dist/babel/plugin.ts +7 -4
  5. package/dist/babel/utils/styleInjection.d.ts +1 -1
  6. package/dist/babel/utils/styleInjection.ts +24 -7
  7. package/dist/parser/aspectRatio.js +1 -1
  8. package/dist/parser/aspectRatio.test.js +1 -1
  9. package/dist/parser/spacing.d.ts +1 -1
  10. package/dist/parser/spacing.js +1 -1
  11. package/dist/parser/spacing.test.js +1 -1
  12. package/dist/runtime.cjs +1 -1
  13. package/dist/runtime.cjs.map +3 -3
  14. package/dist/runtime.js +1 -1
  15. package/dist/runtime.js.map +3 -3
  16. package/dist/runtime.test.js +1 -1
  17. package/package.json +2 -3
  18. package/src/babel/plugin.test.ts +86 -0
  19. package/src/babel/plugin.ts +7 -4
  20. package/src/babel/utils/styleInjection.ts +24 -7
  21. package/src/parser/aspectRatio.test.ts +25 -2
  22. package/src/parser/aspectRatio.ts +3 -3
  23. package/src/parser/spacing.test.ts +63 -0
  24. package/src/parser/spacing.ts +10 -6
  25. package/src/runtime.test.ts +27 -0
  26. package/src/runtime.ts +2 -1
  27. package/dist/babel/index.test.ts +0 -481
  28. package/dist/config/palettes.d.ts +0 -302
  29. package/dist/config/palettes.js +0 -1
  30. package/dist/parser/__snapshots__/aspectRatio.test.js.snap +0 -9
  31. package/dist/parser/__snapshots__/borders.test.js.snap +0 -23
  32. package/dist/parser/__snapshots__/colors.test.js.snap +0 -251
  33. package/dist/parser/__snapshots__/shadows.test.js.snap +0 -76
  34. package/dist/parser/__snapshots__/sizing.test.js.snap +0 -61
  35. package/dist/parser/__snapshots__/spacing.test.js.snap +0 -40
  36. package/dist/parser/__snapshots__/transforms.test.js.snap +0 -58
  37. package/dist/parser/__snapshots__/typography.test.js.snap +0 -30
  38. package/dist/parser/aspectRatio.test.d.ts +0 -1
  39. package/dist/parser/borders.test.d.ts +0 -1
  40. package/dist/parser/colors.test.d.ts +0 -1
  41. package/dist/parser/layout.test.d.ts +0 -1
  42. package/dist/parser/modifiers.test.d.ts +0 -1
  43. package/dist/parser/shadows.test.d.ts +0 -1
  44. package/dist/parser/sizing.test.d.ts +0 -1
  45. package/dist/parser/spacing.test.d.ts +0 -1
  46. package/dist/parser/typography.test.d.ts +0 -1
  47. package/dist/types.d.ts +0 -42
  48. 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"});});});});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mgcrea/react-native-tailwind",
3
- "version": "0.9.0",
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",
@@ -721,3 +721,89 @@ describe("Babel plugin - platform modifier transformation", () => {
721
721
  expect(output).not.toMatch(/(?<!_ios|_android|_web)_p_8:/);
722
722
  });
723
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
+ });
@@ -76,6 +76,8 @@ type PluginState = PluginPass & {
76
76
  // Track tw/twStyle imports from main package
77
77
  twImportNames: Set<string>; // e.g., ['tw', 'twStyle'] or ['tw as customTw']
78
78
  hasTwImport: boolean;
79
+ // Track react-native import path for conditional StyleSheet/Platform injection
80
+ reactNativeImportPath?: NodePath<BabelTypes.ImportDeclaration>;
79
81
  };
80
82
 
81
83
  // Default identifier for the generated StyleSheet constant
@@ -161,17 +163,18 @@ export default function reactNativeTailwindBabelPlugin(
161
163
  return false;
162
164
  });
163
165
 
166
+ // Only track if imports exist - don't mutate yet
167
+ // Actual import injection happens in Program.exit only if needed
164
168
  if (hasStyleSheet) {
165
169
  state.hasStyleSheetImport = true;
166
- } else {
167
- // Add StyleSheet to existing import
168
- node.specifiers.push(t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet")));
169
- state.hasStyleSheetImport = true;
170
170
  }
171
171
 
172
172
  if (hasPlatform) {
173
173
  state.hasPlatformImport = true;
174
174
  }
175
+
176
+ // Store reference to the react-native import for later modification if needed
177
+ state.reactNativeImportPath = path;
175
178
  }
176
179
 
177
180
  // Track tw/twStyle imports from main package (for compile-time transformation)
@@ -7,16 +7,33 @@ import type * as BabelTypes from "@babel/types";
7
7
  import type { StyleObject } from "../../types/core.js";
8
8
 
9
9
  /**
10
- * Add StyleSheet import to the file
10
+ * Add StyleSheet import to the file or merge with existing react-native import
11
11
  */
12
12
  export function addStyleSheetImport(path: NodePath<BabelTypes.Program>, t: typeof BabelTypes): void {
13
- const importDeclaration = t.importDeclaration(
14
- [t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet"))],
15
- t.stringLiteral("react-native"),
16
- );
13
+ // Check if there's already a react-native import
14
+ const body = path.node.body;
15
+ let reactNativeImport: BabelTypes.ImportDeclaration | null = null;
16
+
17
+ for (const statement of body) {
18
+ if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
19
+ reactNativeImport = statement;
20
+ break;
21
+ }
22
+ }
17
23
 
18
- // Add import at the top of the file
19
- path.unshiftContainer("body", importDeclaration);
24
+ if (reactNativeImport) {
25
+ // Add StyleSheet to existing react-native import
26
+ reactNativeImport.specifiers.push(
27
+ t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet")),
28
+ );
29
+ } else {
30
+ // Create new react-native import with StyleSheet
31
+ const importDeclaration = t.importDeclaration(
32
+ [t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet"))],
33
+ t.stringLiteral("react-native"),
34
+ );
35
+ path.unshiftContainer("body", importDeclaration);
36
+ }
20
37
  }
21
38
 
22
39
  /**
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { ASPECT_RATIO_PRESETS, parseAspectRatio } from "./aspectRatio";
3
+ import { parseClassName } from "./index";
3
4
 
4
5
  describe("ASPECT_RATIO_PRESETS", () => {
5
6
  it("should export aspect ratio presets", () => {
@@ -29,8 +30,8 @@ describe("parseAspectRatio - preset values", () => {
29
30
  });
30
31
 
31
32
  it("should parse aspect-auto", () => {
32
- // aspect-auto removes the aspect ratio constraint
33
- expect(parseAspectRatio("aspect-auto")).toEqual({});
33
+ // aspect-auto removes the aspect ratio constraint by explicitly setting to undefined
34
+ expect(parseAspectRatio("aspect-auto")).toEqual({ aspectRatio: undefined });
34
35
  });
35
36
  });
36
37
 
@@ -131,6 +132,28 @@ describe("parseAspectRatio - type validation", () => {
131
132
  });
132
133
  });
133
134
 
135
+ describe("parseAspectRatio - override behavior", () => {
136
+ it("should allow aspect-auto to override previously set aspect ratios", () => {
137
+ // This tests the fix for issue #3 - aspect-auto must explicitly set aspectRatio: undefined
138
+ // to override previous aspect ratio values when using Object.assign
139
+ const result = parseClassName("aspect-square aspect-auto");
140
+ expect(result).toEqual({ aspectRatio: undefined });
141
+ });
142
+
143
+ it("should allow aspect ratios to override aspect-auto", () => {
144
+ const result = parseClassName("aspect-auto aspect-square");
145
+ expect(result).toEqual({ aspectRatio: 1 });
146
+ });
147
+
148
+ it("should apply last aspect ratio in sequence", () => {
149
+ const result = parseClassName("aspect-square aspect-video aspect-auto");
150
+ expect(result).toEqual({ aspectRatio: undefined });
151
+
152
+ const result2 = parseClassName("aspect-auto aspect-square aspect-video");
153
+ expect(result2).toEqual({ aspectRatio: 16 / 9 });
154
+ });
155
+ });
156
+
134
157
  describe("parseAspectRatio - comprehensive coverage", () => {
135
158
  it("should parse all preset variants without errors", () => {
136
159
  const presets = ["aspect-auto", "aspect-square", "aspect-video"];
@@ -52,10 +52,10 @@ export function parseAspectRatio(cls: string): StyleObject | null {
52
52
  // Check for preset values
53
53
  if (cls in ASPECT_RATIO_PRESETS) {
54
54
  const aspectRatio = ASPECT_RATIO_PRESETS[cls];
55
- // aspect-auto removes the aspect ratio constraint by returning empty object
56
- // (this effectively unsets the aspectRatio property)
55
+ // aspect-auto removes the aspect ratio constraint by explicitly setting to undefined
56
+ // This ensures it overrides any previously set aspectRatio in Object.assign
57
57
  if (aspectRatio === undefined) {
58
- return {};
58
+ return { aspectRatio: undefined };
59
59
  }
60
60
  return { aspectRatio };
61
61
  }
@@ -60,6 +60,69 @@ describe("parseSpacing - margin", () => {
60
60
  });
61
61
  });
62
62
 
63
+ describe("parseSpacing - negative margin", () => {
64
+ it("should parse negative margin all sides", () => {
65
+ expect(parseSpacing("-m-0")).toEqual({ margin: -0 }); // JavaScript -0 is distinct from +0
66
+ expect(parseSpacing("-m-4")).toEqual({ margin: -16 });
67
+ expect(parseSpacing("-m-8")).toEqual({ margin: -32 });
68
+ expect(parseSpacing("-m-96")).toEqual({ margin: -384 });
69
+ });
70
+
71
+ it("should parse negative margin with fractional values", () => {
72
+ expect(parseSpacing("-m-0.5")).toEqual({ margin: -2 });
73
+ expect(parseSpacing("-m-1.5")).toEqual({ margin: -6 });
74
+ expect(parseSpacing("-m-2.5")).toEqual({ margin: -10 });
75
+ });
76
+
77
+ it("should parse negative margin horizontal", () => {
78
+ expect(parseSpacing("-mx-4")).toEqual({ marginHorizontal: -16 });
79
+ expect(parseSpacing("-mx-8")).toEqual({ marginHorizontal: -32 });
80
+ });
81
+
82
+ it("should parse negative margin vertical", () => {
83
+ expect(parseSpacing("-my-4")).toEqual({ marginVertical: -16 });
84
+ expect(parseSpacing("-my-8")).toEqual({ marginVertical: -32 });
85
+ });
86
+
87
+ it("should parse negative margin directional", () => {
88
+ expect(parseSpacing("-mt-4")).toEqual({ marginTop: -16 });
89
+ expect(parseSpacing("-mr-4")).toEqual({ marginRight: -16 });
90
+ expect(parseSpacing("-mb-4")).toEqual({ marginBottom: -16 });
91
+ expect(parseSpacing("-ml-4")).toEqual({ marginLeft: -16 });
92
+ });
93
+
94
+ it("should parse negative margin with arbitrary values", () => {
95
+ expect(parseSpacing("-m-[16px]")).toEqual({ margin: -16 });
96
+ expect(parseSpacing("-m-[16]")).toEqual({ margin: -16 });
97
+ expect(parseSpacing("-m-[100px]")).toEqual({ margin: -100 });
98
+ expect(parseSpacing("-m-[100]")).toEqual({ margin: -100 });
99
+ });
100
+
101
+ it("should parse negative margin directional with arbitrary values", () => {
102
+ expect(parseSpacing("-mt-[24px]")).toEqual({ marginTop: -24 });
103
+ expect(parseSpacing("-mr-[32]")).toEqual({ marginRight: -32 });
104
+ expect(parseSpacing("-mb-[16px]")).toEqual({ marginBottom: -16 });
105
+ expect(parseSpacing("-ml-[48]")).toEqual({ marginLeft: -48 });
106
+ });
107
+
108
+ it("should parse negative margin horizontal/vertical with arbitrary values", () => {
109
+ expect(parseSpacing("-mx-[20px]")).toEqual({ marginHorizontal: -20 });
110
+ expect(parseSpacing("-my-[30]")).toEqual({ marginVertical: -30 });
111
+ });
112
+
113
+ it("should not parse negative padding (invalid)", () => {
114
+ expect(parseSpacing("-p-4")).toBeNull();
115
+ expect(parseSpacing("-px-4")).toBeNull();
116
+ expect(parseSpacing("-pt-4")).toBeNull();
117
+ expect(parseSpacing("-p-[16px]")).toBeNull();
118
+ });
119
+
120
+ it("should not parse negative gap (invalid)", () => {
121
+ expect(parseSpacing("-gap-4")).toBeNull();
122
+ expect(parseSpacing("-gap-[16px]")).toBeNull();
123
+ });
124
+ });
125
+
63
126
  describe("parseSpacing - padding", () => {
64
127
  it("should parse padding all sides", () => {
65
128
  expect(parseSpacing("p-0")).toEqual({ padding: 0 });
@@ -69,24 +69,28 @@ function parseArbitrarySpacing(value: string): number | null {
69
69
 
70
70
  /**
71
71
  * Parse spacing classes (margin, padding, gap)
72
- * Examples: m-4, mx-2, mt-8, p-4, px-2, pt-8, gap-4, m-[16px]
72
+ * Examples: m-4, mx-2, mt-8, p-4, px-2, pt-8, gap-4, m-[16px], -m-4, -mt-[10px]
73
73
  */
74
74
  export function parseSpacing(cls: string): StyleObject | null {
75
- // Margin: m-4, mx-2, mt-8, m-[16px], etc.
76
- const marginMatch = cls.match(/^m([xytrbls]?)-(.+)$/);
75
+ // Margin: m-4, mx-2, mt-8, m-[16px], -m-4, -mt-2, etc.
76
+ // Supports negative values for margins (but not padding or gap)
77
+ const marginMatch = cls.match(/^(-?)m([xytrbls]?)-(.+)$/);
77
78
  if (marginMatch) {
78
- const [, dir, valueStr] = marginMatch;
79
+ const [, negativePrefix, dir, valueStr] = marginMatch;
80
+ const isNegative = negativePrefix === "-";
79
81
 
80
82
  // Try arbitrary value first
81
83
  const arbitraryValue = parseArbitrarySpacing(valueStr);
82
84
  if (arbitraryValue !== null) {
83
- return getMarginStyle(dir, arbitraryValue);
85
+ const finalValue = isNegative ? -arbitraryValue : arbitraryValue;
86
+ return getMarginStyle(dir, finalValue);
84
87
  }
85
88
 
86
89
  // Try preset scale
87
90
  const scaleValue = SPACING_SCALE[valueStr];
88
91
  if (scaleValue !== undefined) {
89
- return getMarginStyle(dir, scaleValue);
92
+ const finalValue = isNegative ? -scaleValue : scaleValue;
93
+ return getMarginStyle(dir, finalValue);
90
94
  }
91
95
  }
92
96
 
@@ -45,6 +45,33 @@ describe("runtime", () => {
45
45
  });
46
46
  });
47
47
 
48
+ it("should preserve zero values in template literals", () => {
49
+ // Issue #1 fix: 0 should be treated as valid value, not filtered out
50
+ const result = tw`opacity-${0} m-4`;
51
+ expect(result?.style).toEqual({
52
+ opacity: 0,
53
+ margin: 16,
54
+ });
55
+ });
56
+
57
+ it("should preserve empty string values in template literals", () => {
58
+ // Empty strings should be preserved (even though they don't contribute to className)
59
+ const result = tw`m-4 ${""}p-2`;
60
+ expect(result?.style).toEqual({
61
+ margin: 16,
62
+ padding: 8,
63
+ });
64
+ });
65
+
66
+ it("should handle mixed falsy and truthy numeric values", () => {
67
+ const spacing = 0;
68
+ const result = tw`m-${spacing} p-4`;
69
+ expect(result?.style).toEqual({
70
+ margin: 0,
71
+ padding: 16,
72
+ });
73
+ });
74
+
48
75
  it("should return empty style object for empty className", () => {
49
76
  const result = tw``;
50
77
  expect(result).toEqual({ style: {} });
package/src/runtime.ts CHANGED
@@ -211,8 +211,9 @@ export function tw<T extends NativeStyle = NativeStyle>(
211
211
  const className = strings.reduce((acc, str, i) => {
212
212
  const value = values[i];
213
213
  // Handle falsy values (false, null, undefined) - don't add them
214
+ // Note: 0 and empty string are preserved as they may be valid values
214
215
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
215
- const valueStr = value ? String(value) : "";
216
+ const valueStr = value != null && value !== false ? String(value) : "";
216
217
  return acc + str + valueStr;
217
218
  }, "");
218
219