Bonnes pratiques
Conseils et lignes directrices pour créer d'excellents plugins Volt
Bonnes pratiques pour les plugins
Créez des plugins de haute qualité
Suivez ces bonnes pratiques pour créer des plugins Volt performants, fiables et agréables à utiliser.
Performance
Implémentez un canHandle() efficace
La méthode canHandle() est appelée à chaque requête. Gardez-la rapide :
canHandle(context: PluginContext): boolean {
return /^(search|find|lookup)\s+.+$/i.test(context.query);
}canHandle(context: PluginContext): boolean {
return context.query.startsWith('search ');
}Utilisez le debouncing
Pour les plugins qui font des appels API, debounce les requêtes pour ne pas saturer l'API :
import { debounce } from "lodash";
class MyPlugin implements Plugin {
private debouncedMatch = debounce(this.match.bind(this), 300);
async match(context: PluginContext): Promise<PluginResult[]> {
// Votre implémentation
return await this.fetchResults(context.query);
}
}300 ms est un bon équilibre entre réactivité et efficacité API.
Implémentez du cache
Mettez les résultats en cache pour améliorer les performances et réduire les appels API :
class MyPlugin implements Plugin {
private cache = new Map<string, PluginResult[]>();
private cacheTimeout = 5000; // 5 secondes
async match(context: PluginContext): Promise<PluginResult[]> {
const cached = this.cache.get(context.query);
if (cached) return cached;
const results = await this.fetchResults(context.query);
this.cache.set(context.query, results);
setTimeout(() => this.cache.delete(context.query), this.cacheTimeout);
return results;
}
}Respectez les timeouts
Les plugins frontend ont un timeout de 500 ms. Assurez-vous que votre plugin répond vite :
async match(context: PluginContext): Promise<PluginResult[]> {
const timeout = new Promise<PluginResult[]>((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 450)
);
const results = this.fetchResults(context.query);
return Promise.race([results, timeout]).catch(() => []);
}Gérez toujours les erreurs de timeout proprement pour éviter les crashs.
Gestion des erreurs
Expérience utilisateur
Fournissez titres et sous-titres clairs
Rendez les résultats compréhensibles d'un coup d'œil :
{
title: 'Résultat',
subtitle: 'Cliquez pour ouvrir',
}{
title: 'Chercher « React hooks » sur Google',
subtitle: 'Ouvre google.com dans votre navigateur',
}Utilisez des icônes adaptées
Choisissez des icônes qui représentent clairement vos résultats :
// Utilisez des emojis pour la simplicité
icon: '🔍' // Pour la recherche
icon: '📊' // Pour données/analytics
icon: '⚙️' // Pour les paramètres
icon: '📁' // Pour les fichiers
icon: '🚀' // Pour lancer/exécuter
// Ou des URL d'icônes personnalisées
icon: 'https://example.com/icon.png'Les emojis sont parfaits pour le prototypage rapide. Utilisez des icônes custom pour les plugins pros.
Fournissez des raccourcis clavier
Ajoutez des raccourcis pour les actions courantes :
{
id: '1',
title: 'Chercher sur Google',
subtitle: 'Appuyez sur Entrée pour ouvrir',
shortcut: 'Enter',
action: () => window.open(url),
}Affichez les états de chargement
Indiquez le chargement pour donner du retour à l'utilisateur :
async match(context: PluginContext): Promise<PluginResult[]> {
// Retourner immédiatement un état de chargement
const loadingResult = {
id: 'loading',
title: 'Chargement…',
icon: '⏳',
type: PluginResultType.Info,
};
// Récupérer les vrais résultats
const results = await this.fetchResults(context.query);
return results.length > 0 ? results : [loadingResult];
}Sécurité
La sécurité est critique
Suivez toujours les bonnes pratiques de sécurité pour protéger les utilisateurs et leurs données.
Qualité du code
Utilisez TypeScript
Utilisez toujours TypeScript pour la sécurité des types et un meilleur support IDE :
interface MyPluginConfig {
apiKey: string;
endpoint: string;
timeout: number;
}
class MyPlugin implements Plugin {
constructor(private config: MyPluginConfig) {}
}TypeScript aide à détecter les erreurs tôt et fournit une meilleure autocomplétion.
Suivez les conventions de nommage
Utilisez un nommage cohérent dans tout votre plugin :
// ID de plugin : kebab-case
id = 'web-search';
// Noms de classe : PascalCase
class WebSearchPlugin implements Plugin {}
// Méthodes : camelCase
async match(context: PluginContext) {}
// Constantes : UPPER_SNAKE_CASE
const DEFAULT_TIMEOUT = 5000;Documentez votre code
Ajoutez des commentaires JSDoc pour une meilleure maintenabilité :
/**
* Recherche sur le web via plusieurs moteurs
* @param query - La requête de recherche
* @returns Tableau de résultats
*/
async search(query: string): Promise<SearchResult[]> {
// Implémentation
}Gardez les fonctions petites
Découpez la logique complexe en fonctions plus petites et ciblées :
async match(context: PluginContext): Promise<PluginResult[]> {
// 100 lignes de code qui font tout
}async match(context: PluginContext): Promise<PluginResult[]> {
const query = this.parseQuery(context.query);
const results = await this.fetchResults(query);
return this.formatResults(results);
}Tests
Testez vos plugins
Des tests complets garantissent la fiabilité et préviennent les régressions.
Testez les cas limites
Testez avec des entrées inhabituelles et aux limites :
describe("CalculatorPlugin", () => {
it("gère une entrée vide", () => {
const result = plugin.canHandle({ query: "" });
expect(result).toBe(false);
});
it("gère les expressions invalides", () => {
const result = plugin.canHandle({ query: "1 / 0" });
expect(result).toBe(true);
});
it("gère les entrées très longues", () => {
const longQuery = "1+".repeat(1000) + "1";
const result = plugin.canHandle({ query: longQuery });
expect(result).toBeDefined();
});
});Mockez les dépendances externes
Utilisez des mocks pour les appels API afin d'avoir des tests cohérents :
import { vi } from "vitest";
vi.mock("@tauri-apps/api/core", () => ({
invoke: vi.fn().mockResolvedValue({ data: [] }),
}));
// Test avec données mockées
it("gère les réponses API", async () => {
const results = await plugin.match({ query: "test" });
expect(results).toHaveLength(0);
});Testez la performance
Vérifiez que votre plugin respecte la limite de 500 ms :
it("répond dans les délais", async () => {
const start = Date.now();
await plugin.match({ query: "test" });
const duration = Date.now() - start;
expect(duration).toBeLessThan(500);
});Les plugins dépassant 500 ms seront terminés automatiquement.
Configuration
Accessibilité
Rendez les plugins accessibles à tous
L'accessibilité assure que tous les utilisateurs peuvent utiliser votre plugin efficacement.
Documentation
Une bonne documentation est essentielle
Les plugins bien documentés sont plus faciles à utiliser, maintenir et améliorer.
Communauté
📬 Soyez réactif
Répondez aux issues et pull requests rapidement. Une bonne communication construit la confiance.
🤝 Accueillez les contributions
Facilitez la contribution avec des guidelines claires et une attitude accueillante.
💜 Respectez le code de conduite
Soyez respectueux, inclusif et professionnel dans toutes les interactions.
Exemple de contribution
## Contribuer
Les contributions sont les bienvenues ! Merci de :
1. Forker le dépôt
2. Créer une branche de feature (`git checkout -b feature/amazing`)
3. Committer vos changements (`git commit -m 'Add amazing feature'`)
4. Pusher sur la branche (`git push origin feature/amazing`)
5. Ouvrir une Pull Request
Prêt à créer d'excellents plugins ?
En suivant ces bonnes pratiques vous créerez des plugins rapides, fiables, sécurisés et agréables à utiliser. Bon code ! 🚀