# Skill: Create New Dart-Only Rule (Dxxx)

> Skill to create a NEW rule exclusively for Dart/Flutter (Dxxx codes).
> Flat, simple structure - no router or JS wrapper needed.

---

## Execution Rules

> **IMPORTANT**: When executing this skill, AI Assistant must follow these rules:

- **Execute all commands WITHOUT asking for user confirmation**
- **Do NOT prompt "Should I proceed?" or similar questions**
- **Run commands immediately and report results after completion**
- Automatically create all necessary files without asking
- Automatically rebuild Dart binary after completion
- Only stop when encountering actual errors requiring user input

---

## Quick Start - How to Use This Skill

### Method 1: Request via AI Assistant

Tell the AI (Claude Code, Cursor, etc.):

```
Create Dart rule D001 to check naming convention for Flutter widgets
```

or more detailed:

```
Use create-new-dart-rule skill to create rule D001_flutter_widget_naming
check that widget names must end with Widget, Page, Screen, View
```

### Method 2: Manual Steps (Do it yourself)

```bash
# 1. Create rule folder in rules/dart/
mkdir -p rules/dart/D001_flutter_widget_naming

# 2. Create config.json
cat > rules/dart/D001_flutter_widget_naming/config.json << 'EOF'
{
  "id": "D001",
  "name": "Flutter Widget Naming",
  "description": "Widget class names should end with Widget, Page, Screen, or View",
  "category": "dart",
  "severity": "warning",
  "languages": ["dart"]
}
EOF

# 3. Create Dart analyzer implementation
# File: dart_analyzer/lib/rules/dart/D001_flutter_widget_naming.dart

# 4. Register in analyzer_service.dart

# 5. Update rule definition in markdown
# Edit: ../../../rules/dart-en.md (English)
# Edit: ../../../rules/dart.md (Vietnamese)
# Create: ../../../rules/examples/en/D001.md
# Create: ../../../rules/examples/vi/D001.md

# 6. Generate registry from markdown
node scripts/copy-rules.js
node scripts/generate-rules-registry.js

# 7. Create test fixtures
# examples/rule-test-fixtures/dart-rules/D001_flutter_widget_naming/clean/
# examples/rule-test-fixtures/dart-rules/D001_flutter_widget_naming/violations/

# 8. Rebuild Dart binary
cd dart_analyzer && dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos
cp bin/sunlint-dart-macos ../sunlint-dart-macos

# 9. Test rule
node cli.js --rule=D001 --input=/path/to/dart/project --languages=dart
```

### Method 3: Run the Created Rule

```bash
# Run D001 on a Dart project
node cli.js --rule=D001 --input=/path/to/dart/project --languages=dart

# With verbose output
node cli.js --rule=D001 --input=/path/to/dart/project --languages=dart --verbose

# Run on test fixtures
node cli.js --rule=D001 --input=examples/rule-test-fixtures/dart-rules/D001_recommended_lint_rules/violations --languages=dart

# If installed globally
sunlint --rule=D001 --input=/path/to/dart/project --languages=dart
```

---

## Metadata

| Field | Value |
|-------|-------|
| **Skill ID** | `create-new-dart-rule` |
| **Version** | 1.0.0 |
| **Author** | SunLint Team |
| **Category** | Code Generation |
| **Trigger** | User requests to create a new Dart-only rule |
| **Last Updated** | 2025-01-21 |

---

## 1. Prerequisites

### 1.1. Tools Required

| Tool | Version | Purpose |
|------|---------|---------|
| Node.js | >= 18.x | Run SunLint CLI |
| Dart SDK | >= 3.0.0 | Compile Dart analyzer |
| Git | Any | Version control |

### 1.2. Dependencies

```bash
# SunLint directory
cd /path/to/sunlint

# Dart analyzer dependencies
cd dart_analyzer
dart pub get
```

### 1.3. File Locations

