@mgcrea/react-native-tailwind 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -2031
- package/dist/babel/index.cjs +1726 -1094
- package/dist/babel/plugin/componentScope.d.ts +26 -0
- package/dist/babel/plugin/componentScope.ts +87 -0
- package/dist/babel/plugin/state.d.ts +123 -0
- package/dist/babel/plugin/state.ts +185 -0
- package/dist/babel/plugin/visitors/className.d.ts +11 -0
- package/{src/babel/plugin.test.ts → dist/babel/plugin/visitors/className.test.ts} +285 -572
- package/dist/babel/plugin/visitors/className.ts +652 -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 +116 -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 +116 -0
- package/dist/babel/plugin/visitors/tw.d.ts +16 -0
- package/dist/babel/plugin/visitors/tw.test.ts +771 -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/directionalModifierProcessing.d.ts +34 -0
- package/dist/babel/utils/directionalModifierProcessing.ts +99 -0
- package/dist/babel/utils/modifierProcessing.ts +21 -0
- package/dist/babel/utils/platformModifierProcessing.ts +11 -0
- package/dist/babel/utils/styleInjection.d.ts +31 -0
- package/dist/babel/utils/styleInjection.ts +253 -7
- package/dist/babel/utils/twProcessing.d.ts +2 -0
- package/dist/babel/utils/twProcessing.ts +103 -3
- 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.d.ts +2 -2
- package/dist/parser/index.js +1 -1
- package/dist/parser/layout.js +1 -1
- package/dist/parser/layout.test.js +1 -1
- package/dist/parser/modifiers.d.ts +32 -2
- package/dist/parser/modifiers.js +1 -1
- package/dist/parser/modifiers.test.js +1 -1
- package/dist/parser/sizing.js +1 -1
- package/dist/parser/spacing.d.ts +1 -1
- package/dist/parser/spacing.js +1 -1
- package/dist/parser/spacing.test.js +1 -1
- package/dist/parser/typography.test.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 +6 -6
- package/src/babel/plugin/componentScope.ts +87 -0
- package/src/babel/plugin/state.ts +185 -0
- package/src/babel/plugin/visitors/className.test.ts +1625 -0
- package/src/babel/plugin/visitors/className.ts +652 -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 +116 -0
- package/src/babel/plugin/visitors/program.test.ts +325 -0
- package/src/babel/plugin/visitors/program.ts +116 -0
- package/src/babel/plugin/visitors/tw.test.ts +771 -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/directionalModifierProcessing.ts +99 -0
- package/src/babel/utils/modifierProcessing.ts +21 -0
- package/src/babel/utils/platformModifierProcessing.ts +11 -0
- package/src/babel/utils/styleInjection.ts +253 -7
- package/src/babel/utils/twProcessing.ts +103 -3
- 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 +162 -0
- package/src/parser/borders.ts +67 -9
- package/src/parser/colors.test.ts +249 -0
- package/src/parser/colors.ts +38 -0
- package/src/parser/index.ts +4 -2
- package/src/parser/layout.test.ts +74 -0
- package/src/parser/layout.ts +94 -0
- package/src/parser/modifiers.test.ts +206 -0
- package/src/parser/modifiers.ts +62 -3
- package/src/parser/sizing.ts +11 -0
- package/src/parser/spacing.test.ts +66 -0
- package/src/parser/spacing.ts +15 -5
- package/src/parser/typography.test.ts +8 -0
- package/src/parser/typography.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mgcrea/react-native-tailwind",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Compile-time Tailwind CSS for React Native with zero runtime overhead",
|
|
5
5
|
"author": "Olivier Louvignes <olivier@mgcrea.io> (https://github.com/mgcrea)",
|
|
6
6
|
"homepage": "https://github.com/mgcrea/react-native-tailwind#readme",
|
|
@@ -68,23 +68,23 @@
|
|
|
68
68
|
"@testing-library/react-native": "^13.3.3",
|
|
69
69
|
"@types/babel__core": "^7.20.5",
|
|
70
70
|
"@types/babel__traverse": "^7.28.0",
|
|
71
|
-
"@types/react": "^19.2.
|
|
72
|
-
"@vitest/coverage-v8": "^4.0.
|
|
71
|
+
"@types/react": "^19.2.7",
|
|
72
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
73
73
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
74
74
|
"esbuild": "^0.27.0",
|
|
75
75
|
"eslint": "^9.39.1",
|
|
76
76
|
"jest": "^30.2.0",
|
|
77
|
-
"prettier": "^3.
|
|
77
|
+
"prettier": "^3.7.1",
|
|
78
78
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
79
79
|
"react": "^19.2.0",
|
|
80
80
|
"react-native": "0.82.1",
|
|
81
81
|
"typescript": "^5.9.3",
|
|
82
|
-
"vitest": "^4.0.
|
|
82
|
+
"vitest": "^4.0.14"
|
|
83
83
|
},
|
|
84
84
|
"engines": {
|
|
85
85
|
"node": ">=18"
|
|
86
86
|
},
|
|
87
|
-
"packageManager": "pnpm@10.
|
|
87
|
+
"packageManager": "pnpm@10.24.0",
|
|
88
88
|
"publishConfig": {
|
|
89
89
|
"access": "public"
|
|
90
90
|
},
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component scope detection helpers for hook injection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { NodePath } from "@babel/core";
|
|
6
|
+
import type * as BabelTypes from "@babel/types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if a function path represents a valid component scope for hook injection
|
|
10
|
+
* Valid scopes:
|
|
11
|
+
* - Top-level FunctionDeclaration
|
|
12
|
+
* - FunctionExpression/ArrowFunctionExpression in top-level VariableDeclarator (with PascalCase name)
|
|
13
|
+
* - NOT class methods, NOT nested functions, NOT inline callbacks
|
|
14
|
+
*
|
|
15
|
+
* @param functionPath - Path to the function to check
|
|
16
|
+
* @param t - Babel types
|
|
17
|
+
* @returns true if function is a valid component scope
|
|
18
|
+
*/
|
|
19
|
+
export function isComponentScope(functionPath: NodePath<BabelTypes.Function>, t: typeof BabelTypes): boolean {
|
|
20
|
+
const node = functionPath.node;
|
|
21
|
+
const parent = functionPath.parent;
|
|
22
|
+
const parentPath = functionPath.parentPath;
|
|
23
|
+
|
|
24
|
+
// Reject class methods (class components not supported for hooks)
|
|
25
|
+
if (t.isClassMethod(parent)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Reject if inside a class body
|
|
30
|
+
if (functionPath.findParent((p) => t.isClassBody(p.node))) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Accept top-level FunctionDeclaration
|
|
35
|
+
if (t.isFunctionDeclaration(node)) {
|
|
36
|
+
// Check if it's at program level or in export
|
|
37
|
+
if (t.isProgram(parent) || t.isExportNamedDeclaration(parent) || t.isExportDefaultDeclaration(parent)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Accept FunctionExpression/ArrowFunctionExpression in VariableDeclarator
|
|
43
|
+
if (t.isFunctionExpression(node) || t.isArrowFunctionExpression(node)) {
|
|
44
|
+
if (t.isVariableDeclarator(parent)) {
|
|
45
|
+
// Check if it's at program level (via VariableDeclaration)
|
|
46
|
+
const varDeclarationPath = parentPath?.parentPath;
|
|
47
|
+
if (
|
|
48
|
+
varDeclarationPath &&
|
|
49
|
+
t.isVariableDeclaration(varDeclarationPath.node) &&
|
|
50
|
+
(t.isProgram(varDeclarationPath.parent) || t.isExportNamedDeclaration(varDeclarationPath.parent))
|
|
51
|
+
) {
|
|
52
|
+
// Check for PascalCase naming (component convention)
|
|
53
|
+
if (t.isIdentifier(parent.id)) {
|
|
54
|
+
const name = parent.id.name;
|
|
55
|
+
return /^[A-Z]/.test(name); // Starts with uppercase
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Find the nearest valid component scope for hook injection
|
|
66
|
+
* Climbs the AST from the current path to find a component-level function
|
|
67
|
+
*
|
|
68
|
+
* @param path - Starting path (e.g., JSXAttribute)
|
|
69
|
+
* @param t - Babel types
|
|
70
|
+
* @returns NodePath to component function, or null if not found
|
|
71
|
+
*/
|
|
72
|
+
export function findComponentScope(
|
|
73
|
+
path: NodePath,
|
|
74
|
+
t: typeof BabelTypes,
|
|
75
|
+
): NodePath<BabelTypes.Function> | null {
|
|
76
|
+
let current = path.getFunctionParent();
|
|
77
|
+
|
|
78
|
+
while (current) {
|
|
79
|
+
if (t.isFunction(current.node) && isComponentScope(current, t)) {
|
|
80
|
+
return current;
|
|
81
|
+
}
|
|
82
|
+
// Climb to next parent function
|
|
83
|
+
current = current.getFunctionParent();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin state and options types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { NodePath, PluginPass } from "@babel/core";
|
|
6
|
+
import type * as BabelTypes from "@babel/types";
|
|
7
|
+
import type { SchemeModifierConfig } from "../../types/config.js";
|
|
8
|
+
import type { StyleObject } from "../../types/core.js";
|
|
9
|
+
import type { CustomTheme } from "../config-loader.js";
|
|
10
|
+
import { extractCustomTheme } from "../config-loader.js";
|
|
11
|
+
import { DEFAULT_CLASS_ATTRIBUTES, buildAttributeMatchers } from "../utils/attributeMatchers.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plugin options
|
|
15
|
+
*/
|
|
16
|
+
export type PluginOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* List of JSX attribute names to transform (in addition to or instead of 'className')
|
|
19
|
+
* Supports exact matches and glob patterns:
|
|
20
|
+
* - Exact: 'className', 'containerClassName'
|
|
21
|
+
* - Glob: '*ClassName' (matches any attribute ending in 'ClassName')
|
|
22
|
+
*
|
|
23
|
+
* @default ['className', 'contentContainerClassName', 'columnWrapperClassName', 'ListHeaderComponentClassName', 'ListFooterComponentClassName']
|
|
24
|
+
*/
|
|
25
|
+
attributes?: string[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Custom identifier name for the generated StyleSheet constant
|
|
29
|
+
*
|
|
30
|
+
* @default '_twStyles'
|
|
31
|
+
*/
|
|
32
|
+
stylesIdentifier?: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for the scheme: modifier that expands to both dark: and light: modifiers
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* {
|
|
39
|
+
* darkSuffix: '-dark', // scheme:bg-primary -> dark:bg-primary-dark
|
|
40
|
+
* lightSuffix: '-light' // scheme:bg-primary -> light:bg-primary-light
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* @default { darkSuffix: '-dark', lightSuffix: '-light' }
|
|
44
|
+
*/
|
|
45
|
+
schemeModifier?: {
|
|
46
|
+
darkSuffix?: string;
|
|
47
|
+
lightSuffix?: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Configuration for color scheme hook import (dark:/light: modifiers)
|
|
52
|
+
*
|
|
53
|
+
* Allows using custom color scheme hooks from theme providers instead of
|
|
54
|
+
* React Native's built-in useColorScheme.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Use custom hook from theme provider
|
|
58
|
+
* {
|
|
59
|
+
* importFrom: '@/hooks/useColorScheme',
|
|
60
|
+
* importName: 'useColorScheme'
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Use React Navigation theme
|
|
65
|
+
* {
|
|
66
|
+
* importFrom: '@react-navigation/native',
|
|
67
|
+
* importName: 'useTheme' // You'd wrap this to return ColorSchemeName
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* @default { importFrom: 'react-native', importName: 'useColorScheme' }
|
|
71
|
+
*/
|
|
72
|
+
colorScheme?: {
|
|
73
|
+
/**
|
|
74
|
+
* Module to import the color scheme hook from
|
|
75
|
+
* @default 'react-native'
|
|
76
|
+
*/
|
|
77
|
+
importFrom?: string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Name of the hook to import
|
|
81
|
+
* @default 'useColorScheme'
|
|
82
|
+
*/
|
|
83
|
+
importName?: string;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Plugin state - passed through all visitors
|
|
89
|
+
*/
|
|
90
|
+
export type PluginState = PluginPass & {
|
|
91
|
+
styleRegistry: Map<string, StyleObject>;
|
|
92
|
+
hasClassNames: boolean;
|
|
93
|
+
hasStyleSheetImport: boolean;
|
|
94
|
+
hasPlatformImport: boolean;
|
|
95
|
+
needsPlatformImport: boolean;
|
|
96
|
+
hasColorSchemeImport: boolean;
|
|
97
|
+
needsColorSchemeImport: boolean;
|
|
98
|
+
colorSchemeVariableName: string;
|
|
99
|
+
colorSchemeImportSource: string; // Where to import the hook from (e.g., 'react-native')
|
|
100
|
+
colorSchemeHookName: string; // Name of the hook to import (e.g., 'useColorScheme')
|
|
101
|
+
colorSchemeLocalIdentifier?: string; // Local identifier if hook is already imported with an alias
|
|
102
|
+
hasWindowDimensionsImport: boolean;
|
|
103
|
+
needsWindowDimensionsImport: boolean;
|
|
104
|
+
windowDimensionsVariableName: string;
|
|
105
|
+
windowDimensionsLocalIdentifier?: string; // Local identifier if hook is already imported with an alias
|
|
106
|
+
hasI18nManagerImport: boolean;
|
|
107
|
+
needsI18nManagerImport: boolean;
|
|
108
|
+
i18nManagerVariableName: string; // Variable name for the RTL state (e.g., '_twIsRTL')
|
|
109
|
+
i18nManagerLocalIdentifier?: string; // Local identifier if I18nManager is already imported with an alias
|
|
110
|
+
customTheme: CustomTheme;
|
|
111
|
+
schemeModifierConfig: SchemeModifierConfig;
|
|
112
|
+
supportedAttributes: Set<string>;
|
|
113
|
+
attributePatterns: RegExp[];
|
|
114
|
+
stylesIdentifier: string;
|
|
115
|
+
// Track tw/twStyle imports from main package
|
|
116
|
+
twImportNames: Set<string>; // e.g., ['tw', 'twStyle'] or ['tw as customTw']
|
|
117
|
+
hasTwImport: boolean;
|
|
118
|
+
// Track react-native import path for conditional StyleSheet/Platform injection
|
|
119
|
+
reactNativeImportPath?: NodePath<BabelTypes.ImportDeclaration>;
|
|
120
|
+
// Track function components that need colorScheme hook injection
|
|
121
|
+
functionComponentsNeedingColorScheme: Set<NodePath<BabelTypes.Function>>;
|
|
122
|
+
// Track function components that need windowDimensions hook injection
|
|
123
|
+
functionComponentsNeedingWindowDimensions: Set<NodePath<BabelTypes.Function>>;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Default identifier for the generated StyleSheet constant
|
|
127
|
+
export const DEFAULT_STYLES_IDENTIFIER = "_twStyles";
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create initial plugin state for a file
|
|
131
|
+
*
|
|
132
|
+
* @param options - Plugin options from babel config
|
|
133
|
+
* @param filename - Current file being processed
|
|
134
|
+
* @param colorSchemeImportSource - Where to import the color scheme hook from
|
|
135
|
+
* @param colorSchemeHookName - Name of the color scheme hook to import
|
|
136
|
+
* @param schemeModifierConfig - Configuration for scheme: modifier expansion
|
|
137
|
+
* @returns Initial plugin state
|
|
138
|
+
*/
|
|
139
|
+
export function createInitialState(
|
|
140
|
+
options: PluginOptions | undefined,
|
|
141
|
+
filename: string,
|
|
142
|
+
colorSchemeImportSource: string,
|
|
143
|
+
colorSchemeHookName: string,
|
|
144
|
+
schemeModifierConfig: SchemeModifierConfig,
|
|
145
|
+
): Partial<PluginState> {
|
|
146
|
+
// Build attribute matchers from options
|
|
147
|
+
const attributes = options?.attributes ?? [...DEFAULT_CLASS_ATTRIBUTES];
|
|
148
|
+
const { exactMatches, patterns } = buildAttributeMatchers(attributes);
|
|
149
|
+
const stylesIdentifier = options?.stylesIdentifier ?? DEFAULT_STYLES_IDENTIFIER;
|
|
150
|
+
|
|
151
|
+
// Load custom theme from tailwind.config.*
|
|
152
|
+
const customTheme = extractCustomTheme(filename);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
styleRegistry: new Map(),
|
|
156
|
+
hasClassNames: false,
|
|
157
|
+
hasStyleSheetImport: false,
|
|
158
|
+
hasPlatformImport: false,
|
|
159
|
+
needsPlatformImport: false,
|
|
160
|
+
hasColorSchemeImport: false,
|
|
161
|
+
needsColorSchemeImport: false,
|
|
162
|
+
colorSchemeVariableName: "_twColorScheme",
|
|
163
|
+
colorSchemeImportSource,
|
|
164
|
+
colorSchemeHookName,
|
|
165
|
+
colorSchemeLocalIdentifier: undefined,
|
|
166
|
+
hasWindowDimensionsImport: false,
|
|
167
|
+
needsWindowDimensionsImport: false,
|
|
168
|
+
windowDimensionsVariableName: "_twDimensions",
|
|
169
|
+
windowDimensionsLocalIdentifier: undefined,
|
|
170
|
+
hasI18nManagerImport: false,
|
|
171
|
+
needsI18nManagerImport: false,
|
|
172
|
+
i18nManagerVariableName: "_twIsRTL",
|
|
173
|
+
i18nManagerLocalIdentifier: undefined,
|
|
174
|
+
customTheme,
|
|
175
|
+
schemeModifierConfig,
|
|
176
|
+
supportedAttributes: exactMatches,
|
|
177
|
+
attributePatterns: patterns,
|
|
178
|
+
stylesIdentifier,
|
|
179
|
+
twImportNames: new Set(),
|
|
180
|
+
hasTwImport: false,
|
|
181
|
+
reactNativeImportPath: undefined,
|
|
182
|
+
functionComponentsNeedingColorScheme: new Set(),
|
|
183
|
+
functionComponentsNeedingWindowDimensions: new Set(),
|
|
184
|
+
};
|
|
185
|
+
}
|