Créer des plugins¶
Cette page montre comment écrire un plugin GrapesJS pour l'éditeur, un plugin serveur Silex et un transformateur de publication. Pour les charger, voir Utiliser des plugins.
Licence : Les plugins ne sont pas considérés comme des oeuvres dérivées de Silex au regard de la licence AGPL. Vous pouvez utiliser n'importe quelle licence pour vos plugins.
Pour modifier Silex lui-même, voir le guide de contribution. Les plugins officiels @silexlabs/grapesjs-* sont dans le dépôt de Silex, sous grapesjs-plugins/.
Deux formats de plugin¶
| Format | Signature | S'exécute dans | Chargé par |
|---|---|---|---|
| Plugin GrapesJS | (editor, opts) => {} |
le navigateur | config.grapesJsConfig.plugins dans la config client |
| Plugin Silex | async (config, options) => ({}) |
le navigateur ou Node | config.addPlugin(), SILEX_CLIENT_CONFIG, SILEX_SERVER_CONFIG |
Un plugin Silex reçoit l'objet de config : ClientConfig dans le navigateur, ServerConfig dans Node. Il peut renvoyer un objet, qui est fusionné dans la config. Le fichier de config client et le fichier de config serveur sont eux aussi des plugins Silex.
Plugin GrapesJS¶
// hello-block.js
export default function helloBlock(editor, opts) {
editor.Blocks.add('hello-block', {
label: opts.label ?? 'Hello block',
category: 'Elements',
content: '<p class="hello">Hello</p>',
})
}
Il utilise l'API de GrapesJS : editor.Blocks, editor.Commands, editor.Panels, editor.on(), etc. Chargez-le dans la config client :
// client-config.js
import helloBlock from '/my-plugins/hello-block.js'
export default async function (config) {
config.grapesJsConfig.plugins.push(helloBlock)
config.grapesJsConfig.pluginsOpts[helloBlock.toString()] = { label: 'Hello' }
return {}
}
Le chemin /my-plugins/hello-block.js doit être servi par Silex. C'est ce que fait la section suivante.
Plugin serveur Silex¶
Un plugin serveur ajoute ses routes au démarrage du serveur, dans l'événement startup-start. L'événement fournit l'app Express :
// server-config.js, SILEX_SERVER_CONFIG pointe vers ce fichier
module.exports = async function (config, options) {
config.on('startup-start', ({ app }) => {
app.get('/hello', (req, res) => {
res.json({ message: 'Hello from my plugin' })
})
})
return {}
}
Écrivez le fichier en CommonJS (module.exports, require), comme server/deploy/.silex.js. Utilisez votre propre préfixe pour les routes : /api/ appartient à Silex.
Servir un fichier pour l'éditeur¶
Le même gestionnaire peut servir le plugin GrapesJS ci-dessus, pour que la config client puisse l'importer :
const { join } = require('node:path')
module.exports = async function (config) {
config.on('startup-start', ({ app }) => {
app.get('/my-plugins/hello-block.js', (req, res) => {
res.sendFile(join(__dirname, 'my-plugins/hello-block.js'))
})
})
return {}
}
Écouter les événements du serveur¶
config est un émetteur d'événements. Les événements, dans server/events.ts :
| Événement | Nom dans ServerEvent |
Argument |
|---|---|---|
startup-start |
STARTUP_START |
{ app }, avant que le serveur écoute |
startup-end |
STARTUP_END |
{ app }, le serveur écoute |
publish-start |
PUBLISH_START |
les données de publication : files, pages, assets, styles, settings… |
publish-end |
PUBLISH_END |
rien, ou l'Error. Envoyé quand le connecteur d'hébergement a démarré son travail : FTP et GitLab continuent en arrière-plan |
store-start |
WEBSITE_STORE_START |
{ websiteId, websiteData, connectorId } |
store-end |
WEBSITE_STORE_END |
null, ou l'Error |
asset-store-start |
WEBSITE_ASSET_STORE_START |
{ files, websiteId, connectorId } |
asset-store-end |
WEBSITE_ASSET_STORE_END |
null, ou l'Error |
module.exports = async function (config) {
config.on('publish-end', (error) => {
console.log('Publication', error ? `en échec : ${error.message}` : 'démarrée')
})
return {}
}
Ajouter un connecteur¶
Un connecteur de stockage enregistre les sites et leurs assets. Un connecteur d'hébergement les publie. Ils implémentent les interfaces StorageConnector et HostingConnector de server/connectors/connectors.ts :
| Interface | Membres |
|---|---|
| les deux | connectorId, connectorType, displayName, icon, color, background, disableLogout?, getOAuthUrl, getLoginForm, getSettingsForm, isLoggedIn, setToken, logout, getUser, getOptions |
StorageConnector |
listWebsites, readWebsite, createWebsite, updateWebsite, deleteWebsite, duplicateWebsite, writeAssets, readAsset, deleteAssets, getWebsiteMeta, setWebsiteMeta |
HostingConnector |
publish, getUrl |
Enregistrez le connecteur dans le gestionnaire de startup-start, avec config.addStorageConnector() ou config.addHostingConnector(). .silex.js, chargé après votre fichier, définit les listes de connecteurs à partir de STORAGE_CONNECTORS et HOSTING_CONNECTORS. Cet exemple reprend le connecteur système de fichiers de Silex avec un autre dossier. Les classes de Silex sont dans le dossier dist/server/ de l'installation de Silex, /silex/dist/server/ dans l'image Docker :
const { FsStorage } = require('/silex/dist/server/server/connectors/FsStorage')
class MyStorage extends FsStorage {
connectorId = 'my-storage'
displayName = 'My storage'
}
module.exports = async function (config) {
config.on('startup-start', () => {
config.addStorageConnector(new MyStorage(config, { path: '/data/my-storage' }))
})
return {}
}
Les connecteurs intégrés sont de bons exemples : FtpConnector, GitlabConnector, GitlabHostingConnector et DownloadConnector dans server/plugins/, FsStorage et FsHosting dans server/connectors/.
Transformateur de publication¶
Un transformateur est un objet, ajouté dans la config client avec config.addPublicationTransformers(). Ce n'est pas un plugin GrapesJS. Il s'exécute dans le navigateur quand l'utilisateur publie :
// client-config.js
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'html') {
return { ...file, content: file.content.replace('</body>', '<!-- publié avec Silex --></body>') }
}
return file
},
})
return {}
}
Les méthodes possibles sont renderComponent, renderCssRule, transformFile, transformPermalink et transformPath. Toute autre clé lève une erreur. Voir Transformateurs de publication.