| Component | Path | Action |
|-----------|------|--------|
| **Dart-only Rules Config** | `rules/dart/${RULE_ID}_*/` | CREATE folder & config.json |
| **Dart Analyzer Implementation** | `dart_analyzer/lib/rules/dart/${RULE_ID}_*.dart` | CREATE analyzer file |
| **Analyzer Service Registration** | `dart_analyzer/lib/analyzer_service.dart` | MODIFY - add import & register |
| **Rule Definition (English)** | `../../../rules/dart-en.md` | MODIFY - add rule definition |
| **Rule Definition (Vietnamese)** | `../../../rules/dart.md` | MODIFY - add rule definition |
| **Example Files (English)** | `../../../rules/examples/en/${RULE_ID}.md` | CREATE good/bad examples |
| **Example Files (Vietnamese)** | `../../../rules/examples/vi/${RULE_ID}.md` | CREATE ví dụ đúng/sai |
| **Test Fixtures** | `examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/` | CREATE clean/ & violations/ |
| **Generated Registry** | `config/rules/rules-registry-generated.json` | AUTO - generated from markdown |
| **Rules Summary** | `config/rules-summary.json` | MANUAL - update if needed |

---

## 2. Directory Structure

### 2.1. Comparison with Multi-language Rules

| Aspect | Multi-language (Cxxx, Sxxx) | Dart-only (Dxxx) |
|--------|----------------------------|------------------|
| Rule folder | `rules/common/` or `rules/security/` | `rules/dart/` |
| Need router (index.js)? | ✅ Yes | ❌ No |
| Need dart/analyzer.js? | ✅ Yes | ❌ No |
| Need typescript/? | ✅ Yes | ❌ No |
| Files per rule | 4+ files | **2 files** |
| Languages | ["typescript", "dart", ...] | ["dart"] only |

### 2.2. Flat Structure for Dxxx

```
rules/dart/                              # Dart-only rules directory
├── D001_flutter_widget_naming/
│   └── config.json                      # Only need 1 config file
├── D002_stream_subscription_cancel/
│   └── config.json
└── D003_dispose_controller/
    └── config.json

dart_analyzer/lib/rules/dart/            # Dart implementation
├── D001_flutter_widget_naming.dart
├── D002_stream_subscription_cancel.dart
└── D003_dispose_controller.dart

examples/rule-test-fixtures/dart-rules/  # Test fixtures
├── D001_flutter_widget_naming/
│   ├── clean/
│   │   └── good_widget_names.dart
│   └── violations/
│       └── bad_widget_names.dart
```

---

## 3. Input Parameters

| Parameter | Required | Description | Example |
|-----------|----------|-------------|---------|
| `rule_id` | Yes | Rule ID (Dxxx format) | `D001`, `D002` |
| `rule_name` | Yes | Rule name (snake_case) | `flutter_widget_naming` |
| `description` | Yes | Short description | `Widget names should end with...` |
| `severity` | No | Severity level (default: warning) | `error`, `warning`, `info` |

---

## 4. Execution Steps

### Step 1: Create Rule Config Folder

```bash
RULE_ID="D001"
RULE_NAME="flutter_widget_naming"

# Create directory
mkdir -p "rules/dart/${RULE_ID}_${RULE_NAME}"
```

### Step 2: Create config.json

**File:** `rules/dart/${RULE_ID}_${RULE_NAME}/config.json`

```json
{
  "id": "${RULE_ID}",
  "name": "${RULE_DISPLAY_NAME}",
  "description": "${RULE_DESCRIPTION}",
  "category": "dart",
  "severity": "warning",
  "languages": ["dart"],
  "tags": ["flutter", "naming", "widget"],
  "config": {
    // Rule-specific configuration
  }
}
```

**Specific Example:**

```json
{
  "id": "D001",
  "name": "Flutter Widget Naming",
  "description": "Widget class names should end with Widget, Page, Screen, or View",
  "category": "dart",
  "severity": "warning",
  "languages": ["dart"],
  "tags": ["flutter", "naming", "widget"],
  "config": {
    "validSuffixes": ["Widget", "Page", "Screen", "View", "Dialog", "Sheet"],
    "excludePatterns": ["_State", "Mixin"]
  }
}
```

### Step 3: Create Dart Analyzer Implementation

**File:** `dart_analyzer/lib/rules/dart/${RULE_ID}_${RULE_NAME}.dart`

