@mgcrea/react-native-tailwind 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +576 -0
  2. package/dist/babel/config-loader.d.ts +25 -0
  3. package/dist/babel/config-loader.ts +134 -0
  4. package/dist/babel/index.cjs +1111 -0
  5. package/dist/babel/index.d.ts +16 -0
  6. package/dist/babel/index.ts +286 -0
  7. package/dist/index.d.ts +12 -0
  8. package/dist/index.js +1 -0
  9. package/dist/parser/borders.d.ts +10 -0
  10. package/dist/parser/borders.js +1 -0
  11. package/dist/parser/colors.d.ts +9 -0
  12. package/dist/parser/colors.js +1 -0
  13. package/dist/parser/index.d.ts +25 -0
  14. package/dist/parser/index.js +1 -0
  15. package/dist/parser/layout.d.ts +8 -0
  16. package/dist/parser/layout.js +1 -0
  17. package/dist/parser/sizing.d.ts +10 -0
  18. package/dist/parser/sizing.js +1 -0
  19. package/dist/parser/spacing.d.ts +10 -0
  20. package/dist/parser/spacing.js +1 -0
  21. package/dist/parser/typography.d.ts +9 -0
  22. package/dist/parser/typography.js +1 -0
  23. package/dist/react-native.d.js +1 -0
  24. package/dist/react-native.d.ts +138 -0
  25. package/dist/types.d.ts +11 -0
  26. package/dist/types.js +1 -0
  27. package/dist/utils/styleKey.d.ts +9 -0
  28. package/dist/utils/styleKey.js +1 -0
  29. package/package.json +83 -0
  30. package/src/babel/config-loader.ts +134 -0
  31. package/src/babel/index.ts +286 -0
  32. package/src/index.ts +20 -0
  33. package/src/parser/borders.ts +198 -0
  34. package/src/parser/colors.ts +160 -0
  35. package/src/parser/index.ts +71 -0
  36. package/src/parser/layout.ts +114 -0
  37. package/src/parser/sizing.ts +239 -0
  38. package/src/parser/spacing.ts +222 -0
  39. package/src/parser/typography.ts +156 -0
  40. package/src/react-native.d.ts +138 -0
  41. package/src/types.ts +15 -0
  42. package/src/utils/styleKey.ts +23 -0
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Border utilities (border width, radius, style)
3
+ */
4
+
5
+ import type { StyleObject } from "../types";
6
+
7
+ // Border width scale
8
+ export const BORDER_WIDTH_SCALE: Record<string, number> = {
9
+ "": 1,
10
+ "0": 0,
11
+ "2": 2,
12
+ "4": 4,
13
+ "8": 8,
14
+ };
15
+
16
+ // Border radius scale
17
+ export const BORDER_RADIUS_SCALE: Record<string, number> = {
18
+ none: 0,
19
+ sm: 2,
20
+ "": 4,
21
+ md: 6,
22
+ lg: 8,
23
+ xl: 12,
24
+ "2xl": 16,
25
+ "3xl": 24,
26
+ full: 9999,
27
+ };
28
+
29
+ /**
30
+ * Parse border classes
31
+ */
32
+ export function parseBorder(cls: string): StyleObject | null {
33
+ // Border width (border-0, border-t, border-[8px], etc.)
34
+ if (cls.startsWith("border-")) {
35
+ return parseBorderWidth(cls);
36
+ }
37
+
38
+ if (cls === "border") {
39
+ return { borderWidth: 1 };
40
+ }
41
+
42
+ // Border radius (rounded, rounded-t, rounded-[12px], etc.)
43
+ if (cls.startsWith("rounded")) {
44
+ return parseBorderRadius(cls);
45
+ }
46
+
47
+ // Border style
48
+ if (cls === "border-solid") return { borderStyle: "solid" };
49
+ if (cls === "border-dotted") return { borderStyle: "dotted" };
50
+ if (cls === "border-dashed") return { borderStyle: "dashed" };
51
+
52
+ return null;
53
+ }
54
+
55
+ /**
56
+ * Parse border width classes
57
+ */
58
+ function parseBorderWidth(cls: string): StyleObject | null {
59
+ // All borders with arbitrary values: border-[8px]
60
+ const allArbMatch = cls.match(/^border-\[(\d+)(?:px)?\]$/);
61
+ if (allArbMatch) {
62
+ return { borderWidth: parseInt(allArbMatch[1], 10) };
63
+ }
64
+
65
+ // Directional borders with arbitrary values: border-t-[8px]
66
+ const dirArbMatch = cls.match(/^border-([trbl])-\[(\d+)(?:px)?\]$/);
67
+ if (dirArbMatch) {
68
+ const dir = dirArbMatch[1];
69
+ const value = parseInt(dirArbMatch[2], 10);
70
+ const propMap: Record<string, string> = {
71
+ t: "borderTopWidth",
72
+ r: "borderRightWidth",
73
+ b: "borderBottomWidth",
74
+ l: "borderLeftWidth",
75
+ };
76
+ return { [propMap[dir]]: value };
77
+ }
78
+
79
+ // Preset directional borders: border-t-0, border-t-2, etc.
80
+ const dirMatch = cls.match(/^border-([trbl])-?(\d*)$/);
81
+ if (dirMatch) {
82
+ const dir = dirMatch[1];
83
+ const scaleKey = dirMatch[2] || ""; // empty string for border-t
84
+ const value = BORDER_WIDTH_SCALE[scaleKey];
85
+
86
+ if (typeof value === "number") {
87
+ const propMap: Record<string, string> = {
88
+ t: "borderTopWidth",
89
+ r: "borderRightWidth",
90
+ b: "borderBottomWidth",
91
+ l: "borderLeftWidth",
92
+ };
93
+ return { [propMap[dir]]: value };
94
+ }
95
+ }
96
+
97
+ // Preset all borders: border-0, border-2, etc.
98
+ const allMatch = cls.match(/^border-(\d+)$/);
99
+ if (allMatch) {
100
+ const value = BORDER_WIDTH_SCALE[allMatch[1]];
101
+ if (value !== undefined) {
102
+ return { borderWidth: value };
103
+ }
104
+ }
105
+
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * Parse border radius classes
111
+ */
112
+ function parseBorderRadius(cls: string): StyleObject | null {
113
+ // All corners with arbitrary values: rounded-[12px]
114
+ const allArbMatch = cls.match(/^rounded-\[(\d+)(?:px)?\]$/);
115
+ if (allArbMatch) {
116
+ return { borderRadius: parseInt(allArbMatch[1], 10) };
117
+ }
118
+
119
+ // Specific corners with arbitrary values: rounded-tl-[8px]
120
+ const cornerArbMatch = cls.match(/^rounded-(tl|tr|bl|br)-\[(\d+)(?:px)?\]$/);
121
+ if (cornerArbMatch) {
122
+ const corner = cornerArbMatch[1];
123
+ const value = parseInt(cornerArbMatch[2], 10);
124
+ const propMap: Record<string, string> = {
125
+ tl: "borderTopLeftRadius",
126
+ tr: "borderTopRightRadius",
127
+ bl: "borderBottomLeftRadius",
128
+ br: "borderBottomRightRadius",
129
+ };
130
+ return { [propMap[corner]]: value };
131
+ }
132
+
133
+ // Sides with arbitrary values: rounded-t-[8px]
134
+ const sideArbMatch = cls.match(/^rounded-([trbl])-\[(\d+)(?:px)?\]$/);
135
+ if (sideArbMatch) {
136
+ const side = sideArbMatch[1];
137
+ const value = parseInt(sideArbMatch[2], 10);
138
+ const propMap: Record<string, string[]> = {
139
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
140
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
141
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
142
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
143
+ };
144
+ const result: StyleObject = {};
145
+ propMap[side].forEach((prop) => (result[prop] = value));
146
+ return result;
147
+ }
148
+
149
+ // All corners with preset values: rounded, rounded-lg, etc.
150
+ const allMatch = cls.match(/^rounded(-\w+)?$/);
151
+ if (allMatch) {
152
+ const scaleKey = allMatch[1] ? allMatch[1].substring(1) : ""; // remove leading dash
153
+ const value = BORDER_RADIUS_SCALE[scaleKey];
154
+ if (value !== undefined) {
155
+ return { borderRadius: value };
156
+ }
157
+ }
158
+
159
+ // Sides with preset values: rounded-t, rounded-t-lg, etc.
160
+ const sideMatch = cls.match(/^rounded-([trbl])(?:-(\w+))?$/);
161
+ if (sideMatch) {
162
+ const side = sideMatch[1];
163
+ const scaleKey = sideMatch[2] || ""; // empty string for rounded-t
164
+ const value = BORDER_RADIUS_SCALE[scaleKey];
165
+
166
+ if (value !== undefined) {
167
+ const propMap: Record<string, string[]> = {
168
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
169
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
170
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
171
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
172
+ };
173
+ const result: StyleObject = {};
174
+ propMap[side].forEach((prop) => (result[prop] = value));
175
+ return result;
176
+ }
177
+ }
178
+
179
+ // Specific corners with preset values: rounded-tl, rounded-tl-lg, etc.
180
+ const cornerMatch = cls.match(/^rounded-(tl|tr|bl|br)(?:-(\w+))?$/);
181
+ if (cornerMatch) {
182
+ const corner = cornerMatch[1];
183
+ const scaleKey = cornerMatch[2] || "";
184
+ const value = BORDER_RADIUS_SCALE[scaleKey];
185
+
186
+ if (value !== undefined) {
187
+ const propMap: Record<string, string> = {
188
+ tl: "borderTopLeftRadius",
189
+ tr: "borderTopRightRadius",
190
+ bl: "borderBottomLeftRadius",
191
+ br: "borderBottomRightRadius",
192
+ };
193
+ return { [propMap[corner]]: value };
194
+ }
195
+ }
196
+
197
+ return null;
198
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Color utilities (background, text, border colors)
3
+ */
4
+
5
+ import type { StyleObject } from "../types";
6
+
7
+ // Tailwind color palette
8
+ export const COLORS: Record<string, string> = {
9
+ // Gray
10
+ "gray-50": "#F9FAFB",
11
+ "gray-100": "#F3F4F6",
12
+ "gray-200": "#E5E7EB",
13
+ "gray-300": "#D1D5DB",
14
+ "gray-400": "#9CA3AF",
15
+ "gray-500": "#6B7280",
16
+ "gray-600": "#4B5563",
17
+ "gray-700": "#374151",
18
+ "gray-800": "#1F2937",
19
+ "gray-900": "#111827",
20
+
21
+ // Red
22
+ "red-50": "#FEF2F2",
23
+ "red-100": "#FEE2E2",
24
+ "red-200": "#FECACA",
25
+ "red-300": "#FCA5A5",
26
+ "red-400": "#F87171",
27
+ "red-500": "#EF4444",
28
+ "red-600": "#DC2626",
29
+ "red-700": "#B91C1C",
30
+ "red-800": "#991B1B",
31
+ "red-900": "#7F1D1D",
32
+
33
+ // Blue
34
+ "blue-50": "#EFF6FF",
35
+ "blue-100": "#DBEAFE",
36
+ "blue-200": "#BFDBFE",
37
+ "blue-300": "#93C5FD",
38
+ "blue-400": "#60A5FA",
39
+ "blue-500": "#3B82F6",
40
+ "blue-600": "#2563EB",
41
+ "blue-700": "#1D4ED8",
42
+ "blue-800": "#1E40AF",
43
+ "blue-900": "#1E3A8A",
44
+
45
+ // Green
46
+ "green-50": "#F0FDF4",
47
+ "green-100": "#DCFCE7",
48
+ "green-200": "#BBF7D0",
49
+ "green-300": "#86EFAC",
50
+ "green-400": "#4ADE80",
51
+ "green-500": "#22C55E",
52
+ "green-600": "#16A34A",
53
+ "green-700": "#15803D",
54
+ "green-800": "#166534",
55
+ "green-900": "#14532D",
56
+
57
+ // Yellow
58
+ "yellow-50": "#FEFCE8",
59
+ "yellow-100": "#FEF9C3",
60
+ "yellow-200": "#FEF08A",
61
+ "yellow-300": "#FDE047",
62
+ "yellow-400": "#FACC15",
63
+ "yellow-500": "#EAB308",
64
+ "yellow-600": "#CA8A04",
65
+ "yellow-700": "#A16207",
66
+ "yellow-800": "#854D0E",
67
+ "yellow-900": "#713F12",
68
+
69
+ // Purple
70
+ "purple-50": "#FAF5FF",
71
+ "purple-100": "#F3E8FF",
72
+ "purple-200": "#E9D5FF",
73
+ "purple-300": "#D8B4FE",
74
+ "purple-400": "#C084FC",
75
+ "purple-500": "#A855F7",
76
+ "purple-600": "#9333EA",
77
+ "purple-700": "#7E22CE",
78
+ "purple-800": "#6B21A8",
79
+ "purple-900": "#581C87",
80
+
81
+ // Pink
82
+ "pink-50": "#FDF2F8",
83
+ "pink-100": "#FCE7F3",
84
+ "pink-200": "#FBCFE8",
85
+ "pink-300": "#F9A8D4",
86
+ "pink-400": "#F472B6",
87
+ "pink-500": "#EC4899",
88
+ "pink-600": "#DB2777",
89
+ "pink-700": "#BE185D",
90
+ "pink-800": "#9D174D",
91
+ "pink-900": "#831843",
92
+
93
+ // Orange
94
+ "orange-50": "#FFF7ED",
95
+ "orange-100": "#FFEDD5",
96
+ "orange-200": "#FED7AA",
97
+ "orange-300": "#FDBA74",
98
+ "orange-400": "#FB923C",
99
+ "orange-500": "#F97316",
100
+ "orange-600": "#EA580C",
101
+ "orange-700": "#C2410C",
102
+ "orange-800": "#9A3412",
103
+ "orange-900": "#7C2D12",
104
+
105
+ // Indigo
106
+ "indigo-50": "#EEF2FF",
107
+ "indigo-100": "#E0E7FF",
108
+ "indigo-200": "#C7D2FE",
109
+ "indigo-300": "#A5B4FC",
110
+ "indigo-400": "#818CF8",
111
+ "indigo-500": "#6366F1",
112
+ "indigo-600": "#4F46E5",
113
+ "indigo-700": "#4338CA",
114
+ "indigo-800": "#3730A3",
115
+ "indigo-900": "#312E81",
116
+
117
+ // Basic colors
118
+ white: "#FFFFFF",
119
+ black: "#000000",
120
+ transparent: "transparent",
121
+ };
122
+
123
+ /**
124
+ * Parse color classes (background, text, border)
125
+ */
126
+ export function parseColor(cls: string, customColors?: Record<string, string>): StyleObject | null {
127
+ // Helper to get color with custom override (custom colors take precedence)
128
+ const getColor = (key: string): string | undefined => {
129
+ return customColors?.[key] ?? COLORS[key];
130
+ };
131
+
132
+ // Background color: bg-blue-500
133
+ if (cls.startsWith("bg-")) {
134
+ const colorKey = cls.substring(3);
135
+ const color = getColor(colorKey);
136
+ if (color) {
137
+ return { backgroundColor: color };
138
+ }
139
+ }
140
+
141
+ // Text color: text-blue-500
142
+ if (cls.startsWith("text-")) {
143
+ const colorKey = cls.substring(5);
144
+ const color = getColor(colorKey);
145
+ if (color) {
146
+ return { color: color };
147
+ }
148
+ }
149
+
150
+ // Border color: border-blue-500
151
+ if (cls.startsWith("border-") && !cls.match(/^border-[0-9]/)) {
152
+ const colorKey = cls.substring(7);
153
+ const color = getColor(colorKey);
154
+ if (color) {
155
+ return { borderColor: color };
156
+ }
157
+ }
158
+
159
+ return null;
160
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Tailwind class parser for React Native
3
+ * Converts Tailwind-like class names to React Native style objects
4
+ */
5
+
6
+ import type { StyleObject } from "../types";
7
+ import { parseBorder } from "./borders";
8
+ import { parseColor } from "./colors";
9
+ import { parseLayout } from "./layout";
10
+ import { parseSizing } from "./sizing";
11
+ import { parseSpacing } from "./spacing";
12
+ import { parseTypography } from "./typography";
13
+
14
+ /**
15
+ * Parse a className string and return a React Native style object
16
+ * @param className - Space-separated class names
17
+ * @param customColors - Optional custom colors from tailwind.config
18
+ * @returns React Native style object
19
+ */
20
+ export function parseClassName(className: string, customColors?: Record<string, string>): StyleObject {
21
+ const classes = className.split(/\s+/).filter(Boolean);
22
+ const style: StyleObject = {};
23
+
24
+ for (const cls of classes) {
25
+ const parsedStyle = parseClass(cls, customColors);
26
+ Object.assign(style, parsedStyle);
27
+ }
28
+
29
+ return style;
30
+ }
31
+
32
+ /**
33
+ * Parse a single class name
34
+ * @param cls - Single class name
35
+ * @param customColors - Optional custom colors from tailwind.config
36
+ * @returns React Native style object
37
+ */
38
+ export function parseClass(cls: string, customColors?: Record<string, string>): StyleObject {
39
+ // Try each parser in order
40
+ // parseColor gets custom colors, others don't need it
41
+ const parsers: Array<(cls: string) => StyleObject | null> = [
42
+ parseSpacing,
43
+ (cls: string) => parseColor(cls, customColors),
44
+ parseLayout,
45
+ parseTypography,
46
+ parseBorder,
47
+ parseSizing,
48
+ ];
49
+
50
+ for (const parser of parsers) {
51
+ const result = parser(cls);
52
+ if (result !== null) {
53
+ return result;
54
+ }
55
+ }
56
+
57
+ // Warn about unknown class in development
58
+ if (process.env.NODE_ENV !== "production") {
59
+ console.warn(`[react-native-tailwind] Unknown class: "${cls}"`);
60
+ }
61
+
62
+ return {};
63
+ }
64
+
65
+ // Re-export parsers for testing/advanced usage
66
+ export { parseBorder } from "./borders";
67
+ export { parseColor } from "./colors";
68
+ export { parseLayout } from "./layout";
69
+ export { parseSizing } from "./sizing";
70
+ export { parseSpacing } from "./spacing";
71
+ export { parseTypography } from "./typography";
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Layout utilities (flexbox, positioning, display)
3
+ */
4
+
5
+ import type { StyleObject } from "../types";
6
+
7
+ // Display utilities
8
+ const DISPLAY_MAP: Record<string, StyleObject> = {
9
+ flex: { display: "flex" },
10
+ hidden: { display: "none" },
11
+ };
12
+
13
+ // Flex direction utilities
14
+ const FLEX_DIRECTION_MAP: Record<string, StyleObject> = {
15
+ "flex-row": { flexDirection: "row" },
16
+ "flex-row-reverse": { flexDirection: "row-reverse" },
17
+ "flex-col": { flexDirection: "column" },
18
+ "flex-col-reverse": { flexDirection: "column-reverse" },
19
+ };
20
+
21
+ // Flex wrap utilities
22
+ const FLEX_WRAP_MAP: Record<string, StyleObject> = {
23
+ "flex-wrap": { flexWrap: "wrap" },
24
+ "flex-wrap-reverse": { flexWrap: "wrap-reverse" },
25
+ "flex-nowrap": { flexWrap: "nowrap" },
26
+ };
27
+
28
+ // Flex utilities
29
+ const FLEX_MAP: Record<string, StyleObject> = {
30
+ "flex-1": { flex: 1 },
31
+ "flex-auto": { flex: 1 },
32
+ "flex-none": { flex: 0 },
33
+ };
34
+
35
+ // Flex grow/shrink utilities
36
+ const GROW_SHRINK_MAP: Record<string, StyleObject> = {
37
+ grow: { flexGrow: 1 },
38
+ "grow-0": { flexGrow: 0 },
39
+ shrink: { flexShrink: 1 },
40
+ "shrink-0": { flexShrink: 0 },
41
+ };
42
+
43
+ // Justify content utilities
44
+ const JUSTIFY_CONTENT_MAP: Record<string, StyleObject> = {
45
+ "justify-start": { justifyContent: "flex-start" },
46
+ "justify-end": { justifyContent: "flex-end" },
47
+ "justify-center": { justifyContent: "center" },
48
+ "justify-between": { justifyContent: "space-between" },
49
+ "justify-around": { justifyContent: "space-around" },
50
+ "justify-evenly": { justifyContent: "space-evenly" },
51
+ };
52
+
53
+ // Align items utilities
54
+ const ALIGN_ITEMS_MAP: Record<string, StyleObject> = {
55
+ "items-start": { alignItems: "flex-start" },
56
+ "items-end": { alignItems: "flex-end" },
57
+ "items-center": { alignItems: "center" },
58
+ "items-baseline": { alignItems: "baseline" },
59
+ "items-stretch": { alignItems: "stretch" },
60
+ };
61
+
62
+ // Align self utilities
63
+ const ALIGN_SELF_MAP: Record<string, StyleObject> = {
64
+ "self-auto": { alignSelf: "auto" },
65
+ "self-start": { alignSelf: "flex-start" },
66
+ "self-end": { alignSelf: "flex-end" },
67
+ "self-center": { alignSelf: "center" },
68
+ "self-stretch": { alignSelf: "stretch" },
69
+ "self-baseline": { alignSelf: "baseline" },
70
+ };
71
+
72
+ // Align content utilities
73
+ const ALIGN_CONTENT_MAP: Record<string, StyleObject> = {
74
+ "content-start": { alignContent: "flex-start" },
75
+ "content-end": { alignContent: "flex-end" },
76
+ "content-center": { alignContent: "center" },
77
+ "content-between": { alignContent: "space-between" },
78
+ "content-around": { alignContent: "space-around" },
79
+ "content-stretch": { alignContent: "stretch" },
80
+ };
81
+
82
+ // Position utilities
83
+ const POSITION_MAP: Record<string, StyleObject> = {
84
+ absolute: { position: "absolute" },
85
+ relative: { position: "relative" },
86
+ };
87
+
88
+ // Overflow utilities
89
+ const OVERFLOW_MAP: Record<string, StyleObject> = {
90
+ "overflow-hidden": { overflow: "hidden" },
91
+ "overflow-visible": { overflow: "visible" },
92
+ "overflow-scroll": { overflow: "scroll" },
93
+ };
94
+
95
+ /**
96
+ * Parse layout classes
97
+ */
98
+ export function parseLayout(cls: string): StyleObject | null {
99
+ // Try each lookup table in order
100
+ return (
101
+ DISPLAY_MAP[cls] ??
102
+ FLEX_DIRECTION_MAP[cls] ??
103
+ FLEX_WRAP_MAP[cls] ??
104
+ FLEX_MAP[cls] ??
105
+ GROW_SHRINK_MAP[cls] ??
106
+ JUSTIFY_CONTENT_MAP[cls] ??
107
+ ALIGN_ITEMS_MAP[cls] ??
108
+ ALIGN_SELF_MAP[cls] ??
109
+ ALIGN_CONTENT_MAP[cls] ??
110
+ POSITION_MAP[cls] ??
111
+ OVERFLOW_MAP[cls] ??
112
+ null
113
+ );
114
+ }