El patrón Visitor añade una operación nueva sobre una jerarquía existente sin modificarla.
1. El Problema de Partida
Se quiere mostrar "X min de lectura" en cada post de knowledge. El AST que produce next-mdx-remote al parsear tiene nodos de distinto tipo (paragraph, code, heading, image), y cada uno cuenta distinto para el tiempo de lectura (código se lee más lento, imágenes suman un tiempo fijo). Añadir esta lógica directamente en el parser de MDX ensuciaría una dependencia externa que no debería modificarse.
2. La Solución Visitor
// src/lib/reading-time/ReadingTimeVisitor.js
export class ReadingTimeVisitor {
totalSeconds = 0;
visitParagraph(node) {
const words = node.value.split(/\s+/).length;
this.totalSeconds += (words / 200) * 60; // 200 palabras/min
}
visitCode(node) {
const lines = node.value.split('\n').length;
this.totalSeconds += lines * 2; // 2s por línea de código
}
visitImage() {
this.totalSeconds += 12; // Tiempo fijo por imagen
}
}
export function acceptAstNode(node, visitor) {
const method = `visit${node.type[0].toUpperCase()}${node.type.slice(1)}`;
if (visitor[method]) visitor[method](node);
(node.children ?? []).forEach((child) => acceptAstNode(child, visitor));
}3. Uso
// src/lib/mdx.js
import { ReadingTimeVisitor, acceptAstNode } from './reading-time/ReadingTimeVisitor';
export function calculateReadingTime(mdxAst) {
const visitor = new ReadingTimeVisitor();
acceptAstNode(mdxAst, visitor);
return Math.ceil(visitor.totalSeconds / 60); // minutos redondeados
}4. Ventajas
- El AST de
next-mdx-remoteno se toca para nada; la lógica vive fuera, en el visitante. - Añadir una métrica nueva (ej. contar enlaces externos) = un
LinkCountVisitornuevo, sin tocaracceptAstNode. - Reutilizable para cualquier operación futura sobre el árbol de contenido (índice de tabla de contenidos, extracción de imágenes, etc.).