Estoy tratando de encontrar la forma correcta de atravesar un HTML AST y encontrar todos los nodos con type: tag y empujarlos a una matriz.
También estoy usando html-parse-stringify para convertir mi HTML en un AST si eso ayuda con algo.
He visto algunos videos sobre cómo atravesar HTML AST en YouTube, pero todos comienzan con un objeto como el nodo de inicio principal, en lugar de comenzar con un Array. Pero duda de que es un gran problema.
El conjunto de datos con los que estoy trabajando son datos raspados de sitios web que luego se convierten en un AST utilizando la biblioteca mencionada anteriormente.
A partir de aquí, solo quiero crear una estructura de bucle básica que pueda atravesar completamente mi AST mientras filtra todos los tipos innecesarios, como text & comment , y luego inserta el objeto correcto en una matriz.
Aquí está la estructura de datos con la que estoy trabajando, he colocado una estructura de datos vacía para facilitar la copia.
También me gustaría reducir el uso de bucles tanto como sea posible por el bien de la complejidad del tiempo.
function mainLoop(node) { Array.prototype.forEach.call(node, parent => { console.log(parent.name); const children = parent.children.filter(n => n.type !== 'text' && n.type !== 'comment'); loop(children) }) } function loop(children) { console.log(children.name) if (children) { Array.prototype.forEach.call(children, child => { loop(child); }) } } mainLoop();Estructura de datos vacía
const docTree = [ { attrs: { class: "flex flex-col h-screen", }, children: [ { type: 'tag', name: 'main', attrs: { class: '' }, children: [], } ], name: 'div', type: 'tag', voidElement: false, } ]Si su único objetivo es eliminar texto y comentarios, entonces es bastante sencillo en una sola reducción:
const traverse = (nodes) => { return nodes.reduce((acc,node) => { if(node.type === 'text' || node.type === 'comment') return acc; return [ ...acc, { ...node, children: traverse(node.children) } ] },[]); }En realidad no he ejecutado este código, pero creo que funcionará
Si desea aplanar a todos los niños, entonces haga esto:
const traverse = (nodes) => { return nodes.reduce((acc,{children = [], ...node}) => { if(node.type === 'text' || node.type === 'comment') return acc; return [ ...acc, node, ...traverse(children) ] },[]); }EDICIÓN 2: Ah, me perdí la parte en la que solo quieres la etiqueta de tipo. Eso se hace con esto:
const traverse = (nodes) => { return nodes.reduce((acc,{children = [], ...node}) => { if(node.type !== 'tag') return acc; return [ ...acc, node, ...traverse(children) ] },[]); }Además, no estoy seguro de si desea que los elementos secundarios permanezcan como parte del nodo principal o no. Esto aquí también podría ser lo que quieres:
const traverse = (nodes) => { return nodes.reduce((acc,node) => { if(node.type !== 'tag') return acc; return [ ...acc, node, ...traverse(node.children) ] },[]); }Así que creo que he encontrado una solución que estoy buscando. No he terminado de probarlo por completo, pero está en la línea de esto.
Utiliza un bucle externo para recorrer mi matriz inicial de elementos y luego una función recursiva interna para recorrer todos los datos secundarios que estaba buscando y los inserta en una matriz.
function parentLoop(domAST) { let results = []; Array.prototype.forEach.call(domAST, ele => { function childLoop(node) { const cleaned = node.children.filter(n => n.type !== 'text' && n.type !== 'comment' && n.name !== 'br'); for (let i = 0; i < cleaned.length; i++) { let child = cleaned[i]; if (child.type === 'tag') { results.push(child); } childLoop(child); } } childLoop(ele); }) return results; }Si hay soluciones mejores o más limpias, todavía estoy abierto a ellas.