Saltar al contenido principal

Lanzamiento 7.7.0: Recuperación de errores y TypeScript 3.7

· 10 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 →

¡Hoy lanzamos Babel 7.7.0!

Esta versión incluye nuevas funcionalidades en el parser como top-level await (await x(), Etapa 3) y declaraciones Flow enum (propuesta Flow). ¡Además, @babel/parser ahora puede recuperarse de ciertos errores de sintaxis!

También hemos añadido soporte para TypeScript 3.7: Babel puede analizar y transformar campos privados de clase con anotaciones de tipo, anotaciones de campos públicos definidos con la palabra clave declare, firmas de funciones de aserción de tipo y literales plantilla en declaraciones enum.

Babel ahora reconoce tres nuevos archivos de configuración: babel.config.json, babel.config.cjs y .babelrc.cjs, que se comportan igual que los archivos babel.config.js y .babelrc.js.

Finalmente, Babel 7.7.0 consume un 20% menos de memoria que la versión 7.6.0.

Puedes leer el changelog completo en GitHub.


¡Un reconocimiento a Alejandro Sánchez, Chris Garrett, 彭驰, Daniel Arthur Gallagher, ExE-Boss, Eugene Myunster, Georgii Dolzhykov, Gerald, Linus Unnebäck, Martin Forsgren, Matthew Whitworth, Micah Zoltu, Mohammad Ahmadi y Samuel Kwok por sus primeras contribuciones (PRs)!

Este lanzamiento también ha sido posible gracias a la colaboración con equipos de otros proyectos open source: gracias a Devon Govett (Parcel) por implementar soporte para archivos babel.config.json, y a George Zahariev (Flow) por añadir declaraciones Flow enum a @babel/parser!

Un agradecimiento especial también para Bloomberg por organizar un Hackatón de Open Source para animar a sus ingenieros a contribuir a la comunidad! En particular, a Robin Ricard y Jaideep Bhoosreddy que están trabajando activamente en automatizar las pruebas de transformaciones de Babel contra la suite Test262.

Si tú o tu empresa quieren apoyar a Babel y la evolución de JavaScript pero no están seguros de cómo hacerlo, pueden donar en OpenCollective y, mejor aún, ¡trabajar directamente con nosotros en la implementación de nuevas propuestas de ECMAScript! Como proyecto impulsado por voluntarios, dependemos del apoyo de la comunidad tanto para financiar nuestros esfuerzos en soportar la amplia gama de usuarios de JavaScript como para asumir la responsabilidad del código. Contacta a Henry en henry@babeljs.io si quieres conversar más al respecto.

