@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
@@ -34,6 +34,201 @@ __export(index_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(index_exports);
36
36
 
37
+ // src/babel/config-loader.ts
38
+ var fs = __toESM(require("fs"), 1);
39
+ var path = __toESM(require("path"), 1);
40
+
41
+ // src/utils/flattenColors.ts
42
+ function flattenColors(colors, prefix = "") {
43
+ const result = {};
44
+ for (const [key, value] of Object.entries(colors)) {
45
+ const newKey = key === "DEFAULT" && prefix ? prefix : prefix ? `${prefix}-${key}` : key;
46
+ if (typeof value === "string") {
47
+ result[newKey] = value;
48
+ } else if (typeof value === "object" && value !== null) {
49
+ Object.assign(result, flattenColors(value, newKey));
50
+ }
51
+ }
52
+ return result;
53
+ }
54
+
55
+ // src/babel/config-loader.ts
56
+ var configCache = /* @__PURE__ */ new Map();
57
+ function findTailwindConfig(startDir) {
58
+ let currentDir = startDir;
59
+ const root = path.parse(currentDir).root;
60
+ const configNames = [
61
+ "tailwind.config.mjs",
62
+ "tailwind.config.js",
63
+ "tailwind.config.cjs",
64
+ "tailwind.config.ts"
65
+ ];
66
+ while (currentDir !== root) {
67
+ for (const configName of configNames) {
68
+ const configPath = path.join(currentDir, configName);
69
+ if (fs.existsSync(configPath)) {
70
+ return configPath;
71
+ }
72
+ }
73
+ currentDir = path.dirname(currentDir);
74
+ }
75
+ return null;
76
+ }
77
+ function loadTailwindConfig(configPath) {
78
+ if (configCache.has(configPath)) {
79
+ return configCache.get(configPath);
80
+ }
81
+ try {
82
+ const resolvedPath = require.resolve(configPath);
83
+ delete require.cache[resolvedPath];
84
+ const config = require(configPath);
85
+ const resolved = "default" in config ? config.default : config;
86
+ configCache.set(configPath, resolved);
87
+ return resolved;
88
+ } catch (error) {
89
+ if (process.env.NODE_ENV !== "production") {
90
+ console.warn(`[react-native-tailwind] Failed to load config from ${configPath}:`, error);
91
+ }
92
+ configCache.set(configPath, null);
93
+ return null;
94
+ }
95
+ }
96
+ function extractCustomTheme(filename) {
97
+ const projectDir = path.dirname(filename);
98
+ const configPath = findTailwindConfig(projectDir);
99
+ if (!configPath) {
100
+ return { colors: {}, fontFamily: {}, fontSize: {} };
101
+ }
102
+ const config = loadTailwindConfig(configPath);
103
+ if (!config?.theme) {
104
+ return { colors: {}, fontFamily: {}, fontSize: {} };
105
+ }
106
+ if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
107
+ console.warn(
108
+ "[react-native-tailwind] Using theme.colors will override all default colors. Use theme.extend.colors to add custom colors while keeping defaults."
109
+ );
110
+ }
111
+ const colors = config.theme.extend?.colors ?? config.theme.colors ?? {};
112
+ if (config.theme.fontFamily && !config.theme.extend?.fontFamily && process.env.NODE_ENV !== "production") {
113
+ console.warn(
114
+ "[react-native-tailwind] Using theme.fontFamily will override all default font families. Use theme.extend.fontFamily to add custom fonts while keeping defaults."
115
+ );
116
+ }
117
+ const fontFamily = config.theme.extend?.fontFamily ?? config.theme.fontFamily ?? {};
118
+ const fontFamilyResult = {};
119
+ for (const [key, value] of Object.entries(fontFamily)) {
120
+ if (Array.isArray(value)) {
121
+ fontFamilyResult[key] = value[0];
122
+ } else {
123
+ fontFamilyResult[key] = value;
124
+ }
125
+ }
126
+ if (config.theme.fontSize && !config.theme.extend?.fontSize && process.env.NODE_ENV !== "production") {
127
+ console.warn(
128
+ "[react-native-tailwind] Using theme.fontSize will override all default font sizes. Use theme.extend.fontSize to add custom font sizes while keeping defaults."
129
+ );
130
+ }
131
+ const fontSize = config.theme.extend?.fontSize ?? config.theme.fontSize ?? {};
132
+ const fontSizeResult = {};
133
+ for (const [key, value] of Object.entries(fontSize)) {
134
+ if (typeof value === "number") {
135
+ fontSizeResult[key] = value;
136
+ } else if (typeof value === "string") {
137
+ const parsed = parseFloat(value.replace(/px$/, ""));
138
+ if (!isNaN(parsed)) {
139
+ fontSizeResult[key] = parsed;
140
+ } else {
141
+ if (process.env.NODE_ENV !== "production") {
142
+ console.warn(
143
+ `[react-native-tailwind] Invalid fontSize value for "${key}": ${value}. Expected number or string like "18px".`
144
+ );
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return {
150
+ colors: flattenColors(colors),
151
+ fontFamily: fontFamilyResult,
152
+ fontSize: fontSizeResult
153
+ };
154
+ }
155
+
156
+ // src/babel/utils/attributeMatchers.ts
157
+ var DEFAULT_CLASS_ATTRIBUTES = [
158
+ "className",
159
+ "contentContainerClassName",
160
+ "columnWrapperClassName",
161
+ "ListHeaderComponentClassName",
162
+ "ListFooterComponentClassName"
163
+ ];
164
+ function buildAttributeMatchers(attributes) {
165
+ const exactMatches = /* @__PURE__ */ new Set();
166
+ const patterns = [];
167
+ for (const attr of attributes) {
168
+ if (attr.includes("*")) {
169
+ const regexPattern = "^" + attr.replace(/\*/g, ".*") + "$";
170
+ patterns.push(new RegExp(regexPattern));
171
+ } else {
172
+ exactMatches.add(attr);
173
+ }
174
+ }
175
+ return { exactMatches, patterns };
176
+ }
177
+ function isAttributeSupported(attributeName, exactMatches, patterns) {
178
+ if (exactMatches.has(attributeName)) {
179
+ return true;
180
+ }
181
+ for (const pattern of patterns) {
182
+ if (pattern.test(attributeName)) {
183
+ return true;
184
+ }
185
+ }
186
+ return false;
187
+ }
188
+ function getTargetStyleProp(attributeName) {
189
+ return attributeName.endsWith("ClassName") ? attributeName.replace("ClassName", "Style") : "style";
190
+ }
191
+
192
+ // src/babel/plugin/state.ts
193
+ var DEFAULT_STYLES_IDENTIFIER = "_twStyles";
194
+ function createInitialState(options, filename, colorSchemeImportSource, colorSchemeHookName, schemeModifierConfig) {
195
+ const attributes = options?.attributes ?? [...DEFAULT_CLASS_ATTRIBUTES];
196
+ const { exactMatches, patterns } = buildAttributeMatchers(attributes);
197
+ const stylesIdentifier = options?.stylesIdentifier ?? DEFAULT_STYLES_IDENTIFIER;
198
+ const customTheme = extractCustomTheme(filename);
199
+ return {
200
+ styleRegistry: /* @__PURE__ */ new Map(),
201
+ hasClassNames: false,
202
+ hasStyleSheetImport: false,
203
+ hasPlatformImport: false,
204
+ needsPlatformImport: false,
205
+ hasColorSchemeImport: false,
206
+ needsColorSchemeImport: false,
207
+ colorSchemeVariableName: "_twColorScheme",
208
+ colorSchemeImportSource,
209
+ colorSchemeHookName,
210
+ colorSchemeLocalIdentifier: void 0,
211
+ hasWindowDimensionsImport: false,
212
+ needsWindowDimensionsImport: false,
213
+ windowDimensionsVariableName: "_twDimensions",
214
+ windowDimensionsLocalIdentifier: void 0,
215
+ hasI18nManagerImport: false,
216
+ needsI18nManagerImport: false,
217
+ i18nManagerVariableName: "_twIsRTL",
218
+ i18nManagerLocalIdentifier: void 0,
219
+ customTheme,
220
+ schemeModifierConfig,
221
+ supportedAttributes: exactMatches,
222
+ attributePatterns: patterns,
223
+ stylesIdentifier,
224
+ twImportNames: /* @__PURE__ */ new Set(),
225
+ hasTwImport: false,
226
+ reactNativeImportPath: void 0,
227
+ functionComponentsNeedingColorScheme: /* @__PURE__ */ new Set(),
228
+ functionComponentsNeedingWindowDimensions: /* @__PURE__ */ new Set()
229
+ };
230
+ }
231
+
37
232
  // src/parser/aspectRatio.ts
38
233
  var ASPECT_RATIO_PRESETS = {
39
234
  "aspect-auto": void 0,
@@ -77,187 +272,6 @@ function parseAspectRatio(cls) {
77
272
  return null;
78
273
  }
79
274
 
80
- // src/parser/borders.ts
81
- var BORDER_WIDTH_SCALE = {
82
- "": 1,
83
- "0": 0,
84
- "2": 2,
85
- "4": 4,
86
- "8": 8
87
- };
88
- var BORDER_RADIUS_SCALE = {
89
- none: 0,
90
- sm: 2,
91
- "": 4,
92
- md: 6,
93
- lg: 8,
94
- xl: 12,
95
- "2xl": 16,
96
- "3xl": 24,
97
- full: 9999
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
- }
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" };
151
- if (cls.startsWith("border-")) {
152
- return parseBorderWidth(cls);
153
- }
154
- if (cls === "border") {
155
- return { borderWidth: 1 };
156
- }
157
- if (cls.startsWith("rounded")) {
158
- return parseBorderRadius(cls);
159
- }
160
- return null;
161
- }
162
- function parseBorderWidth(cls) {
163
- const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
164
- if (dirMatch) {
165
- const dir = dirMatch[1];
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;
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;
179
- }
180
- const allMatch = cls.match(/^border-(\d+)$/);
181
- if (allMatch) {
182
- const value = BORDER_WIDTH_SCALE[allMatch[1]];
183
- if (value !== void 0) {
184
- return { borderWidth: value };
185
- }
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
- }
194
- return null;
195
- }
196
- function parseBorderRadius(cls) {
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 };
222
- }
223
- return null;
224
- }
225
- const sideMatch = rest.match(/^([trbl])(?:-(.+))?$/);
226
- if (sideMatch) {
227
- const side = sideMatch[1];
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
- }
240
- if (value !== void 0) {
241
- const result = {};
242
- BORDER_RADIUS_SIDE_MAP[side].forEach((prop) => result[prop] = value);
243
- return result;
244
- }
245
- return null;
246
- }
247
- if (rest.startsWith("[")) {
248
- const arbitraryValue = parseArbitraryBorderRadius(rest);
249
- if (arbitraryValue !== null) {
250
- return { borderRadius: arbitraryValue };
251
- }
252
- return null;
253
- }
254
- const scaleValue = BORDER_RADIUS_SCALE[rest];
255
- if (scaleValue !== void 0) {
256
- return { borderRadius: scaleValue };
257
- }
258
- return null;
259
- }
260
-
261
275
  // src/config/tailwind.ts
262
276
  var TAILWIND_COLORS = {
263
277
  red: {
@@ -548,20 +562,6 @@ var TAILWIND_COLORS = {
548
562
  }
549
563
  };
550
564
 
551
- // src/utils/flattenColors.ts
552
- function flattenColors(colors, prefix = "") {
553
- const result = {};
554
- for (const [key, value] of Object.entries(colors)) {
555
- const newKey = key === "DEFAULT" && prefix ? prefix : prefix ? `${prefix}-${key}` : key;
556
- if (typeof value === "string") {
557
- result[newKey] = value;
558
- } else if (typeof value === "object" && value !== null) {
559
- Object.assign(result, flattenColors(value, newKey));
560
- }
561
- }
562
- return result;
563
- }
564
-
565
565
  // src/parser/colors.ts
566
566
  var COLORS = {
567
567
  ...flattenColors(TAILWIND_COLORS),
@@ -663,101 +663,345 @@ function parseColor(cls, customColors) {
663
663
  return { borderColor: color };
664
664
  }
665
665
  }
666
+ const dirBorderMatch = cls.match(/^border-([trblxy])-(.+)$/);
667
+ if (dirBorderMatch) {
668
+ const dir = dirBorderMatch[1];
669
+ const colorKey = dirBorderMatch[2];
670
+ if (colorKey.startsWith("[") && !colorKey.startsWith("[#")) {
671
+ return null;
672
+ }
673
+ const color = parseColorWithOpacity(colorKey);
674
+ if (color) {
675
+ if (dir === "x") {
676
+ return {
677
+ borderLeftColor: color,
678
+ borderRightColor: color
679
+ };
680
+ }
681
+ if (dir === "y") {
682
+ return {
683
+ borderTopColor: color,
684
+ borderBottomColor: color
685
+ };
686
+ }
687
+ const propMap = {
688
+ t: "borderTopColor",
689
+ r: "borderRightColor",
690
+ b: "borderBottomColor",
691
+ l: "borderLeftColor"
692
+ };
693
+ return { [propMap[dir]]: color };
694
+ }
695
+ }
666
696
  return null;
667
697
  }
668
698
 
