@mgcrea/react-native-tailwind 0.3.0 → 0.5.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 (71) hide show
  1. package/README.md +459 -39
  2. package/dist/babel/index.cjs +810 -279
  3. package/dist/babel/index.d.ts +2 -1
  4. package/dist/babel/index.ts +328 -22
  5. package/dist/components/Pressable.d.ts +32 -0
  6. package/dist/components/Pressable.js +1 -0
  7. package/dist/components/TextInput.d.ts +56 -0
  8. package/dist/components/TextInput.js +1 -0
  9. package/dist/index.d.ts +9 -2
  10. package/dist/index.js +1 -1
  11. package/dist/parser/aspectRatio.d.ts +16 -0
  12. package/dist/parser/aspectRatio.js +1 -0
  13. package/dist/parser/aspectRatio.test.d.ts +1 -0
  14. package/dist/parser/aspectRatio.test.js +1 -0
  15. package/dist/parser/borders.js +1 -1
  16. package/dist/parser/borders.test.d.ts +1 -0
  17. package/dist/parser/borders.test.js +1 -0
  18. package/dist/parser/colors.d.ts +1 -0
  19. package/dist/parser/colors.js +1 -1
  20. package/dist/parser/colors.test.d.ts +1 -0
  21. package/dist/parser/colors.test.js +1 -0
  22. package/dist/parser/index.d.ts +4 -0
  23. package/dist/parser/index.js +1 -1
  24. package/dist/parser/layout.d.ts +2 -0
  25. package/dist/parser/layout.js +1 -1
  26. package/dist/parser/layout.test.d.ts +1 -0
  27. package/dist/parser/layout.test.js +1 -0
  28. package/dist/parser/modifiers.d.ts +47 -0
  29. package/dist/parser/modifiers.js +1 -0
  30. package/dist/parser/modifiers.test.d.ts +1 -0
  31. package/dist/parser/modifiers.test.js +1 -0
  32. package/dist/parser/shadows.d.ts +26 -0
  33. package/dist/parser/shadows.js +1 -0
  34. package/dist/parser/shadows.test.d.ts +1 -0
  35. package/dist/parser/shadows.test.js +1 -0
  36. package/dist/parser/sizing.test.d.ts +1 -0
  37. package/dist/parser/sizing.test.js +1 -0
  38. package/dist/parser/spacing.d.ts +1 -1
  39. package/dist/parser/spacing.js +1 -1
  40. package/dist/parser/spacing.test.d.ts +1 -0
  41. package/dist/parser/spacing.test.js +1 -0
  42. package/dist/parser/typography.d.ts +2 -1
  43. package/dist/parser/typography.js +1 -1
  44. package/dist/parser/typography.test.d.ts +1 -0
  45. package/dist/parser/typography.test.js +1 -0
  46. package/dist/types.d.ts +5 -2
  47. package/package.json +7 -6
  48. package/src/babel/index.ts +328 -22
  49. package/src/components/Pressable.tsx +46 -0
  50. package/src/components/TextInput.tsx +90 -0
  51. package/src/index.ts +20 -2
  52. package/src/parser/aspectRatio.test.ts +191 -0
  53. package/src/parser/aspectRatio.ts +73 -0
  54. package/src/parser/borders.test.ts +329 -0
  55. package/src/parser/borders.ts +187 -108
  56. package/src/parser/colors.test.ts +335 -0
  57. package/src/parser/colors.ts +117 -6
  58. package/src/parser/index.ts +13 -2
  59. package/src/parser/layout.test.ts +459 -0
  60. package/src/parser/layout.ts +128 -0
  61. package/src/parser/modifiers.test.ts +375 -0
  62. package/src/parser/modifiers.ts +104 -0
  63. package/src/parser/shadows.test.ts +201 -0
  64. package/src/parser/shadows.ts +133 -0
  65. package/src/parser/sizing.test.ts +256 -0
  66. package/src/parser/spacing.test.ts +226 -0
  67. package/src/parser/spacing.ts +93 -138
  68. package/src/parser/typography.test.ts +221 -0
  69. package/src/parser/typography.ts +143 -112
  70. package/src/types.ts +2 -2
  71. package/dist/react-native.d.js +0 -1
@@ -34,6 +34,49 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
+ // src/parser/aspectRatio.ts
38
+ var ASPECT_RATIO_PRESETS = {
39
+ "aspect-auto": void 0,
40
+ // Remove aspect ratio
41
+ "aspect-square": 1,
42
+ // 1:1
43
+ "aspect-video": 16 / 9
44
+ // 16:9
45
+ };
46
+ function parseArbitraryAspectRatio(value) {
47
+ const match = value.match(/^\[(\d+)\/(\d+)\]$/);
48
+ if (match) {
49
+ const numerator = Number.parseInt(match[1], 10);
50
+ const denominator = Number.parseInt(match[2], 10);
51
+ if (denominator === 0) {
52
+ if (process.env.NODE_ENV !== "production") {
53
+ console.warn(`[react-native-tailwind] Invalid aspect ratio: ${value}. Denominator cannot be zero.`);
54
+ }
55
+ return null;
56
+ }
57
+ return numerator / denominator;
58
+ }
59
+ return null;
60
+ }
61
+ function parseAspectRatio(cls) {
62
+ if (!cls.startsWith("aspect-")) {
63
+ return null;
64
+ }
65
+ if (cls in ASPECT_RATIO_PRESETS) {
66
+ const aspectRatio2 = ASPECT_RATIO_PRESETS[cls];
67
+ if (aspectRatio2 === void 0) {
68
+ return {};
69
+ }
70
+ return { aspectRatio: aspectRatio2 };
71
+ }
72
+ const arbitraryValue = cls.substring(7);
73
+ const aspectRatio = parseArbitraryAspectRatio(arbitraryValue);
74
+ if (aspectRatio !== null) {
75
+ return { aspectRatio };
76
+ }
77
+ return null;
78
+ }
79
+
37
80
  // src/parser/borders.ts
