@mgcrea/react-native-tailwind 0.6.0 → 0.7.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 +437 -10
- package/dist/babel/config-loader.ts +1 -23
- package/dist/babel/index.cjs +543 -150
- package/dist/babel/index.d.ts +27 -2
- package/dist/babel/index.test.ts +268 -0
- package/dist/babel/index.ts +352 -44
- package/dist/components/Pressable.d.ts +2 -0
- package/dist/components/TextInput.d.ts +2 -0
- package/dist/config/palettes.d.ts +302 -0
- package/dist/config/palettes.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/parser/__snapshots__/colors.test.js.snap +242 -90
- package/dist/parser/__snapshots__/transforms.test.js.snap +58 -0
- package/dist/parser/colors.js +1 -1
- package/dist/parser/colors.test.js +1 -1
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/typography.js +1 -1
- package/dist/parser/typography.test.js +1 -1
- package/dist/runtime.cjs +2 -0
- package/dist/runtime.cjs.map +7 -0
- package/dist/runtime.d.ts +139 -0
- package/dist/runtime.js +2 -0
- package/dist/runtime.js.map +7 -0
- package/dist/runtime.test.js +1 -0
- package/dist/stubs/tw.d.ts +60 -0
- package/dist/stubs/tw.js +1 -0
- package/dist/utils/flattenColors.d.ts +16 -0
- package/dist/utils/flattenColors.js +1 -0
- package/dist/utils/flattenColors.test.js +1 -0
- package/dist/utils/modifiers.d.ts +29 -0
- package/dist/utils/modifiers.js +1 -0
- package/dist/utils/modifiers.test.js +1 -0
- package/dist/utils/styleKey.test.js +1 -0
- package/package.json +15 -3
- package/src/babel/config-loader.ts +1 -23
- package/src/babel/index.test.ts +268 -0
- package/src/babel/index.ts +352 -44
- package/src/components/Pressable.tsx +1 -0
- package/src/components/TextInput.tsx +1 -0
- package/src/config/palettes.ts +304 -0
- package/src/index.ts +5 -0
- package/src/parser/colors.test.ts +47 -31
- package/src/parser/colors.ts +5 -110
- package/src/parser/layout.test.ts +35 -0
- package/src/parser/layout.ts +26 -0
- package/src/parser/typography.test.ts +10 -0
- package/src/parser/typography.ts +8 -0
- package/src/runtime.test.ts +325 -0
- package/src/runtime.ts +280 -0
- package/src/stubs/tw.ts +80 -0
- package/src/utils/flattenColors.test.ts +361 -0
- package/src/utils/flattenColors.ts +32 -0
- package/src/utils/modifiers.test.ts +286 -0
- package/src/utils/modifiers.ts +63 -0
- package/src/utils/styleKey.test.ts +168 -0
package/dist/babel/index.d.ts
CHANGED
|
@@ -5,13 +5,38 @@
|
|
|
5
5
|
import type { PluginObj, PluginPass } from "@babel/core";
|
|
6
6
|
import * as BabelTypes from "@babel/types";
|
|
7
7
|
import { StyleObject } from "src/types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Plugin options
|
|
10
|
+
*/
|
|
11
|
+
export type PluginOptions = {
|
|
12
|
+
/**
|
|
13
|
+
* List of JSX attribute names to transform (in addition to or instead of 'className')
|
|
14
|
+
* Supports exact matches and glob patterns:
|
|
15
|
+
* - Exact: 'className', 'containerClassName'
|
|
16
|
+
* - Glob: '*ClassName' (matches any attribute ending in 'ClassName')
|
|
17
|
+
*
|
|
18
|
+
* @default ['className', 'contentContainerClassName', 'columnWrapperClassName', 'ListHeaderComponentClassName', 'ListFooterComponentClassName']
|
|
19
|
+
*/
|
|
20
|
+
attributes?: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Custom identifier name for the generated StyleSheet constant
|
|
23
|
+
*
|
|
24
|
+
* @default '_twStyles'
|
|
25
|
+
*/
|
|
26
|
+
stylesIdentifier?: string;
|
|
27
|
+
};
|
|
8
28
|
type PluginState = PluginPass & {
|
|
9
29
|
styleRegistry: Map<string, StyleObject>;
|
|
10
30
|
hasClassNames: boolean;
|
|
11
31
|
hasStyleSheetImport: boolean;
|
|
12
32
|
customColors: Record<string, string>;
|
|
33
|
+
supportedAttributes: Set<string>;
|
|
34
|
+
attributePatterns: RegExp[];
|
|
35
|
+
stylesIdentifier: string;
|
|
36
|
+
twImportNames: Set<string>;
|
|
37
|
+
hasTwImport: boolean;
|
|
13
38
|
};
|
|
14
|
-
export default function reactNativeTailwindBabelPlugin({ types: t
|
|
39
|
+
export default function reactNativeTailwindBabelPlugin({ types: t }: {
|
|
15
40
|
types: typeof BabelTypes;
|
|
16
|
-
}): PluginObj<PluginState>;
|
|
41
|
+
}, options?: PluginOptions): PluginObj<PluginState>;
|
|
17
42
|
export {};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { transformSync } from "@babel/core";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import babelPlugin, { type PluginOptions } from "./index.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Helper to transform code with the Babel plugin
|
|
7
|
+
*/
|
|
8
|
+
function transform(code: string, options?: PluginOptions, includeJsx = false) {
|
|
9
|
+
const presets = includeJsx ? ["@babel/preset-react"] : [];
|
|
10
|
+
|
|
11
|
+
const result = transformSync(code, {
|
|
12
|
+
presets,
|
|
13
|
+
plugins: [[babelPlugin, options]],
|
|
14
|
+
filename: "test.tsx",
|
|
15
|
+
configFile: false,
|
|
16
|
+
babelrc: false,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
return result?.code ?? "";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("Babel plugin - tw template tag transformation", () => {
|
|
23
|
+
it("should transform simple tw template literal", () => {
|
|
24
|
+
const input = `
|
|
25
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
26
|
+
const styles = tw\`bg-blue-500 m-4\`;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const output = transform(input);
|
|
30
|
+
|
|
31
|
+
// Should have StyleSheet import (either ESM or CommonJS)
|
|
32
|
+
expect(output).toMatch(/import.*StyleSheet.*from "react-native"|require\("react-native"\)/);
|
|
33
|
+
expect(output).toContain("StyleSheet");
|
|
34
|
+
|
|
35
|
+
// Should have _twStyles definition
|
|
36
|
+
expect(output).toContain("_twStyles");
|
|
37
|
+
expect(output).toContain("StyleSheet.create");
|
|
38
|
+
|
|
39
|
+
// Should transform tw call to object with style property
|
|
40
|
+
expect(output).toContain("style:");
|
|
41
|
+
expect(output).toContain("_twStyles._bg_blue_500_m_4");
|
|
42
|
+
|
|
43
|
+
// Should remove tw import
|
|
44
|
+
expect(output).not.toContain("from '@mgcrea/react-native-tailwind'");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("should transform tw with state modifiers", () => {
|
|
48
|
+
const input = `
|
|
49
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
50
|
+
const styles = tw\`bg-blue-500 active:bg-blue-700 disabled:bg-gray-300\`;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const output = transform(input);
|
|
54
|
+
|
|
55
|
+
// Should have base style
|
|
56
|
+
expect(output).toContain("style:");
|
|
57
|
+
expect(output).toContain("_bg_blue_500");
|
|
58
|
+
|
|
59
|
+
// Should have activeStyle
|
|
60
|
+
expect(output).toContain("activeStyle:");
|
|
61
|
+
expect(output).toContain("_active_bg_blue_700");
|
|
62
|
+
|
|
63
|
+
// Should have disabledStyle
|
|
64
|
+
expect(output).toContain("disabledStyle:");
|
|
65
|
+
expect(output).toContain("_disabled_bg_gray_300");
|
|
66
|
+
|
|
67
|
+
// Should create StyleSheet with all styles
|
|
68
|
+
expect(output).toContain("backgroundColor:");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should inject StyleSheet.create after imports", () => {
|
|
72
|
+
const input = `
|
|
73
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
74
|
+
import { View } from 'react-native';
|
|
75
|
+
|
|
76
|
+
const styles = tw\`m-4\`;
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
const output = transform(input);
|
|
80
|
+
|
|
81
|
+
// Find the position of imports and StyleSheet.create
|
|
82
|
+
const viewImportPos = output.indexOf('require("react-native")');
|
|
83
|
+
const styleSheetCreatePos = output.indexOf("_twStyles");
|
|
84
|
+
|
|
85
|
+
// StyleSheet.create should come after imports
|
|
86
|
+
expect(styleSheetCreatePos).toBeGreaterThan(viewImportPos);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should handle tw in object literals", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
92
|
+
|
|
93
|
+
const sizeVariants = {
|
|
94
|
+
sm: {
|
|
95
|
+
container: tw\`h-9 px-3\`,
|
|
96
|
+
text: tw\`text-sm\`,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
const output = transform(input);
|
|
102
|
+
|
|
103
|
+
// Should define _twStyles before object literal
|
|
104
|
+
const twStylesPos = output.indexOf("_twStyles");
|
|
105
|
+
const sizeVariantsPos = output.indexOf("sizeVariants");
|
|
106
|
+
|
|
107
|
+
expect(twStylesPos).toBeGreaterThan(0);
|
|
108
|
+
expect(twStylesPos).toBeLessThan(sizeVariantsPos);
|
|
109
|
+
|
|
110
|
+
// Should have both styles
|
|
111
|
+
expect(output).toContain("_h_9_px_3");
|
|
112
|
+
expect(output).toContain("_text_sm");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should handle empty tw template literal", () => {
|
|
116
|
+
const input = `
|
|
117
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
118
|
+
const styles = tw\`\`;
|
|
119
|
+
`;
|
|
120
|
+
|
|
121
|
+
const output = transform(input);
|
|
122
|
+
|
|
123
|
+
// Should replace with empty style object
|
|
124
|
+
expect(output).toContain("style:");
|
|
125
|
+
expect(output).toContain("{}");
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should preserve other imports from the same package", () => {
|
|
129
|
+
const input = `
|
|
130
|
+
import { tw, TwStyle, COLORS } from '@mgcrea/react-native-tailwind';
|
|
131
|
+
const styles = tw\`m-4\`;
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
const output = transform(input);
|
|
135
|
+
|
|
136
|
+
// Should remove tw but keep other imports
|
|
137
|
+
expect(output).not.toContain('"tw"');
|
|
138
|
+
expect(output).toContain("TwStyle");
|
|
139
|
+
expect(output).toContain("COLORS");
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("should handle renamed tw import", () => {
|
|
143
|
+
const input = `
|
|
144
|
+
import { tw as customTw } from '@mgcrea/react-native-tailwind';
|
|
145
|
+
const styles = customTw\`m-4 p-2\`;
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
const output = transform(input);
|
|
149
|
+
|
|
150
|
+
// Should still transform the renamed import
|
|
151
|
+
expect(output).toContain("_twStyles");
|
|
152
|
+
expect(output).toContain("_m_4_p_2");
|
|
153
|
+
expect(output).not.toContain("customTw");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should handle multiple tw calls", () => {
|
|
157
|
+
const input = `
|
|
158
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
159
|
+
const style1 = tw\`bg-red-500\`;
|
|
160
|
+
const style2 = tw\`bg-blue-500\`;
|
|
161
|
+
const style3 = tw\`bg-green-500\`;
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
const output = transform(input);
|
|
165
|
+
|
|
166
|
+
// Should have all three styles in StyleSheet
|
|
167
|
+
expect(output).toContain("_bg_red_500");
|
|
168
|
+
expect(output).toContain("_bg_blue_500");
|
|
169
|
+
expect(output).toContain("_bg_green_500");
|
|
170
|
+
|
|
171
|
+
// Should have StyleSheet.create with all styles
|
|
172
|
+
expect(output).toContain("StyleSheet.create");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should use custom stylesIdentifier option", () => {
|
|
176
|
+
const input = `
|
|
177
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
178
|
+
const styles = tw\`m-4\`;
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const output = transform(input, { stylesIdentifier: "myStyles" });
|
|
182
|
+
|
|
183
|
+
// Should use custom identifier
|
|
184
|
+
expect(output).toContain("myStyles");
|
|
185
|
+
expect(output).toContain("myStyles._m_4");
|
|
186
|
+
expect(output).not.toContain("_twStyles");
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("Babel plugin - twStyle function transformation", () => {
|
|
191
|
+
it("should transform twStyle function call", () => {
|
|
192
|
+
const input = `
|
|
193
|
+
import { twStyle } from '@mgcrea/react-native-tailwind';
|
|
194
|
+
const styles = twStyle('bg-blue-500 m-4');
|
|
195
|
+
`;
|
|
196
|
+
|
|
197
|
+
const output = transform(input);
|
|
198
|
+
|
|
199
|
+
// Should transform to object with style property
|
|
200
|
+
expect(output).toContain("style:");
|
|
201
|
+
expect(output).toContain("_twStyles._bg_blue_500_m_4");
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should transform twStyle with modifiers", () => {
|
|
205
|
+
const input = `
|
|
206
|
+
import { twStyle } from '@mgcrea/react-native-tailwind';
|
|
207
|
+
const styles = twStyle('bg-blue-500 active:bg-blue-700');
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
const output = transform(input);
|
|
211
|
+
|
|
212
|
+
expect(output).toContain("style:");
|
|
213
|
+
expect(output).toContain("activeStyle:");
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should handle empty twStyle call", () => {
|
|
217
|
+
const input = `
|
|
218
|
+
import { twStyle } from '@mgcrea/react-native-tailwind';
|
|
219
|
+
const styles = twStyle('');
|
|
220
|
+
`;
|
|
221
|
+
|
|
222
|
+
const output = transform(input);
|
|
223
|
+
|
|
224
|
+
// Should replace with undefined
|
|
225
|
+
expect(output).toContain("undefined");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Note: JSX tests require @babel/preset-react
|
|
230
|
+
describe("Babel plugin - className transformation (existing behavior)", () => {
|
|
231
|
+
it("should still transform className props", () => {
|
|
232
|
+
const input = `
|
|
233
|
+
import { View } from 'react-native';
|
|
234
|
+
export function Component() {
|
|
235
|
+
return <View className="m-4 p-2 bg-blue-500" />;
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
|
|
239
|
+
const output = transform(input, undefined, true); // Enable JSX
|
|
240
|
+
|
|
241
|
+
// Should have StyleSheet
|
|
242
|
+
expect(output).toContain("StyleSheet.create");
|
|
243
|
+
expect(output).toContain("_twStyles");
|
|
244
|
+
|
|
245
|
+
// Should replace className with style
|
|
246
|
+
expect(output).not.toContain("className");
|
|
247
|
+
expect(output).toContain("style:");
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should work with both tw and className in same file", () => {
|
|
251
|
+
const input = `
|
|
252
|
+
import { tw } from '@mgcrea/react-native-tailwind';
|
|
253
|
+
import { View } from 'react-native';
|
|
254
|
+
|
|
255
|
+
const styles = tw\`bg-red-500\`;
|
|
256
|
+
|
|
257
|
+
export function Component() {
|
|
258
|
+
return <View className="m-4 p-2" />;
|
|
259
|
+
}
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
const output = transform(input, undefined, true); // Enable JSX
|
|
263
|
+
|
|
264
|
+
// Should have both styles in StyleSheet
|
|
265
|
+
expect(output).toContain("_bg_red_500");
|
|
266
|
+
expect(output).toContain("_m_4_p_2");
|
|
267
|
+
});
|
|
268
|
+
});
|