@mgcrea/react-native-tailwind 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/babel/index.cjs +1 -1
- package/dist/babel/utils/attributeMatchers.test.ts +294 -0
- package/dist/babel/utils/componentSupport.test.ts +426 -0
- package/dist/parser/colors.test.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +2 -2
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +2 -2
- package/dist/utils/flattenColors.d.ts +1 -0
- package/dist/utils/flattenColors.js +1 -1
- package/dist/utils/flattenColors.test.js +1 -1
- package/package.json +1 -1
- package/src/babel/utils/attributeMatchers.test.ts +294 -0
- package/src/babel/utils/componentSupport.test.ts +426 -0
- package/src/parser/colors.test.ts +32 -0
- package/src/parser/index.ts +1 -1
- package/src/utils/flattenColors.test.ts +100 -0
- package/src/utils/flattenColors.ts +3 -1
package/dist/babel/index.cjs
CHANGED
|
@@ -552,7 +552,7 @@ var TAILWIND_COLORS = {
|
|
|
552
552
|
function flattenColors(colors, prefix = "") {
|
|
553
553
|
const result = {};
|
|
554
554
|
for (const [key, value] of Object.entries(colors)) {
|
|
555
|
-
const newKey = prefix ? `${prefix}-${key}` : key;
|
|
555
|
+
const newKey = key === "DEFAULT" && prefix ? prefix : prefix ? `${prefix}-${key}` : key;
|
|
556
556
|
if (typeof value === "string") {
|
|
557
557
|
result[newKey] = value;
|
|
558
558
|
} else if (typeof value === "object" && value !== null) {
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CLASS_ATTRIBUTES,
|
|
4
|
+
buildAttributeMatchers,
|
|
5
|
+
getTargetStyleProp,
|
|
6
|
+
isAttributeSupported,
|
|
7
|
+
} from "./attributeMatchers";
|
|
8
|
+
|
|
9
|
+
describe("DEFAULT_CLASS_ATTRIBUTES", () => {
|
|
10
|
+
it("should contain standard className attributes", () => {
|
|
11
|
+
expect(DEFAULT_CLASS_ATTRIBUTES).toEqual([
|
|
12
|
+
"className",
|
|
13
|
+
"contentContainerClassName",
|
|
14
|
+
"columnWrapperClassName",
|
|
15
|
+
"ListHeaderComponentClassName",
|
|
16
|
+
"ListFooterComponentClassName",
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should be a readonly array", () => {
|
|
21
|
+
// TypeScript compile-time check - if this compiles, the const assertion works
|
|
22
|
+
const _typeCheck: readonly string[] = DEFAULT_CLASS_ATTRIBUTES;
|
|
23
|
+
expect(_typeCheck).toBeDefined();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe("buildAttributeMatchers", () => {
|
|
28
|
+
it("should separate exact matches from patterns", () => {
|
|
29
|
+
const attributes = ["className", "containerClassName", "*Style", "custom*"];
|
|
30
|
+
const result = buildAttributeMatchers(attributes);
|
|
31
|
+
|
|
32
|
+
// Exact matches should be in a Set
|
|
33
|
+
expect(result.exactMatches).toBeInstanceOf(Set);
|
|
34
|
+
expect(result.exactMatches.has("className")).toBe(true);
|
|
35
|
+
expect(result.exactMatches.has("containerClassName")).toBe(true);
|
|
36
|
+
expect(result.exactMatches.size).toBe(2);
|
|
37
|
+
|
|
38
|
+
// Patterns should be RegExp objects
|
|
39
|
+
expect(result.patterns).toHaveLength(2);
|
|
40
|
+
expect(result.patterns[0]).toBeInstanceOf(RegExp);
|
|
41
|
+
expect(result.patterns[1]).toBeInstanceOf(RegExp);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should handle only exact matches", () => {
|
|
45
|
+
const attributes = ["className", "customClass", "anotherClass"];
|
|
46
|
+
const result = buildAttributeMatchers(attributes);
|
|
47
|
+
|
|
48
|
+
expect(result.exactMatches.size).toBe(3);
|
|
49
|
+
expect(result.patterns).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should handle only patterns", () => {
|
|
53
|
+
const attributes = ["*ClassName", "container*", "*Style*"];
|
|
54
|
+
const result = buildAttributeMatchers(attributes);
|
|
55
|
+
|
|
56
|
+
expect(result.exactMatches.size).toBe(0);
|
|
57
|
+
expect(result.patterns).toHaveLength(3);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should handle empty array", () => {
|
|
61
|
+
const result = buildAttributeMatchers([]);
|
|
62
|
+
|
|
63
|
+
expect(result.exactMatches.size).toBe(0);
|
|
64
|
+
expect(result.patterns).toHaveLength(0);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should convert glob patterns to regex correctly", () => {
|
|
68
|
+
const attributes = ["*ClassName", "container*", "*custom*"];
|
|
69
|
+
const result = buildAttributeMatchers(attributes);
|
|
70
|
+
|
|
71
|
+
// Test that patterns match expected strings
|
|
72
|
+
expect(result.patterns[0]?.test("myClassName")).toBe(true);
|
|
73
|
+
expect(result.patterns[0]?.test("fooClassName")).toBe(true);
|
|
74
|
+
expect(result.patterns[0]?.test("className")).toBe(false); // Doesn't start with anything
|
|
75
|
+
|
|
76
|
+
expect(result.patterns[1]?.test("containerStyle")).toBe(true);
|
|
77
|
+
expect(result.patterns[1]?.test("containerFoo")).toBe(true);
|
|
78
|
+
expect(result.patterns[1]?.test("myContainer")).toBe(false);
|
|
79
|
+
|
|
80
|
+
expect(result.patterns[2]?.test("mycustomattr")).toBe(true);
|
|
81
|
+
expect(result.patterns[2]?.test("customattr")).toBe(true);
|
|
82
|
+
expect(result.patterns[2]?.test("attrcustom")).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("should handle multiple wildcards in same pattern", () => {
|
|
86
|
+
const attributes = ["*custom*Class*"];
|
|
87
|
+
const result = buildAttributeMatchers(attributes);
|
|
88
|
+
|
|
89
|
+
expect(result.patterns[0]?.test("mycustomFooClassName")).toBe(true);
|
|
90
|
+
expect(result.patterns[0]?.test("customClassName")).toBe(true);
|
|
91
|
+
expect(result.patterns[0]?.test("foocustombarClassbaz")).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("isAttributeSupported", () => {
|
|
96
|
+
it("should match exact attributes", () => {
|
|
97
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["className", "customClass"]);
|
|
98
|
+
|
|
99
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
100
|
+
expect(isAttributeSupported("customClass", exactMatches, patterns)).toBe(true);
|
|
101
|
+
expect(isAttributeSupported("otherClass", exactMatches, patterns)).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should match pattern-based attributes", () => {
|
|
105
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["*ClassName", "container*"]);
|
|
106
|
+
|
|
107
|
+
expect(isAttributeSupported("myClassName", exactMatches, patterns)).toBe(true);
|
|
108
|
+
expect(isAttributeSupported("fooClassName", exactMatches, patterns)).toBe(true);
|
|
109
|
+
expect(isAttributeSupported("containerStyle", exactMatches, patterns)).toBe(true);
|
|
110
|
+
expect(isAttributeSupported("containerFoo", exactMatches, patterns)).toBe(true);
|
|
111
|
+
expect(isAttributeSupported("randomAttr", exactMatches, patterns)).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("should match both exact and pattern attributes", () => {
|
|
115
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["className", "*Style"]);
|
|
116
|
+
|
|
117
|
+
// Exact match
|
|
118
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
119
|
+
|
|
120
|
+
// Pattern match
|
|
121
|
+
expect(isAttributeSupported("containerStyle", exactMatches, patterns)).toBe(true);
|
|
122
|
+
expect(isAttributeSupported("customStyle", exactMatches, patterns)).toBe(true);
|
|
123
|
+
|
|
124
|
+
// No match
|
|
125
|
+
expect(isAttributeSupported("otherAttr", exactMatches, patterns)).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should prioritize exact matches (performance)", () => {
|
|
129
|
+
// Even if a pattern would match, exact match should work
|
|
130
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["className", "*Name"]);
|
|
131
|
+
|
|
132
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should handle empty matchers", () => {
|
|
136
|
+
const { exactMatches, patterns } = buildAttributeMatchers([]);
|
|
137
|
+
|
|
138
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(false);
|
|
139
|
+
expect(isAttributeSupported("anyAttr", exactMatches, patterns)).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should be case-sensitive", () => {
|
|
143
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["className"]);
|
|
144
|
+
|
|
145
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
146
|
+
expect(isAttributeSupported("ClassName", exactMatches, patterns)).toBe(false);
|
|
147
|
+
expect(isAttributeSupported("classname", exactMatches, patterns)).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should match default React Native FlatList attributes", () => {
|
|
151
|
+
const { exactMatches, patterns } = buildAttributeMatchers([...DEFAULT_CLASS_ATTRIBUTES]);
|
|
152
|
+
|
|
153
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
154
|
+
expect(isAttributeSupported("contentContainerClassName", exactMatches, patterns)).toBe(true);
|
|
155
|
+
expect(isAttributeSupported("columnWrapperClassName", exactMatches, patterns)).toBe(true);
|
|
156
|
+
expect(isAttributeSupported("ListHeaderComponentClassName", exactMatches, patterns)).toBe(true);
|
|
157
|
+
expect(isAttributeSupported("ListFooterComponentClassName", exactMatches, patterns)).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should work with complex glob patterns", () => {
|
|
161
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["*Container*Class*"]);
|
|
162
|
+
|
|
163
|
+
expect(isAttributeSupported("myContainerFooClassName", exactMatches, patterns)).toBe(true);
|
|
164
|
+
expect(isAttributeSupported("ContainerClassName", exactMatches, patterns)).toBe(true);
|
|
165
|
+
expect(isAttributeSupported("fooContainerBarClassBaz", exactMatches, patterns)).toBe(true);
|
|
166
|
+
expect(isAttributeSupported("ContainerStyle", exactMatches, patterns)).toBe(false);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("getTargetStyleProp", () => {
|
|
171
|
+
it("should convert className to style", () => {
|
|
172
|
+
expect(getTargetStyleProp("className")).toBe("style");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should convert *ClassName to *Style", () => {
|
|
176
|
+
expect(getTargetStyleProp("contentContainerClassName")).toBe("contentContainerStyle");
|
|
177
|
+
expect(getTargetStyleProp("columnWrapperClassName")).toBe("columnWrapperStyle");
|
|
178
|
+
expect(getTargetStyleProp("ListHeaderComponentClassName")).toBe("ListHeaderComponentStyle");
|
|
179
|
+
expect(getTargetStyleProp("ListFooterComponentClassName")).toBe("ListFooterComponentStyle");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should handle custom className attributes", () => {
|
|
183
|
+
expect(getTargetStyleProp("customClassName")).toBe("customStyle");
|
|
184
|
+
expect(getTargetStyleProp("myCustomClassName")).toBe("myCustomStyle");
|
|
185
|
+
expect(getTargetStyleProp("fooBarClassName")).toBe("fooBarStyle");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should return style for attributes not ending in ClassName", () => {
|
|
189
|
+
expect(getTargetStyleProp("customClass")).toBe("style");
|
|
190
|
+
expect(getTargetStyleProp("class")).toBe("style");
|
|
191
|
+
expect(getTargetStyleProp("myAttr")).toBe("style");
|
|
192
|
+
expect(getTargetStyleProp("")).toBe("style");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should handle edge cases", () => {
|
|
196
|
+
// Attribute IS exactly "ClassName"
|
|
197
|
+
expect(getTargetStyleProp("ClassName")).toBe("Style");
|
|
198
|
+
|
|
199
|
+
// Multiple "ClassName" occurrences (only last one replaced)
|
|
200
|
+
expect(getTargetStyleProp("classNameClassName")).toBe("classNameStyle");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it("should be case-sensitive", () => {
|
|
204
|
+
expect(getTargetStyleProp("classname")).toBe("style");
|
|
205
|
+
expect(getTargetStyleProp("CLASSNAME")).toBe("style");
|
|
206
|
+
expect(getTargetStyleProp("classNamee")).toBe("style");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should handle all default attributes correctly", () => {
|
|
210
|
+
const expectedMappings = [
|
|
211
|
+
["className", "style"],
|
|
212
|
+
["contentContainerClassName", "contentContainerStyle"],
|
|
213
|
+
["columnWrapperClassName", "columnWrapperStyle"],
|
|
214
|
+
["ListHeaderComponentClassName", "ListHeaderComponentStyle"],
|
|
215
|
+
["ListFooterComponentClassName", "ListFooterComponentStyle"],
|
|
216
|
+
] as const;
|
|
217
|
+
|
|
218
|
+
for (const [input, expected] of expectedMappings) {
|
|
219
|
+
expect(getTargetStyleProp(input)).toBe(expected);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe("Integration - Real-world scenarios", () => {
|
|
225
|
+
it("should handle FlatList with multiple className attributes", () => {
|
|
226
|
+
const { exactMatches, patterns } = buildAttributeMatchers([...DEFAULT_CLASS_ATTRIBUTES]);
|
|
227
|
+
|
|
228
|
+
// All FlatList className props should be supported
|
|
229
|
+
const flatListAttrs = [
|
|
230
|
+
["className", "style"],
|
|
231
|
+
["contentContainerClassName", "contentContainerStyle"],
|
|
232
|
+
["columnWrapperClassName", "columnWrapperStyle"],
|
|
233
|
+
["ListHeaderComponentClassName", "ListHeaderComponentStyle"],
|
|
234
|
+
["ListFooterComponentClassName", "ListFooterComponentStyle"],
|
|
235
|
+
] as const;
|
|
236
|
+
|
|
237
|
+
for (const [attr, expectedStyle] of flatListAttrs) {
|
|
238
|
+
expect(isAttributeSupported(attr, exactMatches, patterns)).toBe(true);
|
|
239
|
+
expect(getTargetStyleProp(attr)).toBe(expectedStyle);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("should support custom wildcard pattern for all *ClassName attributes", () => {
|
|
244
|
+
const { exactMatches, patterns } = buildAttributeMatchers(["*ClassName"]);
|
|
245
|
+
|
|
246
|
+
// Should match any attribute ending in ClassName (with at least one char before)
|
|
247
|
+
// Note: The regex ^.*ClassName$ requires at least one character due to .*
|
|
248
|
+
expect(isAttributeSupported("myCustomClassName", exactMatches, patterns)).toBe(true);
|
|
249
|
+
expect(isAttributeSupported("fooBarBazClassName", exactMatches, patterns)).toBe(true);
|
|
250
|
+
expect(isAttributeSupported("xClassName", exactMatches, patterns)).toBe(true);
|
|
251
|
+
|
|
252
|
+
// Edge case: bare "className" needs at least one char before it for .* to match
|
|
253
|
+
// If you want to include "className", add it explicitly or use a different pattern
|
|
254
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(false);
|
|
255
|
+
|
|
256
|
+
// Should convert to corresponding style prop
|
|
257
|
+
expect(getTargetStyleProp("myCustomClassName")).toBe("myCustomStyle");
|
|
258
|
+
|
|
259
|
+
// Should not match non-className attributes
|
|
260
|
+
expect(isAttributeSupported("myCustomClass", exactMatches, patterns)).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should support combining exact matches with patterns", () => {
|
|
264
|
+
const { exactMatches, patterns } = buildAttributeMatchers([
|
|
265
|
+
"className",
|
|
266
|
+
"customClass",
|
|
267
|
+
"*ClassName",
|
|
268
|
+
"container*",
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
// Exact matches
|
|
272
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(true);
|
|
273
|
+
expect(isAttributeSupported("customClass", exactMatches, patterns)).toBe(true);
|
|
274
|
+
|
|
275
|
+
// Pattern matches
|
|
276
|
+
expect(isAttributeSupported("myClassName", exactMatches, patterns)).toBe(true);
|
|
277
|
+
expect(isAttributeSupported("containerStyle", exactMatches, patterns)).toBe(true);
|
|
278
|
+
|
|
279
|
+
// No matches
|
|
280
|
+
expect(isAttributeSupported("randomAttr", exactMatches, patterns)).toBe(false);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it("should handle empty configuration gracefully", () => {
|
|
284
|
+
const { exactMatches, patterns } = buildAttributeMatchers([]);
|
|
285
|
+
|
|
286
|
+
// Nothing should be supported
|
|
287
|
+
expect(isAttributeSupported("className", exactMatches, patterns)).toBe(false);
|
|
288
|
+
expect(isAttributeSupported("anything", exactMatches, patterns)).toBe(false);
|
|
289
|
+
|
|
290
|
+
// getTargetStyleProp should still work
|
|
291
|
+
expect(getTargetStyleProp("className")).toBe("style");
|
|
292
|
+
expect(getTargetStyleProp("customClassName")).toBe("customStyle");
|
|
293
|
+
});
|
|
294
|
+
});
|