HTMLRewriter vous permet d'utiliser des sélecteurs CSS pour transformer des documents HTML. Il fonctionne avec Request, Response, ainsi que string. L'implémentation de Bun est basée sur lol-html de Cloudflare.
Utilisation
Un cas d'utilisation courant est la réécriture d'URL dans du contenu HTML. Voici un exemple qui réécrit les sources d'images et les URL de liens pour utiliser un domaine CDN :
// Remplacer toutes les images par un rickroll
const rewriter = new HTMLRewriter().on("img", {
element(img) {
// Miniature de la célèbre vidéo de rickroll
img.setAttribute("src", "https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg");
// Envelopper l'image dans un lien vers la vidéo
img.before('<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">', {
html: true,
});
img.after("</a>", { html: true });
// Ajouter un texte alternatif amusant
img.setAttribute("alt", "Definitely not a rickroll");
},
});
// Un document HTML exemple
const html = `
<html>
<body>
<img src="/cat.jpg">
<img src="dog.png">
<img src="https://example.com/bird.webp">
</body>
</html>
`;
const result = rewriter.transform(html);
console.log(result);Cela remplace toutes les images par une miniature de Rick Astley et enveloppe chaque <img> dans un lien, produisant un diff comme ceci :
<html>
<body>
<img src="/cat.jpg" />
<img src="dog.png" />
<img src="https://example.com/bird.webp" />
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" />
</a>
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" />
</a>
<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
<img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll" />
</a>
</body>
</html>Maintenant, chaque image de la page sera remplacée par une miniature de Rick Astley, et cliquer sur n'importe quelle image mènera à une vidéo très célèbre.
Types d'entrée
HTMLRewriter peut transformer du HTML provenant de diverses sources. L'entrée est automatiquement gérée en fonction de son type :
// Depuis Response
rewriter.transform(new Response("<div>content</div>"));
// Depuis string
rewriter.transform("<div>content</div>");
// Depuis ArrayBuffer
rewriter.transform(new TextEncoder().encode("<div>content</div>").buffer);
// Depuis Blob
rewriter.transform(new Blob(["<div>content</div>"]));
// Depuis File
rewriter.transform(Bun.file("index.html"));Notez que l'implémentation HTMLRewriter de Cloudflare Workers ne prend en charge que les objets Response.
Gestionnaires d'éléments
La méthode on(selector, handlers) vous permet d'enregistrer des gestionnaires pour les éléments HTML qui correspondent à un sélecteur CSS. Les gestionnaires sont appelés pour chaque élément correspondant pendant l'analyse :
rewriter.on("div.content", {
// Gérer les éléments
element(element) {
element.setAttribute("class", "new-content");
element.append("<p>Nouveau contenu</p>", { html: true });
},
// Gérer les nœuds texte
text(text) {
text.replace("nouveau texte");
},
// Gérer les commentaires
comments(comment) {
comment.remove();
},
});Les gestionnaires peuvent être asynchrones et retourner une Promise. Notez que les opérations asynchrones bloqueront la transformation jusqu'à ce qu'elles soient terminées :
rewriter.on("div", {
async element(element) {
await Bun.sleep(1000);
element.setInnerContent("<span>remplacer</span>", { html: true });
},
});Prise en charge des sélecteurs CSS
La méthode on() prend en charge un large éventail de sélecteurs CSS :
// Sélecteurs de balises
rewriter.on("p", handler);
// Sélecteurs de classe
rewriter.on("p.red", handler);
// Sélecteurs d'ID
rewriter.on("h1#header", handler);
// Sélecteurs d'attributs
rewriter.on("p[data-test]", handler); // A l'attribut
rewriter.on('p[data-test="one"]', handler); // Correspondance exacte
rewriter.on('p[data-test="one" i]', handler); // Insensible à la casse
rewriter.on('p[data-test="one" s]', handler); // Sensible à la casse
rewriter.on('p[data-test~="two"]', handler); // Correspondance de mot
rewriter.on('p[data-test^="a"]', handler); // Commence par
rewriter.on('p[data-test$="1"]', handler); // Se termine par
rewriter.on('p[data-test*="b"]', handler); // Contient
rewriter.on('p[data-test|="a"]', handler); // Séparé par tiret
// Combinateurs
rewriter.on("div span", handler); // Descendant
rewriter.on("div > span", handler); // Enfant direct
// Pseudo-classes
rewriter.on("p:nth-child(2)", handler);
rewriter.on("p:first-child", handler);
rewriter.on("p:nth-of-type(2)", handler);
rewriter.on("p:first-of-type", handler);
rewriter.on("p:not(:first-child)", handler);
// Sélecteur universel
rewriter.on("*", handler);Opérations sur les éléments
Les éléments fournissent diverses méthodes de manipulation. Toutes les méthodes de modification retournent l'instance d'élément pour le chaînage :
rewriter.on("div", {
element(el) {
// Attributs
el.setAttribute("class", "new-class").setAttribute("data-id", "123");
const classAttr = el.getAttribute("class"); // "new-class"
const hasId = el.hasAttribute("id"); // boolean
el.removeAttribute("class");
// Manipulation de contenu
el.setInnerContent("Nouveau contenu"); // Échappe le HTML par défaut
el.setInnerContent("<p>Contenu HTML</p>", { html: true }); // Analyse le HTML
el.setInnerContent(""); // Effacer le contenu
// Manipulation de position
el.before("Contenu avant").after("Contenu après").prepend("Premier enfant").append("Dernier enfant");
// Insertion de contenu HTML
el.before("<span>avant</span>", { html: true })
.after("<span>après</span>", { html: true })
.prepend("<span>premier</span>", { html: true })
.append("<span>dernier</span>", { html: true });
// Suppression
el.remove(); // Supprimer l'élément et son contenu
el.removeAndKeepContent(); // Supprimer uniquement les balises de l'élément
// Propriétés
console.log(el.tagName); // Nom de balise en minuscules
console.log(el.namespaceURI); // URI d'espace de noms de l'élément
console.log(el.selfClosing); // Si l'élément est auto-fermant (ex. <div />)
console.log(el.canHaveContent); // Si l'élément peut contenir du contenu (faux pour les éléments vides comme <br>)
console.log(el.removed); // Si l'élément a été supprimé
// Itération des attributs
for (const [name, value] of el.attributes) {
console.log(name, value);
}
// Gestion des balises de fin
el.onEndTag(endTag => {
endTag.before("Avant la balise de fin");
endTag.after("Après la balise de fin");
endTag.remove(); // Supprimer la balise de fin
console.log(endTag.name); // Nom de balise en minuscules
});
},
});Opérations sur le texte
Les gestionnaires de texte fournissent des méthodes de manipulation de texte. Les chunks de texte représentent des portions de contenu texte et fournissent des informations sur leur position dans le nœud texte :
rewriter.on("p", {
text(text) {
// Contenu
console.log(text.text); // Contenu texte
console.log(text.lastInTextNode); // Si c'est le dernier chunk
console.log(text.removed); // Si le texte a été supprimé
// Manipulation
text.before("Texte avant").after("Texte après").replace("Nouveau texte").remove();
// Insertion de contenu HTML
text
.before("<span>avant</span>", { html: true })
.after("<span>après</span>", { html: true })
.replace("<span>remplacer</span>", { html: true });
},
});Opérations sur les commentaires
Les gestionnaires de commentaires permettent la manipulation de commentaires avec des méthodes similaires aux nœuds texte :
rewriter.on("*", {
comments(comment) {
// Contenu
console.log(comment.text); // Texte du commentaire
comment.text = "Nouveau texte de commentaire"; // Définir le texte du commentaire
console.log(comment.removed); // Si le commentaire a été supprimé
// Manipulation
comment.before("Avant le commentaire").after("Après le commentaire").replace("Nouveau commentaire").remove();
// Insertion de contenu HTML
comment
.before("<span>avant</span>", { html: true })
.after("<span>après</span>", { html: true })
.replace("<span>remplacer</span>", { html: true });
},
});Gestionnaires de document
La méthode onDocument(handlers) vous permet de gérer des événements au niveau du document. Ces gestionnaires sont appelés pour des événements qui se produisent au niveau du document plutôt qu'à l'intérieur d'éléments spécifiques :
rewriter.onDocument({
// Gérer le doctype
doctype(doctype) {
console.log(doctype.name); // "html"
console.log(doctype.publicId); // identifiant public si présent
console.log(doctype.systemId); // identifiant système si présent
},
// Gérer les nœuds texte
text(text) {
console.log(text.text);
},
// Gérer les commentaires
comments(comment) {
console.log(comment.text);
},
// Gérer la fin du document
end(end) {
end.append("<!-- Pied de page -->", { html: true });
},
});Gestion des Response
Lors de la transformation d'une Response :
- Le code de statut, les en-têtes et autres propriétés de réponse sont conservés
- Le corps est transformé tout en conservant les capacités de streaming
- Le codage de contenu (comme gzip) est géré automatiquement
- Le corps de réponse original est marqué comme utilisé après la transformation
- Les en-têtes sont clonés vers la nouvelle réponse
Gestion des erreurs
Les opérations HTMLRewriter peuvent lancer des erreurs dans plusieurs cas :
- Syntaxe de sélecteur invalide dans la méthode
on() - Contenu HTML invalide dans les méthodes de transformation
- Erreurs de flux lors du traitement des corps de Response
- Échecs d'allocation mémoire
- Types d'entrée invalides (ex. passer Symbol)
- Erreurs de corps déjà utilisé
Les erreurs doivent être attrapées et gérées de manière appropriée :
try {
const result = rewriter.transform(input);
// Traiter le résultat
} catch (error) {
console.error("Erreur HTMLRewriter :", error);
}Voir aussi
Vous pouvez également lire la documentation Cloudflare, avec laquelle cette API est conçue pour être compatible.