@mgcrea/react-native-tailwind 0.14.0 → 0.15.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.
- package/README.md +12 -8
- package/dist/babel/config-loader.d.ts +10 -0
- package/dist/babel/config-loader.test.ts +75 -21
- package/dist/babel/config-loader.ts +100 -2
- package/dist/babel/index.cjs +101 -33
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.d.ts +3 -1
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/sizing.d.ts +3 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/sizing.test.js +1 -1
- package/dist/parser/spacing.d.ts +3 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.js +1 -1
- package/dist/parser/transforms.d.ts +3 -1
- package/dist/parser/transforms.js +1 -1
- package/dist/parser/transforms.test.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +3 -3
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +3 -3
- package/dist/runtime.test.js +1 -1
- package/package.json +1 -1
- package/src/babel/config-loader.test.ts +75 -21
- package/src/babel/config-loader.ts +100 -2
- package/src/parser/index.ts +6 -5
- package/src/parser/layout.test.ts +94 -0
- package/src/parser/layout.ts +17 -12
- package/src/parser/sizing.test.ts +56 -0
- package/src/parser/sizing.ts +20 -15
- package/src/parser/spacing.test.ts +57 -0
- package/src/parser/spacing.ts +15 -10
- package/src/parser/transforms.test.ts +57 -0
- package/src/parser/transforms.ts +7 -3
- package/src/runtime.test.ts +149 -0
- package/src/runtime.ts +53 -1
package/dist/runtime.test.js
CHANGED
|
@@ -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 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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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`;var style=Array.isArray(result==null?void 0:result.style)?result.style.find(function(s){return s!==false;}):result==null?void 0:result.style;(0,_vitest.expect)(style&&typeof style==="object"&&"backgroundColor"in style?style.backgroundColor:undefined).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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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 theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.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.it)("should set custom fontFamily",function(){(0,_runtime.setConfig)({theme:{extend:{fontFamily:{display:"Inter",body:["Roboto","sans-serif"]}}}});var theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.fontFamily).toEqual({display:"Inter",body:"Roboto"});});(0,_vitest.it)("should set custom fontSize",function(){(0,_runtime.setConfig)({theme:{extend:{fontSize:{tiny:10,small:"12px",medium:16,large:"24"}}}});var theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.fontSize).toEqual({tiny:10,small:12,medium:16,large:24});});(0,_vitest.it)("should set custom spacing",function(){(0,_runtime.setConfig)({theme:{extend:{spacing:{xs:4,sm:"8px",md:16,lg:"2rem"}}}});var theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.spacing).toEqual({xs:4,sm:8,md:16,lg:32});});(0,_vitest.it)("should use custom fontSize in parsing",function(){(0,_runtime.setConfig)({theme:{extend:{fontSize:{tiny:10}}}});var result=(0,_runtime.tw)`text-tiny`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({fontSize:10});});(0,_vitest.it)("should use custom spacing in parsing",function(){(0,_runtime.setConfig)({theme:{extend:{spacing:{xs:4}}}});var result=(0,_runtime.tw)`m-xs p-xs`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({margin:4,padding:4});});(0,_vitest.it)("should reset fontSize and spacing when config is cleared",function(){(0,_runtime.setConfig)({theme:{extend:{fontSize:{tiny:10},spacing:{xs:4}}}});var theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.fontSize).toEqual({tiny:10});(0,_vitest.expect)(theme.spacing).toEqual({xs:4});(0,_runtime.setConfig)({});theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.fontSize).toEqual({});(0,_vitest.expect)(theme.spacing).toEqual({});});(0,_vitest.it)("should handle all theme extensions together",function(){(0,_runtime.setConfig)({theme:{extend:{colors:{primary:"#007AFF"},fontFamily:{display:"Inter"},fontSize:{tiny:10},spacing:{xs:4}}}});var theme=(0,_runtime.getCustomTheme)();(0,_vitest.expect)(theme.colors).toEqual({primary:"#007AFF"});(0,_vitest.expect)(theme.fontFamily).toEqual({display:"Inter"});(0,_vitest.expect)(theme.fontSize).toEqual({tiny:10});(0,_vitest.expect)(theme.spacing).toEqual({xs:4});var result=(0,_runtime.tw)`bg-primary font-display text-tiny m-xs`;(0,_vitest.expect)(result==null?void 0:result.style).toEqual({backgroundColor:"#007AFF",fontFamily:"Inter",fontSize:10,margin:4});});});(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`;var style=Array.isArray(result==null?void 0:result.style)?result.style.find(function(s){return s!==false;}):result==null?void 0:result.style;(0,_vitest.expect)(style&&typeof style==="object"&&"backgroundColor"in style?style.backgroundColor:undefined).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.
|
|
3
|
+
"version": "0.15.0",
|
|
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",
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
extractCustomTheme,
|
|
5
|
+
findTailwindConfig,
|
|
6
|
+
loadTailwindConfig,
|
|
7
|
+
warnUnsupportedThemeKeys,
|
|
8
|
+
} from "./config-loader";
|
|
4
9
|
|
|
5
10
|
// Mock fs
|
|
6
11
|
vi.mock("fs");
|
|
@@ -120,35 +125,84 @@ describe("config-loader", () => {
|
|
|
120
125
|
vi.spyOn(fs, "existsSync").mockReturnValue(false);
|
|
121
126
|
|
|
122
127
|
const result = extractCustomTheme("/project/src/file.ts");
|
|
123
|
-
expect(result).toEqual({ colors: {}, fontFamily: {}, fontSize: {} });
|
|
128
|
+
expect(result).toEqual({ colors: {}, fontFamily: {}, fontSize: {}, spacing: {} });
|
|
124
129
|
});
|
|
130
|
+
});
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
describe("warnUnsupportedThemeKeys", () => {
|
|
133
|
+
it("should warn about unsupported theme keys", () => {
|
|
134
|
+
const configPath = "/project/unsupported/tailwind.config.js";
|
|
135
|
+
const mockConfig = {
|
|
136
|
+
theme: {
|
|
137
|
+
extend: {
|
|
138
|
+
colors: { brand: "#123456" },
|
|
139
|
+
spacing: { "72": "18rem" }, // Supported (now!)
|
|
140
|
+
borderRadius: { xl: "1rem" }, // Unsupported
|
|
141
|
+
lineHeight: { tight: "1.25" }, // Unsupported
|
|
142
|
+
},
|
|
143
|
+
screens: { tablet: "640px" }, // Unsupported
|
|
144
|
+
},
|
|
145
|
+
};
|
|
128
146
|
|
|
129
|
-
vi.spyOn(
|
|
130
|
-
vi.spyOn(require, "resolve").mockReturnValue(configPath);
|
|
147
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
// For integration, we'd need to mock the entire flow
|
|
134
|
-
const result = extractCustomTheme("/project/src/file.ts");
|
|
149
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
135
150
|
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
152
|
+
expect.stringContaining("Unsupported theme configuration detected"),
|
|
153
|
+
);
|
|
154
|
+
// spacing is now supported, so should NOT warn about it
|
|
155
|
+
expect(consoleSpy).not.toHaveBeenCalledWith(expect.stringContaining("theme.extend.spacing"));
|
|
156
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.extend.borderRadius"));
|
|
157
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.extend.lineHeight"));
|
|
158
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.screens"));
|
|
159
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
160
|
+
expect.stringContaining("https://github.com/mgcrea/react-native-tailwind/issues/new"),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
consoleSpy.mockRestore();
|
|
138
164
|
});
|
|
139
165
|
|
|
140
|
-
it("should
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
166
|
+
it("should not warn for supported theme keys only", () => {
|
|
167
|
+
const configPath = "/project/supported/tailwind.config.js";
|
|
168
|
+
const mockConfig = {
|
|
169
|
+
theme: {
|
|
170
|
+
extend: {
|
|
171
|
+
colors: { brand: "#123456" },
|
|
172
|
+
fontFamily: { custom: "CustomFont" },
|
|
173
|
+
fontSize: { huge: "48px" },
|
|
174
|
+
spacing: { "72": "18rem" },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
180
|
+
|
|
181
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
182
|
+
|
|
183
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
184
|
+
|
|
185
|
+
consoleSpy.mockRestore();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should only warn once per config path", () => {
|
|
189
|
+
const configPath = "/project/once/tailwind.config.js";
|
|
190
|
+
const mockConfig = {
|
|
191
|
+
theme: {
|
|
192
|
+
extend: {
|
|
193
|
+
borderRadius: { xl: "1rem" }, // Unsupported
|
|
194
|
+
},
|
|
195
|
+
},
|
|
147
196
|
};
|
|
148
197
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
198
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
199
|
+
|
|
200
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
201
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
202
|
+
|
|
203
|
+
expect(consoleSpy).toHaveBeenCalledTimes(1);
|
|
204
|
+
|
|
205
|
+
consoleSpy.mockRestore();
|
|
152
206
|
});
|
|
153
207
|
});
|
|
154
208
|
});
|
|
@@ -15,13 +15,68 @@ export type TailwindConfig = {
|
|
|
15
15
|
colors?: Record<string, string | Record<string, string>>;
|
|
16
16
|
fontFamily?: Record<string, string | string[]>;
|
|
17
17
|
fontSize?: Record<string, string | number>;
|
|
18
|
+
spacing?: Record<string, string | number>;
|
|
19
|
+
[key: string]: unknown;
|
|
18
20
|
};
|
|
19
21
|
colors?: Record<string, string | Record<string, string>>;
|
|
20
22
|
fontFamily?: Record<string, string | string[]>;
|
|
21
23
|
fontSize?: Record<string, string | number>;
|
|
24
|
+
spacing?: Record<string, string | number>;
|
|
25
|
+
[key: string]: unknown;
|
|
22
26
|
};
|
|
23
27
|
};
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Theme keys currently supported by react-native-tailwind
|
|
31
|
+
*/
|
|
32
|
+
const SUPPORTED_THEME_KEYS = new Set(["colors", "fontFamily", "fontSize", "spacing", "extend"]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Cache for warned config paths to avoid duplicate warnings
|
|
36
|
+
*/
|
|
37
|
+
const warnedConfigPaths = new Set<string>();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check for unsupported theme extensions and warn the user
|
|
41
|
+
* @internal Exported for testing
|
|
42
|
+
*/
|
|
43
|
+
export function warnUnsupportedThemeKeys(config: TailwindConfig, configPath: string): void {
|
|
44
|
+
if (process.env.NODE_ENV === "production" || warnedConfigPaths.has(configPath)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const unsupportedKeys: string[] = [];
|
|
49
|
+
|
|
50
|
+
// Check theme.extend keys
|
|
51
|
+
if (config.theme?.extend && typeof config.theme.extend === "object") {
|
|
52
|
+
for (const key of Object.keys(config.theme.extend)) {
|
|
53
|
+
if (!SUPPORTED_THEME_KEYS.has(key)) {
|
|
54
|
+
unsupportedKeys.push(`theme.extend.${key}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check direct theme keys (excluding 'extend')
|
|
60
|
+
if (config.theme && typeof config.theme === "object") {
|
|
61
|
+
for (const key of Object.keys(config.theme)) {
|
|
62
|
+
if (key !== "extend" && !SUPPORTED_THEME_KEYS.has(key)) {
|
|
63
|
+
unsupportedKeys.push(`theme.${key}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (unsupportedKeys.length > 0) {
|
|
69
|
+
warnedConfigPaths.add(configPath);
|
|
70
|
+
console.warn(
|
|
71
|
+
`[react-native-tailwind] Unsupported theme configuration detected:\n` +
|
|
72
|
+
` ${unsupportedKeys.join(", ")}\n\n` +
|
|
73
|
+
` Currently supported: colors, fontFamily, fontSize, spacing\n\n` +
|
|
74
|
+
` These extensions will be ignored. If you need support for these features,\n` +
|
|
75
|
+
` please open an issue: https://github.com/mgcrea/react-native-tailwind/issues/new`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
25
80
|
// Cache configs per path to avoid repeated file I/O
|
|
26
81
|
const configCache = new Map<string, TailwindConfig | null>();
|
|
27
82
|
|
|
@@ -92,6 +147,7 @@ export type CustomTheme = {
|
|
|
92
147
|
colors: Record<string, string>;
|
|
93
148
|
fontFamily: Record<string, string>;
|
|
94
149
|
fontSize: Record<string, number>;
|
|
150
|
+
spacing: Record<string, number>;
|
|
95
151
|
};
|
|
96
152
|
|
|
97
153
|
/**
|
|
@@ -103,14 +159,17 @@ export function extractCustomTheme(filename: string): CustomTheme {
|
|
|
103
159
|
const configPath = findTailwindConfig(projectDir);
|
|
104
160
|
|
|
105
161
|
if (!configPath) {
|
|
106
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
162
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
107
163
|
}
|
|
108
164
|
|
|
109
165
|
const config = loadTailwindConfig(configPath);
|
|
110
166
|
if (!config?.theme) {
|
|
111
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
167
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
112
168
|
}
|
|
113
169
|
|
|
170
|
+
// Warn about unsupported theme keys
|
|
171
|
+
warnUnsupportedThemeKeys(config, configPath);
|
|
172
|
+
|
|
114
173
|
// Extract colors
|
|
115
174
|
/* v8 ignore next 5 */
|
|
116
175
|
if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
|
|
@@ -173,9 +232,48 @@ export function extractCustomTheme(filename: string): CustomTheme {
|
|
|
173
232
|
}
|
|
174
233
|
}
|
|
175
234
|
|
|
235
|
+
// Extract spacing
|
|
236
|
+
/* v8 ignore next 5 */
|
|
237
|
+
if (config.theme.spacing && !config.theme.extend?.spacing && process.env.NODE_ENV !== "production") {
|
|
238
|
+
console.warn(
|
|
239
|
+
"[react-native-tailwind] Using theme.spacing will override all default spacing. " +
|
|
240
|
+
"Use theme.extend.spacing to add custom spacing while keeping defaults.",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const spacing = config.theme.extend?.spacing ?? config.theme.spacing ?? {};
|
|
244
|
+
|
|
245
|
+
// Convert spacing values to numbers (handle rem, px, or number values)
|
|
246
|
+
const spacingResult: Record<string, number> = {};
|
|
247
|
+
for (const [key, value] of Object.entries(spacing)) {
|
|
248
|
+
if (typeof value === "number") {
|
|
249
|
+
spacingResult[key] = value;
|
|
250
|
+
} else if (typeof value === "string") {
|
|
251
|
+
// Parse string values: "18rem" -> 288, "16px" -> 16, "16" -> 16
|
|
252
|
+
let parsed: number;
|
|
253
|
+
if (value.endsWith("rem")) {
|
|
254
|
+
// Convert rem to px (1rem = 16px)
|
|
255
|
+
parsed = parseFloat(value.replace(/rem$/, "")) * 16;
|
|
256
|
+
} else {
|
|
257
|
+
// Parse px or unitless values
|
|
258
|
+
parsed = parseFloat(value.replace(/px$/, ""));
|
|
259
|
+
}
|
|
260
|
+
if (!isNaN(parsed)) {
|
|
261
|
+
spacingResult[key] = parsed;
|
|
262
|
+
} else {
|
|
263
|
+
/* v8 ignore next 5 */
|
|
264
|
+
if (process.env.NODE_ENV !== "production") {
|
|
265
|
+
console.warn(
|
|
266
|
+
`[react-native-tailwind] Invalid spacing value for "${key}": ${value}. Expected number or string like "16px" or "1rem".`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
176
273
|
return {
|
|
177
274
|
colors: flattenColors(colors),
|
|
178
275
|
fontFamily: fontFamilyResult,
|
|
179
276
|
fontSize: fontSizeResult,
|
|
277
|
+
spacing: spacingResult,
|
|
180
278
|
};
|
|
181
279
|
}
|
package/src/parser/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type CustomTheme = {
|
|
|
21
21
|
colors?: Record<string, string>;
|
|
22
22
|
fontFamily?: Record<string, string>;
|
|
23
23
|
fontSize?: Record<string, number>;
|
|
24
|
+
spacing?: Record<string, number>;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
/**
|
|
@@ -50,17 +51,17 @@ export function parseClassName(className: string, customTheme?: CustomTheme): St
|
|
|
50
51
|
export function parseClass(cls: string, customTheme?: CustomTheme): StyleObject {
|
|
51
52
|
// Try each parser in order
|
|
52
53
|
// Note: parseBorder must come before parseColor to avoid border-[3px] being parsed as a color
|
|
53
|
-
//
|
|
54
|
+
// Parsers receive relevant custom theme properties
|
|
54
55
|
const parsers: Array<(cls: string) => StyleObject | null> = [
|
|
55
|
-
parseSpacing,
|
|
56
|
+
(cls: string) => parseSpacing(cls, customTheme?.spacing),
|
|
56
57
|
(cls: string) => parseBorder(cls, customTheme?.colors),
|
|
57
58
|
(cls: string) => parseColor(cls, customTheme?.colors),
|
|
58
|
-
parseLayout,
|
|
59
|
+
(cls: string) => parseLayout(cls, customTheme?.spacing),
|
|
59
60
|
(cls: string) => parseTypography(cls, customTheme?.fontFamily, customTheme?.fontSize),
|
|
60
|
-
parseSizing,
|
|
61
|
+
(cls: string) => parseSizing(cls, customTheme?.spacing),
|
|
61
62
|
parseShadow,
|
|
62
63
|
parseAspectRatio,
|
|
63
|
-
parseTransform,
|
|
64
|
+
(cls: string) => parseTransform(cls, customTheme?.spacing),
|
|
64
65
|
];
|
|
65
66
|
|
|
66
67
|
for (const parser of parsers) {
|
|
@@ -719,3 +719,97 @@ describe("parseLayout - logical inset (RTL-aware)", () => {
|
|
|
719
719
|
expect(parseLayout("inset-e-[20%]")).toEqual({ end: "20%" });
|
|
720
720
|
});
|
|
721
721
|
});
|
|
722
|
+
|
|
723
|
+
describe("parseLayout - custom spacing", () => {
|
|
724
|
+
const customSpacing = {
|
|
725
|
+
xs: 4,
|
|
726
|
+
sm: 8,
|
|
727
|
+
md: 16,
|
|
728
|
+
lg: 32,
|
|
729
|
+
xl: 64,
|
|
730
|
+
"4": 20, // Override default (16)
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
it("should support custom spacing values for top/right/bottom/left", () => {
|
|
734
|
+
expect(parseLayout("top-xs", customSpacing)).toEqual({ top: 4 });
|
|
735
|
+
expect(parseLayout("top-sm", customSpacing)).toEqual({ top: 8 });
|
|
736
|
+
expect(parseLayout("top-md", customSpacing)).toEqual({ top: 16 });
|
|
737
|
+
expect(parseLayout("top-lg", customSpacing)).toEqual({ top: 32 });
|
|
738
|
+
expect(parseLayout("top-xl", customSpacing)).toEqual({ top: 64 });
|
|
739
|
+
|
|
740
|
+
expect(parseLayout("right-xs", customSpacing)).toEqual({ right: 4 });
|
|
741
|
+
expect(parseLayout("bottom-sm", customSpacing)).toEqual({ bottom: 8 });
|
|
742
|
+
expect(parseLayout("left-md", customSpacing)).toEqual({ left: 16 });
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it("should override default spacing values", () => {
|
|
746
|
+
// Without custom spacing, top-4 is 16
|
|
747
|
+
expect(parseLayout("top-4")).toEqual({ top: 16 });
|
|
748
|
+
|
|
749
|
+
// With custom spacing, top-4 is 20 (overridden)
|
|
750
|
+
expect(parseLayout("top-4", customSpacing)).toEqual({ top: 20 });
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it("should merge custom spacing with defaults", () => {
|
|
754
|
+
// Custom spacing should merge with defaults
|
|
755
|
+
// top-0 should still work (from defaults)
|
|
756
|
+
expect(parseLayout("top-0", customSpacing)).toEqual({ top: 0 });
|
|
757
|
+
|
|
758
|
+
// top-8 should still work (from defaults, not overridden)
|
|
759
|
+
expect(parseLayout("top-8", customSpacing)).toEqual({ top: 32 });
|
|
760
|
+
|
|
761
|
+
// top-xs should work (from custom)
|
|
762
|
+
expect(parseLayout("top-xs", customSpacing)).toEqual({ top: 4 });
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("should support custom spacing for start/end (RTL-aware)", () => {
|
|
766
|
+
expect(parseLayout("start-xs", customSpacing)).toEqual({ start: 4 });
|
|
767
|
+
expect(parseLayout("end-lg", customSpacing)).toEqual({ end: 32 });
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
it("should support custom spacing for inset utilities", () => {
|
|
771
|
+
expect(parseLayout("inset-xs", customSpacing)).toEqual({
|
|
772
|
+
top: 4,
|
|
773
|
+
right: 4,
|
|
774
|
+
bottom: 4,
|
|
775
|
+
left: 4,
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
expect(parseLayout("inset-x-sm", customSpacing)).toEqual({
|
|
779
|
+
left: 8,
|
|
780
|
+
right: 8,
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
expect(parseLayout("inset-y-md", customSpacing)).toEqual({
|
|
784
|
+
top: 16,
|
|
785
|
+
bottom: 16,
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
it("should support custom spacing for inset-s/inset-e", () => {
|
|
790
|
+
expect(parseLayout("inset-s-lg", customSpacing)).toEqual({ start: 32 });
|
|
791
|
+
expect(parseLayout("inset-e-xl", customSpacing)).toEqual({ end: 64 });
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
it("should support negative values with custom spacing for start/end", () => {
|
|
795
|
+
// Note: -top-*, -left-*, -right-*, -bottom-* negative prefixes are not supported
|
|
796
|
+
// Use arbitrary values like top-[-10px] for negative positioning
|
|
797
|
+
expect(parseLayout("-start-sm", customSpacing)).toEqual({ start: -8 });
|
|
798
|
+
expect(parseLayout("-end-md", customSpacing)).toEqual({ end: -16 });
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
it("should still support arbitrary values with custom spacing", () => {
|
|
802
|
+
expect(parseLayout("top-[100px]", customSpacing)).toEqual({ top: 100 });
|
|
803
|
+
expect(parseLayout("inset-[50%]", customSpacing)).toEqual({
|
|
804
|
+
top: "50%",
|
|
805
|
+
right: "50%",
|
|
806
|
+
bottom: "50%",
|
|
807
|
+
left: "50%",
|
|
808
|
+
});
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it("should handle non-layout classes as null", () => {
|
|
812
|
+
expect(parseLayout("text-xl", customSpacing)).toBeNull();
|
|
813
|
+
expect(parseLayout("bg-blue-500", customSpacing)).toBeNull();
|
|
814
|
+
});
|
|
815
|
+
});
|
package/src/parser/layout.ts
CHANGED
|
@@ -237,8 +237,13 @@ export const INSET_SCALE: Record<string, number> = {
|
|
|
237
237
|
|
|
238
238
|
/**
|
|
239
239
|
* Parse layout classes
|
|
240
|
+
* @param cls - The class name to parse
|
|
241
|
+
* @param customSpacing - Optional custom spacing values from tailwind.config (for inset utilities)
|
|
240
242
|
*/
|
|
241
|
-
export function parseLayout(cls: string): StyleObject | null {
|
|
243
|
+
export function parseLayout(cls: string, customSpacing?: Record<string, number>): StyleObject | null {
|
|
244
|
+
// Merge custom spacing with defaults for inset utilities
|
|
245
|
+
const insetMap = customSpacing ? { ...INSET_SCALE, ...customSpacing } : INSET_SCALE;
|
|
246
|
+
|
|
242
247
|
// Z-index: z-0, z-10, z-20, z-[999], etc.
|
|
243
248
|
if (cls.startsWith("z-")) {
|
|
244
249
|
const zKey = cls.substring(2);
|
|
@@ -270,7 +275,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
270
275
|
return { top: arbitraryTop };
|
|
271
276
|
}
|
|
272
277
|
|
|
273
|
-
const topValue =
|
|
278
|
+
const topValue = insetMap[topKey];
|
|
274
279
|
if (topValue !== undefined) {
|
|
275
280
|
return { top: topValue };
|
|
276
281
|
}
|
|
@@ -291,7 +296,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
291
296
|
return { right: arbitraryRight };
|
|
292
297
|
}
|
|
293
298
|
|
|
294
|
-
const rightValue =
|
|
299
|
+
const rightValue = insetMap[rightKey];
|
|
295
300
|
if (rightValue !== undefined) {
|
|
296
301
|
return { right: rightValue };
|
|
297
302
|
}
|
|
@@ -312,7 +317,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
312
317
|
return { bottom: arbitraryBottom };
|
|
313
318
|
}
|
|
314
319
|
|
|
315
|
-
const bottomValue =
|
|
320
|
+
const bottomValue = insetMap[bottomKey];
|
|
316
321
|
if (bottomValue !== undefined) {
|
|
317
322
|
return { bottom: bottomValue };
|
|
318
323
|
}
|
|
@@ -333,7 +338,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
333
338
|
return { left: arbitraryLeft };
|
|
334
339
|
}
|
|
335
340
|
|
|
336
|
-
const leftValue =
|
|
341
|
+
const leftValue = insetMap[leftKey];
|
|
337
342
|
if (leftValue !== undefined) {
|
|
338
343
|
return { left: leftValue };
|
|
339
344
|
}
|
|
@@ -364,7 +369,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
364
369
|
return { start: arbitraryStart };
|
|
365
370
|
}
|
|
366
371
|
|
|
367
|
-
const startValue =
|
|
372
|
+
const startValue = insetMap[startKey];
|
|
368
373
|
if (startValue !== undefined) {
|
|
369
374
|
return { start: isNegative ? -startValue : startValue };
|
|
370
375
|
}
|
|
@@ -395,7 +400,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
395
400
|
return { end: arbitraryEnd };
|
|
396
401
|
}
|
|
397
402
|
|
|
398
|
-
const endValue =
|
|
403
|
+
const endValue = insetMap[endKey];
|
|
399
404
|
if (endValue !== undefined) {
|
|
400
405
|
return { end: isNegative ? -endValue : endValue };
|
|
401
406
|
}
|
|
@@ -411,7 +416,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
411
416
|
return { left: arbitraryInset, right: arbitraryInset };
|
|
412
417
|
}
|
|
413
418
|
|
|
414
|
-
const insetValue =
|
|
419
|
+
const insetValue = insetMap[insetKey];
|
|
415
420
|
if (insetValue !== undefined) {
|
|
416
421
|
return { left: insetValue, right: insetValue };
|
|
417
422
|
}
|
|
@@ -427,7 +432,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
427
432
|
return { top: arbitraryInset, bottom: arbitraryInset };
|
|
428
433
|
}
|
|
429
434
|
|
|
430
|
-
const insetValue =
|
|
435
|
+
const insetValue = insetMap[insetKey];
|
|
431
436
|
if (insetValue !== undefined) {
|
|
432
437
|
return { top: insetValue, bottom: insetValue };
|
|
433
438
|
}
|
|
@@ -443,7 +448,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
443
448
|
return { start: arbitraryInset };
|
|
444
449
|
}
|
|
445
450
|
|
|
446
|
-
const insetValue =
|
|
451
|
+
const insetValue = insetMap[insetKey];
|
|
447
452
|
if (insetValue !== undefined) {
|
|
448
453
|
return { start: insetValue };
|
|
449
454
|
}
|
|
@@ -459,7 +464,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
459
464
|
return { end: arbitraryInset };
|
|
460
465
|
}
|
|
461
466
|
|
|
462
|
-
const insetValue =
|
|
467
|
+
const insetValue = insetMap[insetKey];
|
|
463
468
|
if (insetValue !== undefined) {
|
|
464
469
|
return { end: insetValue };
|
|
465
470
|
}
|
|
@@ -475,7 +480,7 @@ export function parseLayout(cls: string): StyleObject | null {
|
|
|
475
480
|
return { top: arbitraryInset, right: arbitraryInset, bottom: arbitraryInset, left: arbitraryInset };
|
|
476
481
|
}
|
|
477
482
|
|
|
478
|
-
const insetValue =
|
|
483
|
+
const insetValue = insetMap[insetKey];
|
|
479
484
|
if (insetValue !== undefined) {
|
|
480
485
|
return { top: insetValue, right: insetValue, bottom: insetValue, left: insetValue };
|
|
481
486
|
}
|
|
@@ -254,3 +254,59 @@ describe("parseSizing - comprehensive coverage", () => {
|
|
|
254
254
|
expect(parseSizing("h-full")).toEqual({ height: "100%" });
|
|
255
255
|
});
|
|
256
256
|
});
|
|
257
|
+
|
|
258
|
+
describe("parseSizing - custom spacing", () => {
|
|
259
|
+
const customSpacing = {
|
|
260
|
+
xs: 4,
|
|
261
|
+
sm: 8,
|
|
262
|
+
md: 16,
|
|
263
|
+
lg: 32,
|
|
264
|
+
xl: 64,
|
|
265
|
+
"4": 20, // Override default (16)
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
it("should support custom spacing values for width", () => {
|
|
269
|
+
expect(parseSizing("w-xs", customSpacing)).toEqual({ width: 4 });
|
|
270
|
+
expect(parseSizing("w-sm", customSpacing)).toEqual({ width: 8 });
|
|
271
|
+
expect(parseSizing("w-lg", customSpacing)).toEqual({ width: 32 });
|
|
272
|
+
expect(parseSizing("w-xl", customSpacing)).toEqual({ width: 64 });
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should support custom spacing values for height", () => {
|
|
276
|
+
expect(parseSizing("h-xs", customSpacing)).toEqual({ height: 4 });
|
|
277
|
+
expect(parseSizing("h-md", customSpacing)).toEqual({ height: 16 });
|
|
278
|
+
expect(parseSizing("h-xl", customSpacing)).toEqual({ height: 64 });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("should support custom spacing values for min/max dimensions", () => {
|
|
282
|
+
expect(parseSizing("min-w-sm", customSpacing)).toEqual({ minWidth: 8 });
|
|
283
|
+
expect(parseSizing("min-h-lg", customSpacing)).toEqual({ minHeight: 32 });
|
|
284
|
+
expect(parseSizing("max-w-xl", customSpacing)).toEqual({ maxWidth: 64 });
|
|
285
|
+
expect(parseSizing("max-h-md", customSpacing)).toEqual({ maxHeight: 16 });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("should allow custom spacing to override preset values", () => {
|
|
289
|
+
expect(parseSizing("w-4", customSpacing)).toEqual({ width: 20 }); // Custom 20, not default 16
|
|
290
|
+
expect(parseSizing("h-4", customSpacing)).toEqual({ height: 20 }); // Custom 20, not default 16
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should prefer arbitrary values over custom spacing", () => {
|
|
294
|
+
expect(parseSizing("w-[24px]", customSpacing)).toEqual({ width: 24 }); // Arbitrary wins
|
|
295
|
+
expect(parseSizing("h-[50]", customSpacing)).toEqual({ height: 50 }); // Arbitrary wins
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should fall back to preset scale for unknown custom keys", () => {
|
|
299
|
+
expect(parseSizing("w-8", customSpacing)).toEqual({ width: 32 }); // Not overridden, uses preset
|
|
300
|
+
expect(parseSizing("h-12", customSpacing)).toEqual({ height: 48 }); // Not overridden, uses preset
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it("should preserve percentage values with custom spacing", () => {
|
|
304
|
+
expect(parseSizing("w-full", customSpacing)).toEqual({ width: "100%" });
|
|
305
|
+
expect(parseSizing("h-1/2", customSpacing)).toEqual({ height: "50%" });
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("should work without custom spacing (backward compatible)", () => {
|
|
309
|
+
expect(parseSizing("w-4")).toEqual({ width: 16 }); // Default behavior
|
|
310
|
+
expect(parseSizing("h-8")).toEqual({ height: 32 }); // Default behavior
|
|
311
|
+
});
|
|
312
|
+
});
|