8.8 KiB
Roadmap – Plugin Nuxt v-can / v-cannot
Ce document sert de guide de travail pour construire le plugin Nuxt et son transformateur Vue associés. Il décrit les objectifs fonctionnels, les dépendances, ainsi que les phases successives pour livrer un module publiable.
🎯 Objectif produit
- Offrir des directives
v-can/v-cannotlisibles permettant de contrôler le rendu des templates Nuxt via une fonction d’autorisation__can__. - Générer automatiquement un objet proxy global
can(accessible en template et typé) pour éviter tout bruit TypeScript. - Remplacer chaque directive par des gardes
v-ifcompilés (__can__ou!__can__) tout en fusionnant proprement avec des conditions existantes. - Valider la config (permissions, import de
__can__) à la compilation et produire des erreurs DX claires lorsque l’usage est incorrect. - Intégrer proprement le module dans l’écosystème Nuxt (builder, runtime plugin, types, HMR, tests).
🔧 Pré-requis & Setup
-
Module starter Nuxt
- Utiliser la structure existante (
src/module.ts,src/runtime,playground,test). - Installer les dépendances (
bun install) puis lancerbun run dev:preparepour générer les artefacts initiaux.
- Utiliser la structure existante (
-
Scripts utiles
bun run dev: playground pour tester rapidement les directives.bun run lint,bun run test,bun run test:types: validation de la qualité.bun run prepack/bun run release: build & publication.
-
Convention de code
- TypeScript, style Nuxt (2 espaces, single quotes).
- Préfixer les exports/composables avec un nom de module lorsque nécessaire.
🧭 Phases de développement
🚀 Phase 1 — Setup du module Nuxt
- Définir
defineNuxtModuledanssrc/module.tsavecmeta,defaults,configKey. - Options visées :
permissions: Record<string, string[]>canFunctionImport: string(chemin d’import vers la fonction__can__)
- Ajouter le transformateur via
nuxt.options.vue.compilerOptions.nodeTransforms. - Enregistrer le plugin runtime (
addPlugin). - Exposer les options côté runtime via
runtimeConfig.public.
🚀 Phase 2 — Proxy global can
- Implémenter un proxy récursif dans
src/runtime/utils/can-proxy.ts(ou équivalent) qui:- Retourne une string chemin (
employee.view) tout en collectant les segments. - Supporte un nombre arbitraire de niveaux.
- Retourne une string chemin (
- Dans le plugin runtime, injecter
app.config.globalProperties.canet l’injecter via provide/injection si utile. - Générer les types
.d.ts(viaaddTypeTemplate) à partir des permissions de la config Nuxt.
🚀 Phase 3 — Analyse AST
- Créer un fichier
src/runtime/transformers/can-directives.ts(ou similaire) enregistré dansnodeTransforms. - Lors du parcours :
- Identifier directives
v-canetv-cannot. - Associer chaque
v-cannotauv-canprécédent (même niveau DOM). - Extraire et valider l’expression
can.*.*(interdire autres patterns).
- Identifier directives
🚀 Phase 4 — Transformation des nœuds v-can
- Convertir l’expression en chemin
['segment1','segment2',…]. - Chercher un
v-ifexistant :v-if="expr"→v-if="(expr) && __can__(path)".- Sans
v-if→ ajouterv-if="__can__(path)".
- Interdire
v-cansur des éléments possédant déjàv-else/v-else-if(lever une erreur compilateur).
🚀 Phase 5 — Transformation des nœuds v-cannot
- Vérifier qu’un
v-canimmédiatement précédent existe et qu’aucun autrev-cannotn’est déjà lié. - Générer un nouveau
v-if="!__can__(path)"(jamais de merge avec d’autresv-if). - Refuser toute expression ou argument sur
v-cannot.
🚀 Phase 6 — Gestion des erreurs DX
- Cas à couvrir (throw via
context.error(...)ounuxt.logger.fatal):v-cannotsansv-canvoisin.- Multiples
v-cannotpour le mêmev-can. - Expression
v-caninvalide ou dynamique non supportée. - Présence de
v-ifsurv-cannot. v-cansur un nœud déjà structuré (v-else,v-else-if).v-cannotavec une expression/argument.
- Rédiger des messages précis pour guider l’utilisateur.
🚀 Phase 7 — Intégration runtime
- Importer la fonction
__can__depuis la chaînecanFunctionImport. - L’injecter globalement (e.g.
nuxtApp.provide('__can__', canFn)+app.config.globalProperties.__can__ = canFn). - S’assurer que le proxy
canappelle seulement des placeholders et que la logique réelle réside dans__can__. - Vérifier compatibilité HMR (recharger templates/types via
updateTemplatessi nécessaire).
🚀 Phase 8 — Documentation
- Compléter
README.md/docs:- Installation (
bun install,modules: ['nuxt-can']). - Configuration (
permissions,canFunctionImport). - Exemples
v-can,v-cannot, cas avecv-if. - Limites connues (v-cannot isolé, interdiction v-else, etc.).
- Installation (
🚀 Phase 9 — Tests
- Unitaires (Vitest) sur le transformateur : couvrir chaque combinaison (simple,
v-if,v-for, erreurs). - Fixtures Nuxt sous
test/fixtures/*pour valider l’injection runtime et les types. - Playground : démontrer un scénario complet (liste d’employés, bouton conditionnel, message d’erreur).
📚 Exemples de transformation
Exemple 1 — v-can simple
Avant :
<button v-can="can.employee.view">Voir le dossier</button>
Après compilation :
<button v-if="__can__('employee', 'view')">Voir le dossier</button>
Exemple 2 — v-if + v-can
Avant :
<div v-if="isReady" v-can="can.contract.edit">
Modifier le contrat
</div>
Après compilation :
<div v-if="(isReady) && __can__('contract', 'edit')">
Modifier le contrat
</div>
Exemple 3 — v-can suivi de v-cannot
Avant :
<button v-if="ctaVisible" v-can="can.employee.view">Voir</button>
<p v-cannot>Acces refuse</p>
Après compilation :
<button v-if="(ctaVisible) && __can__('employee', 'view')">Voir</button>
<p v-if="!__can__('employee', 'view')">Acces refuse</p>
Exemple 4 — v-if / v-else-if / v-else + v-can / v-cannot
Usage attendu :
<template v-if="isOwner">
<button v-can="can.employee.edit">Modifier</button>
<p v-cannot>Contactez votre admin</p>
</template>
<template v-else-if="isManager">
<p>Vue manager</p>
</template>
<template v-else>
<p>Profil standard</p>
</template>
Résultat :
<template v-if="isOwner">
<button v-if="__can__('employee', 'edit')">Modifier</button>
<p v-if="!__can__('employee', 'edit')">Contactez votre admin</p>
</template>
<template v-else-if="isManager">
<p>Vue manager</p>
</template>
<template v-else>
<p>Profil standard</p>
</template>
⚠️ Règle : v-can ne peut pas se trouver sur un nœud utilisant v-else ou v-else-if. Encapsuler les branches dans un <template> et placer les directives seulement sur les éléments internes comme ci-dessus. (ça affichera une erreur si on utilise v-can sur un v-if/v-else ou v-if/v-if-else/v-else)
🗒️ Checklist exécution
- Phase 1 — Module configuré (
defineNuxtModule, options, plugin runtime déclaré, transformateur enregistré). - Phase 2 — Proxy
canopérationnel + types.d.tsgénérés viaaddTypeTemplate. - Phase 3 — Analyse AST implantée (détection
v-can/v-cannot, parsingcan.foo.bar). - Phase 4 — Transformation
v-can(fusionv-if, garde contrev-else). - Phase 5 — Transformation
v-cannot(lien direct auv-can, condition négative). - Phase 6 — Gestion des erreurs compile-time (tous les cas listés couverts).
- Phase 7 — Intégration runtime (
__can__importé/injecté, proxy exposé, HMR OK). - Phase 8 — Documentation mise à jour (README + exemples).
- Phase 9 — Tests automatisés verts (unitaires, fixtures, playground vérifié).
- Release readiness —
bun run lint,bun run test,bun run test:types,bun run prepack.
✅ Livrables attendus
- Module Nuxt compilable (
bun run prepack) et partageable (package.jsonprêt à être packé). - Types générés automatiquement et inclus dans
nuxt.d.ts. - Suite de tests verte (
bun run lint && bun run test && bun run test:types). - Documentation à jour (README + roadmap).
📌 Notes pratiques
- Organiser les helpers runtime dans
src/runtimepour respecter la structure du module starter. - Utiliser
createResolver(import.meta.url)pour référencer les chemins des plugins/transformers/types. - Pour tester dans une app externe :
bun packpuisbun install ../nuxt-can/my-module.tgz. - Garder des commits Conventional (
feat:,fix:, etc.) pour préparer l’automatisation release.
Cette roadmap sert de référence pour planifier et suivre l’avancement du plugin v-can. Elle synthétise les besoins fonctionnels, les étapes techniques et les règles de qualité attendues avant publication.