669
- // src/parser/layout.ts
670
- function parseArbitraryInset(value) {
671
- const pxMatch = value.match(/^\[(-?\d+)(?:px)?\]$/);
699
+ // src/parser/borders.ts
700
+ var BORDER_WIDTH_SCALE = {
701
+ "": 1,
702
+ "0": 0,
703
+ "2": 2,
704
+ "4": 4,
705
+ "8": 8
706
+ };
707
+ var BORDER_RADIUS_SCALE = {
708
+ none: 0,
709
+ sm: 2,
710
+ "": 4,
711
+ md: 6,
712
+ lg: 8,
713
+ xl: 12,
714
+ "2xl": 16,
715
+ "3xl": 24,
716
+ full: 9999
717
+ };
718
+ var BORDER_WIDTH_PROP_MAP = {
719
+ t: "borderTopWidth",
720
+ r: "borderRightWidth",
721
+ b: "borderBottomWidth",
722
+ l: "borderLeftWidth",
723
+ s: "borderStartWidth",
724
+ e: "borderEndWidth"
725
+ };
726
+ var BORDER_RADIUS_CORNER_MAP = {
727
+ tl: "borderTopLeftRadius",
728
+ tr: "borderTopRightRadius",
729
+ bl: "borderBottomLeftRadius",
730
+ br: "borderBottomRightRadius"
731
+ };
732
+ var BORDER_RADIUS_LOGICAL_CORNER_MAP = {
733
+ ss: "borderTopStartRadius",
734
+ se: "borderTopEndRadius",
735
+ es: "borderBottomStartRadius",
736
+ ee: "borderBottomEndRadius"
737
+ };
738
+ var BORDER_RADIUS_SIDE_MAP = {
739
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
740
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
741
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
742
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
743
+ s: ["borderTopStartRadius", "borderBottomStartRadius"],
744
+ e: ["borderTopEndRadius", "borderBottomEndRadius"]
745
+ };
746
+ function parseArbitraryBorderWidth(value) {
747
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
672
748
  if (pxMatch) {
673
749
  return parseInt(pxMatch[1], 10);
674
750
  }
675
- const percentMatch = value.match(/^\[(-?\d+(?:\.\d+)?)%\]$/);
676
- if (percentMatch) {
677
- return `${percentMatch[1]}%`;
678
- }
679
751
  if (value.startsWith("[") && value.endsWith("]")) {
680
752
  if (process.env.NODE_ENV !== "production") {
681
753
  console.warn(
682
- `[react-native-tailwind] Unsupported arbitrary inset unit: ${value}. Only px and % are supported.`
754
+ `[react-native-tailwind] Unsupported arbitrary border width value: ${value}. Only px values are supported (e.g., [8px] or [8]).`
683
755
  );
684
756
  }
685
757
  return null;
686
758
  }
687
759
  return null;
688
760
  }
689
- function parseArbitraryZIndex(value) {
690
- const zMatch = value.match(/^\[(-?\d+)\]$/);
691
- if (zMatch) {
692
- return parseInt(zMatch[1], 10);
761
+ function parseArbitraryBorderRadius(value) {
762
+ const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
763
+ if (pxMatch) {
764
+ return parseInt(pxMatch[1], 10);
693
765
  }
694
766
  if (value.startsWith("[") && value.endsWith("]")) {
695
767
  if (process.env.NODE_ENV !== "production") {
696
768
  console.warn(
697
- `[react-native-tailwind] Invalid arbitrary z-index: ${value}. Only integers are supported.`
769
+ `[react-native-tailwind] Unsupported arbitrary border radius value: ${value}. Only px values are supported (e.g., [12px] or [12]).`
698
770
  );
699
771
  }
700
772
  return null;
701
773
  }
702
774
  return null;
703
775
  }
704
- function parseArbitraryGrowShrink(value) {
705
- const match = value.match(/^\[(\d+(?:\.\d+)?|\.\d+)\]$/);
706
- if (match) {
707
- return parseFloat(match[1]);
776
+ function parseBorder(cls, customColors) {
777
+ if (cls === "border-solid") return { borderStyle: "solid" };
778
+ if (cls === "border-dotted") return { borderStyle: "dotted" };
779
+ if (cls === "border-dashed") return { borderStyle: "dashed" };
780
+ if (cls.startsWith("border-")) {
781
+ return parseBorderWidth(cls, customColors);
708
782
  }
709
- if (value.startsWith("[") && value.endsWith("]")) {
710
- if (process.env.NODE_ENV !== "production") {
711
- console.warn(
712
- `[react-native-tailwind] Invalid arbitrary grow/shrink value: ${value}. Only non-negative numbers are supported (e.g., [1.5], [2], [0.5], [.5]).`
713
- );
783
+ if (cls === "border") {
784
+ return { borderWidth: 1 };
785
+ }
786
+ if (cls.startsWith("rounded")) {
787
+ return parseBorderRadius(cls);
788
+ }
789
+ return null;
790
+ }
791
+ function parseBorderWidth(cls, customColors) {
792
+ const dirMatch = cls.match(/^border-([trblse])(?:-(.+))?$/);
793
+ if (dirMatch) {
794
+ const dir = dirMatch[1];
795
+ const valueStr = dirMatch[2] || "";
796
+ if (valueStr && dir !== "s" && dir !== "e") {
797
+ const colorResult = parseColor(cls, customColors);
798
+ if (colorResult !== null) {
799
+ return null;
800
+ }
801
+ }
802
+ if (valueStr.startsWith("[")) {
803
+ const arbitraryValue = parseArbitraryBorderWidth(valueStr);
804
+ if (arbitraryValue !== null) {
805
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: arbitraryValue };
806
+ }
807
+ return null;
808
+ }
809
+ const scaleValue = BORDER_WIDTH_SCALE[valueStr];
810
+ if (scaleValue !== void 0) {
811
+ return { [BORDER_WIDTH_PROP_MAP[dir]]: scaleValue };
714
812
  }
715
813
  return null;
716
814
  }
815
+ const allMatch = cls.match(/^border-(\d+)$/);
816
+ if (allMatch) {
817
+ const value = BORDER_WIDTH_SCALE[allMatch[1]];
818
+ if (value !== void 0) {
819
+ return { borderWidth: value };
820
+ }
821
+ }
822
+ const allArbMatch = cls.match(/^border-(\[.+\])$/);
823
+ if (allArbMatch) {
824
+ const arbitraryValue = parseArbitraryBorderWidth(allArbMatch[1]);
825
+ if (arbitraryValue !== null) {
826
+ return { borderWidth: arbitraryValue };
827
+ }
828
+ }
717
829
  return null;
718
830
  }
719
- var DISPLAY_MAP = {
720
- flex: { display: "flex" },
721
- hidden: { display: "none" }
722
- };
723
- var FLEX_DIRECTION_MAP = {
724
- "flex-row": { flexDirection: "row" },
725
- "flex-row-reverse": { flexDirection: "row-reverse" },
726
- "flex-col": { flexDirection: "column" },
727
- "flex-col-reverse": { flexDirection: "column-reverse" }
728
- };
729
- var FLEX_WRAP_MAP = {
730
- "flex-wrap": { flexWrap: "wrap" },
731
- "flex-wrap-reverse": { flexWrap: "wrap-reverse" },
732
- "flex-nowrap": { flexWrap: "nowrap" }
733
- };
734
- var FLEX_MAP = {
735
- "flex-1": { flex: 1 },
736
- "flex-auto": { flex: 1 },
737
- "flex-none": { flex: 0 }
738
- };
739
- var GROW_SHRINK_MAP = {
740
- grow: { flexGrow: 1 },
741
- "grow-0": { flexGrow: 0 },
742
- shrink: { flexShrink: 1 },
743
- "shrink-0": { flexShrink: 0 },
744
- // CSS-style aliases
745
- "flex-grow": { flexGrow: 1 },
746
- "flex-grow-0": { flexGrow: 0 },
747
- "flex-shrink": { flexShrink: 1 },
748
- "flex-shrink-0": { flexShrink: 0 }
749
- };
750
- var JUSTIFY_CONTENT_MAP = {
751
- "justify-start": { justifyContent: "flex-start" },
752
- "justify-end": { justifyContent: "flex-end" },
753
- "justify-center": { justifyContent: "center" },
754
- "justify-between": { justifyContent: "space-between" },
755
- "justify-around": { justifyContent: "space-around" },
756
- "justify-evenly": { justifyContent: "space-evenly" }
757
- };
758
- var ALIGN_ITEMS_MAP = {
759
- "items-start": { alignItems: "flex-start" },
760
- "items-end": { alignItems: "flex-end" },
831
+ function parseBorderRadius(cls) {
832
+ const withoutPrefix = cls.substring(7);
833
+ if (withoutPrefix === "") {
834
+ return { borderRadius: BORDER_RADIUS_SCALE[""] };
835
+ }
836
+ if (!withoutPrefix.startsWith("-")) {
837
+ return null;
838
+ }
839
+ const rest = withoutPrefix.substring(1);
840
+ if (rest === "") {
841
+ return null;
842
+ }
843
+ const cornerMatch = rest.match(/^(tl|tr|bl|br)(?:-(.+))?$/);
844
+ if (cornerMatch) {
845
+ const corner = cornerMatch[1];
846
+ const valueStr = cornerMatch[2] || "";
847
+ if (valueStr.startsWith("[")) {
848
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
849
+ if (arbitraryValue !== null) {
850
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: arbitraryValue };
851
+ }
852
+ return null;
853
+ }
854
+ const scaleValue2 = BORDER_RADIUS_SCALE[valueStr];
855
+ if (scaleValue2 !== void 0) {
856
+ return { [BORDER_RADIUS_CORNER_MAP[corner]]: scaleValue2 };
857
+ }
858
+ return null;
859
+ }
860
+ const logicalCornerMatch = rest.match(/^(ss|se|es|ee)(?:-(.+))?$/);
861
+ if (logicalCornerMatch) {
862
+ const corner = logicalCornerMatch[1];
863
+ const valueStr = logicalCornerMatch[2] || "";
864
+ if (valueStr.startsWith("[")) {
865
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
866
+ if (arbitraryValue !== null) {
867
+ return { [BORDER_RADIUS_LOGICAL_CORNER_MAP[corner]]: arbitraryValue };
868
+ }
869
+ return null;
870
+ }
871
+ const scaleValue2 = BORDER_RADIUS_SCALE[valueStr];
872
+ if (scaleValue2 !== void 0) {
873
+ return { [BORDER_RADIUS_LOGICAL_CORNER_MAP[corner]]: scaleValue2 };
874
+ }
875
+ return null;
876
+ }
877
+ const sideMatch = rest.match(/^([trblse])(?:-(.+))?$/);
878
+ if (sideMatch) {
879
+ const side = sideMatch[1];
880
+ const valueStr = sideMatch[2] || "";
881
+ let value;
882
+ if (valueStr.startsWith("[")) {
883
+ const arbitraryValue = parseArbitraryBorderRadius(valueStr);
884
+ if (arbitraryValue !== null) {
885
+ value = arbitraryValue;
886
+ } else {
887
+ return null;
888
+ }
889
+ } else {
890
+ value = BORDER_RADIUS_SCALE[valueStr];
891
+ }
892
+ if (value !== void 0) {
893
+ const result = {};
894
+ BORDER_RADIUS_SIDE_MAP[side].forEach((prop) => result[prop] = value);
895
+ return result;
896
+ }
897
+ return null;
898
+ }
899
+ if (rest.startsWith("[")) {
900
+ const arbitraryValue = parseArbitraryBorderRadius(rest);
901
+ if (arbitraryValue !== null) {
902
+ return { borderRadius: arbitraryValue };
903
+ }
904
+ return null;
905
+ }
906
+ const scaleValue = BORDER_RADIUS_SCALE[rest];
907
+ if (scaleValue !== void 0) {
908
+ return { borderRadius: scaleValue };
909
+ }
910
+ return null;
911
+ }
912
+
913
+ // src/parser/layout.ts
914
+ function parseArbitraryInset(value) {
915
+ const pxMatch = value.match(/^\[(-?\d+)(?:px)?\]$/);
916
+ if (pxMatch) {
917
+ return parseInt(pxMatch[1], 10);
918
+ }
919
+ const percentMatch = value.match(/^\[(-?\d+(?:\.\d+)?)%\]$/);
920
+ if (percentMatch) {
921
+ return `${percentMatch[1]}%`;
922
+ }
923
+ if (value.startsWith("[") && value.endsWith("]")) {
924
+ if (process.env.NODE_ENV !== "production") {
925
+ console.warn(
926
+ `[react-native-tailwind] Unsupported arbitrary inset unit: ${value}. Only px and % are supported.`
927
+ );
928
+ }
929
+ return null;
930
+ }
931
+ return null;
932
+ }
933
+ function parseArbitraryZIndex(value) {
934
+ const zMatch = value.match(/^\[(-?\d+)\]$/);
935
+ if (zMatch) {
936
+ return parseInt(zMatch[1], 10);
937
+ }
938
+ if (value.startsWith("[") && value.endsWith("]")) {
939
+ if (process.env.NODE_ENV !== "production") {
940
+ console.warn(
941
+ `[react-native-tailwind] Invalid arbitrary z-index: ${value}. Only integers are supported.`
942
+ );
943
+ }
944
+ return null;
945
+ }
946
+ return null;
947
+ }
948
+ function parseArbitraryGrowShrink(value) {
949
+ const match = value.match(/^\[(\d+(?:\.\d+)?|\.\d+)\]$/);
950
+ if (match) {
951
+ return parseFloat(match[1]);
952
+ }
953
+ if (value.startsWith("[") && value.endsWith("]")) {
954
+ if (process.env.NODE_ENV !== "production") {
955
+ console.warn(
956
+ `[react-native-tailwind] Invalid arbitrary grow/shrink value: ${value}. Only non-negative numbers are supported (e.g., [1.5], [2], [0.5], [.5]).`
957
+ );
958
+ }
959
+ return null;
960
+ }
961
+ return null;
962
+ }
963
+ var DISPLAY_MAP = {
964
+ flex: { display: "flex" },
965
+ hidden: { display: "none" }
966
+ };
967
+ var FLEX_DIRECTION_MAP = {
968
+ "flex-row": { flexDirection: "row" },
969
+ "flex-row-reverse": { flexDirection: "row-reverse" },
970
+ "flex-col": { flexDirection: "column" },
971
+ "flex-col-reverse": { flexDirection: "column-reverse" }
972
+ };
973
+ var FLEX_WRAP_MAP = {
974
+ "flex-wrap": { flexWrap: "wrap" },
975
+ "flex-wrap-reverse": { flexWrap: "wrap-reverse" },
976
+ "flex-nowrap": { flexWrap: "nowrap" }
977
+ };
978
+ var FLEX_MAP = {
979
+ "flex-1": { flex: 1 },
980
+ "flex-auto": { flex: 1 },
981
+ "flex-none": { flex: 0 }
982
+ };
983
+ var GROW_SHRINK_MAP = {
984
+ grow: { flexGrow: 1 },
985
+ "grow-0": { flexGrow: 0 },
986
+ shrink: { flexShrink: 1 },
987
+ "shrink-0": { flexShrink: 0 },
988
+ // CSS-style aliases
989
+ "flex-grow": { flexGrow: 1 },
990
+ "flex-grow-0": { flexGrow: 0 },
991
+ "flex-shrink": { flexShrink: 1 },
992
+ "flex-shrink-0": { flexShrink: 0 }
993
+ };
994
+ var JUSTIFY_CONTENT_MAP = {
995
+ "justify-start": { justifyContent: "flex-start" },
996
+ "justify-end": { justifyContent: "flex-end" },
997
+ "justify-center": { justifyContent: "center" },
998
+ "justify-between": { justifyContent: "space-between" },
999
+ "justify-around": { justifyContent: "space-around" },
1000
+ "justify-evenly": { justifyContent: "space-evenly" }
1001
+ };
1002
+ var ALIGN_ITEMS_MAP = {
1003
+ "items-start": { alignItems: "flex-start" },
1004
+ "items-end": { alignItems: "flex-end" },
761
1005
  "items-center": { alignItems: "center" },
762
1006
  "items-baseline": { alignItems: "baseline" },
763
1007
  "items-stretch": { alignItems: "stretch" }
@@ -907,6 +1151,52 @@ function parseLayout(cls) {
907
1151
  return { left: leftValue };
908
1152
  }
909
1153
  }
1154
+ const startMatch = cls.match(/^(-?)start-(.+)$/);
1155
+ if (startMatch) {
1156
+ const [, negPrefix, startKey] = startMatch;
1157
+ const isNegative = negPrefix === "-";
1158
+ if (startKey === "auto") {
1159
+ return {};
1160
+ }
1161
+ const arbitraryStart = parseArbitraryInset(startKey);
1162
+ if (arbitraryStart !== null) {
1163
+ if (typeof arbitraryStart === "number") {
1164
+ return { start: isNegative ? -arbitraryStart : arbitraryStart };
1165
+ }
1166
+ if (isNegative && arbitraryStart.endsWith("%")) {
1167
+ const numValue = parseFloat(arbitraryStart);
1168
+ return { start: `${-numValue}%` };
1169
+ }
1170
+ return { start: arbitraryStart };
1171
+ }
1172
+ const startValue = INSET_SCALE[startKey];
1173
+ if (startValue !== void 0) {
1174
+ return { start: isNegative ? -startValue : startValue };
1175
+ }
1176
+ }
1177
+ const endMatch = cls.match(/^(-?)end-(.+)$/);
1178
+ if (endMatch) {
1179
+ const [, negPrefix, endKey] = endMatch;
1180
+ const isNegative = negPrefix === "-";
1181
+ if (endKey === "auto") {
1182
+ return {};
1183
+ }
1184
+ const arbitraryEnd = parseArbitraryInset(endKey);
1185
+ if (arbitraryEnd !== null) {
1186
+ if (typeof arbitraryEnd === "number") {
1187
+ return { end: isNegative ? -arbitraryEnd : arbitraryEnd };
1188
+ }
1189
+ if (isNegative && arbitraryEnd.endsWith("%")) {
1190
+ const numValue = parseFloat(arbitraryEnd);
1191
+ return { end: `${-numValue}%` };
1192
+ }
1193
+ return { end: arbitraryEnd };
1194
+ }
1195
+ const endValue = INSET_SCALE[endKey];
1196
+ if (endValue !== void 0) {
1197
+ return { end: isNegative ? -endValue : endValue };
1198
+ }
1199
+ }
910
1200
  if (cls.startsWith("inset-x-")) {
911
1201
  const insetKey = cls.substring(8);
912
1202
  const arbitraryInset = parseArbitraryInset(insetKey);
@@ -929,6 +1219,28 @@ function parseLayout(cls) {
929
1219
  return { top: insetValue, bottom: insetValue };
930
1220
  }
931
1221
  }
1222
+ if (cls.startsWith("inset-s-")) {
1223
+ const insetKey = cls.substring(8);
1224
+ const arbitraryInset = parseArbitraryInset(insetKey);
1225
+ if (arbitraryInset !== null) {
1226
+ return { start: arbitraryInset };
1227
+ }
1228
+ const insetValue = INSET_SCALE[insetKey];
1229
+ if (insetValue !== void 0) {
1230
+ return { start: insetValue };
1231
+ }
1232
+ }
1233
+ if (cls.startsWith("inset-e-")) {
1234
+ const insetKey = cls.substring(8);
1235
+ const arbitraryInset = parseArbitraryInset(insetKey);
1236
+ if (arbitraryInset !== null) {
1237
+ return { end: arbitraryInset };
1238
+ }
1239
+ const insetValue = INSET_SCALE[insetKey];
1240
+ if (insetValue !== void 0) {
1241
+ return { end: insetValue };
1242
+ }
1243
+ }
932
1244
  if (cls.startsWith("inset-")) {
933
1245
  const insetKey = cls.substring(6);
934
1246
  const arbitraryInset = parseArbitraryInset(insetKey);
@@ -1018,6 +1330,9 @@ function parseShadow(cls) {
1018
1330
  return null;
1019
1331
  }
1020
1332
 
1333
+ // src/config/markers.ts
1334
+ var RUNTIME_DIMENSIONS_MARKER = "{{RUNTIME:dimensions.";
1335
+
1021
1336
  // src/parser/sizing.ts
1022
1337
  var SIZE_SCALE = {
1023
1338
  0: 0,
@@ -1095,6 +1410,9 @@ function parseArbitrarySize(value) {
1095
1410
  function parseSizing(cls) {
1096
1411
  if (cls.startsWith("w-")) {
1097
1412
  const sizeKey = cls.substring(2);
1413
+ if (sizeKey === "screen") {
1414
+ return { width: `${RUNTIME_DIMENSIONS_MARKER}width}}` };
1415
+ }
1098
1416
  const arbitrarySize = parseArbitrarySize(sizeKey);
1099
1417
  if (arbitrarySize !== null) {
1100
1418
  return { width: arbitrarySize };
@@ -1113,6 +1431,9 @@ function parseSizing(cls) {
1113
1431
  }
1114
1432
  if (cls.startsWith("h-")) {
1115
1433
  const sizeKey = cls.substring(2);
1434
+ if (sizeKey === "screen") {
1435
+ return { height: `${RUNTIME_DIMENSIONS_MARKER}height}}` };
1436
+ }
1116
1437
  const arbitrarySize = parseArbitrarySize(sizeKey);
1117
1438
  if (arbitrarySize !== null) {
1118
1439
  return { height: arbitrarySize };
@@ -1245,7 +1566,7 @@ function parseArbitrarySpacing(value) {
1245
1566
  return null;
1246
1567
  }
1247
1568
  function parseSpacing(cls) {
1248
- const marginMatch = cls.match(/^(-?)m([xytrbls]?)-(.+)$/);
1569
+ const marginMatch = cls.match(/^(-?)m([xytrblse]?)-(.+)$/);
1249
1570
  if (marginMatch) {
1250
1571
  const [, negativePrefix, dir, valueStr] = marginMatch;
1251
1572
  const isNegative = negativePrefix === "-";
@@ -1260,7 +1581,7 @@ function parseSpacing(cls) {
1260
1581
  return getMarginStyle(dir, finalValue);
1261
1582
  }
1262
1583
  }
1263
- const paddingMatch = cls.match(/^p([xytrbls]?)-(.+)$/);
1584
+ const paddingMatch = cls.match(/^p([xytrblse]?)-(.+)$/);
1264
1585
  if (paddingMatch) {
1265
1586
  const [, dir, valueStr] = paddingMatch;
1266
1587
  const arbitraryValue = parseArbitrarySpacing(valueStr);
@@ -1302,6 +1623,10 @@ function getMarginStyle(dir, value) {
1302
1623
  return { marginBottom: value };
1303
1624
  case "l":
1304
1625
  return { marginLeft: value };
1626
+ case "s":
1627
+ return { marginStart: value };
1628
+ case "e":
1629
+ return { marginEnd: value };
1305
1630
  default:
1306
1631
  return {};
1307
1632
  }
@@ -1322,6 +1647,10 @@ function getPaddingStyle(dir, value) {
1322
1647
  return { paddingBottom: value };
1323
1648
  case "l":
1324
1649
  return { paddingLeft: value };
1650
+ case "s":
1651
+ return { paddingStart: value };
1652
+ case "e":
1653
+ return { paddingEnd: value };
1325
1654
  default:
1326
1655
  return {};
1327
1656
  }
@@ -1810,11 +2139,13 @@ var STATE_MODIFIERS = [
1810
2139
  var PLATFORM_MODIFIERS = ["ios", "android", "web"];
1811
2140
  var COLOR_SCHEME_MODIFIERS = ["dark", "light"];
1812
2141
  var SCHEME_MODIFIERS = ["scheme"];
2142
+ var DIRECTIONAL_MODIFIERS = ["rtl", "ltr"];
1813
2143
  var SUPPORTED_MODIFIERS = [
1814
2144
  ...STATE_MODIFIERS,
1815
2145
  ...PLATFORM_MODIFIERS,
1816
2146
  ...COLOR_SCHEME_MODIFIERS,
1817
- ...SCHEME_MODIFIERS
2147
+ ...SCHEME_MODIFIERS,
2148
+ ...DIRECTIONAL_MODIFIERS
1818
2149
  ];
1819
2150
  function parseModifier(cls) {
1820
2151
  const colonIndex = cls.indexOf(":");
@@ -1849,6 +2180,9 @@ function isColorSchemeModifier(modifier) {
1849
2180
  function isSchemeModifier(modifier) {
1850
2181
  return SCHEME_MODIFIERS.includes(modifier);
1851
2182
  }
2183
+ function isDirectionalModifier(modifier) {
2184
+ return DIRECTIONAL_MODIFIERS.includes(modifier);
2185
+ }
1852
2186
  function isColorClass(className) {
1853
2187
  return className.startsWith("text-") || className.startsWith("bg-") || className.startsWith("border-");
1854
2188
  }
@@ -1893,11 +2227,24 @@ function expandSchemeModifier(schemeModifier, customColors, darkSuffix = "-dark"
1893
2227
  }
1894
2228
  ];
1895
2229
  }
2230
+ var DIRECTIONAL_TEXT_ALIGN_EXPANSIONS = {
2231
+ "text-start": { ltr: "text-left", rtl: "text-right" },
2232
+ "text-end": { ltr: "text-right", rtl: "text-left" }
2233
+ };
2234
+ function getDirectionalExpansion(cls) {
2235
+ return DIRECTIONAL_TEXT_ALIGN_EXPANSIONS[cls];
2236
+ }
1896
2237
  function splitModifierClasses(className) {
1897
2238
  const classes = className.trim().split(/\s+/).filter(Boolean);
1898
2239
  const baseClasses = [];
1899
2240
  const modifierClasses = [];
1900
2241
  for (const cls of classes) {
2242
+ const directionalExpansion = getDirectionalExpansion(cls);
2243
+ if (directionalExpansion) {
2244
+ modifierClasses.push({ modifier: "ltr", baseClass: directionalExpansion.ltr });
2245
+ modifierClasses.push({ modifier: "rtl", baseClass: directionalExpansion.rtl });
2246
+ continue;
2247
+ }
1901
2248
  const parsed = parseModifier(cls);
1902
2249
  if (parsed) {
1903
2250
  modifierClasses.push(parsed);
@@ -1921,7 +2268,7 @@ function parseClassName(className, customTheme) {
1921
2268
  function parseClass(cls, customTheme) {
1922
2269
  const parsers = [
1923
2270
  parseSpacing,
1924
- parseBorder,
2271
+ (cls2) => parseBorder(cls2, customTheme?.colors),
1925
2272
  (cls2) => parseColor(cls2, customTheme?.colors),
1926
2273
  parseLayout,
1927
2274
  (cls2) => parseTypography(cls2, customTheme?.fontFamily, customTheme?.fontSize),
@@ -1949,143 +2296,49 @@ function generateStyleKey(className) {
1949
2296
  return key;
1950
2297
  }
1951
2298
 
1952
- // src/babel/config-loader.ts
1953
- var fs = __toESM(require("fs"), 1);
1954
- var path = __toESM(require("path"), 1);
1955
- var configCache = /* @__PURE__ */ new Map();
1956
- function findTailwindConfig(startDir) {
1957
- let currentDir = startDir;
1958
- const root = path.parse(currentDir).root;
1959
- const configNames = [
1960
- "tailwind.config.mjs",
1961
- "tailwind.config.js",
1962
- "tailwind.config.cjs",
1963
- "tailwind.config.ts"
1964
- ];
1965
- while (currentDir !== root) {
1966
- for (const configName of configNames) {
1967
- const configPath = path.join(currentDir, configName);
1968
- if (fs.existsSync(configPath)) {
1969
- return configPath;
2299
+ // src/babel/utils/windowDimensionsProcessing.ts
2300
+ function hasRuntimeDimensions(styleObject) {
2301
+ return Object.values(styleObject).some(
2302
+ (value) => typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)
2303
+ );
2304
+ }
2305
+ function createRuntimeDimensionObject(styleObject, state, t) {
2306
+ state.needsWindowDimensionsImport = true;
2307
+ const properties = [];
2308
+ for (const [key, value] of Object.entries(styleObject)) {
2309
+ let valueNode;
2310
+ if (typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)) {
2311
+ const match = value.match(/dimensions\.(\w+)/);
2312
+ const prop = match?.[1];
2313
+ if (prop) {
2314
+ valueNode = t.memberExpression(t.identifier(state.windowDimensionsVariableName), t.identifier(prop));
2315
+ } else {
2316
+ valueNode = t.stringLiteral(value);
1970
2317
  }
2318
+ } else if (typeof value === "number") {
2319
+ valueNode = t.numericLiteral(value);
2320
+ } else if (typeof value === "string") {
2321
+ valueNode = t.stringLiteral(value);
2322
+ } else if (typeof value === "object" && value !== null) {
2323
+ valueNode = t.valueToNode(value);
2324
+ } else {
2325
+ valueNode = t.valueToNode(value);
1971
2326
  }
1972
- currentDir = path.dirname(currentDir);
2327
+ properties.push(t.objectProperty(t.identifier(key), valueNode));
1973
2328
  }
1974
- return null;
2329
+ return t.objectExpression(properties);
1975
2330
  }
1976
- function loadTailwindConfig(configPath) {
1977
- if (configCache.has(configPath)) {
1978
- return configCache.get(configPath);
1979
- }
1980
- try {
1981
- const resolvedPath = require.resolve(configPath);
1982
- delete require.cache[resolvedPath];
1983
- const config = require(configPath);
1984
- const resolved = "default" in config ? config.default : config;
1985
- configCache.set(configPath, resolved);
1986
- return resolved;
1987
- } catch (error) {
1988
- if (process.env.NODE_ENV !== "production") {
1989
- console.warn(`[react-native-tailwind] Failed to load config from ${configPath}:`, error);
2331
+ function splitStaticAndRuntimeStyles(styleObject) {
2332
+ const staticStyles = {};
2333
+ const runtimeStyles = {};
2334
+ for (const [key, value] of Object.entries(styleObject)) {
2335
+ if (typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)) {
2336
+ runtimeStyles[key] = value;
2337
+ } else {
2338
+ staticStyles[key] = value;
1990
2339
  }
1991
- configCache.set(configPath, null);
1992
- return null;
1993
2340
  }
1994
- }
1995
- function extractCustomTheme(filename) {
1996
- const projectDir = path.dirname(filename);
1997
- const configPath = findTailwindConfig(projectDir);
1998
- if (!configPath) {
1999
- return { colors: {}, fontFamily: {}, fontSize: {} };
2000
- }
2001
- const config = loadTailwindConfig(configPath);
2002
- if (!config?.theme) {
2003
- return { colors: {}, fontFamily: {}, fontSize: {} };
2004
- }
2005
- if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
2006
- console.warn(
2007
- "[react-native-tailwind] Using theme.colors will override all default colors. Use theme.extend.colors to add custom colors while keeping defaults."
2008
- );
2009
- }
2010
- const colors = config.theme.extend?.colors ?? config.theme.colors ?? {};
2011
- if (config.theme.fontFamily && !config.theme.extend?.fontFamily && process.env.NODE_ENV !== "production") {
2012
- console.warn(
2013
- "[react-native-tailwind] Using theme.fontFamily will override all default font families. Use theme.extend.fontFamily to add custom fonts while keeping defaults."
2014
- );
2015
- }
2016
- const fontFamily = config.theme.extend?.fontFamily ?? config.theme.fontFamily ?? {};
2017
- const fontFamilyResult = {};
2018
- for (const [key, value] of Object.entries(fontFamily)) {
2019
- if (Array.isArray(value)) {
2020
- fontFamilyResult[key] = value[0];
2021
- } else {
2022
- fontFamilyResult[key] = value;
2023
- }
2024
- }
2025
- if (config.theme.fontSize && !config.theme.extend?.fontSize && process.env.NODE_ENV !== "production") {
2026
- console.warn(
2027
- "[react-native-tailwind] Using theme.fontSize will override all default font sizes. Use theme.extend.fontSize to add custom font sizes while keeping defaults."
2028
- );
2029
- }
2030
- const fontSize = config.theme.extend?.fontSize ?? config.theme.fontSize ?? {};
2031
- const fontSizeResult = {};
2032
- for (const [key, value] of Object.entries(fontSize)) {
2033
- if (typeof value === "number") {
2034
- fontSizeResult[key] = value;
2035
- } else if (typeof value === "string") {
2036
- const parsed = parseFloat(value.replace(/px$/, ""));
2037
- if (!isNaN(parsed)) {
2038
- fontSizeResult[key] = parsed;
2039
- } else {
2040
- if (process.env.NODE_ENV !== "production") {
2041
- console.warn(
2042
- `[react-native-tailwind] Invalid fontSize value for "${key}": ${value}. Expected number or string like "18px".`
2043
- );
2044
- }
2045
- }
2046
- }
2047
- }
2048
- return {
2049
- colors: flattenColors(colors),
2050
- fontFamily: fontFamilyResult,
2051
- fontSize: fontSizeResult
2052
- };
2053
- }
2054
-
2055
- // src/babel/utils/attributeMatchers.ts
2056
- var DEFAULT_CLASS_ATTRIBUTES = [
2057
- "className",
2058
- "contentContainerClassName",
2059
- "columnWrapperClassName",
2060
- "ListHeaderComponentClassName",
2061
- "ListFooterComponentClassName"
2062
- ];
2063
- function buildAttributeMatchers(attributes) {
2064
- const exactMatches = /* @__PURE__ */ new Set();
2065
- const patterns = [];
2066
- for (const attr of attributes) {
2067
- if (attr.includes("*")) {
2068
- const regexPattern = "^" + attr.replace(/\*/g, ".*") + "$";
2069
- patterns.push(new RegExp(regexPattern));
2070
- } else {
2071
- exactMatches.add(attr);
2072
- }
2073
- }
2074
- return { exactMatches, patterns };
2075
- }
2076
- function isAttributeSupported(attributeName, exactMatches, patterns) {
2077
- if (exactMatches.has(attributeName)) {
2078
- return true;
2079
- }
2080
- for (const pattern of patterns) {
2081
- if (pattern.test(attributeName)) {
2082
- return true;
2083
- }
2084
- }
2085
- return false;
2086
- }
2087
- function getTargetStyleProp(attributeName) {
2088
- return attributeName.endsWith("ClassName") ? attributeName.replace("ClassName", "Style") : "style";
2341
+ return { static: staticStyles, runtime: runtimeStyles };
2089
2342
  }
2090
2343
 
2091
2344
  // src/babel/utils/colorSchemeModifierProcessing.ts
@@ -2106,6 +2359,11 @@ function processColorSchemeModifiers(colorSchemeModifiers, state, parseClassName
2106
2359
  for (const [scheme, modifiers] of modifiersByScheme) {
2107
2360
  const classNames = modifiers.map((m) => m.baseClass).join(" ");
2108
2361
  const styleObject = parseClassName2(classNames, state.customTheme);
2362
+ if (hasRuntimeDimensions(styleObject)) {
2363
+ throw new Error(
2364
+ `w-screen and h-screen cannot be combined with color scheme modifiers (dark:, light:, scheme:). Found in: "${scheme}:${classNames}". Use w-screen/h-screen without modifiers instead.`
2365
+ );
2366
+ }
2109
2367
  const styleKey = generateStyleKey2(`${scheme}_${classNames}`);
2110
2368
  state.styleRegistry.set(styleKey, styleObject);
2111
2369
  const colorSchemeCheck = t.binaryExpression(
@@ -2142,6 +2400,8 @@ function getComponentModifierSupport(jsxElement, t) {
2142
2400
  switch (componentName) {
2143
2401
  case "Pressable":
2144
2402
  return { component: "Pressable", supportedModifiers: ["active", "hover", "focus", "disabled"] };
2403
+ case "TouchableOpacity":
2404
+ return { component: "TouchableOpacity", supportedModifiers: ["active", "disabled"] };
2145
2405
  case "TextInput":
2146
2406
  return { component: "TextInput", supportedModifiers: ["focus", "disabled", "placeholder"] };
2147
2407
  default:
@@ -2163,6 +2423,40 @@ function getStatePropertyForModifier(modifier) {
2163
2423
  }
2164
2424
  }
2165
2425
 
2426
+ // src/babel/utils/directionalModifierProcessing.ts
2427
+ function processDirectionalModifiers(directionalModifiers, state, parseClassName2, generateStyleKey2, t) {
2428
+ state.needsI18nManagerImport = true;
2429
+ const modifiersByDirection = /* @__PURE__ */ new Map();
2430
+ for (const mod of directionalModifiers) {
2431
+ const direction = mod.modifier;
2432
+ if (!modifiersByDirection.has(direction)) {
2433
+ modifiersByDirection.set(direction, []);
2434
+ }
2435
+ const directionGroup = modifiersByDirection.get(direction);
2436
+ if (directionGroup) {
2437
+ directionGroup.push(mod);
2438
+ }
2439
+ }
2440
+ const conditionalExpressions = [];
2441
+ for (const [direction, modifiers] of modifiersByDirection) {
2442
+ const classNames = modifiers.map((m) => m.baseClass).join(" ");
2443
+ const styleObject = parseClassName2(classNames, state.customTheme);
2444
+ if (hasRuntimeDimensions(styleObject)) {
2445
+ throw new Error(
2446
+ `w-screen and h-screen cannot be combined with directional modifiers (rtl:, ltr:). Found in: "${direction}:${classNames}". Use w-screen/h-screen without modifiers instead.`
2447
+ );
2448
+ }
2449
+ const styleKey = generateStyleKey2(`${direction}_${classNames}`);
2450
+ state.styleRegistry.set(styleKey, styleObject);
2451
+ const rtlVariable = t.identifier(state.i18nManagerVariableName);
2452
+ const directionCheck = direction === "rtl" ? rtlVariable : t.unaryExpression("!", rtlVariable);
2453
+ const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
2454
+ const conditionalExpression = t.logicalExpression("&&", directionCheck, styleReference);
2455
+ conditionalExpressions.push(conditionalExpression);
2456
+ }
2457
+ return conditionalExpressions;
2458
+ }
2459
+
2166
2460
  // src/babel/utils/dynamicProcessing.ts
2167
2461
  function processDynamicExpression(expression, state, parseClassName2, generateStyleKey2, splitModifierClasses2, processPlatformModifiers2, processColorSchemeModifiers2, componentScope, isPlatformModifier2, isColorSchemeModifier2, isSchemeModifier2, expandSchemeModifier2, t) {
2168
2462
  if (t.isTemplateLiteral(expression)) {
@@ -2482,6 +2776,11 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
2482
2776
  if (baseClasses.length > 0) {
2483
2777
  const baseClassName = baseClasses.join(" ");
2484
2778
  const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
2779
+ if (hasRuntimeDimensions(baseStyleObject)) {
2780
+ throw new Error(
2781
+ `w-screen and h-screen cannot be combined with state modifiers (active:, hover:, focus:, etc.) or platform modifiers (ios:, android:, web:). Found in: "${baseClassName}". Use w-screen/h-screen without modifiers instead.`
2782
+ );
2783
+ }
2485
2784
  const baseStyleKey = generateStyleKey2(baseClassName);
2486
2785
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
2487
2786
  baseStyleExpression = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey));
@@ -2503,6 +2802,11 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
2503
2802
  for (const [modifierType, modifiers] of modifiersByType) {
2504
2803
  const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
2505
2804
  const modifierStyleObject = parseClassName2(modifierClassNames, state.customTheme);
2805
+ if (hasRuntimeDimensions(modifierStyleObject)) {
2806
+ throw new Error(
2807
+ `w-screen and h-screen cannot be combined with state modifiers (active:, hover:, focus:, etc.) or platform modifiers (ios:, android:, web:). Found in: "${modifierType}:${modifierClassNames}". Use w-screen/h-screen without modifiers instead.`
2808
+ );
2809
+ }
2506
2810
  const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
2507
2811
  state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
2508
2812
  const stateProperty = getStatePropertyForModifier(modifierType);
@@ -2530,36 +2834,684 @@ function createStyleFunction(styleExpression, modifierTypes, t) {
2530
2834
  );
2531
2835
  }
2532
2836
  }
2533
- const param = t.objectPattern(paramProperties);
2534
- return t.arrowFunctionExpression([param], styleExpression);
2837
+ const param = t.objectPattern(paramProperties);
2838
+ return t.arrowFunctionExpression([param], styleExpression);
2839
+ }
2840
+
2841
+ // src/babel/utils/platformModifierProcessing.ts
2842
+ function processPlatformModifiers(platformModifiers, state, parseClassName2, generateStyleKey2, t) {
2843
+ state.needsPlatformImport = true;
2844
+ const modifiersByPlatform = /* @__PURE__ */ new Map();
2845
+ for (const mod of platformModifiers) {
2846
+ const platform = mod.modifier;
2847
+ if (!modifiersByPlatform.has(platform)) {
2848
+ modifiersByPlatform.set(platform, []);
2849
+ }
2850
+ const platformGroup = modifiersByPlatform.get(platform);
2851
+ if (platformGroup) {
2852
+ platformGroup.push(mod);
2853
+ }
2854
+ }
2855
+ const selectProperties = [];
2856
+ for (const [platform, modifiers] of modifiersByPlatform) {
2857
+ const classNames = modifiers.map((m) => m.baseClass).join(" ");
2858
+ const styleObject = parseClassName2(classNames, state.customTheme);
2859
+ if (hasRuntimeDimensions(styleObject)) {
2860
+ throw new Error(
2861
+ `w-screen and h-screen cannot be combined with platform modifiers (ios:, android:, web:). Found in: "${platform}:${classNames}". Use w-screen/h-screen without modifiers instead.`
2862
+ );
2863
+ }
2864
+ const styleKey = generateStyleKey2(`${platform}_${classNames}`);
2865
+ state.styleRegistry.set(styleKey, styleObject);
2866
+ const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
2867
+ selectProperties.push(t.objectProperty(t.identifier(platform), styleReference));
2868
+ }
2869
+ return t.callExpression(t.memberExpression(t.identifier("Platform"), t.identifier("select")), [
2870
+ t.objectExpression(selectProperties)
2871
+ ]);
2872
+ }
2873
+
2874
+ // src/babel/utils/styleTransforms.ts
2875
+ function getStyleExpression(styleAttribute, t) {
2876
+ const value = styleAttribute.value;
2877
+ if (!t.isJSXExpressionContainer(value)) return null;
2878
+ const expression = value.expression;
2879
+ if (t.isJSXEmptyExpression(expression)) return null;
2880
+ return expression;
2881
+ }
2882
+ function findStyleAttribute(path2, targetStyleProp, t) {
2883
+ const parent = path2.parent;
2884
+ return parent.attributes.find(
2885
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === targetStyleProp
2886
+ );
2887
+ }
2888
+ function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, stylesIdentifier, t) {
2889
+ const styleAttribute = t.jsxAttribute(
2890
+ t.jsxIdentifier(targetStyleProp),
2891
+ t.jsxExpressionContainer(t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)))
2892
+ );
2893
+ classNamePath.replaceWith(styleAttribute);
2894
+ }
2895
+ function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, stylesIdentifier, t) {
2896
+ const existingStyle = getStyleExpression(styleAttribute, t);
2897
+ if (!existingStyle) return;
2898
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2899
+ const paramIdentifier = t.identifier("_state");
2900
+ const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2901
+ const mergedArray = t.arrayExpression([
2902
+ t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2903
+ functionCall
2904
+ ]);
2905
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2906
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2907
+ } else {
2908
+ const styleArray = t.arrayExpression([
2909
+ t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2910
+ existingStyle
2911
+ ]);
2912
+ styleAttribute.value = t.jsxExpressionContainer(styleArray);
2913
+ }
2914
+ classNamePath.remove();
2915
+ }
2916
+ function replaceDynamicWithStyleAttribute(classNamePath, result, targetStyleProp, t) {
2917
+ const styleAttribute = t.jsxAttribute(
2918
+ t.jsxIdentifier(targetStyleProp),
2919
+ t.jsxExpressionContainer(result.expression)
2920
+ );
2921
+ classNamePath.replaceWith(styleAttribute);
2922
+ }
2923
+ function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
2924
+ const existingStyle = getStyleExpression(styleAttribute, t);
2925
+ if (!existingStyle) return;
2926
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2927
+ const paramIdentifier = t.identifier("_state");
2928
+ const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2929
+ const mergedArray = t.arrayExpression([result.expression, functionCall]);
2930
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2931
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2932
+ } else {
2933
+ let styleArray;
2934
+ if (t.isArrayExpression(existingStyle)) {
2935
+ styleArray = t.arrayExpression([result.expression, ...existingStyle.elements]);
2936
+ } else {
2937
+ styleArray = t.arrayExpression([result.expression, existingStyle]);
2938
+ }
2939
+ styleAttribute.value = t.jsxExpressionContainer(styleArray);
2940
+ }
2941
+ classNamePath.remove();
2942
+ }
2943
+ function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
2944
+ const styleAttribute = t.jsxAttribute(
2945
+ t.jsxIdentifier(targetStyleProp),
2946
+ t.jsxExpressionContainer(styleFunctionExpression)
2947
+ );
2948
+ classNamePath.replaceWith(styleAttribute);
2949
+ }
2950
+ function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
2951
+ const existingStyle = getStyleExpression(styleAttribute, t);
2952
+ if (!existingStyle) return;
2953
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2954
+ const paramIdentifier = t.identifier("_state");
2955
+ const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2956
+ const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
2957
+ const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
2958
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2959
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2960
+ } else {
2961
+ const paramIdentifier = t.identifier("_state");
2962
+ const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2963
+ const mergedArray = t.arrayExpression([functionCall, existingStyle]);
2964
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2965
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2966
+ }
2967
+ classNamePath.remove();
2968
+ }
2969
+ function addOrMergePlaceholderTextColorProp(jsxOpeningElement, color, t) {
2970
+ const existingProp = jsxOpeningElement.attributes.find(
2971
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === "placeholderTextColor"
2972
+ );
2973
+ if (existingProp) {
2974
+ if (process.env.NODE_ENV !== "production") {
2975
+ console.warn(
2976
+ `[react-native-tailwind] placeholderTextColor prop will be overridden by className placeholder: modifier. Remove the explicit prop or the placeholder: modifier to avoid confusion.`
2977
+ );
2978
+ }
2979
+ existingProp.value = t.stringLiteral(color);
2980
+ } else {
2981
+ const newProp = t.jsxAttribute(t.jsxIdentifier("placeholderTextColor"), t.stringLiteral(color));
2982
+ jsxOpeningElement.attributes.push(newProp);
2983
+ }
2984
+ }
2985
+
2986
+ // src/babel/plugin/componentScope.ts
2987
+ function isComponentScope(functionPath, t) {
2988
+ const node = functionPath.node;
2989
+ const parent = functionPath.parent;
2990
+ const parentPath = functionPath.parentPath;
2991
+ if (t.isClassMethod(parent)) {
2992
+ return false;
2993
+ }
2994
+ if (functionPath.findParent((p) => t.isClassBody(p.node))) {
2995
+ return false;
2996
+ }
2997
+ if (t.isFunctionDeclaration(node)) {
2998
+ if (t.isProgram(parent) || t.isExportNamedDeclaration(parent) || t.isExportDefaultDeclaration(parent)) {
2999
+ return true;
3000
+ }
3001
+ }
3002
+ if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
3003
+ if (t.isVariableDeclarator(parent)) {
3004
+ const varDeclarationPath = parentPath?.parentPath;
3005
+ if (varDeclarationPath && t.isVariableDeclaration(varDeclarationPath.node) && (t.isProgram(varDeclarationPath.parent) || t.isExportNamedDeclaration(varDeclarationPath.parent))) {
3006
+ if (t.isIdentifier(parent.id)) {
3007
+ const name = parent.id.name;
3008
+ return /^[A-Z]/.test(name);
3009
+ }
3010
+ }
3011
+ }
3012
+ }
3013
+ return false;
3014
+ }
3015
+ function findComponentScope(path2, t) {
3016
+ let current = path2.getFunctionParent();
3017
+ while (current) {
3018
+ if (t.isFunction(current.node) && isComponentScope(current, t)) {
3019
+ return current;
3020
+ }
3021
+ current = current.getFunctionParent();
3022
+ }
3023
+ return null;
3024
+ }
3025
+
3026
+ // src/babel/plugin/visitors/className.ts
3027
+ function jsxAttributeVisitor(path2, state, t) {
3028
+ const node = path2.node;
3029
+ if (!t.isJSXIdentifier(node.name)) {
3030
+ return;
3031
+ }
3032
+ const attributeName = node.name.name;
3033
+ if (!isAttributeSupported(attributeName, state.supportedAttributes, state.attributePatterns)) {
3034
+ return;
3035
+ }
3036
+ const value = node.value;
3037
+ const targetStyleProp = getTargetStyleProp(attributeName);
3038
+ const processStaticClassName = (className) => {
3039
+ const trimmedClassName = className.trim();
3040
+ if (!trimmedClassName) {
3041
+ path2.remove();
3042
+ return true;
3043
+ }
3044
+ state.hasClassNames = true;
3045
+ const { baseClasses, modifierClasses: rawModifierClasses } = splitModifierClasses(trimmedClassName);
3046
+ const modifierClasses = [];
3047
+ for (const modifier of rawModifierClasses) {
3048
+ if (isSchemeModifier(modifier.modifier)) {
3049
+ const expanded = expandSchemeModifier(
3050
+ modifier,
3051
+ state.customTheme.colors ?? {},
3052
+ state.schemeModifierConfig.darkSuffix,
3053
+ state.schemeModifierConfig.lightSuffix
3054
+ );
3055
+ modifierClasses.push(...expanded);
3056
+ } else {
3057
+ modifierClasses.push(modifier);
3058
+ }
3059
+ }
3060
+ const placeholderModifiers = modifierClasses.filter((m) => m.modifier === "placeholder");
3061
+ const platformModifiers = modifierClasses.filter((m) => isPlatformModifier(m.modifier));
3062
+ const colorSchemeModifiers = modifierClasses.filter((m) => isColorSchemeModifier(m.modifier));
3063
+ const directionalModifiers = modifierClasses.filter((m) => isDirectionalModifier(m.modifier));
3064
+ const stateModifiers = modifierClasses.filter(
3065
+ (m) => isStateModifier(m.modifier) && m.modifier !== "placeholder"
3066
+ );
3067
+ if (placeholderModifiers.length > 0) {
3068
+ const jsxOpeningElement = path2.parent;
3069
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3070
+ if (componentSupport?.supportedModifiers.includes("placeholder")) {
3071
+ const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
3072
+ const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
3073
+ if (placeholderColor) {
3074
+ addOrMergePlaceholderTextColorProp(jsxOpeningElement, placeholderColor, t);
3075
+ }
3076
+ } else {
3077
+ if (process.env.NODE_ENV !== "production") {
3078
+ console.warn(
3079
+ `[react-native-tailwind] placeholder: modifier can only be used on TextInput component at ${state.file.opts.filename ?? "unknown"}`
3080
+ );
3081
+ }
3082
+ }
3083
+ }
3084
+ const hasPlatformModifiers = platformModifiers.length > 0;
3085
+ const hasColorSchemeModifiers = colorSchemeModifiers.length > 0;
3086
+ const hasDirectionalModifiers = directionalModifiers.length > 0;
3087
+ const hasStateModifiers = stateModifiers.length > 0;
3088
+ const hasBaseClasses = baseClasses.length > 0;
3089
+ let componentScope = null;
3090
+ if (hasColorSchemeModifiers) {
3091
+ componentScope = findComponentScope(path2, t);
3092
+ if (componentScope) {
3093
+ state.functionComponentsNeedingColorScheme.add(componentScope);
3094
+ } else {
3095
+ if (process.env.NODE_ENV !== "production") {
3096
+ console.warn(
3097
+ `[react-native-tailwind] dark:/light: modifiers require a function component scope. Found in non-component context at ${state.file.opts.filename ?? "unknown"}. These modifiers are not supported in class components or nested callbacks.`
3098
+ );
3099
+ }
3100
+ }
3101
+ }
3102
+ if (hasStateModifiers && (hasPlatformModifiers || hasColorSchemeModifiers || hasDirectionalModifiers)) {
3103
+ const jsxOpeningElement = path2.parent;
3104
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3105
+ if (componentSupport) {
3106
+ const styleArrayElements = [];
3107
+ if (hasBaseClasses) {
3108
+ const baseClassName = baseClasses.join(" ");
3109
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3110
+ if (hasRuntimeDimensions(baseStyleObject)) {
3111
+ throw path2.buildCodeFrameError(
3112
+ `w-screen and h-screen cannot be combined with modifiers. Found: "${baseClassName}" with state, platform, color scheme, or directional modifiers. Use w-screen/h-screen without modifiers instead.`
3113
+ );
3114
+ }
3115
+ const baseStyleKey = generateStyleKey(baseClassName);
3116
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
3117
+ styleArrayElements.push(
3118
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
3119
+ );
3120
+ }
3121
+ if (hasPlatformModifiers) {
3122
+ const platformSelectExpression = processPlatformModifiers(
3123
+ platformModifiers,
3124
+ state,
3125
+ parseClassName,
3126
+ generateStyleKey,
3127
+ t
3128
+ );
3129
+ styleArrayElements.push(platformSelectExpression);
3130
+ }
3131
+ if (hasColorSchemeModifiers && componentScope) {
3132
+ const colorSchemeConditionals = processColorSchemeModifiers(
3133
+ colorSchemeModifiers,
3134
+ state,
3135
+ parseClassName,
3136
+ generateStyleKey,
3137
+ t
3138
+ );
3139
+ styleArrayElements.push(...colorSchemeConditionals);
3140
+ }
3141
+ if (hasDirectionalModifiers) {
3142
+ const directionalConditionals = processDirectionalModifiers(
3143
+ directionalModifiers,
3144
+ state,
3145
+ parseClassName,
3146
+ generateStyleKey,
3147
+ t
3148
+ );
3149
+ styleArrayElements.push(...directionalConditionals);
3150
+ }
3151
+ const modifiersByType = /* @__PURE__ */ new Map();
3152
+ for (const mod of stateModifiers) {
3153
+ const modType = mod.modifier;
3154
+ if (!modifiersByType.has(modType)) {
3155
+ modifiersByType.set(modType, []);
3156
+ }
3157
+ modifiersByType.get(modType)?.push(mod);
3158
+ }
3159
+ for (const [modifierType, modifiers] of modifiersByType) {
3160
+ if (!componentSupport.supportedModifiers.includes(modifierType)) {
3161
+ continue;
3162
+ }
3163
+ const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
3164
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
3165
+ const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
3166
+ state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
3167
+ const stateProperty = getStatePropertyForModifier(modifierType);
3168
+ const conditionalExpression = t.logicalExpression(
3169
+ "&&",
3170
+ t.identifier(stateProperty),
3171
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(modifierStyleKey))
3172
+ );
3173
+ styleArrayElements.push(conditionalExpression);
3174
+ }
3175
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier))).filter(
3176
+ (mod) => componentSupport.supportedModifiers.includes(mod)
3177
+ );
3178
+ const styleArrayExpression = t.arrayExpression(styleArrayElements);
3179
+ const styleFunctionExpression = createStyleFunction(styleArrayExpression, usedModifiers, t);
3180
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3181
+ if (styleAttribute2) {
3182
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3183
+ } else {
3184
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3185
+ }
3186
+ return true;
3187
+ } else {
3188
+ }
3189
+ }
3190
+ if ((hasPlatformModifiers || hasColorSchemeModifiers || hasDirectionalModifiers) && !hasStateModifiers) {
3191
+ const styleExpressions = [];
3192
+ if (hasBaseClasses) {
3193
+ const baseClassName = baseClasses.join(" ");
3194
+ const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3195
+ if (hasRuntimeDimensions(baseStyleObject)) {
3196
+ throw path2.buildCodeFrameError(
3197
+ `w-screen and h-screen cannot be combined with modifiers. Found: "${baseClassName}" with platform, color scheme, or directional modifiers. Use w-screen/h-screen without modifiers instead.`
3198
+ );
3199
+ }
3200
+ const baseStyleKey = generateStyleKey(baseClassName);
3201
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
3202
+ styleExpressions.push(
3203
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
3204
+ );
3205
+ }
3206
+ if (hasPlatformModifiers) {
3207
+ const platformSelectExpression = processPlatformModifiers(
3208
+ platformModifiers,
3209
+ state,
3210
+ parseClassName,
3211
+ generateStyleKey,
3212
+ t
3213
+ );
3214
+ styleExpressions.push(platformSelectExpression);
3215
+ }
3216
+ if (hasColorSchemeModifiers && componentScope) {
3217
+ const colorSchemeConditionals = processColorSchemeModifiers(
3218
+ colorSchemeModifiers,
3219
+ state,
3220
+ parseClassName,
3221
+ generateStyleKey,
3222
+ t
3223
+ );
3224
+ styleExpressions.push(...colorSchemeConditionals);
3225
+ }
3226
+ if (hasDirectionalModifiers) {
3227
+ const directionalConditionals = processDirectionalModifiers(
3228
+ directionalModifiers,
3229
+ state,
3230
+ parseClassName,
3231
+ generateStyleKey,
3232
+ t
3233
+ );
3234
+ styleExpressions.push(...directionalConditionals);
3235
+ }
3236
+ const styleExpression = styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
3237
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3238
+ if (styleAttribute2) {
3239
+ const existingStyle = styleAttribute2.value;
3240
+ if (t.isJSXExpressionContainer(existingStyle) && !t.isJSXEmptyExpression(existingStyle.expression)) {
3241
+ const existing = existingStyle.expression;
3242
+ const mergedArray = t.isArrayExpression(existing) ? t.arrayExpression([styleExpression, ...existing.elements]) : t.arrayExpression([styleExpression, existing]);
3243
+ styleAttribute2.value = t.jsxExpressionContainer(mergedArray);
3244
+ } else {
3245
+ styleAttribute2.value = t.jsxExpressionContainer(styleExpression);
3246
+ }
3247
+ path2.remove();
3248
+ } else {
3249
+ path2.node.name = t.jsxIdentifier(targetStyleProp);
3250
+ path2.node.value = t.jsxExpressionContainer(styleExpression);
3251
+ }
3252
+ return true;
3253
+ }
3254
+ if (hasStateModifiers) {
3255
+ const jsxOpeningElement = path2.parent;
3256
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3257
+ if (componentSupport) {
3258
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
3259
+ const unsupportedModifiers = usedModifiers.filter(
3260
+ (mod) => !componentSupport.supportedModifiers.includes(mod)
3261
+ );
3262
+ if (unsupportedModifiers.length > 0) {
3263
+ if (process.env.NODE_ENV !== "production") {
3264
+ console.warn(
3265
+ `[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(", ")}`
3266
+ );
3267
+ }
3268
+ const supportedModifierClasses = stateModifiers.filter(
3269
+ (m) => componentSupport.supportedModifiers.includes(m.modifier)
3270
+ );
3271
+ if (supportedModifierClasses.length === 0) {
3272
+ } else {
3273
+ const filteredClassName = baseClasses.join(" ") + " " + supportedModifierClasses.map((m) => `${m.modifier}:${m.baseClass}`).join(" ");
3274
+ const styleExpression = processStaticClassNameWithModifiers(
3275
+ filteredClassName.trim(),
3276
+ state,
3277
+ parseClassName,
3278
+ generateStyleKey,
3279
+ splitModifierClasses,
3280
+ t
3281
+ );
3282
+ const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
3283
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
3284
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3285
+ if (styleAttribute2) {
3286
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3287
+ } else {
3288
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3289
+ }
3290
+ return true;
3291
+ }
3292
+ } else {
3293
+ const styleExpression = processStaticClassNameWithModifiers(
3294
+ trimmedClassName,
3295
+ state,
3296
+ parseClassName,
3297
+ generateStyleKey,
3298
+ splitModifierClasses,
3299
+ t
3300
+ );
3301
+ const modifierTypes = usedModifiers;
3302
+ const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
3303
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3304
+ if (styleAttribute2) {
3305
+ mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3306
+ } else {
3307
+ replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3308
+ }
3309
+ return true;
3310
+ }
3311
+ } else {
3312
+ if (process.env.NODE_ENV !== "production") {
3313
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
3314
+ console.warn(
3315
+ `[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"}`
3316
+ );
3317
+ }
3318
+ }
3319
+ }
3320
+ const classNameForStyle = baseClasses.join(" ");
3321
+ if (!classNameForStyle) {
3322
+ path2.remove();
3323
+ return true;
3324
+ }
3325
+ const styleObject = parseClassName(classNameForStyle, state.customTheme);
3326
+ if (hasRuntimeDimensions(styleObject)) {
3327
+ const { static: staticStyles, runtime: runtimeStyles } = splitStaticAndRuntimeStyles(styleObject);
3328
+ const componentScope2 = findComponentScope(path2, t);
3329
+ if (componentScope2) {
3330
+ state.hasClassNames = true;
3331
+ state.functionComponentsNeedingWindowDimensions.add(componentScope2);
3332
+ state.needsWindowDimensionsImport = true;
3333
+ const styleExpressions = [];
3334
+ if (Object.keys(staticStyles).length > 0) {
3335
+ const styleKey2 = generateStyleKey(classNameForStyle);
3336
+ state.styleRegistry.set(styleKey2, staticStyles);
3337
+ styleExpressions.push(
3338
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey2))
3339
+ );
3340
+ }
3341
+ const runtimeDimensionObject = createRuntimeDimensionObject(runtimeStyles, state, t);
3342
+ styleExpressions.push(runtimeDimensionObject);
3343
+ const styleExpression = styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
3344
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3345
+ if (styleAttribute2) {
3346
+ const existingStyle = styleAttribute2.value;
3347
+ if (t.isJSXExpressionContainer(existingStyle) && !t.isJSXEmptyExpression(existingStyle.expression)) {
3348
+ const existing = existingStyle.expression;
3349
+ if (t.isArrowFunctionExpression(existing) || t.isFunctionExpression(existing)) {
3350
+ const paramIdentifier = t.identifier("_state");
3351
+ const functionCall = t.callExpression(existing, [paramIdentifier]);
3352
+ const mergedArray = t.arrayExpression([styleExpression, functionCall]);
3353
+ const wrappedFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
3354
+ styleAttribute2.value = t.jsxExpressionContainer(wrappedFunction);
3355
+ } else {
3356
+ const mergedArray = t.isArrayExpression(existing) ? t.arrayExpression([styleExpression, ...existing.elements]) : t.arrayExpression([styleExpression, existing]);
3357
+ styleAttribute2.value = t.jsxExpressionContainer(mergedArray);
3358
+ }
3359
+ } else {
3360
+ styleAttribute2.value = t.jsxExpressionContainer(styleExpression);
3361
+ }
3362
+ path2.remove();
3363
+ } else {
3364
+ path2.node.name = t.jsxIdentifier(targetStyleProp);
3365
+ path2.node.value = t.jsxExpressionContainer(styleExpression);
3366
+ }
3367
+ return true;
3368
+ } else {
3369
+ if (process.env.NODE_ENV !== "production") {
3370
+ console.warn(
3371
+ `[react-native-tailwind] w-screen/h-screen classes require a function component scope. Found in non-component context at ${state.file.opts.filename ?? "unknown"}. These classes are not supported in class components or nested callbacks.`
3372
+ );
3373
+ }
3374
+ }
3375
+ }
3376
+ const styleKey = generateStyleKey(classNameForStyle);
3377
+ state.styleRegistry.set(styleKey, styleObject);
3378
+ const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
3379
+ if (styleAttribute) {
3380
+ mergeStyleAttribute(path2, styleAttribute, styleKey, state.stylesIdentifier, t);
3381
+ } else {
3382
+ replaceWithStyleAttribute(path2, styleKey, targetStyleProp, state.stylesIdentifier, t);
3383
+ }
3384
+ return true;
3385
+ };
3386
+ if (t.isStringLiteral(value)) {
3387
+ if (processStaticClassName(value.value)) {
3388
+ return;
3389
+ }
3390
+ }
3391
+ if (t.isJSXExpressionContainer(value)) {
3392
+ const expression = value.expression;
3393
+ if (t.isJSXEmptyExpression(expression)) {
3394
+ return;
3395
+ }
3396
+ if (t.isStringLiteral(expression)) {
3397
+ if (processStaticClassName(expression.value)) {
3398
+ return;
3399
+ }
3400
+ }
3401
+ try {
3402
+ const componentScope = findComponentScope(path2, t);
3403
+ const result = processDynamicExpression(
3404
+ expression,
3405
+ state,
3406
+ parseClassName,
3407
+ generateStyleKey,
3408
+ splitModifierClasses,
3409
+ processPlatformModifiers,
3410
+ processColorSchemeModifiers,
3411
+ componentScope,
3412
+ isPlatformModifier,
3413
+ isColorSchemeModifier,
3414
+ isSchemeModifier,
3415
+ expandSchemeModifier,
3416
+ t
3417
+ );
3418
+ if (result) {
3419
+ state.hasClassNames = true;
3420
+ const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
3421
+ if (styleAttribute) {
3422
+ mergeDynamicStyleAttribute(path2, styleAttribute, result, t);
3423
+ } else {
3424
+ replaceDynamicWithStyleAttribute(path2, result, targetStyleProp, t);
3425
+ }
3426
+ return;
3427
+ }
3428
+ } catch (error) {
3429
+ if (process.env.NODE_ENV !== "production") {
3430
+ console.warn(
3431
+ `[react-native-tailwind] Failed to process dynamic ${attributeName} at ${state.file.opts.filename ?? "unknown"}: ${error instanceof Error ? error.message : String(error)}`
3432
+ );
3433
+ }
3434
+ }
3435
+ }
3436
+ if (process.env.NODE_ENV !== "production") {
3437
+ const filename = state.file.opts.filename ?? "unknown";
3438
+ console.warn(
3439
+ `[react-native-tailwind] Dynamic ${attributeName} values are not fully supported at ${filename}. Use the ${targetStyleProp} prop for dynamic values.`
3440
+ );
3441
+ }
2535
3442
  }
2536
3443
 
2537
- // src/babel/utils/platformModifierProcessing.ts
2538
- function processPlatformModifiers(platformModifiers, state, parseClassName2, generateStyleKey2, t) {
2539
- state.needsPlatformImport = true;
2540
- const modifiersByPlatform = /* @__PURE__ */ new Map();
2541
- for (const mod of platformModifiers) {
2542
- const platform = mod.modifier;
2543
- if (!modifiersByPlatform.has(platform)) {
2544
- modifiersByPlatform.set(platform, []);
3444
+ // src/babel/plugin/visitors/imports.ts
3445
+ function importDeclarationVisitor(path2, state, t) {
3446
+ const node = path2.node;
3447
+ if (node.source.value === "react-native") {
3448
+ const specifiers = node.specifiers;
3449
+ const hasStyleSheet = specifiers.some((spec) => {
3450
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3451
+ return spec.imported.name === "StyleSheet";
3452
+ }
3453
+ return false;
3454
+ });
3455
+ const hasPlatform = specifiers.some((spec) => {
3456
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3457
+ return spec.imported.name === "Platform";
3458
+ }
3459
+ return false;
3460
+ });
3461
+ if (node.importKind !== "type") {
3462
+ for (const spec of specifiers) {
3463
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3464
+ if (spec.imported.name === "I18nManager") {
3465
+ state.hasI18nManagerImport = true;
3466
+ state.i18nManagerLocalIdentifier = spec.local.name;
3467
+ break;
3468
+ }
3469
+ }
3470
+ }
2545
3471
  }
2546
- const platformGroup = modifiersByPlatform.get(platform);
2547
- if (platformGroup) {
2548
- platformGroup.push(mod);
3472
+ if (node.importKind !== "type") {
3473
+ for (const spec of specifiers) {
3474
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3475
+ if (spec.imported.name === "useWindowDimensions") {
3476
+ state.hasWindowDimensionsImport = true;
3477
+ state.windowDimensionsLocalIdentifier = spec.local.name;
3478
+ break;
3479
+ }
3480
+ }
3481
+ }
3482
+ }
3483
+ if (hasStyleSheet) {
3484
+ state.hasStyleSheetImport = true;
2549
3485
  }
3486
+ if (hasPlatform) {
3487
+ state.hasPlatformImport = true;
3488
+ }
3489
+ state.reactNativeImportPath = path2;
2550
3490
  }
2551
- const selectProperties = [];
2552
- for (const [platform, modifiers] of modifiersByPlatform) {
2553
- const classNames = modifiers.map((m) => m.baseClass).join(" ");
2554
- const styleObject = parseClassName2(classNames, state.customTheme);
2555
- const styleKey = generateStyleKey2(`${platform}_${classNames}`);
2556
- state.styleRegistry.set(styleKey, styleObject);
2557
- const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
2558
- selectProperties.push(t.objectProperty(t.identifier(platform), styleReference));
3491
+ if (node.source.value === state.colorSchemeImportSource && node.importKind !== "type") {
3492
+ const specifiers = node.specifiers;
3493
+ for (const spec of specifiers) {
3494
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3495
+ if (spec.imported.name === state.colorSchemeHookName) {
3496
+ state.hasColorSchemeImport = true;
3497
+ state.colorSchemeLocalIdentifier = spec.local.name;
3498
+ break;
3499
+ }
3500
+ }
3501
+ }
3502
+ }
3503
+ if (node.source.value === "@mgcrea/react-native-tailwind") {
3504
+ const specifiers = node.specifiers;
3505
+ specifiers.forEach((spec) => {
3506
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3507
+ const importedName = spec.imported.name;
3508
+ if (importedName === "tw" || importedName === "twStyle") {
3509
+ const localName = spec.local.name;
3510
+ state.twImportNames.add(localName);
3511
+ }
3512
+ }
3513
+ });
2559
3514
  }
2560
- return t.callExpression(t.memberExpression(t.identifier("Platform"), t.identifier("select")), [
2561
- t.objectExpression(selectProperties)
2562
- ]);
2563
3515
  }
2564
3516
 
2565
3517
  // src/babel/utils/styleInjection.ts
@@ -2687,152 +3639,170 @@ function injectColorSchemeHook(functionPath, colorSchemeVariableName, hookName,
2687
3639
  body.body.unshift(hookCall);
2688
3640
  return true;
2689
3641
  }
2690
- function injectStylesAtTop(path2, styleRegistry, stylesIdentifier, t) {
2691
- const styleProperties = [];
2692
- for (const [key, styleObject] of styleRegistry) {
2693
- const properties = Object.entries(styleObject).map(([styleProp, styleValue]) => {
2694
- let valueNode;
2695
- if (typeof styleValue === "number") {
2696
- valueNode = t.numericLiteral(styleValue);
2697
- } else if (typeof styleValue === "string") {
2698
- valueNode = t.stringLiteral(styleValue);
2699
- } else {
2700
- valueNode = t.valueToNode(styleValue);
3642
+ function addI18nManagerImport(path2, t) {
3643
+ const body = path2.node.body;
3644
+ let existingValueImport = null;
3645
+ for (const statement of body) {
3646
+ if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
3647
+ if (statement.importKind === "type") {
3648
+ continue;
2701
3649
  }
2702
- return t.objectProperty(t.identifier(styleProp), valueNode);
2703
- });
2704
- styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
3650
+ const hasNamespaceImport = statement.specifiers.some((spec) => t.isImportNamespaceSpecifier(spec));
3651
+ if (hasNamespaceImport) {
3652
+ continue;
3653
+ }
3654
+ existingValueImport = statement;
3655
+ break;
3656
+ }
2705
3657
  }
2706
- const styleSheet = t.variableDeclaration("const", [
3658
+ if (existingValueImport) {
3659
+ const hasI18nManager = existingValueImport.specifiers.some(
3660
+ (spec) => t.isImportSpecifier(spec) && spec.imported.type === "Identifier" && spec.imported.name === "I18nManager"
3661
+ );
3662
+ if (!hasI18nManager) {
3663
+ existingValueImport.specifiers.push(
3664
+ t.importSpecifier(t.identifier("I18nManager"), t.identifier("I18nManager"))
3665
+ );
3666
+ }
3667
+ } else {
3668
+ const importDeclaration = t.importDeclaration(
3669
+ [t.importSpecifier(t.identifier("I18nManager"), t.identifier("I18nManager"))],
3670
+ t.stringLiteral("react-native")
3671
+ );
3672
+ path2.unshiftContainer("body", importDeclaration);
3673
+ }
3674
+ }
3675
+ function injectI18nManagerVariable(path2, variableName, localIdentifier, t) {
3676
+ const body = path2.node.body;
3677
+ for (const statement of body) {
3678
+ if (t.isVariableDeclaration(statement) && statement.declarations.length > 0 && t.isVariableDeclarator(statement.declarations[0])) {
3679
+ const declarator = statement.declarations[0];
3680
+ if (t.isIdentifier(declarator.id) && declarator.id.name === variableName) {
3681
+ return;
3682
+ }
3683
+ }
3684
+ }
3685
+ const identifierToUse = localIdentifier ?? "I18nManager";
3686
+ const i18nVariable = t.variableDeclaration("const", [
2707
3687
  t.variableDeclarator(
2708
- t.identifier(stylesIdentifier),
2709
- t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
2710
- t.objectExpression(styleProperties)
2711
- ])
3688
+ t.identifier(variableName),
3689
+ t.memberExpression(t.identifier(identifierToUse), t.identifier("isRTL"))
2712
3690
  )
2713
3691
  ]);
2714
- const body = path2.node.body;
2715
3692
  let insertIndex = 0;
2716
3693
  for (let i = 0; i < body.length; i++) {
2717
- if (t.isImportDeclaration(body[i])) {
3694
+ const statement = body[i];
3695
+ if (t.isExpressionStatement(statement) && t.isStringLiteral(statement.expression)) {
2718
3696
  insertIndex = i + 1;
2719
- } else {
2720
- break;
3697
+ continue;
3698
+ }
3699
+ if (t.isImportDeclaration(statement)) {
3700
+ insertIndex = i + 1;
3701
+ continue;
2721
3702
  }
3703
+ break;
2722
3704
  }
2723
- body.splice(insertIndex, 0, styleSheet);
2724
- }
2725
-
2726
- // src/babel/utils/styleTransforms.ts
2727
- function getStyleExpression(styleAttribute, t) {
2728
- const value = styleAttribute.value;
2729
- if (!t.isJSXExpressionContainer(value)) return null;
2730
- const expression = value.expression;
2731
- if (t.isJSXEmptyExpression(expression)) return null;
2732
- return expression;
2733
- }
2734
- function findStyleAttribute(path2, targetStyleProp, t) {
2735
- const parent = path2.parent;
2736
- return parent.attributes.find(
2737
- (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === targetStyleProp
2738
- );
2739
- }
2740
- function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, stylesIdentifier, t) {
2741
- const styleAttribute = t.jsxAttribute(
2742
- t.jsxIdentifier(targetStyleProp),
2743
- t.jsxExpressionContainer(t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)))
2744
- );
2745
- classNamePath.replaceWith(styleAttribute);
3705
+ body.splice(insertIndex, 0, i18nVariable);
2746
3706
  }
2747
- function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, stylesIdentifier, t) {
2748
- const existingStyle = getStyleExpression(styleAttribute, t);
2749
- if (!existingStyle) return;
2750
- if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2751
- const paramIdentifier = t.identifier("_state");
2752
- const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2753
- const mergedArray = t.arrayExpression([
2754
- t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2755
- functionCall
2756
- ]);
2757
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2758
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
3707
+ function addWindowDimensionsImport(path2, t) {
3708
+ const body = path2.node.body;
3709
+ let existingValueImport = null;
3710
+ for (const statement of body) {
3711
+ if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
3712
+ if (statement.importKind !== "type") {
3713
+ existingValueImport = statement;
3714
+ break;
3715
+ }
3716
+ }
3717
+ }
3718
+ if (existingValueImport) {
3719
+ const hasHook = existingValueImport.specifiers.some(
3720
+ (spec) => t.isImportSpecifier(spec) && spec.imported.type === "Identifier" && spec.imported.name === "useWindowDimensions"
3721
+ );
3722
+ if (!hasHook) {
3723
+ existingValueImport.specifiers.push(
3724
+ t.importSpecifier(t.identifier("useWindowDimensions"), t.identifier("useWindowDimensions"))
3725
+ );
3726
+ }
2759
3727
  } else {
2760
- const styleArray = t.arrayExpression([
2761
- t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2762
- existingStyle
2763
- ]);
2764
- styleAttribute.value = t.jsxExpressionContainer(styleArray);
3728
+ const importDeclaration = t.importDeclaration(
3729
+ [t.importSpecifier(t.identifier("useWindowDimensions"), t.identifier("useWindowDimensions"))],
3730
+ t.stringLiteral("react-native")
3731
+ );
3732
+ path2.unshiftContainer("body", importDeclaration);
2765
3733
  }
2766
- classNamePath.remove();
2767
- }
2768
- function replaceDynamicWithStyleAttribute(classNamePath, result, targetStyleProp, t) {
2769
- const styleAttribute = t.jsxAttribute(
2770
- t.jsxIdentifier(targetStyleProp),
2771
- t.jsxExpressionContainer(result.expression)
2772
- );
2773
- classNamePath.replaceWith(styleAttribute);
2774
3734
  }
2775
- function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
2776
- const existingStyle = getStyleExpression(styleAttribute, t);
2777
- if (!existingStyle) return;
2778
- if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2779
- const paramIdentifier = t.identifier("_state");
2780
- const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2781
- const mergedArray = t.arrayExpression([result.expression, functionCall]);
2782
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2783
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2784
- } else {
2785
- let styleArray;
2786
- if (t.isArrayExpression(existingStyle)) {
2787
- styleArray = t.arrayExpression([result.expression, ...existingStyle.elements]);
3735
+ function injectWindowDimensionsHook(functionPath, dimensionsVariableName, hookName, localIdentifier, t) {
3736
+ let body = functionPath.node.body;
3737
+ if (!t.isBlockStatement(body)) {
3738
+ if (t.isArrowFunctionExpression(functionPath.node) && t.isExpression(body)) {
3739
+ const returnStatement = t.returnStatement(body);
3740
+ const blockStatement = t.blockStatement([returnStatement]);
3741
+ functionPath.node.body = blockStatement;
3742
+ body = blockStatement;
2788
3743
  } else {
2789
- styleArray = t.arrayExpression([result.expression, existingStyle]);
3744
+ return false;
2790
3745
  }
2791
- styleAttribute.value = t.jsxExpressionContainer(styleArray);
2792
3746
  }
2793
- classNamePath.remove();
2794
- }
2795
- function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
2796
- const styleAttribute = t.jsxAttribute(
2797
- t.jsxIdentifier(targetStyleProp),
2798
- t.jsxExpressionContainer(styleFunctionExpression)
2799
- );
2800
- classNamePath.replaceWith(styleAttribute);
3747
+ const hasHook = body.body.some((statement) => {
3748
+ if (t.isVariableDeclaration(statement) && statement.declarations.length > 0 && t.isVariableDeclarator(statement.declarations[0])) {
3749
+ const declarator = statement.declarations[0];
3750
+ return t.isIdentifier(declarator.id) && declarator.id.name === dimensionsVariableName;
3751
+ }
3752
+ return false;
3753
+ });
3754
+ if (hasHook) {
3755
+ return false;
3756
+ }
3757
+ const identifierToCall = localIdentifier ?? hookName;
3758
+ const hookCall = t.variableDeclaration("const", [
3759
+ t.variableDeclarator(
3760
+ t.identifier(dimensionsVariableName),
3761
+ t.callExpression(t.identifier(identifierToCall), [])
3762
+ )
3763
+ ]);
3764
+ body.body.unshift(hookCall);
3765
+ return true;
2801
3766
  }
2802
- function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
2803
- const existingStyle = getStyleExpression(styleAttribute, t);
2804
- if (!existingStyle) return;
2805
- if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2806
- const paramIdentifier = t.identifier("_state");
2807
- const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2808
- const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
2809
- const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
2810
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2811
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2812
- } else {
2813
- const paramIdentifier = t.identifier("_state");
2814
- const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2815
- const mergedArray = t.arrayExpression([functionCall, existingStyle]);
2816
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2817
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
3767
+ function injectStylesAtTop(path2, styleRegistry, stylesIdentifier, t) {
3768
+ const styleProperties = [];
3769
+ for (const [key, styleObject] of styleRegistry) {
3770
+ const properties = Object.entries(styleObject).map(([styleProp, styleValue]) => {
3771
+ let valueNode;
3772
+ if (typeof styleValue === "number") {
3773
+ valueNode = t.numericLiteral(styleValue);
3774
+ } else if (typeof styleValue === "string") {
3775
+ valueNode = t.stringLiteral(styleValue);
3776
+ } else {
3777
+ valueNode = t.valueToNode(styleValue);
3778
+ }
3779
+ return t.objectProperty(t.identifier(styleProp), valueNode);
3780
+ });
3781
+ styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
2818
3782
  }
2819
- classNamePath.remove();
2820
- }
2821
- function addOrMergePlaceholderTextColorProp(jsxOpeningElement, color, t) {
2822
- const existingProp = jsxOpeningElement.attributes.find(
2823
- (attr) => t.isJSXAttribute(attr) && attr.name.name === "placeholderTextColor"
2824
- );
2825
- if (existingProp) {
2826
- if (process.env.NODE_ENV !== "production") {
2827
- console.warn(
2828
- `[react-native-tailwind] placeholderTextColor prop will be overridden by className placeholder: modifier. Remove the explicit prop or the placeholder: modifier to avoid confusion.`
2829
- );
3783
+ const styleSheet = t.variableDeclaration("const", [
3784
+ t.variableDeclarator(
3785
+ t.identifier(stylesIdentifier),
3786
+ t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
3787
+ t.objectExpression(styleProperties)
3788
+ ])
3789
+ )
3790
+ ]);
3791
+ const body = path2.node.body;
3792
+ let insertIndex = 0;
3793
+ for (let i = 0; i < body.length; i++) {
3794
+ const statement = body[i];
3795
+ if (t.isExpressionStatement(statement) && t.isStringLiteral(statement.expression)) {
3796
+ insertIndex = i + 1;
3797
+ continue;
2830
3798
  }
2831
- existingProp.value = t.stringLiteral(color);
2832
- } else {
2833
- const newProp = t.jsxAttribute(t.jsxIdentifier("placeholderTextColor"), t.stringLiteral(color));
2834
- jsxOpeningElement.attributes.push(newProp);
3799
+ if (t.isImportDeclaration(statement)) {
3800
+ insertIndex = i + 1;
3801
+ continue;
3802
+ }
3803
+ break;
2835
3804
  }
3805
+ body.splice(insertIndex, 0, styleSheet);
2836
3806
  }
2837
3807
 
2838
3808
  // src/babel/utils/twProcessing.ts
@@ -2856,6 +3826,11 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
2856
3826
  if (baseClasses.length > 0) {
2857
3827
  const baseClassName = baseClasses.join(" ");
2858
3828
  const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
3829
+ if (hasRuntimeDimensions(baseStyleObject)) {
3830
+ throw path2.buildCodeFrameError(
3831
+ `w-screen and h-screen are not supported in tw\`\` or twStyle() calls. Found: "${baseClassName}". Use them in className attributes instead.`
3832
+ );
3833
+ }
2859
3834
  const baseStyleKey = generateStyleKey2(baseClassName);
2860
3835
  state.styleRegistry.set(baseStyleKey, baseStyleObject);
2861
3836
  objectProperties.push(
@@ -2869,8 +3844,9 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
2869
3844
  }
2870
3845
  const colorSchemeModifiers = modifierClasses.filter((m) => isColorSchemeModifier(m.modifier));
2871
3846
  const platformModifiers = modifierClasses.filter((m) => isPlatformModifier(m.modifier));
3847
+ const directionalModifiers = modifierClasses.filter((m) => isDirectionalModifier(m.modifier));
2872
3848
  const otherModifiers = modifierClasses.filter(
2873
- (m) => !isColorSchemeModifier(m.modifier) && !isPlatformModifier(m.modifier)
3849
+ (m) => !isColorSchemeModifier(m.modifier) && !isPlatformModifier(m.modifier) && !isDirectionalModifier(m.modifier)
2874
3850
  );
2875
3851
  const hasColorSchemeModifiers = colorSchemeModifiers.length > 0;
2876
3852
  let componentScope = null;
@@ -3004,6 +3980,62 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
3004
3980
  );
3005
3981
  }
3006
3982
  }
3983
+ const hasDirectionalModifiers = directionalModifiers.length > 0;
3984
+ if (hasDirectionalModifiers) {
3985
+ state.needsI18nManagerImport = true;
3986
+ const directionalConditionals = processDirectionalModifiers(
3987
+ directionalModifiers,
3988
+ state,
3989
+ parseClassName2,
3990
+ generateStyleKey2,
3991
+ t
3992
+ );
3993
+ const styleProperty = objectProperties.find(
3994
+ (prop) => t.isIdentifier(prop.key) && prop.key.name === "style"
3995
+ );
3996
+ if (styleProperty && t.isArrayExpression(styleProperty.value)) {
3997
+ styleProperty.value.elements.push(...directionalConditionals);
3998
+ } else {
3999
+ const styleArrayElements = [];
4000
+ if (baseClasses.length > 0) {
4001
+ const baseClassName = baseClasses.join(" ");
4002
+ const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
4003
+ const baseStyleKey = generateStyleKey2(baseClassName);
4004
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
4005
+ styleArrayElements.push(
4006
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
4007
+ );
4008
+ }
4009
+ styleArrayElements.push(...directionalConditionals);
4010
+ objectProperties[0] = t.objectProperty(t.identifier("style"), t.arrayExpression(styleArrayElements));
4011
+ }
4012
+ const rtlModifiers = directionalModifiers.filter((m) => m.modifier === "rtl");
4013
+ const ltrModifiers = directionalModifiers.filter((m) => m.modifier === "ltr");
4014
+ if (rtlModifiers.length > 0) {
4015
+ const rtlClassNames = rtlModifiers.map((m) => m.baseClass).join(" ");
4016
+ const rtlStyleObject = parseClassName2(rtlClassNames, state.customTheme);
4017
+ const rtlStyleKey = generateStyleKey2(`rtl_${rtlClassNames}`);
4018
+ state.styleRegistry.set(rtlStyleKey, rtlStyleObject);
4019
+ objectProperties.push(
4020
+ t.objectProperty(
4021
+ t.identifier("rtlStyle"),
4022
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(rtlStyleKey))
4023
+ )
4024
+ );
4025
+ }
4026
+ if (ltrModifiers.length > 0) {
4027
+ const ltrClassNames = ltrModifiers.map((m) => m.baseClass).join(" ");
4028
+ const ltrStyleObject = parseClassName2(ltrClassNames, state.customTheme);
4029
+ const ltrStyleKey = generateStyleKey2(`ltr_${ltrClassNames}`);
4030
+ state.styleRegistry.set(ltrStyleKey, ltrStyleObject);
4031
+ objectProperties.push(
4032
+ t.objectProperty(
4033
+ t.identifier("ltrStyle"),
4034
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(ltrStyleKey))
4035
+ )
4036
+ );
4037
+ }
4038
+ }
3007
4039
  const modifiersByType = /* @__PURE__ */ new Map();
3008
4040
  for (const mod of otherModifiers) {
3009
4041
  if (!modifiersByType.has(mod.modifier)) {
@@ -3053,586 +4085,186 @@ function removeTwImports(path2, t) {
3053
4085
  });
3054
4086
  }
3055
4087
 
3056
- // src/babel/plugin.ts
3057
- var DEFAULT_STYLES_IDENTIFIER = "_twStyles";
3058
- function isComponentScope(functionPath, t) {
3059
- const node = functionPath.node;
3060
- const parent = functionPath.parent;
3061
- const parentPath = functionPath.parentPath;
3062
- if (t.isClassMethod(parent)) {
3063
- return false;
4088
+ // src/babel/plugin/visitors/program.ts
4089
+ function programEnter(_path, _state) {
4090
+ }
4091
+ function programExit(path2, state, t) {
4092
+ if (state.hasTwImport) {
4093
+ removeTwImports(path2, t);
3064
4094
  }
3065
- if (functionPath.findParent((p) => t.isClassBody(p.node))) {
3066
- return false;
4095
+ if (!state.hasClassNames && !state.needsWindowDimensionsImport && !state.needsColorSchemeImport && !state.needsI18nManagerImport) {
4096
+ return;
3067
4097
  }
3068
- if (t.isFunctionDeclaration(node)) {
3069
- if (t.isProgram(parent) || t.isExportNamedDeclaration(parent) || t.isExportDefaultDeclaration(parent)) {
3070
- return true;
4098
+ if (!state.hasStyleSheetImport && state.styleRegistry.size > 0) {
4099
+ addStyleSheetImport(path2, t);
4100
+ }
4101
+ if (state.needsPlatformImport && !state.hasPlatformImport) {
4102
+ addPlatformImport(path2, t);
4103
+ }
4104
+ if (state.needsI18nManagerImport && !state.hasI18nManagerImport) {
4105
+ addI18nManagerImport(path2, t);
4106
+ }
4107
+ if (state.needsI18nManagerImport) {
4108
+ injectI18nManagerVariable(path2, state.i18nManagerVariableName, state.i18nManagerLocalIdentifier, t);
4109
+ }
4110
+ if (state.needsColorSchemeImport && !state.hasColorSchemeImport) {
4111
+ addColorSchemeImport(path2, state.colorSchemeImportSource, state.colorSchemeHookName, t);
4112
+ }
4113
+ if (state.needsColorSchemeImport) {
4114
+ for (const functionPath of state.functionComponentsNeedingColorScheme) {
4115
+ injectColorSchemeHook(
4116
+ functionPath,
4117
+ state.colorSchemeVariableName,
4118
+ state.colorSchemeHookName,
4119
+ state.colorSchemeLocalIdentifier,
4120
+ t
4121
+ );
3071
4122
  }
3072
4123
  }
3073
- if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
3074
- if (t.isVariableDeclarator(parent)) {
3075
- const varDeclarationPath = parentPath?.parentPath;
3076
- if (varDeclarationPath && t.isVariableDeclaration(varDeclarationPath.node) && (t.isProgram(varDeclarationPath.parent) || t.isExportNamedDeclaration(varDeclarationPath.parent))) {
3077
- if (t.isIdentifier(parent.id)) {
3078
- const name = parent.id.name;
3079
- return /^[A-Z]/.test(name);
3080
- }
3081
- }
4124
+ if (state.needsWindowDimensionsImport && !state.hasWindowDimensionsImport) {
4125
+ addWindowDimensionsImport(path2, t);
4126
+ }
4127
+ if (state.needsWindowDimensionsImport) {
4128
+ for (const functionPath of state.functionComponentsNeedingWindowDimensions) {
4129
+ injectWindowDimensionsHook(
4130
+ functionPath,
4131
+ state.windowDimensionsVariableName,
4132
+ "useWindowDimensions",
4133
+ state.windowDimensionsLocalIdentifier,
4134
+ t
4135
+ );
3082
4136
  }
3083
4137
  }
3084
- return false;
4138
+ if (state.styleRegistry.size > 0) {
4139
+ injectStylesAtTop(path2, state.styleRegistry, state.stylesIdentifier, t);
4140
+ }
3085
4141
  }
3086
- function findComponentScope(path2, t) {
3087
- let current = path2.getFunctionParent();
3088
- while (current) {
3089
- if (t.isFunction(current.node) && isComponentScope(current, t)) {
3090
- return current;
4142
+
4143
+ // src/babel/plugin/visitors/tw.ts
4144
+ function taggedTemplateVisitor(path2, state, t) {
4145
+ const node = path2.node;
4146
+ if (!t.isIdentifier(node.tag)) {
4147
+ return;
4148
+ }
4149
+ const tagName = node.tag.name;
4150
+ if (!state.twImportNames.has(tagName)) {
4151
+ return;
4152
+ }
4153
+ const quasi = node.quasi;
4154
+ if (!t.isTemplateLiteral(quasi)) {
4155
+ return;
4156
+ }
4157
+ if (quasi.expressions.length > 0) {
4158
+ if (process.env.NODE_ENV !== "production") {
4159
+ console.warn(
4160
+ `[react-native-tailwind] Dynamic tw\`...\` with interpolations is not supported at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
4161
+ );
3091
4162
  }
3092
- current = current.getFunctionParent();
4163
+ return;
3093
4164
  }
3094
- return null;
4165
+ const className = quasi.quasis[0]?.value.cooked?.trim() ?? "";
4166
+ if (!className) {
4167
+ path2.replaceWith(t.objectExpression([t.objectProperty(t.identifier("style"), t.objectExpression([]))]));
4168
+ state.hasTwImport = true;
4169
+ return;
4170
+ }
4171
+ state.hasClassNames = true;
4172
+ processTwCall(
4173
+ className,
4174
+ path2,
4175
+ state,
4176
+ parseClassName,
4177
+ generateStyleKey,
4178
+ splitModifierClasses,
4179
+ findComponentScope,
4180
+ t
4181
+ );
4182
+ state.hasTwImport = true;
4183
+ }
4184
+ function callExpressionVisitor(path2, state, t) {
4185
+ const node = path2.node;
4186
+ if (!t.isIdentifier(node.callee)) {
4187
+ return;
4188
+ }
4189
+ const calleeName = node.callee.name;
4190
+ if (!state.twImportNames.has(calleeName)) {
4191
+ return;
4192
+ }
4193
+ if (node.arguments.length !== 1) {
4194
+ if (process.env.NODE_ENV !== "production") {
4195
+ console.warn(
4196
+ `[react-native-tailwind] twStyle() expects exactly one argument at ${state.file.opts.filename ?? "unknown"}`
4197
+ );
4198
+ }
4199
+ return;
4200
+ }
4201
+ const arg = node.arguments[0];
4202
+ if (!t.isStringLiteral(arg)) {
4203
+ if (process.env.NODE_ENV !== "production") {
4204
+ console.warn(
4205
+ `[react-native-tailwind] twStyle() only supports static string literals at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
4206
+ );
4207
+ }
4208
+ return;
4209
+ }
4210
+ const className = arg.value.trim();
4211
+ if (!className) {
4212
+ path2.replaceWith(t.identifier("undefined"));
4213
+ state.hasTwImport = true;
4214
+ return;
4215
+ }
4216
+ state.hasClassNames = true;
4217
+ processTwCall(
4218
+ className,
4219
+ path2,
4220
+ state,
4221
+ parseClassName,
4222
+ generateStyleKey,
4223
+ splitModifierClasses,
4224
+ findComponentScope,
4225
+ t
4226
+ );
4227
+ state.hasTwImport = true;
3095
4228
  }
4229
+
4230
+ // src/babel/plugin.ts
3096
4231
  function reactNativeTailwindBabelPlugin({ types: t }, options) {
3097
- const attributes = options?.attributes ?? [...DEFAULT_CLASS_ATTRIBUTES];
3098
- const { exactMatches, patterns } = buildAttributeMatchers(attributes);
3099
- const stylesIdentifier = options?.stylesIdentifier ?? DEFAULT_STYLES_IDENTIFIER;
4232
+ const colorSchemeImportSource = options?.colorScheme?.importFrom ?? "react-native";
4233
+ const colorSchemeHookName = options?.colorScheme?.importName ?? "useColorScheme";
3100
4234
  const schemeModifierConfig = {
3101
4235
  darkSuffix: options?.schemeModifier?.darkSuffix ?? "-dark",
3102
4236
  lightSuffix: options?.schemeModifier?.lightSuffix ?? "-light"
3103
4237
  };
3104
- const colorSchemeImportSource = options?.colorScheme?.importFrom ?? "react-native";
3105
- const colorSchemeHookName = options?.colorScheme?.importName ?? "useColorScheme";
3106
4238
  return {
3107
4239
  name: "react-native-tailwind",
3108
4240
  visitor: {
3109
4241
  Program: {
3110
- enter(_path, state) {
3111
- state.styleRegistry = /* @__PURE__ */ new Map();
3112
- state.hasClassNames = false;
3113
- state.hasStyleSheetImport = false;
3114
- state.hasPlatformImport = false;
3115
- state.needsPlatformImport = false;
3116
- state.hasColorSchemeImport = false;
3117
- state.needsColorSchemeImport = false;
3118
- state.colorSchemeVariableName = "_twColorScheme";
3119
- state.colorSchemeImportSource = colorSchemeImportSource;
3120
- state.colorSchemeHookName = colorSchemeHookName;
3121
- state.supportedAttributes = exactMatches;
3122
- state.attributePatterns = patterns;
3123
- state.stylesIdentifier = stylesIdentifier;
3124
- state.twImportNames = /* @__PURE__ */ new Set();
3125
- state.hasTwImport = false;
3126
- state.functionComponentsNeedingColorScheme = /* @__PURE__ */ new Set();
3127
- state.hasColorSchemeImport = false;
3128
- state.colorSchemeLocalIdentifier = void 0;
3129
- state.needsPlatformImport = false;
3130
- state.hasPlatformImport = false;
3131
- state.customTheme = extractCustomTheme(state.file.opts.filename ?? "");
3132
- state.schemeModifierConfig = schemeModifierConfig;
4242
+ enter(path2, state) {
4243
+ const initialState = createInitialState(
4244
+ options,
4245
+ state.file.opts.filename ?? "",
4246
+ colorSchemeImportSource,
4247
+ colorSchemeHookName,
4248
+ schemeModifierConfig
4249
+ );
4250
+ Object.assign(state, initialState);
4251
+ programEnter(path2, state);
3133
4252
  },
3134
4253
  exit(path2, state) {
3135
- if (state.hasTwImport) {
3136
- removeTwImports(path2, t);
3137
- }
3138
- if (!state.hasClassNames || state.styleRegistry.size === 0) {
3139
- return;
3140
- }
3141
- if (!state.hasStyleSheetImport) {
3142
- addStyleSheetImport(path2, t);
3143
- }
3144
- if (state.needsPlatformImport && !state.hasPlatformImport) {
3145
- addPlatformImport(path2, t);
3146
- }
3147
- if (state.needsColorSchemeImport && !state.hasColorSchemeImport) {
3148
- addColorSchemeImport(path2, state.colorSchemeImportSource, state.colorSchemeHookName, t);
3149
- }
3150
- if (state.needsColorSchemeImport) {
3151
- for (const functionPath of state.functionComponentsNeedingColorScheme) {
3152
- injectColorSchemeHook(
3153
- functionPath,
3154
- state.colorSchemeVariableName,
3155
- state.colorSchemeHookName,
3156
- state.colorSchemeLocalIdentifier,
3157
- t
3158
- );
3159
- }
3160
- }
3161
- injectStylesAtTop(path2, state.styleRegistry, state.stylesIdentifier, t);
4254
+ programExit(path2, state, t);
3162
4255
  }
3163
4256
  },
3164
- // Check if StyleSheet/Platform are already imported and track tw/twStyle imports
3165
4257
  ImportDeclaration(path2, state) {
3166
- const node = path2.node;
3167
- if (node.source.value === "react-native") {
3168
- const specifiers = node.specifiers;
3169
- const hasStyleSheet = specifiers.some((spec) => {
3170
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3171
- return spec.imported.name === "StyleSheet";
3172
- }
3173
- return false;
3174
- });
3175
- const hasPlatform = specifiers.some((spec) => {
3176
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3177
- return spec.imported.name === "Platform";
3178
- }
3179
- return false;
3180
- });
3181
- if (hasStyleSheet) {
3182
- state.hasStyleSheetImport = true;
3183
- }
3184
- if (hasPlatform) {
3185
- state.hasPlatformImport = true;
3186
- }
3187
- state.reactNativeImportPath = path2;
3188
- }
3189
- if (node.source.value === state.colorSchemeImportSource && node.importKind !== "type") {
3190
- const specifiers = node.specifiers;
3191
- for (const spec of specifiers) {
3192
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3193
- if (spec.imported.name === state.colorSchemeHookName) {
3194
- state.hasColorSchemeImport = true;
3195
- state.colorSchemeLocalIdentifier = spec.local.name;
3196
- break;
3197
- }
3198
- }
3199
- }
3200
- }
3201
- if (node.source.value === "@mgcrea/react-native-tailwind") {
3202
- const specifiers = node.specifiers;
3203
- specifiers.forEach((spec) => {
3204
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
3205
- const importedName = spec.imported.name;
3206
- if (importedName === "tw" || importedName === "twStyle") {
3207
- const localName = spec.local.name;
3208
- state.twImportNames.add(localName);
3209
- }
3210
- }
3211
- });
3212
- }
4258
+ importDeclarationVisitor(path2, state, t);
3213
4259
  },
3214
- // Handle tw`...` tagged template expressions
3215
4260
  TaggedTemplateExpression(path2, state) {
3216
- const node = path2.node;
3217
- if (!t.isIdentifier(node.tag)) {
3218
- return;
3219
- }
3220
- const tagName = node.tag.name;
3221
- if (!state.twImportNames.has(tagName)) {
3222
- return;
3223
- }
3224
- const quasi = node.quasi;
3225
- if (!t.isTemplateLiteral(quasi)) {
3226
- return;
3227
- }
3228
- if (quasi.expressions.length > 0) {
3229
- if (process.env.NODE_ENV !== "production") {
3230
- console.warn(
3231
- `[react-native-tailwind] Dynamic tw\`...\` with interpolations is not supported at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
3232
- );
3233
- }
3234
- return;
3235
- }
3236
- const className = quasi.quasis[0]?.value.cooked?.trim() ?? "";
3237
- if (!className) {
3238
- path2.replaceWith(
3239
- t.objectExpression([t.objectProperty(t.identifier("style"), t.objectExpression([]))])
3240
- );
3241
- state.hasTwImport = true;
3242
- return;
3243
- }
3244
- state.hasClassNames = true;
3245
- processTwCall(
3246
- className,
3247
- path2,
3248
- state,
3249
- parseClassName,
3250
- generateStyleKey,
3251
- splitModifierClasses,
3252
- findComponentScope,
3253
- t
3254
- );
3255
- state.hasTwImport = true;
4261
+ taggedTemplateVisitor(path2, state, t);
3256
4262
  },
3257
- // Handle twStyle('...') call expressions
3258
4263
  CallExpression(path2, state) {
3259
- const node = path2.node;
3260
- if (!t.isIdentifier(node.callee)) {
3261
- return;
3262
- }
3263
- const calleeName = node.callee.name;
3264
- if (!state.twImportNames.has(calleeName)) {
3265
- return;
3266
- }
3267
- if (node.arguments.length !== 1) {
3268
- if (process.env.NODE_ENV !== "production") {
3269
- console.warn(
3270
- `[react-native-tailwind] twStyle() expects exactly one argument at ${state.file.opts.filename ?? "unknown"}`
3271
- );
3272
- }
3273
- return;
3274
- }
3275
- const arg = node.arguments[0];
3276
- if (!t.isStringLiteral(arg)) {
3277
- if (process.env.NODE_ENV !== "production") {
3278
- console.warn(
3279
- `[react-native-tailwind] twStyle() only supports static string literals at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
3280
- );
3281
- }
3282
- return;
3283
- }
3284
- const className = arg.value.trim();
3285
- if (!className) {
3286
- path2.replaceWith(t.identifier("undefined"));
3287
- state.hasTwImport = true;
3288
- return;
3289
- }
3290
- state.hasClassNames = true;
3291
- processTwCall(
3292
- className,
3293
- path2,
3294
- state,
3295
- parseClassName,
3296
- generateStyleKey,
3297
- splitModifierClasses,
3298
- findComponentScope,
3299
- t
3300
- );
3301
- state.hasTwImport = true;
4264
+ callExpressionVisitor(path2, state, t);
3302
4265
  },
3303
4266
  JSXAttribute(path2, state) {
3304
- const node = path2.node;
3305
- if (!t.isJSXIdentifier(node.name)) {
3306
- return;
3307
- }
3308
- const attributeName = node.name.name;
3309
- if (!isAttributeSupported(attributeName, state.supportedAttributes, state.attributePatterns)) {
3310
- return;
3311
- }
3312
- const value = node.value;
3313
- const targetStyleProp = getTargetStyleProp(attributeName);
3314
- const processStaticClassName = (className) => {
3315
- const trimmedClassName = className.trim();
3316
- if (!trimmedClassName) {
3317
- path2.remove();
3318
- return true;
3319
- }
3320
- state.hasClassNames = true;
3321
- const { baseClasses, modifierClasses: rawModifierClasses } = splitModifierClasses(trimmedClassName);
3322
- const modifierClasses = [];
3323
- for (const modifier of rawModifierClasses) {
3324
- if (isSchemeModifier(modifier.modifier)) {
3325
- const expanded = expandSchemeModifier(
3326
- modifier,
3327
- state.customTheme.colors ?? {},
3328
- state.schemeModifierConfig.darkSuffix,
3329
- state.schemeModifierConfig.lightSuffix
3330
- );
3331
- modifierClasses.push(...expanded);
3332
- } else {
3333
- modifierClasses.push(modifier);
3334
- }
3335
- }
3336
- const placeholderModifiers = modifierClasses.filter((m) => m.modifier === "placeholder");
3337
- const platformModifiers = modifierClasses.filter((m) => isPlatformModifier(m.modifier));
3338
- const colorSchemeModifiers = modifierClasses.filter((m) => isColorSchemeModifier(m.modifier));
3339
- const stateModifiers = modifierClasses.filter(
3340
- (m) => isStateModifier(m.modifier) && m.modifier !== "placeholder"
3341
- );
3342
- if (placeholderModifiers.length > 0) {
3343
- const jsxOpeningElement = path2.parent;
3344
- const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3345
- if (componentSupport?.supportedModifiers.includes("placeholder")) {
3346
- const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
3347
- const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
3348
- if (placeholderColor) {
3349
- addOrMergePlaceholderTextColorProp(jsxOpeningElement, placeholderColor, t);
3350
- }
3351
- } else {
3352
- if (process.env.NODE_ENV !== "production") {
3353
- console.warn(
3354
- `[react-native-tailwind] placeholder: modifier can only be used on TextInput component at ${state.file.opts.filename ?? "unknown"}`
3355
- );
3356
- }
3357
- }
3358
- }
3359
- const hasPlatformModifiers = platformModifiers.length > 0;
3360
- const hasColorSchemeModifiers = colorSchemeModifiers.length > 0;
3361
- const hasStateModifiers = stateModifiers.length > 0;
3362
- const hasBaseClasses = baseClasses.length > 0;
3363
- let componentScope = null;
3364
- if (hasColorSchemeModifiers) {
3365
- componentScope = findComponentScope(path2, t);
3366
- if (componentScope) {
3367
- state.functionComponentsNeedingColorScheme.add(componentScope);
3368
- } else {
3369
- if (process.env.NODE_ENV !== "production") {
3370
- console.warn(
3371
- `[react-native-tailwind] dark:/light: modifiers require a function component scope. Found in non-component context at ${state.file.opts.filename ?? "unknown"}. These modifiers are not supported in class components or nested callbacks.`
3372
- );
3373
- }
3374
- }
3375
- }
3376
- if (hasStateModifiers && (hasPlatformModifiers || hasColorSchemeModifiers)) {
3377
- const jsxOpeningElement = path2.parent;
3378
- const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3379
- if (componentSupport) {
3380
- const styleArrayElements = [];
3381
- if (hasBaseClasses) {
3382
- const baseClassName = baseClasses.join(" ");
3383
- const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3384
- const baseStyleKey = generateStyleKey(baseClassName);
3385
- state.styleRegistry.set(baseStyleKey, baseStyleObject);
3386
- styleArrayElements.push(
3387
- t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
3388
- );
3389
- }
3390
- if (hasPlatformModifiers) {
3391
- const platformSelectExpression = processPlatformModifiers(
3392
- platformModifiers,
3393
- state,
3394
- parseClassName,
3395
- generateStyleKey,
3396
- t
3397
- );
3398
- styleArrayElements.push(platformSelectExpression);
3399
- }
3400
- if (hasColorSchemeModifiers && componentScope) {
3401
- const colorSchemeConditionals = processColorSchemeModifiers(
3402
- colorSchemeModifiers,
3403
- state,
3404
- parseClassName,
3405
- generateStyleKey,
3406
- t
3407
- );
3408
- styleArrayElements.push(...colorSchemeConditionals);
3409
- }
3410
- const modifiersByType = /* @__PURE__ */ new Map();
3411
- for (const mod of stateModifiers) {
3412
- const modType = mod.modifier;
3413
- if (!modifiersByType.has(modType)) {
3414
- modifiersByType.set(modType, []);
3415
- }
3416
- modifiersByType.get(modType)?.push(mod);
3417
- }
3418
- for (const [modifierType, modifiers] of modifiersByType) {
3419
- if (!componentSupport.supportedModifiers.includes(modifierType)) {
3420
- continue;
3421
- }
3422
- const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
3423
- const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
3424
- const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
3425
- state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
3426
- const stateProperty = getStatePropertyForModifier(modifierType);
3427
- const conditionalExpression = t.logicalExpression(
3428
- "&&",
3429
- t.identifier(stateProperty),
3430
- t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(modifierStyleKey))
3431
- );
3432
- styleArrayElements.push(conditionalExpression);
3433
- }
3434
- const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier))).filter(
3435
- (mod) => componentSupport.supportedModifiers.includes(mod)
3436
- );
3437
- const styleArrayExpression = t.arrayExpression(styleArrayElements);
3438
- const styleFunctionExpression = createStyleFunction(styleArrayExpression, usedModifiers, t);
3439
- const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3440
- if (styleAttribute2) {
3441
- mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3442
- } else {
3443
- replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3444
- }
3445
- return true;
3446
- } else {
3447
- }
3448
- }
3449
- if ((hasPlatformModifiers || hasColorSchemeModifiers) && !hasStateModifiers) {
3450
- const styleExpressions = [];
3451
- if (hasBaseClasses) {
3452
- const baseClassName = baseClasses.join(" ");
3453
- const baseStyleObject = parseClassName(baseClassName, state.customTheme);
3454
- const baseStyleKey = generateStyleKey(baseClassName);
3455
- state.styleRegistry.set(baseStyleKey, baseStyleObject);
3456
- styleExpressions.push(
3457
- t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
3458
- );
3459
- }
3460
- if (hasPlatformModifiers) {
3461
- const platformSelectExpression = processPlatformModifiers(
3462
- platformModifiers,
3463
- state,
3464
- parseClassName,
3465
- generateStyleKey,
3466
- t
3467
- );
3468
- styleExpressions.push(platformSelectExpression);
3469
- }
3470
- if (hasColorSchemeModifiers && componentScope) {
3471
- const colorSchemeConditionals = processColorSchemeModifiers(
3472
- colorSchemeModifiers,
3473
- state,
3474
- parseClassName,
3475
- generateStyleKey,
3476
- t
3477
- );
3478
- styleExpressions.push(...colorSchemeConditionals);
3479
- }
3480
- const styleExpression = styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
3481
- const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3482
- if (styleAttribute2) {
3483
- const existingStyle = styleAttribute2.value;
3484
- if (t.isJSXExpressionContainer(existingStyle) && !t.isJSXEmptyExpression(existingStyle.expression)) {
3485
- const existing = existingStyle.expression;
3486
- const mergedArray = t.isArrayExpression(existing) ? t.arrayExpression([styleExpression, ...existing.elements]) : t.arrayExpression([styleExpression, existing]);
3487
- styleAttribute2.value = t.jsxExpressionContainer(mergedArray);
3488
- } else {
3489
- styleAttribute2.value = t.jsxExpressionContainer(styleExpression);
3490
- }
3491
- path2.remove();
3492
- } else {
3493
- path2.node.name = t.jsxIdentifier(targetStyleProp);
3494
- path2.node.value = t.jsxExpressionContainer(styleExpression);
3495
- }
3496
- return true;
3497
- }
3498
- if (hasStateModifiers) {
3499
- const jsxOpeningElement = path2.parent;
3500
- const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
3501
- if (componentSupport) {
3502
- const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
3503
- const unsupportedModifiers = usedModifiers.filter(
3504
- (mod) => !componentSupport.supportedModifiers.includes(mod)
3505
- );
3506
- if (unsupportedModifiers.length > 0) {
3507
- if (process.env.NODE_ENV !== "production") {
3508
- console.warn(
3509
- `[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(", ")}`
3510
- );
3511
- }
3512
- const supportedModifierClasses = stateModifiers.filter(
3513
- (m) => componentSupport.supportedModifiers.includes(m.modifier)
3514
- );
3515
- if (supportedModifierClasses.length === 0) {
3516
- } else {
3517
- const filteredClassName = baseClasses.join(" ") + " " + supportedModifierClasses.map((m) => `${m.modifier}:${m.baseClass}`).join(" ");
3518
- const styleExpression = processStaticClassNameWithModifiers(
3519
- filteredClassName.trim(),
3520
- state,
3521
- parseClassName,
3522
- generateStyleKey,
3523
- splitModifierClasses,
3524
- t
3525
- );
3526
- const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
3527
- const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
3528
- const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3529
- if (styleAttribute2) {
3530
- mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3531
- } else {
3532
- replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3533
- }
3534
- return true;
3535
- }
3536
- } else {
3537
- const styleExpression = processStaticClassNameWithModifiers(
3538
- trimmedClassName,
3539
- state,
3540
- parseClassName,
3541
- generateStyleKey,
3542
- splitModifierClasses,
3543
- t
3544
- );
3545
- const modifierTypes = usedModifiers;
3546
- const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
3547
- const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
3548
- if (styleAttribute2) {
3549
- mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
3550
- } else {
3551
- replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
3552
- }
3553
- return true;
3554
- }
3555
- } else {
3556
- if (process.env.NODE_ENV !== "production") {
3557
- const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
3558
- console.warn(
3559
- `[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"}`
3560
- );
3561
- }
3562
- }
3563
- }
3564
- const classNameForStyle = baseClasses.join(" ");
3565
- if (!classNameForStyle) {
3566
- path2.remove();
3567
- return true;
3568
- }
3569
- const styleObject = parseClassName(classNameForStyle, state.customTheme);
3570
- const styleKey = generateStyleKey(classNameForStyle);
3571
- state.styleRegistry.set(styleKey, styleObject);
3572
- const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
3573
- if (styleAttribute) {
3574
- mergeStyleAttribute(path2, styleAttribute, styleKey, state.stylesIdentifier, t);
3575
- } else {
3576
- replaceWithStyleAttribute(path2, styleKey, targetStyleProp, state.stylesIdentifier, t);
3577
- }
3578
- return true;
3579
- };
3580
- if (t.isStringLiteral(value)) {
3581
- if (processStaticClassName(value.value)) {
3582
- return;
3583
- }
3584
- }
3585
- if (t.isJSXExpressionContainer(value)) {
3586
- const expression = value.expression;
3587
- if (t.isJSXEmptyExpression(expression)) {
3588
- return;
3589
- }
3590
- if (t.isStringLiteral(expression)) {
3591
- if (processStaticClassName(expression.value)) {
3592
- return;
3593
- }
3594
- }
3595
- try {
3596
- const componentScope = findComponentScope(path2, t);
3597
- const result = processDynamicExpression(
3598
- expression,
3599
- state,
3600
- parseClassName,
3601
- generateStyleKey,
3602
- splitModifierClasses,
3603
- processPlatformModifiers,
3604
- processColorSchemeModifiers,
3605
- componentScope,
3606
- isPlatformModifier,
3607
- isColorSchemeModifier,
3608
- isSchemeModifier,
3609
- expandSchemeModifier,
3610
- t
3611
- );
3612
- if (result) {
3613
- state.hasClassNames = true;
3614
- const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
3615
- if (styleAttribute) {
3616
- mergeDynamicStyleAttribute(path2, styleAttribute, result, t);
3617
- } else {
3618
- replaceDynamicWithStyleAttribute(path2, result, targetStyleProp, t);
3619
- }
3620
- return;
3621
- }
3622
- } catch (error) {
3623
- if (process.env.NODE_ENV !== "production") {
3624
- console.warn(
3625
- `[react-native-tailwind] Failed to process dynamic ${attributeName} at ${state.file.opts.filename ?? "unknown"}: ${error instanceof Error ? error.message : String(error)}`
3626
- );
3627
- }
3628
- }
3629
- }
3630
- if (process.env.NODE_ENV !== "production") {
3631
- const filename = state.file.opts.filename ?? "unknown";
3632
- console.warn(
3633
- `[react-native-tailwind] Dynamic ${attributeName} values are not fully supported at ${filename}. Use the ${targetStyleProp} prop for dynamic values.`
3634
- );
3635
- }
4267
+ jsxAttributeVisitor(path2, state, t);
3636
4268
  }
3637
4269
  }
3638
4270
  };