𝗕𝗲𝘆𝗼𝗻𝗱 𝗠𝗶𝘀𝘀𝗶𝗻𝗴 𝗘𝘅𝗽𝗼𝗿𝘁𝘀: 𝗕𝘂𝗶𝗹𝗱𝗶𝗻𝗴 𝗮𝗻 𝗘𝗮𝗿𝗹𝘆 𝗚𝗮𝗿𝗯𝗮𝗴𝗲 𝗖𝗼𝗹𝗹𝗲𝗰𝘁𝗼𝗿
I tried to use a standard plugin to fix missing links in Webpack documentation. It failed.
Integrating a plugin into a large codebase is not a simple task. It became an architectural challenge. I had to manage Abstract Syntax Tree (AST) manipulation, memory use, and recursive loops.
The Problem with Standard Plugins
I ran three experiments to find a solution.
Experiment 1: The plugin recovered 135 types. However, it put them in an internal module. Our tool created a separate folder for them. We had to sort them manually. The documentation was also structurally wrong.
Experiment 2: I turned on a setting to hide external types. This worked for some things. It reduced the payload to 60 Webpack types. But TypeDoc ignored nested dependencies. It left many types hidden while they still used memory.
Experiment 3: I tried to map types back to their original modules. This caused a recursive loop. The plugin kept extracting nested interfaces until it created 630 types. The documentation was full of noise. It ruined the user experience.
The Solution: Early Garbage Collection
I needed the types in their correct modules without the extra noise. I stopped trying to fix the output and started fixing the process.
I used a hook called EVENT_RESOLVE_END. This let me intercept the AST right after resolution. I did this before TypeDoc assigned categories or used heavy memory.
My logic followed three steps:
- Find the internal module in the AST.
- Check every node using a custom utility.
- Delete the noise. I used project.removeReflection to delete 300 unneeded nodes immediately. This let Node.js free the memory.
- Move the important types. I merged the remaining 300 types into the root scope.
The result: I saved 240 vital types. They are now visible and correctly routed. I avoided the recursive noise of the third experiment.
Lessons Learned
• Handle ASTs early. Removing unneeded nodes prevents memory waste later. • Use hooks instead of hacks. Understanding the compiler lifecycle allows for clean work. • Feedback improves architecture. Maintainer reviews helped me build a better system. • More data is not better. Good engineering means finding the balance between data and usability.
Oltre le esportazioni mancanti: costruire un garbage collector nelle fasi iniziali per l'AST di TypeDoc in Webpack
Nel mondo dello sviluppo web moderno, strumenti come Webpack e TypeDoc sono diventati indispensabili. Webpack gestisce il bundling dei moduli, mentre TypeDoc genera documentazione accurata partendo dai tipi TypeScript. Tuttavia, quando questi due mondi si incontrano in progetti di grandi dimensioni, emerge un problema di prestazioni spesso trascurato: l'overhead dell'AST (Abstract Syntax Tree) di TypeDoc.
Il problema dell'AST di TypeDoc
TypeDoc crea una rappresentazione dettagliata del codice sorgente per generare la documentazione. Questa rappresentazione, l'AST, è estremamente ricca di informazioni. Sebbene questa ricchezza sia necessaria per una documentazione completa, essa porta con sé un peso significativo in termini di memoria e tempo di elaborazione.
Il problema principale non è solo la dimensione dell'AST, ma la presenza di una quantità enorme di nodi che non sono necessari per la documentazione finale. Questi nodi includono:
- Simboli interni non esportati.
- Strutture di dati temporanee.
- Metadati che non influenzano l'API pubblica.
Perché le "esportazioni mancanti" non bastano
Molti sviluppatori si concentrano sul risolvere il problema delle "esportazioni mancanti" (missing exports), assicurandosi che tutto ciò che deve essere documentato sia effettivamente esportato. Sebbene questo sia fondamentale per la correttezza della documentazione, non affronta la causa principale dell'inefficienza: l'accumulo di dati inutilizzati all'interno dell'albero stesso.
Anche se esportiamo solo ciò che serve, l'AST di TypeDoc continua a mantenere riferimenti a nodi che non sono parte dell'API pubblica, gonfiando il grafo delle dipendenze e rallentando i processi di build di Webpack.
Costruire un Garbage Collector per l'AST
Per mitigare questo problema, l'idea è quella di implementare un "garbage collector" (raccoglitore di rifiuti) che operi nelle fasi iniziali del processo di build. L'obiettivo è identificare e rimuovere i nodi dell'AST che non sono raggiungibili dal grafo delle esportazioni principali.
L'approccio
Il nostro garbage collector deve seguire questi passaggi:
- Identificazione del Root: Determinare il punto di ingresso dell'API pubblica (le esportazioni principali).
- Traversata del Grafo: Utilizzare un algoritmo di ricerca (come BFS o DFS) per marcare tutti i nodi raggiungibili a partire dal root.
- Rimozione dei Nodi non Marcati: Scansionare l'intero AST e rimuovere tutti i nodi che non sono stati marcati durante la traversata.
Esempio di implementazione (Concettuale)
Ecco un esempio di come potremmo strutturare la logica di rimozione utilizzando un approccio basato su plugin:
// Esempio concettuale di rimozione nodi
function pruneAST(ast: TypeDocAST) {
const reachableNodes = new Set<Node>();
const rootExports = ast.getPublicExports();
// Fase 1: Marcatura
for (const exportNode of rootExports) {
markReachable(exportNode, reachableNodes);
}
// Fase 2: Potatura
ast.traverse((node) => {
if (!reachableNodes.has(node)) {
node.remove();
}
});
}
Risultati e Performance
Implementando questo tipo di ottimizzazione, si possono ottenere benefici significativi:
- Riduzione dell'impronta di memoria: L'AST occupa meno spazio in RAM durante la build.
- Tempi di build più rapidi: Meno nodi da elaborare significa una generazione della documentazione più veloce.
- Migliore integrazione con Webpack: Riducendo il carico di lavoro di TypeDoc, Webpack può gestire il processo di bundling in modo più fluido.
Conclusione
Andare oltre la semplice gestione delle esportazioni e affrontare direttamente la struttura dell'AST è un passo fondamentale per ottimizzare i flussi di lavoro di build complessi. Un garbage collector nelle fasi iniziali per l'AST di TypeDoc non è solo un'ottimizzazione, ma una necessità per i progetti moderni su larga scala.