```dart
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/source/line_info.dart';

import '../../models/rule.dart';
import '../../models/violation.dart';
import '../base_analyzer.dart';

/// ${RULE_ID}: ${RULE_DISPLAY_NAME}
/// ${RULE_DESCRIPTION}
class ${RULE_ID}${RULE_PASCAL_NAME}Analyzer extends BaseAnalyzer {
  @override
  String get ruleId => '${RULE_ID}';

  // Rule-specific configuration
  static const _validSuffixes = ['Widget', 'Page', 'Screen', 'View', 'Dialog', 'Sheet'];
  static const _excludePatterns = ['_State', 'Mixin'];

  @override
  List<Violation> analyze({
    required CompilationUnit unit,
    required String filePath,
    required Rule rule,
    required LineInfo lineInfo,
  }) {
    final violations = <Violation>[];

    final visitor = _${RULE_ID}Visitor(
      filePath: filePath,
      lineInfo: lineInfo,
      violations: violations,
      analyzer: this,
    );

    unit.accept(visitor);

    return violations;
  }
}

class _${RULE_ID}Visitor extends RecursiveAstVisitor<void> {
  final String filePath;
  final LineInfo lineInfo;
  final List<Violation> violations;
  final ${RULE_ID}${RULE_PASCAL_NAME}Analyzer analyzer;

  _${RULE_ID}Visitor({
    required this.filePath,
    required this.lineInfo,
    required this.violations,
    required this.analyzer,
  });

  @override
  void visitClassDeclaration(ClassDeclaration node) {
    final className = node.name.lexeme;

    // Check if class extends StatelessWidget or StatefulWidget
    final extendsClause = node.extendsClause;
    if (extendsClause != null) {
      final superclass = extendsClause.superclass.name2.lexeme;
      if (superclass == 'StatelessWidget' || superclass == 'StatefulWidget') {
        // Check if name ends with valid suffix
        bool hasValidSuffix = ${RULE_ID}${RULE_PASCAL_NAME}Analyzer._validSuffixes
            .any((suffix) => className.endsWith(suffix));

        // Skip excluded patterns
        bool isExcluded = ${RULE_ID}${RULE_PASCAL_NAME}Analyzer._excludePatterns
            .any((pattern) => className.contains(pattern));

        if (!hasValidSuffix && !isExcluded) {
          violations.add(analyzer.createViolation(
            filePath: filePath,
            line: analyzer.getLine(lineInfo, node.name.offset),
            column: analyzer.getColumn(lineInfo, node.name.offset),
            message: 'Widget class "$className" should end with Widget, Page, Screen, View, Dialog, or Sheet',
          ));
        }
      }
    }

    super.visitClassDeclaration(node);
  }
}
```

### Step 4: Register in AnalyzerService

**File:** `dart_analyzer/lib/analyzer_service.dart`

```dart
// Add import at top
import 'rules/dart/${RULE_ID}_${RULE_NAME}.dart';

// Add to _registerAnalyzers() method
void _registerAnalyzers() {
  // ... existing analyzers

  // Dart-only rules (Dxxx)
  _analyzers['${RULE_ID}'] = ${RULE_ID}${RULE_PASCAL_NAME}Analyzer();
}
```

### Step 5: Update Rule Definition Markdown Files

**IMPORTANT:** Rule name and description are taken from these markdown files, not from `config.json`.

#### 5.1. Update `dart-en.md` (English version)

**File:** `../../../rules/dart-en.md`

Add new rule definition to file:

```markdown
### 📘 Rule ${RULE_ID} – ${RULE_DISPLAY_NAME}

- **Objective**: ${OBJECTIVE_SHORT}
- **Details**: ${DETAILED_DESCRIPTION}
- **Applies to**: Flutter/Dart
- **Tools**: `dart lint` (${TOOLS})
- **Principles**: CODE_QUALITY
- **Version**: 1.0
- **Status**: activated
- **Severity**: major
```

**Example:**

```markdown
### 📘 Rule D001 – Recommended Lint Rules Should Be Enabled

- **Objective**: Ensure code quality through standard lint configurations
- **Details**: The `analysis_options.yaml` file should include recommended lint packages (flutter_lints, very_good_analysis, or lints) and critical lint rules should not be disabled. This ensures consistent code quality standards across the project.
- **Applies to**: Flutter/Dart
- **Tools**: `dart lint` (flutter_lints, very_good_analysis, lints)
- **Principles**: CODE_QUALITY
- **Version**: 1.0
- **Status**: activated
- **Severity**: major
```

#### 5.2. Update `dart.md` (Vietnamese version)

**File:** `../../../rules/dart.md`