Análisis de await de nivel superior (#10449)

La propuesta de await de nivel superior permite usar await en promesas dentro de módulos como si estuvieran envueltas en una gran función asíncrona. Esto es útil, por ejemplo, para cargar dependencias condicionalmente o realizar inicializaciones de aplicaciones:

JavaScript
// Dynamic dependency path
const strings = await import(`./i18n/${navigator.language}.mjs`);

// Resource initialization
const connection = await dbConnector();

@babel/parser ha permitido el uso de await fuera de funciones asíncronas mediante la opción allowAwaitOutsideFunction desde la versión 7.0.0.

La versión 7.7.0 introduce un nuevo plugin de análisis sintáctico topLevelAwait, que tiene diferencias clave:

  • Solo permite await de nivel superior en módulos, no en scripts, según exige la propuesta. Esto es necesario porque sistemas de módulos síncronos basados en scripts (como CommonJS) no pueden manejar dependencias asíncronas.

  • Permite detectar el sourceType correcto cuando se usa sourceType: "unambiguous". Como await es identificador válido en scripts, muchas construcciones aparentemente inequívocas son ambiguas y Babel las analizará como scripts.
    Por ejemplo, await -1 podría ser una expresión que espera -1 o una resta entre await y 1.

Si usas @babel/parser directamente, puedes activar el plugin topLevelAwait:

JavaScript
parser.parse(inputCode, {
plugins: ["topLevelAwait"]
});

También creamos el paquete @babel/plugin-syntax-top-level-await que puedes añadir a tu configuración de Babel:


module.exports = {
plugins: [
"@babel/plugin-syntax-top-level-await"
]
}

Nota: El uso de await de nivel superior requiere soporte en tu empaquetador de módulos. Babel no realiza transformaciones: con Rollup activa experimentalTopLevelAwait, y webpack 5 soporta experiments.topLevelAwait.

Desde esta versión, @babel/preset-env activa automáticamente @babel/plugin-syntax-top-level-await si el caller lo soporta. Nota: babel-loader y rollup-plugin-babel aún no indican soporte, pero estamos trabajando con sus mantenedores.

Recuperación de errores del analizador sintáctico (#10363)

Como otros analizadores de JavaScript, @babel/parser lanza errores ante sintaxis inválida. Esto funciona para Babel, pues transformar programas requiere entradas válidas.

Dada su popularidad, muchas herramientas dependen de @babel/parser: especialmente babel-eslint y Prettier. Para ellas, un analizador que falle al primer error es subóptimo.

Considera este código inválido por duplicar __proto__:

JavaScript
let a = {
__proto__: x,
__proto__: y
}

let a = 2;

El flujo actual con ESLint y Prettier es:

  1. Prettier no puede formatear el archivo

  2. ESLint reporta error Redefinition of __proto__ property

  3. Eliminas el segundo __proto__

  4. Prettier no puede formatear el archivo

  5. ESLint reporta Identifier 'a' has already been declared

  6. Eliminas el segundo let

  7. Prettier formatea el archivo

¿No sería mejor así?

  1. Prettier formatea el archivo

  2. ESLint reporta dos errores: Redefinition of __proto__ property e Identifier 'a' has already been declared

  3. Eliminas la segunda propiedad __proto__ y la segunda palabra clave let

En esta versión, agregamos una nueva opción a @babel/parser: errorRecovery. Cuando se activa, el AST resultante incluirá una propiedad errors con todos los errores de los que @babel/parser pudo recuperarse:

JavaScript
const input = `
let a = {
__proto__: x,
__proto__: y
}

let a = 2;
`;

parser.parse(input); // Throws "Redefinition of __proto__ property"

const ast = parser.parse(input, { errorRecovery: true });
ast.errors == [
SyntaxError: "Redefinition of __proto__ property",
SyntaxError: "Identifier 'a' has already been declared",
];

@babel/parser aún puede lanzar errores ya que no todos son recuperables actualmente. ¡Seguiremos mejorando estos casos!

Nuevas extensiones de archivos de configuración (#10501, #10599)

Babel 6 solo admitía un archivo de configuración: .babelrc, cuyo contenido debía especificarse en JSON.

Babel 7 cambió el significado de los .babelrc e introdujo dos nuevos archivos de configuración: babel.config.js y .babelrc.js (puedes leer sobre sus diferencias en la documentación). Agregamos archivos JavaScript para permitir lógica personalizada al habilitar/deshabilitar plugins y opciones.

Sin embargo, una gran ventaja de los archivos JSON es su mejor cacheabilidad. Un mismo archivo JavaScript puede producir valores diferentes en ejecuciones distintas, mientras que un JSON siempre evalúa al mismo objeto. Además, las configuraciones JSON son fácilmente serializables, a diferencia de valores JavaScript como funciones u objetos con datos implícitos.

Nota: Babel también almacena en caché transformaciones con configuraciones JavaScript, pero el archivo debe evaluarse (para verificar validez del caché) y el caché requiere configuración manual.

Por estas razones, Babel 7.7.0 introduce soporte para babel.config.json, con comportamiento idéntico a babel.config.js.

També́n añadimos soporte para dos archivos de configuración: babel.config.cjs y .babelrc.cjs, necesarios al usar la opción "type": "module" de Node en package.json (Babel no soporta módulos ECMAScript en configuraciones). Aparte de esta diferencia del "type": "module", se comportan igual que babel.config.js y .babelrc.js.

TypeScript 3.7 (#10543, #10545)

TypeScript 3.7 RC incluye encadenamiento opcional, operador de fusión nula, funciones de aserción, declaraciones de campos solo-tipo y más características tipadas.

El encadenamiento opcional (a?.b) y fusión nula (a ?? b) ya eran compatibles en Babel desde 7.0.0 mediante @babel/plugin-proposal-optional-chaining y @babel/plugin-proposal-nullish-coalescing-operator.

En Babel 7.7.0 puedes usar funciones de aserción y declare en campos de clase:

function assertString(x): assert x is string {
if (typeof x !== "string") throw new Error("It must be a string!");
}

class Developer extends Person {
declare usingBabel: boolean;
}

Para evitar cambios disruptivos, el soporte para declare en campos de clase requiere el flag "allowDeclareFields" en @babel/plugin-transform-typescript y @babel/preset-typescript. Probablemente será comportamiento predeterminado, por lo que recomendamos migrar tu configuración:

{
"presets": [
["@babel/preset-typescript", {
"allowDeclareFields": true
}]
]
}

Usar object spread en JSX compilado (#10572)

Al usar propiedades spread en elementos JSX, Babel inyecta un helper de runtime por defecto:

JSX
<a x {...y} />

// 🡇 🡇 🡇

function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }

React.createElement("a", _extends({
x: true
}, y));

En 2016, al mejorar el soporte para ES6 nativo, añadimos la opción useBuiltIns en @babel/plugin-transform-react-jsx permitiendo usar directamente Object.assign y eliminar código redundante:

JSX
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", Object.assign({
x: true
}, y));

Sin embargo, dado el soporte nativo para la propagación de objetos, esto nos permite generar código aún más optimizado:

JSX
<a x {...y} />

// 🡇 🡇 🡇

React.createElement("a", { x: true, ...y });

Puedes activarlo usando la opción useSpread con @babel/preset-react o @babel/plugin-transform-react-jsx:

{
presets: [
["@babel/react", { useSpread: true }]
]
}

Mejoras en el uso de memoria (#10480)

Desde el inicio, hemos realizado esfuerzos (#433, #3475, #7028, etc.) para mejorar el rendimiento. Babel 7.7.0 ahora usa un 20% menos de memoria y transforma archivos grandes un 8% más rápido en comparación con la versión 7.6.0.

Para lograr estos resultados, optimizamos diversas operaciones realizadas durante el ciclo de vida de los objetos NodePath (utilizados para envolver cada nodo AST):

  1. Ahora evitamos inicializar algunas propiedades de objeto poco utilizadas hasta que sean necesarias, lo que nos permite evitar una asignación Object.create(null) para casi cada nodo AST.

  2. Redujimos la carga de trabajo de seguimiento para cada visita de nodo, reemplazando algunas propiedades poco comunes con getters para que @babel/traverse pueda omitir actualizarlas.

  3. Optimizamos el uso de memoria comprimiendo varias propiedades booleanas utilizadas para representar el estado de un recorrido de nodo (es decir, omitido, detenido o eliminado) en un bit array.

Todas estas mejoras se suman a la siguiente diferencia en el rendimiento de transformación y uso de memoria:

PerformanceMemory usage

También puedes consultar los datos sin procesar de los gráficos anteriores. Si deseas leer más sobre este tema, puedes revisar el análisis detallado de Jùnliàng sobre los cambios que implementó para obtener estas mejoras!