Dal 31 marzo 2015 il transpiler JavaScript che prima si chiamava 6to5 diventa Babel e, con la release 5.0.0, apre un’API pubblica per i plugin: il codice di trasformazione, prima cablato nel core, ora si estende dall’esterno. Il post di rilascio ufficiale lo documenta. Il passaggio è netto: da convertitore ES6→ES5 a infrastruttura per trasformazioni sintattiche generiche.
Contesto
ECMAScript 2015 (ES6) è agli ultimi passi: il sesto draft di ECMA-262 è in fase di candidatura e la General Assembly di Ecma lo adotterà a giugno 2015. Le novità sintattiche sono molte — let/const, arrow function, classi, template literal, destructuring, moduli — ma i runtime di produzione, browser correnti e versioni di Node.js diffuse, parlano ES5. Per scrivere ES6 oggi e farlo girare ovunque serve un passo di compilazione da sorgente a sorgente.
A coprire questo caso c’è 6to5, partito a settembre 2014 da Sebastian McKenzie. Il nome però inchioda il progetto a una sola transizione di versione, e a febbraio 2015 cambia in Babel. Il post not-born-to-die (15 febbraio 2015) lo spiega senza giri di parole: con ES6 ribattezzato ECMAScript 2015 e un ciclo di standardizzazione annuale alle porte, un tool che si chiama “da 6 a 5” nasce già con la scadenza addosso. Il nuovo obiettivo è offrire una base di parsing e trasformazione che altri strumenti possano riusare — minificatori, beautifier, linter, strumenti di code coverage.
Architettura
Babel lavora in tre stadi. Il sorgente passa per un parser — un fork di acorn esteso per coprire la sintassi ES6+ e JSX — che produce un Abstract Syntax Tree (AST). L’albero entra in una fase di trasformazione, dove un visitor percorre i nodi e applica le riscritture. Alla fine un generator serializza l’AST risultante in codice JavaScript target, con la possibilità di affiancargli una source map che riconduce ogni posizione del file generato alla riga e alla colonna del sorgente originale.
La novità della 5.0 sta nella fase intermedia, riscritta e resa estendibile. Un plugin è una funzione che riceve l’oggetto Babel e restituisce un visitor: un insieme di metodi indicizzati per tipo di nodo (ArrowFunctionExpression, ClassDeclaration, e così via) che l’attraversamento richiama uno a uno. Dentro un visitor ispezioni e modifichi il nodo corrente, sostituisci sottoalberi, inserisci o togli dichiarazioni. Le trasformazioni che convertono ES6 in ES5 girano sullo stesso meccanismo aperto agli autori esterni; non c’è una corsia interna privilegiata, separata da quella pubblica.
Sopra questi tre stadi sta .babelrc, un file di configurazione JSON che tutte le integrazioni del progetto leggono allo stesso modo: il require hook babel/register, babel-node, il loader per Webpack babel-loader, il transform per Browserify babelify. Prima della 5.0 la configurazione andava ripetuta a ogni punto di ingresso; con il file unico le opzioni — preset di trasformazioni, plugin attivi, stage del linguaggio — stanno in un solo posto, alla radice del progetto.
Punto critico
La scelta di design più discutibile della release riguarda le proposte sperimentali. Nelle versioni 6to5 le feature non ancora standard si attivavano con un’opzione experimental binaria: o tutto o niente. La 5.0 la toglie e adotta gli stage del processo TC39, i cinque livelli (da 0, strawman, a 4, finished) con cui il comitato classifica la maturità di ogni proposta.
Il problema sta nel default: in Babel 5.0 le proposte allo stage 2 o superiore sono attive senza configurazione esplicita. Stage 2 vuol dire “draft” — una proposta dalla semantica abbozzata, non ancora stabile, esposta a modifiche incompatibili nei round successivi del comitato. Sintassi come le class properties (es7.classProperties), i decorator (es7.decorators) e le export extension (es7.exportExtensions) finiscono così dentro il perimetro di default. Chi le adotta scrive codice contro una specifica in movimento: una mossa del comitato su uno di questi stadi può rompere la build, e l’allineamento futuro con la sintassi che verrà eventualmente standardizzata non è garantito. È una scelta che mette l’accesso anticipato alle feature davanti alla stabilità del sorgente, e in un codebase di produzione va fatta sapendo cosa comporta.
Implicazioni
Ora che il visitor è esposto, si sposta il confine di ciò che puoi fare senza forkare lo strumento. Un’organizzazione che voglia, poniamo, riscrivere in automatico certe chiamate di funzione, iniettare strumentazione o vietare alcuni costrutti sintattici già in compilazione, lo fa come plugin caricato via .babelrc, non più con una patch al core. Lo stesso AST e lo stesso attraversamento che servono per il down-level di ES6 diventano disponibili per trasformazioni applicative arbitrarie.
C’è anche un effetto meno evidente, legato al cambiamento dell’hoisting degli import nella 5.0: gli import non si compilano più in linea dove compaiono, ma vengono sollevati in cima al modulo, secondo la semantica dei moduli ES6. È un adeguamento alla specifica, ma cambia l’ordine di esecuzione rispetto al codice scritto: un effetto collaterale che ti aspetti a valle di un import può ora scattare prima di istruzioni che, sul testo, lo precedono. Conviene controllarlo quando aggiorni da una 6to5 precedente.
Limiti
La traspilazione risolve la sintassi, non il runtime. Convertire una class ES6 in una funzione costruttore ES5 dà codice che gira su un motore vecchio, ma metodi come Array.prototype.includes o oggetti come Promise e Map appartengono alla libreria standard, non alla grammatica: dalla trasformazione dell’AST non escono, e vanno forniti a parte con un polyfill caricato a runtime. Conviene tenere distinte le due cose — quello che il transpiler riscrive e quello che il polyfill aggiunge — per non dare per scontato che il codice traspilato giri su un browser dove, in realtà, quei metodi di libreria mancano.
Resta il costo del passo di build. Adottare Babel significa che il sorgente eseguito non è quello scritto; le source map ricuciono il debug, ma la pipeline va mantenuta, versionata e resa riproducibile. Su progetti che girano solo su runtime già aggiornati il guadagno può non valere la complessità in più — una valutazione da fare caso per caso, non un default.
https://babeljs.io/blog/2015/03/31/5.0.0 https://babeljs.io/blog/2015/02/15/not-born-to-die https://babeljs.io/blog/2015/01/12/6to5-esnext https://www.infoq.com/news/2015/02/babel-new-name-for-6to5/ https://262.ecma-international.org/6.0/
Immagine di copertina: Logo wordmark di Babel: la scritta “babel” in giallo oro su sfondo trasparente — logo di Henry Zhu, CC BY-SA 4.0 — https://commons.wikimedia.org/wiki/File:Babel_Logo.svg