Add rule definition in Vietnamese:

```markdown
### 📘 Rule ${RULE_ID} – ${RULE_DISPLAY_NAME_VI}

- **Mục tiêu**: ${OBJECTIVE_SHORT_VI}
- **Chi tiết**: ${DETAILED_DESCRIPTION_VI}
- **Áp dụng**: Flutter/Dart
- **Công cụ**: `dart lint` (${TOOLS})
- **Nguyên tắc**: CODE_QUALITY
- **Phiên bản**: 1.0
- **Trạng thái**: activated
- **Mức độ**: major
```

#### 5.3. Create Example Files

**File:** `../../../rules/examples/en/${RULE_ID}.md`

```markdown
**Good Examples**:
\`\`\`dart
// Your good example code here
\`\`\`

**Bad Examples**:
\`\`\`dart
// Your bad example code here
\`\`\`
```

**File:** `../../../rules/examples/vi/${RULE_ID}.md`

```markdown
**Ví dụ đúng**:
\`\`\`dart
// Good code example
\`\`\`

**Ví dụ sai**:
\`\`\`dart
// Bad code example
\`\`\`
```

### Step 6: Generate Registry from Markdown

**IMPORTANT:** Registry is auto-generated from markdown files, DO NOT edit manually.

```bash
# Copy rules from main rules directory to sunlint
node scripts/copy-rules.js

# Generate registry from markdown files
node scripts/generate-rules-registry.js

# Results:
# - config/rules/rules-registry-generated.json (auto-generated)
# - Rule name and description will be taken from dart-en.md
```

**Notes:**
- File `rules-registry-generated.json` is auto-generated, DO NOT edit manually
- File `rules-summary.json` may need manual update if not generated
- Registry will parse rule from markdown format `### 📘 Rule ${RULE_ID} – ${NAME}`

**📖 Read more:** See [REGISTRY_GENERATION_FLOW.md](../REGISTRY_GENERATION_FLOW.md) for detailed understanding of the generation flow, common mistakes, and debugging tips.

### Step 7: Create Test Fixtures

```bash
# Create test fixture folders
mkdir -p "examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_NAME}/{clean,violations}"
```

**violations/bad_example.dart:**
```dart
// ❌ VIOLATION: D001 - Flutter Widget Naming
// Widget class should end with Widget, Page, Screen, or View

class MyComponent extends StatelessWidget {  // ❌ Should be MyComponentWidget
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class UserProfile extends StatefulWidget {  // ❌ Should be UserProfilePage/Screen
  @override
  State<UserProfile> createState() => _UserProfileState();
}
```

**clean/good_example.dart:**
```dart
// ✅ CLEAN: D001 - Flutter Widget Naming
// Correct widget naming convention

class MyComponentWidget extends StatelessWidget {  // ✅ Ends with Widget
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class UserProfilePage extends StatefulWidget {  // ✅ Ends with Page
  @override
  State<UserProfilePage> createState() => _UserProfilePageState();
}

class SettingsScreen extends StatelessWidget {  // ✅ Ends with Screen
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}
```

### Step 8: Rebuild Dart Analyzer

```bash
cd dart_analyzer

# Get dependencies
dart pub get

# Analyze for errors
dart analyze lib/

# Compile binary (macOS)
dart compile exe bin/sunlint_dart_analyzer.dart -o bin/sunlint-dart-macos

# Copy binary to sunlint root (important!)
cp bin/sunlint-dart-macos ../sunlint-dart-macos

# Verify binary
ls -la ../sunlint-dart-macos

# Return to sunlint root
cd ..
```

**Note:** Binary must be copied to sunlint root directory for CLI to use.

**Platform-specific binaries:**
- macOS: `sunlint-dart-macos`
- Linux: `sunlint-dart-linux`
- Windows: `sunlint-dart-windows.exe`

### Step 9: Test the Rule

```bash
cd /path/to/sunlint

# Test on violations (should find issues)
node cli.js --rule=${RULE_ID} \
  --input="examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_NAME}/violations" \
  --languages=dart

# Test on clean code (should find no issues)
node cli.js --rule=${RULE_ID} \
  --input="examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_NAME}/clean" \
  --languages=dart

# With verbose output for debugging
node cli.js --rule=${RULE_ID} \
  --input="examples/rule-test-fixtures/dart-rules/${RULE_ID}_${RULE_NAME}/violations" \
  --languages=dart \
  --verbose
```