38
81
  var BORDER_WIDTH_SCALE = {
39
82
  "": 1,
@@ -53,7 +96,58 @@ var BORDER_RADIUS_SCALE = {
53
96
  "3xl": 24,
54
97
  full: 9999
55
98
  };
99
+ var BORDER_WIDTH_PROP_MAP = {
100
+ t: "borderTopWidth",
101
+ r: "borderRightWidth",
102
+ b: "borderBottomWidth",
103
+ l: "borderLeftWidth"
104
+ };
105
+ var BORDER_RADIUS_CORNER_MAP = {
106
+ tl: "borderTopLeftRadius",
107
+ tr: "borderTopRightRadius",
108
+ bl: "borderBottomLeftRadius",
109
+ br: "borderBottomRightRadius"
110
+ };
111
+ var BORDER_RADIUS_SIDE_MAP = {
112
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
113
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
114
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
115
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
116
+ };
117
+ function parseArbitraryBorderWidth(value) {
118
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
119
+ if (pxMatch) {
120
+ return parseInt(pxMatch[1], 10);
121
+ }
122
+ if (value.startsWith("[") && value.endsWith("]")) {
123
+ if (process.env.NODE_ENV !== "production") {
124
+ console.warn(
125
+ `[react-native-tailwind] Unsupported arbitrary border width value: ${value}. Only px values are supported (e.g., [8px] or [8]).`
126
+ );
127
+ }
128
+ return null;
129
+ }
130
+ return null;
131
+ }
132
+ function parseArbitraryBorderRadius(value) {
133
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
134
+ if (pxMatch) {
135
+ return parseInt(pxMatch[1], 10);
136
+ }
137
+ if (value.startsWith("[") && value.endsWith("]")) {
138
+ if (process.env.NODE_ENV !== "production") {
139
+ console.warn(
140
+ `[react-native-tailwind] Unsupported arbitrary border radius value: ${value}. Only px values are supported (e.g., [12px] or [12]).`
141
+ );
142
+ }
143
+ return null;
144
+ }
145
+ return null;
146
+ }
56
147
  function parseBorder(cls) {
148
+ if (cls === "border-solid") return { borderStyle: "solid" };
149
+ if (cls === "border-dotted") return { borderStyle: "dotted" };
150
+ if (cls === "border-dashed") return { borderStyle: "dashed" };
57
151
  if (cls.startsWith("border-")) {
58
152
  return parseBorderWidth(cls);
59
153
  }
@@ -63,42 +157,25 @@ function parseBorder(cls) {
63
157
  if (cls.startsWith("rounded")) {
64
158
  return parseBorderRadius(cls);
65
159
  }
66
- if (cls === "border-solid") return { borderStyle: "solid" };
67
- if (cls === "border-dotted") return { borderStyle: "dotted" };
68
- if (cls === "border-dashed") return { borderStyle: "dashed" };
69
160
  return null;
70
161
  }
71
162
  function parseBorderWidth(cls) {
72
- const allArbMatch = cls.match(/^border-\[(\d+)(?:px)?\]$/);
73
- if (allArbMatch) {
74
- return { borderWidth: parseInt(allArbMatch[1], 10) };
75
- }
76
- const dirArbMatch = cls.match(/^border-([trbl])-\[(\d+)(?:px)?\]$/);
77
- if (dirArbMatch) {
78
- const dir = dirArbMatch[1];
79
- const value = parseInt(dirArbMatch[2], 10);
80
- const propMap = {
81
- t: "borderTopWidth",
82
- r: "borderRightWidth",
83
- b: "borderBottomWidth",
84
- l: "borderLeftWidth"
85
- };
86
- return { [propMap[dir]]: value };
87
- }
88
- const dirMatch = cls.match(/^border-([trbl])-?(\d*)$/);
163
+ const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
89
164
  if (dirMatch) {
90
165
  const dir = dirMatch[1];
91
- const scaleKey = dirMatch[2] || "";
92
- const value = BORDER_WIDTH_SCALE[scaleKey];
93
- if (typeof value === "number") {
94
- const propMap = {
95
- t: "borderTopWidth",
96
- r: "borderRightWidth",
97
- b: "borderBottomWidth",
98
- l: "borderLeftWidth"
99
- };
100
- return { [propMap[dir]]: value };
166
+ const valueStr = dirMatch[2] || "";
167
+ if (valueStr.startsWith("[")) {
168
+ const arbitraryValue = parseArbitraryBorderWidth(valueStr);
169
+ if (arbitraryValue !== null) {
170
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: arbitraryValue };
171
+ }
172
+ return null;
101
173
  }
174
+ const scaleValue = BORDER_WIDTH_SCALE[valueStr];
175
+ if (scaleValue !== void 0) {
176
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: scaleValue };
177
+ }
178
+ return null;
102
179
  }
103
180
  const allMatch = cls.match(/^border-(\d+)$/);
104
181
  if (allMatch) {
@@ -107,78 +184,76 @@ function parseBorderWidth(cls) {
107
184
  return { borderWidth: value };
108
185
  }
109
186
  }
187
+ const allArbMatch = cls.match(/^border-(\[.+\])$/);
188
+ if (allArbMatch) {
189
+ const arbitraryValue = parseArbitraryBorderWidth(allArbMatch[1]);
190
+ if (arbitraryValue !== null) {
191
+ return { borderWidth: arbitraryValue };
192
+ }
193
+ }
110
194
  return null;
111
195
  }
112
196
  function parseBorderRadius(cls) {
113
- const allArbMatch = cls.match(/^rounded-\[(\d+)(?:px)?\]$/);
114
- if (allArbMatch) {
115
- return { borderRadius: parseInt(allArbMatch[1], 10) };
116
- }
117
- const cornerArbMatch = cls.match(/^rounded-(tl|tr|bl|br)-\[(\d+)(?:px)?\]$/);
118
- if (cornerArbMatch) {
119
- const corner = cornerArbMatch[1];
120
- const value = parseInt(cornerArbMatch[2], 10);
121
- const propMap = {
122
- tl: "borderTopLeftRadius",
123
- tr: "borderTopRightRadius",
124
- bl: "borderBottomLeftRadius",
125
- br: "borderBottomRightRadius"
126
- };
127
- return { [propMap[corner]]: value };
128
- }
129
- const sideArbMatch = cls.match(/^rounded-([trbl])-\[(\d+)(?:px)?\]$/);
130
- if (sideArbMatch) {
131
- const side = sideArbMatch[1];
132
- const value = parseInt(sideArbMatch[2], 10);
133
- const propMap = {
134
- t: ["borderTopLeftRadius", "borderTopRightRadius"],
135
- r: ["borderTopRightRadius", "borderBottomRightRadius"],
136
- b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
137
- l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
138
- };
139
- const result = {};
140
- propMap[side].forEach((prop) => result[prop] = value);
141
- return result;
142
- }
143
- const allMatch = cls.match(/^rounded(-\w+)?$/);
144
- if (allMatch) {
145
- const scaleKey = allMatch[1] ? allMatch[1].substring(1) : "";
146
- const value = BORDER_RADIUS_SCALE[scaleKey];
147
- if (value !== void 0) {
148
- return { borderRadius: value };
197
+ const withoutPrefix = cls.substring(7);
198
+ if (withoutPrefix === "") {
199
+ return { borderRadius: BORDER_RADIUS_SCALE[""] };
200
+ }
201
+ if (!withoutPrefix.startsWith("-")) {
202
+ return null;
203
+ }
204
+ const rest = withoutPrefix.substring(1);
205
+ if (rest === "") {
206
+ return null;
207
+ }
208
+ const cornerMatch = rest.match(/^(tl|tr|bl|br)(?:-(.+))?$/);
209
+ if (cornerMatch) {
210
+ const corner = cornerMatch[1];
211
+ const valueStr = cornerMatch[2] || "";
212
+ if (valueStr.startsWith("[")) {
213
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
214
+ if (arbitraryValue !== null) {
215
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: arbitraryValue };
216
+ }
217
+ return null;
218
+ }
219
+ const scaleValue2 = BORDER_RADIUS_SCALE[valueStr];
220
+ if (scaleValue2 !== void 0) {
221
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: scaleValue2 };
149
222
  }
223
+ return null;
150
224
  }
151
- const sideMatch = cls.match(/^rounded-([trbl])(?:-(\w+))?$/);
225
+ const sideMatch = rest.match(/^([trbl])(?:-(.+))?$/);
152
226
  if (sideMatch) {
153
227
  const side = sideMatch[1];
154
- const scaleKey = sideMatch[2] || "";
155
- const value = BORDER_RADIUS_SCALE[scaleKey];
228
+ const valueStr = sideMatch[2] || "";
229
+ let value;
230
+ if (valueStr.startsWith("[")) {
231
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
232
+ if (arbitraryValue !== null) {
233
+ value = arbitraryValue;
234
+ } else {
235
+ return null;
236
+ }
237
+ } else {
238
+ value = BORDER_RADIUS_SCALE[valueStr];
239
+ }
156
240
  if (value !== void 0) {
157
- const propMap = {
158
- t: ["borderTopLeftRadius", "borderTopRightRadius"],
159
- r: ["borderTopRightRadius", "borderBottomRightRadius"],
160
- b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
161
- l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
162
- };
163
241
  const result = {};
164
- propMap[side].forEach((prop) => result[prop] = value);
242
+ BORDER_RADIUS_SIDE_MAP[side].forEach((prop) => result[prop] = value);
165
243
  return result;
166
244
  }
245
+ return null;
167
246
  }
168
- const cornerMatch = cls.match(/^rounded-(tl|tr|bl|br)(?:-(\w+))?$/);
169
- if (cornerMatch) {
170
- const corner = cornerMatch[1];
171
- const scaleKey = cornerMatch[2] || "";
172
- const value = BORDER_RADIUS_SCALE[scaleKey];
173
- if (value !== void 0) {
174
- const propMap = {
175
- tl: "borderTopLeftRadius",
176
- tr: "borderTopRightRadius",
177
- bl: "borderBottomLeftRadius",
178
- br: "borderBottomRightRadius"
179
- };
180
- return { [propMap[corner]]: value };
247
+ if (rest.startsWith("[")) {
248
+ const arbitraryValue = parseArbitraryBorderRadius(rest);
249
+ if (arbitraryValue !== null) {
250
+ return { borderRadius: arbitraryValue };
181
251
  }
252
+ return null;
253
+ }
254
+ const scaleValue = BORDER_RADIUS_SCALE[rest];
255
+ if (scaleValue !== void 0) {
256
+ return { borderRadius: scaleValue };
182
257
  }
183
258
  return null;
184
259
  }
@@ -289,27 +364,86 @@ var COLORS = {
289
364
  black: "#000000",
290
365
  transparent: "transparent"
291
366
  };
367
+ function applyOpacity(hex, opacity) {
368
+ if (hex === "transparent") {
369
+ return "transparent";
370
+ }
371
+ const cleanHex = hex.replace(/^#/, "");
372
+ const fullHex = cleanHex.length === 3 ? cleanHex.split("").map((char) => char + char).join("") : cleanHex;
373
+ const alpha = Math.round(opacity / 100 * 255);
374
+ const alphaHex = alpha.toString(16).padStart(2, "0").toUpperCase();
375
+ return `#${fullHex.toUpperCase()}${alphaHex}`;
376
+ }
377
+ function parseArbitraryColor(value) {
378
+ const hexMatch = value.match(/^\[#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})\]$/);
379
+ if (hexMatch) {
380
+ const hex = hexMatch[1];
381
+ if (hex.length === 3) {
382
+ const expanded = hex.split("").map((char) => char + char).join("");
383
+ return `#${expanded}`;
384
+ }
385
+ return `#${hex}`;
386
+ }
387
+ if (value.startsWith("[") && value.endsWith("]")) {
388
+ if (process.env.NODE_ENV !== "production") {
389
+ console.warn(
390
+ `[react-native-tailwind] Unsupported arbitrary color value: ${value}. Only hex colors are supported (e.g., [#ff0000], [#f00], or [#ff0000aa]).`
391
+ );
392
+ }
393
+ return null;
394
+ }
395
+ return null;
396
+ }
292
397
  function parseColor(cls, customColors) {
293
398
  const getColor = (key) => {
294
399
  return customColors?.[key] ?? COLORS[key];
295
400
  };
401
+ const parseColorWithOpacity = (colorKey) => {
402
+ const opacityMatch = colorKey.match(/^(.+)\/(\d+)$/);
403
+ if (opacityMatch) {
404
+ const baseColorKey = opacityMatch[1];
405
+ const opacity = Number.parseInt(opacityMatch[2], 10);
406
+ if (opacity < 0 || opacity > 100) {
407
+ if (process.env.NODE_ENV !== "production") {
408
+ console.warn(
409
+ `[react-native-tailwind] Invalid opacity value: ${opacity}. Opacity must be between 0 and 100.`
410
+ );
411
+ }
412
+ return null;
413
+ }
414
+ const arbitraryColor2 = parseArbitraryColor(baseColorKey);
415
+ if (arbitraryColor2 !== null) {
416
+ return applyOpacity(arbitraryColor2, opacity);
417
+ }
418
+ const color = getColor(baseColorKey);
419
+ if (color) {
420
+ return applyOpacity(color, opacity);
421
+ }
422
+ return null;
423
+ }
424
+ const arbitraryColor = parseArbitraryColor(colorKey);
425
+ if (arbitraryColor !== null) {
426
+ return arbitraryColor;
427
+ }
428
+ return getColor(colorKey) ?? null;
429
+ };
296
430
  if (cls.startsWith("bg-")) {
297
431
  const colorKey = cls.substring(3);
298
- const color = getColor(colorKey);
432
+ const color = parseColorWithOpacity(colorKey);
299
433
  if (color) {
300
434
  return { backgroundColor: color };
301
435
  }
302
436
  }
303
437
  if (cls.startsWith("text-")) {
304
438
  const colorKey = cls.substring(5);
305
- const color = getColor(colorKey);
439
+ const color = parseColorWithOpacity(colorKey);
306
440
  if (color) {
307
441
  return { color };
308
442
  }
309
443
  }
310
444
  if (cls.startsWith("border-") && !cls.match(/^border-[0-9]/)) {
311
445
  const colorKey = cls.substring(7);
312
- const color = getColor(colorKey);
446
+ const color = parseColorWithOpacity(colorKey);
313
447
  if (color) {
314
448
  return { borderColor: color };
315
449
  }
@@ -384,10 +518,203 @@ var OVERFLOW_MAP = {
384
518
  "overflow-visible": { overflow: "visible" },
385
519
  "overflow-scroll": { overflow: "scroll" }
386
520
  };
521
+ var Z_INDEX_SCALE = {
522
+ 0: 0,
523
+ 10: 10,
524
+ 20: 20,
525
+ 30: 30,
526
+ 40: 40,
527
+ 50: 50,
528
+ auto: 0
529
+ // React Native doesn't have 'auto', default to 0
530
+ };
531
+ var INSET_SCALE = {
532
+ 0: 0,
533
+ 0.5: 2,
534
+ 1: 4,
535
+ 1.5: 6,
536
+ 2: 8,
537
+ 2.5: 10,
538
+ 3: 12,
539
+ 3.5: 14,
540
+ 4: 16,
541
+ 5: 20,
542
+ 6: 24,
543
+ 8: 32,
544
+ 10: 40,
545
+ 12: 48,
546
+ 16: 64,
547
+ 20: 80,
548
+ 24: 96
549
+ };
387
550
  function parseLayout(cls) {
551
+ if (cls.startsWith("z-")) {
552
+ const zKey = cls.substring(2);
553
+ const zValue = Z_INDEX_SCALE[zKey];
554
+ if (zValue !== void 0) {
555
+ return { zIndex: zValue };
556
+ }
557
+ }
558
+ if (cls.startsWith("top-")) {
559
+ const topKey = cls.substring(4);
560
+ if (topKey === "auto") {
561
+ return {};
562
+ }
563
+ const topValue = INSET_SCALE[topKey];
564
+ if (topValue !== void 0) {
565
+ return { top: topValue };
566
+ }
567
+ }
568
+ if (cls.startsWith("right-")) {
569
+ const rightKey = cls.substring(6);
570
+ if (rightKey === "auto") {
571
+ return {};
572
+ }
573
+ const rightValue = INSET_SCALE[rightKey];
574
+ if (rightValue !== void 0) {
575
+ return { right: rightValue };
576
+ }
577
+ }
578
+ if (cls.startsWith("bottom-")) {
579
+ const bottomKey = cls.substring(7);
580
+ if (bottomKey === "auto") {
581
+ return {};
582
+ }
583
+ const bottomValue = INSET_SCALE[bottomKey];
584
+ if (bottomValue !== void 0) {
585
+ return { bottom: bottomValue };
586
+ }
587
+ }
588
+ if (cls.startsWith("left-")) {
589
+ const leftKey = cls.substring(5);
590
+ if (leftKey === "auto") {
591
+ return {};
592
+ }
593
+ const leftValue = INSET_SCALE[leftKey];
594
+ if (leftValue !== void 0) {
595
+ return { left: leftValue };
596
+ }
597
+ }
598
+ if (cls.startsWith("inset-")) {
599
+ const insetKey = cls.substring(6);
600
+ const insetValue = INSET_SCALE[insetKey];
601
+ if (insetValue !== void 0) {
602
+ return { top: insetValue, right: insetValue, bottom: insetValue, left: insetValue };
603
+ }
604
+ }
605
+ if (cls.startsWith("inset-x-")) {
606
+ const insetKey = cls.substring(8);
607
+ const insetValue = INSET_SCALE[insetKey];
608
+ if (insetValue !== void 0) {
609
+ return { left: insetValue, right: insetValue };
610
+ }
611
+ }
612
+ if (cls.startsWith("inset-y-")) {
613
+ const insetKey = cls.substring(8);
614
+ const insetValue = INSET_SCALE[insetKey];
615
+ if (insetValue !== void 0) {
616
+ return { top: insetValue, bottom: insetValue };
617
+ }
618
+ }
388
619
  return DISPLAY_MAP[cls] ?? FLEX_DIRECTION_MAP[cls] ?? FLEX_WRAP_MAP[cls] ?? FLEX_MAP[cls] ?? GROW_SHRINK_MAP[cls] ?? JUSTIFY_CONTENT_MAP[cls] ?? ALIGN_ITEMS_MAP[cls] ?? ALIGN_SELF_MAP[cls] ?? ALIGN_CONTENT_MAP[cls] ?? POSITION_MAP[cls] ?? OVERFLOW_MAP[cls] ?? null;
389
620
  }
390
621
 
622
+ // src/parser/shadows.ts
623
+ var import_react_native = require("react-native");
624
+ var SHADOW_DEFINITIONS = {
625
+ "shadow-sm": {
626
+ ios: {
627
+ shadowColor: "#000000",
628
+ shadowOffset: { width: 0, height: 1 },
629
+ shadowOpacity: 0.05,
630
+ shadowRadius: 1
631
+ },
632
+ android: {
633
+ elevation: 1
634
+ }
635
+ },
636
+ shadow: {
637
+ ios: {
638
+ shadowColor: "#000000",
639
+ shadowOffset: { width: 0, height: 1 },
640
+ shadowOpacity: 0.1,
641
+ shadowRadius: 2
642
+ },
643
+ android: {
644
+ elevation: 2
645
+ }
646
+ },
647
+ "shadow-md": {
648
+ ios: {
649
+ shadowColor: "#000000",
650
+ shadowOffset: { width: 0, height: 3 },
651
+ shadowOpacity: 0.15,
652
+ shadowRadius: 4
653
+ },
654
+ android: {
655
+ elevation: 4
656
+ }
657
+ },
658
+ "shadow-lg": {
659
+ ios: {
660
+ shadowColor: "#000000",
661
+ shadowOffset: { width: 0, height: 6 },
662
+ shadowOpacity: 0.2,
663
+ shadowRadius: 8
664
+ },
665
+ android: {
666
+ elevation: 8
667
+ }
668
+ },
669
+ "shadow-xl": {
670
+ ios: {
671
+ shadowColor: "#000000",
672
+ shadowOffset: { width: 0, height: 10 },
673
+ shadowOpacity: 0.25,
674
+ shadowRadius: 12
675
+ },
676
+ android: {
677
+ elevation: 12
678
+ }
679
+ },
680
+ "shadow-2xl": {
681
+ ios: {
682
+ shadowColor: "#000000",
683
+ shadowOffset: { width: 0, height: 20 },
684
+ shadowOpacity: 0.3,
685
+ shadowRadius: 24
686
+ },
687
+ android: {
688
+ elevation: 16
689
+ }
690
+ },
691
+ "shadow-none": {
692
+ ios: {
693
+ shadowColor: "transparent",
694
+ shadowOffset: { width: 0, height: 0 },
695
+ shadowOpacity: 0,
696
+ shadowRadius: 0
697
+ },
698
+ android: {
699
+ elevation: 0
700
+ }
701
+ }
702
+ };
703
+ function buildShadowScale() {
704
+ const scale = {};
705
+ for (const [key, value] of Object.entries(SHADOW_DEFINITIONS)) {
706
+ scale[key] = import_react_native.Platform.select(value);
707
+ }
708
+ return scale;
709
+ }
710
+ var SHADOW_SCALE = buildShadowScale();
711
+ function parseShadow(cls) {
712
+ if (cls in SHADOW_SCALE) {
713
+ return SHADOW_SCALE[cls];
714
+ }
715
+ return null;
716
+ }
717
+
391
718
  // src/parser/sizing.ts
392
719
  var SIZE_SCALE = {
393
720
  0: 0,
@@ -599,131 +926,99 @@ var SPACING_SCALE = {
599
926
  80: 320,
600
927
  96: 384
601
928
  };
602
- function parseSpacing(cls) {
603
- if (cls.startsWith("m-") || cls.startsWith("m")) {
604
- return parseMargin(cls);
605
- }
606
- if (cls.startsWith("p-") || cls.startsWith("p")) {
607
- return parsePadding(cls);
929
+ function parseArbitrarySpacing(value) {
930
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
931
+ if (pxMatch) {
932
+ return parseInt(pxMatch[1], 10);
608
933
  }
609
- if (cls.startsWith("gap-")) {
610
- return parseGap(cls);
934
+ if (value.startsWith("[") && value.endsWith("]")) {
935
+ if (process.env.NODE_ENV !== "production") {
936
+ console.warn(
937
+ `[react-native-tailwind] Unsupported arbitrary spacing value: ${value}. Only px values are supported (e.g., [16px] or [16]).`
938
+ );
939
+ }
940
+ return null;
611
941
  }
612
942
  return null;
613
943
  }
614
- function parseMargin(cls) {
615
- const allMatch = cls.match(/^m-(\d+(?:\.\d+)?)$/);
616
- if (allMatch) {
617
- const value = SPACING_SCALE[allMatch[1]];
618
- if (value !== void 0) {
619
- return { margin: value };
944
+ function parseSpacing(cls) {
945
+ const marginMatch = cls.match(/^m([xytrbls]?)-(.+)$/);
946
+ if (marginMatch) {
947
+ const [, dir, valueStr] = marginMatch;
948
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
949
+ if (arbitraryValue !== null) {
950
+ return getMarginStyle(dir, arbitraryValue);
620
951
  }
621
- }
622
- const xMatch = cls.match(/^mx-(\d+(?:\.\d+)?)$/);
623
- if (xMatch) {
624
- const value = SPACING_SCALE[xMatch[1]];
625
- if (value !== void 0) {
626
- return { marginHorizontal: value };
952
+ const scaleValue = SPACING_SCALE[valueStr];
953
+ if (scaleValue !== void 0) {
954
+ return getMarginStyle(dir, scaleValue);
627
955
  }
628
956
  }
629
- const yMatch = cls.match(/^my-(\d+(?:\.\d+)?)$/);
630
- if (yMatch) {
631
- const value = SPACING_SCALE[yMatch[1]];
632
- if (value !== void 0) {
633
- return { marginVertical: value };
957
+ const paddingMatch = cls.match(/^p([xytrbls]?)-(.+)$/);
958
+ if (paddingMatch) {
959
+ const [, dir, valueStr] = paddingMatch;
960
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
961
+ if (arbitraryValue !== null) {
962
+ return getPaddingStyle(dir, arbitraryValue);
634
963
  }
635
- }
636
- const tMatch = cls.match(/^mt-(\d+(?:\.\d+)?)$/);
637
- if (tMatch) {
638
- const value = SPACING_SCALE[tMatch[1]];
639
- if (value !== void 0) {
640
- return { marginTop: value };
964
+ const scaleValue = SPACING_SCALE[valueStr];
965
+ if (scaleValue !== void 0) {
966
+ return getPaddingStyle(dir, scaleValue);
641
967
  }
642
968
  }
643
- const rMatch = cls.match(/^mr-(\d+(?:\.\d+)?)$/);
644
- if (rMatch) {
645
- const value = SPACING_SCALE[rMatch[1]];
646
- if (value !== void 0) {
647
- return { marginRight: value };
969
+ const gapMatch = cls.match(/^gap-(.+)$/);
970
+ if (gapMatch) {
971
+ const valueStr = gapMatch[1];
972
+ const arbitraryValue = parseArbitrarySpacing(valueStr);
973
+ if (arbitraryValue !== null) {
974
+ return { gap: arbitraryValue };
648
975
  }
649
- }
650
- const bMatch = cls.match(/^mb-(\d+(?:\.\d+)?)$/);
651
- if (bMatch) {
652
- const value = SPACING_SCALE[bMatch[1]];
653
- if (value !== void 0) {
654
- return { marginBottom: value };
976
+ const scaleValue = SPACING_SCALE[valueStr];
977
+ if (scaleValue !== void 0) {
978
+ return { gap: scaleValue };
655
979
  }
656
980
  }
657
- const lMatch = cls.match(/^ml-(\d+(?:\.\d+)?)$/);
658
- if (lMatch) {
659
- const value = SPACING_SCALE[lMatch[1]];
660
- if (value !== void 0) {
981
+ return null;
982
+ }
983
+ function getMarginStyle(dir, value) {
984
+ switch (dir) {
985
+ case "":
986
+ return { margin: value };
987
+ case "x":
988
+ return { marginHorizontal: value };
989
+ case "y":
990
+ return { marginVertical: value };
991
+ case "t":
992
+ return { marginTop: value };
993
+ case "r":
994
+ return { marginRight: value };
995
+ case "b":
996
+ return { marginBottom: value };
997
+ case "l":
661
998
  return { marginLeft: value };
662
- }
999
+ default:
1000
+ return {};
663
1001
  }
664
- return null;
665
1002
  }
666
- function parsePadding(cls) {
667
- const allMatch = cls.match(/^p-(\d+(?:\.\d+)?)$/);
668
- if (allMatch) {
669
- const value = SPACING_SCALE[allMatch[1]];
670
- if (value !== void 0) {
1003
+ function getPaddingStyle(dir, value) {
1004
+ switch (dir) {
1005
+ case "":
671
1006
  return { padding: value };
672
- }
673
- }
674
- const xMatch = cls.match(/^px-(\d+(?:\.\d+)?)$/);
675
- if (xMatch) {
676
- const value = SPACING_SCALE[xMatch[1]];
677
- if (value !== void 0) {
1007
+ case "x":
678
1008
  return { paddingHorizontal: value };
679
- }
680
- }
681
- const yMatch = cls.match(/^py-(\d+(?:\.\d+)?)$/);
682
- if (yMatch) {
683
- const value = SPACING_SCALE[yMatch[1]];
684
- if (value !== void 0) {
1009
+ case "y":
685
1010
  return { paddingVertical: value };
686
- }
687
- }
688
- const tMatch = cls.match(/^pt-(\d+(?:\.\d+)?)$/);
689
- if (tMatch) {
690
- const value = SPACING_SCALE[tMatch[1]];
691
- if (value !== void 0) {
1011
+ case "t":
692
1012
  return { paddingTop: value };
693
- }
694
- }
695
- const rMatch = cls.match(/^pr-(\d+(?:\.\d+)?)$/);
696
- if (rMatch) {
697
- const value = SPACING_SCALE[rMatch[1]];
698
- if (value !== void 0) {
1013
+ case "r":
699
1014
  return { paddingRight: value };
700
- }
701
- }
702
- const bMatch = cls.match(/^pb-(\d+(?:\.\d+)?)$/);
703
- if (bMatch) {
704
- const value = SPACING_SCALE[bMatch[1]];
705
- if (value !== void 0) {
1015
+ case "b":
706
1016
  return { paddingBottom: value };
707
- }
708
- }
709
- const lMatch = cls.match(/^pl-(\d+(?:\.\d+)?)$/);
710
- if (lMatch) {
711
- const value = SPACING_SCALE[lMatch[1]];
712
- if (value !== void 0) {
1017
+ case "l":
713
1018
  return { paddingLeft: value };
714
- }
1019
+ default:
1020
+ return {};
715
1021
  }
716
- return null;
717
- }
718
- function parseGap(cls) {
719
- const match = cls.match(/^gap-(\d+(?:\.\d+)?)$/);
720
- if (match) {
721
- const value = SPACING_SCALE[match[1]];
722
- if (value !== void 0) {
723
- return { gap: value };
724
- }
725
- }
726
- return null;
727
1022
  }
728
1023
 
729
1024
  // src/parser/typography.ts
@@ -742,99 +1037,142 @@ var FONT_SIZES = {
742
1037
  "8xl": 96,
743
1038
  "9xl": 128
744
1039
  };
1040
+ var FONT_WEIGHT_MAP = {
1041
+ "font-thin": { fontWeight: "100" },
1042
+ "font-extralight": { fontWeight: "200" },
1043
+ "font-light": { fontWeight: "300" },
1044
+ "font-normal": { fontWeight: "400" },
1045
+ "font-medium": { fontWeight: "500" },
1046
+ "font-semibold": { fontWeight: "600" },
1047
+ "font-bold": { fontWeight: "700" },
1048
+ "font-extrabold": { fontWeight: "800" },
1049
+ "font-black": { fontWeight: "900" }
1050
+ };
1051
+ var FONT_STYLE_MAP = {
1052
+ italic: { fontStyle: "italic" },
1053
+ "not-italic": { fontStyle: "normal" }
1054
+ };
1055
+ var TEXT_ALIGN_MAP = {
1056
+ "text-left": { textAlign: "left" },
1057
+ "text-center": { textAlign: "center" },
1058
+ "text-right": { textAlign: "right" },
1059
+ "text-justify": { textAlign: "justify" }
1060
+ };
1061
+ var TEXT_DECORATION_MAP = {
1062
+ underline: { textDecorationLine: "underline" },
1063
+ "line-through": { textDecorationLine: "line-through" },
1064
+ "no-underline": { textDecorationLine: "none" }
1065
+ };
1066
+ var TEXT_TRANSFORM_MAP = {
1067
+ uppercase: { textTransform: "uppercase" },
1068
+ lowercase: { textTransform: "lowercase" },
1069
+ capitalize: { textTransform: "capitalize" },
1070
+ "normal-case": { textTransform: "none" }
1071
+ };
1072
+ var LINE_HEIGHT_MAP = {
1073
+ "leading-none": { lineHeight: 16 },
1074
+ "leading-tight": { lineHeight: 20 },
1075
+ "leading-snug": { lineHeight: 22 },
1076
+ "leading-normal": { lineHeight: 24 },
1077
+ "leading-relaxed": { lineHeight: 28 },
1078
+ "leading-loose": { lineHeight: 32 }
1079
+ };
1080
+ var TRACKING_MAP = {
1081
+ "tracking-tighter": { letterSpacing: -0.8 },
1082
+ "tracking-tight": { letterSpacing: -0.4 },
1083
+ "tracking-normal": { letterSpacing: 0 },
1084
+ "tracking-wide": { letterSpacing: 0.4 },
1085
+ "tracking-wider": { letterSpacing: 0.8 },
1086
+ "tracking-widest": { letterSpacing: 1.6 }
1087
+ };
1088
+ function parseArbitraryFontSize(value) {
1089
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
1090
+ if (pxMatch) {
1091
+ return parseInt(pxMatch[1], 10);
1092
+ }
1093
+ if (value.startsWith("[") && value.endsWith("]")) {
1094
+ if (process.env.NODE_ENV !== "production") {
1095
+ console.warn(
1096
+ `[react-native-tailwind] Unsupported arbitrary font size value: ${value}. Only px values are supported (e.g., [18px] or [18]).`
1097
+ );
1098
+ }
1099
+ return null;
1100
+ }
1101
+ return null;
1102
+ }
1103
+ function parseArbitraryLineHeight(value) {
1104
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
1105
+ if (pxMatch) {
1106
+ return parseInt(pxMatch[1], 10);
1107
+ }
1108
+ if (value.startsWith("[") && value.endsWith("]")) {
1109
+ if (process.env.NODE_ENV !== "production") {
1110
+ console.warn(
1111
+ `[react-native-tailwind] Unsupported arbitrary line height value: ${value}. Only px values are supported (e.g., [24px] or [24]).`
1112
+ );
1113
+ }
1114
+ return null;
1115
+ }
1116
+ return null;
1117
+ }
745
1118
  function parseTypography(cls) {
746
1119
  if (cls.startsWith("text-")) {
747
1120
  const sizeKey = cls.substring(5);
1121
+ const arbitraryValue = parseArbitraryFontSize(sizeKey);
1122
+ if (arbitraryValue !== null) {
1123
+ return { fontSize: arbitraryValue };
1124
+ }
748
1125
  const fontSize = FONT_SIZES[sizeKey];
749
1126
  if (fontSize !== void 0) {
750
1127
  return { fontSize };
751
1128
  }
752
1129
  }
753
- if (cls === "font-thin") {
754
- return { fontWeight: "100" };
755
- }
756
- if (cls === "font-extralight") {
757
- return { fontWeight: "200" };
758
- }
759
- if (cls === "font-light") {
760
- return { fontWeight: "300" };
761
- }
762
- if (cls === "font-normal") {
763
- return { fontWeight: "400" };
764
- }
765
- if (cls === "font-medium") {
766
- return { fontWeight: "500" };
767
- }
768
- if (cls === "font-semibold") {
769
- return { fontWeight: "600" };
770
- }
771
- if (cls === "font-bold") {
772
- return { fontWeight: "700" };
773
- }
774
- if (cls === "font-extrabold") {
775
- return { fontWeight: "800" };
776
- }
777
- if (cls === "font-black") {
778
- return { fontWeight: "900" };
779
- }
780
- if (cls === "italic") {
781
- return { fontStyle: "italic" };
782
- }
783
- if (cls === "not-italic") {
784
- return { fontStyle: "normal" };
785
- }
786
- if (cls === "text-left") {
787
- return { textAlign: "left" };
788
- }
789
- if (cls === "text-center") {
790
- return { textAlign: "center" };
791
- }
792
- if (cls === "text-right") {
793
- return { textAlign: "right" };
794
- }
795
- if (cls === "text-justify") {
796
- return { textAlign: "justify" };
797
- }
798
- if (cls === "underline") {
799
- return { textDecorationLine: "underline" };
800
- }
801
- if (cls === "line-through") {
802
- return { textDecorationLine: "line-through" };
803
- }
804
- if (cls === "no-underline") {
805
- return { textDecorationLine: "none" };
806
- }
807
- if (cls === "uppercase") {
808
- return { textTransform: "uppercase" };
809
- }
810
- if (cls === "lowercase") {
811
- return { textTransform: "lowercase" };
812
- }
813
- if (cls === "capitalize") {
814
- return { textTransform: "capitalize" };
815
- }
816
- if (cls === "normal-case") {
817
- return { textTransform: "none" };
818
- }
819
- if (cls === "leading-none") {
820
- return { lineHeight: 16 };
1130
+ if (cls.startsWith("leading-")) {
1131
+ const heightKey = cls.substring(8);
1132
+ const arbitraryValue = parseArbitraryLineHeight(heightKey);
1133
+ if (arbitraryValue !== null) {
1134
+ return { lineHeight: arbitraryValue };
1135
+ }
821
1136
  }
822
- if (cls === "leading-tight") {
823
- return { lineHeight: 20 };
1137
+ return FONT_WEIGHT_MAP[cls] ?? FONT_STYLE_MAP[cls] ?? TEXT_ALIGN_MAP[cls] ?? TEXT_DECORATION_MAP[cls] ?? TEXT_TRANSFORM_MAP[cls] ?? LINE_HEIGHT_MAP[cls] ?? TRACKING_MAP[cls] ?? null;
1138
+ }
1139
+
1140
+ // src/parser/modifiers.ts
1141
+ var SUPPORTED_MODIFIERS = ["active", "hover", "focus", "disabled"];
1142
+ function parseModifier(cls) {
1143
+ const colonIndex = cls.indexOf(":");
1144
+ if (colonIndex === -1) {
1145
+ return null;
824
1146
  }
825
- if (cls === "leading-snug") {
826
- return { lineHeight: 22 };
1147
+ const potentialModifier = cls.slice(0, colonIndex);
1148
+ const baseClass = cls.slice(colonIndex + 1);
1149
+ if (!SUPPORTED_MODIFIERS.includes(potentialModifier)) {
1150
+ return null;
827
1151
  }
828
- if (cls === "leading-normal") {
829
- return { lineHeight: 24 };
1152
+ if (baseClass.includes(":")) {
1153
+ return null;
830
1154
  }
831
- if (cls === "leading-relaxed") {
832
- return { lineHeight: 28 };
1155
+ if (!baseClass) {
1156
+ return null;
833
1157
  }
834
- if (cls === "leading-loose") {
835
- return { lineHeight: 32 };
1158
+ return {
1159
+ modifier: potentialModifier,
1160
+ baseClass
1161
+ };
1162
+ }
1163
+ function splitModifierClasses(className) {
1164
+ const classes = className.trim().split(/\s+/).filter(Boolean);
1165
+ const baseClasses = [];
1166
+ const modifierClasses = [];
1167
+ for (const cls of classes) {
1168
+ const parsed = parseModifier(cls);
1169
+ if (parsed) {
1170
+ modifierClasses.push(parsed);
1171
+ } else {
1172
+ baseClasses.push(cls);
1173
+ }
836
1174
  }
837
- return null;
1175
+ return { baseClasses, modifierClasses };
838
1176
  }
839
1177
 
840
1178
  // src/parser/index.ts
@@ -850,11 +1188,13 @@ function parseClassName(className, customColors) {
850
1188
  function parseClass(cls, customColors) {
851
1189
  const parsers = [
852
1190
  parseSpacing,
1191
+ parseBorder,
853
1192
  (cls2) => parseColor(cls2, customColors),
854
1193
  parseLayout,
855
1194
  parseTypography,
856
- parseBorder,
857
- parseSizing
1195
+ parseSizing,
1196
+ parseShadow,
1197
+ parseAspectRatio
858
1198
  ];
859
1199
  for (const parser of parsers) {
860
1200
  const result = parser(cls);
@@ -950,14 +1290,19 @@ function extractCustomColors(filename) {
950
1290
  }
951
1291
 
952
1292
  // src/babel/index.ts
1293
+ var STYLES_IDENTIFIER = "_twStyles";
953
1294
  var SUPPORTED_CLASS_ATTRIBUTES = [
954
1295
  "className",
1296
+ "containerClassName",
955
1297
  "contentContainerClassName",
956
1298
  "columnWrapperClassName",
957
1299
  "ListHeaderComponentClassName",
958
1300
  "ListFooterComponentClassName"
959
1301
  ];
960
1302
  function getTargetStyleProp(attributeName) {
1303
+ if (attributeName === "containerClassName") {
1304
+ return "containerStyle";
1305
+ }
961
1306
  if (attributeName === "contentContainerClassName") {
962
1307
  return "contentContainerStyle";
963
1308
  }
@@ -972,6 +1317,33 @@ function getTargetStyleProp(attributeName) {
972
1317
  }
973
1318
  return "style";
974
1319
  }
1320
+ function getComponentModifierSupport(jsxElement, t) {
1321
+ if (!t.isJSXOpeningElement(jsxElement)) {
1322
+ return null;
1323
+ }
1324
+ const name = jsxElement.name;
1325
+ let componentName = null;
1326
+ if (t.isJSXIdentifier(name)) {
1327
+ componentName = name.name;
1328
+ }
1329
+ if (t.isJSXMemberExpression(name)) {
1330
+ const property = name.property;
1331
+ if (t.isJSXIdentifier(property)) {
1332
+ componentName = property.name;
1333
+ }
1334
+ }
1335
+ if (!componentName) {
1336
+ return null;
1337
+ }
1338
+ switch (componentName) {
1339
+ case "Pressable":
1340
+ return { component: "Pressable", supportedModifiers: ["active", "hover", "focus", "disabled"] };
1341
+ case "TextInput":
1342
+ return { component: "TextInput", supportedModifiers: ["focus", "disabled"] };
1343
+ default:
1344
+ return null;
1345
+ }
1346
+ }
975
1347
  function processDynamicExpression(expression, state, t) {
976
1348
  if (t.isTemplateLiteral(expression)) {
977
1349
  return processTemplateLiteral(expression, state, t);
@@ -997,7 +1369,7 @@ function processTemplateLiteral(node, state, t) {
997
1369
  const styleKey = generateStyleKey2(cls);
998
1370
  state.styleRegistry.set(styleKey, styleObject);
999
1371
  staticParts.push(cls);
1000
- parts.push(t.memberExpression(t.identifier("styles"), t.identifier(styleKey)));
1372
+ parts.push(t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(styleKey)));
1001
1373
  }
1002
1374
  }
1003
1375
  if (i < node.expressions.length) {
@@ -1052,7 +1424,7 @@ function processStringOrExpression(node, state, t) {
1052
1424
  const styleObject = parseClassName2(className, state.customColors);
1053
1425
  const styleKey = generateStyleKey2(className);
1054
1426
  state.styleRegistry.set(styleKey, styleObject);
1055
- return t.memberExpression(t.identifier("styles"), t.identifier(styleKey));
1427
+ return t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(styleKey));
1056
1428
  }
1057
1429
  if (t.isConditionalExpression(node)) {
1058
1430
  const result = processConditionalExpression(node, state, t);
@@ -1068,6 +1440,77 @@ function processStringOrExpression(node, state, t) {
1068
1440
  }
1069
1441
  return null;
1070
1442
  }
1443
+ function processStaticClassNameWithModifiers(className, state, t) {
1444
+ const { baseClasses, modifierClasses } = splitModifierClasses(className);
1445
+ let baseStyleExpression = null;
1446
+ if (baseClasses.length > 0) {
1447
+ const baseClassName = baseClasses.join(" ");
1448
+ const baseStyleObject = parseClassName2(baseClassName, state.customColors);
1449
+ const baseStyleKey = generateStyleKey2(baseClassName);
1450
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
1451
+ baseStyleExpression = t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(baseStyleKey));
1452
+ }
1453
+ const modifiersByType = /* @__PURE__ */ new Map();
1454
+ for (const mod of modifierClasses) {
1455
+ if (!modifiersByType.has(mod.modifier)) {
1456
+ modifiersByType.set(mod.modifier, []);
1457
+ }
1458
+ const modGroup = modifiersByType.get(mod.modifier);
1459
+ if (modGroup) {
1460
+ modGroup.push(mod);
1461
+ }
1462
+ }
1463
+ const styleArrayElements = [];
1464
+ if (baseStyleExpression) {
1465
+ styleArrayElements.push(baseStyleExpression);
1466
+ }
1467
+ for (const [modifierType, modifiers] of modifiersByType) {
1468
+ const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
1469
+ const modifierStyleObject = parseClassName2(modifierClassNames, state.customColors);
1470
+ const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
1471
+ state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
1472
+ const stateProperty = getStatePropertyForModifier(modifierType);
1473
+ const conditionalExpression = t.logicalExpression(
1474
+ "&&",
1475
+ t.identifier(stateProperty),
1476
+ t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(modifierStyleKey))
1477
+ );
1478
+ styleArrayElements.push(conditionalExpression);
1479
+ }
1480
+ if (styleArrayElements.length === 1) {
1481
+ return styleArrayElements[0];
1482
+ }
1483
+ return t.arrayExpression(styleArrayElements);
1484
+ }
1485
+ function getStatePropertyForModifier(modifier) {
1486
+ switch (modifier) {
1487
+ case "active":
1488
+ return "pressed";
1489
+ case "hover":
1490
+ return "hovered";
1491
+ case "focus":
1492
+ return "focused";
1493
+ case "disabled":
1494
+ return "disabled";
1495
+ default:
1496
+ return "pressed";
1497
+ }
1498
+ }
1499
+ function createStyleFunction(styleExpression, modifierTypes, t) {
1500
+ const paramProperties = [];
1501
+ const usedStateProps = /* @__PURE__ */ new Set();
1502
+ for (const modifierType of modifierTypes) {
1503
+ const stateProperty = getStatePropertyForModifier(modifierType);
1504
+ if (!usedStateProps.has(stateProperty)) {
1505
+ usedStateProps.add(stateProperty);
1506
+ paramProperties.push(
1507
+ t.objectProperty(t.identifier(stateProperty), t.identifier(stateProperty), false, true)
1508
+ );
1509
+ }
1510
+ }
1511
+ const param = t.objectPattern(paramProperties);
1512
+ return t.arrowFunctionExpression([param], styleExpression);
1513
+ }
1071
1514
  function reactNativeTailwindBabelPlugin({
1072
1515
  types: t
1073
1516
  }) {
@@ -1125,6 +1568,69 @@ function reactNativeTailwindBabelPlugin({
1125
1568
  return;
1126
1569
  }
1127
1570
  state.hasClassNames = true;
1571
+ const { baseClasses, modifierClasses } = splitModifierClasses(className);
1572
+ if (modifierClasses.length > 0) {
1573
+ const jsxOpeningElement = path2.parent;
1574
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
1575
+ if (componentSupport) {
1576
+ const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
1577
+ const unsupportedModifiers = usedModifiers.filter(
1578
+ (mod) => !componentSupport.supportedModifiers.includes(mod)
1579
+ );
1580
+ if (unsupportedModifiers.length > 0) {
1581
+ if (process.env.NODE_ENV !== "production") {
1582
+ console.warn(
1583
+ `[react-native-tailwind] Modifiers (${unsupportedModifiers.map((m) => `${m}:`).join(", ")}) are not supported on ${componentSupport.component} component at ${state.file.opts.filename ?? "unknown"}. Supported modifiers: ${componentSupport.supportedModifiers.join(", ")}`
1584
+ );
1585
+ }
1586
+ const supportedModifierClasses = modifierClasses.filter(
1587
+ (m) => componentSupport.supportedModifiers.includes(m.modifier)
1588
+ );
1589
+ if (supportedModifierClasses.length === 0) {
1590
+ } else {
1591
+ const filteredClassName = baseClasses.join(" ") + " " + supportedModifierClasses.map((m) => `${m.modifier}:${m.baseClass}`).join(" ");
1592
+ const styleExpression = processStaticClassNameWithModifiers(
1593
+ filteredClassName.trim(),
1594
+ state,
1595
+ t
1596
+ );
1597
+ const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
1598
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
1599
+ const parent2 = path2.parent;
1600
+ const styleAttribute2 = parent2.attributes.find(
1601
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
1602
+ );
1603
+ if (styleAttribute2) {
1604
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
1605
+ } else {
1606
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
1607
+ }
1608
+ return;
1609
+ }
1610
+ } else {
1611
+ const styleExpression = processStaticClassNameWithModifiers(className, state, t);
1612
+ const modifierTypes = usedModifiers;
1613
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
1614
+ const parent2 = path2.parent;
1615
+ const styleAttribute2 = parent2.attributes.find(
1616
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
1617
+ );
1618
+ if (styleAttribute2) {
1619
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
1620
+ } else {
1621
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
1622
+ }
1623
+ return;
1624
+ }
1625
+ } else {
1626
+ if (process.env.NODE_ENV !== "production") {
1627
+ const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
1628
+ console.warn(
1629
+ `[react-native-tailwind] Modifiers (${usedModifiers.map((m) => `${m}:`).join(", ")}) can only be used on compatible components (Pressable, TextInput). Found on unsupported element at ${state.file.opts.filename ?? "unknown"}`
1630
+ );
1631
+ }
1632
+ }
1633
+ }
1128
1634
  const styleObject = parseClassName2(className, state.customColors);
1129
1635
  const styleKey = generateStyleKey2(className);
1130
1636
  state.styleRegistry.set(styleKey, styleObject);
@@ -1187,14 +1693,14 @@ function addStyleSheetImport(path2, t) {
1187
1693
  function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, t) {
1188
1694
  const styleAttribute = t.jsxAttribute(
1189
1695
  t.jsxIdentifier(targetStyleProp),
1190
- t.jsxExpressionContainer(t.memberExpression(t.identifier("styles"), t.identifier(styleKey)))
1696
+ t.jsxExpressionContainer(t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(styleKey)))
1191
1697
  );
1192
1698
  classNamePath.replaceWith(styleAttribute);
1193
1699
  }
1194
1700
  function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, t) {
1195
1701
  const existingStyle = styleAttribute.value.expression;
1196
1702
  const styleArray = t.arrayExpression([
1197
- t.memberExpression(t.identifier("styles"), t.identifier(styleKey)),
1703
+ t.memberExpression(t.identifier(STYLES_IDENTIFIER), t.identifier(styleKey)),
1198
1704
  existingStyle
1199
1705
  ]);
1200
1706
  styleAttribute.value = t.jsxExpressionContainer(styleArray);
@@ -1218,6 +1724,31 @@ function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
1218
1724
  styleAttribute.value = t.jsxExpressionContainer(styleArray);
1219
1725
  classNamePath.remove();
1220
1726
  }
1727
+ function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
1728
+ const styleAttribute = t.jsxAttribute(
1729
+ t.jsxIdentifier(targetStyleProp),
1730
+ t.jsxExpressionContainer(styleFunctionExpression)
1731
+ );
1732
+ classNamePath.replaceWith(styleAttribute);
1733
+ }
1734
+ function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
1735
+ const existingStyle = styleAttribute.value.expression;
1736
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
1737
+ const paramIdentifier = t.identifier("_state");
1738
+ const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
1739
+ const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
1740
+ const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
1741
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
1742
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
1743
+ } else {
1744
+ const paramIdentifier = t.identifier("_state");
1745
+ const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
1746
+ const mergedArray = t.arrayExpression([functionCall, existingStyle]);
1747
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
1748
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
1749
+ }
1750
+ classNamePath.remove();
1751
+ }
1221
1752
  function injectStyles(path2, styleRegistry, t) {
1222
1753
  const styleProperties = [];
1223
1754
  for (const [key, styleObject] of styleRegistry) {
@@ -1236,7 +1767,7 @@ function injectStyles(path2, styleRegistry, t) {
1236
1767
  }
1237
1768
  const styleSheet = t.variableDeclaration("const", [
1238
1769
  t.variableDeclarator(
1239
- t.identifier("styles"),
1770
+ t.identifier(STYLES_IDENTIFIER),
1240
1771
  t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
1241
1772
  t.objectExpression(styleProperties)
1242
1773
  ])