Transformateurs de publication¶
Intervenez dans le pipeline de publication pour personnaliser le rendu et le déploiement des sites web.
Aperçu¶
Les transformateurs de publication sont cinq hooks qui s'exécutent pendant le processus de publication :
- renderComponent — Redéfinir le rendu HTML des composants GrapesJS
- renderCssRule — Redéfinir le rendu des règles CSS de GrapesJS
- transformFile — Modifier les fichiers (HTML, CSS, ressources) avant la publication
- transformPermalink — Réécrire les URL dans le HTML et le CSS
- transformPath — Changer l'emplacement de publication des fichiers sur le serveur
Les transformateurs sont enchaînés : chacun reçoit la sortie du précédent, ce qui permet à plusieurs transformateurs de se composer.
Prérequis¶
- Compréhension de la configuration client
- Familiarité avec le flux de publication
- Connaissances de base en JavaScript
Interface PublicationTransformer¶
export interface PublicationTransformer {
renderComponent?(
component: Component,
toHtml: () => string
): string | undefined
renderCssRule?(
rule: CssRule,
initialRule: () => StyleProps
): StyleProps | undefined
transformFile?(file: ClientSideFile): ClientSideFile
transformPermalink?(
link: string,
type: ClientSideFileType,
initiator: Initiator
): string
transformPath?(
path: string,
type: ClientSideFileType
): string
}
Cas d'utilisation¶
Minifier le HTML et le CSS¶
const minifyHtml = require('html-minifier').minify
const postcss = require('postcss')
const cssnano = require('cssnano')
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
// Minify HTML
if (file.type === 'html') {
file.content = minifyHtml(file.content, {
removeComments: true,
collapseWhitespace: true,
minifyCSS: true,
})
}
// Minify CSS
if (file.type === 'css') {
const result = postcss([cssnano()]).process(file.content)
file.content = result.css
}
return file
},
})
}
Ajouter du cache busting aux ressources¶
export default async function (config) {
const assetHashes = new Map()
config.addPublicationTransformers({
transformFile(file) {
// Generate hash for assets
if (file.type === 'asset') {
const crypto = require('crypto')
const hash = crypto
.createHash('md5')
.update(file.content)
.digest('hex')
.slice(0, 8)
const newName = file.name.replace(
/(\.[^.]+)$/,
`.${hash}$1`
)
assetHashes.set(file.name, newName)
file.name = newName
}
return file
},
transformPermalink(link, type, initiator) {
// Rewrite asset references in HTML/CSS
if (type === 'asset') {
const hashed = assetHashes.get(link)
return hashed ? hashed : link
}
return link
},
})
}
Réécrire les URL pour un CDN¶
export default async function (config) {
config.addPublicationTransformers({
transformPermalink(link, type, initiator) {
// Skip external URLs
if (link.startsWith('http')) {
return link
}
// Point assets to CDN
if (type === 'asset') {
return `https://cdn.example.com${link}`
}
// Keep HTML links local
return link
},
})
}
Changer la structure des fichiers¶
export default async function (config) {
config.addPublicationTransformers({
transformPath(path, type) {
// Move CSS to a different directory
if (type === 'css') {
return path.replace(/^\/css/, '/styles')
}
// Flatten file structure
if (type === 'asset') {
return '/' + path.split('/').pop()
}
return path
},
transformPermalink(link, type, initiator) {
// Update references in HTML/CSS
if (type === 'css') {
link = link.replace(/^\/css/, '/styles')
}
if (type === 'asset' && initiator === 'css') {
// Assets referenced in CSS: adjust relative path
const newPath = '/' + link.split('/').pop()
// Go up one level from CSS directory
return '../' + newPath
}
return link
},
})
}
Supprimer les commentaires HTML en production¶
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'html' && process.env.NODE_ENV === 'production') {
// Remove HTML comments
file.content = file.content.replace(/<!--[\s\S]*?-->/g, '')
}
return file
},
})
}
Ajouter des scripts d'analytics¶
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'html') {
const analyticsScript = `
<script async src="https://analytics.example.com/track.js"></script>
<script>
window.trackingId = '${process.env.TRACKING_ID}'
</script>
`
// Inject before closing </head>
file.content = file.content.replace(
/<\/head>/,
analyticsScript + '</head>'
)
}
return file
},
})
}
API des transformateurs¶
renderComponent¶
Interceptez le rendu des composants avant que GrapesJS ne les convertisse en HTML :
export default async function (config) {
config.addPublicationTransformers({
renderComponent(component, toHtml) {
// Check component type
if (component.get('type') === 'image') {
// Custom logic for images
const src = component.get('attributes').src
console.log(`Rendering image: ${src}`)
}
// Call the default renderer
return toHtml()
},
})
}
Retourner undefined utilise le rendu par défaut.
renderCssRule¶
Interceptez le rendu des règles CSS :
export default async function (config) {
config.addPublicationTransformers({
renderCssRule(rule, initialRule) {
// Get the CSS rule
const selector = rule.getSelector()
const style = initialRule()
// Transform the style
if (selector.includes('button')) {
style['cursor'] = 'pointer'
}
return style
},
})
}
Retourner undefined utilise les styles par défaut.
transformFile¶
Modifiez l'objet fichier (contenu, nom, type, chemin) :
export interface ClientSideFile {
name: string // Filename
type: ClientSideFileType // 'html', 'css', 'asset'
content: string | Buffer // File content
path?: string // File path on server
}
Exemple :
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
// Rename file
if (file.type === 'css') {
file.name = file.name + '.min'
}
// Transform content
if (file.type === 'html') {
file.content = file.content.toUpperCase()
}
return file
},
})
}
transformPermalink¶
Réécrivez les URL référencées dans le HTML et le CSS :
export default async function (config) {
config.addPublicationTransformers({
transformPermalink(link, type, initiator) {
// type: 'html', 'css', 'asset'
// initiator: 'html' (in HTML file), 'css' (in CSS file)
// Example: Make all links absolute
if (!link.startsWith('http')) {
return 'https://example.com' + (link.startsWith('/') ? link : '/' + link)
}
return link
},
})
}
transformPath¶
Changez l'emplacement de publication des fichiers :
export default async function (config) {
config.addPublicationTransformers({
transformPath(path, type) {
// type: 'html', 'css', 'asset'
// Example: Prefix all files with '/v1/'
return '/v1' + (path.startsWith('/') ? path : '/' + path)
},
})
}
Enchaîner les transformateurs¶
Plusieurs transformateurs se composent :
export default async function (config) {
// Transformer 1: Add hashes
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'css') {
file.name = file.name + '.hash'
}
return file
},
})
// Transformer 2: Minify
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'css') {
file.content = minifyCSS(file.content)
}
return file
},
})
}
Les transformateurs s'exécutent dans l'ordre. La sortie du transformateur 1 devient l'entrée du transformateur 2.
Patrons courants¶
Transformations basées sur l'environnement¶
export default async function (config) {
if (process.env.NODE_ENV === 'production') {
config.addPublicationTransformers({
transformFile(file) {
// Minify in production only
if (file.type === 'html') {
file.content = minifyHtml(file.content)
}
return file
},
})
}
}
Transformations conditionnelles basées sur le fichier¶
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
// Only transform specific files
if (file.name === 'index.html') {
// Special handling for homepage
file.content = file.content.replace(/OLD_TEXT/g, 'NEW_TEXT')
}
return file
},
})
}
Journalisation et débogage¶
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
console.log(`Processing ${file.name} (${file.type})`)
return file
},
transformPath(path, type) {
console.log(`Path: ${path} → Type: ${type}`)
return path
},
})
}
Considérations de performance¶
Les transformateurs s'exécutent à chaque publication. Optimisez :
// Cache expensive operations
const styleCache = new Map()
export default async function (config) {
config.addPublicationTransformers({
transformFile(file) {
if (file.type === 'css') {
// Check cache first
if (styleCache.has(file.name)) {
return { ...file, content: styleCache.get(file.name) }
}
// Expensive operation
const processed = processCSS(file.content)
styleCache.set(file.name, processed)
file.content = processed
}
return file
},
})
}
Dépannage¶
Le transformateur n'est pas appliqué¶
Vérifiez :
- Le transformateur est ajouté dans la configuration client :
config.addPublicationTransformers(...) - La syntaxe est correcte (toutes les méthodes sont optionnelles)
- Pas d'erreurs dans la console du navigateur
- Le transformateur s'exécute sur les bons fichiers (vérifiez le paramètre
type)
Ajoutez des journaux pour déboguer :
config.addPublicationTransformers({
transformFile(file) {
console.log('Transforming:', file.name, file.type)
return file
},
})
Les URL sont cassées après la transformation¶
Assurez-vous que transformPermalink correspond à transformPath :
config.addPublicationTransformers({
transformPath(path, type) {
if (type === 'css') {
return '/styles/' + path.split('/').pop()
}
return path
},
transformPermalink(link, type, initiator) {
// If CSS is in /styles/, update references
if (type === 'css' && initiator === 'html') {
return '/styles/' + link.split('/').pop()
}
return link
},
})
Problèmes de performance¶
Si la publication est lente :
- Profilez avec les DevTools
- Mettez en cache les opérations coûteuses
- Minimisez les lectures/écritures de fichiers
- Évitez les opérations bloquantes
Voir aussi¶
- Configuration client et plugins — API de configuration client
- Construction build awesome
- Comment fonctionne la publication — Flux de publication