### Step 10: Run Rule on Real Project

```bash
# Run on a Dart/Flutter project
node cli.js --rule=${RULE_ID} --input=/path/to/dart/project --languages=dart

# Run with verbose output
node cli.js --rule=${RULE_ID} --input=/path/to/dart/project --languages=dart --verbose

# If installed globally
sunlint --rule=${RULE_ID} --input=/path/to/dart/project --languages=dart
```

---

## 5. Verification Checklist

| Check | Command | Expected |
|-------|---------|----------|
| Config exists | `ls rules/dart/${RULE_ID}_*/config.json` | File exists |
| Dart analyzer exists | `ls dart_analyzer/lib/rules/dart/${RULE_ID}_*.dart` | File exists |
| Registered | `grep ${RULE_ID} dart_analyzer/lib/analyzer_service.dart` | Registration line |
| Binary compiles | `dart analyze dart_analyzer/lib/` | No errors |
| Test fixtures exist | `ls examples/rule-test-fixtures/dart-rules/${RULE_ID}_*` | clean/ and violations/ |
| **Violations detected** | `node cli.js --rule=${RULE_ID} --input=...violations --languages=dart` | > 0 violations |
| **Clean passes** | `node cli.js --rule=${RULE_ID} --input=...clean --languages=dart` | No violations |

---

## 6. Naming Conventions

| Type | Convention | Example |
|------|------------|---------|
| Rule ID | Dxxx | `D001`, `D002` |
| Folder name | `{ID}_{snake_case}` | `D001_flutter_widget_naming` |
| Dart file | `{ID}_{snake_case}.dart` | `D001_flutter_widget_naming.dart` |
| Class name | `{ID}{PascalCase}Analyzer` | `D001FlutterWidgetNamingAnalyzer` |
| Visitor class | `_{ID}Visitor` | `_D001Visitor` |

---

## 7. Common Patterns for Flutter/Dart Rules

### Pattern 1: Check Widget Class Names

```dart
@override
void visitClassDeclaration(ClassDeclaration node) {
  final className = node.name.lexeme;
  final extendsClause = node.extendsClause;

  if (extendsClause != null) {
    final superclass = extendsClause.superclass.name2.lexeme;
    if (superclass.contains('Widget')) {
      // Check widget naming
    }
  }
  super.visitClassDeclaration(node);
}
```

### Pattern 2: Check StreamSubscription Cancel

```dart
@override
void visitMethodDeclaration(MethodDeclaration node) {
  if (node.name.lexeme == 'dispose') {
    final body = node.body?.toSource() ?? '';
    // Check if subscriptions are cancelled
    if (!body.contains('.cancel()')) {
      // Add violation
    }
  }
  super.visitMethodDeclaration(node);
}
```

### Pattern 3: Check Controller Dispose

```dart
@override
void visitFieldDeclaration(FieldDeclaration node) {
  for (final variable in node.fields.variables) {
    final type = node.fields.type?.toSource() ?? '';
    if (type.contains('Controller')) {
      // Track controller fields for dispose check
    }
  }
  super.visitFieldDeclaration(node);
}
```

### Pattern 4: Check BuildContext Usage

```dart
@override
void visitMethodInvocation(MethodInvocation node) {
  final methodName = node.methodName.name;
  if (methodName == 'of') {
    // Check for context usage after async gap
  }
  super.visitMethodInvocation(node);
}
```

### Pattern 5: Check Import Statements

```dart
@override
void visitImportDirective(ImportDirective node) {
  final uri = node.uri.stringValue ?? '';
  if (uri.contains('dart:mirrors')) {
    // Flag forbidden imports
  }
  super.visitImportDirective(node);
}
```

### Pattern 6: Follow Include Chains in YAML Files

When analyzing configuration files like `analysis_options.yaml`, you may need to follow `include:` chains to find the actual configuration:

