Créer des plugins — vue d'ensemble

Un plugin étend Flexweg CMS en s'accrochant au pipeline de publication, à l'éditeur, au tableau de bord ou à l'UI admin. Les plugins sont des modules JavaScript qui enregistrent des filtres (mutent

Un plugin étend Flexweg CMS en s'accrochant au pipeline de publication, à l'éditeur, au tableau de bord ou à l'UI admin. Les plugins sont des modules JavaScript qui enregistrent des filtres (mutent des valeurs), des actions (effets de bord), des blocs (contenu de l'éditeur), des cartes de tableau de bord et des cibles de régénération — tout via une fonction unique register(api).

Le modèle mental est celui des filtres/actions WordPress, mais avec un typage TypeScript et une surface d'API beaucoup plus petite.

Deux façons de livrer un plugin

  • Plugins in-tree — un dossier sous src/plugins/ qui est bundlé dans l'admin aux côtés des plugins intégrés. Mieux quand vous maintenez votre propre fork de l'admin.
  • Plugins externes — un ZIP avec un bundle ESM pré-compilé, installé au runtime via le bouton Install plugin. Mieux quand vous voulez distribuer des plugins indépendamment des releases admin.

Les deux partagent le même shape de manifest et la même API runtime. Cette section couvre le chemin in-tree d'abord ; Construire un bundle plugin externe couvre ce qui diffère pour les externes.

Ce qu'est un plugin, structurellement

src/plugins/<id>/
├── manifest.ts           ← export default le PluginManifest
├── i18n.ts               ← exports des bundles { en, fr, de, ... }
├── SettingsPage.tsx      ← optionnel, le composant React de réglages
├── generator.ts          ← optionnel, la logique métier
├── xsl.ts                ← optionnel, stylesheets XML
└── README.md             ← optionnel, doc embarquée

Le seul fichier obligatoire est manifest.ts. Les autres sont des conventions pour garder le code organisé.

Le manifest, au plus simple

TS
import type { PluginManifest } from "@flexweg/cms-runtime";

const manifest: PluginManifest = {
  id: "my-plugin",                  // unique, kebab-case
  name: "My Plugin",                // affichage UI
  version: "1.0.0",                 // semver
  description: "Ce que mon plugin fait",
  author: "Mon nom",
  register(api) {
    api.addFilter("page.head.extra", (head) => {
      return head + '<meta name="generator" content="My Plugin" />\n';
    });
  },
};

export default manifest;

Et le déclarer dans src/plugins/index.ts :

TS
import myPlugin from "./my-plugin/manifest";

export const PLUGINS = [
  // ... autres plugins intégrés
  myPlugin,
];

C'est tout. Au prochain npm run dev ou npm run build, le plugin apparaît dans /admin/plugins, peut être activé / désactivé, et son hook est appliqué dès l'activation.

Le cycle de vie d'un plugin

  1. L'admin charge l'appapplyPluginRegistration(enabledFlags) tourne dans CmsDataContext
  2. Pour chaque plugin activé, manifest.register(api) est appelé — qui enregistre filtres / actions / blocs / cartes / cibles
  3. Le pipeline de publication ou l'UI lit la registry et applique ce qui est enregistré
  4. Si les réglages changent (un autre plugin est activé / désactivé), applyPluginRegistration re-tourne — resetRegistry() vide tout, puis re-enregistre depuis les plugins actifs

Donc votre code de plugin ne maintient PAS d'état entre les register(). Tout l'état configurable vit dans settings.pluginConfigs[<id>].

Que peut faire un plugin

  • Filtres et actions : interagir avec le pipeline de publication (Hooks)
  • Blocs d'éditeur : ajouter des blocs personnalisés à l'éditeur (Blocs)
  • Cartes de tableau de bord : ajouter des cartes au dashboard (Cartes)
  • Cibles de régénération : ajouter une entrée au menu Regenerate
  • Page de réglages : exposer une UI de configuration (Page de réglages)
  • Bundles i18n : traduire votre UI en 7 langues

Ce qu'un plugin ne peut pas faire :

  • Ajouter ou modifier des templates de thème (c'est le boulot d'un thème)
  • Modifier le routage de l'admin (les routes sont fixes)
  • Tourner du code côté serveur (il n'y a pas de serveur)
  • Accéder au backend (Firebase / SQLite) sans passer par les dispatchers du runtime

Prochaines étapes