add-dart-lint-validation-rule

Instructions pour ajouter une nouvelle règle de validation et un flag CLI à dart_skills_lint.

npx skills add https://github.com/flutter/skills --skill add-dart-lint-validation-rule

Ajouter une Nouvelle Règle de Validation et un Flag

Utilisez cette skill quand vous devez ajouter une nouvelle règle de validation au package dart_skills_lint, l'exposer comme un flag CLI basculable, et vérifier son comportement.


🛠️ Implémentation Étape par Étape

1. Créer la Classe de Règle

Créez un nouveau fichier dans lib/src/rules/ étendant SkillRule.

[!TIP] Si votre règle attend une structure spécifique dans le frontmatter YAML de la skill (par exemple, dans metadata), documentez clairement cette structure dans la docstring Dart de la classe.

// lib/src/rules/my_new_rule.dart

import '../models/analysis_severity.dart';
import '../models/skill_context.dart';
import '../models/skill_rule.dart';
import '../models/validation_error.dart';

class MyNewRule extends SkillRule {
  MyNewRule({super.severity});

  @override
  Future<List<ValidationError>> validate(SkillContext context) async {
    final errors = <ValidationError>[];
    // Add validation logic here using context.rawContent or context.directory
    return errors;
  }
}

Accéder au Frontmatter YAML

Si votre règle a besoin de configuration depuis le frontmatter YAML de la skill, vous pouvez y accéder via context.parsedYaml.

  @override
  Future<List<ValidationError>> validate(SkillContext context) async {
    final errors = <ValidationError>[];
    final yaml = context.parsedYaml;
    if (yaml != null) {
      final metadata = yaml['metadata'];
      if (metadata is Map) {
        // Read your custom config here
      }
    }
    return errors;
  }

2. Enregistrer la Règle dans lib/src/rule_registry.dart

Ajoutez une nouvelle instance CheckType à la liste RuleRegistry.allChecks. Cela expose automatiquement le flag CLI.

// lib/src/rule_registry.dart in allChecks list

  const CheckType(
    name: MyNewRule.ruleName,
    defaultSeverity: MyNewRule.defaultSeverity,
    help: 'Description of what the rule does for CLI help.',
  ),

Ensuite, ajoutez un cas à RuleRegistry.createRule pour instancier votre règle :

// lib/src/rule_registry.dart in createRule method

  static SkillRule? createRule(String name, AnalysisSeverity severity) {
    switch (name) {
      // ... other rules
      case MyNewRule.ruleName:
        return MyNewRule(severity: severity);
      default:
        return null;
    }
  }

3. Gérer les Règles Désactivées par Défaut (Si applicable)

Si la règle est désactivée par défaut (defaultSeverity: AnalysisSeverity.disabled), passer le flag --check-my-new-rule l'activera automatiquement avec la sévérité AnalysisSeverity.error (géré dans entry_point.dart).


🧪 Tester la Nouvelle Règle

Vous devez écrire des tests automatisés vérifiant que votre règle se déclenche quand elle doit et s'ignore quand elle ne doit pas.

Approche Privilégiée : Tests Unitaires en Mémoire

Au lieu d'écrire des fichiers sur le disque, testez la règle directement en utilisant un SkillContext mock. C'est plus rapide et évite les dépendances I/O.

// test/my_new_rule_test.dart

import 'dart:io';
import 'package:dart_skills_lint/src/models/analysis_severity.dart';
import 'package:dart_skills_lint/src/models/skill_context.dart';
import 'package:dart_skills_lint/src/models/validation_error.dart';
import 'package:dart_skills_lint/src/rules/my_new_rule.dart';
import 'package:test/test.dart';

void main() {
  group('MyNewRule', () {
    test('flags invalid content', () async {
      final rule = MyNewRule(severity: AnalysisSeverity.warning);
      final context = SkillContext(
        directory: Directory('dummy'),
        rawContent: 'Invalid content',
      );

      final List<ValidationError> errors = await rule.validate(context);

      expect(errors, isNotEmpty);
      expect(errors.first.message, contains('Expected error message'));
    });

    test('passes valid content', () async {
      final rule = MyNewRule(severity: AnalysisSeverity.warning);
      final context = SkillContext(
        directory: Directory('dummy'),
        rawContent: 'Valid content',
      );

      final List<ValidationError> errors = await rule.validate(context);

      expect(errors, isEmpty);
    });
  });
}

Approche Alternative : Interaction avec le Système de Fichiers

Si la règle interagit avec le système de fichiers ou enveloppe un outil CLI externe (comme popmark), vous devriez utiliser un répertoire temporaire pour les tests au lieu de mocks en mémoire.

    late Directory tempDir;

    setUp(() async {
      tempDir = await Directory.systemTemp.createTemp('my_rule_test.');
    });

    tearDown(() async {
      if (tempDir.existsSync()) {
        await tempDir.delete(recursive: true);
      }
    });

    test('flags invalid file content', () async {
      final Directory skillDir = await Directory('${tempDir.path}/test-skill').create();
      await File('${skillDir.path}/SKILL.md').writeAsString('Invalid content');

      final rule = MyNewRule(severity: AnalysisSeverity.warning);
      final context = SkillContext(directory: skillDir, rawContent: 'Invalid content');

      final List<ValidationError> errors = await rule.validate(context);

      expect(errors, isNotEmpty);
    });

Tests d'Intégration

Si la règle interagit avec des flags CLI ou des fichiers de configuration, ajoutez un test dans test/cli_integration_test.dart en utilisant TestProcess.

[!IMPORTANT] Lors de l'écriture de tests d'intégration utilisant des fichiers de configuration et TestProcess, assurez-vous que les chemins dans le fichier de configuration et les chemins passés au CLI correspondent en style (tous relatifs ou tous absolus) pour éviter les problèmes de correspondance de chemins dans entry_point.dart.


📚 Mises à Jour de la Documentation

Quand une nouvelle règle est introduite, vérifiez que vous synchronisez les fichiers markdown frères !

  1. README.md:
    • Ajoutez votre flag dans les sections Usage et Flags pour que les utilisateurs sachent qu'il existe.
  2. documentation/knowledge/SPECIFICATION.md:
    • Documentez la contrainte formelle dans la spécification si elle définit un standard pour les fichiers de skill.

🚦 Checklist Avant de Soumettre une PR

  • [ ] Classe de règle créée dans lib/src/rules/.
  • [ ] Règle enregistrée dans lib/src/rule_registry.dart.
  • [ ] Tests unitaires ajoutés dans test/ utilisant SkillContext en mémoire.
  • [ ] Usage listé dans README.md.
  • [ ] Schéma documenté dans documentation/knowledge/SPECIFICATION.md (si applicable).
  • [ ] Exécuter dart format . pour formater le code.
  • [ ] Exécuter dart analyze --fatal-infos pour s'assurer qu'il n'y a pas de problèmes.
  • [ ] Exécuter dart test pour s'assurer que les tests passent.

Skills similaires