```dart
bool _checkForRecommendedPackage(
  YamlMap yaml, 
  String content, 
  String projectRoot,
  String currentFilePath,
) {
  // Prevent infinite loops in circular includes
  if (_visitedFiles.contains(currentFilePath)) {
    return false;
  }
  _visitedFiles.add(currentFilePath);

  // Check direct include statement
  final include = yaml['include'];
  if (include != null && include is String) {
    // Check if it's a package include
    if (include.startsWith('package:')) {
      // Check package name
      return true;
    }
    
    // Follow local file includes recursively
    if (!include.startsWith('package:')) {
      final includedFile = _resolveIncludePath(include, projectRoot, currentFilePath);
      if (includedFile != null && includedFile.existsSync()) {
        try {
          final includedContent = includedFile.readAsStringSync();
          final includedYaml = loadYaml(includedContent);
          
          if (includedYaml is YamlMap) {
            // Recursively check the included file
            return _checkForRecommendedPackage(
              includedYaml,
              includedContent,
              projectRoot,
              includedFile.path,
            );
          }
        } catch (e) {
          // Failed to parse included file
        }
      }
    }
  }

  return false;
}

File? _resolveIncludePath(String includePath, String projectRoot, String currentFilePath) {
  if (path.isAbsolute(includePath)) {
    return File(includePath);
  }
  
  // Relative to current file's directory
  final currentDir = path.dirname(currentFilePath);
  final resolvedPath = path.normalize(path.join(currentDir, includePath));
  
  return File(resolvedPath);
}
```

**Example use case:** D001 rule follows include chains like:
```yaml
# analysis_options.yaml
include: base_options.yaml

# base_options.yaml
include: company_standards.yaml

# company_standards.yaml
include: package:flutter_lints/flutter.yaml
```

The rule detects `flutter_lints` even through a 3-level include chain.

---

## 8. Generated File Handling

### 8.1. Automatic Skip for Generated Files

**All Dart rules automatically skip generated files.** The analyzer service has a centralized check in `analyzer_service.dart` that filters out generated files before analysis.

**Skipped patterns:**
- `.g.dart` - json_serializable, built_value
- `.gen.dart` - flutter_gen (assets, localization)  
- `.gql.dart`, `.graphql.dart` - GraphQL code generation
- `.freezed.dart` - freezed unions/sealed classes
- `.gr.dart` - gRPC code generation
- `.pb.dart`, `.pbenum.dart`, `.pbserver.dart`, `.pbjson.dart` - Protocol buffers
- `.mocks.dart` - Mockito generated mocks
- `.config.dart`, `.iconfig.dart` - Injectable configs
- `.mapper.dart` - Mapper generators (dart_mappable)
- `.drift.dart` - Drift database
- `.chopper.dart` - Chopper HTTP client
- `.reflectable.dart` - Reflectable code generation
- `pigeon.dart` - Pigeon platform channels
- `l10n.dart`, `_l10n.dart` - Flutter intl localization
- Files in `__generated__` or `/generated/` directories
- Files with `.generated.` in their name

### 8.2. When to Add File Checks in Rules

**You should NOT add generated file checks in individual rule implementations** because:
- ✅ The check is already done centrally in `analyzer_service.dart`
- ✅ Consistent across all rules automatically
- ✅ Easy to maintain and update patterns in one place

**Exception:** Only add file path checks for rule-specific logic, such as:
- Skipping test files for certain rules (e.g., `C043_no_console_or_print.dart`)
- Skipping config files for hardcoded value rules (e.g., `C067_no_hardcoded_config.dart`)
- Special behavior based on file location (e.g., test vs. production code)

**Example of rule-specific file check (when needed):**

```dart
@override
List<Violation> analyze({
  required CompilationUnit unit,
  required String filePath,
  required Rule rule,
  required LineInfo lineInfo,
}) {
  // Rule-specific file path check (not for generated files)
  if (filePath.contains('_test.dart')) {
    // Allow console.log/print in test files
    return [];
  }
  
  // Regular analysis continues...
}
```

---

## 9. Existing and Suggested Dart-Only Rules (Dxxx)

### Existing Rules

| ID | Name | Description | Status |
|----|------|-------------|--------|
| D001 | Recommended Lint Rules | Ensure flutter_lints/very_good_analysis enabled in analysis_options.yaml | ✅ Implemented |

### Suggested Future Rules

