@mgcrea/react-native-tailwind 0.14.0 → 0.15.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 +12 -8
- package/dist/babel/config-loader.d.ts +10 -0
- package/dist/babel/config-loader.test.ts +75 -21
- package/dist/babel/config-loader.ts +100 -2
- package/dist/babel/index.cjs +101 -33
- package/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.d.ts +3 -1
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/sizing.d.ts +3 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/sizing.test.js +1 -1
- package/dist/parser/spacing.d.ts +3 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.js +1 -1
- package/dist/parser/transforms.d.ts +3 -1
- package/dist/parser/transforms.js +1 -1
- package/dist/parser/transforms.test.js +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +3 -3
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +3 -3
- package/dist/runtime.test.js +1 -1
- package/package.json +1 -1
- package/src/babel/config-loader.test.ts +75 -21
- package/src/babel/config-loader.ts +100 -2
- package/src/parser/index.ts +6 -5
- package/src/parser/layout.test.ts +94 -0
- package/src/parser/layout.ts +17 -12
- package/src/parser/sizing.test.ts +56 -0
- package/src/parser/sizing.ts +20 -15
- package/src/parser/spacing.test.ts +57 -0
- package/src/parser/spacing.ts +15 -10
- package/src/parser/transforms.test.ts +57 -0
- package/src/parser/transforms.ts +7 -3
- package/src/runtime.test.ts +149 -0
- package/src/runtime.ts +53 -1
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ The Babel plugin transforms your code at compile time:
|
|
|
112
112
|
**Input** (what you write):
|
|
113
113
|
|
|
114
114
|
```tsx
|
|
115
|
-
<View className=
|
|
115
|
+
<View className={`rounded-lg p-4 ${isSelected ? "bg-blue-500 border border-blue-700" : "bg-gray-200"}`} />
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
**Output** (what Babel generates):
|
|
@@ -120,15 +120,19 @@ The Babel plugin transforms your code at compile time:
|
|
|
120
120
|
```tsx
|
|
121
121
|
import { StyleSheet } from "react-native";
|
|
122
122
|
|
|
123
|
-
<View
|
|
123
|
+
<View
|
|
124
|
+
style={[
|
|
125
|
+
_twStyles._rounded_lg,
|
|
126
|
+
_twStyles._p_4,
|
|
127
|
+
isSelected ? _twStyles._bg_blue_500_border_border_blue_700 : _twStyles._bg_gray_200,
|
|
128
|
+
]}
|
|
129
|
+
/>;
|
|
124
130
|
|
|
125
131
|
const _twStyles = StyleSheet.create({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
borderRadius: 8,
|
|
131
|
-
},
|
|
132
|
+
_rounded_lg: { borderRadius: 8 },
|
|
133
|
+
_p_4: { padding: 16 },
|
|
134
|
+
_bg_blue_500_border_border_blue_700: { backgroundColor: "#3B82F6", borderWidth: 1, borderColor: "#1D4ED8" },
|
|
135
|
+
_bg_gray_200: { backgroundColor: "#E5E7EB" },
|
|
132
136
|
});
|
|
133
137
|
```
|
|
134
138
|
|
|
@@ -8,12 +8,21 @@ export type TailwindConfig = {
|
|
|
8
8
|
colors?: Record<string, string | Record<string, string>>;
|
|
9
9
|
fontFamily?: Record<string, string | string[]>;
|
|
10
10
|
fontSize?: Record<string, string | number>;
|
|
11
|
+
spacing?: Record<string, string | number>;
|
|
12
|
+
[key: string]: unknown;
|
|
11
13
|
};
|
|
12
14
|
colors?: Record<string, string | Record<string, string>>;
|
|
13
15
|
fontFamily?: Record<string, string | string[]>;
|
|
14
16
|
fontSize?: Record<string, string | number>;
|
|
17
|
+
spacing?: Record<string, string | number>;
|
|
18
|
+
[key: string]: unknown;
|
|
15
19
|
};
|
|
16
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* Check for unsupported theme extensions and warn the user
|
|
23
|
+
* @internal Exported for testing
|
|
24
|
+
*/
|
|
25
|
+
export declare function warnUnsupportedThemeKeys(config: TailwindConfig, configPath: string): void;
|
|
17
26
|
/**
|
|
18
27
|
* Find tailwind.config.* file by traversing up from startDir
|
|
19
28
|
*/
|
|
@@ -29,6 +38,7 @@ export type CustomTheme = {
|
|
|
29
38
|
colors: Record<string, string>;
|
|
30
39
|
fontFamily: Record<string, string>;
|
|
31
40
|
fontSize: Record<string, number>;
|
|
41
|
+
spacing: Record<string, number>;
|
|
32
42
|
};
|
|
33
43
|
/**
|
|
34
44
|
* Extract all custom theme extensions from tailwind config
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
extractCustomTheme,
|
|
5
|
+
findTailwindConfig,
|
|
6
|
+
loadTailwindConfig,
|
|
7
|
+
warnUnsupportedThemeKeys,
|
|
8
|
+
} from "./config-loader";
|
|
4
9
|
|
|
5
10
|
// Mock fs
|
|
6
11
|
vi.mock("fs");
|
|
@@ -120,35 +125,84 @@ describe("config-loader", () => {
|
|
|
120
125
|
vi.spyOn(fs, "existsSync").mockReturnValue(false);
|
|
121
126
|
|
|
122
127
|
const result = extractCustomTheme("/project/src/file.ts");
|
|
123
|
-
expect(result).toEqual({ colors: {}, fontFamily: {}, fontSize: {} });
|
|
128
|
+
expect(result).toEqual({ colors: {}, fontFamily: {}, fontSize: {}, spacing: {} });
|
|
124
129
|
});
|
|
130
|
+
});
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
describe("warnUnsupportedThemeKeys", () => {
|
|
133
|
+
it("should warn about unsupported theme keys", () => {
|
|
134
|
+
const configPath = "/project/unsupported/tailwind.config.js";
|
|
135
|
+
const mockConfig = {
|
|
136
|
+
theme: {
|
|
137
|
+
extend: {
|
|
138
|
+
colors: { brand: "#123456" },
|
|
139
|
+
spacing: { "72": "18rem" }, // Supported (now!)
|
|
140
|
+
borderRadius: { xl: "1rem" }, // Unsupported
|
|
141
|
+
lineHeight: { tight: "1.25" }, // Unsupported
|
|
142
|
+
},
|
|
143
|
+
screens: { tablet: "640px" }, // Unsupported
|
|
144
|
+
},
|
|
145
|
+
};
|
|
128
146
|
|
|
129
|
-
vi.spyOn(
|
|
130
|
-
vi.spyOn(require, "resolve").mockReturnValue(configPath);
|
|
147
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
131
148
|
|
|
132
|
-
|
|
133
|
-
// For integration, we'd need to mock the entire flow
|
|
134
|
-
const result = extractCustomTheme("/project/src/file.ts");
|
|
149
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
135
150
|
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
152
|
+
expect.stringContaining("Unsupported theme configuration detected"),
|
|
153
|
+
);
|
|
154
|
+
// spacing is now supported, so should NOT warn about it
|
|
155
|
+
expect(consoleSpy).not.toHaveBeenCalledWith(expect.stringContaining("theme.extend.spacing"));
|
|
156
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.extend.borderRadius"));
|
|
157
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.extend.lineHeight"));
|
|
158
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("theme.screens"));
|
|
159
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
160
|
+
expect.stringContaining("https://github.com/mgcrea/react-native-tailwind/issues/new"),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
consoleSpy.mockRestore();
|
|
138
164
|
});
|
|
139
165
|
|
|
140
|
-
it("should
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
166
|
+
it("should not warn for supported theme keys only", () => {
|
|
167
|
+
const configPath = "/project/supported/tailwind.config.js";
|
|
168
|
+
const mockConfig = {
|
|
169
|
+
theme: {
|
|
170
|
+
extend: {
|
|
171
|
+
colors: { brand: "#123456" },
|
|
172
|
+
fontFamily: { custom: "CustomFont" },
|
|
173
|
+
fontSize: { huge: "48px" },
|
|
174
|
+
spacing: { "72": "18rem" },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
180
|
+
|
|
181
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
182
|
+
|
|
183
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
184
|
+
|
|
185
|
+
consoleSpy.mockRestore();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("should only warn once per config path", () => {
|
|
189
|
+
const configPath = "/project/once/tailwind.config.js";
|
|
190
|
+
const mockConfig = {
|
|
191
|
+
theme: {
|
|
192
|
+
extend: {
|
|
193
|
+
borderRadius: { xl: "1rem" }, // Unsupported
|
|
194
|
+
},
|
|
195
|
+
},
|
|
147
196
|
};
|
|
148
197
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
198
|
+
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(vi.fn());
|
|
199
|
+
|
|
200
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
201
|
+
warnUnsupportedThemeKeys(mockConfig, configPath);
|
|
202
|
+
|
|
203
|
+
expect(consoleSpy).toHaveBeenCalledTimes(1);
|
|
204
|
+
|
|
205
|
+
consoleSpy.mockRestore();
|
|
152
206
|
});
|
|
153
207
|
});
|
|
154
208
|
});
|
|
@@ -15,13 +15,68 @@ export type TailwindConfig = {
|
|
|
15
15
|
colors?: Record<string, string | Record<string, string>>;
|
|
16
16
|
fontFamily?: Record<string, string | string[]>;
|
|
17
17
|
fontSize?: Record<string, string | number>;
|
|
18
|
+
spacing?: Record<string, string | number>;
|
|
19
|
+
[key: string]: unknown;
|
|
18
20
|
};
|
|
19
21
|
colors?: Record<string, string | Record<string, string>>;
|
|
20
22
|
fontFamily?: Record<string, string | string[]>;
|
|
21
23
|
fontSize?: Record<string, string | number>;
|
|
24
|
+
spacing?: Record<string, string | number>;
|
|
25
|
+
[key: string]: unknown;
|
|
22
26
|
};
|
|
23
27
|
};
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Theme keys currently supported by react-native-tailwind
|
|
31
|
+
*/
|
|
32
|
+
const SUPPORTED_THEME_KEYS = new Set(["colors", "fontFamily", "fontSize", "spacing", "extend"]);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Cache for warned config paths to avoid duplicate warnings
|
|
36
|
+
*/
|
|
37
|
+
const warnedConfigPaths = new Set<string>();
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check for unsupported theme extensions and warn the user
|
|
41
|
+
* @internal Exported for testing
|
|
42
|
+
*/
|
|
43
|
+
export function warnUnsupportedThemeKeys(config: TailwindConfig, configPath: string): void {
|
|
44
|
+
if (process.env.NODE_ENV === "production" || warnedConfigPaths.has(configPath)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const unsupportedKeys: string[] = [];
|
|
49
|
+
|
|
50
|
+
// Check theme.extend keys
|
|
51
|
+
if (config.theme?.extend && typeof config.theme.extend === "object") {
|
|
52
|
+
for (const key of Object.keys(config.theme.extend)) {
|
|
53
|
+
if (!SUPPORTED_THEME_KEYS.has(key)) {
|
|
54
|
+
unsupportedKeys.push(`theme.extend.${key}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check direct theme keys (excluding 'extend')
|
|
60
|
+
if (config.theme && typeof config.theme === "object") {
|
|
61
|
+
for (const key of Object.keys(config.theme)) {
|
|
62
|
+
if (key !== "extend" && !SUPPORTED_THEME_KEYS.has(key)) {
|
|
63
|
+
unsupportedKeys.push(`theme.${key}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (unsupportedKeys.length > 0) {
|
|
69
|
+
warnedConfigPaths.add(configPath);
|
|
70
|
+
console.warn(
|
|
71
|
+
`[react-native-tailwind] Unsupported theme configuration detected:\n` +
|
|
72
|
+
` ${unsupportedKeys.join(", ")}\n\n` +
|
|
73
|
+
` Currently supported: colors, fontFamily, fontSize, spacing\n\n` +
|
|
74
|
+
` These extensions will be ignored. If you need support for these features,\n` +
|
|
75
|
+
` please open an issue: https://github.com/mgcrea/react-native-tailwind/issues/new`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
25
80
|
// Cache configs per path to avoid repeated file I/O
|
|
26
81
|
const configCache = new Map<string, TailwindConfig | null>();
|
|
27
82
|
|
|
@@ -92,6 +147,7 @@ export type CustomTheme = {
|
|
|
92
147
|
colors: Record<string, string>;
|
|
93
148
|
fontFamily: Record<string, string>;
|
|
94
149
|
fontSize: Record<string, number>;
|
|
150
|
+
spacing: Record<string, number>;
|
|
95
151
|
};
|
|
96
152
|
|
|
97
153
|
/**
|
|
@@ -103,14 +159,17 @@ export function extractCustomTheme(filename: string): CustomTheme {
|
|
|
103
159
|
const configPath = findTailwindConfig(projectDir);
|
|
104
160
|
|
|
105
161
|
if (!configPath) {
|
|
106
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
162
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
107
163
|
}
|
|
108
164
|
|
|
109
165
|
const config = loadTailwindConfig(configPath);
|
|
110
166
|
if (!config?.theme) {
|
|
111
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
167
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
112
168
|
}
|
|
113
169
|
|
|
170
|
+
// Warn about unsupported theme keys
|
|
171
|
+
warnUnsupportedThemeKeys(config, configPath);
|
|
172
|
+
|
|
114
173
|
// Extract colors
|
|
115
174
|
/* v8 ignore next 5 */
|
|
116
175
|
if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
|
|
@@ -173,9 +232,48 @@ export function extractCustomTheme(filename: string): CustomTheme {
|
|
|
173
232
|
}
|
|
174
233
|
}
|
|
175
234
|
|
|
235
|
+
// Extract spacing
|
|
236
|
+
/* v8 ignore next 5 */
|
|
237
|
+
if (config.theme.spacing && !config.theme.extend?.spacing && process.env.NODE_ENV !== "production") {
|
|
238
|
+
console.warn(
|
|
239
|
+
"[react-native-tailwind] Using theme.spacing will override all default spacing. " +
|
|
240
|
+
"Use theme.extend.spacing to add custom spacing while keeping defaults.",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
const spacing = config.theme.extend?.spacing ?? config.theme.spacing ?? {};
|
|
244
|
+
|
|
245
|
+
// Convert spacing values to numbers (handle rem, px, or number values)
|
|
246
|
+
const spacingResult: Record<string, number> = {};
|
|
247
|
+
for (const [key, value] of Object.entries(spacing)) {
|
|
248
|
+
if (typeof value === "number") {
|
|
249
|
+
spacingResult[key] = value;
|
|
250
|
+
} else if (typeof value === "string") {
|
|
251
|
+
// Parse string values: "18rem" -> 288, "16px" -> 16, "16" -> 16
|
|
252
|
+
let parsed: number;
|
|
253
|
+
if (value.endsWith("rem")) {
|
|
254
|
+
// Convert rem to px (1rem = 16px)
|
|
255
|
+
parsed = parseFloat(value.replace(/rem$/, "")) * 16;
|
|
256
|
+
} else {
|
|
257
|
+
// Parse px or unitless values
|
|
258
|
+
parsed = parseFloat(value.replace(/px$/, ""));
|
|
259
|
+
}
|
|
260
|
+
if (!isNaN(parsed)) {
|
|
261
|
+
spacingResult[key] = parsed;
|
|
262
|
+
} else {
|
|
263
|
+
/* v8 ignore next 5 */
|
|
264
|
+
if (process.env.NODE_ENV !== "production") {
|
|
265
|
+
console.warn(
|
|
266
|
+
`[react-native-tailwind] Invalid spacing value for "${key}": ${value}. Expected number or string like "16px" or "1rem".`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
176
273
|
return {
|
|
177
274
|
colors: flattenColors(colors),
|
|
178
275
|
fontFamily: fontFamilyResult,
|
|
179
276
|
fontSize: fontSizeResult,
|
|
277
|
+
spacing: spacingResult,
|
|
180
278
|
};
|
|
181
279
|
}
|
package/dist/babel/index.cjs
CHANGED
|
@@ -53,6 +53,40 @@ function flattenColors(colors, prefix = "") {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// src/babel/config-loader.ts
|
|
56
|
+
var SUPPORTED_THEME_KEYS = /* @__PURE__ */ new Set(["colors", "fontFamily", "fontSize", "spacing", "extend"]);
|
|
57
|
+
var warnedConfigPaths = /* @__PURE__ */ new Set();
|
|
58
|
+
function warnUnsupportedThemeKeys(config, configPath) {
|
|
59
|
+
if (process.env.NODE_ENV === "production" || warnedConfigPaths.has(configPath)) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const unsupportedKeys = [];
|
|
63
|
+
if (config.theme?.extend && typeof config.theme.extend === "object") {
|
|
64
|
+
for (const key of Object.keys(config.theme.extend)) {
|
|
65
|
+
if (!SUPPORTED_THEME_KEYS.has(key)) {
|
|
66
|
+
unsupportedKeys.push(`theme.extend.${key}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (config.theme && typeof config.theme === "object") {
|
|
71
|
+
for (const key of Object.keys(config.theme)) {
|
|
72
|
+
if (key !== "extend" && !SUPPORTED_THEME_KEYS.has(key)) {
|
|
73
|
+
unsupportedKeys.push(`theme.${key}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (unsupportedKeys.length > 0) {
|
|
78
|
+
warnedConfigPaths.add(configPath);
|
|
79
|
+
console.warn(
|
|
80
|
+
`[react-native-tailwind] Unsupported theme configuration detected:
|
|
81
|
+
${unsupportedKeys.join(", ")}
|
|
82
|
+
|
|
83
|
+
Currently supported: colors, fontFamily, fontSize, spacing
|
|
84
|
+
|
|
85
|
+
These extensions will be ignored. If you need support for these features,
|
|
86
|
+
please open an issue: https://github.com/mgcrea/react-native-tailwind/issues/new`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
56
90
|
var configCache = /* @__PURE__ */ new Map();
|
|
57
91
|
function findTailwindConfig(startDir) {
|
|
58
92
|
let currentDir = startDir;
|
|
@@ -97,12 +131,13 @@ function extractCustomTheme(filename) {
|
|
|
97
131
|
const projectDir = path.dirname(filename);
|
|
98
132
|
const configPath = findTailwindConfig(projectDir);
|
|
99
133
|
if (!configPath) {
|
|
100
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
134
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
101
135
|
}
|
|
102
136
|
const config = loadTailwindConfig(configPath);
|
|
103
137
|
if (!config?.theme) {
|
|
104
|
-
return { colors: {}, fontFamily: {}, fontSize: {} };
|
|
138
|
+
return { colors: {}, fontFamily: {}, fontSize: {}, spacing: {} };
|
|
105
139
|
}
|
|
140
|
+
warnUnsupportedThemeKeys(config, configPath);
|
|
106
141
|
if (config.theme.colors && !config.theme.extend?.colors && process.env.NODE_ENV !== "production") {
|
|
107
142
|
console.warn(
|
|
108
143
|
"[react-native-tailwind] Using theme.colors will override all default colors. Use theme.extend.colors to add custom colors while keeping defaults."
|
|
@@ -146,10 +181,39 @@ function extractCustomTheme(filename) {
|
|
|
146
181
|
}
|
|
147
182
|
}
|
|
148
183
|
}
|
|
184
|
+
if (config.theme.spacing && !config.theme.extend?.spacing && process.env.NODE_ENV !== "production") {
|
|
185
|
+
console.warn(
|
|
186
|
+
"[react-native-tailwind] Using theme.spacing will override all default spacing. Use theme.extend.spacing to add custom spacing while keeping defaults."
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
const spacing = config.theme.extend?.spacing ?? config.theme.spacing ?? {};
|
|
190
|
+
const spacingResult = {};
|
|
191
|
+
for (const [key, value] of Object.entries(spacing)) {
|
|
192
|
+
if (typeof value === "number") {
|
|
193
|
+
spacingResult[key] = value;
|
|
194
|
+
} else if (typeof value === "string") {
|
|
195
|
+
let parsed;
|
|
196
|
+
if (value.endsWith("rem")) {
|
|
197
|
+
parsed = parseFloat(value.replace(/rem$/, "")) * 16;
|
|
198
|
+
} else {
|
|
199
|
+
parsed = parseFloat(value.replace(/px$/, ""));
|
|
200
|
+
}
|
|
201
|
+
if (!isNaN(parsed)) {
|
|
202
|
+
spacingResult[key] = parsed;
|
|
203
|
+
} else {
|
|
204
|
+
if (process.env.NODE_ENV !== "production") {
|
|
205
|
+
console.warn(
|
|
206
|
+
`[react-native-tailwind] Invalid spacing value for "${key}": ${value}. Expected number or string like "16px" or "1rem".`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
149
212
|
return {
|
|
150
213
|
colors: flattenColors(colors),
|
|
151
214
|
fontFamily: fontFamilyResult,
|
|
152
|
-
fontSize: fontSizeResult
|
|
215
|
+
fontSize: fontSizeResult,
|
|
216
|
+
spacing: spacingResult
|
|
153
217
|
};
|
|
154
218
|
}
|
|
155
219
|
|
|
@@ -1083,7 +1147,8 @@ var INSET_SCALE = {
|
|
|
1083
1147
|
20: 80,
|
|
1084
1148
|
24: 96
|
|
1085
1149
|
};
|
|
1086
|
-
function parseLayout(cls) {
|
|
1150
|
+
function parseLayout(cls, customSpacing) {
|
|
1151
|
+
const insetMap = customSpacing ? { ...INSET_SCALE, ...customSpacing } : INSET_SCALE;
|
|
1087
1152
|
if (cls.startsWith("z-")) {
|
|
1088
1153
|
const zKey = cls.substring(2);
|
|
1089
1154
|
const arbitraryZ = parseArbitraryZIndex(zKey);
|
|
@@ -1104,7 +1169,7 @@ function parseLayout(cls) {
|
|
|
1104
1169
|
if (arbitraryTop !== null) {
|
|
1105
1170
|
return { top: arbitraryTop };
|
|
1106
1171
|
}
|
|
1107
|
-
const topValue =
|
|
1172
|
+
const topValue = insetMap[topKey];
|
|
1108
1173
|
if (topValue !== void 0) {
|
|
1109
1174
|
return { top: topValue };
|
|
1110
1175
|
}
|
|
@@ -1118,7 +1183,7 @@ function parseLayout(cls) {
|
|
|
1118
1183
|
if (arbitraryRight !== null) {
|
|
1119
1184
|
return { right: arbitraryRight };
|
|
1120
1185
|
}
|
|
1121
|
-
const rightValue =
|
|
1186
|
+
const rightValue = insetMap[rightKey];
|
|
1122
1187
|
if (rightValue !== void 0) {
|
|
1123
1188
|
return { right: rightValue };
|
|
1124
1189
|
}
|
|
@@ -1132,7 +1197,7 @@ function parseLayout(cls) {
|
|
|
1132
1197
|
if (arbitraryBottom !== null) {
|
|
1133
1198
|
return { bottom: arbitraryBottom };
|
|
1134
1199
|
}
|
|
1135
|
-
const bottomValue =
|
|
1200
|
+
const bottomValue = insetMap[bottomKey];
|
|
1136
1201
|
if (bottomValue !== void 0) {
|
|
1137
1202
|
return { bottom: bottomValue };
|
|
1138
1203
|
}
|
|
@@ -1146,7 +1211,7 @@ function parseLayout(cls) {
|
|
|
1146
1211
|
if (arbitraryLeft !== null) {
|
|
1147
1212
|
return { left: arbitraryLeft };
|
|
1148
1213
|
}
|
|
1149
|
-
const leftValue =
|
|
1214
|
+
const leftValue = insetMap[leftKey];
|
|
1150
1215
|
if (leftValue !== void 0) {
|
|
1151
1216
|
return { left: leftValue };
|
|
1152
1217
|
}
|
|
@@ -1169,7 +1234,7 @@ function parseLayout(cls) {
|
|
|
1169
1234
|
}
|
|
1170
1235
|
return { start: arbitraryStart };
|
|
1171
1236
|
}
|
|
1172
|
-
const startValue =
|
|
1237
|
+
const startValue = insetMap[startKey];
|
|
1173
1238
|
if (startValue !== void 0) {
|
|
1174
1239
|
return { start: isNegative ? -startValue : startValue };
|
|
1175
1240
|
}
|
|
@@ -1192,7 +1257,7 @@ function parseLayout(cls) {
|
|
|
1192
1257
|
}
|
|
1193
1258
|
return { end: arbitraryEnd };
|
|
1194
1259
|
}
|
|
1195
|
-
const endValue =
|
|
1260
|
+
const endValue = insetMap[endKey];
|
|
1196
1261
|
if (endValue !== void 0) {
|
|
1197
1262
|
return { end: isNegative ? -endValue : endValue };
|
|
1198
1263
|
}
|
|
@@ -1203,7 +1268,7 @@ function parseLayout(cls) {
|
|
|
1203
1268
|
if (arbitraryInset !== null) {
|
|
1204
1269
|
return { left: arbitraryInset, right: arbitraryInset };
|
|
1205
1270
|
}
|
|
1206
|
-
const insetValue =
|
|
1271
|
+
const insetValue = insetMap[insetKey];
|
|
1207
1272
|
if (insetValue !== void 0) {
|
|
1208
1273
|
return { left: insetValue, right: insetValue };
|
|
1209
1274
|
}
|
|
@@ -1214,7 +1279,7 @@ function parseLayout(cls) {
|
|
|
1214
1279
|
if (arbitraryInset !== null) {
|
|
1215
1280
|
return { top: arbitraryInset, bottom: arbitraryInset };
|
|
1216
1281
|
}
|
|
1217
|
-
const insetValue =
|
|
1282
|
+
const insetValue = insetMap[insetKey];
|
|
1218
1283
|
if (insetValue !== void 0) {
|
|
1219
1284
|
return { top: insetValue, bottom: insetValue };
|
|
1220
1285
|
}
|
|
@@ -1225,7 +1290,7 @@ function parseLayout(cls) {
|
|
|
1225
1290
|
if (arbitraryInset !== null) {
|
|
1226
1291
|
return { start: arbitraryInset };
|
|
1227
1292
|
}
|
|
1228
|
-
const insetValue =
|
|
1293
|
+
const insetValue = insetMap[insetKey];
|
|
1229
1294
|
if (insetValue !== void 0) {
|
|
1230
1295
|
return { start: insetValue };
|
|
1231
1296
|
}
|
|
@@ -1236,7 +1301,7 @@ function parseLayout(cls) {
|
|
|
1236
1301
|
if (arbitraryInset !== null) {
|
|
1237
1302
|
return { end: arbitraryInset };
|
|
1238
1303
|
}
|
|
1239
|
-
const insetValue =
|
|
1304
|
+
const insetValue = insetMap[insetKey];
|
|
1240
1305
|
if (insetValue !== void 0) {
|
|
1241
1306
|
return { end: insetValue };
|
|
1242
1307
|
}
|
|
@@ -1247,7 +1312,7 @@ function parseLayout(cls) {
|
|
|
1247
1312
|
if (arbitraryInset !== null) {
|
|
1248
1313
|
return { top: arbitraryInset, right: arbitraryInset, bottom: arbitraryInset, left: arbitraryInset };
|
|
1249
1314
|
}
|
|
1250
|
-
const insetValue =
|
|
1315
|
+
const insetValue = insetMap[insetKey];
|
|
1251
1316
|
if (insetValue !== void 0) {
|
|
1252
1317
|
return { top: insetValue, right: insetValue, bottom: insetValue, left: insetValue };
|
|
1253
1318
|
}
|
|
@@ -1407,7 +1472,8 @@ function parseArbitrarySize(value) {
|
|
|
1407
1472
|
}
|
|
1408
1473
|
return null;
|
|
1409
1474
|
}
|
|
1410
|
-
function parseSizing(cls) {
|
|
1475
|
+
function parseSizing(cls, customSpacing) {
|
|
1476
|
+
const sizeMap = customSpacing ? { ...SIZE_SCALE, ...customSpacing } : SIZE_SCALE;
|
|
1411
1477
|
if (cls.startsWith("w-")) {
|
|
1412
1478
|
const sizeKey = cls.substring(2);
|
|
1413
1479
|
if (sizeKey === "screen") {
|
|
@@ -1421,7 +1487,7 @@ function parseSizing(cls) {
|
|
|
1421
1487
|
if (percentage) {
|
|
1422
1488
|
return { width: percentage };
|
|
1423
1489
|
}
|
|
1424
|
-
const numericSize =
|
|
1490
|
+
const numericSize = sizeMap[sizeKey];
|
|
1425
1491
|
if (numericSize !== void 0) {
|
|
1426
1492
|
return { width: numericSize };
|
|
1427
1493
|
}
|
|
@@ -1442,7 +1508,7 @@ function parseSizing(cls) {
|
|
|
1442
1508
|
if (percentage) {
|
|
1443
1509
|
return { height: percentage };
|
|
1444
1510
|
}
|
|
1445
|
-
const numericSize =
|
|
1511
|
+
const numericSize = sizeMap[sizeKey];
|
|
1446
1512
|
if (numericSize !== void 0) {
|
|
1447
1513
|
return { height: numericSize };
|
|
1448
1514
|
}
|
|
@@ -1460,7 +1526,7 @@ function parseSizing(cls) {
|
|
|
1460
1526
|
if (percentage) {
|
|
1461
1527
|
return { minWidth: percentage };
|
|
1462
1528
|
}
|
|
1463
|
-
const numericSize =
|
|
1529
|
+
const numericSize = sizeMap[sizeKey];
|
|
1464
1530
|
if (numericSize !== void 0) {
|
|
1465
1531
|
return { minWidth: numericSize };
|
|
1466
1532
|
}
|
|
@@ -1475,7 +1541,7 @@ function parseSizing(cls) {
|
|
|
1475
1541
|
if (percentage) {
|
|
1476
1542
|
return { minHeight: percentage };
|
|
1477
1543
|
}
|
|
1478
|
-
const numericSize =
|
|
1544
|
+
const numericSize = sizeMap[sizeKey];
|
|
1479
1545
|
if (numericSize !== void 0) {
|
|
1480
1546
|
return { minHeight: numericSize };
|
|
1481
1547
|
}
|
|
@@ -1490,7 +1556,7 @@ function parseSizing(cls) {
|
|
|
1490
1556
|
if (percentage) {
|
|
1491
1557
|
return { maxWidth: percentage };
|
|
1492
1558
|
}
|
|
1493
|
-
const numericSize =
|
|
1559
|
+
const numericSize = sizeMap[sizeKey];
|
|
1494
1560
|
if (numericSize !== void 0) {
|
|
1495
1561
|
return { maxWidth: numericSize };
|
|
1496
1562
|
}
|
|
@@ -1505,7 +1571,7 @@ function parseSizing(cls) {
|
|
|
1505
1571
|
if (percentage) {
|
|
1506
1572
|
return { maxHeight: percentage };
|
|
1507
1573
|
}
|
|
1508
|
-
const numericSize =
|
|
1574
|
+
const numericSize = sizeMap[sizeKey];
|
|
1509
1575
|
if (numericSize !== void 0) {
|
|
1510
1576
|
return { maxHeight: numericSize };
|
|
1511
1577
|
}
|
|
@@ -1565,7 +1631,8 @@ function parseArbitrarySpacing(value) {
|
|
|
1565
1631
|
}
|
|
1566
1632
|
return null;
|
|
1567
1633
|
}
|
|
1568
|
-
function parseSpacing(cls) {
|
|
1634
|
+
function parseSpacing(cls, customSpacing) {
|
|
1635
|
+
const spacingMap = customSpacing ? { ...SPACING_SCALE, ...customSpacing } : SPACING_SCALE;
|
|
1569
1636
|
const marginMatch = cls.match(/^(-?)m([xytrblse]?)-(.+)$/);
|
|
1570
1637
|
if (marginMatch) {
|
|
1571
1638
|
const [, negativePrefix, dir, valueStr] = marginMatch;
|
|
@@ -1575,7 +1642,7 @@ function parseSpacing(cls) {
|
|
|
1575
1642
|
const finalValue = isNegative ? -arbitraryValue : arbitraryValue;
|
|
1576
1643
|
return getMarginStyle(dir, finalValue);
|
|
1577
1644
|
}
|
|
1578
|
-
const scaleValue =
|
|
1645
|
+
const scaleValue = spacingMap[valueStr];
|
|
1579
1646
|
if (scaleValue !== void 0) {
|
|
1580
1647
|
const finalValue = isNegative ? -scaleValue : scaleValue;
|
|
1581
1648
|
return getMarginStyle(dir, finalValue);
|
|
@@ -1588,7 +1655,7 @@ function parseSpacing(cls) {
|
|
|
1588
1655
|
if (arbitraryValue !== null) {
|
|
1589
1656
|
return getPaddingStyle(dir, arbitraryValue);
|
|
1590
1657
|
}
|
|
1591
|
-
const scaleValue =
|
|
1658
|
+
const scaleValue = spacingMap[valueStr];
|
|
1592
1659
|
if (scaleValue !== void 0) {
|
|
1593
1660
|
return getPaddingStyle(dir, scaleValue);
|
|
1594
1661
|
}
|
|
@@ -1600,7 +1667,7 @@ function parseSpacing(cls) {
|
|
|
1600
1667
|
if (arbitraryValue !== null) {
|
|
1601
1668
|
return { gap: arbitraryValue };
|
|
1602
1669
|
}
|
|
1603
|
-
const scaleValue =
|
|
1670
|
+
const scaleValue = spacingMap[valueStr];
|
|
1604
1671
|
if (scaleValue !== void 0) {
|
|
1605
1672
|
return { gap: scaleValue };
|
|
1606
1673
|
}
|
|
@@ -1766,7 +1833,8 @@ function parseArbitraryPerspective(value) {
|
|
|
1766
1833
|
}
|
|
1767
1834
|
return null;
|
|
1768
1835
|
}
|
|
1769
|
-
function parseTransform(cls) {
|
|
1836
|
+
function parseTransform(cls, customSpacing) {
|
|
1837
|
+
const spacingMap = customSpacing ? { ...SPACING_SCALE, ...customSpacing } : SPACING_SCALE;
|
|
1770
1838
|
if (cls.startsWith("origin-")) {
|
|
1771
1839
|
if (process.env.NODE_ENV !== "production") {
|
|
1772
1840
|
console.warn(
|
|
@@ -1872,7 +1940,7 @@ function parseTransform(cls) {
|
|
|
1872
1940
|
const value = typeof arbitraryTranslate === "number" ? isNegative ? -arbitraryTranslate : arbitraryTranslate : isNegative ? `-${arbitraryTranslate}` : arbitraryTranslate;
|
|
1873
1941
|
return { transform: [{ translateX: value }] };
|
|
1874
1942
|
}
|
|
1875
|
-
const translateValue =
|
|
1943
|
+
const translateValue = spacingMap[translateKey];
|
|
1876
1944
|
if (translateValue !== void 0) {
|
|
1877
1945
|
const value = isNegative ? -translateValue : translateValue;
|
|
1878
1946
|
return { transform: [{ translateX: value }] };
|
|
@@ -1886,7 +1954,7 @@ function parseTransform(cls) {
|
|
|
1886
1954
|
const value = typeof arbitraryTranslate === "number" ? isNegative ? -arbitraryTranslate : arbitraryTranslate : isNegative ? `-${arbitraryTranslate}` : arbitraryTranslate;
|
|
1887
1955
|
return { transform: [{ translateY: value }] };
|
|
1888
1956
|
}
|
|
1889
|
-
const translateValue =
|
|
1957
|
+
const translateValue = spacingMap[translateKey];
|
|
1890
1958
|
if (translateValue !== void 0) {
|
|
1891
1959
|
const value = isNegative ? -translateValue : translateValue;
|
|
1892
1960
|
return { transform: [{ translateY: value }] };
|
|
@@ -2267,15 +2335,15 @@ function parseClassName(className, customTheme) {
|
|
|
2267
2335
|
}
|
|
2268
2336
|
function parseClass(cls, customTheme) {
|
|
2269
2337
|
const parsers = [
|
|
2270
|
-
parseSpacing,
|
|
2338
|
+
(cls2) => parseSpacing(cls2, customTheme?.spacing),
|
|
2271
2339
|
(cls2) => parseBorder(cls2, customTheme?.colors),
|
|
2272
2340
|
(cls2) => parseColor(cls2, customTheme?.colors),
|
|
2273
|
-
parseLayout,
|
|
2341
|
+
(cls2) => parseLayout(cls2, customTheme?.spacing),
|
|
2274
2342
|
(cls2) => parseTypography(cls2, customTheme?.fontFamily, customTheme?.fontSize),
|
|
2275
|
-
parseSizing,
|
|
2343
|
+
(cls2) => parseSizing(cls2, customTheme?.spacing),
|
|
2276
2344
|
parseShadow,
|
|
2277
2345
|
parseAspectRatio,
|
|
2278
|
-
parseTransform
|
|
2346
|
+
(cls2) => parseTransform(cls2, customTheme?.spacing)
|
|
2279
2347
|
];
|
|
2280
2348
|
for (const parser of parsers) {
|
|
2281
2349
|
const result = parser(cls);
|
package/dist/parser/index.d.ts
CHANGED