@mgcrea/react-native-tailwind 0.6.1 → 0.7.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 +404 -0
  2. package/dist/babel/config-loader.ts +1 -23
  3. package/dist/babel/index.cjs +227 -60
  4. package/dist/babel/index.d.ts +27 -2
  5. package/dist/babel/index.test.ts +268 -0
  6. package/dist/babel/index.ts +352 -44
  7. package/dist/index.d.ts +3 -0
  8. package/dist/index.js +1 -1
  9. package/dist/parser/__snapshots__/colors.test.js.snap +242 -90
  10. package/dist/parser/__snapshots__/transforms.test.js.snap +58 -0
  11. package/dist/parser/colors.js +1 -1
  12. package/dist/runtime.cjs +2 -0
  13. package/dist/runtime.cjs.map +7 -0
  14. package/dist/runtime.d.ts +139 -0
  15. package/dist/runtime.js +2 -0
  16. package/dist/runtime.js.map +7 -0
  17. package/dist/runtime.test.js +1 -0
  18. package/dist/stubs/tw.d.ts +60 -0
  19. package/dist/stubs/tw.js +1 -0
  20. package/dist/utils/flattenColors.d.ts +16 -0
  21. package/dist/utils/flattenColors.js +1 -0
  22. package/dist/utils/flattenColors.test.js +1 -0
  23. package/dist/utils/modifiers.d.ts +29 -0
  24. package/dist/utils/modifiers.js +1 -0
  25. package/dist/utils/modifiers.test.js +1 -0
  26. package/dist/utils/styleKey.test.js +1 -0
  27. package/package.json +15 -3
  28. package/src/babel/config-loader.ts +1 -23
  29. package/src/babel/index.test.ts +268 -0
  30. package/src/babel/index.ts +352 -44
  31. package/src/index.ts +5 -0
  32. package/src/parser/colors.ts +8 -22
  33. package/src/runtime.test.ts +325 -0
  34. package/src/runtime.ts +280 -0
  35. package/src/stubs/tw.ts +80 -0
  36. package/src/utils/flattenColors.test.ts +361 -0
  37. package/src/utils/flattenColors.ts +32 -0
  38. package/src/utils/modifiers.test.ts +286 -0
  39. package/src/utils/modifiers.ts +63 -0
  40. package/src/utils/styleKey.test.ts +168 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Shared utilities for parsing state modifiers (active:, focus:, disabled:)