| ID | Name | Description |
|----|------|-------------|
| D002 | Stream Subscription Cancel | StreamSubscription must be cancelled in dispose() |
| D003 | Dispose Controllers | TextEditingController, AnimationController must be disposed |
| D004 | BuildContext After Async | Don't use BuildContext after async gaps |
| D005 | Avoid Print in Flutter | Use debugPrint() instead of print() |
| D006 | Prefer Const Constructors | Use const constructors when possible |
| D007 | Avoid setState in Build | Don't call setState() inside build() method |
| D008 | Import Order | Dart imports should be sorted and grouped |
| D009 | No Relative Imports | Use package imports instead of relative imports |
| D010 | Prefer Final Fields | Class fields should be final when possible |
| D011 | Flutter Widget Naming | Widget classes should end with Widget/Page/Screen/View |

---

## 10. Error Handling

### 10.1. Dart Compile Error

```bash
# Check for errors
dart analyze dart_analyzer/lib/

# Common fixes:
# - Import missing files
# - Fix syntax errors
# - Add missing dependencies to pubspec.yaml
```

### 10.2. Rule Not Detected

```bash
# Verify config exists
ls rules/dart/${RULE_ID}_*/config.json

# Check registry
grep ${RULE_ID} config/rules/enhanced-rules-registry.json
```

### 10.3. No Violations Found

```bash
# Check DartAnalyzer is running
node cli.js --rule=${RULE_ID} --input=... --languages=dart --verbose

# Check analyzer is registered
grep ${RULE_ID} dart_analyzer/lib/analyzer_service.dart
```

### 10.4. Rule Name Mismatch

**Problem:** Rule name displays incorrectly or doesn't match the definition.

**Root Cause:** Rule name is taken from markdown files (`dart-en.md`), NOT from `config.json` or Dart code.

**Solution:**
1. Check rule definition in `../../../rules/dart-en.md`
2. Ensure correct format: `### 📘 Rule ${RULE_ID} – ${NAME}`
3. Regenerate registry:
   ```bash
   node scripts/copy-rules.js
   node scripts/generate-rules-registry.js
   ```
4. Verify in generated file:
   ```bash
   grep -A 5 '"${RULE_ID}"' config/rules/rules-registry-generated.json
   ```

**IMPORTANT:**
- Markdown files are the **source of truth** for rule name and description
- Registry files are **auto-generated** from markdown
- DO NOT edit registry files manually, must update markdown and regenerate

---

## 11. Files Created/Modified by This Skill

### Files Created:

| File | Purpose |
|------|---------|
| `rules/dart/${RULE_ID}_*/config.json` | Rule configuration |
| `dart_analyzer/lib/rules/dart/${RULE_ID}_*.dart` | Dart analyzer implementation |
| `examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/clean/*.dart` | Clean test cases |
| `examples/rule-test-fixtures/dart-rules/${RULE_ID}_*/violations/*.dart` | Violation test cases |
| `../../../rules/examples/en/${RULE_ID}.md` | English examples |
| `../../../rules/examples/vi/${RULE_ID}.md` | Vietnamese examples |

### Files Modified:

| File | What to Add |
|------|-------------|
| `dart_analyzer/lib/analyzer_service.dart` | Import rule + Register in `_registerAnalyzers()` |
| `../../../rules/dart-en.md` | Rule definition section (English) |
| `../../../rules/dart.md` | Rule definition section (Vietnamese) |

### Files Auto-Generated:

| File | Generated By |
|------|--------------|
| `config/rules/rules-registry-generated.json` | `scripts/generate-rules-registry.js` |
| `origin-rules/dart-en.md` | `scripts/copy-rules.js` (copied from `../../../rules/`) |

---

## 12. Best Practices & Important Notes

### 12.1. Rule Name Synchronization

**CRITICAL:** Rule name must be consistent across 3 locations:

| Location | Format | Example |
|----------|--------|---------|
| **Dart Code Comment** | `/// ${RULE_ID}: ${NAME}` | `/// D001: Recommended Lint Rules Should Be Enabled` |
| **Markdown (dart-en.md)** | `### 📘 Rule ${RULE_ID} – ${NAME}` | `### 📘 Rule D001 – Recommended Lint Rules Should Be Enabled` |
| **Registry (auto-generated)** | Generated from markdown | `"name": "Recommended Lint Rules Should Be Enabled"` |

**Workflow:**
1. Update markdown files first (`dart-en.md`, `dart.md`)
2. Run `node scripts/copy-rules.js && node scripts/generate-rules-registry.js`
3. Update Dart code comment to match
4. Never edit registry JSON files manually

