@mgcrea/react-native-tailwind 0.8.1 → 0.9.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 (39) hide show
  1. package/README.md +152 -0
  2. package/dist/babel/config-loader.ts +2 -0
  3. package/dist/babel/index.cjs +177 -4
  4. package/dist/babel/plugin.d.ts +2 -0
  5. package/dist/babel/plugin.test.ts +241 -0
  6. package/dist/babel/plugin.ts +187 -10
  7. package/dist/babel/utils/platformModifierProcessing.d.ts +30 -0
  8. package/dist/babel/utils/platformModifierProcessing.ts +80 -0
  9. package/dist/babel/utils/styleInjection.d.ts +4 -0
  10. package/dist/babel/utils/styleInjection.ts +28 -0
  11. package/dist/babel/utils/styleTransforms.ts +1 -0
  12. package/dist/parser/index.d.ts +2 -2
  13. package/dist/parser/index.js +1 -1
  14. package/dist/parser/modifiers.d.ts +20 -2
  15. package/dist/parser/modifiers.js +1 -1
  16. package/dist/runtime.cjs +1 -1
  17. package/dist/runtime.cjs.map +4 -4
  18. package/dist/runtime.js +1 -1
  19. package/dist/runtime.js.map +4 -4
  20. package/dist/stubs/tw.test.js +1 -0
  21. package/package.json +6 -5
  22. package/src/babel/config-loader.ts +2 -0
  23. package/src/babel/plugin.test.ts +241 -0
  24. package/src/babel/plugin.ts +187 -10
  25. package/src/babel/utils/platformModifierProcessing.ts +80 -0
  26. package/src/babel/utils/styleInjection.ts +28 -0
  27. package/src/babel/utils/styleTransforms.ts +1 -0
  28. package/src/parser/aspectRatio.ts +1 -0
  29. package/src/parser/borders.ts +2 -0
  30. package/src/parser/colors.ts +2 -0
  31. package/src/parser/index.ts +9 -2
  32. package/src/parser/layout.ts +2 -0
  33. package/src/parser/modifiers.ts +38 -4
  34. package/src/parser/placeholder.ts +1 -0
  35. package/src/parser/sizing.ts +1 -0
  36. package/src/parser/spacing.ts +1 -0
  37. package/src/parser/transforms.ts +5 -0
  38. package/src/parser/typography.ts +2 -0
  39. package/src/stubs/tw.test.ts +27 -0
@@ -5,7 +5,14 @@
5
5
 
6
6
  import type { NodePath, PluginObj, PluginPass } from "@babel/core";
7
7
  import * as BabelTypes from "@babel/types";
8
- import { parseClassName, parsePlaceholderClasses, splitModifierClasses } from "../parser/index.js";
8
+ import type { ParsedModifier, StateModifierType } from "../parser/index.js";
9
+ import {
10
+ isPlatformModifier,
11
+ isStateModifier,
12
+ parseClassName,
13
+ parsePlaceholderClasses,
14
+ splitModifierClasses,
15
+ } from "../parser/index.js";
9
16
  import type { StyleObject } from "../types/core.js";
10
17
  import { generateStyleKey } from "../utils/styleKey.js";
11
18
  import { extractCustomColors } from "./config-loader.js";
@@ -17,10 +24,11 @@ import {
17
24
  getTargetStyleProp,
18
25
  isAttributeSupported,
19
26
  } from "./utils/attributeMatchers.js";
20
- import { getComponentModifierSupport } from "./utils/componentSupport.js";
27
+ import { getComponentModifierSupport, getStatePropertyForModifier } from "./utils/componentSupport.js";
21
28
  import { processDynamicExpression } from "./utils/dynamicProcessing.js";
22
29
  import { createStyleFunction, processStaticClassNameWithModifiers } from "./utils/modifierProcessing.js";
