@mgcrea/react-native-tailwind 0.7.0 → 0.8.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 (81) hide show
  1. package/README.md +2 -1
  2. package/dist/babel/index.cjs +333 -195
  3. package/dist/babel/index.d.ts +4 -40
  4. package/dist/babel/index.test.ts +214 -1
  5. package/dist/babel/index.ts +4 -1169
  6. package/dist/babel/plugin.d.ts +42 -0
  7. package/{src/babel/index.test.ts → dist/babel/plugin.test.ts} +216 -2
  8. package/dist/babel/plugin.ts +491 -0
  9. package/dist/babel/utils/attributeMatchers.d.ts +23 -0
  10. package/dist/babel/utils/attributeMatchers.ts +71 -0
  11. package/dist/babel/utils/componentSupport.d.ts +18 -0
  12. package/dist/babel/utils/componentSupport.ts +68 -0
  13. package/dist/babel/utils/dynamicProcessing.d.ts +32 -0
  14. package/dist/babel/utils/dynamicProcessing.ts +223 -0
  15. package/dist/babel/utils/modifierProcessing.d.ts +26 -0
  16. package/dist/babel/utils/modifierProcessing.ts +118 -0
  17. package/dist/babel/utils/styleInjection.d.ts +15 -0
  18. package/dist/babel/utils/styleInjection.ts +80 -0
  19. package/dist/babel/utils/styleTransforms.d.ts +39 -0
  20. package/dist/babel/utils/styleTransforms.test.ts +349 -0
  21. package/dist/babel/utils/styleTransforms.ts +258 -0
  22. package/dist/babel/utils/twProcessing.d.ts +28 -0
  23. package/dist/babel/utils/twProcessing.ts +124 -0
  24. package/dist/components/TextInput.d.ts +171 -14
  25. package/dist/config/tailwind.d.ts +302 -0
  26. package/dist/config/tailwind.js +1 -0
  27. package/dist/index.d.ts +5 -4
  28. package/dist/index.js +1 -1
  29. package/dist/parser/colors.js +1 -1
  30. package/dist/parser/index.d.ts +1 -0
  31. package/dist/parser/index.js +1 -1
  32. package/dist/parser/modifiers.d.ts +2 -2
  33. package/dist/parser/modifiers.js +1 -1
  34. package/dist/parser/placeholder.d.ts +36 -0
  35. package/dist/parser/placeholder.js +1 -0
  36. package/dist/parser/placeholder.test.js +1 -0
  37. package/dist/parser/typography.d.ts +1 -0
  38. package/dist/parser/typography.js +1 -1
  39. package/dist/parser/typography.test.js +1 -1
  40. package/dist/runtime.cjs +1 -1
  41. package/dist/runtime.cjs.map +4 -4
  42. package/dist/runtime.d.ts +1 -14
  43. package/dist/runtime.js +1 -1
  44. package/dist/runtime.js.map +4 -4
  45. package/dist/stubs/tw.d.ts +1 -14
  46. package/dist/types/core.d.ts +40 -0
  47. package/dist/types/core.js +0 -0
  48. package/dist/types/index.d.ts +2 -0
  49. package/dist/types/index.js +1 -0
  50. package/dist/types/runtime.d.ts +15 -0
  51. package/dist/types/runtime.js +1 -0
  52. package/dist/types/util.d.ts +3 -0
  53. package/dist/types/util.js +0 -0
  54. package/package.json +1 -1
  55. package/src/babel/index.ts +4 -1169
  56. package/src/babel/plugin.test.ts +482 -0
  57. package/src/babel/plugin.ts +491 -0
  58. package/src/babel/utils/attributeMatchers.ts +71 -0
  59. package/src/babel/utils/componentSupport.ts +68 -0
  60. package/src/babel/utils/dynamicProcessing.ts +223 -0
  61. package/src/babel/utils/modifierProcessing.ts +118 -0
  62. package/src/babel/utils/styleInjection.ts +80 -0
  63. package/src/babel/utils/styleTransforms.test.ts +349 -0
  64. package/src/babel/utils/styleTransforms.ts +258 -0
  65. package/src/babel/utils/twProcessing.ts +124 -0
  66. package/src/components/TextInput.tsx +17 -14
  67. package/src/config/{palettes.ts → tailwind.ts} +2 -2
  68. package/src/index.ts +6 -3
  69. package/src/parser/colors.ts +2 -2
  70. package/src/parser/index.ts +1 -0
  71. package/src/parser/modifiers.ts +10 -4
  72. package/src/parser/placeholder.test.ts +105 -0
  73. package/src/parser/placeholder.ts +78 -0
  74. package/src/parser/typography.test.ts +11 -0
  75. package/src/parser/typography.ts +20 -2
  76. package/src/runtime.ts +1 -16
  77. package/src/stubs/tw.ts +1 -16
  78. package/src/{types.ts → types/core.ts} +0 -4
  79. package/src/types/index.ts +2 -0
  80. package/src/types/runtime.ts +17 -0
  81. package/src/types/util.ts +1 -0
