@mgcrea/react-native-tailwind 0.12.1 → 0.13.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.
- package/README.md +29 -2014
- package/dist/babel/index.cjs +1462 -1155
- package/dist/babel/plugin/componentScope.d.ts +26 -0
- package/dist/babel/plugin/componentScope.ts +87 -0
- package/dist/babel/plugin/state.d.ts +119 -0
- package/dist/babel/plugin/state.ts +177 -0
- package/dist/babel/plugin/visitors/className.d.ts +11 -0
- package/{src/babel/plugin.test.ts → dist/babel/plugin/visitors/className.test.ts} +74 -674
- package/dist/babel/plugin/visitors/className.ts +624 -0
- package/dist/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/dist/babel/plugin/visitors/imports.d.ts +11 -0
- package/dist/babel/plugin/visitors/imports.test.ts +88 -0
- package/dist/babel/plugin/visitors/imports.ts +101 -0
- package/dist/babel/plugin/visitors/program.d.ts +15 -0
- package/dist/babel/plugin/visitors/program.test.ts +325 -0
- package/dist/babel/plugin/visitors/program.ts +99 -0
- package/dist/babel/plugin/visitors/tw.d.ts +16 -0
- package/dist/babel/plugin/visitors/tw.test.ts +620 -0
- package/dist/babel/plugin/visitors/tw.ts +148 -0
- package/dist/babel/plugin.d.ts +3 -96
- package/dist/babel/plugin.test.ts +470 -0
- package/dist/babel/plugin.ts +28 -963
- package/dist/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/dist/babel/utils/componentSupport.test.ts +20 -7
- package/dist/babel/utils/componentSupport.ts +2 -0
- package/dist/babel/utils/modifierProcessing.ts +21 -0
- package/dist/babel/utils/platformModifierProcessing.ts +11 -0
- package/dist/babel/utils/styleInjection.d.ts +15 -0
- package/dist/babel/utils/styleInjection.ts +115 -0
- package/dist/babel/utils/twProcessing.ts +11 -0
- package/dist/babel/utils/windowDimensionsProcessing.d.ts +56 -0
- package/dist/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/dist/components/TouchableOpacity.d.ts +35 -0
- package/dist/components/TouchableOpacity.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +1 -0
- package/dist/config/markers.d.ts +5 -0
- package/dist/config/markers.js +1 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +1 -1
- package/dist/parser/borders.d.ts +3 -1
- package/dist/parser/borders.js +1 -1
- package/dist/parser/borders.test.js +1 -1
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.js +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +4 -4
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +4 -4
- package/package.json +1 -1
- package/src/babel/plugin/componentScope.ts +87 -0
- package/src/babel/plugin/state.ts +177 -0
- package/src/babel/plugin/visitors/className.test.ts +1312 -0
- package/src/babel/plugin/visitors/className.ts +624 -0
- package/src/babel/plugin/visitors/className.windowDimensions.test.ts +406 -0
- package/src/babel/plugin/visitors/imports.test.ts +88 -0
- package/src/babel/plugin/visitors/imports.ts +101 -0
- package/src/babel/plugin/visitors/program.test.ts +325 -0
- package/src/babel/plugin/visitors/program.ts +99 -0
- package/src/babel/plugin/visitors/tw.test.ts +620 -0
- package/src/babel/plugin/visitors/tw.ts +148 -0
- package/src/babel/plugin.ts +28 -963
- package/src/babel/utils/colorSchemeModifierProcessing.ts +11 -0
- package/src/babel/utils/componentSupport.test.ts +20 -7
- package/src/babel/utils/componentSupport.ts +2 -0
- package/src/babel/utils/modifierProcessing.ts +21 -0
- package/src/babel/utils/platformModifierProcessing.ts +11 -0
- package/src/babel/utils/styleInjection.ts +115 -0
- package/src/babel/utils/twProcessing.ts +11 -0
- package/src/babel/utils/windowDimensionsProcessing.ts +121 -0
- package/src/components/TouchableOpacity.tsx +71 -0
- package/src/components/index.ts +3 -0
- package/src/config/markers.ts +5 -0
- package/src/index.ts +4 -5
- package/src/parser/borders.test.ts +58 -0
- package/src/parser/borders.ts +18 -3
- package/src/parser/colors.test.ts +249 -0
- package/src/parser/colors.ts +38 -0
- package/src/parser/index.ts +2 -2
- package/src/parser/sizing.ts +11 -0
package/dist/babel/index.cjs
CHANGED
|
@@ -34,6 +34,197 @@ __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
|
+
customTheme,
|
|
216
|
+
schemeModifierConfig,
|
|
217
|
+
supportedAttributes: exactMatches,
|
|
218
|
+
attributePatterns: patterns,
|
|
219
|
+
stylesIdentifier,
|
|
220
|
+
twImportNames: /* @__PURE__ */ new Set(),
|
|
221
|
+
hasTwImport: false,
|
|
222
|
+
reactNativeImportPath: void 0,
|
|
223
|
+
functionComponentsNeedingColorScheme: /* @__PURE__ */ new Set(),
|
|
224
|
+
functionComponentsNeedingWindowDimensions: /* @__PURE__ */ new Set()
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
37
228
|
// src/parser/aspectRatio.ts
|
|
38
229
|
var ASPECT_RATIO_PRESETS = {
|
|
39
230
|
"aspect-auto": void 0,
|
|
@@ -77,187 +268,6 @@ function parseAspectRatio(cls) {
|
|
|
77
268
|
return null;
|
|
78
269
|
}
|
|
79
270
|
|
|
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
271
|
// src/config/tailwind.ts
|
|
262
272
|
var TAILWIND_COLORS = {
|
|
263
273
|
red: {
|
|
@@ -548,20 +558,6 @@ var TAILWIND_COLORS = {
|
|
|
548
558
|
}
|
|
549
559
|
};
|
|
550
560
|
|
|
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
561
|
// src/parser/colors.ts
|
|
566
562
|
var COLORS = {
|
|
567
563
|
...flattenColors(TAILWIND_COLORS),
|
|
@@ -663,95 +659,312 @@ function parseColor(cls, customColors) {
|
|
|
663
659
|
return { borderColor: color };
|
|
664
660
|
}
|
|
665
661
|
}
|
|
662
|
+
const dirBorderMatch = cls.match(/^border-([trblxy])-(.+)$/);
|
|
663
|
+
if (dirBorderMatch) {
|
|
664
|
+
const dir = dirBorderMatch[1];
|
|
665
|
+
const colorKey = dirBorderMatch[2];
|
|
666
|
+
if (colorKey.startsWith("[") && !colorKey.startsWith("[#")) {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
const color = parseColorWithOpacity(colorKey);
|
|
670
|
+
if (color) {
|
|
671
|
+
if (dir === "x") {
|
|
672
|
+
return {
|
|
673
|
+
borderLeftColor: color,
|
|
674
|
+
borderRightColor: color
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
if (dir === "y") {
|
|
678
|
+
return {
|
|
679
|
+
borderTopColor: color,
|
|
680
|
+
borderBottomColor: color
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
const propMap = {
|
|
684
|
+
t: "borderTopColor",
|
|
685
|
+
r: "borderRightColor",
|
|
686
|
+
b: "borderBottomColor",
|
|
687
|
+
l: "borderLeftColor"
|
|
688
|
+
};
|
|
689
|
+
return { [propMap[dir]]: color };
|
|
690
|
+
}
|
|
691
|
+
}
|
|
666
692
|
return null;
|
|
667
693
|
}
|
|
668
694
|
|
|
669
|
-
// src/parser/
|
|
670
|
-
|
|
671
|
-
|
|
695
|
+
// src/parser/borders.ts
|
|
696
|
+
var BORDER_WIDTH_SCALE = {
|
|
697
|
+
"": 1,
|
|
698
|
+
"0": 0,
|
|
699
|
+
"2": 2,
|
|
700
|
+
"4": 4,
|
|
701
|
+
"8": 8
|
|
702
|
+
};
|
|
703
|
+
var BORDER_RADIUS_SCALE = {
|
|
704
|
+
none: 0,
|
|
705
|
+
sm: 2,
|
|
706
|
+
"": 4,
|
|
707
|
+
md: 6,
|
|
708
|
+
lg: 8,
|
|
709
|
+
xl: 12,
|
|
710
|
+
"2xl": 16,
|
|
711
|
+
"3xl": 24,
|
|
712
|
+
full: 9999
|
|
713
|
+
};
|
|
714
|
+
var BORDER_WIDTH_PROP_MAP = {
|
|
715
|
+
t: "borderTopWidth",
|
|
716
|
+
r: "borderRightWidth",
|
|
717
|
+
b: "borderBottomWidth",
|
|
718
|
+
l: "borderLeftWidth"
|
|
719
|
+
};
|
|
720
|
+
var BORDER_RADIUS_CORNER_MAP = {
|
|
721
|
+
tl: "borderTopLeftRadius",
|
|
722
|
+
tr: "borderTopRightRadius",
|
|
723
|
+
bl: "borderBottomLeftRadius",
|
|
724
|
+
br: "borderBottomRightRadius"
|
|
725
|
+
};
|
|
726
|
+
var BORDER_RADIUS_SIDE_MAP = {
|
|
727
|
+
t: ["borderTopLeftRadius", "borderTopRightRadius"],
|
|
728
|
+
r: ["borderTopRightRadius", "borderBottomRightRadius"],
|
|
729
|
+
b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
|
|
730
|
+
l: ["borderTopLeftRadius", "borderBottomLeftRadius"]
|
|
731
|
+
};
|
|
732
|
+
function parseArbitraryBorderWidth(value) {
|
|
733
|
+
const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
|
|
672
734
|
if (pxMatch) {
|
|
673
735
|
return parseInt(pxMatch[1], 10);
|
|
674
736
|
}
|
|
675
|
-
const percentMatch = value.match(/^\[(-?\d+(?:\.\d+)?)%\]$/);
|
|
676
|
-
if (percentMatch) {
|
|
677
|
-
return `${percentMatch[1]}%`;
|
|
678
|
-
}
|
|
679
737
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
680
738
|
if (process.env.NODE_ENV !== "production") {
|
|
681
739
|
console.warn(
|
|
682
|
-
`[react-native-tailwind] Unsupported arbitrary
|
|
740
|
+
`[react-native-tailwind] Unsupported arbitrary border width value: ${value}. Only px values are supported (e.g., [8px] or [8]).`
|
|
683
741
|
);
|
|
684
742
|
}
|
|
685
743
|
return null;
|
|
686
744
|
}
|
|
687
745
|
return null;
|
|
688
746
|
}
|
|
689
|
-
function
|
|
690
|
-
const
|
|
691
|
-
if (
|
|
692
|
-
return parseInt(
|
|
747
|
+
function parseArbitraryBorderRadius(value) {
|
|
748
|
+
const pxMatch = value.match(/^\[(\d+)(?:px)?\]$/);
|
|
749
|
+
if (pxMatch) {
|
|
750
|
+
return parseInt(pxMatch[1], 10);
|
|
693
751
|
}
|
|
694
752
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
695
753
|
if (process.env.NODE_ENV !== "production") {
|
|
696
754
|
console.warn(
|
|
697
|
-
`[react-native-tailwind]
|
|
755
|
+
`[react-native-tailwind] Unsupported arbitrary border radius value: ${value}. Only px values are supported (e.g., [12px] or [12]).`
|
|
698
756
|
);
|
|
699
757
|
}
|
|
700
758
|
return null;
|
|
701
759
|
}
|
|
702
760
|
return null;
|
|
703
761
|
}
|
|
704
|
-
function
|
|
705
|
-
|
|
706
|
-
if (
|
|
707
|
-
|
|
762
|
+
function parseBorder(cls, customColors) {
|
|
763
|
+
if (cls === "border-solid") return { borderStyle: "solid" };
|
|
764
|
+
if (cls === "border-dotted") return { borderStyle: "dotted" };
|
|
765
|
+
if (cls === "border-dashed") return { borderStyle: "dashed" };
|
|
766
|
+
if (cls.startsWith("border-")) {
|
|
767
|
+
return parseBorderWidth(cls, customColors);
|
|
708
768
|
}
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
769
|
+
if (cls === "border") {
|
|
770
|
+
return { borderWidth: 1 };
|
|
771
|
+
}
|
|
772
|
+
if (cls.startsWith("rounded")) {
|
|
773
|
+
return parseBorderRadius(cls);
|
|
774
|
+
}
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
function parseBorderWidth(cls, customColors) {
|
|
778
|
+
const dirMatch = cls.match(/^border-([trbl])(?:-(.+))?$/);
|
|
779
|
+
if (dirMatch) {
|
|
780
|
+
const dir = dirMatch[1];
|
|
781
|
+
const valueStr = dirMatch[2] || "";
|
|
782
|
+
if (valueStr) {
|
|
783
|
+
const colorResult = parseColor(cls, customColors);
|
|
784
|
+
if (colorResult !== null) {
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (valueStr.startsWith("[")) {
|
|
789
|
+
const arbitraryValue = parseArbitraryBorderWidth(valueStr);
|
|
790
|
+
if (arbitraryValue !== null) {
|
|
791
|
+
return { [BORDER_WIDTH_PROP_MAP[dir]]: arbitraryValue };
|
|
792
|
+
}
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
const scaleValue = BORDER_WIDTH_SCALE[valueStr];
|
|
796
|
+
if (scaleValue !== void 0) {
|
|
797
|
+
return { [BORDER_WIDTH_PROP_MAP[dir]]: scaleValue };
|
|
714
798
|
}
|
|
715
799
|
return null;
|
|
716
800
|
}
|
|
801
|
+
const allMatch = cls.match(/^border-(\d+)$/);
|
|
802
|
+
if (allMatch) {
|
|
803
|
+
const value = BORDER_WIDTH_SCALE[allMatch[1]];
|
|
804
|
+
if (value !== void 0) {
|
|
805
|
+
return { borderWidth: value };
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
const allArbMatch = cls.match(/^border-(\[.+\])$/);
|
|
809
|
+
if (allArbMatch) {
|
|
810
|
+
const arbitraryValue = parseArbitraryBorderWidth(allArbMatch[1]);
|
|
811
|
+
if (arbitraryValue !== null) {
|
|
812
|
+
return { borderWidth: arbitraryValue };
|
|
813
|
+
}
|
|
814
|
+
}
|
|
717
815
|
return null;
|
|
718
816
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
};
|
|
723
|
-
|
|
724
|
-
"
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
817
|
+
function parseBorderRadius(cls) {
|
|
818
|
+
const withoutPrefix = cls.substring(7);
|
|
819
|
+
if (withoutPrefix === "") {
|
|
820
|
+
return { borderRadius: BORDER_RADIUS_SCALE[""] };
|
|
821
|
+
}
|
|
822
|
+
if (!withoutPrefix.startsWith("-")) {
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
const rest = withoutPrefix.substring(1);
|
|
826
|
+
if (rest === "") {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
const cornerMatch = rest.match(/^(tl|tr|bl|br)(?:-(.+))?$/);
|
|
830
|
+
if (cornerMatch) {
|
|
831
|
+
const corner = cornerMatch[1];
|
|
832
|
+
const valueStr = cornerMatch[2] || "";
|
|
833
|
+
if (valueStr.startsWith("[")) {
|
|
834
|
+
const arbitraryValue = parseArbitraryBorderRadius(valueStr);
|
|
835
|
+
if (arbitraryValue !== null) {
|
|
836
|
+
return { [BORDER_RADIUS_CORNER_MAP[corner]]: arbitraryValue };
|
|
837
|
+
}
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
const scaleValue2 = BORDER_RADIUS_SCALE[valueStr];
|
|
841
|
+
if (scaleValue2 !== void 0) {
|
|
842
|
+
return { [BORDER_RADIUS_CORNER_MAP[corner]]: scaleValue2 };
|
|
843
|
+
}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
846
|
+
const sideMatch = rest.match(/^([trbl])(?:-(.+))?$/);
|
|
847
|
+
if (sideMatch) {
|
|
848
|
+
const side = sideMatch[1];
|
|
849
|
+
const valueStr = sideMatch[2] || "";
|
|
850
|
+
let value;
|
|
851
|
+
if (valueStr.startsWith("[")) {
|
|
852
|
+
const arbitraryValue = parseArbitraryBorderRadius(valueStr);
|
|
853
|
+
if (arbitraryValue !== null) {
|
|
854
|
+
value = arbitraryValue;
|
|
855
|
+
} else {
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
} else {
|
|
859
|
+
value = BORDER_RADIUS_SCALE[valueStr];
|
|
860
|
+
}
|
|
861
|
+
if (value !== void 0) {
|
|
862
|
+
const result = {};
|
|
863
|
+
BORDER_RADIUS_SIDE_MAP[side].forEach((prop) => result[prop] = value);
|
|
864
|
+
return result;
|
|
865
|
+
}
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
if (rest.startsWith("[")) {
|
|
869
|
+
const arbitraryValue = parseArbitraryBorderRadius(rest);
|
|
870
|
+
if (arbitraryValue !== null) {
|
|
871
|
+
return { borderRadius: arbitraryValue };
|
|
872
|
+
}
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
const scaleValue = BORDER_RADIUS_SCALE[rest];
|
|
876
|
+
if (scaleValue !== void 0) {
|
|
877
|
+
return { borderRadius: scaleValue };
|
|
878
|
+
}
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/parser/layout.ts
|
|
883
|
+
function parseArbitraryInset(value) {
|
|
884
|
+
const pxMatch = value.match(/^\[(-?\d+)(?:px)?\]$/);
|
|
885
|
+
if (pxMatch) {
|
|
886
|
+
return parseInt(pxMatch[1], 10);
|
|
887
|
+
}
|
|
888
|
+
const percentMatch = value.match(/^\[(-?\d+(?:\.\d+)?)%\]$/);
|
|
889
|
+
if (percentMatch) {
|
|
890
|
+
return `${percentMatch[1]}%`;
|
|
891
|
+
}
|
|
892
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
893
|
+
if (process.env.NODE_ENV !== "production") {
|
|
894
|
+
console.warn(
|
|
895
|
+
`[react-native-tailwind] Unsupported arbitrary inset unit: ${value}. Only px and % are supported.`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
function parseArbitraryZIndex(value) {
|
|
903
|
+
const zMatch = value.match(/^\[(-?\d+)\]$/);
|
|
904
|
+
if (zMatch) {
|
|
905
|
+
return parseInt(zMatch[1], 10);
|
|
906
|
+
}
|
|
907
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
908
|
+
if (process.env.NODE_ENV !== "production") {
|
|
909
|
+
console.warn(
|
|
910
|
+
`[react-native-tailwind] Invalid arbitrary z-index: ${value}. Only integers are supported.`
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
function parseArbitraryGrowShrink(value) {
|
|
918
|
+
const match = value.match(/^\[(\d+(?:\.\d+)?|\.\d+)\]$/);
|
|
919
|
+
if (match) {
|
|
920
|
+
return parseFloat(match[1]);
|
|
921
|
+
}
|
|
922
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
923
|
+
if (process.env.NODE_ENV !== "production") {
|
|
924
|
+
console.warn(
|
|
925
|
+
`[react-native-tailwind] Invalid arbitrary grow/shrink value: ${value}. Only non-negative numbers are supported (e.g., [1.5], [2], [0.5], [.5]).`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
return null;
|
|
931
|
+
}
|
|
932
|
+
var DISPLAY_MAP = {
|
|
933
|
+
flex: { display: "flex" },
|
|
934
|
+
hidden: { display: "none" }
|
|
935
|
+
};
|
|
936
|
+
var FLEX_DIRECTION_MAP = {
|
|
937
|
+
"flex-row": { flexDirection: "row" },
|
|
938
|
+
"flex-row-reverse": { flexDirection: "row-reverse" },
|
|
939
|
+
"flex-col": { flexDirection: "column" },
|
|
940
|
+
"flex-col-reverse": { flexDirection: "column-reverse" }
|
|
941
|
+
};
|
|
942
|
+
var FLEX_WRAP_MAP = {
|
|
943
|
+
"flex-wrap": { flexWrap: "wrap" },
|
|
944
|
+
"flex-wrap-reverse": { flexWrap: "wrap-reverse" },
|
|
945
|
+
"flex-nowrap": { flexWrap: "nowrap" }
|
|
946
|
+
};
|
|
947
|
+
var FLEX_MAP = {
|
|
948
|
+
"flex-1": { flex: 1 },
|
|
949
|
+
"flex-auto": { flex: 1 },
|
|
950
|
+
"flex-none": { flex: 0 }
|
|
951
|
+
};
|
|
952
|
+
var GROW_SHRINK_MAP = {
|
|
953
|
+
grow: { flexGrow: 1 },
|
|
954
|
+
"grow-0": { flexGrow: 0 },
|
|
955
|
+
shrink: { flexShrink: 1 },
|
|
956
|
+
"shrink-0": { flexShrink: 0 },
|
|
957
|
+
// CSS-style aliases
|
|
958
|
+
"flex-grow": { flexGrow: 1 },
|
|
959
|
+
"flex-grow-0": { flexGrow: 0 },
|
|
960
|
+
"flex-shrink": { flexShrink: 1 },
|
|
961
|
+
"flex-shrink-0": { flexShrink: 0 }
|
|
962
|
+
};
|
|
963
|
+
var JUSTIFY_CONTENT_MAP = {
|
|
964
|
+
"justify-start": { justifyContent: "flex-start" },
|
|
965
|
+
"justify-end": { justifyContent: "flex-end" },
|
|
966
|
+
"justify-center": { justifyContent: "center" },
|
|
967
|
+
"justify-between": { justifyContent: "space-between" },
|
|
755
968
|
"justify-around": { justifyContent: "space-around" },
|
|
756
969
|
"justify-evenly": { justifyContent: "space-evenly" }
|
|
757
970
|
};
|
|
@@ -1018,6 +1231,9 @@ function parseShadow(cls) {
|
|
|
1018
1231
|
return null;
|
|
1019
1232
|
}
|
|
1020
1233
|
|
|
1234
|
+
// src/config/markers.ts
|
|
1235
|
+
var RUNTIME_DIMENSIONS_MARKER = "{{RUNTIME:dimensions.";
|
|
1236
|
+
|
|
1021
1237
|
// src/parser/sizing.ts
|
|
1022
1238
|
var SIZE_SCALE = {
|
|
1023
1239
|
0: 0,
|
|
@@ -1095,6 +1311,9 @@ function parseArbitrarySize(value) {
|
|
|
1095
1311
|
function parseSizing(cls) {
|
|
1096
1312
|
if (cls.startsWith("w-")) {
|
|
1097
1313
|
const sizeKey = cls.substring(2);
|
|
1314
|
+
if (sizeKey === "screen") {
|
|
1315
|
+
return { width: `${RUNTIME_DIMENSIONS_MARKER}width}}` };
|
|
1316
|
+
}
|
|
1098
1317
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
1099
1318
|
if (arbitrarySize !== null) {
|
|
1100
1319
|
return { width: arbitrarySize };
|
|
@@ -1113,6 +1332,9 @@ function parseSizing(cls) {
|
|
|
1113
1332
|
}
|
|
1114
1333
|
if (cls.startsWith("h-")) {
|
|
1115
1334
|
const sizeKey = cls.substring(2);
|
|
1335
|
+
if (sizeKey === "screen") {
|
|
1336
|
+
return { height: `${RUNTIME_DIMENSIONS_MARKER}height}}` };
|
|
1337
|
+
}
|
|
1116
1338
|
const arbitrarySize = parseArbitrarySize(sizeKey);
|
|
1117
1339
|
if (arbitrarySize !== null) {
|
|
1118
1340
|
return { height: arbitrarySize };
|
|
@@ -1921,7 +2143,7 @@ function parseClassName(className, customTheme) {
|
|
|
1921
2143
|
function parseClass(cls, customTheme) {
|
|
1922
2144
|
const parsers = [
|
|
1923
2145
|
parseSpacing,
|
|
1924
|
-
parseBorder,
|
|
2146
|
+
(cls2) => parseBorder(cls2, customTheme?.colors),
|
|
1925
2147
|
(cls2) => parseColor(cls2, customTheme?.colors),
|
|
1926
2148
|
parseLayout,
|
|
1927
2149
|
(cls2) => parseTypography(cls2, customTheme?.fontFamily, customTheme?.fontSize),
|
|
@@ -1949,173 +2171,84 @@ function generateStyleKey(className) {
|
|
|
1949
2171
|
return key;
|
|
1950
2172
|
}
|
|
1951
2173
|
|
|
1952
|
-
// src/babel/
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
"
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2174
|
+
// src/babel/utils/windowDimensionsProcessing.ts
|
|
2175
|
+
function hasRuntimeDimensions(styleObject) {
|
|
2176
|
+
return Object.values(styleObject).some(
|
|
2177
|
+
(value) => typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)
|
|
2178
|
+
);
|
|
2179
|
+
}
|
|
2180
|
+
function createRuntimeDimensionObject(styleObject, state, t) {
|
|
2181
|
+
state.needsWindowDimensionsImport = true;
|
|
2182
|
+
const properties = [];
|
|
2183
|
+
for (const [key, value] of Object.entries(styleObject)) {
|
|
2184
|
+
let valueNode;
|
|
2185
|
+
if (typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)) {
|
|
2186
|
+
const match = value.match(/dimensions\.(\w+)/);
|
|
2187
|
+
const prop = match?.[1];
|
|
2188
|
+
if (prop) {
|
|
2189
|
+
valueNode = t.memberExpression(t.identifier(state.windowDimensionsVariableName), t.identifier(prop));
|
|
2190
|
+
} else {
|
|
2191
|
+
valueNode = t.stringLiteral(value);
|
|
1970
2192
|
}
|
|
2193
|
+
} else if (typeof value === "number") {
|
|
2194
|
+
valueNode = t.numericLiteral(value);
|
|
2195
|
+
} else if (typeof value === "string") {
|
|
2196
|
+
valueNode = t.stringLiteral(value);
|
|
2197
|
+
} else if (typeof value === "object" && value !== null) {
|
|
2198
|
+
valueNode = t.valueToNode(value);
|
|
2199
|
+
} else {
|
|
2200
|
+
valueNode = t.valueToNode(value);
|
|
1971
2201
|
}
|
|
1972
|
-
|
|
2202
|
+
properties.push(t.objectProperty(t.identifier(key), valueNode));
|
|
1973
2203
|
}
|
|
1974
|
-
return
|
|
2204
|
+
return t.objectExpression(properties);
|
|
1975
2205
|
}
|
|
1976
|
-
function
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
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);
|
|
2206
|
+
function splitStaticAndRuntimeStyles(styleObject) {
|
|
2207
|
+
const staticStyles = {};
|
|
2208
|
+
const runtimeStyles = {};
|
|
2209
|
+
for (const [key, value] of Object.entries(styleObject)) {
|
|
2210
|
+
if (typeof value === "string" && value.startsWith(RUNTIME_DIMENSIONS_MARKER)) {
|
|
2211
|
+
runtimeStyles[key] = value;
|
|
2212
|
+
} else {
|
|
2213
|
+
staticStyles[key] = value;
|
|
1990
2214
|
}
|
|
1991
|
-
configCache.set(configPath, null);
|
|
1992
|
-
return null;
|
|
1993
2215
|
}
|
|
2216
|
+
return { static: staticStyles, runtime: runtimeStyles };
|
|
1994
2217
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
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;
|
|
2218
|
+
|
|
2219
|
+
// src/babel/utils/colorSchemeModifierProcessing.ts
|
|
2220
|
+
function processColorSchemeModifiers(colorSchemeModifiers, state, parseClassName2, generateStyleKey2, t) {
|
|
2221
|
+
state.needsColorSchemeImport = true;
|
|
2222
|
+
const modifiersByScheme = /* @__PURE__ */ new Map();
|
|
2223
|
+
for (const mod of colorSchemeModifiers) {
|
|
2224
|
+
const scheme = mod.modifier;
|
|
2225
|
+
if (!modifiersByScheme.has(scheme)) {
|
|
2226
|
+
modifiersByScheme.set(scheme, []);
|
|
2227
|
+
}
|
|
2228
|
+
const schemeGroup = modifiersByScheme.get(scheme);
|
|
2229
|
+
if (schemeGroup) {
|
|
2230
|
+
schemeGroup.push(mod);
|
|
2023
2231
|
}
|
|
2024
2232
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2233
|
+
const conditionalExpressions = [];
|
|
2234
|
+
for (const [scheme, modifiers] of modifiersByScheme) {
|
|
2235
|
+
const classNames = modifiers.map((m) => m.baseClass).join(" ");
|
|
2236
|
+
const styleObject = parseClassName2(classNames, state.customTheme);
|
|
2237
|
+
if (hasRuntimeDimensions(styleObject)) {
|
|
2238
|
+
throw new Error(
|
|
2239
|
+
`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.`
|
|
2240
|
+
);
|
|
2241
|
+
}
|
|
2242
|
+
const styleKey = generateStyleKey2(`${scheme}_${classNames}`);
|
|
2243
|
+
state.styleRegistry.set(styleKey, styleObject);
|
|
2244
|
+
const colorSchemeCheck = t.binaryExpression(
|
|
2245
|
+
"===",
|
|
2246
|
+
t.identifier(state.colorSchemeVariableName),
|
|
2247
|
+
t.stringLiteral(scheme)
|
|
2028
2248
|
);
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
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";
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
// src/babel/utils/colorSchemeModifierProcessing.ts
|
|
2092
|
-
function processColorSchemeModifiers(colorSchemeModifiers, state, parseClassName2, generateStyleKey2, t) {
|
|
2093
|
-
state.needsColorSchemeImport = true;
|
|
2094
|
-
const modifiersByScheme = /* @__PURE__ */ new Map();
|
|
2095
|
-
for (const mod of colorSchemeModifiers) {
|
|
2096
|
-
const scheme = mod.modifier;
|
|
2097
|
-
if (!modifiersByScheme.has(scheme)) {
|
|
2098
|
-
modifiersByScheme.set(scheme, []);
|
|
2099
|
-
}
|
|
2100
|
-
const schemeGroup = modifiersByScheme.get(scheme);
|
|
2101
|
-
if (schemeGroup) {
|
|
2102
|
-
schemeGroup.push(mod);
|
|
2103
|
-
}
|
|
2104
|
-
}
|
|
2105
|
-
const conditionalExpressions = [];
|
|
2106
|
-
for (const [scheme, modifiers] of modifiersByScheme) {
|
|
2107
|
-
const classNames = modifiers.map((m) => m.baseClass).join(" ");
|
|
2108
|
-
const styleObject = parseClassName2(classNames, state.customTheme);
|
|
2109
|
-
const styleKey = generateStyleKey2(`${scheme}_${classNames}`);
|
|
2110
|
-
state.styleRegistry.set(styleKey, styleObject);
|
|
2111
|
-
const colorSchemeCheck = t.binaryExpression(
|
|
2112
|
-
"===",
|
|
2113
|
-
t.identifier(state.colorSchemeVariableName),
|
|
2114
|
-
t.stringLiteral(scheme)
|
|
2115
|
-
);
|
|
2116
|
-
const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
|
|
2117
|
-
const conditionalExpression = t.logicalExpression("&&", colorSchemeCheck, styleReference);
|
|
2118
|
-
conditionalExpressions.push(conditionalExpression);
|
|
2249
|
+
const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
|
|
2250
|
+
const conditionalExpression = t.logicalExpression("&&", colorSchemeCheck, styleReference);
|
|
2251
|
+
conditionalExpressions.push(conditionalExpression);
|
|
2119
2252
|
}
|
|
2120
2253
|
return conditionalExpressions;
|
|
2121
2254
|
}
|
|
@@ -2142,6 +2275,8 @@ function getComponentModifierSupport(jsxElement, t) {
|
|
|
2142
2275
|
switch (componentName) {
|
|
2143
2276
|
case "Pressable":
|
|
2144
2277
|
return { component: "Pressable", supportedModifiers: ["active", "hover", "focus", "disabled"] };
|
|
2278
|
+
case "TouchableOpacity":
|
|
2279
|
+
return { component: "TouchableOpacity", supportedModifiers: ["active", "disabled"] };
|
|
2145
2280
|
case "TextInput":
|
|
2146
2281
|
return { component: "TextInput", supportedModifiers: ["focus", "disabled", "placeholder"] };
|
|
2147
2282
|
default:
|
|
@@ -2482,6 +2617,11 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
|
|
|
2482
2617
|
if (baseClasses.length > 0) {
|
|
2483
2618
|
const baseClassName = baseClasses.join(" ");
|
|
2484
2619
|
const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
|
|
2620
|
+
if (hasRuntimeDimensions(baseStyleObject)) {
|
|
2621
|
+
throw new Error(
|
|
2622
|
+
`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.`
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2485
2625
|
const baseStyleKey = generateStyleKey2(baseClassName);
|
|
2486
2626
|
state.styleRegistry.set(baseStyleKey, baseStyleObject);
|
|
2487
2627
|
baseStyleExpression = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey));
|
|
@@ -2491,75 +2631,695 @@ function processStaticClassNameWithModifiers(className, state, parseClassName2,
|
|
|
2491
2631
|
if (!modifiersByType.has(mod.modifier)) {
|
|
2492
2632
|
modifiersByType.set(mod.modifier, []);
|
|
2493
2633
|
}
|
|
2494
|
-
const modGroup = modifiersByType.get(mod.modifier);
|
|
2495
|
-
if (modGroup) {
|
|
2496
|
-
modGroup.push(mod);
|
|
2634
|
+
const modGroup = modifiersByType.get(mod.modifier);
|
|
2635
|
+
if (modGroup) {
|
|
2636
|
+
modGroup.push(mod);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
const styleArrayElements = [];
|
|
2640
|
+
if (baseStyleExpression) {
|
|
2641
|
+
styleArrayElements.push(baseStyleExpression);
|
|
2642
|
+
}
|
|
2643
|
+
for (const [modifierType, modifiers] of modifiersByType) {
|
|
2644
|
+
const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
|
|
2645
|
+
const modifierStyleObject = parseClassName2(modifierClassNames, state.customTheme);
|
|
2646
|
+
if (hasRuntimeDimensions(modifierStyleObject)) {
|
|
2647
|
+
throw new Error(
|
|
2648
|
+
`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.`
|
|
2649
|
+
);
|
|
2650
|
+
}
|
|
2651
|
+
const modifierStyleKey = generateStyleKey2(`${modifierType}_${modifierClassNames}`);
|
|
2652
|
+
state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
|
|
2653
|
+
const stateProperty = getStatePropertyForModifier(modifierType);
|
|
2654
|
+
const conditionalExpression = t.logicalExpression(
|
|
2655
|
+
"&&",
|
|
2656
|
+
t.identifier(stateProperty),
|
|
2657
|
+
t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(modifierStyleKey))
|
|
2658
|
+
);
|
|
2659
|
+
styleArrayElements.push(conditionalExpression);
|
|
2660
|
+
}
|
|
2661
|
+
if (styleArrayElements.length === 1) {
|
|
2662
|
+
return styleArrayElements[0];
|
|
2663
|
+
}
|
|
2664
|
+
return t.arrayExpression(styleArrayElements);
|
|
2665
|
+
}
|
|
2666
|
+
function createStyleFunction(styleExpression, modifierTypes, t) {
|
|
2667
|
+
const paramProperties = [];
|
|
2668
|
+
const usedStateProps = /* @__PURE__ */ new Set();
|
|
2669
|
+
for (const modifierType of modifierTypes) {
|
|
2670
|
+
const stateProperty = getStatePropertyForModifier(modifierType);
|
|
2671
|
+
if (!usedStateProps.has(stateProperty)) {
|
|
2672
|
+
usedStateProps.add(stateProperty);
|
|
2673
|
+
paramProperties.push(
|
|
2674
|
+
t.objectProperty(t.identifier(stateProperty), t.identifier(stateProperty), false, true)
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
const param = t.objectPattern(paramProperties);
|
|
2679
|
+
return t.arrowFunctionExpression([param], styleExpression);
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// src/babel/utils/platformModifierProcessing.ts
|
|
2683
|
+
function processPlatformModifiers(platformModifiers, state, parseClassName2, generateStyleKey2, t) {
|
|
2684
|
+
state.needsPlatformImport = true;
|
|
2685
|
+
const modifiersByPlatform = /* @__PURE__ */ new Map();
|
|
2686
|
+
for (const mod of platformModifiers) {
|
|
2687
|
+
const platform = mod.modifier;
|
|
2688
|
+
if (!modifiersByPlatform.has(platform)) {
|
|
2689
|
+
modifiersByPlatform.set(platform, []);
|
|
2690
|
+
}
|
|
2691
|
+
const platformGroup = modifiersByPlatform.get(platform);
|
|
2692
|
+
if (platformGroup) {
|
|
2693
|
+
platformGroup.push(mod);
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
const selectProperties = [];
|
|
2697
|
+
for (const [platform, modifiers] of modifiersByPlatform) {
|
|
2698
|
+
const classNames = modifiers.map((m) => m.baseClass).join(" ");
|
|
2699
|
+
const styleObject = parseClassName2(classNames, state.customTheme);
|
|
2700
|
+
if (hasRuntimeDimensions(styleObject)) {
|
|
2701
|
+
throw new Error(
|
|
2702
|
+
`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.`
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
const styleKey = generateStyleKey2(`${platform}_${classNames}`);
|
|
2706
|
+
state.styleRegistry.set(styleKey, styleObject);
|
|
2707
|
+
const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
|
|
2708
|
+
selectProperties.push(t.objectProperty(t.identifier(platform), styleReference));
|
|
2709
|
+
}
|
|
2710
|
+
return t.callExpression(t.memberExpression(t.identifier("Platform"), t.identifier("select")), [
|
|
2711
|
+
t.objectExpression(selectProperties)
|
|
2712
|
+
]);
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
// src/babel/utils/styleTransforms.ts
|
|
2716
|
+
function getStyleExpression(styleAttribute, t) {
|
|
2717
|
+
const value = styleAttribute.value;
|
|
2718
|
+
if (!t.isJSXExpressionContainer(value)) return null;
|
|
2719
|
+
const expression = value.expression;
|
|
2720
|
+
if (t.isJSXEmptyExpression(expression)) return null;
|
|
2721
|
+
return expression;
|
|
2722
|
+
}
|
|
2723
|
+
function findStyleAttribute(path2, targetStyleProp, t) {
|
|
2724
|
+
const parent = path2.parent;
|
|
2725
|
+
return parent.attributes.find(
|
|
2726
|
+
(attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === targetStyleProp
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, stylesIdentifier, t) {
|
|
2730
|
+
const styleAttribute = t.jsxAttribute(
|
|
2731
|
+
t.jsxIdentifier(targetStyleProp),
|
|
2732
|
+
t.jsxExpressionContainer(t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)))
|
|
2733
|
+
);
|
|
2734
|
+
classNamePath.replaceWith(styleAttribute);
|
|
2735
|
+
}
|
|
2736
|
+
function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, stylesIdentifier, t) {
|
|
2737
|
+
const existingStyle = getStyleExpression(styleAttribute, t);
|
|
2738
|
+
if (!existingStyle) return;
|
|
2739
|
+
if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
|
|
2740
|
+
const paramIdentifier = t.identifier("_state");
|
|
2741
|
+
const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
|
|
2742
|
+
const mergedArray = t.arrayExpression([
|
|
2743
|
+
t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
|
|
2744
|
+
functionCall
|
|
2745
|
+
]);
|
|
2746
|
+
const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
|
|
2747
|
+
styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
|
|
2748
|
+
} else {
|
|
2749
|
+
const styleArray = t.arrayExpression([
|
|
2750
|
+
t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
|
|
2751
|
+
existingStyle
|
|
2752
|
+
]);
|
|
2753
|
+
styleAttribute.value = t.jsxExpressionContainer(styleArray);
|
|
2754
|
+
}
|
|
2755
|
+
classNamePath.remove();
|
|
2756
|
+
}
|
|
2757
|
+
function replaceDynamicWithStyleAttribute(classNamePath, result, targetStyleProp, t) {
|
|
2758
|
+
const styleAttribute = t.jsxAttribute(
|
|
2759
|
+
t.jsxIdentifier(targetStyleProp),
|
|
2760
|
+
t.jsxExpressionContainer(result.expression)
|
|
2761
|
+
);
|
|
2762
|
+
classNamePath.replaceWith(styleAttribute);
|
|
2763
|
+
}
|
|
2764
|
+
function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
|
|
2765
|
+
const existingStyle = getStyleExpression(styleAttribute, t);
|
|
2766
|
+
if (!existingStyle) return;
|
|
2767
|
+
if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
|
|
2768
|
+
const paramIdentifier = t.identifier("_state");
|
|
2769
|
+
const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
|
|
2770
|
+
const mergedArray = t.arrayExpression([result.expression, functionCall]);
|
|
2771
|
+
const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
|
|
2772
|
+
styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
|
|
2773
|
+
} else {
|
|
2774
|
+
let styleArray;
|
|
2775
|
+
if (t.isArrayExpression(existingStyle)) {
|
|
2776
|
+
styleArray = t.arrayExpression([result.expression, ...existingStyle.elements]);
|
|
2777
|
+
} else {
|
|
2778
|
+
styleArray = t.arrayExpression([result.expression, existingStyle]);
|
|
2779
|
+
}
|
|
2780
|
+
styleAttribute.value = t.jsxExpressionContainer(styleArray);
|
|
2781
|
+
}
|
|
2782
|
+
classNamePath.remove();
|
|
2783
|
+
}
|
|
2784
|
+
function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
|
|
2785
|
+
const styleAttribute = t.jsxAttribute(
|
|
2786
|
+
t.jsxIdentifier(targetStyleProp),
|
|
2787
|
+
t.jsxExpressionContainer(styleFunctionExpression)
|
|
2788
|
+
);
|
|
2789
|
+
classNamePath.replaceWith(styleAttribute);
|
|
2790
|
+
}
|
|
2791
|
+
function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
|
|
2792
|
+
const existingStyle = getStyleExpression(styleAttribute, t);
|
|
2793
|
+
if (!existingStyle) return;
|
|
2794
|
+
if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
|
|
2795
|
+
const paramIdentifier = t.identifier("_state");
|
|
2796
|
+
const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
|
|
2797
|
+
const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
|
|
2798
|
+
const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
|
|
2799
|
+
const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
|
|
2800
|
+
styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
|
|
2801
|
+
} else {
|
|
2802
|
+
const paramIdentifier = t.identifier("_state");
|
|
2803
|
+
const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
|
|
2804
|
+
const mergedArray = t.arrayExpression([functionCall, existingStyle]);
|
|
2805
|
+
const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
|
|
2806
|
+
styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
|
|
2807
|
+
}
|
|
2808
|
+
classNamePath.remove();
|
|
2809
|
+
}
|
|
2810
|
+
function addOrMergePlaceholderTextColorProp(jsxOpeningElement, color, t) {
|
|
2811
|
+
const existingProp = jsxOpeningElement.attributes.find(
|
|
2812
|
+
(attr) => t.isJSXAttribute(attr) && attr.name.name === "placeholderTextColor"
|
|
2813
|
+
);
|
|
2814
|
+
if (existingProp) {
|
|
2815
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2816
|
+
console.warn(
|
|
2817
|
+
`[react-native-tailwind] placeholderTextColor prop will be overridden by className placeholder: modifier. Remove the explicit prop or the placeholder: modifier to avoid confusion.`
|
|
2818
|
+
);
|
|
2819
|
+
}
|
|
2820
|
+
existingProp.value = t.stringLiteral(color);
|
|
2821
|
+
} else {
|
|
2822
|
+
const newProp = t.jsxAttribute(t.jsxIdentifier("placeholderTextColor"), t.stringLiteral(color));
|
|
2823
|
+
jsxOpeningElement.attributes.push(newProp);
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
// src/babel/plugin/componentScope.ts
|
|
2828
|
+
function isComponentScope(functionPath, t) {
|
|
2829
|
+
const node = functionPath.node;
|
|
2830
|
+
const parent = functionPath.parent;
|
|
2831
|
+
const parentPath = functionPath.parentPath;
|
|
2832
|
+
if (t.isClassMethod(parent)) {
|
|
2833
|
+
return false;
|
|
2834
|
+
}
|
|
2835
|
+
if (functionPath.findParent((p) => t.isClassBody(p.node))) {
|
|
2836
|
+
return false;
|
|
2837
|
+
}
|
|
2838
|
+
if (t.isFunctionDeclaration(node)) {
|
|
2839
|
+
if (t.isProgram(parent) || t.isExportNamedDeclaration(parent) || t.isExportDefaultDeclaration(parent)) {
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
|
|
2844
|
+
if (t.isVariableDeclarator(parent)) {
|
|
2845
|
+
const varDeclarationPath = parentPath?.parentPath;
|
|
2846
|
+
if (varDeclarationPath && t.isVariableDeclaration(varDeclarationPath.node) && (t.isProgram(varDeclarationPath.parent) || t.isExportNamedDeclaration(varDeclarationPath.parent))) {
|
|
2847
|
+
if (t.isIdentifier(parent.id)) {
|
|
2848
|
+
const name = parent.id.name;
|
|
2849
|
+
return /^[A-Z]/.test(name);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
return false;
|
|
2855
|
+
}
|
|
2856
|
+
function findComponentScope(path2, t) {
|
|
2857
|
+
let current = path2.getFunctionParent();
|
|
2858
|
+
while (current) {
|
|
2859
|
+
if (t.isFunction(current.node) && isComponentScope(current, t)) {
|
|
2860
|
+
return current;
|
|
2861
|
+
}
|
|
2862
|
+
current = current.getFunctionParent();
|
|
2863
|
+
}
|
|
2864
|
+
return null;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// src/babel/plugin/visitors/className.ts
|
|
2868
|
+
function jsxAttributeVisitor(path2, state, t) {
|
|
2869
|
+
const node = path2.node;
|
|
2870
|
+
if (!t.isJSXIdentifier(node.name)) {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
const attributeName = node.name.name;
|
|
2874
|
+
if (!isAttributeSupported(attributeName, state.supportedAttributes, state.attributePatterns)) {
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
const value = node.value;
|
|
2878
|
+
const targetStyleProp = getTargetStyleProp(attributeName);
|
|
2879
|
+
const processStaticClassName = (className) => {
|
|
2880
|
+
const trimmedClassName = className.trim();
|
|
2881
|
+
if (!trimmedClassName) {
|
|
2882
|
+
path2.remove();
|
|
2883
|
+
return true;
|
|
2884
|
+
}
|
|
2885
|
+
state.hasClassNames = true;
|
|
2886
|
+
const { baseClasses, modifierClasses: rawModifierClasses } = splitModifierClasses(trimmedClassName);
|
|
2887
|
+
const modifierClasses = [];
|
|
2888
|
+
for (const modifier of rawModifierClasses) {
|
|
2889
|
+
if (isSchemeModifier(modifier.modifier)) {
|
|
2890
|
+
const expanded = expandSchemeModifier(
|
|
2891
|
+
modifier,
|
|
2892
|
+
state.customTheme.colors ?? {},
|
|
2893
|
+
state.schemeModifierConfig.darkSuffix,
|
|
2894
|
+
state.schemeModifierConfig.lightSuffix
|
|
2895
|
+
);
|
|
2896
|
+
modifierClasses.push(...expanded);
|
|
2897
|
+
} else {
|
|
2898
|
+
modifierClasses.push(modifier);
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
const placeholderModifiers = modifierClasses.filter((m) => m.modifier === "placeholder");
|
|
2902
|
+
const platformModifiers = modifierClasses.filter((m) => isPlatformModifier(m.modifier));
|
|
2903
|
+
const colorSchemeModifiers = modifierClasses.filter((m) => isColorSchemeModifier(m.modifier));
|
|
2904
|
+
const stateModifiers = modifierClasses.filter(
|
|
2905
|
+
(m) => isStateModifier(m.modifier) && m.modifier !== "placeholder"
|
|
2906
|
+
);
|
|
2907
|
+
if (placeholderModifiers.length > 0) {
|
|
2908
|
+
const jsxOpeningElement = path2.parent;
|
|
2909
|
+
const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
|
|
2910
|
+
if (componentSupport?.supportedModifiers.includes("placeholder")) {
|
|
2911
|
+
const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
|
|
2912
|
+
const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customTheme.colors);
|
|
2913
|
+
if (placeholderColor) {
|
|
2914
|
+
addOrMergePlaceholderTextColorProp(jsxOpeningElement, placeholderColor, t);
|
|
2915
|
+
}
|
|
2916
|
+
} else {
|
|
2917
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2918
|
+
console.warn(
|
|
2919
|
+
`[react-native-tailwind] placeholder: modifier can only be used on TextInput component at ${state.file.opts.filename ?? "unknown"}`
|
|
2920
|
+
);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
const hasPlatformModifiers = platformModifiers.length > 0;
|
|
2925
|
+
const hasColorSchemeModifiers = colorSchemeModifiers.length > 0;
|
|
2926
|
+
const hasStateModifiers = stateModifiers.length > 0;
|
|
2927
|
+
const hasBaseClasses = baseClasses.length > 0;
|
|
2928
|
+
let componentScope = null;
|
|
2929
|
+
if (hasColorSchemeModifiers) {
|
|
2930
|
+
componentScope = findComponentScope(path2, t);
|
|
2931
|
+
if (componentScope) {
|
|
2932
|
+
state.functionComponentsNeedingColorScheme.add(componentScope);
|
|
2933
|
+
} else {
|
|
2934
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2935
|
+
console.warn(
|
|
2936
|
+
`[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.`
|
|
2937
|
+
);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
if (hasStateModifiers && (hasPlatformModifiers || hasColorSchemeModifiers)) {
|
|
2942
|
+
const jsxOpeningElement = path2.parent;
|
|
2943
|
+
const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
|
|
2944
|
+
if (componentSupport) {
|
|
2945
|
+
const styleArrayElements = [];
|
|
2946
|
+
if (hasBaseClasses) {
|
|
2947
|
+
const baseClassName = baseClasses.join(" ");
|
|
2948
|
+
const baseStyleObject = parseClassName(baseClassName, state.customTheme);
|
|
2949
|
+
if (hasRuntimeDimensions(baseStyleObject)) {
|
|
2950
|
+
throw path2.buildCodeFrameError(
|
|
2951
|
+
`w-screen and h-screen cannot be combined with modifiers. Found: "${baseClassName}" with state, platform, or color scheme modifiers. Use w-screen/h-screen without modifiers instead.`
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
const baseStyleKey = generateStyleKey(baseClassName);
|
|
2955
|
+
state.styleRegistry.set(baseStyleKey, baseStyleObject);
|
|
2956
|
+
styleArrayElements.push(
|
|
2957
|
+
t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
if (hasPlatformModifiers) {
|
|
2961
|
+
const platformSelectExpression = processPlatformModifiers(
|
|
2962
|
+
platformModifiers,
|
|
2963
|
+
state,
|
|
2964
|
+
parseClassName,
|
|
2965
|
+
generateStyleKey,
|
|
2966
|
+
t
|
|
2967
|
+
);
|
|
2968
|
+
styleArrayElements.push(platformSelectExpression);
|
|
2969
|
+
}
|
|
2970
|
+
if (hasColorSchemeModifiers && componentScope) {
|
|
2971
|
+
const colorSchemeConditionals = processColorSchemeModifiers(
|
|
2972
|
+
colorSchemeModifiers,
|
|
2973
|
+
state,
|
|
2974
|
+
parseClassName,
|
|
2975
|
+
generateStyleKey,
|
|
2976
|
+
t
|
|
2977
|
+
);
|
|
2978
|
+
styleArrayElements.push(...colorSchemeConditionals);
|
|
2979
|
+
}
|
|
2980
|
+
const modifiersByType = /* @__PURE__ */ new Map();
|
|
2981
|
+
for (const mod of stateModifiers) {
|
|
2982
|
+
const modType = mod.modifier;
|
|
2983
|
+
if (!modifiersByType.has(modType)) {
|
|
2984
|
+
modifiersByType.set(modType, []);
|
|
2985
|
+
}
|
|
2986
|
+
modifiersByType.get(modType)?.push(mod);
|
|
2987
|
+
}
|
|
2988
|
+
for (const [modifierType, modifiers] of modifiersByType) {
|
|
2989
|
+
if (!componentSupport.supportedModifiers.includes(modifierType)) {
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
|
|
2993
|
+
const modifierStyleObject = parseClassName(modifierClassNames, state.customTheme);
|
|
2994
|
+
const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
|
|
2995
|
+
state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
|
|
2996
|
+
const stateProperty = getStatePropertyForModifier(modifierType);
|
|
2997
|
+
const conditionalExpression = t.logicalExpression(
|
|
2998
|
+
"&&",
|
|
2999
|
+
t.identifier(stateProperty),
|
|
3000
|
+
t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(modifierStyleKey))
|
|
3001
|
+
);
|
|
3002
|
+
styleArrayElements.push(conditionalExpression);
|
|
3003
|
+
}
|
|
3004
|
+
const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier))).filter(
|
|
3005
|
+
(mod) => componentSupport.supportedModifiers.includes(mod)
|
|
3006
|
+
);
|
|
3007
|
+
const styleArrayExpression = t.arrayExpression(styleArrayElements);
|
|
3008
|
+
const styleFunctionExpression = createStyleFunction(styleArrayExpression, usedModifiers, t);
|
|
3009
|
+
const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
|
|
3010
|
+
if (styleAttribute2) {
|
|
3011
|
+
mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
|
|
3012
|
+
} else {
|
|
3013
|
+
replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
|
|
3014
|
+
}
|
|
3015
|
+
return true;
|
|
3016
|
+
} else {
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
if ((hasPlatformModifiers || hasColorSchemeModifiers) && !hasStateModifiers) {
|
|
3020
|
+
const styleExpressions = [];
|
|
3021
|
+
if (hasBaseClasses) {
|
|
3022
|
+
const baseClassName = baseClasses.join(" ");
|
|
3023
|
+
const baseStyleObject = parseClassName(baseClassName, state.customTheme);
|
|
3024
|
+
if (hasRuntimeDimensions(baseStyleObject)) {
|
|
3025
|
+
throw path2.buildCodeFrameError(
|
|
3026
|
+
`w-screen and h-screen cannot be combined with modifiers. Found: "${baseClassName}" with platform or color scheme modifiers. Use w-screen/h-screen without modifiers instead.`
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
const baseStyleKey = generateStyleKey(baseClassName);
|
|
3030
|
+
state.styleRegistry.set(baseStyleKey, baseStyleObject);
|
|
3031
|
+
styleExpressions.push(
|
|
3032
|
+
t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey))
|
|
3033
|
+
);
|
|
3034
|
+
}
|
|
3035
|
+
if (hasPlatformModifiers) {
|
|
3036
|
+
const platformSelectExpression = processPlatformModifiers(
|
|
3037
|
+
platformModifiers,
|
|
3038
|
+
state,
|
|
3039
|
+
parseClassName,
|
|
3040
|
+
generateStyleKey,
|
|
3041
|
+
t
|
|
3042
|
+
);
|
|
3043
|
+
styleExpressions.push(platformSelectExpression);
|
|
3044
|
+
}
|
|
3045
|
+
if (hasColorSchemeModifiers && componentScope) {
|
|
3046
|
+
const colorSchemeConditionals = processColorSchemeModifiers(
|
|
3047
|
+
colorSchemeModifiers,
|
|
3048
|
+
state,
|
|
3049
|
+
parseClassName,
|
|
3050
|
+
generateStyleKey,
|
|
3051
|
+
t
|
|
3052
|
+
);
|
|
3053
|
+
styleExpressions.push(...colorSchemeConditionals);
|
|
3054
|
+
}
|
|
3055
|
+
const styleExpression = styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
|
|
3056
|
+
const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
|
|
3057
|
+
if (styleAttribute2) {
|
|
3058
|
+
const existingStyle = styleAttribute2.value;
|
|
3059
|
+
if (t.isJSXExpressionContainer(existingStyle) && !t.isJSXEmptyExpression(existingStyle.expression)) {
|
|
3060
|
+
const existing = existingStyle.expression;
|
|
3061
|
+
const mergedArray = t.isArrayExpression(existing) ? t.arrayExpression([styleExpression, ...existing.elements]) : t.arrayExpression([styleExpression, existing]);
|
|
3062
|
+
styleAttribute2.value = t.jsxExpressionContainer(mergedArray);
|
|
3063
|
+
} else {
|
|
3064
|
+
styleAttribute2.value = t.jsxExpressionContainer(styleExpression);
|
|
3065
|
+
}
|
|
3066
|
+
path2.remove();
|
|
3067
|
+
} else {
|
|
3068
|
+
path2.node.name = t.jsxIdentifier(targetStyleProp);
|
|
3069
|
+
path2.node.value = t.jsxExpressionContainer(styleExpression);
|
|
3070
|
+
}
|
|
3071
|
+
return true;
|
|
3072
|
+
}
|
|
3073
|
+
if (hasStateModifiers) {
|
|
3074
|
+
const jsxOpeningElement = path2.parent;
|
|
3075
|
+
const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
|
|
3076
|
+
if (componentSupport) {
|
|
3077
|
+
const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
|
|
3078
|
+
const unsupportedModifiers = usedModifiers.filter(
|
|
3079
|
+
(mod) => !componentSupport.supportedModifiers.includes(mod)
|
|
3080
|
+
);
|
|
3081
|
+
if (unsupportedModifiers.length > 0) {
|
|
3082
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3083
|
+
console.warn(
|
|
3084
|
+
`[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(", ")}`
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
const supportedModifierClasses = stateModifiers.filter(
|
|
3088
|
+
(m) => componentSupport.supportedModifiers.includes(m.modifier)
|
|
3089
|
+
);
|
|
3090
|
+
if (supportedModifierClasses.length === 0) {
|
|
3091
|
+
} else {
|
|
3092
|
+
const filteredClassName = baseClasses.join(" ") + " " + supportedModifierClasses.map((m) => `${m.modifier}:${m.baseClass}`).join(" ");
|
|
3093
|
+
const styleExpression = processStaticClassNameWithModifiers(
|
|
3094
|
+
filteredClassName.trim(),
|
|
3095
|
+
state,
|
|
3096
|
+
parseClassName,
|
|
3097
|
+
generateStyleKey,
|
|
3098
|
+
splitModifierClasses,
|
|
3099
|
+
t
|
|
3100
|
+
);
|
|
3101
|
+
const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
|
|
3102
|
+
const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
|
|
3103
|
+
const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
|
|
3104
|
+
if (styleAttribute2) {
|
|
3105
|
+
mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
|
|
3106
|
+
} else {
|
|
3107
|
+
replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
|
|
3108
|
+
}
|
|
3109
|
+
return true;
|
|
3110
|
+
}
|
|
3111
|
+
} else {
|
|
3112
|
+
const styleExpression = processStaticClassNameWithModifiers(
|
|
3113
|
+
trimmedClassName,
|
|
3114
|
+
state,
|
|
3115
|
+
parseClassName,
|
|
3116
|
+
generateStyleKey,
|
|
3117
|
+
splitModifierClasses,
|
|
3118
|
+
t
|
|
3119
|
+
);
|
|
3120
|
+
const modifierTypes = usedModifiers;
|
|
3121
|
+
const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
|
|
3122
|
+
const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
|
|
3123
|
+
if (styleAttribute2) {
|
|
3124
|
+
mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
|
|
3125
|
+
} else {
|
|
3126
|
+
replaceWithStyleFunctionAttribute(path2, styleFunctionExpression, targetStyleProp, t);
|
|
3127
|
+
}
|
|
3128
|
+
return true;
|
|
3129
|
+
}
|
|
3130
|
+
} else {
|
|
3131
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3132
|
+
const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
|
|
3133
|
+
console.warn(
|
|
3134
|
+
`[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"}`
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
const classNameForStyle = baseClasses.join(" ");
|
|
3140
|
+
if (!classNameForStyle) {
|
|
3141
|
+
path2.remove();
|
|
3142
|
+
return true;
|
|
3143
|
+
}
|
|
3144
|
+
const styleObject = parseClassName(classNameForStyle, state.customTheme);
|
|
3145
|
+
if (hasRuntimeDimensions(styleObject)) {
|
|
3146
|
+
const { static: staticStyles, runtime: runtimeStyles } = splitStaticAndRuntimeStyles(styleObject);
|
|
3147
|
+
const componentScope2 = findComponentScope(path2, t);
|
|
3148
|
+
if (componentScope2) {
|
|
3149
|
+
state.hasClassNames = true;
|
|
3150
|
+
state.functionComponentsNeedingWindowDimensions.add(componentScope2);
|
|
3151
|
+
state.needsWindowDimensionsImport = true;
|
|
3152
|
+
const styleExpressions = [];
|
|
3153
|
+
if (Object.keys(staticStyles).length > 0) {
|
|
3154
|
+
const styleKey2 = generateStyleKey(classNameForStyle);
|
|
3155
|
+
state.styleRegistry.set(styleKey2, staticStyles);
|
|
3156
|
+
styleExpressions.push(
|
|
3157
|
+
t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey2))
|
|
3158
|
+
);
|
|
3159
|
+
}
|
|
3160
|
+
const runtimeDimensionObject = createRuntimeDimensionObject(runtimeStyles, state, t);
|
|
3161
|
+
styleExpressions.push(runtimeDimensionObject);
|
|
3162
|
+
const styleExpression = styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
|
|
3163
|
+
const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
|
|
3164
|
+
if (styleAttribute2) {
|
|
3165
|
+
const existingStyle = styleAttribute2.value;
|
|
3166
|
+
if (t.isJSXExpressionContainer(existingStyle) && !t.isJSXEmptyExpression(existingStyle.expression)) {
|
|
3167
|
+
const existing = existingStyle.expression;
|
|
3168
|
+
if (t.isArrowFunctionExpression(existing) || t.isFunctionExpression(existing)) {
|
|
3169
|
+
const paramIdentifier = t.identifier("_state");
|
|
3170
|
+
const functionCall = t.callExpression(existing, [paramIdentifier]);
|
|
3171
|
+
const mergedArray = t.arrayExpression([styleExpression, functionCall]);
|
|
3172
|
+
const wrappedFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
|
|
3173
|
+
styleAttribute2.value = t.jsxExpressionContainer(wrappedFunction);
|
|
3174
|
+
} else {
|
|
3175
|
+
const mergedArray = t.isArrayExpression(existing) ? t.arrayExpression([styleExpression, ...existing.elements]) : t.arrayExpression([styleExpression, existing]);
|
|
3176
|
+
styleAttribute2.value = t.jsxExpressionContainer(mergedArray);
|
|
3177
|
+
}
|
|
3178
|
+
} else {
|
|
3179
|
+
styleAttribute2.value = t.jsxExpressionContainer(styleExpression);
|
|
3180
|
+
}
|
|
3181
|
+
path2.remove();
|
|
3182
|
+
} else {
|
|
3183
|
+
path2.node.name = t.jsxIdentifier(targetStyleProp);
|
|
3184
|
+
path2.node.value = t.jsxExpressionContainer(styleExpression);
|
|
3185
|
+
}
|
|
3186
|
+
return true;
|
|
3187
|
+
} else {
|
|
3188
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3189
|
+
console.warn(
|
|
3190
|
+
`[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.`
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
const styleKey = generateStyleKey(classNameForStyle);
|
|
3196
|
+
state.styleRegistry.set(styleKey, styleObject);
|
|
3197
|
+
const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
|
|
3198
|
+
if (styleAttribute) {
|
|
3199
|
+
mergeStyleAttribute(path2, styleAttribute, styleKey, state.stylesIdentifier, t);
|
|
3200
|
+
} else {
|
|
3201
|
+
replaceWithStyleAttribute(path2, styleKey, targetStyleProp, state.stylesIdentifier, t);
|
|
3202
|
+
}
|
|
3203
|
+
return true;
|
|
3204
|
+
};
|
|
3205
|
+
if (t.isStringLiteral(value)) {
|
|
3206
|
+
if (processStaticClassName(value.value)) {
|
|
3207
|
+
return;
|
|
2497
3208
|
}
|
|
2498
3209
|
}
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
const stateProperty = getStatePropertyForModifier(modifierType);
|
|
2526
|
-
if (!usedStateProps.has(stateProperty)) {
|
|
2527
|
-
usedStateProps.add(stateProperty);
|
|
2528
|
-
paramProperties.push(
|
|
2529
|
-
t.objectProperty(t.identifier(stateProperty), t.identifier(stateProperty), false, true)
|
|
3210
|
+
if (t.isJSXExpressionContainer(value)) {
|
|
3211
|
+
const expression = value.expression;
|
|
3212
|
+
if (t.isJSXEmptyExpression(expression)) {
|
|
3213
|
+
return;
|
|
3214
|
+
}
|
|
3215
|
+
if (t.isStringLiteral(expression)) {
|
|
3216
|
+
if (processStaticClassName(expression.value)) {
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
try {
|
|
3221
|
+
const componentScope = findComponentScope(path2, t);
|
|
3222
|
+
const result = processDynamicExpression(
|
|
3223
|
+
expression,
|
|
3224
|
+
state,
|
|
3225
|
+
parseClassName,
|
|
3226
|
+
generateStyleKey,
|
|
3227
|
+
splitModifierClasses,
|
|
3228
|
+
processPlatformModifiers,
|
|
3229
|
+
processColorSchemeModifiers,
|
|
3230
|
+
componentScope,
|
|
3231
|
+
isPlatformModifier,
|
|
3232
|
+
isColorSchemeModifier,
|
|
3233
|
+
isSchemeModifier,
|
|
3234
|
+
expandSchemeModifier,
|
|
3235
|
+
t
|
|
2530
3236
|
);
|
|
3237
|
+
if (result) {
|
|
3238
|
+
state.hasClassNames = true;
|
|
3239
|
+
const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
|
|
3240
|
+
if (styleAttribute) {
|
|
3241
|
+
mergeDynamicStyleAttribute(path2, styleAttribute, result, t);
|
|
3242
|
+
} else {
|
|
3243
|
+
replaceDynamicWithStyleAttribute(path2, result, targetStyleProp, t);
|
|
3244
|
+
}
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
} catch (error) {
|
|
3248
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3249
|
+
console.warn(
|
|
3250
|
+
`[react-native-tailwind] Failed to process dynamic ${attributeName} at ${state.file.opts.filename ?? "unknown"}: ${error instanceof Error ? error.message : String(error)}`
|
|
3251
|
+
);
|
|
3252
|
+
}
|
|
2531
3253
|
}
|
|
2532
3254
|
}
|
|
2533
|
-
|
|
2534
|
-
|
|
3255
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3256
|
+
const filename = state.file.opts.filename ?? "unknown";
|
|
3257
|
+
console.warn(
|
|
3258
|
+
`[react-native-tailwind] Dynamic ${attributeName} values are not fully supported at ${filename}. Use the ${targetStyleProp} prop for dynamic values.`
|
|
3259
|
+
);
|
|
3260
|
+
}
|
|
2535
3261
|
}
|
|
2536
3262
|
|
|
2537
|
-
// src/babel/
|
|
2538
|
-
function
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
const
|
|
2543
|
-
|
|
2544
|
-
|
|
3263
|
+
// src/babel/plugin/visitors/imports.ts
|
|
3264
|
+
function importDeclarationVisitor(path2, state, t) {
|
|
3265
|
+
const node = path2.node;
|
|
3266
|
+
if (node.source.value === "react-native") {
|
|
3267
|
+
const specifiers = node.specifiers;
|
|
3268
|
+
const hasStyleSheet = specifiers.some((spec) => {
|
|
3269
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
3270
|
+
return spec.imported.name === "StyleSheet";
|
|
3271
|
+
}
|
|
3272
|
+
return false;
|
|
3273
|
+
});
|
|
3274
|
+
const hasPlatform = specifiers.some((spec) => {
|
|
3275
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
3276
|
+
return spec.imported.name === "Platform";
|
|
3277
|
+
}
|
|
3278
|
+
return false;
|
|
3279
|
+
});
|
|
3280
|
+
if (node.importKind !== "type") {
|
|
3281
|
+
for (const spec of specifiers) {
|
|
3282
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
3283
|
+
if (spec.imported.name === "useWindowDimensions") {
|
|
3284
|
+
state.hasWindowDimensionsImport = true;
|
|
3285
|
+
state.windowDimensionsLocalIdentifier = spec.local.name;
|
|
3286
|
+
break;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
2545
3290
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
platformGroup.push(mod);
|
|
3291
|
+
if (hasStyleSheet) {
|
|
3292
|
+
state.hasStyleSheetImport = true;
|
|
2549
3293
|
}
|
|
3294
|
+
if (hasPlatform) {
|
|
3295
|
+
state.hasPlatformImport = true;
|
|
3296
|
+
}
|
|
3297
|
+
state.reactNativeImportPath = path2;
|
|
2550
3298
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
const
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
3299
|
+
if (node.source.value === state.colorSchemeImportSource && node.importKind !== "type") {
|
|
3300
|
+
const specifiers = node.specifiers;
|
|
3301
|
+
for (const spec of specifiers) {
|
|
3302
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
3303
|
+
if (spec.imported.name === state.colorSchemeHookName) {
|
|
3304
|
+
state.hasColorSchemeImport = true;
|
|
3305
|
+
state.colorSchemeLocalIdentifier = spec.local.name;
|
|
3306
|
+
break;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
if (node.source.value === "@mgcrea/react-native-tailwind") {
|
|
3312
|
+
const specifiers = node.specifiers;
|
|
3313
|
+
specifiers.forEach((spec) => {
|
|
3314
|
+
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
|
|
3315
|
+
const importedName = spec.imported.name;
|
|
3316
|
+
if (importedName === "tw" || importedName === "twStyle") {
|
|
3317
|
+
const localName = spec.local.name;
|
|
3318
|
+
state.twImportNames.add(localName);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
});
|
|
2559
3322
|
}
|
|
2560
|
-
return t.callExpression(t.memberExpression(t.identifier("Platform"), t.identifier("select")), [
|
|
2561
|
-
t.objectExpression(selectProperties)
|
|
2562
|
-
]);
|
|
2563
3323
|
}
|
|
2564
3324
|
|
|
2565
3325
|
// src/babel/utils/styleInjection.ts
|
|
@@ -2687,152 +3447,100 @@ function injectColorSchemeHook(functionPath, colorSchemeVariableName, hookName,
|
|
|
2687
3447
|
body.body.unshift(hookCall);
|
|
2688
3448
|
return true;
|
|
2689
3449
|
}
|
|
2690
|
-
function
|
|
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);
|
|
2701
|
-
}
|
|
2702
|
-
return t.objectProperty(t.identifier(styleProp), valueNode);
|
|
2703
|
-
});
|
|
2704
|
-
styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
|
|
2705
|
-
}
|
|
2706
|
-
const styleSheet = t.variableDeclaration("const", [
|
|
2707
|
-
t.variableDeclarator(
|
|
2708
|
-
t.identifier(stylesIdentifier),
|
|
2709
|
-
t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
|
|
2710
|
-
t.objectExpression(styleProperties)
|
|
2711
|
-
])
|
|
2712
|
-
)
|
|
2713
|
-
]);
|
|
3450
|
+
function addWindowDimensionsImport(path2, t) {
|
|
2714
3451
|
const body = path2.node.body;
|
|
2715
|
-
let
|
|
2716
|
-
for (
|
|
2717
|
-
if (t.isImportDeclaration(
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
3452
|
+
let existingValueImport = null;
|
|
3453
|
+
for (const statement of body) {
|
|
3454
|
+
if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
|
|
3455
|
+
if (statement.importKind !== "type") {
|
|
3456
|
+
existingValueImport = statement;
|
|
3457
|
+
break;
|
|
3458
|
+
}
|
|
2721
3459
|
}
|
|
2722
3460
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
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);
|
|
2746
|
-
}
|
|
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);
|
|
2759
|
-
} else {
|
|
2760
|
-
const styleArray = t.arrayExpression([
|
|
2761
|
-
t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
|
|
2762
|
-
existingStyle
|
|
2763
|
-
]);
|
|
2764
|
-
styleAttribute.value = t.jsxExpressionContainer(styleArray);
|
|
2765
|
-
}
|
|
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
|
-
}
|
|
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]);
|
|
2788
|
-
} else {
|
|
2789
|
-
styleArray = t.arrayExpression([result.expression, existingStyle]);
|
|
3461
|
+
if (existingValueImport) {
|
|
3462
|
+
const hasHook = existingValueImport.specifiers.some(
|
|
3463
|
+
(spec) => t.isImportSpecifier(spec) && spec.imported.type === "Identifier" && spec.imported.name === "useWindowDimensions"
|
|
3464
|
+
);
|
|
3465
|
+
if (!hasHook) {
|
|
3466
|
+
existingValueImport.specifiers.push(
|
|
3467
|
+
t.importSpecifier(t.identifier("useWindowDimensions"), t.identifier("useWindowDimensions"))
|
|
3468
|
+
);
|
|
2790
3469
|
}
|
|
2791
|
-
|
|
3470
|
+
} else {
|
|
3471
|
+
const importDeclaration = t.importDeclaration(
|
|
3472
|
+
[t.importSpecifier(t.identifier("useWindowDimensions"), t.identifier("useWindowDimensions"))],
|
|
3473
|
+
t.stringLiteral("react-native")
|
|
3474
|
+
);
|
|
3475
|
+
path2.unshiftContainer("body", importDeclaration);
|
|
2792
3476
|
}
|
|
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);
|
|
2801
3477
|
}
|
|
2802
|
-
function
|
|
2803
|
-
|
|
2804
|
-
if (!
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
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);
|
|
3478
|
+
function injectWindowDimensionsHook(functionPath, dimensionsVariableName, hookName, localIdentifier, t) {
|
|
3479
|
+
let body = functionPath.node.body;
|
|
3480
|
+
if (!t.isBlockStatement(body)) {
|
|
3481
|
+
if (t.isArrowFunctionExpression(functionPath.node) && t.isExpression(body)) {
|
|
3482
|
+
const returnStatement = t.returnStatement(body);
|
|
3483
|
+
const blockStatement = t.blockStatement([returnStatement]);
|
|
3484
|
+
functionPath.node.body = blockStatement;
|
|
3485
|
+
body = blockStatement;
|
|
3486
|
+
} else {
|
|
3487
|
+
return false;
|
|
3488
|
+
}
|
|
2818
3489
|
}
|
|
2819
|
-
|
|
3490
|
+
const hasHook = body.body.some((statement) => {
|
|
3491
|
+
if (t.isVariableDeclaration(statement) && statement.declarations.length > 0 && t.isVariableDeclarator(statement.declarations[0])) {
|
|
3492
|
+
const declarator = statement.declarations[0];
|
|
3493
|
+
return t.isIdentifier(declarator.id) && declarator.id.name === dimensionsVariableName;
|
|
3494
|
+
}
|
|
3495
|
+
return false;
|
|
3496
|
+
});
|
|
3497
|
+
if (hasHook) {
|
|
3498
|
+
return false;
|
|
3499
|
+
}
|
|
3500
|
+
const identifierToCall = localIdentifier ?? hookName;
|
|
3501
|
+
const hookCall = t.variableDeclaration("const", [
|
|
3502
|
+
t.variableDeclarator(
|
|
3503
|
+
t.identifier(dimensionsVariableName),
|
|
3504
|
+
t.callExpression(t.identifier(identifierToCall), [])
|
|
3505
|
+
)
|
|
3506
|
+
]);
|
|
3507
|
+
body.body.unshift(hookCall);
|
|
3508
|
+
return true;
|
|
2820
3509
|
}
|
|
2821
|
-
function
|
|
2822
|
-
const
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
3510
|
+
function injectStylesAtTop(path2, styleRegistry, stylesIdentifier, t) {
|
|
3511
|
+
const styleProperties = [];
|
|
3512
|
+
for (const [key, styleObject] of styleRegistry) {
|
|
3513
|
+
const properties = Object.entries(styleObject).map(([styleProp, styleValue]) => {
|
|
3514
|
+
let valueNode;
|
|
3515
|
+
if (typeof styleValue === "number") {
|
|
3516
|
+
valueNode = t.numericLiteral(styleValue);
|
|
3517
|
+
} else if (typeof styleValue === "string") {
|
|
3518
|
+
valueNode = t.stringLiteral(styleValue);
|
|
3519
|
+
} else {
|
|
3520
|
+
valueNode = t.valueToNode(styleValue);
|
|
3521
|
+
}
|
|
3522
|
+
return t.objectProperty(t.identifier(styleProp), valueNode);
|
|
3523
|
+
});
|
|
3524
|
+
styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
|
|
3525
|
+
}
|
|
3526
|
+
const styleSheet = t.variableDeclaration("const", [
|
|
3527
|
+
t.variableDeclarator(
|
|
3528
|
+
t.identifier(stylesIdentifier),
|
|
3529
|
+
t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
|
|
3530
|
+
t.objectExpression(styleProperties)
|
|
3531
|
+
])
|
|
3532
|
+
)
|
|
3533
|
+
]);
|
|
3534
|
+
const body = path2.node.body;
|
|
3535
|
+
let insertIndex = 0;
|
|
3536
|
+
for (let i = 0; i < body.length; i++) {
|
|
3537
|
+
if (t.isImportDeclaration(body[i])) {
|
|
3538
|
+
insertIndex = i + 1;
|
|
3539
|
+
} else {
|
|
3540
|
+
break;
|
|
2830
3541
|
}
|
|
2831
|
-
existingProp.value = t.stringLiteral(color);
|
|
2832
|
-
} else {
|
|
2833
|
-
const newProp = t.jsxAttribute(t.jsxIdentifier("placeholderTextColor"), t.stringLiteral(color));
|
|
2834
|
-
jsxOpeningElement.attributes.push(newProp);
|
|
2835
3542
|
}
|
|
3543
|
+
body.splice(insertIndex, 0, styleSheet);
|
|
2836
3544
|
}
|
|
2837
3545
|
|
|
2838
3546
|
// src/babel/utils/twProcessing.ts
|
|
@@ -2856,6 +3564,11 @@ function processTwCall(className, path2, state, parseClassName2, generateStyleKe
|
|
|
2856
3564
|
if (baseClasses.length > 0) {
|
|
2857
3565
|
const baseClassName = baseClasses.join(" ");
|
|
2858
3566
|
const baseStyleObject = parseClassName2(baseClassName, state.customTheme);
|
|
3567
|
+
if (hasRuntimeDimensions(baseStyleObject)) {
|
|
3568
|
+
throw path2.buildCodeFrameError(
|
|
3569
|
+
`w-screen and h-screen are not supported in tw\`\` or twStyle() calls. Found: "${baseClassName}". Use them in className attributes instead.`
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
2859
3572
|
const baseStyleKey = generateStyleKey2(baseClassName);
|
|
2860
3573
|
state.styleRegistry.set(baseStyleKey, baseStyleObject);
|
|
2861
3574
|
objectProperties.push(
|
|
@@ -3053,586 +3766,180 @@ function removeTwImports(path2, t) {
|
|
|
3053
3766
|
});
|
|
3054
3767
|
}
|
|
3055
3768
|
|
|
3056
|
-
// src/babel/plugin.ts
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
if (t.isClassMethod(parent)) {
|
|
3063
|
-
return false;
|
|
3769
|
+
// src/babel/plugin/visitors/program.ts
|
|
3770
|
+
function programEnter(_path, _state) {
|
|
3771
|
+
}
|
|
3772
|
+
function programExit(path2, state, t) {
|
|
3773
|
+
if (state.hasTwImport) {
|
|
3774
|
+
removeTwImports(path2, t);
|
|
3064
3775
|
}
|
|
3065
|
-
if (
|
|
3066
|
-
return
|
|
3776
|
+
if (!state.hasClassNames && !state.needsWindowDimensionsImport && !state.needsColorSchemeImport) {
|
|
3777
|
+
return;
|
|
3067
3778
|
}
|
|
3068
|
-
if (
|
|
3069
|
-
|
|
3070
|
-
|
|
3779
|
+
if (!state.hasStyleSheetImport && state.styleRegistry.size > 0) {
|
|
3780
|
+
addStyleSheetImport(path2, t);
|
|
3781
|
+
}
|
|
3782
|
+
if (state.needsPlatformImport && !state.hasPlatformImport) {
|
|
3783
|
+
addPlatformImport(path2, t);
|
|
3784
|
+
}
|
|
3785
|
+
if (state.needsColorSchemeImport && !state.hasColorSchemeImport) {
|
|
3786
|
+
addColorSchemeImport(path2, state.colorSchemeImportSource, state.colorSchemeHookName, t);
|
|
3787
|
+
}
|
|
3788
|
+
if (state.needsColorSchemeImport) {
|
|
3789
|
+
for (const functionPath of state.functionComponentsNeedingColorScheme) {
|
|
3790
|
+
injectColorSchemeHook(
|
|
3791
|
+
functionPath,
|
|
3792
|
+
state.colorSchemeVariableName,
|
|
3793
|
+
state.colorSchemeHookName,
|
|
3794
|
+
state.colorSchemeLocalIdentifier,
|
|
3795
|
+
t
|
|
3796
|
+
);
|
|
3071
3797
|
}
|
|
3072
3798
|
}
|
|
3073
|
-
if (
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3799
|
+
if (state.needsWindowDimensionsImport && !state.hasWindowDimensionsImport) {
|
|
3800
|
+
addWindowDimensionsImport(path2, t);
|
|
3801
|
+
}
|
|
3802
|
+
if (state.needsWindowDimensionsImport) {
|
|
3803
|
+
for (const functionPath of state.functionComponentsNeedingWindowDimensions) {
|
|
3804
|
+
injectWindowDimensionsHook(
|
|
3805
|
+
functionPath,
|
|
3806
|
+
state.windowDimensionsVariableName,
|
|
3807
|
+
"useWindowDimensions",
|
|
3808
|
+
state.windowDimensionsLocalIdentifier,
|
|
3809
|
+
t
|
|
3810
|
+
);
|
|
3082
3811
|
}
|
|
3083
3812
|
}
|
|
3084
|
-
|
|
3813
|
+
if (state.styleRegistry.size > 0) {
|
|
3814
|
+
injectStylesAtTop(path2, state.styleRegistry, state.stylesIdentifier, t);
|
|
3815
|
+
}
|
|
3085
3816
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3817
|
+
|
|
3818
|
+
// src/babel/plugin/visitors/tw.ts
|
|
3819
|
+
function taggedTemplateVisitor(path2, state, t) {
|
|
3820
|
+
const node = path2.node;
|
|
3821
|
+
if (!t.isIdentifier(node.tag)) {
|
|
3822
|
+
return;
|
|
3823
|
+
}
|
|
3824
|
+
const tagName = node.tag.name;
|
|
3825
|
+
if (!state.twImportNames.has(tagName)) {
|
|
3826
|
+
return;
|
|
3827
|
+
}
|
|
3828
|
+
const quasi = node.quasi;
|
|
3829
|
+
if (!t.isTemplateLiteral(quasi)) {
|
|
3830
|
+
return;
|
|
3831
|
+
}
|
|
3832
|
+
if (quasi.expressions.length > 0) {
|
|
3833
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3834
|
+
console.warn(
|
|
3835
|
+
`[react-native-tailwind] Dynamic tw\`...\` with interpolations is not supported at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
|
|
3836
|
+
);
|
|
3091
3837
|
}
|
|
3092
|
-
|
|
3838
|
+
return;
|
|
3093
3839
|
}
|
|
3094
|
-
|
|
3840
|
+
const className = quasi.quasis[0]?.value.cooked?.trim() ?? "";
|
|
3841
|
+
if (!className) {
|
|
3842
|
+
path2.replaceWith(t.objectExpression([t.objectProperty(t.identifier("style"), t.objectExpression([]))]));
|
|
3843
|
+
state.hasTwImport = true;
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3846
|
+
state.hasClassNames = true;
|
|
3847
|
+
processTwCall(
|
|
3848
|
+
className,
|
|
3849
|
+
path2,
|
|
3850
|
+
state,
|
|
3851
|
+
parseClassName,
|
|
3852
|
+
generateStyleKey,
|
|
3853
|
+
splitModifierClasses,
|
|
3854
|
+
findComponentScope,
|
|
3855
|
+
t
|
|
3856
|
+
);
|
|
3857
|
+
state.hasTwImport = true;
|
|
3858
|
+
}
|
|
3859
|
+
function callExpressionVisitor(path2, state, t) {
|
|
3860
|
+
const node = path2.node;
|
|
3861
|
+
if (!t.isIdentifier(node.callee)) {
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
const calleeName = node.callee.name;
|
|
3865
|
+
if (!state.twImportNames.has(calleeName)) {
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3868
|
+
if (node.arguments.length !== 1) {
|
|
3869
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3870
|
+
console.warn(
|
|
3871
|
+
`[react-native-tailwind] twStyle() expects exactly one argument at ${state.file.opts.filename ?? "unknown"}`
|
|
3872
|
+
);
|
|
3873
|
+
}
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
const arg = node.arguments[0];
|
|
3877
|
+
if (!t.isStringLiteral(arg)) {
|
|
3878
|
+
if (process.env.NODE_ENV !== "production") {
|
|
3879
|
+
console.warn(
|
|
3880
|
+
`[react-native-tailwind] twStyle() only supports static string literals at ${state.file.opts.filename ?? "unknown"}. Use style prop for dynamic values.`
|
|
3881
|
+
);
|
|
3882
|
+
}
|
|
3883
|
+
return;
|
|
3884
|
+
}
|
|
3885
|
+
const className = arg.value.trim();
|
|
3886
|
+
if (!className) {
|
|
3887
|
+
path2.replaceWith(t.identifier("undefined"));
|
|
3888
|
+
state.hasTwImport = true;
|
|
3889
|
+
return;
|
|
3890
|
+
}
|
|
3891
|
+
state.hasClassNames = true;
|
|
3892
|
+
processTwCall(
|
|
3893
|
+
className,
|
|
3894
|
+
path2,
|
|
3895
|
+
state,
|
|
3896
|
+
parseClassName,
|
|
3897
|
+
generateStyleKey,
|
|
3898
|
+
splitModifierClasses,
|
|
3899
|
+
findComponentScope,
|
|
3900
|
+
t
|
|
3901
|
+
);
|
|
3902
|
+
state.hasTwImport = true;
|
|
3095
3903
|
}
|
|
3904
|
+
|
|
3905
|
+
// src/babel/plugin.ts
|
|
3096
3906
|
function reactNativeTailwindBabelPlugin({ types: t }, options) {
|
|
3097
|
-
const
|
|
3098
|
-
const
|
|
3099
|
-
const stylesIdentifier = options?.stylesIdentifier ?? DEFAULT_STYLES_IDENTIFIER;
|
|
3907
|
+
const colorSchemeImportSource = options?.colorScheme?.importFrom ?? "react-native";
|
|
3908
|
+
const colorSchemeHookName = options?.colorScheme?.importName ?? "useColorScheme";
|
|
3100
3909
|
const schemeModifierConfig = {
|
|
3101
3910
|
darkSuffix: options?.schemeModifier?.darkSuffix ?? "-dark",
|
|
3102
3911
|
lightSuffix: options?.schemeModifier?.lightSuffix ?? "-light"
|
|
3103
3912
|
};
|
|
3104
|
-
const colorSchemeImportSource = options?.colorScheme?.importFrom ?? "react-native";
|
|
3105
|
-
const colorSchemeHookName = options?.colorScheme?.importName ?? "useColorScheme";
|
|
3106
3913
|
return {
|
|
3107
3914
|
name: "react-native-tailwind",
|
|
3108
3915
|
visitor: {
|
|
3109
3916
|
Program: {
|
|
3110
|
-
enter(
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
state
|
|
3119
|
-
state
|
|
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;
|
|
3917
|
+
enter(path2, state) {
|
|
3918
|
+
const initialState = createInitialState(
|
|
3919
|
+
options,
|
|
3920
|
+
state.file.opts.filename ?? "",
|
|
3921
|
+
colorSchemeImportSource,
|
|
3922
|
+
colorSchemeHookName,
|
|
3923
|
+
schemeModifierConfig
|
|
3924
|
+
);
|
|
3925
|
+
Object.assign(state, initialState);
|
|
3926
|
+
programEnter(path2, state);
|
|
3133
3927
|
},
|
|
3134
3928
|
exit(path2, state) {
|
|
3135
|
-
|
|
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);
|
|
3929
|
+
programExit(path2, state, t);
|
|
3162
3930
|
}
|
|
3163
3931
|
},
|
|
3164
|
-
// Check if StyleSheet/Platform are already imported and track tw/twStyle imports
|
|
3165
3932
|
ImportDeclaration(path2, state) {
|
|
3166
|
-
|
|
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
|
-
}
|
|
3933
|
+
importDeclarationVisitor(path2, state, t);
|
|
3213
3934
|
},
|
|
3214
|
-
// Handle tw`...` tagged template expressions
|
|
3215
3935
|
TaggedTemplateExpression(path2, state) {
|
|
3216
|
-
|
|
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;
|
|
3936
|
+
taggedTemplateVisitor(path2, state, t);
|
|
3256
3937
|
},
|
|
3257
|
-
// Handle twStyle('...') call expressions
|
|
3258
3938
|
CallExpression(path2, state) {
|
|
3259
|
-
|
|
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;
|
|
3939
|
+
callExpressionVisitor(path2, state, t);
|
|
3302
3940
|
},
|
|
3303
3941
|
JSXAttribute(path2, state) {
|
|
3304
|
-
|
|
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
|
-
}
|
|
3942
|
+
jsxAttributeVisitor(path2, state, t);
|
|
3636
3943
|
}
|
|
3637
3944
|
}
|
|
3638
3945
|
};
|