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 dansentry_point.dart.
📚 Mises à Jour de la Documentation
Quand une nouvelle règle est introduite, vérifiez que vous synchronisez les fichiers markdown frères !
README.md:- Ajoutez votre flag dans les sections Usage et Flags pour que les utilisateurs sachent qu'il existe.
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/utilisantSkillContexten 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-infospour s'assurer qu'il n'y a pas de problèmes. - [ ] Exécuter
dart testpour s'assurer que les tests passent.