or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

component-rules.mdindex.mdplugin-configuration.mdstyle-quality-rules.mdstylesheet-rules.md
tile.json

component-rules.mddocs/

Component Rules

Rules that manage React Native component usage patterns and platform-specific requirements, ensuring proper text wrapping and platform separation.

Capabilities

Split Platform Components Rule

Enforces using platform-specific filenames when components contain Android or iOS specific code, promoting proper code organization and platform separation.

/**
 * ESLint rule that enforces platform-specific components in appropriate files
 * Ensures Android and iOS components are placed in correctly named files
 */
module.exports = {
  meta: {
    fixable: "code", // Supports auto-fixing in some scenarios
    schema: [{
      type: "object",
      properties: {
        androidPathRegex: {
          type: "string" // Custom regex pattern for Android file paths
        },
        iosPathRegex: {
          type: "string" // Custom regex pattern for iOS file paths
        }
      },
      additionalProperties: false
    }]
  },
  create: function(context) {
    // Returns ESLint visitors object with:
    // - VariableDeclarator: Tracks React component destructuring
    // - ImportDeclaration: Tracks react-native imports with platform components
    // - 'Program:exit': Reports platform-specific components in wrong files
  }
};

Configuration Examples:

// Default configuration (built-in patterns)
"react-native/split-platform-components": "error"

// Custom file patterns
"react-native/split-platform-components": ["error", {
  "androidPathRegex": "\\.android\\.(js|ts|jsx|tsx)$",
  "iosPathRegex": "\\.ios\\.(js|ts|jsx|tsx)$"
}]

// Custom patterns for different naming conventions
"react-native/split-platform-components": ["error", {
  "androidPathRegex": "_android\\.(js|ts)$",
  "iosPathRegex": "_ios\\.(js|ts)$"
}]

Usage Examples:

// File: Button.js (would trigger the rule)
import { Platform, TouchableOpacityIOS, TouchableNativeFeedbackAndroid } from 'react-native';

// This should be split into platform-specific files

// File: Button.ios.js (correct)
import { TouchableOpacityIOS } from 'react-native';
export const PlatformButton = TouchableOpacityIOS;

// File: Button.android.js (correct)  
import { TouchableNativeFeedbackAndroid } from 'react-native';
export const PlatformButton = TouchableNativeFeedbackAndroid;

// File: Button.js (shared code - acceptable)
import { View, Text } from 'react-native';

No Raw Text Rule

Detects raw text outside of Text components, ensuring all text content is properly wrapped for React Native compatibility.

/**
 * ESLint rule that detects raw text outside of Text components
 * Ensures all text content is properly wrapped in React Native Text components
 */
module.exports = {
  meta: {
    schema: [{
      type: "object",
      properties: {
        skip: {
          type: "array",
          items: {
            type: "string" // Array of component names to skip validation for
          }
        }
      },
      additionalProperties: false
    }]
  },
  create: function(context) {
    // Returns ESLint visitors object with:
    // - Literal: Analyzes string literals in JSX for raw text
    // - JSXText: Analyzes JSX text nodes for raw text
    // - TemplateLiteral: Analyzes template literals in JSX for raw text
  }
};

Configuration Examples:

// Default configuration
"react-native/no-raw-text": "error"

// Skip validation for custom text components
"react-native/no-raw-text": ["error", {
  "skip": ["CustomText", "StyledText", "FormattedText"]
}]

Usage Examples:

// These would trigger the rule
function BadComponent() {
  return (
    <View>
      Hello World {/* Raw text - not allowed */}
      <Text>Good text</Text>
      {someCondition && "Conditional text"} {/* Raw string - not allowed */}
      <View>
        Some text here {/* Raw text in View - not allowed */}
      </View>
    </View>
  );
}

// Correct usage
function GoodComponent() {
  return (
    <View>
      <Text>Hello World</Text> {/* Properly wrapped */}
      <Text>Good text</Text>
      {someCondition && <Text>Conditional text</Text>} {/* Properly wrapped */}
      <View>
        <Text>Some text here</Text> {/* Properly wrapped */}
      </View>
    </View>
  );
}

// With custom skip components
function ComponentWithCustomText() {
  return (
    <View>
      <CustomText>
        This text is allowed {/* Skipped due to configuration */}
      </CustomText>
      <StyledText>
        This is also allowed {/* Skipped due to configuration */}
      </StyledText>
    </View>
  );
}

Default Allowed Components

The no-raw-text rule includes several components that are allowed to contain raw text by default:

const defaultAllowedElements = [
  'Text',           // Standard React Native Text component
  'TSpan',          // SVG text element
  'StyledText',     // Common styled-components pattern
  'Animated.Text'   // Animated Text component
];

Error Messages

Split Platform Components Messages

// Error messages
"Android components should be placed in android files"
"IOS components should be placed in ios files"
"IOS and Android components can't be mixed" // When both are found in same file

No Raw Text Messages

// Error format: "Raw text ({text}) cannot be used outside of a <Text> tag"
"Raw text (Hello World) cannot be used outside of a <Text> tag"
"Raw text (Click here) cannot be used outside of a <Text> tag"

// For whitespace-only content
"Whitespace(s) cannot be used outside of a <Text> tag"

// For template literals
"TemplateLiteral: ${variableName} cannot be used outside of a <Text> tag"

Platform-Specific Development Patterns

File Organization

// Recommended file structure
components/
├── Button/
│   ├── index.js          // Platform selector or shared logic
│   ├── Button.ios.js     // iOS-specific implementation
│   ├── Button.android.js // Android-specific implementation
│   └── Button.shared.js  // Shared utilities and styles

// Platform selector pattern (index.js)
import { Platform } from 'react-native';
import ButtonIOS from './Button.ios';
import ButtonAndroid from './Button.android';

export default Platform.select({
  ios: ButtonIOS,
  android: ButtonAndroid,
});

Component Detection

The rules automatically detect platform-specific components from common imports:

// Automatically detected as iOS-specific
import { 
  TouchableOpacityIOS,
  ActionSheetIOS,
  DatePickerIOS,
  PickerIOS,
  TabBarIOS,
  NavigatorIOS
} from 'react-native';

// Automatically detected as Android-specific
import {
  TouchableNativeFeedbackAndroid,
  DrawerLayoutAndroid,
  ProgressBarAndroid,
  ToolbarAndroid,
  ViewPagerAndroid
} from 'react-native';

Text Component Patterns

The no-raw-text rule supports various text component patterns:

// Standard patterns (all supported)
import { Text } from 'react-native';
import { Text as RNText } from 'react-native';
import { Animated } from 'react-native';

<Text>Standard text</Text>
<RNText>Aliased text</RNText>
<Animated.Text>Animated text</Animated.Text>

// Custom components via configuration
<CustomText>Custom wrapped text</CustomText>
<StyledText>Styled component text</StyledText>

Best Practices

Platform Separation

  1. Keep shared logic in base files: Common functionality should remain in non-platform-specific files
  2. Use Platform.select for runtime decisions: When platform differences are minor
  3. Use separate files for major differences: When implementations differ significantly
  4. Document platform-specific behavior: Comment why platform separation is needed

Text Wrapping

  1. Always wrap text content: Even single words should be in Text components
  2. Handle conditional text properly: Use Text components inside conditionals
  3. Consider accessibility: Text components provide better accessibility support
  4. Use semantic text components: Create custom text components for different text types (headings, body, etc.)