@mgcrea/react-native-tailwind 0.10.0 → 0.11.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.
Files changed (40) hide show
  1. package/README.md +30 -13
  2. package/dist/babel/config-loader.d.ts +12 -3
  3. package/dist/babel/config-loader.test.ts +14 -12
  4. package/dist/babel/config-loader.ts +41 -9
  5. package/dist/babel/index.cjs +50 -27
  6. package/dist/babel/plugin.d.ts +2 -1
  7. package/dist/babel/plugin.ts +11 -10
  8. package/dist/babel/utils/colorSchemeModifierProcessing.d.ts +3 -3
  9. package/dist/babel/utils/colorSchemeModifierProcessing.ts +4 -4
  10. package/dist/babel/utils/dynamicProcessing.d.ts +5 -5
  11. package/dist/babel/utils/dynamicProcessing.ts +11 -11
  12. package/dist/babel/utils/modifierProcessing.d.ts +3 -3
  13. package/dist/babel/utils/modifierProcessing.ts +5 -5
  14. package/dist/babel/utils/platformModifierProcessing.d.ts +3 -3
  15. package/dist/babel/utils/platformModifierProcessing.ts +4 -4
  16. package/dist/babel/utils/twProcessing.d.ts +3 -3
  17. package/dist/babel/utils/twProcessing.ts +6 -6
  18. package/dist/parser/index.d.ts +11 -4
  19. package/dist/parser/index.js +1 -1
  20. package/dist/parser/typography.d.ts +3 -1
  21. package/dist/parser/typography.js +1 -1
  22. package/dist/runtime.cjs +1 -1
  23. package/dist/runtime.cjs.map +3 -3
  24. package/dist/runtime.d.ts +8 -1
  25. package/dist/runtime.js +1 -1
  26. package/dist/runtime.js.map +3 -3
  27. package/dist/runtime.test.js +1 -1
  28. package/package.json +1 -1
  29. package/src/babel/config-loader.test.ts +14 -12
  30. package/src/babel/config-loader.ts +41 -9
  31. package/src/babel/plugin.ts +11 -10
  32. package/src/babel/utils/colorSchemeModifierProcessing.ts +4 -4
  33. package/src/babel/utils/dynamicProcessing.ts +11 -11
  34. package/src/babel/utils/modifierProcessing.ts +5 -5
  35. package/src/babel/utils/platformModifierProcessing.ts +4 -4
  36. package/src/babel/utils/twProcessing.ts +6 -6
  37. package/src/parser/index.ts +16 -8
  38. package/src/parser/typography.ts +14 -2
  39. package/src/runtime.test.ts +7 -7
  40. package/src/runtime.ts +37 -14