@@ -258,8 +258,8 @@ function parseBorderRadius(cls) {
258
258
  return null;
259
259
  }
260
260
 
261
- // src/config/palettes.ts
262
- var TAILWIND_PALETTES = {
261
+ // src/config/tailwind.ts
262
+ var TAILWIND_COLORS = {
263
263
  red: {
264
264
  "50": "#fef2f2",
265
265
  "100": "#ffe2e2",
@@ -564,7 +564,7 @@ function flattenColors(colors, prefix = "") {
564
564
 
565
565
  // src/parser/colors.ts
566
566
  var COLORS = {
567
- ...flattenColors(TAILWIND_PALETTES),
567
+ ...flattenColors(TAILWIND_COLORS),
568
568
  // Add basic colors
569
569
  white: "#FFFFFF",
570
570
  black: "#000000",
@@ -1619,6 +1619,16 @@ var TEXT_TRANSFORM_MAP = {
1619
1619
  capitalize: { textTransform: "capitalize" },
1620
1620
  "normal-case": { textTransform: "none" }
1621
1621
  };
1622
+ var LINE_HEIGHT_SCALE = {
1623
+ 3: 12,
1624
+ 4: 16,
1625
+ 5: 20,
1626
+ 6: 24,
1627
+ 7: 28,
1628
+ 8: 32,
1629
+ 9: 36,
1630
+ 10: 40
1631
+ };
1622
1632
  var LINE_HEIGHT_MAP = {
1623
1633
  "leading-none": { lineHeight: 16 },
1624
1634
  "leading-tight": { lineHeight: 20 },
@@ -1683,12 +1693,50 @@ function parseTypography(cls) {
1683
1693
  if (arbitraryValue !== null) {
1684
1694
  return { lineHeight: arbitraryValue };
1685
1695
  }
1696
+ const lineHeight = LINE_HEIGHT_SCALE[heightKey];
1697
+ if (lineHeight !== void 0) {
1698
+ return { lineHeight };
1699
+ }
1686
1700
  }
1687
1701
  return FONT_FAMILY_MAP[cls] ?? FONT_WEIGHT_MAP[cls] ?? FONT_STYLE_MAP[cls] ?? TEXT_ALIGN_MAP[cls] ?? TEXT_DECORATION_MAP[cls] ?? TEXT_TRANSFORM_MAP[cls] ?? LINE_HEIGHT_MAP[cls] ?? TRACKING_MAP[cls] ?? null;
1688
1702
  }
1689
1703
 
1704
+ // src/parser/placeholder.ts
1705
+ function parsePlaceholderClass(cls, customColors) {
1706
+ if (!cls.startsWith("text-")) {
1707
+ if (process.env.NODE_ENV !== "production") {
1708
+ console.warn(
1709
+ `[react-native-tailwind] Only text color utilities are supported in placeholder: modifier. Class "${cls}" will be ignored. React Native only supports placeholderTextColor prop.`
1710
+ );
1711
+ }
1712
+ return null;
1713
+ }
1714
+ const styleObject = parseColor(cls, customColors);
1715
+ if (!styleObject?.color) {
1716
+ return null;
1717
+ }
1718
+ return styleObject.color;
1719
+ }
1720
+ function parsePlaceholderClasses(classes, customColors) {
1721
+ const classList = classes.trim().split(/\s+/).filter(Boolean);
1722
+ let finalColor = null;
1723
+ for (const cls of classList) {
1724
+ const color = parsePlaceholderClass(cls, customColors);
1725
+ if (color) {
1726
+ finalColor = color;
1727
+ }
1728
+ }
1729
+ return finalColor;
1730
+ }
1731
+
1690
1732
  // src/parser/modifiers.ts
1691
- var SUPPORTED_MODIFIERS = ["active", "hover", "focus", "disabled"];
1733
+ var SUPPORTED_MODIFIERS = [
1734
+ "active",
1735
+ "hover",
1736
+ "focus",
1737
+ "disabled",
1738
+ "placeholder"
1739
+ ];
1692
1740
  function parseModifier(cls) {
1693
1741
  const colonIndex = cls.indexOf(":");
1694
1742
  if (colonIndex === -1) {
@@ -1828,8 +1876,7 @@ function extractCustomColors(filename) {
1828
1876
  return flattenColors(colors);
1829
1877
  }
1830
1878
 
1831
- // src/babel/index.ts
1832
- var DEFAULT_STYLES_IDENTIFIER = "_twStyles";
1879
+ // src/babel/utils/attributeMatchers.ts
1833
1880
  var DEFAULT_CLASS_ATTRIBUTES = [
1834
1881
  "className",
1835
1882
  "contentContainerClassName",
@@ -1864,6 +1911,8 @@ function isAttributeSupported(attributeName, exactMatches, patterns) {
1864
1911
  function getTargetStyleProp(attributeName) {
1865
1912
  return attributeName.endsWith("ClassName") ? attributeName.replace("ClassName", "Style") : "style";
1866
1913
  }
1914
+
1915
+ // src/babel/utils/componentSupport.ts
1867
1916
  function getComponentModifierSupport(jsxElement, t) {
1868
1917
  if (!t.isJSXOpeningElement(jsxElement)) {
1869
1918
  return null;
@@ -1886,24 +1935,40 @@ function getComponentModifierSupport(jsxElement, t) {
1886
1935
  case "Pressable":
1887
1936
  return { component: "Pressable", supportedModifiers: ["active", "hover", "focus", "disabled"] };
1888
1937
  case "TextInput":
1889
- return { component: "TextInput", supportedModifiers: ["focus", "disabled"] };
1938
+ return { component: "TextInput", supportedModifiers: ["focus", "disabled", "placeholder"] };
1890
1939
  default:
1891
1940
  return null;
1892
1941
  }
1893
1942
  }
1894
- function processDynamicExpression(expression, state, t) {
1943
+ function getStatePropertyForModifier(modifier) {
1944
+ switch (modifier) {
1945
+ case "active":
1946
+ return "pressed";
1947
+ case "hover":
1948
+ return "hovered";
1949
+ case "focus":
1950
+ return "focused";
1951
+ case "disabled":
1952
+ return "disabled";
1953
+ default:
1954
+ return "pressed";
1955
+ }
1956
+ }
1957
+
1958
+ // src/babel/utils/dynamicProcessing.ts
1959
+ function processDynamicExpression(expression, state, parseClassName2, generateStyleKey2, t) {
1895
1960
  if (t.isTemplateLiteral(expression)) {
1896
- return processTemplateLiteral(expression, state, t);
1961
+ return processTemplateLiteral(expression, state, parseClassName2, generateStyleKey2, t);
1897
1962
  }
1898
1963
  if (t.isConditionalExpression(expression)) {
1899
- return processConditionalExpression(expression, state, t);
1964
+ return processConditionalExpression(expression, state, parseClassName2, generateStyleKey2, t);
1900
1965
  }
1901
1966
  if (t.isLogicalExpression(expression)) {
1902
- return processLogicalExpression(expression, state, t);
1967
+ return processLogicalExpression(expression, state, parseClassName2, generateStyleKey2, t);
1903
1968
  }
1904
1969
  return null;
1905
1970
  }
1906
- function processTemplateLiteral(node, state, t) {
1971
+ function processTemplateLiteral(node, state, parseClassName2, generateStyleKey2, t) {
1907
1972
  const parts = [];
1908
1973
  const staticParts = [];
1909
1974
  for (let i = 0; i < node.quasis.length; i++) {
@@ -1921,7 +1986,13 @@ function processTemplateLiteral(node, state, t) {
1921
1986
  }
1922
1987
  if (i < node.expressions.length) {
1923
1988
  const expr = node.expressions[i];
1924
- const result = processDynamicExpression(expr, state, t);
1989
+ const result = processDynamicExpression(
1990
+ expr,
1991
+ state,
1992
+ parseClassName2,
1993
+ generateStyleKey2,
1994
+ t
1995
+ );
1925
1996
  if (result) {
1926
1997
  parts.push(result.expression);
1927
1998
  } else {
@@ -1938,9 +2009,9 @@ function processTemplateLiteral(node, state, t) {
1938
2009
  staticParts: staticParts.length > 0 ? staticParts : void 0
1939
2010
  };
1940
2011
  }
1941
- function processConditionalExpression(node, state, t) {
1942
- const consequent = processStringOrExpression(node.consequent, state, t);
1943
- const alternate = processStringOrExpression(node.alternate, state, t);
2012
+ function processConditionalExpression(node, state, parseClassName2, generateStyleKey2, t) {
2013
+ const consequent = processStringOrExpression(node.consequent, state, parseClassName2, generateStyleKey2, t);
2014
+ const alternate = processStringOrExpression(node.alternate, state, parseClassName2, generateStyleKey2, t);
1944
2015
  if (!consequent && !alternate) {
1945
2016
  return null;
1946
2017
  }
@@ -1951,18 +2022,18 @@ function processConditionalExpression(node, state, t) {
1951
2022
  );
1952
2023
  return { expression };
1953
2024
  }
1954
- function processLogicalExpression(node, state, t) {
2025
+ function processLogicalExpression(node, state, parseClassName2, generateStyleKey2, t) {
1955
2026
  if (node.operator !== "&&") {
1956
2027
  return null;
1957
2028
  }
1958
- const right = processStringOrExpression(node.right, state, t);
2029
+ const right = processStringOrExpression(node.right, state, parseClassName2, generateStyleKey2, t);
1959
2030
  if (!right) {
1960
2031
  return null;
1961
2032
  }
1962
2033
  const expression = t.logicalExpression("&&", node.left, right);
1963
2034
  return { expression };
1964
2035
  }
1965
- function processStringOrExpression(node, state, t) {
2036
+ function processStringOrExpression(node, state, parseClassName2, generateStyleKey2, t) {
1966
2037
  if (t.isStringLiteral(node)) {
1967
2038
  const className = node.value.trim();
1968
2039
  if (!className) {
@@ -1974,21 +2045,23 @@ function processStringOrExpression(node, state, t) {
1974
2045
  return t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
1975
2046
  }
1976
2047
  if (t.isConditionalExpression(node)) {
1977
- const result = processConditionalExpression(node, state, t);
2048
+ const result = processConditionalExpression(node, state, parseClassName2, generateStyleKey2, t);
1978
2049
  return result?.expression ?? null;
1979
2050
  }
1980
2051
  if (t.isLogicalExpression(node)) {
1981
- const result = processLogicalExpression(node, state, t);
2052
+ const result = processLogicalExpression(node, state, parseClassName2, generateStyleKey2, t);
1982
2053
  return result?.expression ?? null;
1983
2054
  }
1984
2055
  if (t.isTemplateLiteral(node)) {
1985
- const result = processTemplateLiteral(node, state, t);
2056
+ const result = processTemplateLiteral(node, state, parseClassName2, generateStyleKey2, t);
1986
2057
  return result?.expression ?? null;
1987
2058
  }
1988
2059
  return null;
1989
2060
  }
1990
- function processStaticClassNameWithModifiers(className, state, t) {
1991
- const { baseClasses, modifierClasses } = splitModifierClasses(className);
2061
+
2062
+ // src/babel/utils/modifierProcessing.ts
2063
+ function processStaticClassNameWithModifiers(className, state, parseClassName2, generateStyleKey2, splitModifierClasses2, t) {
2064
+ const { baseClasses, modifierClasses } = splitModifierClasses2(className);
1992
2065
  let baseStyleExpression = null;
1993
2066
  if (baseClasses.length > 0) {
1994
2067
  const baseClassName = baseClasses.join(" ");
@@ -2029,20 +2102,6 @@ function processStaticClassNameWithModifiers(className, state, t) {
2029
2102
  }
2030
2103
  return t.arrayExpression(styleArrayElements);
2031
2104
  }
2032
- function getStatePropertyForModifier(modifier) {
2033
- switch (modifier) {
2034
- case "active":
2035
- return "pressed";
2036
- case "hover":
2037
- return "hovered";
2038
- case "focus":
2039
- return "focused";
2040
- case "disabled":
2041
- return "disabled";
2042
- default:
2043
- return "pressed";
2044
- }
2045
- }
2046
2105
  function createStyleFunction(styleExpression, modifierTypes, t) {
2047
2106
  const paramProperties = [];
2048
2107
  const usedStateProps = /* @__PURE__ */ new Set();
@@ -2058,8 +2117,166 @@ function createStyleFunction(styleExpression, modifierTypes, t) {
2058
2117
  const param = t.objectPattern(paramProperties);
2059
2118
  return t.arrowFunctionExpression([param], styleExpression);
2060
2119
  }
2061
- function processTwCall(className, path2, state, t) {
2062
- const { baseClasses, modifierClasses } = splitModifierClasses(className);
2120
+
2121
+ // src/babel/utils/styleInjection.ts
2122
+ function addStyleSheetImport(path2, t) {
2123
+ const importDeclaration = t.importDeclaration(
2124
+ [t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet"))],
2125
+ t.stringLiteral("react-native")
2126
+ );
2127
+ path2.unshiftContainer("body", importDeclaration);
2128
+ }
2129
+ function injectStylesAtTop(path2, styleRegistry, stylesIdentifier, t) {
2130
+ const styleProperties = [];
2131
+ for (const [key, styleObject] of styleRegistry) {
2132
+ const properties = Object.entries(styleObject).map(([styleProp, styleValue]) => {
2133
+ let valueNode;
2134
+ if (typeof styleValue === "number") {
2135
+ valueNode = t.numericLiteral(styleValue);
2136
+ } else if (typeof styleValue === "string") {
2137
+ valueNode = t.stringLiteral(styleValue);
2138
+ } else {
2139
+ valueNode = t.valueToNode(styleValue);
2140
+ }
2141
+ return t.objectProperty(t.identifier(styleProp), valueNode);
2142
+ });
2143
+ styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
2144
+ }
2145
+ const styleSheet = t.variableDeclaration("const", [
2146
+ t.variableDeclarator(
2147
+ t.identifier(stylesIdentifier),
2148
+ t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
2149
+ t.objectExpression(styleProperties)
2150
+ ])
2151
+ )
2152
+ ]);
2153
+ const body = path2.node.body;
2154
+ let insertIndex = 0;
2155
+ for (let i = 0; i < body.length; i++) {
2156
+ if (t.isImportDeclaration(body[i])) {
2157
+ insertIndex = i + 1;
2158
+ } else {
2159
+ break;
2160
+ }
2161
+ }
2162
+ body.splice(insertIndex, 0, styleSheet);
2163
+ }
2164
+
2165
+ // src/babel/utils/styleTransforms.ts
2166
+ function getStyleExpression(styleAttribute, t) {
2167
+ const value = styleAttribute.value;
2168
+ if (!t.isJSXExpressionContainer(value)) return null;
2169
+ const expression = value.expression;
2170
+ if (t.isJSXEmptyExpression(expression)) return null;
2171
+ return expression;
2172
+ }
2173
+ function findStyleAttribute(path2, targetStyleProp, t) {
2174
+ const parent = path2.parent;
2175
+ return parent.attributes.find(
2176
+ (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === targetStyleProp
2177
+ );
2178
+ }
2179
+ function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, stylesIdentifier, t) {
2180
+ const styleAttribute = t.jsxAttribute(
2181
+ t.jsxIdentifier(targetStyleProp),
2182
+ t.jsxExpressionContainer(t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)))
2183
+ );
2184
+ classNamePath.replaceWith(styleAttribute);
2185
+ }
2186
+ function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, stylesIdentifier, t) {
2187
+ const existingStyle = getStyleExpression(styleAttribute, t);
2188
+ if (!existingStyle) return;
2189
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2190
+ const paramIdentifier = t.identifier("_state");
2191
+ const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2192
+ const mergedArray = t.arrayExpression([
2193
+ t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2194
+ functionCall
2195
+ ]);
2196
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2197
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2198
+ } else {
2199
+ const styleArray = t.arrayExpression([
2200
+ t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2201
+ existingStyle
2202
+ ]);
2203
+ styleAttribute.value = t.jsxExpressionContainer(styleArray);
2204
+ }
2205
+ classNamePath.remove();
2206
+ }
2207
+ function replaceDynamicWithStyleAttribute(classNamePath, result, targetStyleProp, t) {
2208
+ const styleAttribute = t.jsxAttribute(
2209
+ t.jsxIdentifier(targetStyleProp),
2210
+ t.jsxExpressionContainer(result.expression)
2211
+ );
2212
+ classNamePath.replaceWith(styleAttribute);
2213
+ }
2214
+ function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
2215
+ const existingStyle = getStyleExpression(styleAttribute, t);
2216
+ if (!existingStyle) return;
2217
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2218
+ const paramIdentifier = t.identifier("_state");
2219
+ const functionCall = t.callExpression(existingStyle, [paramIdentifier]);
2220
+ const mergedArray = t.arrayExpression([result.expression, functionCall]);
2221
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2222
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2223
+ } else {
2224
+ let styleArray;
2225
+ if (t.isArrayExpression(existingStyle)) {
2226
+ styleArray = t.arrayExpression([result.expression, ...existingStyle.elements]);
2227
+ } else {
2228
+ styleArray = t.arrayExpression([result.expression, existingStyle]);
2229
+ }
2230
+ styleAttribute.value = t.jsxExpressionContainer(styleArray);
2231
+ }
2232
+ classNamePath.remove();
2233
+ }
2234
+ function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
2235
+ const styleAttribute = t.jsxAttribute(
2236
+ t.jsxIdentifier(targetStyleProp),
2237
+ t.jsxExpressionContainer(styleFunctionExpression)
2238
+ );
2239
+ classNamePath.replaceWith(styleAttribute);
2240
+ }
2241
+ function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
2242
+ const existingStyle = getStyleExpression(styleAttribute, t);
2243
+ if (!existingStyle) return;
2244
+ if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2245
+ const paramIdentifier = t.identifier("_state");
2246
+ const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2247
+ const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
2248
+ const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
2249
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2250
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2251
+ } else {
2252
+ const paramIdentifier = t.identifier("_state");
2253
+ const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2254
+ const mergedArray = t.arrayExpression([functionCall, existingStyle]);
2255
+ const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2256
+ styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2257
+ }
2258
+ classNamePath.remove();
2259
+ }
2260
+ function addOrMergePlaceholderTextColorProp(jsxOpeningElement, color, t) {
2261
+ const existingProp = jsxOpeningElement.attributes.find(
2262
+ (attr) => t.isJSXAttribute(attr) && attr.name.name === "placeholderTextColor"
2263
+ );
2264
+ if (existingProp) {
2265
+ if (process.env.NODE_ENV !== "production") {
2266
+ console.warn(
2267
+ `[react-native-tailwind] placeholderTextColor prop will be overridden by className placeholder: modifier. Remove the explicit prop or the placeholder: modifier to avoid confusion.`
2268
+ );
2269
+ }
2270
+ existingProp.value = t.stringLiteral(color);
2271
+ } else {
2272
+ const newProp = t.jsxAttribute(t.jsxIdentifier("placeholderTextColor"), t.stringLiteral(color));
2273
+ jsxOpeningElement.attributes.push(newProp);
2274
+ }
2275
+ }
2276
+
2277
+ // src/babel/utils/twProcessing.ts
2278
+ function processTwCall(className, path2, state, parseClassName2, generateStyleKey2, splitModifierClasses2, t) {
2279
+ const { baseClasses, modifierClasses } = splitModifierClasses2(className);
2063
2280
  const objectProperties = [];
2064
2281
  if (baseClasses.length > 0) {
2065
2282
  const baseClassName = baseClasses.join(" ");
@@ -2101,6 +2318,31 @@ function processTwCall(className, path2, state, t) {
2101
2318
  const twStyleObject = t.objectExpression(objectProperties);
2102
2319
  path2.replaceWith(twStyleObject);
2103
2320
  }
2321
+ function removeTwImports(path2, t) {
2322
+ path2.traverse({
2323
+ ImportDeclaration(importPath) {
2324
+ const node = importPath.node;
2325
+ if (node.source.value !== "@mgcrea/react-native-tailwind") {
2326
+ return;
2327
+ }
2328
+ const remainingSpecifiers = node.specifiers.filter((spec) => {
2329
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
2330
+ const importedName = spec.imported.name;
2331
+ return importedName !== "tw" && importedName !== "twStyle";
2332
+ }
2333
+ return true;
2334
+ });
2335
+ if (remainingSpecifiers.length === 0) {
2336
+ importPath.remove();
2337
+ } else if (remainingSpecifiers.length < node.specifiers.length) {
2338
+ node.specifiers = remainingSpecifiers;
2339
+ }
2340
+ }
2341
+ });
2342
+ }
2343
+
2344
+ // src/babel/plugin.ts
2345
+ var DEFAULT_STYLES_IDENTIFIER = "_twStyles";
2104
2346
  function reactNativeTailwindBabelPlugin({ types: t }, options) {
2105
2347
  const attributes = options?.attributes ?? [...DEFAULT_CLASS_ATTRIBUTES];
2106
2348
  const { exactMatches, patterns } = buildAttributeMatchers(attributes);
@@ -2195,7 +2437,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2195
2437
  return;
2196
2438
  }
2197
2439
  state.hasClassNames = true;
2198
- processTwCall(className, path2, state, t);
2440
+ processTwCall(className, path2, state, parseClassName, generateStyleKey, splitModifierClasses, t);
2199
2441
  },
2200
2442
  // Handle twStyle('...') call expressions
2201
2443
  CallExpression(path2, state) {
@@ -2230,10 +2472,13 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2230
2472
  return;
2231
2473
  }
2232
2474
  state.hasClassNames = true;
2233
- processTwCall(className, path2, state, t);
2475
+ processTwCall(className, path2, state, parseClassName, generateStyleKey, splitModifierClasses, t);
2234
2476
  },
2235
2477
  JSXAttribute(path2, state) {
2236
2478
  const node = path2.node;
2479
+ if (!t.isJSXIdentifier(node.name)) {
2480
+ return;
2481
+ }
2237
2482
  const attributeName = node.name.name;
2238
2483
  if (!isAttributeSupported(attributeName, state.supportedAttributes, state.attributePatterns)) {
2239
2484
  return;
@@ -2248,11 +2493,30 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2248
2493
  }
2249
2494
  state.hasClassNames = true;
2250
2495
  const { baseClasses, modifierClasses } = splitModifierClasses(className);
2251
- if (modifierClasses.length > 0) {
2496
+ const placeholderModifiers = modifierClasses.filter((m) => m.modifier === "placeholder");
2497
+ const stateModifiers = modifierClasses.filter((m) => m.modifier !== "placeholder");
2498
+ if (placeholderModifiers.length > 0) {
2499
+ const jsxOpeningElement = path2.parent;
2500
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
2501
+ if (componentSupport?.supportedModifiers.includes("placeholder")) {
2502
+ const placeholderClasses = placeholderModifiers.map((m) => m.baseClass).join(" ");
2503
+ const placeholderColor = parsePlaceholderClasses(placeholderClasses, state.customColors);
2504
+ if (placeholderColor) {
2505
+ addOrMergePlaceholderTextColorProp(jsxOpeningElement, placeholderColor, t);
2506
+ }
2507
+ } else {
2508
+ if (process.env.NODE_ENV !== "production") {
2509
+ console.warn(
2510
+ `[react-native-tailwind] placeholder: modifier can only be used on TextInput component at ${state.file.opts.filename ?? "unknown"}`
2511
+ );
2512
+ }
2513
+ }
2514
+ }
2515
+ if (stateModifiers.length > 0) {
2252
2516
  const jsxOpeningElement = path2.parent;
2253
2517
  const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
2254
2518
  if (componentSupport) {
2255
- const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
2519
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
2256
2520
  const unsupportedModifiers = usedModifiers.filter(
2257
2521
  (mod) => !componentSupport.supportedModifiers.includes(mod)
2258
2522
  );
@@ -2262,7 +2526,7 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2262
2526
  `[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(", ")}`
2263
2527
  );
2264
2528
  }
2265
- const supportedModifierClasses = modifierClasses.filter(
2529
+ const supportedModifierClasses = stateModifiers.filter(
2266
2530
  (m) => componentSupport.supportedModifiers.includes(m.modifier)
2267
2531
  );
2268
2532
  if (supportedModifierClasses.length === 0) {
@@ -2271,14 +2535,14 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2271
2535
  const styleExpression = processStaticClassNameWithModifiers(
2272
2536
  filteredClassName.trim(),
2273
2537
  state,
2538
+ parseClassName,
2539
+ generateStyleKey,
2540
+ splitModifierClasses,
2274
2541
  t
2275
2542
  );
2276
2543
  const modifierTypes = Array.from(new Set(supportedModifierClasses.map((m) => m.modifier)));
2277
2544
  const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
2278
- const parent2 = path2.parent;
2279
- const styleAttribute2 = parent2.attributes.find(
2280
- (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
2281
- );
2545
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
2282
2546
  if (styleAttribute2) {
2283
2547
  mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
2284
2548
  } else {
@@ -2287,13 +2551,17 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2287
2551
  return;
2288
2552
  }
2289
2553
  } else {
2290
- const styleExpression = processStaticClassNameWithModifiers(className, state, t);
2554
+ const styleExpression = processStaticClassNameWithModifiers(
2555
+ className,
2556
+ state,
2557
+ parseClassName,
2558
+ generateStyleKey,
2559
+ splitModifierClasses,
2560
+ t
2561
+ );
2291
2562
  const modifierTypes = usedModifiers;
2292
2563
  const styleFunctionExpression = createStyleFunction(styleExpression, modifierTypes, t);
2293
- const parent2 = path2.parent;
2294
- const styleAttribute2 = parent2.attributes.find(
2295
- (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
2296
- );
2564
+ const styleAttribute2 = findStyleAttribute(path2, targetStyleProp, t);
2297
2565
  if (styleAttribute2) {
2298
2566
  mergeStyleFunctionAttribute(path2, styleAttribute2, styleFunctionExpression, t);
2299
2567
  } else {
@@ -2303,20 +2571,22 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2303
2571
  }
2304
2572
  } else {
2305
2573
  if (process.env.NODE_ENV !== "production") {
2306
- const usedModifiers = Array.from(new Set(modifierClasses.map((m) => m.modifier)));
2574
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier)));
2307
2575
  console.warn(
2308
2576
  `[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"}`
2309
2577
  );
2310
2578
  }
2311
2579
  }
2312
2580
  }
2313
- const styleObject = parseClassName2(className, state.customColors);
2314
- const styleKey = generateStyleKey2(className);
2581
+ const classNameForStyle = baseClasses.join(" ");
2582
+ if (!classNameForStyle) {
2583
+ path2.remove();
2584
+ return;
2585
+ }
2586
+ const styleObject = parseClassName(classNameForStyle, state.customColors);
2587
+ const styleKey = generateStyleKey(classNameForStyle);
2315
2588
  state.styleRegistry.set(styleKey, styleObject);
2316
- const parent = path2.parent;
2317
- const styleAttribute = parent.attributes.find(
2318
- (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
2319
- );
2589
+ const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
2320
2590
  if (styleAttribute) {
2321
2591
  mergeStyleAttribute(path2, styleAttribute, styleKey, state.stylesIdentifier, t);
2322
2592
  } else {
@@ -2330,13 +2600,10 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2330
2600
  return;
2331
2601
  }
2332
2602
  try {
2333
- const result = processDynamicExpression(expression, state, t);
2603
+ const result = processDynamicExpression(expression, state, parseClassName, generateStyleKey, t);
2334
2604
  if (result) {
2335
2605
  state.hasClassNames = true;
2336
- const parent = path2.parent;
2337
- const styleAttribute = parent.attributes.find(
2338
- (attr) => t.isJSXAttribute(attr) && attr.name.name === targetStyleProp
2339
- );
2606
+ const styleAttribute = findStyleAttribute(path2, targetStyleProp, t);
2340
2607
  if (styleAttribute) {
2341
2608
  mergeDynamicStyleAttribute(path2, styleAttribute, result, t);
2342
2609
  } else {
@@ -2362,132 +2629,3 @@ function reactNativeTailwindBabelPlugin({ types: t }, options) {
2362
2629
  }
2363
2630
  };
2364
2631
  }
2365
- function addStyleSheetImport(path2, t) {
2366
- const importDeclaration = t.importDeclaration(
2367
- [t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet"))],
2368
- t.stringLiteral("react-native")
2369
- );
2370
- path2.unshiftContainer("body", importDeclaration);
2371
- }
2372
- function removeTwImports(path2, t) {
2373
- path2.traverse({
2374
- ImportDeclaration(importPath) {
2375
- const node = importPath.node;
2376
- if (node.source.value !== "@mgcrea/react-native-tailwind") {
2377
- return;
2378
- }
2379
- const remainingSpecifiers = node.specifiers.filter((spec) => {
2380
- if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
2381
- const importedName = spec.imported.name;
2382
- return importedName !== "tw" && importedName !== "twStyle";
2383
- }
2384
- return true;
2385
- });
2386
- if (remainingSpecifiers.length === 0) {
2387
- importPath.remove();
2388
- } else if (remainingSpecifiers.length < node.specifiers.length) {
2389
- node.specifiers = remainingSpecifiers;
2390
- }
2391
- }
2392
- });
2393
- }
2394
- function replaceWithStyleAttribute(classNamePath, styleKey, targetStyleProp, stylesIdentifier, t) {
2395
- const styleAttribute = t.jsxAttribute(
2396
- t.jsxIdentifier(targetStyleProp),
2397
- t.jsxExpressionContainer(t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)))
2398
- );
2399
- classNamePath.replaceWith(styleAttribute);
2400
- }
2401
- function mergeStyleAttribute(classNamePath, styleAttribute, styleKey, stylesIdentifier, t) {
2402
- const existingStyle = styleAttribute.value.expression;
2403
- const styleArray = t.arrayExpression([
2404
- t.memberExpression(t.identifier(stylesIdentifier), t.identifier(styleKey)),
2405
- existingStyle
2406
- ]);
2407
- styleAttribute.value = t.jsxExpressionContainer(styleArray);
2408
- classNamePath.remove();
2409
- }
2410
- function replaceDynamicWithStyleAttribute(classNamePath, result, targetStyleProp, t) {
2411
- const styleAttribute = t.jsxAttribute(
2412
- t.jsxIdentifier(targetStyleProp),
2413
- t.jsxExpressionContainer(result.expression)
2414
- );
2415
- classNamePath.replaceWith(styleAttribute);
2416
- }
2417
- function mergeDynamicStyleAttribute(classNamePath, styleAttribute, result, t) {
2418
- const existingStyle = styleAttribute.value.expression;
2419
- let styleArray;
2420
- if (t.isArrayExpression(existingStyle)) {
2421
- styleArray = t.arrayExpression([result.expression, ...existingStyle.elements]);
2422
- } else {
2423
- styleArray = t.arrayExpression([result.expression, existingStyle]);
2424
- }
2425
- styleAttribute.value = t.jsxExpressionContainer(styleArray);
2426
- classNamePath.remove();
2427
- }
2428
- function replaceWithStyleFunctionAttribute(classNamePath, styleFunctionExpression, targetStyleProp, t) {
2429
- const styleAttribute = t.jsxAttribute(
2430
- t.jsxIdentifier(targetStyleProp),
2431
- t.jsxExpressionContainer(styleFunctionExpression)
2432
- );
2433
- classNamePath.replaceWith(styleAttribute);
2434
- }
2435
- function mergeStyleFunctionAttribute(classNamePath, styleAttribute, styleFunctionExpression, t) {
2436
- const existingStyle = styleAttribute.value.expression;
2437
- if (t.isArrowFunctionExpression(existingStyle) || t.isFunctionExpression(existingStyle)) {
2438
- const paramIdentifier = t.identifier("_state");
2439
- const newFunctionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2440
- const existingFunctionCall = t.callExpression(existingStyle, [paramIdentifier]);
2441
- const mergedArray = t.arrayExpression([newFunctionCall, existingFunctionCall]);
2442
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2443
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2444
- } else {
2445
- const paramIdentifier = t.identifier("_state");
2446
- const functionCall = t.callExpression(styleFunctionExpression, [paramIdentifier]);
2447
- const mergedArray = t.arrayExpression([functionCall, existingStyle]);
2448
- const wrapperFunction = t.arrowFunctionExpression([paramIdentifier], mergedArray);
2449
- styleAttribute.value = t.jsxExpressionContainer(wrapperFunction);
2450
- }
2451
- classNamePath.remove();
2452
- }
2453
- function injectStylesAtTop(path2, styleRegistry, stylesIdentifier, t) {
2454
- const styleProperties = [];
2455
- for (const [key, styleObject] of styleRegistry) {
2456
- const properties = Object.entries(styleObject).map(([styleProp, styleValue]) => {
2457
- let valueNode;
2458
- if (typeof styleValue === "number") {
2459
- valueNode = t.numericLiteral(styleValue);
2460
- } else if (typeof styleValue === "string") {
2461
- valueNode = t.stringLiteral(styleValue);
2462
- } else {
2463
- valueNode = t.valueToNode(styleValue);
2464
- }
2465
- return t.objectProperty(t.identifier(styleProp), valueNode);
2466
- });
2467
- styleProperties.push(t.objectProperty(t.identifier(key), t.objectExpression(properties)));
2468
- }
2469
- const styleSheet = t.variableDeclaration("const", [
2470
- t.variableDeclarator(
2471
- t.identifier(stylesIdentifier),
2472
- t.callExpression(t.memberExpression(t.identifier("StyleSheet"), t.identifier("create")), [
2473
- t.objectExpression(styleProperties)
2474
- ])
2475
- )
2476
- ]);
2477
- const body = path2.node.body;
2478
- let insertIndex = 0;
2479
- for (let i = 0; i < body.length; i++) {
2480
- if (t.isImportDeclaration(body[i])) {
2481
- insertIndex = i + 1;
2482
- } else {
2483
- break;
2484
- }
2485
- }
2486
- body.splice(insertIndex, 0, styleSheet);
2487
- }
2488
- function parseClassName2(className, customColors) {
2489
- return parseClassName(className, customColors);
2490
- }
2491
- function generateStyleKey2(className) {
2492
- return generateStyleKey(className);
2493
- }