@mgcrea/react-native-tailwind 0.12.1 → 0.14.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 (104) hide show
  1. package/README.md +45 -2031
  2. package/dist/babel/index.cjs +1726 -1094
  3. package/dist/babel/plugin/componentScope.d.ts +26 -0
  4. package/dist/babel/plugin/componentScope.ts +87 -0
  5. package/dist/babel/plugin/state.d.ts +123 -0
  6. package/dist/babel/plugin/state.ts +185 -0
  7. package/dist/babel/plugin/visitors/className.d.ts +11 -0
  8. package/{src/babel/plugin.test.ts → dist/babel/plugin/visitors/className.test.ts} +285 -572
  9. package/dist/babel/plugin/visitors/className.ts +652 -0
  10. package/dist/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
  11. package/dist/babel/plugin/visitors/imports.d.ts +11 -0
  12. package/dist/babel/plugin/visitors/imports.test.ts +88 -0
  13. package/dist/babel/plugin/visitors/imports.ts +116 -0
  14. package/dist/babel/plugin/visitors/program.d.ts +15 -0
  15. package/dist/babel/plugin/visitors/program.test.ts +325 -0
  16. package/dist/babel/plugin/visitors/program.ts +116 -0
  17. package/dist/babel/plugin/visitors/tw.d.ts +16 -0
  18. package/dist/babel/plugin/visitors/tw.test.ts +771 -0
  19. package/dist/babel/plugin/visitors/tw.ts +148 -0
  20. package/dist/babel/plugin.d.ts +3 -96
  21. package/dist/babel/plugin.test.ts +470 -0
  22. package/dist/babel/plugin.ts +28 -963
  23. package/dist/babel/utils/colorSchemeModifierProcessing.ts +11 -0
  24. package/dist/babel/utils/componentSupport.test.ts +20 -7
  25. package/dist/babel/utils/componentSupport.ts +2 -0
  26. package/dist/babel/utils/directionalModifierProcessing.d.ts +34 -0
  27. package/dist/babel/utils/directionalModifierProcessing.ts +99 -0
  28. package/dist/babel/utils/modifierProcessing.ts +21 -0
  29. package/dist/babel/utils/platformModifierProcessing.ts +11 -0
  30. package/dist/babel/utils/styleInjection.d.ts +31 -0
  31. package/dist/babel/utils/styleInjection.ts +253 -7
  32. package/dist/babel/utils/twProcessing.d.ts +2 -0
  33. package/dist/babel/utils/twProcessing.ts +103 -3
  34. package/dist/babel/utils/windowDimensionsProcessing.d.ts +56 -0
  35. package/dist/babel/utils/windowDimensionsProcessing.ts +121 -0
  36. package/dist/components/TouchableOpacity.d.ts +35 -0
  37. package/dist/components/TouchableOpacity.js +1 -0
  38. package/dist/components/index.d.ts +3 -0
  39. package/dist/components/index.js +1 -0
  40. package/dist/config/markers.d.ts +5 -0
  41. package/dist/config/markers.js +1 -0
  42. package/dist/index.d.ts +2 -5
  43. package/dist/index.js +1 -1
  44. package/dist/parser/borders.d.ts +3 -1
  45. package/dist/parser/borders.js +1 -1
  46. package/dist/parser/borders.test.js +1 -1
  47. package/dist/parser/colors.js +1 -1
  48. package/dist/parser/colors.test.js +1 -1
  49. package/dist/parser/index.d.ts +2 -2
  50. package/dist/parser/index.js +1 -1
  51. package/dist/parser/layout.js +1 -1
  52. package/dist/parser/layout.test.js +1 -1
  53. package/dist/parser/modifiers.d.ts +32 -2
  54. package/dist/parser/modifiers.js +1 -1
  55. package/dist/parser/modifiers.test.js +1 -1
  56. package/dist/parser/sizing.js +1 -1
  57. package/dist/parser/spacing.d.ts +1 -1
  58. package/dist/parser/spacing.js +1 -1
  59. package/dist/parser/spacing.test.js +1 -1
  60. package/dist/parser/typography.test.js +1 -1
  61. package/dist/runtime.cjs +1 -1
  62. package/dist/runtime.cjs.map +4 -4
  63. package/dist/runtime.js +1 -1
  64. package/dist/runtime.js.map +4 -4
  65. package/package.json +6 -6
  66. package/src/babel/plugin/componentScope.ts +87 -0
  67. package/src/babel/plugin/state.ts +185 -0
  68. package/src/babel/plugin/visitors/className.test.ts +1625 -0
  69. package/src/babel/plugin/visitors/className.ts +652 -0
  70. package/src/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
  71. package/src/babel/plugin/visitors/imports.test.ts +88 -0
  72. package/src/babel/plugin/visitors/imports.ts +116 -0
  73. package/src/babel/plugin/visitors/program.test.ts +325 -0
  74. package/src/babel/plugin/visitors/program.ts +116 -0
  75. package/src/babel/plugin/visitors/tw.test.ts +771 -0
  76. package/src/babel/plugin/visitors/tw.ts +148 -0
  77. package/src/babel/plugin.ts +28 -963
  78. package/src/babel/utils/colorSchemeModifierProcessing.ts +11 -0
  79. package/src/babel/utils/componentSupport.test.ts +20 -7
  80. package/src/babel/utils/componentSupport.ts +2 -0
  81. package/src/babel/utils/directionalModifierProcessing.ts +99 -0
  82. package/src/babel/utils/modifierProcessing.ts +21 -0
  83. package/src/babel/utils/platformModifierProcessing.ts +11 -0
  84. package/src/babel/utils/styleInjection.ts +253 -7
  85. package/src/babel/utils/twProcessing.ts +103 -3
  86. package/src/babel/utils/windowDimensionsProcessing.ts +121 -0
  87. package/src/components/TouchableOpacity.tsx +71 -0
  88. package/src/components/index.ts +3 -0
  89. package/src/config/markers.ts +5 -0
  90. package/src/index.ts +4 -5
  91. package/src/parser/borders.test.ts +162 -0
  92. package/src/parser/borders.ts +67 -9
  93. package/src/parser/colors.test.ts +249 -0
  94. package/src/parser/colors.ts +38 -0
  95. package/src/parser/index.ts +4 -2
  96. package/src/parser/layout.test.ts +74 -0
  97. package/src/parser/layout.ts +94 -0
  98. package/src/parser/modifiers.test.ts +206 -0
  99. package/src/parser/modifiers.ts +62 -3
  100. package/src/parser/sizing.ts +11 -0
  101. package/src/parser/spacing.test.ts +66 -0
  102. package/src/parser/spacing.ts +15 -5
  103. package/src/parser/typography.test.ts +8 -0
  104. package/src/parser/typography.ts +4 -0