@@ -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 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 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`;(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.10.0",
3
+ "version": "0.11.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,6 @@
1
1
  import * as fs from "fs";
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
- import { extractCustomColors, findTailwindConfig, loadTailwindConfig } from "./config-loader";
3
+ import { extractCustomTheme, findTailwindConfig, loadTailwindConfig } from "./config-loader";
4
4
 
5
5
  // Mock fs
6
6
  vi.mock("fs");
@@ -115,15 +115,15 @@ describe("config-loader", () => {
115
115
  });
116
116
  });
117
117
 
118
- describe("extractCustomColors", () => {
119
- it("should return empty object when no config found", () => {
118
+ describe("extractCustomTheme", () => {
119
+ it("should return empty theme when no config found", () => {
120
120
  vi.spyOn(fs, "existsSync").mockReturnValue(false);
121
121
 
122
- const result = extractCustomColors("/project/src/file.ts");
123
- expect(result).toEqual({});
122
+ const result = extractCustomTheme("/project/src/file.ts");
123
+ expect(result).toEqual({ colors: {}, fontFamily: {} });
124
124
  });
125
125
 
126
- it("should return empty object when config has no theme", () => {
126
+ it("should return empty theme when config has no theme", () => {
127
127
  const configPath = "/project/tailwind.config.js";
128
128
 
129
129
  vi.spyOn(fs, "existsSync").mockImplementation((filepath) => filepath === configPath);
@@ -131,22 +131,24 @@ describe("config-loader", () => {
131
131
 
132
132
  // loadTailwindConfig will be called, but we've already tested it
133
133
  // For integration, we'd need to mock the entire flow
134
- const result = extractCustomColors("/project/src/file.ts");
134
+ const result = extractCustomTheme("/project/src/file.ts");
135
135
 
136
136
  // Without actual config loading, this returns empty
137
- expect(result).toEqual({});
137
+ expect(result).toEqual({ colors: {}, fontFamily: {} });
138
138
  });
139
139
 
140
- it("should extract colors from theme.extend.colors", () => {
140
+ it("should extract colors and fontFamily from theme.extend", () => {
141
141
  // This would require complex mocking of the entire require flow
142
- // Testing the logic: theme.extend.colors is preferred
142
+ // Testing the logic: theme.extend is preferred
143
143
  const colors = { brand: { light: "#fff", dark: "#000" } };
144
+ const fontFamily = { sans: ['"SF Pro"'], custom: ['"Custom Font"'] };
144
145
  const theme = {
145
- extend: { colors },
146
+ extend: { colors, fontFamily },
146
147
  };
147
148
 
148
- // If we had the config, we'd flatten the colors
149
+ // If we had the config, we'd flatten the colors and convert fontFamily
149
150
  expect(theme.extend.colors).toEqual(colors);
151
+ expect(theme.extend.fontFamily).toEqual(fontFamily);
150
152
  });
151
153
  });
152
154
  });
@@ -13,8 +13,10 @@ export type TailwindConfig = {
13
13
  theme?: {
14
14
  extend?: {
15
15
  colors?: Record<string, string | Record<string, string>>;
16
+ fontFamily?: Record<string, string | string[]>;
16
17
  };
17
18
  colors?: Record<string, string | Record<string, string>>;
19
+ fontFamily?: Record<string, string | string[]>;
18
20
  };
19
21
  };
20
22
 
@@ -82,23 +84,31 @@ export function loadTailwindConfig(configPath: string): TailwindConfig | null {
82
84
  }
83
85
 
84
86
  /**
85
- * Extract custom colors from tailwind config
86
- * Prefers theme.extend.colors over theme.colors to avoid overriding defaults
87
+ * Custom theme configuration extracted from tailwind.config
87
88
  */
88
- export function extractCustomColors(filename: string): Record<string, string> {
89
+ export type CustomTheme = {
90
+ colors: Record<string, string>;
91
+ fontFamily: Record<string, string>;
92
+ };
93
+
94
+ /**
95
+ * Extract all custom theme extensions from tailwind config
96
+ * Prefers theme.extend.* over theme.* to avoid overriding defaults
97
+ */
98
+ export function extractCustomTheme(filename: string): CustomTheme {
89
99
  const projectDir = path.dirname(filename);
90
100
  const configPath = findTailwindConfig(projectDir);
91
101
 
92
102
  if (!configPath) {
93
- return {};
103
+ return { colors: {}, fontFamily: {} };
94
104
  }
95
105
 
96
106
  const config = loadTailwindConfig(configPath);
97
107
  if (!config?.theme) {
98
- return {};
108
+ return { colors: {}, fontFamily: {} };
99
109
  }
100
110
 
101
- // Warn if using theme.colors instead of theme.extend.colors
111
+ // Extract colors
102
112
  /* v8 ignore next 5 */
103
113
  if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
104
114
  console.warn(
@@ -106,9 +116,31 @@ export function extractCustomColors(filename: string): Record<string, string> {
106
116
  "Use theme.extend.colors to add custom colors while keeping defaults.",
107
117
  );
108
118
  }
109
-
110
- // Prefer theme.extend.colors
111
119
  const colors = config.theme.extend?.colors ?? config.theme.colors ?? {};
112
120
 
113
- return flattenColors(colors);
121
+ // Extract fontFamily
122
+ /* v8 ignore next 5 */
123
+ if (config.theme.fontFamily && !config.theme.extend?.fontFamily && process.env.NODE_ENV !== "production") {
124
+ console.warn(
125
+ "[react-native-tailwind] Using theme.fontFamily will override all default font families. " +
126
+ "Use theme.extend.fontFamily to add custom fonts while keeping defaults.",
127
+ );
128
+ }
129
+ const fontFamily = config.theme.extend?.fontFamily ?? config.theme.fontFamily ?? {};
130
+
131
+ // Convert fontFamily values to strings (take first value if array)
132
+ const fontFamilyResult: Record<string, string> = {};
133
+ for (const [key, value] of Object.entries(fontFamily)) {
134
+ if (Array.isArray(value)) {
135
+ // Take first font in the array (React Native doesn't support font stacks)
136
+ fontFamilyResult[key] = value[0];
137
+ } else {
138
+ fontFamilyResult[key] = value;
139
+ }
140
+ }
141
+
142
+ return {
143
+ colors: flattenColors(colors),
144
+ fontFamily: fontFamilyResult,
145
+ };
114
146
  }
@@ -18,7 +18,8 @@ import {
18
18
  } from "../parser/index.js";
19
19
  import type { StyleObject } from "../types/core.js";
20
20
  import { generateStyleKey } from "../utils/styleKey.js";
21
- import { extractCustomColors } from "./config-loader.js";
21
+ import type { CustomTheme } from "./config-loader.js";
22
+ import { extractCustomTheme } from "./config-loader.js";
22
23
 
23
24
  // Import utility functions
24
25
  import type { SchemeModifierConfig } from "../types/config.js";
@@ -99,7 +100,7 @@ type PluginState = PluginPass & {
99
100
  hasColorSchemeImport: boolean;
100
101
  needsColorSchemeImport: boolean;
101
102
  colorSchemeVariableName: string;
102
- customColors: Record<string, string>;
103
+ customTheme: CustomTheme;
103
104
  schemeModifierConfig: SchemeModifierConfig;
104
105
  supportedAttributes: Set<string>;
105
106
  attributePatterns: RegExp[];
@@ -231,8 +232,8 @@ export default function reactNativeTailwindBabelPlugin(
231
232
  state.hasTwImport = false;
232
233
  state.functionComponentsNeedingColorScheme = new Set();
233
234
 
234
- // Load custom colors from tailwind.config.*
235
- state.customColors = extractCustomColors(state.file.opts.filename ?? "");
235
+ // Load custom theme from tailwind.config.*
236
+ state.customTheme = extractCustomTheme(state.file.opts.filename ?? "");
236
237
 
237
238
  // Use scheme modifier config from plugin options
238
239
  state.schemeModifierConfig = schemeModifierConfig;
@@ -480,7 +481,7 @@ export default function reactNativeTailwindBabelPlugin(
480
481
  // Expand scheme: into dark: and light:
481
482
  const expanded = expandSchemeModifier(
482
483
  modifier,
483
- state.customColors,
484
+ state.customTheme.colors ?? {},
484
485
  state.schemeModifierConfig.darkSuffix,
485
486
  state.schemeModifierConfig.lightSuffix,
486
487
  );
@@ -507,7 +508,7 @@ export default function reactNativeTailwindBabelPlugin(
507
508
 
508
509
  if (componentSupport?.supportedModifiers.includes("placeholder")) {
509
510
  const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
510
- const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customColors);
511
+ const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
511
512
 
512
513
  if (placeholderColor) {
513
514
  // Add or merge placeholderTextColor prop
@@ -561,7 +562,7 @@ export default function reactNativeTailwindBabelPlugin(
561
562
  // Add base classes
562
563
  if (hasBaseClasses) {
563
564
  const baseClassName = baseClasses.join(" ");
564
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
565
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
565
566
  const baseStyleKey = generateStyleKey(baseClassName);
566
567
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
567
568
  styleArrayElements.push(
@@ -611,7 +612,7 @@ export default function reactNativeTailwindBabelPlugin(
611
612
  }
612
613
 
613
614
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
614
- const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
615
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
615
616
  const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
616
617
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
617
618
 
@@ -653,7 +654,7 @@ export default function reactNativeTailwindBabelPlugin(
653
654
  // Add base classes
654
655
  if (hasBaseClasses) {
655
656
  const baseClassName = baseClasses.join(" ");
656
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
657
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
657
658
  const baseStyleKey = generateStyleKey(baseClassName);
658
659
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
659
660
  styleExpressions.push(
@@ -815,7 +816,7 @@ export default function reactNativeTailwindBabelPlugin(
815
816
  return;
816
817
  }
817
818
 
818
- const styleObject = parseClassName(classNameForStyle, state.customColors);
819
+ const styleObject = parseClassName(classNameForStyle, state.customTheme);
819
820
  const styleKey = generateStyleKey(classNameForStyle);
820
821
  state.styleRegistry.set(styleKey, styleObject);
821
822
 
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type * as BabelTypes from "@babel/types";
6
- import type { ColorSchemeModifierType, ParsedModifier } from "../../parser/index.js";
6
+ import type { ColorSchemeModifierType, CustomTheme, ParsedModifier } from "../../parser/index.js";
7
7
  import type { StyleObject } from "../../types/core.js";
8
8
 
9
9
  /**
@@ -12,7 +12,7 @@ import type { StyleObject } from "../../types/core.js";
12
12
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
13
13
  export interface ColorSchemeModifierProcessingState {
14
14
  styleRegistry: Map<string, StyleObject>;
15
- customColors: Record<string, string>;
15
+ customTheme: CustomTheme;
16
16
  stylesIdentifier: string;
17
17
  needsColorSchemeImport: boolean;
18
18
  colorSchemeVariableName: string;
@@ -38,7 +38,7 @@ export interface ColorSchemeModifierProcessingState {
38
38
  export function processColorSchemeModifiers(
39
39
  colorSchemeModifiers: ParsedModifier[],
40
40
  state: ColorSchemeModifierProcessingState,
41
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
41
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
42
42
  generateStyleKey: (className: string) => string,
43
43
  t: typeof BabelTypes,
44
44
  ): BabelTypes.Expression[] {
@@ -65,7 +65,7 @@ export function processColorSchemeModifiers(
65
65
  for (const [scheme, modifiers] of modifiersByScheme) {
66
66
  // Parse all classes for this color scheme together
67
67
  const classNames = modifiers.map((m) => m.baseClass).join(" ");
68
- const styleObject = parseClassName(classNames, state.customColors);
68
+ const styleObject = parseClassName(classNames, state.customTheme);
69
69
  const styleKey = generateStyleKey(`${scheme}_${classNames}`);
70
70
 
71
71
  // Register style in the registry
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type { NodePath } from "@babel/core";
6
6
  import type * as BabelTypes from "@babel/types";
7
- import type { ParsedModifier } from "../../parser/index.js";
7
+ import type { CustomTheme, ParsedModifier } from "../../parser/index.js";
8
8
  import type { SchemeModifierConfig } from "../../types/config.js";
9
9
  import type { StyleObject } from "../../types/core.js";
10
10
 
@@ -14,7 +14,7 @@ import type { StyleObject } from "../../types/core.js";
14
14
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
15
15
  export interface DynamicProcessingState {
16
16
  styleRegistry: Map<string, StyleObject>;
17
- customColors: Record<string, string>;
17
+ customTheme: CustomTheme;
18
18
  schemeModifierConfig: SchemeModifierConfig;
19
19
  stylesIdentifier: string;
20
20
  needsPlatformImport: boolean;
@@ -37,7 +37,7 @@ export type SplitModifierClassesFn = (className: string) => {
37
37
  export type ProcessPlatformModifiersFn = (
38
38
  modifiers: ParsedModifier[],
39
39
  state: DynamicProcessingState,
40
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
40
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
41
41
  generateStyleKey: (className: string) => string,
42
42
  t: typeof BabelTypes,
43
43
  ) => BabelTypes.Expression;
@@ -48,7 +48,7 @@ export type ProcessPlatformModifiersFn = (
48
48
  export type ProcessColorSchemeModifiersFn = (
49
49
  modifiers: ParsedModifier[],
50
50
  state: DynamicProcessingState,
51
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
51
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
52
52
  generateStyleKey: (className: string) => string,
53
53
  t: typeof BabelTypes,
54
54
  ) => BabelTypes.Expression[];
@@ -85,7 +85,7 @@ export type DynamicExpressionResult = {
85
85
  export function processDynamicExpression(
86
86
  expression: BabelTypes.Expression,
87
87
  state: DynamicProcessingState,
88
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
88
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
89
89
  generateStyleKey: (className: string) => string,
90
90
  splitModifierClasses: SplitModifierClassesFn,
91
91
  processPlatformModifiers: ProcessPlatformModifiersFn,
@@ -164,7 +164,7 @@ export function processDynamicExpression(
164
164
  function processTemplateLiteral(
165
165
  node: BabelTypes.TemplateLiteral,
166
166
  state: DynamicProcessingState,
167
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
167
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
168
168
  generateStyleKey: (className: string) => string,
169
169
  splitModifierClasses: SplitModifierClassesFn,
170
170
  processPlatformModifiers: ProcessPlatformModifiersFn,
@@ -262,7 +262,7 @@ function processTemplateLiteral(
262
262
  function processConditionalExpression(
263
263
  node: BabelTypes.ConditionalExpression,
264
264
  state: DynamicProcessingState,
265
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
265
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
266
266
  generateStyleKey: (className: string) => string,
267
267
  splitModifierClasses: SplitModifierClassesFn,
268
268
  processPlatformModifiers: ProcessPlatformModifiersFn,
@@ -325,7 +325,7 @@ function processConditionalExpression(
325
325
  function processLogicalExpression(
326
326
  node: BabelTypes.LogicalExpression,
327
327
  state: DynamicProcessingState,
328
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
328
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
329
329
  generateStyleKey: (className: string) => string,
330
330
  splitModifierClasses: SplitModifierClassesFn,
331
331
  processPlatformModifiers: ProcessPlatformModifiersFn,
@@ -376,7 +376,7 @@ function processLogicalExpression(
376
376
  function processStringOrExpressionHelper(
377
377
  node: BabelTypes.StringLiteral | BabelTypes.Expression,
378
378
  state: DynamicProcessingState,
379
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
379
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
380
380
  generateStyleKey: (className: string) => string,
381
381
  splitModifierClasses: SplitModifierClassesFn,
382
382
  processPlatformModifiers: ProcessPlatformModifiersFn,
@@ -405,7 +405,7 @@ function processStringOrExpressionHelper(
405
405
  // Expand scheme: into dark: and light:
406
406
  const expanded = expandSchemeModifier(
407
407
  modifier,
408
- state.customColors,
408
+ state.customTheme.colors ?? {},
409
409
  state.schemeModifierConfig.darkSuffix ?? "-dark",
410
410
  state.schemeModifierConfig.lightSuffix ?? "-light",
411
411
  );
@@ -425,7 +425,7 @@ function processStringOrExpressionHelper(
425
425
  // Process base classes
426
426
  if (baseClasses.length > 0) {
427
427
  const baseClassName = baseClasses.join(" ");
428
- const styleObject = parseClassName(baseClassName, state.customColors);
428
+ const styleObject = parseClassName(baseClassName, state.customTheme);
429
429
  const styleKey = generateStyleKey(baseClassName);
430
430
  state.styleRegistry.set(styleKey, styleObject);
431
431
  styleElements.push(t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey)));
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type * as BabelTypes from "@babel/types";
6
- import type { ModifierType, ParsedModifier } from "../../parser/index.js";
6
+ import type { CustomTheme, ModifierType, ParsedModifier } from "../../parser/index.js";
7
7
  import type { StyleObject } from "../../types/core.js";
8
8
  import { getStatePropertyForModifier } from "./componentSupport.js";
9
9
 
@@ -13,7 +13,7 @@ import { getStatePropertyForModifier } from "./componentSupport.js";
13
13
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
14
14
  export interface ModifierProcessingState {
15
15
  styleRegistry: Map<string, StyleObject>;
16
- customColors: Record<string, string>;
16
+ customTheme: CustomTheme;
17
17
  stylesIdentifier: string;
18
18
  }
19
19
 
@@ -24,7 +24,7 @@ export interface ModifierProcessingState {
24
24
  export function processStaticClassNameWithModifiers(
25
25
  className: string,
26
26
  state: ModifierProcessingState,
27
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
27
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
28
28
  generateStyleKey: (className: string) => string,
29
29
  splitModifierClasses: (className: string) => { baseClasses: string[]; modifierClasses: ParsedModifier[] },
30
30
  t: typeof BabelTypes,
@@ -35,7 +35,7 @@ export function processStaticClassNameWithModifiers(
35
35
  let baseStyleExpression: BabelTypes.Node | null = null;
36
36
  if (baseClasses.length > 0) {
37
37
  const baseClassName = baseClasses.join(" ");
38
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
38
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
39
39
  const baseStyleKey = generateStyleKey(baseClassName);
40
40
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
41
41
  baseStyleExpression = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey));
@@ -66,7 +66,7 @@ export function processStaticClassNameWithModifiers(
66
66
  for (const [modifierType, modifiers] of modifiersByType) {
67
67
  // Parse all modifier classes together
68
68
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
69
- const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
69
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
70
70
  const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
71
71
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
72
72
 
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type * as BabelTypes from "@babel/types";
6
- import type { ParsedModifier, PlatformModifierType } from "../../parser/index.js";
6
+ import type { CustomTheme, ParsedModifier, PlatformModifierType } from "../../parser/index.js";
7
7
  import type { StyleObject } from "../../types/core.js";
8
8
 
9
9
  /**
@@ -12,7 +12,7 @@ import type { StyleObject } from "../../types/core.js";
12
12
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
13
13
  export interface PlatformModifierProcessingState {
14
14
  styleRegistry: Map<string, StyleObject>;
15
- customColors: Record<string, string>;
15
+ customTheme: CustomTheme;
16
16
  stylesIdentifier: string;
17
17
  needsPlatformImport: boolean;
18
18
  }
@@ -34,7 +34,7 @@ export interface PlatformModifierProcessingState {
34
34
  export function processPlatformModifiers(
35
35
  platformModifiers: ParsedModifier[],
36
36
  state: PlatformModifierProcessingState,
37
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
37
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
38
38
  generateStyleKey: (className: string) => string,
39
39
  t: typeof BabelTypes,
40
40
  ): BabelTypes.Expression {
@@ -61,7 +61,7 @@ export function processPlatformModifiers(
61
61
  for (const [platform, modifiers] of modifiersByPlatform) {
62
62
  // Parse all classes for this platform together
63
63
  const classNames = modifiers.map((m) => m.baseClass).join(" ");
64
- const styleObject = parseClassName(classNames, state.customColors);
64
+ const styleObject = parseClassName(classNames, state.customTheme);
65
65
  const styleKey = generateStyleKey(`${platform}_${classNames}`);
66
66
 
67
67
  // Register style in the registry
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type { NodePath } from "@babel/core";
6
6
  import type * as BabelTypes from "@babel/types";
7
- import type { ModifierType, ParsedModifier } from "../../parser/index.js";
7
+ import type { CustomTheme, ModifierType, ParsedModifier } from "../../parser/index.js";
8
8
  import { expandSchemeModifier, isSchemeModifier } from "../../parser/index.js";
9
9
  import type { SchemeModifierConfig } from "../../types/config.js";
10
10
  import type { StyleObject } from "../../types/core.js";
@@ -15,7 +15,7 @@ import type { StyleObject } from "../../types/core.js";
15
15
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
16
16
  export interface TwProcessingState {
17
17
  styleRegistry: Map<string, StyleObject>;
18
- customColors: Record<string, string>;
18
+ customTheme: CustomTheme;
19
19
  schemeModifierConfig: SchemeModifierConfig;
20
20
  stylesIdentifier: string;
21
21
  }
@@ -28,7 +28,7 @@ export function processTwCall(
28
28
  className: string,
29
29
  path: NodePath,
30
30
  state: TwProcessingState,
31
- parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
31
+ parseClassName: (className: string, customTheme?: CustomTheme) => StyleObject,
32
32
  generateStyleKey: (className: string) => string,
33
33
  splitModifierClasses: (className: string) => { baseClasses: string[]; modifierClasses: ParsedModifier[] },
34
34
  t: typeof BabelTypes,
@@ -42,7 +42,7 @@ export function processTwCall(
42
42
  // Expand scheme: into dark: and light:
43
43
  const expanded = expandSchemeModifier(
44
44
  modifier,
45
- state.customColors,
45
+ state.customTheme.colors ?? {},
46
46
  state.schemeModifierConfig.darkSuffix ?? "-dark",
47
47
  state.schemeModifierConfig.lightSuffix ?? "-light",
48
48
  );
@@ -59,7 +59,7 @@ export function processTwCall(
59
59
  // Parse and add base styles
60
60
  if (baseClasses.length > 0) {
61
61
  const baseClassName = baseClasses.join(" ");
62
- const baseStyleObject = parseClassName(baseClassName, state.customColors);
62
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
63
63
  const baseStyleKey = generateStyleKey(baseClassName);
64
64
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
65
65
 
@@ -89,7 +89,7 @@ export function processTwCall(
89
89
  // Add modifier styles
90
90
  for (const [modifierType, modifiers] of modifiersByType) {
91
91
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
92
- const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
92
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
93
93
  const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
94
94
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
95
95
 
@@ -14,18 +14,26 @@ import { parseSpacing } from "./spacing";
14
14
  import { parseTransform } from "./transforms";
15
15
  import { parseTypography } from "./typography";
16
16
 
17
+ /**
18
+ * Custom theme configuration (subset of tailwind.config theme extensions)
19
+ */
20
+ export type CustomTheme = {
21
+ colors?: Record<string, string>;
22
+ fontFamily?: Record<string, string>;
23
+ };
24
+
17
25
  /**
18
26
  * Parse a className string and return a React Native style object
19
27
  * @param className - Space-separated class names
20
- * @param customColors - Optional custom colors from tailwind.config
28
+ * @param customTheme - Optional custom theme from tailwind.config
21
29
  * @returns React Native style object
22
30
  */
23
- export function parseClassName(className: string, customColors?: Record<string, string>): StyleObject {
31
+ export function parseClassName(className: string, customTheme?: CustomTheme): StyleObject {
24
32
  const classes = className.split(/\s+/).filter(Boolean);
25
33
  const style: StyleObject = {};
26
34
 
27
35
  for (const cls of classes) {
28
- const parsedStyle = parseClass(cls, customColors);
36
+ const parsedStyle = parseClass(cls, customTheme);
29
37
  Object.assign(style, parsedStyle);
30
38
  }
31
39
 
@@ -35,19 +43,19 @@ export function parseClassName(className: string, customColors?: Record<string,
35
43
  /**
36
44
  * Parse a single class name
37
45
  * @param cls - Single class name
38
- * @param customColors - Optional custom colors from tailwind.config
46
+ * @param customTheme - Optional custom theme from tailwind.config
39
47
  * @returns React Native style object
40
48
  */
41
- export function parseClass(cls: string, customColors?: Record<string, string>): StyleObject {
49
+ export function parseClass(cls: string, customTheme?: CustomTheme): StyleObject {
42
50
  // Try each parser in order
43
51
  // Note: parseBorder must come before parseColor to avoid border-[3px] being parsed as a color
44
- // parseColor gets custom colors, others don't need it
52
+ // parseColor and parseTypography get custom theme, others don't need it
45
53
  const parsers: Array<(cls: string) => StyleObject | null> = [
46
54
  parseSpacing,
47
55
  parseBorder,
48
- (cls: string) => parseColor(cls, customColors),
56
+ (cls: string) => parseColor(cls, customTheme?.colors),
49
57
  parseLayout,
50
- parseTypography,
58
+ (cls: string) => parseTypography(cls, customTheme?.fontFamily),
51
59
  parseSizing,
52
60
  parseShadow,
53
61
  parseAspectRatio,
@@ -164,8 +164,20 @@ function parseArbitraryLineHeight(value: string): number | null {
164
164
 
165
165
  /**
166
166
  * Parse typography classes
167
+ * @param cls - Class name to parse
168
+ * @param customFontFamily - Optional custom fontFamily from tailwind.config
167
169
  */
168
- export function parseTypography(cls: string): StyleObject | null {
170
+ export function parseTypography(cls: string, customFontFamily?: Record<string, string>): StyleObject | null {
171
+ // Merge custom fontFamily with defaults (custom takes precedence)
172
+ const fontFamilyMap = customFontFamily
173
+ ? {
174
+ ...FONT_FAMILY_MAP,
175
+ ...Object.fromEntries(
176
+ Object.entries(customFontFamily).map(([key, value]) => [`font-${key}`, { fontFamily: value }]),
177
+ ),
178
+ }
179
+ : FONT_FAMILY_MAP;
180
+
169
181
  // Font size: text-base, text-lg, text-[18px], etc.
170
182
  if (cls.startsWith("text-")) {
171
183
  const sizeKey = cls.substring(5);
@@ -202,7 +214,7 @@ export function parseTypography(cls: string): StyleObject | null {
202
214
 
203
215
  // Try each lookup table in order
204
216
  return (
205
- FONT_FAMILY_MAP[cls] ??
217
+ fontFamilyMap[cls] ??
206
218
  FONT_WEIGHT_MAP[cls] ??
207
219
  FONT_STYLE_MAP[cls] ??
208
220
  TEXT_ALIGN_MAP[cls] ??