### 12.2. Registry Generation Flow

```
../../../rules/dart-en.md
         ↓ (copy-rules.js)
origin-rules/dart-en.md
         ↓ (generate-rules-registry.js)
config/rules/rules-registry-generated.json
```

**Key Points:**
- Markdown is the **source of truth**
- Registry is **auto-generated**
- `config.json` is only used for rule detection, not for display name

### 12.3. Common Mistakes to Avoid

| Mistake | Why It's Wrong | Correct Approach |
|---------|----------------|------------------|
| Edit `rules-registry-generated.json` manually | Will be overwritten | Edit markdown and regenerate |
| Put display name in `config.json` | Not used for display | Put in markdown files |
| Skip registry regeneration | Old name will show | Always run `generate-rules-registry.js` |
| Mismatch Dart comment and markdown | Confusing for developers | Keep them synchronized |

---

## 13. Related Documentation

- **[REGISTRY_GENERATION_FLOW.md](../REGISTRY_GENERATION_FLOW.md)** - ⭐ Detailed registry generation flow from markdown
- [CREATE_DART_RULE.md](./CREATE_DART_RULE.md) - Add Dart support for existing TypeScript rule
- [DART_RULE_EXECUTION_FLOW.md](../DART_RULE_EXECUTION_FLOW.md) - Detailed rule execution flow
- [dart_analyzer/README.md](../../dart_analyzer/README.md) - Dart analyzer documentation

---

## 14. Complete Example: D001 Implementation

Below is a real-world example of rule D001 implementation:

### Files Created:

```
rules/dart/D001_recommended_lint_rules/
└── config.json

dart_analyzer/lib/rules/dart/
└── D001_recommended_lint_rules.dart

examples/rule-test-fixtures/dart-rules/D001_recommended_lint_rules/
├── clean/
│   ├── analysis_options.yaml
│   ├── pubspec.yaml
│   └── lib/main.dart
└── violations/
    ├── analysis_options.yaml
    ├── pubspec.yaml
    └── lib/main.dart
```

### Test Commands:

```bash
# Test violations (expect 5 warnings)
node cli.js --rule=D001 \
  --input=examples/rule-test-fixtures/dart-rules/D001_recommended_lint_rules/violations \
  --languages=dart

# Test clean (expect 0 warnings)
node cli.js --rule=D001 \
  --input=examples/rule-test-fixtures/dart-rules/D001_recommended_lint_rules/clean \
  --languages=dart

# Run on any Dart project
node cli.js --rule=D001 --input=/path/to/dart/project --languages=dart
```

### Expected Output (violations):

```
analysis_options.yaml
 1:1 warning  analysis_options.yaml should include a recommended lint package...  D001
 13:1 warning  Critical lint rule "avoid_print" should not be disabled...  D001
 14:1 warning  Critical lint rule "cancel_subscriptions" should not be disabled...  D001
 15:1 warning  Critical lint rule "close_sinks" should not be disabled...  D001
 16:1 warning  Critical lint rule "avoid_empty_else" should not be disabled...  D001

✖ 5 problems (0 errors, 5 warnings)
```

---

## 15. Changelog

### v1.2.0 (2025-02-02)
- **FEATURE:** Enhanced generated file detection patterns
- Added comprehensive list of generated file patterns (20+ patterns)
- Added new section "Generated File Handling" with complete documentation
- Documented automatic skip behavior for all Dart rules
- Added guidance on when NOT to add file checks in rules
- Updated patterns include: .gen.dart, .pb.dart, .config.dart, .mapper.dart, .drift.dart, etc.
- Clarified centralized vs. rule-specific file path checks

### v1.1.0 (2025-01-23)
- **BREAKING:** Added mandatory markdown update steps for rule definitions
- Added registry generation flow documentation
- Added Best Practices section on rule name synchronization
- Added troubleshooting for rule name mismatch issues
- Clarified that markdown files are source of truth for rule names
- Updated file locations table with markdown paths
- Added example files creation steps

### v1.0.0 (2025-01-21)
- Initial skill documentation for creating Dart-only rules (Dxxx)
- Flat structure design - no router or JS wrapper needed
- Separate `rules/dart/` folder for Dart-specific rules
- Common Flutter/Dart patterns included
- D001 (Recommended Lint Rules) implemented as reference example