@@ -1,236 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-function */
2
- import { transformSync } from "@babel/core";
3
2
  import { describe, expect, it, vi } from "vitest";
4
- import babelPlugin, { type PluginOptions } from "./plugin.js";
3
+ import { transform } from "../../../../test/helpers/babelTransform.js";
5
4
 
6
- /**
7
- * Helper to transform code with the Babel plugin
8
- */
9
- function transform(code: string, options?: PluginOptions, includeJsx = false) {
10
- const presets = includeJsx
11
- ? ["@babel/preset-react", ["@babel/preset-typescript", { isTSX: true, allExtensions: true }]]
12
- : [];
13
-
14
- const result = transformSync(code, {
15
- presets,
16
- plugins: [[babelPlugin, options]],
17
- filename: "test.tsx",
18
- configFile: false,
19
- babelrc: false,
20
- });
21
-
22
- return result?.code ?? "";
23
- }
24
-
25
- describe("Babel plugin - tw template tag transformation", () => {
26
- it("should transform simple tw template literal", () => {
27
- const input = `
28
- import { tw } from '@mgcrea/react-native-tailwind';
29
- const styles = tw\`bg-blue-500 m-4\`;
30
- `;
31
-
32
- const output = transform(input);
33
-
34
- // Should have StyleSheet import (either ESM or CommonJS)
35
- expect(output).toMatch(/import.*StyleSheet.*from "react-native"|require\("react-native"\)/);
36
- expect(output).toContain("StyleSheet");
37
-
38
- // Should have _twStyles definition
39
- expect(output).toContain("_twStyles");
40
- expect(output).toContain("StyleSheet.create");
41
-
42
- // Should transform tw call to object with style property
43
- expect(output).toContain("style:");
44
- expect(output).toContain("_twStyles._bg_blue_500_m_4");
45
-
46
- // Should remove tw import
47
- expect(output).not.toContain("from '@mgcrea/react-native-tailwind'");
48
- });
49
-
50
- it("should transform tw with state modifiers", () => {
51
- const input = `
52
- import { tw } from '@mgcrea/react-native-tailwind';
53
- const styles = tw\`bg-blue-500 active:bg-blue-700 disabled:bg-gray-300\`;
54
- `;
55
-
56
- const output = transform(input);
57
-
58
- // Should have base style
59
- expect(output).toContain("style:");
60
- expect(output).toContain("_bg_blue_500");
61
-
62
- // Should have activeStyle
63
- expect(output).toContain("activeStyle:");
64
- expect(output).toContain("_active_bg_blue_700");
65
-
66
- // Should have disabledStyle
67
- expect(output).toContain("disabledStyle:");
68
- expect(output).toContain("_disabled_bg_gray_300");
69
-
70
- // Should create StyleSheet with all styles
71
- expect(output).toContain("backgroundColor:");
72
- });
73
-
74
- it("should inject StyleSheet.create after imports", () => {
75
- const input = `
76
- import { tw } from '@mgcrea/react-native-tailwind';
77
- import { View } from 'react-native';
78
-
79
- const styles = tw\`m-4\`;
80
- `;
81
-
82
- const output = transform(input);
83
-
84
- // Find the position of imports and StyleSheet.create
85
- const viewImportPos = output.indexOf('require("react-native")');
86
- const styleSheetCreatePos = output.indexOf("_twStyles");
87
-
88
- // StyleSheet.create should come after imports
89
- expect(styleSheetCreatePos).toBeGreaterThan(viewImportPos);
90
- });
91
-
92
- it("should handle tw in object literals", () => {
93
- const input = `
94
- import { tw } from '@mgcrea/react-native-tailwind';
95
-
96
- const sizeVariants = {
97
- sm: {
98
- container: tw\`h-9 px-3\`,
99
- text: tw\`text-sm\`,
100
- },
101
- };
102
- `;
103
-
104
- const output = transform(input);
105
-
106
- // Should define _twStyles before object literal
107
- const twStylesPos = output.indexOf("_twStyles");
108
- const sizeVariantsPos = output.indexOf("sizeVariants");
109
-
110
- expect(twStylesPos).toBeGreaterThan(0);
111
- expect(twStylesPos).toBeLessThan(sizeVariantsPos);
112
-
113
- // Should have both styles
114
- expect(output).toContain("_h_9_px_3");
115
- expect(output).toContain("_text_sm");
116
- });
117
-
118
- it("should handle empty tw template literal", () => {
119
- const input = `
120
- import { tw } from '@mgcrea/react-native-tailwind';
121
- const styles = tw\`\`;
122
- `;
123
-
124
- const output = transform(input);
125
-
126
- // Should replace with empty style object
127
- expect(output).toContain("style:");
128
- expect(output).toContain("{}");
129
- });
130
-
131
- it("should preserve other imports from the same package", () => {
132
- const input = `
133
- import { tw, TwStyle, COLORS } from '@mgcrea/react-native-tailwind';
134
- const styles = tw\`m-4\`;
135
- `;
136
-
137
- const output = transform(input);
138
-
139
- // Should remove tw but keep other imports
140
- expect(output).not.toContain('"tw"');
141
- expect(output).toContain("TwStyle");
142
- expect(output).toContain("COLORS");
143
- });
144
-
145
- it("should handle renamed tw import", () => {
146
- const input = `
147
- import { tw as customTw } from '@mgcrea/react-native-tailwind';
148
- const styles = customTw\`m-4 p-2\`;
149
- `;
150
-
151
- const output = transform(input);
152
-
153
- // Should still transform the renamed import
154
- expect(output).toContain("_twStyles");
155
- expect(output).toContain("_m_4_p_2");
156
- expect(output).not.toContain("customTw");
157
- });
158
-
159
- it("should handle multiple tw calls", () => {
160
- const input = `
161
- import { tw } from '@mgcrea/react-native-tailwind';
162
- const style1 = tw\`bg-red-500\`;
163
- const style2 = tw\`bg-blue-500\`;
164
- const style3 = tw\`bg-green-500\`;
165
- `;
166
-
167
- const output = transform(input);
168
-
169
- // Should have all three styles in StyleSheet
170
- expect(output).toContain("_bg_red_500");
171
- expect(output).toContain("_bg_blue_500");
172
- expect(output).toContain("_bg_green_500");
173
-
174
- // Should have StyleSheet.create with all styles
175
- expect(output).toContain("StyleSheet.create");
176
- });
177
-
178
- it("should use custom stylesIdentifier option", () => {
179
- const input = `
180
- import { tw } from '@mgcrea/react-native-tailwind';
181
- const styles = tw\`m-4\`;
182
- `;
183
-
184
- const output = transform(input, { stylesIdentifier: "myStyles" });
185
-
186
- // Should use custom identifier
187
- expect(output).toContain("myStyles");
188
- expect(output).toContain("myStyles._m_4");
189
- expect(output).not.toContain("_twStyles");
190
- });
191
- });
192
-
193
- describe("Babel plugin - twStyle function transformation", () => {
194
- it("should transform twStyle function call", () => {
195
- const input = `
196
- import { twStyle } from '@mgcrea/react-native-tailwind';
197
- const styles = twStyle('bg-blue-500 m-4');
198
- `;
199
-
200
- const output = transform(input);
201
-
202
- // Should transform to object with style property
203
- expect(output).toContain("style:");
204
- expect(output).toContain("_twStyles._bg_blue_500_m_4");
205
- });
206
-
207
- it("should transform twStyle with modifiers", () => {
208
- const input = `
209
- import { twStyle } from '@mgcrea/react-native-tailwind';
210
- const styles = twStyle('bg-blue-500 active:bg-blue-700');
211
- `;
212
-
213
- const output = transform(input);
214
-
215
- expect(output).toContain("style:");
216
- expect(output).toContain("activeStyle:");
217
- });
218
-
219
- it("should handle empty twStyle call", () => {
220
- const input = `
221
- import { twStyle } from '@mgcrea/react-native-tailwind';
222
- const styles = twStyle('');
223
- `;
224
-
225
- const output = transform(input);
226
-
227
- // Should replace with undefined
228
- expect(output).toContain("undefined");
229
- });
230
- });
231
-
232
- // Note: JSX tests require @babel/preset-react
233
- describe("Babel plugin - className transformation (existing behavior)", () => {
5
+ describe("className visitor - basic transformation", () => {
234
6
  it("should still transform className props", () => {
235
7
  const input = `
236
8
  import { View } from 'react-native';
@@ -304,6 +76,35 @@ describe("Babel plugin - className transformation (existing behavior)", () => {
304
76
  expect(output).toMatch(/_state\s*=>/);
305
77
  });
306
78
 
79
+ it("should preserve 'use client' directive when injecting StyleSheet.create", () => {
80
+ const input = `
81
+ 'use client';
82
+ import { View } from 'react-native';
83
+ export function Component() {
84
+ return <View className="m-4 p-2" />;
85
+ }
86
+ `;
87
+
88
+ const output = transform(input, undefined, true);
89
+
90
+ // 'use client' should be the first statement
91
+ const lines = output.split("\n").filter((l: string) => l.trim());
92
+ const useClientIndex = lines.findIndex(
93
+ (l: string) => l.includes("'use client'") || l.includes('"use client"'),
94
+ );
95
+ expect(useClientIndex).toBe(0);
96
+
97
+ // StyleSheet.create should be in the output
98
+ expect(output).toContain("StyleSheet.create");
99
+ expect(output).toContain("_twStyles");
100
+
101
+ // Imports should come after 'use client', before StyleSheet.create
102
+ const importIndex = lines.findIndex((l: string) => l.includes("import"));
103
+ const styleSheetIndex = lines.findIndex((l: string) => l.includes("StyleSheet.create"));
104
+ expect(importIndex).toBeGreaterThan(useClientIndex);
105
+ expect(styleSheetIndex).toBeGreaterThan(importIndex);
106
+ });
107
+
307
108
  it("should merge dynamic className with function-based style prop", () => {
308
109
  const input = `
309
110
  import { TextInput } from 'react-native';
@@ -419,7 +220,7 @@ describe("Babel plugin - className transformation (existing behavior)", () => {
419
220
  });
420
221
  });
421
222
 
422
- describe("Babel plugin - placeholder: modifier transformation", () => {
223
+ describe("className visitor - placeholder: modifier", () => {
423
224
  it("should transform placeholder:text-{color} to placeholderTextColor prop", () => {
424
225
  const input = `
425
226
  import { TextInput } from 'react-native';
@@ -566,7 +367,7 @@ describe("Babel plugin - placeholder: modifier transformation", () => {
566
367
  });
567
368
  });
568
369
 
569
- describe("Babel plugin - platform modifier transformation", () => {
370
+ describe("className visitor - platform modifiers", () => {
570
371
  it("should transform platform modifiers to Platform.select()", () => {
571
372
  const input = `
572
373
  import React from 'react';
@@ -807,7 +608,7 @@ describe("Babel plugin - platform modifier transformation", () => {
807
608
  });
808
609
  });
809
610
 
810
- describe("Babel plugin - color scheme modifier transformation", () => {
611
+ describe("className visitor - color scheme modifiers", () => {
811
612
  it("should transform dark: modifier to conditional expression", () => {
812
613
  const input = `
813
614
  import React from 'react';
@@ -1107,7 +908,7 @@ describe("Babel plugin - color scheme modifier transformation", () => {
1107
908
  });
1108
909
  });
1109
910
 
1110
- describe("Babel plugin - custom color scheme hook import", () => {
911
+ describe("className visitor - custom color scheme hook", () => {
1111
912
  it("should use custom import source for color scheme hook", () => {
1112
913
  const input = `
1113
914
  import React from 'react';
@@ -1407,506 +1208,418 @@ describe("Babel plugin - custom color scheme hook import", () => {
1407
1208
  });
1408
1209
  });
1409
1210
 
1410
- describe("Babel plugin - import injection", () => {
1411
- it("should not add StyleSheet import to files without className usage", () => {
1211
+ describe("className visitor - directional border colors", () => {
1212
+ it("should transform directional border colors with preset values", () => {
1412
1213
  const input = `
1413
- import { View, Text } from 'react-native';
1414
-
1415
- function MyComponent() {
1416
- return <View><Text>Hello</Text></View>;
1214
+ import { View } from 'react-native';
1215
+ export function Component() {
1216
+ return <View className="border-t-red-500 border-l-blue-500" />;
1417
1217
  }
1418
1218
  `;
1419
1219
 
1420
1220
  const output = transform(input, undefined, true);
1421
1221
 
1422
- // Should not mutate the import by adding StyleSheet
1423
- // Count occurrences of "StyleSheet" in output
1424
- const styleSheetCount = (output.match(/StyleSheet/g) ?? []).length;
1425
- expect(styleSheetCount).toBe(0);
1222
+ // Should have StyleSheet
1223
+ expect(output).toContain("StyleSheet.create");
1426
1224
 
1427
- // Should not have _twStyles definition
1428
- expect(output).not.toContain("_twStyles");
1429
- expect(output).not.toContain("StyleSheet.create");
1225
+ // Should generate styles with borderTopColor and borderLeftColor
1226
+ expect(output).toMatch(/borderTopColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1227
+ expect(output).toMatch(/borderLeftColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1430
1228
 
1431
- // Original imports should remain unchanged
1432
- expect(output).toContain("View");
1433
- expect(output).toContain("Text");
1229
+ // Should not have className in output
1230
+ expect(output).not.toContain("className");
1434
1231
  });
1435
1232
 
1436
- it("should add StyleSheet import only when className is used", () => {
1233
+ it("should combine directional border width and color", () => {
1437
1234
  const input = `
1438
1235
  import { View } from 'react-native';
1439
-
1440
- function MyComponent() {
1441
- return <View className="m-4 p-2" />;
1236
+ export function Component() {
1237
+ return <View className="border-l-2 border-l-red-500" />;
1442
1238
  }
1443
1239
  `;
1444
1240
 
1445
1241
  const output = transform(input, undefined, true);
1446
1242
 
1447
- // Should have StyleSheet import (both single and double quotes)
1448
- expect(output).toMatch(/import.*StyleSheet.*from ['"]react-native['"]|require\(['"]react-native['"]\)/);
1243
+ // Should have both borderLeftWidth and borderLeftColor in the StyleSheet
1244
+ expect(output).toMatch(/borderLeftWidth[:\s]*2/);
1245
+ expect(output).toMatch(/borderLeftColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1449
1246
 
1450
- // Should have _twStyles definition
1451
- expect(output).toContain("_twStyles");
1452
- expect(output).toContain("StyleSheet.create");
1247
+ // Should not have className in output
1248
+ expect(output).not.toContain("className");
1453
1249
  });
1454
1250
 
1455
- it("should add Platform import only when platform modifiers are used", () => {
1251
+ it("should support directional border colors with opacity", () => {
1456
1252
  const input = `
1457
1253
  import { View } from 'react-native';
1458
-
1459
- function MyComponent() {
1460
- return <View className="ios:m-4 android:m-2" />;
1254
+ export function Component() {
1255
+ return <View className="border-t-red-500/50 border-b-blue-500/80" />;
1461
1256
  }
1462
1257
  `;
1463
1258
 
1464
1259
  const output = transform(input, undefined, true);
1465
1260
 
1466
- // Should have Platform import
1467
- expect(output).toContain("Platform");
1468
-
1469
- // Should have StyleSheet import too
1470
- expect(output).toContain("StyleSheet");
1471
-
1472
- // Should use Platform.select
1473
- expect(output).toContain("Platform.select");
1261
+ // Should have 8-digit hex colors with alpha channel
1262
+ expect(output).toMatch(/borderTopColor[:\s]*['"]#[0-9A-F]{8}['"]/i);
1263
+ expect(output).toMatch(/borderBottomColor[:\s]*['"]#[0-9A-F]{8}['"]/i);
1474
1264
  });
1475
1265
 
1476
- it("should not add Platform import without platform modifiers", () => {
1266
+ it("should support directional border colors with arbitrary hex values", () => {
1477
1267
  const input = `
1478
1268
  import { View } from 'react-native';
1479
-
1480
- function MyComponent() {
1481
- return <View className="m-4 p-2" />;
1269
+ export function Component() {
1270
+ return <View className="border-t-[#ff0000] border-r-[#abc]" />;
1482
1271
  }
1483
1272
  `;
1484
1273
 
1485
1274
  const output = transform(input, undefined, true);
1486
1275
 
1487
- // Should not have Platform import
1488
- const platformCount = (output.match(/Platform/g) ?? []).length;
1489
- expect(platformCount).toBe(0);
1490
-
1491
- // Should still have StyleSheet
1492
- expect(output).toContain("StyleSheet");
1276
+ // Should have borderTopColor and borderRightColor
1277
+ expect(output).toMatch(/borderTopColor[:\s]*['"]#[0-9a-fA-F]{6}['"]/);
1278
+ expect(output).toMatch(/borderRightColor[:\s]*['"]#[0-9a-fA-F]{6}['"]/);
1493
1279
  });
1494
- });
1495
1280
 
1496
- describe("Babel plugin - scheme: modifier", () => {
1497
- it.skip("should expand scheme: modifier into dark: and light: modifiers", () => {
1498
- // Note: This test requires tailwind.config.js with custom colors defined
1499
- // The scheme: modifier expands to dark: and light: modifiers which require
1500
- // the color variants to exist in customColors (e.g., systemGray-dark, systemGray-light)
1501
- //
1502
- // Integration test should be done in a real project with tailwind.config.js
1281
+ it("should support all four directional border colors", () => {
1503
1282
  const input = `
1504
1283
  import { View } from 'react-native';
1505
-
1506
- function MyComponent() {
1507
- return <View className="scheme:text-systemGray" />;
1284
+ export function Component() {
1285
+ return (
1286
+ <View className="border-t-red-500 border-r-blue-500 border-b-green-500 border-l-yellow-500" />
1287
+ );
1508
1288
  }
1509
1289
  `;
1510
1290
 
1511
1291
  const output = transform(input, undefined, true);
1512
1292
 
1513
- // Should generate both dark and light variants
1514
- expect(output).toContain("_dark_text_systemGray_dark");
1515
- expect(output).toContain("_light_text_systemGray_light");
1516
-
1517
- // Should inject useColorScheme hook
1518
- expect(output).toContain("useColorScheme");
1519
- expect(output).toContain("_twColorScheme");
1520
-
1521
- // Should have conditional expressions
1522
- expect(output).toContain("_twColorScheme === 'dark'");
1523
- expect(output).toContain("_twColorScheme === 'light'");
1293
+ // Should have all four directional color properties
1294
+ expect(output).toMatch(/borderTopColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1295
+ expect(output).toMatch(/borderRightColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1296
+ expect(output).toMatch(/borderBottomColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1297
+ expect(output).toMatch(/borderLeftColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1524
1298
  });
1525
- });
1526
1299
 
1527
- describe("Babel plugin - color scheme modifiers in tw/twStyle", () => {
1528
- it("should transform tw with dark: modifier inside component", () => {
1300
+ it("should combine directional widths, colors, and general border color", () => {
1529
1301
  const input = `
1530
- import { tw } from '@mgcrea/react-native-tailwind';
1531
-
1532
- function MyComponent() {
1533
- const styles = tw\`bg-white dark:bg-gray-900\`;
1534
- return null;
1302
+ import { View } from 'react-native';
1303
+ export function Component() {
1304
+ return (
1305
+ <View className="border border-gray-300 border-l-4 border-l-blue-500" />
1306
+ );
1535
1307
  }
1536
1308
  `;
1537
1309
 
1538
- const output = transform(input);
1539
-
1540
- // Should inject useColorScheme hook
1541
- expect(output).toContain("useColorScheme");
1542
- expect(output).toContain("_twColorScheme");
1310
+ const output = transform(input, undefined, true);
1543
1311
 
1544
- // Should generate style array with conditionals
1545
- expect(output).toContain("style: [");
1546
- expect(output).toContain('_twColorScheme === "dark"');
1547
- expect(output).toContain("_twStyles._dark_bg_gray_900");
1548
- expect(output).toContain("_twStyles._bg_white");
1312
+ // Should have general border properties
1313
+ expect(output).toMatch(/borderWidth[:\s]*1/);
1314
+ expect(output).toMatch(/borderColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1549
1315
 
1550
- // Should have StyleSheet.create
1551
- expect(output).toContain("StyleSheet.create");
1316
+ // Should have directional left border properties
1317
+ expect(output).toMatch(/borderLeftWidth[:\s]*4/);
1318
+ expect(output).toMatch(/borderLeftColor[:\s]*['"]#[0-9A-F]{6}['"]/i);
1552
1319
  });
1553
1320
 
1554
- it("should transform twStyle with light: modifier inside component", () => {
1321
+ it("should work with dynamic className containing directional border colors", () => {
1555
1322
  const input = `
1556
- import { twStyle } from '@mgcrea/react-native-tailwind';
1557
-
1558
- export const MyComponent = () => {
1559
- const buttonStyles = twStyle('text-gray-900 light:text-gray-100');
1560
- return null;
1561
- };
1323
+ import { View } from 'react-native';
1324
+ export function Component({ isError }) {
1325
+ return (
1326
+ <View className={\`border-t-2 \${isError ? 'border-t-red-500' : 'border-t-gray-300'}\`} />
1327
+ );
1328
+ }
1562
1329
  `;
1563
1330
 
1564
- const output = transform(input);
1331
+ const output = transform(input, undefined, true);
1565
1332
 
1566
- // Should inject useColorScheme hook
1567
- expect(output).toContain("useColorScheme");
1568
- expect(output).toContain("_twColorScheme");
1333
+ // Should have StyleSheet with both color options
1334
+ expect(output).toContain("_border_t_2");
1335
+ expect(output).toContain("_border_t_red_500");
1336
+ expect(output).toContain("_border_t_gray_300");
1569
1337
 
1570
- // Should generate style array with conditionals
1571
- expect(output).toContain("style: [");
1572
- expect(output).toContain('_twColorScheme === "light"');
1573
- expect(output).toContain("_twStyles._light_text_gray_100");
1574
- expect(output).toContain("_twStyles._text_gray_900");
1338
+ // Should have conditional expression with both styles
1339
+ expect(output).toMatch(/isError\s*\?\s*_twStyles\._border_t_red_500/);
1575
1340
  });
1341
+ });
1576
1342
 
1577
- it("should transform tw with both dark: and light: modifiers", () => {
1343
+ describe("className visitor - directional modifiers (RTL/LTR)", () => {
1344
+ it("should transform rtl: modifier and inject I18nManager import", () => {
1578
1345
  const input = `
1579
- import { tw } from '@mgcrea/react-native-tailwind';
1580
-
1581
- function MyComponent() {
1582
- const styles = tw\`bg-blue-500 dark:bg-blue-900 light:bg-blue-100\`;
1583
- return null;
1346
+ import { View } from 'react-native';
1347
+ export function Component() {
1348
+ return <View className="rtl:mr-4" />;
1584
1349
  }
1585
1350
  `;
1586
1351
 
1587
- const output = transform(input);
1588
-
1589
- // Should have both conditionals
1590
- expect(output).toContain('_twColorScheme === "dark"');
1591
- expect(output).toContain('_twColorScheme === "light"');
1592
- expect(output).toContain("_twStyles._dark_bg_blue_900");
1593
- expect(output).toContain("_twStyles._light_bg_blue_100");
1594
- expect(output).toContain("_twStyles._bg_blue_500");
1595
- });
1596
-
1597
- it("should combine color scheme modifiers with state modifiers", () => {
1598
- const input = `
1599
- import { tw } from '@mgcrea/react-native-tailwind';
1352
+ const output = transform(input, undefined, true);
1600
1353
 
1601
- function MyComponent() {
1602
- const styles = tw\`bg-white dark:bg-gray-900 active:bg-blue-500\`;
1603
- return null;
1604
- }
1605
- `;
1354
+ // Should import I18nManager
1355
+ expect(output).toContain("I18nManager");
1606
1356
 
1607
- const output = transform(input);
1357
+ // Should declare _twIsRTL variable
1358
+ expect(output).toContain("_twIsRTL");
1359
+ expect(output).toContain("I18nManager.isRTL");
1608
1360
 
1609
- // Should have color scheme conditionals in style array
1610
- expect(output).toContain("style: [");
1611
- expect(output).toContain('_twColorScheme === "dark"');
1361
+ // Should have StyleSheet with rtl style
1362
+ expect(output).toContain("StyleSheet.create");
1363
+ expect(output).toContain("_rtl_mr_4");
1612
1364
 
1613
- // Should have activeStyle property (separate from color scheme)
1614
- expect(output).toContain("activeStyle:");
1615
- expect(output).toContain("_twStyles._active_bg_blue_500");
1365
+ // Should have conditional for RTL
1366
+ expect(output).toMatch(/_twIsRTL\s*&&\s*_twStyles\._rtl_mr_4/);
1616
1367
  });
1617
1368
 
1618
- it("should warn if tw with color scheme modifiers used outside component", () => {
1619
- const consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
1620
-
1369
+ it("should transform ltr: modifier with negated conditional", () => {
1621
1370
  const input = `
1622
- import { tw } from '@mgcrea/react-native-tailwind';
1623
-
1624
- const globalStyles = tw\`bg-white dark:bg-gray-900\`;
1371
+ import { View } from 'react-native';
1372
+ export function Component() {
1373
+ return <View className="ltr:ml-4" />;
1374
+ }
1625
1375
  `;
1626
1376
 
1627
- const output = transform(input);
1628
-
1629
- // Should warn about usage outside component
1630
- expect(consoleWarnSpy).toHaveBeenCalledWith(
1631
- expect.stringContaining("Color scheme modifiers (dark:, light:) in tw/twStyle calls"),
1632
- );
1377
+ const output = transform(input, undefined, true);
1633
1378
 
1634
- // Should not inject hook (no component scope)
1635
- expect(output).not.toContain("useColorScheme");
1379
+ // Should import I18nManager
1380
+ expect(output).toContain("I18nManager");
1636
1381
 
1637
- // Should still generate styles but without runtime conditionals
1638
- expect(output).toContain("_twStyles");
1382
+ // Should have StyleSheet with ltr style
1383
+ expect(output).toContain("_ltr_ml_4");
1639
1384
 
1640
- consoleWarnSpy.mockRestore();
1385
+ // Should have negated conditional for LTR (!_twIsRTL)
1386
+ expect(output).toMatch(/!\s*_twIsRTL\s*&&\s*_twStyles\._ltr_ml_4/);
1641
1387
  });
1642
1388
 
1643
- it("should handle tw with only dark: modifier (no base class)", () => {
1389
+ it("should combine rtl: and ltr: modifiers", () => {
1644
1390
  const input = `
1645
- import { tw } from '@mgcrea/react-native-tailwind';
1646
-
1647
- function MyComponent() {
1648
- const styles = tw\`dark:bg-gray-900\`;
1649
- return null;
1391
+ import { View } from 'react-native';
1392
+ export function Component() {
1393
+ return <View className="rtl:mr-4 ltr:ml-4" />;
1650
1394
  }
1651
1395
  `;
1652
1396
 
1653
- const output = transform(input);
1397
+ const output = transform(input, undefined, true);
1398
+
1399
+ // Should have both styles
1400
+ expect(output).toContain("_rtl_mr_4");
1401
+ expect(output).toContain("_ltr_ml_4");
1654
1402
 
1655
- // Should still generate style array
1656
- expect(output).toContain("style: [");
1657
- expect(output).toContain('_twColorScheme === "dark"');
1658
- expect(output).toContain("_twStyles._dark_bg_gray_900");
1403
+ // Should have both conditionals
1404
+ expect(output).toMatch(/_twIsRTL\s*&&\s*_twStyles\._rtl_mr_4/);
1405
+ expect(output).toMatch(/!\s*_twIsRTL\s*&&\s*_twStyles\._ltr_ml_4/);
1659
1406
  });
1660
1407
 
1661
- it("should work with custom color scheme hook import", () => {
1408
+ it("should combine directional modifiers with base classes", () => {
1662
1409
  const input = `
1663
- import { tw } from '@mgcrea/react-native-tailwind';
1664
- import { useTheme } from '@react-navigation/native';
1665
-
1666
- function MyComponent() {
1667
- const styles = tw\`bg-white dark:bg-gray-900\`;
1668
- return null;
1410
+ import { View } from 'react-native';
1411
+ export function Component() {
1412
+ return <View className="p-4 bg-white rtl:pr-8 ltr:pl-8" />;
1669
1413
  }
1670
1414
  `;
1671
1415
 
1672
- const options: PluginOptions = {
1673
- colorScheme: {
1674
- importFrom: "@react-navigation/native",
1675
- importName: "useTheme",
1676
- },
1677
- };
1416
+ const output = transform(input, undefined, true);
1678
1417
 
1679
- const output = transform(input, options);
1418
+ // Should have base style
1419
+ expect(output).toContain("_bg_white_p_4");
1680
1420
 
1681
- // Should use existing import (not duplicate)
1682
- const themeImportCount = (output.match(/useTheme/g) ?? []).length;
1683
- // Should appear in import statement and hook call
1684
- expect(themeImportCount).toBeGreaterThanOrEqual(2);
1421
+ // Should have directional styles
1422
+ expect(output).toContain("_rtl_pr_8");
1423
+ expect(output).toContain("_ltr_pl_8");
1685
1424
 
1686
- // Should call the custom hook
1687
- expect(output).toContain("useTheme()");
1425
+ // Should generate an array with base and conditional styles
1426
+ expect(output).toMatch(/style:\s*\[/);
1688
1427
  });
1689
1428
 
1690
- it("should generate both style array and darkStyle/lightStyle properties", () => {
1429
+ it("should combine directional modifiers with platform modifiers", () => {
1691
1430
  const input = `
1692
- import { tw } from '@mgcrea/react-native-tailwind';
1693
-
1694
- function MyComponent() {
1695
- const styles = tw\`bg-white dark:bg-gray-900 light:bg-gray-50\`;
1696
- return null;
1431
+ import { View } from 'react-native';
1432
+ export function Component() {
1433
+ return <View className="p-4 ios:p-6 rtl:mr-4" />;
1697
1434
  }
1698
1435
  `;
1699
1436
 
1700
- const output = transform(input);
1701
-
1702
- // Should have runtime conditional in style array
1703
- expect(output).toContain("style: [");
1704
- expect(output).toContain('_twColorScheme === "dark"');
1705
- expect(output).toContain('_twColorScheme === "light"');
1706
-
1707
- // Should ALSO have darkStyle and lightStyle properties for manual access
1708
- expect(output).toContain("darkStyle:");
1709
- expect(output).toContain("lightStyle:");
1710
- expect(output).toContain("_twStyles._dark_bg_gray_900");
1711
- expect(output).toContain("_twStyles._light_bg_gray_50");
1712
- });
1437
+ const output = transform(input, undefined, true);
1713
1438
 
1714
- it("should allow accessing raw color values from darkStyle/lightStyle", () => {
1715
- const input = `
1716
- import { tw } from '@mgcrea/react-native-tailwind';
1439
+ // Should have Platform import
1440
+ expect(output).toContain("Platform");
1717
1441
 
1718
- function MyComponent() {
1719
- const btnStyles = tw\`bg-blue-500 dark:bg-blue-900\`;
1720
- // User can access raw hex for Reanimated
1721
- const darkBgColor = btnStyles.darkStyle?.backgroundColor;
1722
- return null;
1723
- }
1724
- `;
1442
+ // Should have I18nManager import
1443
+ expect(output).toContain("I18nManager");
1725
1444
 
1726
- const output = transform(input);
1445
+ // Should have all styles
1446
+ expect(output).toContain("_p_4");
1447
+ expect(output).toContain("_ios_p_6");
1448
+ expect(output).toContain("_rtl_mr_4");
1727
1449
 
1728
- // Should have darkStyle property available
1729
- expect(output).toContain("darkStyle:");
1730
- expect(output).toContain("_twStyles._dark_bg_blue_900");
1450
+ // Should have Platform.select
1451
+ expect(output).toContain("Platform.select");
1731
1452
 
1732
- // The actual usage line should be preserved (TypeScript/Babel doesn't remove it)
1733
- expect(output).toContain("btnStyles.darkStyle");
1453
+ // Should have RTL conditional
1454
+ expect(output).toMatch(/_twIsRTL\s*&&/);
1734
1455
  });
1735
1456
 
1736
- // Platform modifier tests for tw/twStyle
1737
- it("should transform tw with ios: modifier", () => {
1457
+ it("should not add I18nManager import if already present", () => {
1738
1458
  const input = `
1739
- import { tw } from '@mgcrea/react-native-tailwind';
1740
-
1741
- function MyComponent() {
1742
- const styles = tw\`bg-white ios:p-6\`;
1743
- return null;
1459
+ import { View, I18nManager } from 'react-native';
1460
+ export function Component() {
1461
+ return <View className="rtl:mr-4" />;
1744
1462
  }
1745
1463
  `;
1746
1464
 
1747
- const output = transform(input);
1748
-
1749
- // Should add Platform import
1750
- expect(output).toContain("Platform");
1751
- expect(output).toContain('from "react-native"');
1752
-
1753
- // Should generate style array with Platform.select()
1754
- expect(output).toContain("style: [");
1755
- expect(output).toContain("Platform.select");
1756
- expect(output).toContain("ios:");
1757
- expect(output).toContain("_twStyles._ios_p_6");
1758
- expect(output).toContain("_twStyles._bg_white");
1465
+ const output = transform(input, undefined, true);
1759
1466
 
1760
- // Should have StyleSheet.create
1761
- expect(output).toContain("StyleSheet.create");
1467
+ // Should have only one I18nManager import (merged, not duplicated)
1468
+ const i18nMatches = output.match(/I18nManager/g);
1469
+ // Should have I18nManager in: import, variable declaration, and style usage
1470
+ expect(i18nMatches).toBeTruthy();
1471
+ // Should not have duplicate imports
1472
+ expect(output).not.toMatch(/import\s*\{[^}]*I18nManager[^}]*I18nManager[^}]*\}/);
1762
1473
  });
1763
1474
 
1764
- it("should transform twStyle with android: modifier", () => {
1475
+ it("should work with directional logical properties", () => {
1765
1476
  const input = `
1766
- import { twStyle } from '@mgcrea/react-native-tailwind';
1767
-
1768
- export const MyComponent = () => {
1769
- const buttonStyles = twStyle('bg-blue-500 android:p-8');
1770
- return null;
1771
- };
1477
+ import { View } from 'react-native';
1478
+ export function Component() {
1479
+ return <View className="rtl:ms-4 ltr:me-4" />;
1480
+ }
1772
1481
  `;
1773
1482
 
1774
- const output = transform(input);
1483
+ const output = transform(input, undefined, true);
1775
1484
 
1776
- // Should add Platform import
1777
- expect(output).toContain("Platform");
1485
+ // Should have logical property styles
1486
+ expect(output).toContain("_rtl_ms_4");
1487
+ expect(output).toContain("_ltr_me_4");
1778
1488
 
1779
- // Should generate style array with Platform.select()
1780
- expect(output).toContain("style: [");
1781
- expect(output).toContain("Platform.select");
1782
- expect(output).toContain("android:");
1783
- expect(output).toContain("_twStyles._android_p_8");
1784
- expect(output).toContain("_twStyles._bg_blue_500");
1489
+ // Should contain marginStart and marginEnd in the StyleSheet
1490
+ expect(output).toContain("marginStart");
1491
+ expect(output).toContain("marginEnd");
1785
1492
  });
1786
1493
 
1787
- it("should transform tw with multiple platform modifiers", () => {
1494
+ it("should combine directional modifiers with color scheme modifiers", () => {
1788
1495
  const input = `
1789
- import { tw } from '@mgcrea/react-native-tailwind';
1790
-
1791
- function MyComponent() {
1792
- const styles = tw\`bg-white ios:p-6 android:p-8 web:p-4\`;
1793
- return null;
1496
+ import { View } from 'react-native';
1497
+ export function Component() {
1498
+ return <View className="bg-white dark:bg-gray-900 rtl:pr-4" />;
1794
1499
  }
1795
1500
  `;
1796
1501
 
1797
- const output = transform(input);
1502
+ const output = transform(input, undefined, true);
1503
+
1504
+ // Should have useColorScheme
1505
+ expect(output).toContain("useColorScheme");
1798
1506
 
1799
- // Should generate Platform.select() with all platforms
1800
- expect(output).toContain("Platform.select");
1801
- expect(output).toContain("ios:");
1802
- expect(output).toContain("android:");
1803
- expect(output).toContain("web:");
1804
- expect(output).toContain("_twStyles._ios_p_6");
1805
- expect(output).toContain("_twStyles._android_p_8");
1806
- expect(output).toContain("_twStyles._web_p_4");
1507
+ // Should have I18nManager
1508
+ expect(output).toContain("I18nManager");
1509
+
1510
+ // Should have all styles
1511
+ expect(output).toContain("_bg_white");
1512
+ expect(output).toContain("_dark_bg_gray_900");
1513
+ expect(output).toContain("_rtl_pr_4");
1514
+
1515
+ // Should have both conditionals
1516
+ expect(output).toMatch(/_twColorScheme\s*===\s*["']dark["']/);
1517
+ expect(output).toMatch(/_twIsRTL\s*&&/);
1807
1518
  });
1808
1519
 
1809
- it("should combine platform modifiers with color-scheme modifiers", () => {
1520
+ it("should handle aliased I18nManager import", () => {
1810
1521
  const input = `
1811
- import { tw } from '@mgcrea/react-native-tailwind';
1812
-
1813
- function MyComponent() {
1814
- const styles = tw\`bg-white ios:p-6 dark:bg-gray-900\`;
1815
- return null;
1522
+ import { View, I18nManager as RTL } from 'react-native';
1523
+ export function Component() {
1524
+ // Use RTL somewhere so TypeScript doesn't strip the unused import
1525
+ const isRtl = RTL.isRTL;
1526
+ return <View className="rtl:mr-4" />;
1816
1527
  }
1817
1528
  `;
1818
1529
 
1819
- const output = transform(input);
1820
-
1821
- // Should have both Platform and useColorScheme
1822
- expect(output).toContain("Platform");
1823
- expect(output).toContain("useColorScheme");
1824
- expect(output).toContain("_twColorScheme");
1530
+ const output = transform(input, undefined, true);
1825
1531
 
1826
- // Should have both conditionals in style array
1827
- expect(output).toContain("Platform.select");
1828
- expect(output).toContain('_twColorScheme === "dark"');
1532
+ // Should use the aliased identifier RTL.isRTL instead of I18nManager.isRTL
1533
+ expect(output).toContain("RTL.isRTL");
1534
+ // Should preserve the aliased import
1535
+ expect(output).toContain("I18nManager as RTL");
1536
+ // Should not add a separate I18nManager import without alias
1537
+ expect(output).not.toMatch(/I18nManager,|,\s*I18nManager\s*[,}]/);
1829
1538
  });
1830
1539
 
1831
- it("should generate iosStyle/androidStyle/webStyle properties for manual access", () => {
1540
+ it("should preserve 'use client' directive when injecting I18nManager variable", () => {
1832
1541
  const input = `
1833
- import { tw } from '@mgcrea/react-native-tailwind';
1834
-
1835
- function MyComponent() {
1836
- const styles = tw\`bg-white ios:p-6 android:p-8 web:p-4\`;
1837
- return null;
1542
+ 'use client';
1543
+ import { View } from 'react-native';
1544
+ export function Component() {
1545
+ return <View className="rtl:mr-4" />;
1838
1546
  }
1839
1547
  `;
1840
1548
 
1841
- const output = transform(input);
1549
+ const output = transform(input, undefined, true);
1842
1550
 
1843
- // Should have separate platform style properties
1844
- expect(output).toContain("iosStyle:");
1845
- expect(output).toContain("_twStyles._ios_p_6");
1846
- expect(output).toContain("androidStyle:");
1847
- expect(output).toContain("_twStyles._android_p_8");
1848
- expect(output).toContain("webStyle:");
1849
- expect(output).toContain("_twStyles._web_p_4");
1551
+ // 'use client' should be the first statement
1552
+ const lines = output.split("\n").filter((l: string) => l.trim());
1553
+ const useClientIndex = lines.findIndex(
1554
+ (l: string) => l.includes("'use client'") || l.includes('"use client"'),
1555
+ );
1556
+ expect(useClientIndex).toBe(0);
1850
1557
 
1851
- // Should also have runtime Platform.select() in style array
1852
- expect(output).toContain("Platform.select");
1558
+ // I18nManager variable should come after imports, not before 'use client'
1559
+ expect(output).toContain("_twIsRTL");
1560
+ expect(output).toContain("I18nManager.isRTL");
1853
1561
  });
1854
1562
 
1855
- it("should work with only platform modifiers (no base class)", () => {
1563
+ it("should preserve 'use strict' directive when injecting I18nManager variable", () => {
1856
1564
  const input = `
1857
- import { tw } from '@mgcrea/react-native-tailwind';
1858
-
1859
- function MyComponent() {
1860
- const styles = tw\`ios:p-6 android:p-8\`;
1861
- return null;
1565
+ 'use strict';
1566
+ import { View } from 'react-native';
1567
+ export function Component() {
1568
+ return <View className="rtl:mr-4" />;
1862
1569
  }
1863
1570
  `;
1864
1571
 
1865
- const output = transform(input);
1572
+ const output = transform(input, undefined, true);
1573
+
1574
+ // 'use strict' should be preserved at the top
1575
+ const lines = output.split("\n").filter((l: string) => l.trim());
1576
+ const useStrictIndex = lines.findIndex(
1577
+ (l: string) => l.includes("'use strict'") || l.includes('"use strict"'),
1578
+ );
1579
+ expect(useStrictIndex).toBe(0);
1866
1580
 
1867
- // Should generate Platform.select() even without base classes
1868
- expect(output).toContain("Platform.select");
1869
- expect(output).toContain("_twStyles._ios_p_6");
1870
- expect(output).toContain("_twStyles._android_p_8");
1581
+ // I18nManager variable should work correctly
1582
+ expect(output).toContain("_twIsRTL");
1583
+ expect(output).toContain("I18nManager.isRTL");
1871
1584
  });
1872
1585
 
1873
- it("should allow accessing platform-specific styles manually", () => {
1586
+ it("should expand text-start to directional modifiers", () => {
1874
1587
  const input = `
1875
- import { tw } from '@mgcrea/react-native-tailwind';
1876
-
1877
- function MyComponent() {
1878
- const btnStyles = tw\`bg-blue-500 ios:p-6\`;
1879
- const iosPadding = btnStyles.iosStyle;
1880
- return null;
1588
+ import { Text } from 'react-native';
1589
+ export function Component() {
1590
+ return <Text className="text-start" />;
1881
1591
  }
1882
1592
  `;
1883
1593
 
1884
- const output = transform(input);
1594
+ const output = transform(input, undefined, true);
1595
+
1596
+ // Should have I18nManager import (text-start expands to ltr:/rtl: modifiers)
1597
+ expect(output).toContain("I18nManager");
1885
1598
 
1886
- // Should have iosStyle property available
1887
- expect(output).toContain("iosStyle:");
1888
- expect(output).toContain("_twStyles._ios_p_6");
1599
+ // Should have both ltr and rtl styles
1600
+ expect(output).toContain("_ltr_text_left");
1601
+ expect(output).toContain("_rtl_text_right");
1889
1602
 
1890
- // The actual usage line should be preserved
1891
- expect(output).toContain("btnStyles.iosStyle");
1603
+ // Should have conditionals for both
1604
+ expect(output).toMatch(/_twIsRTL\s*&&/);
1605
+ expect(output).toMatch(/!\s*_twIsRTL\s*&&/);
1892
1606
  });
1893
1607
 
1894
- it("should combine state modifiers with platform modifiers", () => {
1608
+ it("should expand text-end to directional modifiers", () => {
1895
1609
  const input = `
1896
- import { tw } from '@mgcrea/react-native-tailwind';
1897
-
1898
- function MyComponent() {
1899
- const styles = tw\`bg-white active:bg-blue-500 ios:p-6\`;
1900
- return null;
1610
+ import { Text } from 'react-native';
1611
+ export function Component() {
1612
+ return <Text className="text-end" />;
1901
1613
  }
1902
1614
  `;
1903
1615
 
1904
- const output = transform(input);
1616
+ const output = transform(input, undefined, true);
1617
+
1618
+ // Should have I18nManager import
1619
+ expect(output).toContain("I18nManager");
1905
1620
 
1906
- // Should have both activeStyle and platform modifiers
1907
- expect(output).toContain("activeStyle:");
1908
- expect(output).toContain("_twStyles._active_bg_blue_500");
1909
- expect(output).toContain("Platform.select");
1910
- expect(output).toContain("_twStyles._ios_p_6");
1621
+ // text-end expands to ltr:text-right rtl:text-left
1622
+ expect(output).toContain("_ltr_text_right");
1623
+ expect(output).toContain("_rtl_text_left");
1911
1624
  });
1912
1625
  });