Saltar al contenido principal

Transformación de código sin configuración con babel-plugin-macros

· 7 min de lectura
Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Babel comenzó como un transpilador para escribir la última versión de la especificación ECMAScript y ejecutarla en entornos que aún no implementaban esas características. Pero se ha convertido en mucho más que eso. "Los compiladores son los nuevos frameworks" dice Tom Dale y no podría estar más de acuerdo. Estamos viendo cada vez más optimizaciones en tiempo de compilación para bibliotecas y frameworks. No me refiero a extensiones sintácticas del lenguaje, sino a transformaciones de código simples que habilitan patrones difíciles de lograr de otra manera.

Una de mis cosas favoritas de los plugins de compilador es que permiten optimizar simultáneamente la experiencia del usuario y del desarrollador. (Lee más sobre "Cómo escribir plugins personalizados para Babel y ESLint puede aumentar la productividad y mejorar la experiencia de usuario").

Sin embargo, tengo algunos problemas con los plugins de Babel:

  1. Pueden causar confusión porque al revisar código en un proyecto, quizás no sepas que hay un plugin transformando ese código.

  2. Requieren configuración global o externa (en un .babelrc o configuración de webpack).

  3. Pueden generar conflictos confusos debido a que todos los plugins de Babel se ejecutan simultáneamente (en un único recorrido del AST de Babel).

Estos problemas se resolverían si pudiéramos importar plugins de Babel y aplicarlos directamente en nuestro código. Esto haría la transformación más explícita, eliminaría la necesidad de añadirlos a la configuración y el orden seguiría la secuencia de importación. ¡¿No sería genial?!

Presentando babel-plugin-macros 🎣

¡Adivina qué! ¡Existe una herramienta así! babel-plugin-macros es un nuevo plugin de Babel que permite hacer exactamente lo que describimos. Es un enfoque "nuevo" para la transformación de código. Permite tener transformaciones de código importables sin configuración. La idea vino de Sunil Pai y captó mi atención en este issue de create-react-app.

¿Y cómo se ve? ¡Vaya! Ya existen varios paquetes de babel-plugin-macros que puedes probar hoy mismo.

Aquí un ejemplo real usando preval.macro para incrustar un SVG en una aplicación universal construida con Next.js:

JavaScript
// search.js
// this file runs in the browser
import preval from 'preval.macro'
import glamorous from 'glamorous'

const base64SearchSVG = preval.require('./search-svg')
// this will be transpiled to something like:
// const base64SearchSVG = 'PD94bWwgdmVyc2lv...etc...')

const SearchBox = glamorous.input('algolia_searchbox', props => ({
backgroundImage: `url("data:image/svg+xml;base64,${base64SearchSVG}")`,
// ...
}))


// search-svg.js
// this file runs at build-time only
// because it's required using preval.require function, which is a macro!
const fs = require('fs')
const path = require('path')

const svgPath = path.join(__dirname, 'svgs/search.svg')
const svgString = fs.readFileSync(svgPath, 'utf8')
const base64String = new Buffer(svgString).toString('base64')

module.exports = base64String

¿Qué tiene de interesante? Bueno, la alternativa se vería igual que el ejemplo anterior excepto que:

  1. Sería menos explícita porque no habría import preval from 'preval.macro' en el código fuente.

  2. Debes agregar babel-plugin-preval a tu configuración de Babel.

  3. Necesitas actualizar tu configuración de ESLint para permitir la variable preval como global.

  4. Si configuras mal babel-plugin-preval, obtendrás un críptico error en tiempo de ejecución como: Uncaught ReferenceError: preval is not defined.

Al usar preval.macro con babel-plugin-macros, no tenemos ninguno de esos problemas porque:

  1. La importación está presente y se usa explícitamente.

  2. babel-plugin-macros debe agregarse a tu configuración, pero solo una vez, luego puedes usar todas las macros que desees (¡incluso macros locales!).

  3. No necesitas actualizar la configuración de ESLint porque es explícito.

  4. Si configuras mal babel-plugin-macros, obtendrás un mensaje de error en tiempo de compilación mucho más amigable que indica cuál es el problema real y te dirige a la documentación.

¿Qué es realmente? La versión resumida es que babel-plugin-macros es una forma más sencilla de escribir y usar transformaciones de Babel.

Ya existen varias macros babel-plugin-macros publicadas que puedes usar, incluyendo preval.macro, codegen.macro, idx.macro, emotion/macro, tagged-translations/macro, babel-plugin-console/scope.macro, y glamor 🔜.

Otro ejemplo

babel-plugin-macros es una forma de tener plugins de Babel sin configuración para transformaciones no sintácticas. Muchos plugins de Babel existentes podrían implementarse como macros. Aquí hay otro ejemplo de babel-plugin-console que expone una versión macro de sí mismo:

JavaScript
import scope from 'babel-plugin-console/scope.macro'

function add100(a) {
const oneHundred = 100
scope('Add 100 to another number')
return add(a, oneHundred)
}

function add(a, b) {
return a + b;
}

Ahora, cuando se ejecuta ese código, la función scope hace cosas bastante ingeniosas:

Navegador:

Consola del navegador con alcance add100

Node:

Node console scoping add100

¿Genial, verdad? Y usarlo es como usar cualquier otra dependencia, excepto que tiene todos los beneficios mencionados anteriormente.

Conclusión

Creo que apenas hemos comenzado a explorar lo que babel-plugin-macros puede hacer. Espero que podamos integrarlo en create-react-app para que los usuarios de create-react-app tengan aún más poder sin configuración adicional. Estoy muy emocionado de ver más plugins de Babel que expongan una macro además de la funcionalidad de plugin que ya tienen. No puedo esperar a ver a la gente crear macros específicas para las necesidades de sus proyectos.

Crear macros es incluso más fácil que un plugin regular de Babel, pero requiere cierto conocimiento sobre ASTs y Babel. Si esto es nuevo para ti, hay algunos recursos disponibles para ti 😀

¡Buena suerte a todos! 👋

PD: Debo mencionar que las macros de lenguaje no son un concepto nuevo en absoluto. La capacidad de enseñar nuevos trucos a un lenguaje existe desde hace mucho tiempo. De hecho, ya existe una herramienta así para JavaScript e incluso una implementada como plugin de Babel. Sin embargo, babel-plugin-macros adopta un enfoque ligeramente diferente. Mientras que las macros tradicionalmente se asociaban con definir nueva sintaxis para un lenguaje, ese no es el objetivo de babel-plugin-macros en absoluto. En el caso de babel-plugin-macros, se trata más de transformaciones de código.