3
+ * Used by both runtime parser and Babel plugin
4
+ */
5
+
6
+ // Supported state modifiers for Pressable/TextInput components
7
+ export const SUPPORTED_MODIFIERS = ["active", "focus", "disabled"] as const;
8
+ export type SupportedModifier = (typeof SUPPORTED_MODIFIERS)[number];
9
+
10
+ /**
11
+ * Detect if a className contains any state modifiers (active:, focus:, disabled:)
12
+ */
13
+ export function hasModifiers(className: string): boolean {
14
+ return SUPPORTED_MODIFIERS.some((modifier) => className.includes(`${modifier}:`));
15
+ }
16
+
17
+ /**
18
+ * Split className into base classes and modifier-specific classes
19
+ * Returns: { base: string[], modifiers: Map<modifier, string[]> }
20
+ *
21
+ * @example
22
+ * splitModifierClasses('bg-blue-500 active:bg-blue-700 disabled:bg-gray-300')
23
+ * // Returns:
24
+ * // {
25
+ * // base: ['bg-blue-500'],
26
+ * // modifiers: Map {
27
+ * // 'active' => ['bg-blue-700'],
28
+ * // 'disabled' => ['bg-gray-300']
29
+ * // }
30
+ * // }
31
+ */
32
+ export function splitModifierClasses(className: string): {
33
+ base: string[];
34
+ modifiers: Map<SupportedModifier, string[]>;
35
+ } {
36
+ const classes = className.split(/\s+/).filter(Boolean);
37
+ const base: string[] = [];
38
+ const modifiers = new Map<SupportedModifier, string[]>();
39
+
40
+ for (const cls of classes) {
41
+ let matched = false;
42
+ for (const modifier of SUPPORTED_MODIFIERS) {
43
+ const prefix = `${modifier}:`;
44
+ if (cls.startsWith(prefix)) {
45
+ const cleanClass = cls.slice(prefix.length);
46
+ if (!modifiers.has(modifier)) {
47
+ modifiers.set(modifier, []);
48
+ }
49
+ const modifierClasses = modifiers.get(modifier);
50
+ if (modifierClasses) {
51
+ modifierClasses.push(cleanClass);
52
+ }
53
+ matched = true;
54
+ break;
55
+ }
56
+ }
57
+ if (!matched) {
58
+ base.push(cls);
59
+ }
60
+ }
61
+
62
+ return { base, modifiers };
63
+ }
@@ -0,0 +1,168 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { generateStyleKey } from "./styleKey";
3
+
4
+ describe("generateStyleKey", () => {
5
+ it("should generate key with leading underscore", () => {
6
+ expect(generateStyleKey("m-4")).toBe("_m_4");
7
+ expect(generateStyleKey("p-2")).toBe("_p_2");
8
+ expect(generateStyleKey("bg-blue-500")).toBe("_bg_blue_500");
9
+ });
10
+
11
+ it("should sort classes alphabetically for consistency", () => {
12
+ expect(generateStyleKey("m-4 p-2")).toBe(generateStyleKey("p-2 m-4"));
13
+ expect(generateStyleKey("bg-blue-500 text-white")).toBe(generateStyleKey("text-white bg-blue-500"));
14
+ expect(generateStyleKey("flex items-center justify-center")).toBe(
15
+ generateStyleKey("justify-center flex items-center"),
16
+ );
17
+ });
18
+
19
+ it("should handle single class", () => {
20
+ expect(generateStyleKey("flex")).toBe("_flex");
21
+ expect(generateStyleKey("hidden")).toBe("_hidden");
22
+ expect(generateStyleKey("absolute")).toBe("_absolute");
23
+ });
24
+
25
+ it("should handle multiple classes", () => {
26
+ expect(generateStyleKey("m-4 p-2 bg-blue-500")).toBe("_bg_blue_500_m_4_p_2");
27
+ expect(generateStyleKey("flex items-center justify-center")).toBe("_flex_items_center_justify_center");
28
+ });
29
+
30
+ it("should replace special characters with underscores", () => {
31
+ expect(generateStyleKey("bg-[#ff0000]")).toBe("_bg_ff0000_");
32
+ expect(generateStyleKey("text-[14px]")).toBe("_text_14px_");
33
+ expect(generateStyleKey("m-[16px]")).toBe("_m_16px_");
34
+ });
35
+
36
+ it("should collapse multiple underscores", () => {
37
+ expect(generateStyleKey("bg-[#ff0000]")).toBe("_bg_ff0000_");
38
+ expect(generateStyleKey("w-[100%]")).toBe("_w_100_");
39
+ });
40
+
41
+ it("should handle classes with hyphens", () => {
42
+ expect(generateStyleKey("bg-blue-500")).toBe("_bg_blue_500");
43
+ expect(generateStyleKey("text-gray-700")).toBe("_text_gray_700");
44
+ expect(generateStyleKey("border-red-300")).toBe("_border_red_300");
45
+ });
46
+
47
+ it("should handle classes with numbers", () => {
48
+ expect(generateStyleKey("m-4")).toBe("_m_4");
49
+ expect(generateStyleKey("p-8")).toBe("_p_8");
50
+ expect(generateStyleKey("gap-96")).toBe("_gap_96");
51
+ expect(generateStyleKey("text-2xl")).toBe("_text_2xl");
52
+ });
53
+
54
+ it("should handle classes with decimals", () => {
55
+ expect(generateStyleKey("m-0.5")).toBe("_m_0_5");
56
+ expect(generateStyleKey("p-1.5")).toBe("_p_1_5");
57
+ expect(generateStyleKey("gap-2.5")).toBe("_gap_2_5");
58
+ });
59
+
60
+ it("should handle empty string", () => {
61
+ expect(generateStyleKey("")).toBe("_");
62
+ });
63
+
64
+ it("should handle whitespace-only string", () => {
65
+ expect(generateStyleKey(" ")).toBe("_");
66
+ expect(generateStyleKey("\t\n")).toBe("_");
67
+ });
68
+
69
+ it("should handle multiple consecutive spaces", () => {
70
+ expect(generateStyleKey("m-4 p-2 bg-blue-500")).toBe("_bg_blue_500_m_4_p_2");
71
+ expect(generateStyleKey("flex items-center")).toBe("_flex_items_center");
72
+ });
73
+
74
+ it("should handle leading and trailing spaces", () => {
75
+ expect(generateStyleKey(" m-4 p-2 ")).toBe("_m_4_p_2");
76
+ expect(generateStyleKey("\tflex items-center\n")).toBe("_flex_items_center");
77
+ });
78
+
79
+ it("should handle opacity modifiers", () => {
80
+ expect(generateStyleKey("bg-black/50")).toBe("_bg_black_50");
81
+ expect(generateStyleKey("text-white/80")).toBe("_text_white_80");
82
+ expect(generateStyleKey("border-blue-500/30")).toBe("_border_blue_500_30");
83
+ });
84
+
85
+ it("should handle state modifiers", () => {
86
+ expect(generateStyleKey("active:bg-blue-700")).toBe("_active_bg_blue_700");
87
+ expect(generateStyleKey("focus:border-blue-500")).toBe("_focus_border_blue_500");
88
+ expect(generateStyleKey("disabled:opacity-50")).toBe("_disabled_opacity_50");
89
+ });
90
+
91
+ it("should handle complex combinations", () => {
92
+ expect(generateStyleKey("m-4 p-2 bg-blue-500 text-white rounded-lg")).toBe(
93
+ "_bg_blue_500_m_4_p_2_rounded_lg_text_white",
94
+ );
95
+ expect(generateStyleKey("flex items-center justify-between p-4 bg-gray-100 border-b-2")).toBe(
96
+ "_bg_gray_100_border_b_2_flex_items_center_justify_between_p_4",
97
+ );
98
+ });
99
+
100
+ it("should handle arbitrary values in complex scenarios", () => {
101
+ expect(generateStyleKey("m-[16px] p-[8px] bg-[#ff0000]")).toBe("_bg_ff0000_m_16px_p_8px_");
102
+ expect(generateStyleKey("w-[100%] h-[50px] text-[14px]")).toBe("_h_50px_text_14px_w_100_");
103
+ });
104
+
105
+ it("should produce consistent keys regardless of class order", () => {
106
+ const combinations = [
107
+ "m-4 p-2 bg-blue-500",
108
+ "p-2 m-4 bg-blue-500",
109
+ "bg-blue-500 m-4 p-2",
110
+ "bg-blue-500 p-2 m-4",
111
+ "m-4 bg-blue-500 p-2",
112
+ "p-2 bg-blue-500 m-4",
113
+ ];
114
+
115
+ const keys = combinations.map(generateStyleKey);
116
+ const firstKey = keys[0];
117
+ keys.forEach((key) => {
118
+ expect(key).toBe(firstKey);
119
+ });
120
+ });
121
+
122
+ it("should handle duplicate classes", () => {
123
+ // Note: duplicates will appear in output since we don't dedupe
124
+ expect(generateStyleKey("m-4 m-4")).toBe("_m_4_m_4");
125
+ expect(generateStyleKey("flex flex flex")).toBe("_flex_flex_flex");
126
+ });
127
+
128
+ it("should handle transform classes", () => {
129
+ expect(generateStyleKey("scale-110")).toBe("_scale_110");
130
+ expect(generateStyleKey("rotate-45")).toBe("_rotate_45");
131
+ expect(generateStyleKey("translate-x-4")).toBe("_translate_x_4");
132
+ expect(generateStyleKey("-rotate-90")).toBe("__rotate_90");
133
+ });
134
+
135
+ it("should handle negative values", () => {
136
+ expect(generateStyleKey("-m-4")).toBe("__m_4");
137
+ expect(generateStyleKey("-translate-x-8")).toBe("__translate_x_8");
138
+ expect(generateStyleKey("-rotate-180")).toBe("__rotate_180");
139
+ });
140
+
141
+ it("should be deterministic", () => {
142
+ const className = "m-4 p-2 bg-blue-500 text-white rounded-lg flex items-center";
143
+ const key1 = generateStyleKey(className);
144
+ const key2 = generateStyleKey(className);
145
+ const key3 = generateStyleKey(className);
146
+
147
+ expect(key1).toBe(key2);
148
+ expect(key2).toBe(key3);
149
+ });
150
+
151
+ it("should create valid JavaScript identifiers", () => {
152
+ // Valid identifiers start with _, $, or letter, and contain _, $, letters, or digits
153
+ const keys = [
154
+ generateStyleKey("m-4"),
155
+ generateStyleKey("bg-blue-500"),
156
+ generateStyleKey("text-[14px]"),
157
+ generateStyleKey("flex items-center"),
158
+ generateStyleKey("active:bg-red-500"),
159
+ ];
160
+
161
+ keys.forEach((key) => {
162
+ // Should start with underscore
163
+ expect(key).toMatch(/^_/);
164
+ // Should only contain alphanumeric and underscores
165
+ expect(key).toMatch(/^[_a-zA-Z0-9]+$/);
166
+ });
167
+ });
168
+ });