23
- import { addStyleSheetImport, injectStylesAtTop } from "./utils/styleInjection.js";
30
+ import { processPlatformModifiers } from "./utils/platformModifierProcessing.js";
31
+ import { addPlatformImport, addStyleSheetImport, injectStylesAtTop } from "./utils/styleInjection.js";
24
32
  import {
25
33
  addOrMergePlaceholderTextColorProp,
26
34
  findStyleAttribute,
@@ -59,6 +67,8 @@ type PluginState = PluginPass & {
59
67
  styleRegistry: Map<string, StyleObject>;
60
68
  hasClassNames: boolean;
61
69
  hasStyleSheetImport: boolean;
70
+ hasPlatformImport: boolean;
71
+ needsPlatformImport: boolean;
62
72
  customColors: Record<string, string>;
63
73
  supportedAttributes: Set<string>;
64
74
  attributePatterns: RegExp[];
@@ -90,6 +100,8 @@ export default function reactNativeTailwindBabelPlugin(
90
100
  state.styleRegistry = new Map();
91
101
  state.hasClassNames = false;
92
102
  state.hasStyleSheetImport = false;
103
+ state.hasPlatformImport = false;
104
+ state.needsPlatformImport = false;
93
105
  state.supportedAttributes = exactMatches;
94
106
  state.attributePatterns = patterns;
95
107
  state.stylesIdentifier = stylesIdentifier;
@@ -116,19 +128,25 @@ export default function reactNativeTailwindBabelPlugin(
116
128
  addStyleSheetImport(path, t);
117
129
  }
118
130
 
131
+ // Add Platform import if platform modifiers were used and not already present
132
+ if (state.needsPlatformImport && !state.hasPlatformImport) {
133
+ addPlatformImport(path, t);
134
+ }
135
+
119
136
  // Generate and inject StyleSheet.create at the beginning of the file (after imports)
120
137
  // This ensures _twStyles is defined before any code that references it
121
138
  injectStylesAtTop(path, state.styleRegistry, state.stylesIdentifier, t);
122
139
  },
123
140
  },
124
141
 
125
- // Check if StyleSheet is already imported and track tw/twStyle imports
142
+ // Check if StyleSheet/Platform are already imported and track tw/twStyle imports
126
143
  ImportDeclaration(path, state) {
127
144
  const node = path.node;
128
145
 
129
- // Track react-native StyleSheet import
146
+ // Track react-native StyleSheet and Platform imports
130
147
  if (node.source.value === "react-native") {
131
148
  const specifiers = node.specifiers;
149
+
132
150
  const hasStyleSheet = specifiers.some((spec) => {
133
151
  if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
134
152
  return spec.imported.name === "StyleSheet";
@@ -136,6 +154,13 @@ export default function reactNativeTailwindBabelPlugin(
136
154
  return false;
137
155
  });
138
156
 
157
+ const hasPlatform = specifiers.some((spec) => {
158
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported)) {
159
+ return spec.imported.name === "Platform";
160
+ }
161
+ return false;
162
+ });
163
+
139
164
  if (hasStyleSheet) {
140
165
  state.hasStyleSheetImport = true;
141
166
  } else {
@@ -143,6 +168,10 @@ export default function reactNativeTailwindBabelPlugin(
143
168
  node.specifiers.push(t.importSpecifier(t.identifier("StyleSheet"), t.identifier("StyleSheet")));
144
169
  state.hasStyleSheetImport = true;
145
170
  }
171
+
172
+ if (hasPlatform) {
173
+ state.hasPlatformImport = true;
174
+ }
146
175
  }
147
176
 
148
177
  // Track tw/twStyle imports from main package (for compile-time transformation)
@@ -291,12 +320,15 @@ export default function reactNativeTailwindBabelPlugin(
291
320
 
292
321
  state.hasClassNames = true;
293
322
 
294
- // Check if className contains modifiers (active:, hover:, focus:, placeholder:)
323
+ // Check if className contains modifiers (active:, hover:, focus:, placeholder:, ios:, android:, web:)
295
324
  const { baseClasses, modifierClasses } = splitModifierClasses(className);
296
325
 
297
- // Separate placeholder modifiers from state modifiers
326
+ // Separate modifiers by type
298
327
  const placeholderModifiers = modifierClasses.filter((m) => m.modifier === "placeholder");
299
- const stateModifiers = modifierClasses.filter((m) => m.modifier !== "placeholder");
328
+ const platformModifiers = modifierClasses.filter((m) => isPlatformModifier(m.modifier));
329
+ const stateModifiers = modifierClasses.filter(
330
+ (m) => isStateModifier(m.modifier) && m.modifier !== "placeholder",
331
+ );
300
332
 
301
333
  // Handle placeholder modifiers first (they generate placeholderTextColor prop, not style)
302
334
  if (placeholderModifiers.length > 0) {
@@ -322,8 +354,153 @@ export default function reactNativeTailwindBabelPlugin(
322
354
  }
323
355
  }
324
356
 
325
- // If there are state modifiers, check if this component supports them
326
- if (stateModifiers.length > 0) {
357
+ // Handle combination of modifiers
358
+ const hasPlatformModifiers = platformModifiers.length > 0;
359
+ const hasStateModifiers = stateModifiers.length > 0;
360
+ const hasBaseClasses = baseClasses.length > 0;
361
+
362
+ // If we have both state and platform modifiers, or platform modifiers with complex state,
363
+ // we need to combine them in an array expression wrapped in an arrow function
364
+ if (hasStateModifiers && hasPlatformModifiers) {
365
+ // Get the JSX opening element for component support checking
366
+ const jsxOpeningElement = path.parent;
367
+ const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
368
+
369
+ if (componentSupport) {
370
+ // Build style array: [baseStyle, Platform.select(...), stateConditionals]
371
+ const styleArrayElements: BabelTypes.Expression[] = [];
372
+
373
+ // Add base classes
374
+ if (hasBaseClasses) {
375
+ const baseClassName = baseClasses.join(" ");
376
+ const baseStyleObject = parseClassName(baseClassName, state.customColors);
377
+ const baseStyleKey = generateStyleKey(baseClassName);
378
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
379
+ styleArrayElements.push(
380
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey)),
381
+ );
382
+ }
383
+
384
+ // Add platform modifiers as Platform.select()
385
+ const platformSelectExpression = processPlatformModifiers(
386
+ platformModifiers,
387
+ state,
388
+ parseClassName,
389
+ generateStyleKey,
390
+ t,
391
+ );
392
+ styleArrayElements.push(platformSelectExpression);
393
+
394
+ // Add state modifiers as conditionals
395
+ // Group by modifier type
396
+ const modifiersByType = new Map<StateModifierType, ParsedModifier[]>();
397
+ for (const mod of stateModifiers) {
398
+ const modType = mod.modifier as StateModifierType;
399
+ if (!modifiersByType.has(modType)) {
400
+ modifiersByType.set(modType, []);
401
+ }
402
+ modifiersByType.get(modType)?.push(mod);
403
+ }
404
+
405
+ // Build conditionals for each state modifier type
406
+ for (const [modifierType, modifiers] of modifiersByType) {
407
+ if (!componentSupport.supportedModifiers.includes(modifierType)) {
408
+ continue; // Skip unsupported modifiers
409
+ }
410
+
411
+ const modifierClassNames = modifiers.map((m) => m.baseClass).join(" ");
412
+ const modifierStyleObject = parseClassName(modifierClassNames, state.customColors);
413
+ const modifierStyleKey = generateStyleKey(`${modifierType}_${modifierClassNames}`);
414
+ state.styleRegistry.set(modifierStyleKey, modifierStyleObject);
415
+
416
+ const stateProperty = getStatePropertyForModifier(modifierType);
417
+ const conditionalExpression = t.logicalExpression(
418
+ "&&",
419
+ t.identifier(stateProperty),
420
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(modifierStyleKey)),
421
+ );
422
+
423
+ styleArrayElements.push(conditionalExpression);
424
+ }
425
+
426
+ // Wrap in arrow function for state support
427
+ const usedModifiers = Array.from(new Set(stateModifiers.map((m) => m.modifier))).filter((mod) =>
428
+ componentSupport.supportedModifiers.includes(mod),
429
+ );
430
+ const styleArrayExpression = t.arrayExpression(styleArrayElements);
431
+ const styleFunctionExpression = createStyleFunction(styleArrayExpression, usedModifiers, t);
432
+
433
+ const styleAttribute = findStyleAttribute(path, targetStyleProp, t);
434
+ if (styleAttribute) {
435
+ mergeStyleFunctionAttribute(path, styleAttribute, styleFunctionExpression, t);
436
+ } else {
437
+ replaceWithStyleFunctionAttribute(path, styleFunctionExpression, targetStyleProp, t);
438
+ }
439
+ return;
440
+ } else {
441
+ // Component doesn't support state modifiers, but we can still use platform modifiers
442
+ // Fall through to platform-only handling
443
+ }
444
+ }
445
+
446
+ // Handle platform-only modifiers (no state modifiers)
447
+ if (hasPlatformModifiers && !hasStateModifiers) {
448
+ // Build style array/expression: [baseStyle, Platform.select(...)]
449
+ const styleExpressions: BabelTypes.Expression[] = [];
450
+
451
+ // Add base classes
452
+ if (hasBaseClasses) {
453
+ const baseClassName = baseClasses.join(" ");
454
+ const baseStyleObject = parseClassName(baseClassName, state.customColors);
455
+ const baseStyleKey = generateStyleKey(baseClassName);
456
+ state.styleRegistry.set(baseStyleKey, baseStyleObject);
457
+ styleExpressions.push(
458
+ t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(baseStyleKey)),
459
+ );
460
+ }
461
+
462
+ // Add platform modifiers as Platform.select()
463
+ const platformSelectExpression = processPlatformModifiers(
464
+ platformModifiers,
465
+ state,
466
+ parseClassName,
467
+ generateStyleKey,
468
+ t,
469
+ );
470
+ styleExpressions.push(platformSelectExpression);
471
+
472
+ // Generate style attribute
473
+ const styleExpression =
474
+ styleExpressions.length === 1 ? styleExpressions[0] : t.arrayExpression(styleExpressions);
475
+
476
+ const styleAttribute = findStyleAttribute(path, targetStyleProp, t);
477
+ if (styleAttribute) {
478
+ // Merge with existing style attribute
479
+ const existingStyle = styleAttribute.value;
480
+ if (
481
+ t.isJSXExpressionContainer(existingStyle) &&
482
+ !t.isJSXEmptyExpression(existingStyle.expression)
483
+ ) {
484
+ const existing = existingStyle.expression;
485
+ // Merge as array: [ourStyles, existingStyles]
486
+ const mergedArray = t.isArrayExpression(existing)
487
+ ? t.arrayExpression([styleExpression, ...existing.elements])
488
+ : t.arrayExpression([styleExpression, existing]);
489
+ styleAttribute.value = t.jsxExpressionContainer(mergedArray);
490
+ } else {
491
+ styleAttribute.value = t.jsxExpressionContainer(styleExpression);
492
+ }
493
+ path.remove();
494
+ } else {
495
+ // Replace className with style prop containing our expression
496
+ path.node.name = t.jsxIdentifier(targetStyleProp);
497
+ path.node.value = t.jsxExpressionContainer(styleExpression);
498
+ }
499
+ return;
500
+ }
501
+
502
+ // If there are state modifiers (and no platform modifiers), check if this component supports them
503
+ if (hasStateModifiers) {
327
504
  // Get the JSX opening element (the direct parent of the attribute)
328
505
  const jsxOpeningElement = path.parent;
329
506
  const componentSupport = getComponentModifierSupport(jsxOpeningElement, t);
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Utility functions for processing platform modifiers (ios:, android:, web:)
3
+ */
4
+ import type * as BabelTypes from "@babel/types";
5
+ import type { ParsedModifier } from "../../parser/index.js";
6
+ import type { StyleObject } from "../../types/core.js";
7
+ /**
8
+ * Plugin state interface (subset needed for platform modifier processing)
9
+ */
10
+ export interface PlatformModifierProcessingState {
11
+ styleRegistry: Map<string, StyleObject>;
12
+ customColors: Record<string, string>;
13
+ stylesIdentifier: string;
14
+ needsPlatformImport: boolean;
15
+ }
16
+ /**
17
+ * Process platform modifiers and generate Platform.select() expression
18
+ *
19
+ * @param platformModifiers - Array of parsed platform modifiers
20
+ * @param state - Plugin state
21
+ * @param parseClassName - Function to parse class names into style objects
22
+ * @param generateStyleKey - Function to generate unique style keys
23
+ * @param t - Babel types
24
+ * @returns AST node for Platform.select() call
25
+ *
26
+ * @example
27
+ * Input: [{ modifier: "ios", baseClass: "shadow-lg" }, { modifier: "android", baseClass: "elevation-4" }]
28
+ * Output: Platform.select({ ios: styles._ios_shadow_lg, android: styles._android_elevation_4 })
29
+ */
30
+ export declare function processPlatformModifiers(platformModifiers: ParsedModifier[], state: PlatformModifierProcessingState, parseClassName: (className: string, customColors: Record<string, string>) => StyleObject, generateStyleKey: (className: string) => string, t: typeof BabelTypes): BabelTypes.Expression;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Utility functions for processing platform modifiers (ios:, android:, web:)
3
+ */
4
+
5
+ import type * as BabelTypes from "@babel/types";
6
+ import type { ParsedModifier, PlatformModifierType } from "../../parser/index.js";
7
+ import type { StyleObject } from "../../types/core.js";
8
+
9
+ /**
10
+ * Plugin state interface (subset needed for platform modifier processing)
11
+ */
12
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
13
+ export interface PlatformModifierProcessingState {
14
+ styleRegistry: Map<string, StyleObject>;
15
+ customColors: Record<string, string>;
16
+ stylesIdentifier: string;
17
+ needsPlatformImport: boolean;
18
+ }
19
+
20
+ /**
21
+ * Process platform modifiers and generate Platform.select() expression
22
+ *
23
+ * @param platformModifiers - Array of parsed platform modifiers
24
+ * @param state - Plugin state
25
+ * @param parseClassName - Function to parse class names into style objects
26
+ * @param generateStyleKey - Function to generate unique style keys
27
+ * @param t - Babel types
28
+ * @returns AST node for Platform.select() call
29
+ *
30
+ * @example
31
+ * Input: [{ modifier: "ios", baseClass: "shadow-lg" }, { modifier: "android", baseClass: "elevation-4" }]
32
+ * Output: Platform.select({ ios: styles._ios_shadow_lg, android: styles._android_elevation_4 })
33
+ */
34
+ export function processPlatformModifiers(
35
+ platformModifiers: ParsedModifier[],
36
+ state: PlatformModifierProcessingState,
37
+ parseClassName: (className: string, customColors: Record<string, string>) => StyleObject,
38
+ generateStyleKey: (className: string) => string,
39
+ t: typeof BabelTypes,
40
+ ): BabelTypes.Expression {
41
+ // Mark that we need Platform import
42
+ state.needsPlatformImport = true;
43
+
44
+ // Group modifiers by platform
45
+ const modifiersByPlatform = new Map<PlatformModifierType, ParsedModifier[]>();
46
+
47
+ for (const mod of platformModifiers) {
48
+ const platform = mod.modifier as PlatformModifierType;
49
+ if (!modifiersByPlatform.has(platform)) {
50
+ modifiersByPlatform.set(platform, []);
51
+ }
52
+ const platformGroup = modifiersByPlatform.get(platform);
53
+ if (platformGroup) {
54
+ platformGroup.push(mod);
55
+ }
56
+ }
57
+
58
+ // Build Platform.select() object properties
59
+ const selectProperties: BabelTypes.ObjectProperty[] = [];
60
+
61
+ for (const [platform, modifiers] of modifiersByPlatform) {
62
+ // Parse all classes for this platform together
63
+ const classNames = modifiers.map((m) => m.baseClass).join(" ");
64
+ const styleObject = parseClassName(classNames, state.customColors);
65
+ const styleKey = generateStyleKey(`${platform}_${classNames}`);
66
+
67
+ // Register style in the registry
68
+ state.styleRegistry.set(styleKey, styleObject);
69
+
70
+ // Create property: ios: styles._ios_shadow_lg
71
+ const styleReference = t.memberExpression(t.identifier(state.stylesIdentifier), t.identifier(styleKey));
72
+
73
+ selectProperties.push(t.objectProperty(t.identifier(platform), styleReference));
74
+ }
75
+
76
+ // Create Platform.select({ ios: ..., android: ... })
77
+ return t.callExpression(t.memberExpression(t.identifier("Platform"), t.identifier("select")), [
78
+ t.objectExpression(selectProperties),
79
+ ]);
80
+ }
@@ -8,6 +8,10 @@ import type { StyleObject } from "../../types/core.js";
8
8
  * Add StyleSheet import to the file
9
9
  */
10
10
  export declare function addStyleSheetImport(path: NodePath<BabelTypes.Program>, t: typeof BabelTypes): void;
11
+ /**
12
+ * Add Platform import to the file or merge with existing react-native import
13
+ */
14
+ export declare function addPlatformImport(path: NodePath<BabelTypes.Program>, t: typeof BabelTypes): void;
11
15
  /**
12
16
  * Inject StyleSheet.create with all collected styles at the top of the file
13
17
  * This ensures the styles object is defined before any code that references it
@@ -19,6 +19,34 @@ export function addStyleSheetImport(path: NodePath<BabelTypes.Program>, t: typeo
19
19
  path.unshiftContainer("body", importDeclaration);
20
20
  }
21
21
 
22
+ /**
23
+ * Add Platform import to the file or merge with existing react-native import
24
+ */
25
+ export function addPlatformImport(path: NodePath<BabelTypes.Program>, t: typeof BabelTypes): void {
26
+ // Check if there's already a react-native import
27
+ const body = path.node.body;
28
+ let reactNativeImport: BabelTypes.ImportDeclaration | null = null;
29
+
30
+ for (const statement of body) {
31
+ if (t.isImportDeclaration(statement) && statement.source.value === "react-native") {
32
+ reactNativeImport = statement;
33
+ break;
34
+ }
35
+ }
36
+
37
+ if (reactNativeImport) {
38
+ // Add Platform to existing react-native import
39
+ reactNativeImport.specifiers.push(t.importSpecifier(t.identifier("Platform"), t.identifier("Platform")));
40
+ } else {
41
+ // Create new react-native import with Platform
42
+ const importDeclaration = t.importDeclaration(
43
+ [t.importSpecifier(t.identifier("Platform"), t.identifier("Platform"))],
44
+ t.stringLiteral("react-native"),
45
+ );
46
+ path.unshiftContainer("body", importDeclaration);
47
+ }
48
+ }
49
+
22
50
  /**
23
51
  * Inject StyleSheet.create with all collected styles at the top of the file
24
52
  * This ensures the styles object is defined before any code that references it
@@ -242,6 +242,7 @@ export function addOrMergePlaceholderTextColorProp(
242
242
  if (existingProp) {
243
243
  // If explicit prop exists, don't override it (explicit props take precedence)
244
244
  // This matches the behavior of style prop precedence
245
+ /* v8 ignore next 5 */
245
246
  if (process.env.NODE_ENV !== "production") {
246
247
  console.warn(
247
248
  `[react-native-tailwind] placeholderTextColor prop will be overridden by className placeholder: modifier. ` +
@@ -27,5 +27,5 @@ export { parseSizing } from "./sizing";
27
27
  export { parseSpacing } from "./spacing";
28
28
  export { parseTransform } from "./transforms";
29
29
  export { parseTypography } from "./typography";
30
- export { hasModifier, parseModifier, splitModifierClasses } from "./modifiers";
31
- export type { ModifierType, ParsedModifier } from "./modifiers";
30
+ export { hasModifier, isPlatformModifier, isStateModifier, parseModifier, splitModifierClasses, } from "./modifiers";
31
+ export type { ModifierType, ParsedModifier, PlatformModifierType, StateModifierType } from "./modifiers";
@@ -1 +1 @@
1
- Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"hasModifier",{enumerable:true,get:function get(){return _modifiers.hasModifier;}});Object.defineProperty(exports,"parseAspectRatio",{enumerable:true,get:function get(){return _aspectRatio.parseAspectRatio;}});Object.defineProperty(exports,"parseBorder",{enumerable:true,get:function get(){return _borders.parseBorder;}});exports.parseClass=parseClass;exports.parseClassName=parseClassName;Object.defineProperty(exports,"parseColor",{enumerable:true,get:function get(){return _colors.parseColor;}});Object.defineProperty(exports,"parseLayout",{enumerable:true,get:function get(){return _layout.parseLayout;}});Object.defineProperty(exports,"parseModifier",{enumerable:true,get:function get(){return _modifiers.parseModifier;}});Object.defineProperty(exports,"parsePlaceholderClass",{enumerable:true,get:function get(){return _placeholder.parsePlaceholderClass;}});Object.defineProperty(exports,"parsePlaceholderClasses",{enumerable:true,get:function get(){return _placeholder.parsePlaceholderClasses;}});Object.defineProperty(exports,"parseShadow",{enumerable:true,get:function get(){return _shadows.parseShadow;}});Object.defineProperty(exports,"parseSizing",{enumerable:true,get:function get(){return _sizing.parseSizing;}});Object.defineProperty(exports,"parseSpacing",{enumerable:true,get:function get(){return _spacing.parseSpacing;}});Object.defineProperty(exports,"parseTransform",{enumerable:true,get:function get(){return _transforms.parseTransform;}});Object.defineProperty(exports,"parseTypography",{enumerable:true,get:function get(){return _typography.parseTypography;}});Object.defineProperty(exports,"splitModifierClasses",{enumerable:true,get:function get(){return _modifiers.splitModifierClasses;}});var _aspectRatio=require("./aspectRatio");var _borders=require("./borders");var _colors=require("./colors");var _layout=require("./layout");var _shadows=require("./shadows");var _sizing=require("./sizing");var _spacing=require("./spacing");var _transforms=require("./transforms");var _typography=require("./typography");var _placeholder=require("./placeholder");var _modifiers=require("./modifiers");function parseClassName(className,customColors){var classes=className.split(/\s+/).filter(Boolean);var style={};for(var cls of classes){var parsedStyle=parseClass(cls,customColors);Object.assign(style,parsedStyle);}return style;}function parseClass(cls,customColors){var parsers=[_spacing.parseSpacing,_borders.parseBorder,function(cls){return(0,_colors.parseColor)(cls,customColors);},_layout.parseLayout,_typography.parseTypography,_sizing.parseSizing,_shadows.parseShadow,_aspectRatio.parseAspectRatio,_transforms.parseTransform];for(var parser of parsers){var result=parser(cls);if(result!==null){return result;}}if(process.env.NODE_ENV!=="production"){console.warn(`[react-native-tailwind] Unknown class: "${cls}"`);}return{};}
1
+ Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"hasModifier",{enumerable:true,get:function get(){return _modifiers.hasModifier;}});Object.defineProperty(exports,"isPlatformModifier",{enumerable:true,get:function get(){return _modifiers.isPlatformModifier;}});Object.defineProperty(exports,"isStateModifier",{enumerable:true,get:function get(){return _modifiers.isStateModifier;}});Object.defineProperty(exports,"parseAspectRatio",{enumerable:true,get:function get(){return _aspectRatio.parseAspectRatio;}});Object.defineProperty(exports,"parseBorder",{enumerable:true,get:function get(){return _borders.parseBorder;}});exports.parseClass=parseClass;exports.parseClassName=parseClassName;Object.defineProperty(exports,"parseColor",{enumerable:true,get:function get(){return _colors.parseColor;}});Object.defineProperty(exports,"parseLayout",{enumerable:true,get:function get(){return _layout.parseLayout;}});Object.defineProperty(exports,"parseModifier",{enumerable:true,get:function get(){return _modifiers.parseModifier;}});Object.defineProperty(exports,"parsePlaceholderClass",{enumerable:true,get:function get(){return _placeholder.parsePlaceholderClass;}});Object.defineProperty(exports,"parsePlaceholderClasses",{enumerable:true,get:function get(){return _placeholder.parsePlaceholderClasses;}});Object.defineProperty(exports,"parseShadow",{enumerable:true,get:function get(){return _shadows.parseShadow;}});Object.defineProperty(exports,"parseSizing",{enumerable:true,get:function get(){return _sizing.parseSizing;}});Object.defineProperty(exports,"parseSpacing",{enumerable:true,get:function get(){return _spacing.parseSpacing;}});Object.defineProperty(exports,"parseTransform",{enumerable:true,get:function get(){return _transforms.parseTransform;}});Object.defineProperty(exports,"parseTypography",{enumerable:true,get:function get(){return _typography.parseTypography;}});Object.defineProperty(exports,"splitModifierClasses",{enumerable:true,get:function get(){return _modifiers.splitModifierClasses;}});var _aspectRatio=require("./aspectRatio");var _borders=require("./borders");var _colors=require("./colors");var _layout=require("./layout");var _shadows=require("./shadows");var _sizing=require("./sizing");var _spacing=require("./spacing");var _transforms=require("./transforms");var _typography=require("./typography");var _placeholder=require("./placeholder");var _modifiers=require("./modifiers");function parseClassName(className,customColors){var classes=className.split(/\s+/).filter(Boolean);var style={};for(var cls of classes){var parsedStyle=parseClass(cls,customColors);Object.assign(style,parsedStyle);}return style;}function parseClass(cls,customColors){var parsers=[_spacing.parseSpacing,_borders.parseBorder,function(cls){return(0,_colors.parseColor)(cls,customColors);},_layout.parseLayout,_typography.parseTypography,_sizing.parseSizing,_shadows.parseShadow,_aspectRatio.parseAspectRatio,_transforms.parseTransform];for(var parser of parsers){var result=parser(cls);if(result!==null){return result;}}if(process.env.NODE_ENV!=="production"){console.warn(`[react-native-tailwind] Unknown class: "${cls}"`);}return{};}
@@ -1,7 +1,11 @@
1
1
  /**
2
- * Modifier parsing utilities for state-based class names (active:, hover:, focus:, placeholder:)
2
+ * Modifier parsing utilities for state-based and platform-specific class names
3
+ * - State modifiers: active:, hover:, focus:, disabled:, placeholder:
4
+ * - Platform modifiers: ios:, android:, web:
3
5
  */
4
- export type ModifierType = "active" | "hover" | "focus" | "disabled" | "placeholder";
6
+ export type StateModifierType = "active" | "hover" | "focus" | "disabled" | "placeholder";
7
+ export type PlatformModifierType = "ios" | "android" | "web";
8
+ export type ModifierType = StateModifierType | PlatformModifierType;
5
9
  export type ParsedModifier = {
6
10
  modifier: ModifierType;
7
11
  baseClass: string;
@@ -25,6 +29,20 @@ export declare function parseModifier(cls: string): ParsedModifier | null;
25
29
  * @returns true if class has a supported modifier prefix
26
30
  */
27
31
  export declare function hasModifier(cls: string): boolean;
32
+ /**
33
+ * Check if a modifier is a state modifier (active, hover, focus, disabled, placeholder)
34
+ *
35
+ * @param modifier - Modifier type to check
36
+ * @returns true if modifier is a state modifier
37
+ */
38
+ export declare function isStateModifier(modifier: ModifierType): modifier is StateModifierType;
39
+ /**
40
+ * Check if a modifier is a platform modifier (ios, android, web)
41
+ *
42
+ * @param modifier - Modifier type to check
43
+ * @returns true if modifier is a platform modifier
44
+ */
45
+ export declare function isPlatformModifier(modifier: ModifierType): modifier is PlatformModifierType;
28
46
  /**
29
47
  * Split a space-separated className string into base and modifier classes
30
48
  *
@@ -1 +1 @@
1
- Object.defineProperty(exports,"__esModule",{value:true});exports.hasModifier=hasModifier;exports.parseModifier=parseModifier;exports.splitModifierClasses=splitModifierClasses;var SUPPORTED_MODIFIERS=["active","hover","focus","disabled","placeholder"];function parseModifier(cls){var colonIndex=cls.indexOf(":");if(colonIndex===-1){return null;}var potentialModifier=cls.slice(0,colonIndex);var baseClass=cls.slice(colonIndex+1);if(!SUPPORTED_MODIFIERS.includes(potentialModifier)){return null;}if(baseClass.includes(":")){return null;}if(!baseClass){return null;}return{modifier:potentialModifier,baseClass:baseClass};}function hasModifier(cls){return parseModifier(cls)!==null;}function splitModifierClasses(className){var classes=className.trim().split(/\s+/).filter(Boolean);var baseClasses=[];var modifierClasses=[];for(var cls of classes){var parsed=parseModifier(cls);if(parsed){modifierClasses.push(parsed);}else{baseClasses.push(cls);}}return{baseClasses:baseClasses,modifierClasses:modifierClasses};}
1
+ Object.defineProperty(exports,"__esModule",{value:true});exports.hasModifier=hasModifier;exports.isPlatformModifier=isPlatformModifier;exports.isStateModifier=isStateModifier;exports.parseModifier=parseModifier;exports.splitModifierClasses=splitModifierClasses;var STATE_MODIFIERS=["active","hover","focus","disabled","placeholder"];var PLATFORM_MODIFIERS=["ios","android","web"];var SUPPORTED_MODIFIERS=[].concat(STATE_MODIFIERS,PLATFORM_MODIFIERS);function parseModifier(cls){var colonIndex=cls.indexOf(":");if(colonIndex===-1){return null;}var potentialModifier=cls.slice(0,colonIndex);var baseClass=cls.slice(colonIndex+1);if(!SUPPORTED_MODIFIERS.includes(potentialModifier)){return null;}if(baseClass.includes(":")){return null;}if(!baseClass){return null;}return{modifier:potentialModifier,baseClass:baseClass};}function hasModifier(cls){return parseModifier(cls)!==null;}function isStateModifier(modifier){return STATE_MODIFIERS.includes(modifier);}function isPlatformModifier(modifier){return PLATFORM_MODIFIERS.includes(modifier);}function splitModifierClasses(className){var classes=className.trim().split(/\s+/).filter(Boolean);var baseClasses=[];var modifierClasses=[];for(var cls of classes){var parsed=parseModifier(cls);if(parsed){modifierClasses.push(parsed);}else{baseClasses.push(cls);}}return{baseClasses:baseClasses,modifierClasses:modifierClasses};}