Planificación para la versión 7.0
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Si aún no lo sabían, ¡estamos planeando lanzar la versión 7.0 pronto 🙌! El trabajo realmente comenzó en febrero, cuando simplemente quería hacer un lanzamiento para eliminar el soporte de Node 0.10/0.12 y quitar babel-runtime junto con otros códigos. Desde entonces, hemos realizado lanzamientos hasta alpha.20.
Actualizaremos esta publicación durante los lanzamientos beta
Al seguir siendo un proyecto voluntario, ha sido difícil para la mayoría del equipo mantener el enfoque y la motivación para implementar estos cambios y continuar manteniendo un proyecto del que dependen tantas empresas, bootcamps y herramientas. Mientras tanto, hemos logrado avances significativos: reuniones/notas semanales, participación como invitados en TC39 durante los últimos encuentros, facilitación tanto de RGSoC como de GSoC, y creación de un Open Collective.
Actualización
Para la mayoría de proyectos, actualizar debería ser tan simple como cambiar las dependencias en tu
package.jsona7.0.0-beta.0. Durante todo el desarrollo de la 7.0, la hemos usado en el propio Babel (qué metalingüístico) y en mi lugar de trabajo en Behance.
Fijaremos todas las dependencias a versiones exactas hasta el lanzamiento oficial.
{
"devDependencies": {
"babel-cli": "7.0.0-beta.0"
}
}
Paquetes específicos:
Details
babel packages in the monorepo should all be >= 7.0.0-beta.0
babel-preset-env should be at least 2.0.0-beta.0
babel-eslint can be >= 8.0.0
babel-loader should be >= 7.0.0 (out of beta since it uses babel-core as a peerDependency)
grunt-babel can be >= 7.0.0
gulp-babel can be >= 7.0.0
rollup-plugin-babel can be >= 3.0.2
Consulta nuestra guía de actualización y otra guía específica para creadores de herramientas, que actualizaremos según sea necesario.
Quiero repasar algunos cambios notables para nuestro primer lanzamiento beta (aún tiene un alcance mucho menor en cambios con rupturas de compatibilidad comparado con el lanzamiento previo de la 6.0).
Reiteración de los objetivos del proyecto
Antes de continuar, quiero reiterar cuál es el propósito de Babel.
Desde que Babel pasó de llamarse 6to5, los navegadores han implementado más partes de la especificación y los usuarios se sienten más cómodos usando las últimas herramientas de sintaxis y construcción. Sin embargo, no debería sorprender que los objetivos de Babel no hayan cambiado mucho.
Nuestros dos objetivos van de la mano:
-
Ayudar a los desarrolladores a transformar nueva sintaxis en código compatible con versiones anteriores (abstraer la compatibilidad con navegadores)
-
Ser un puente para que TC39 reciba retroalimentación sobre nuevas propuestas de ECMAScript y la comunidad tenga voz en el futuro del lenguaje.
Por lo tanto, creo que es quedarse corto al decir que Babel es una parte vital de la comunidad JavaScript (casi 10 millones de descargas mensuales de babel-core) y que necesita urgentemente apoyo. (Las únicas charlas que he intentado dar tratan este punto: JSConf EU, React Rally, TC39). Recientemente planteé: "¿Qué pasa si Babel muere"? ¿Qué ocurre cuando el grupo actual interesado en este proyecto se aburre/sufre desgaste/pasa a otras cosas? (¿Y si ya sucedió?). ¿Vamos a hacer algo al respecto? No quiero simplemente pedirles que nos ayuden, ustedes ya son nosotros como usuarios del proyecto.
¡Bien, entonces hablemos de algunos cambios!
Eliminación del soporte para versiones de Node no mantenidas: 0.10, 0.12, 5 (#4315)
El avance en proyectos de código abierto a menudo implica costos de actualización para sus usuarios. Por esta razón, siempre hemos sido cautelosos al introducir cambios importantes que requieran un incremento de versión mayor. Al dejar de dar soporte a versiones no mantenidas de Node, no solo podemos realizar mejoras en la base de código, sino también actualizar dependencias y herramientas (ESLint, Yarn, Jest, Lerna, etc).
👓 Actualizaciones de propuestas/Cumplimiento de especificaciones
También conocidas como las únicas cosas que a la mayoría les importa 😅
Filosofía (Propuestas: especificación, modo flexible, comportamiento predeterminado)
Hemos creado un nuevo repositorio: babel/proposals para seguir nuestro progreso en las diversas Propuestas de TC39 y reuniones.
También añadí una sección sobre cómo aceptamos nuevas propuestas. Nuestro enfoque básico es que comenzaremos a aceptar PRs para cualquier propuesta que un champion de TC39 vaya a presentar (Etapa 0). Y las actualizaremos (¡con su ayuda!) cuando la especificación cambie.
Naturalmente, aprovecharemos para ser lo más compatibles posible con la especificación (dentro de una velocidad razonable) como comportamiento predeterminado. Esto significa que si necesitas una compilación más rápida/ligera, deberías usar la opción loose que ignorará intencionalmente ciertos cambios de especificación como comprobaciones en tiempo de ejecución y otros casos extremos. La razón por la que es optativa es porque esperamos que sepas lo que haces, mientras que otros deberían poder actualizar babel-preset-env sin problemas para usar la versión nativa de cada sintaxis o dejar de usar Babel por completo.
Etapa 3: Propiedades de clase (desde Etapa 2)
babel-plugin-transform-class-properties: el comportamiento predeterminado ahora es lo que antes era la opción "spec", que usaObject.definePropertyen lugar de asignación simple.
Esto actualmente rompe el plugin de decoradores legacy (que convertimos en el plugin "transform-decorators" en 7.0) si intentas decorar una propiedad de clase. Necesitarás usar la opción
loosepara ser compatible con la versión actual de decoradores hasta que lancemos el plugin de decoradores de Etapa 2.
Campos privados están en desarrollo: #6120
Entrada
class Bork {
static a = 'foo';
x = 'bar';
}
Salida (predeterminada)
class Bork {
constructor() {
Object.defineProperty(this, "x", {
configurable: true,
enumerable: true,
writable: true,
value: 'bar'
});
}
};
Object.defineProperty(Bork, "a", {
configurable: true,
enumerable: true,
writable: true,
value: 'foo'
});
Salida (modo flexible)
class Bork {
constructor() {
this.x = 'bar';
}
};
Bork.a = 'foo';
Etapa 3: Operador de propagación y descanso en objetos (desde Etapa 2)
babel-plugin-transform-object-rest-spread: Ahora el plugin maneja claves no-string (ej: Number/Symbol)
Entrada
// Rest Properties
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// Spread Properties
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
También prohibido
var { ...{ x } } = obj;
var { ...[ y ] } = obj;
Etapa 3: Enlace opcional en catch (nuevo)
babel-plugin-transform-optional-catch-binding: permite a los desarrolladores usar try/catch sin crear un enlace no utilizado.
Entrada
try {
throw 0;
} catch {
doSomethingWhichDoesNotCareAboutTheValueThrown();
}
Salida
try {
throw 0;
} catch (_unused) {
doSomethingWhichDoesNotCareAboutTheValueThrown();
}
Etapa 3: Expresiones regulares con propiedades Unicode (nuevo)
babel-plugin-transform-unicode-property-regex: compila secuencias de escape de propiedades Unicode (\p{…}y\P{…}) en expresiones regulares compatibles con ES5 o ES6 que funcionen en entornos actuales.
Entrada
var regex = /\p{ASCII_Hex_Digit}/u;
Salida
var regex = /[0-9A-Fa-f]/;
Etapa 3: BigInt (nuevo, incompleto)
babel-plugin-transform-bigint: #6015. No se incluirá en los presets Stage porque envolver todos los operadores sería lento.
Entrada
50000n + 60n;
Salida
import babelCheckBinaryExpressions from "babel-check-binary-expressions";
babelCheckBinaryExpressions(new BigInt("50000"), new BigInt("60"), "+");
Etapa 3: Importación dinámica (desde Etapa 2)
babel-plugin-syntax-dynamic-import: Solo necesitas analizar la sintaxis ya que herramientas como Webpack pueden manejar la transformación en lugar de Babel. También existe un plugin para Node
Entrada
const testModule = import('test-module');
Etapa 2: import.meta (solo sintaxis)
Propiedad meta válida solo sintácticamente en módulos, destinada a meta-información sobre el módulo actual proporcionada por el entorno de ejecución.
Entrada
const size = import.meta.scriptElement.dataset.size || 300;
Etapa 2: Separadores numéricos (nuevo)
babel-plugin-transform-numeric-separator: hace literales numéricos más legibles creando una separación visual (_) entre grupos de dígitos.
Entrada
1_000_000_000
0b1010_0001_1000_0101
0xA0_B0_C0
Salida
1000000000
0b1010000110000101
0xA0B0C0
Etapa 2: Decoradores (desde Etapa 1), aún en desarrollo
No permitido
// no computed decorator keys
@dec[foo]
class A {}
// no parameter decorators (a separate proposal)
class Foo {
constructor(@foo x) {}
}
// no decorators on object methods
var o = {
@baz
foo() {}
}
// decorator cannot be attached to the export
@foo
export default class {}
Válido
// decorators with a call expression
@foo('bar')
class A {
// decorators on computed methods
@autobind
[method](arg) {}
// decorators on generator functions
@deco
*gen() {}
// decorators with a member expression
@a.b.c(e, f)
m() {}
}
// exported decorator classes
export default @foo class {}
No soportado (en desarrollo)
// decorated class properties
class A {
@dec name = 0
}
Etapa 2: function.sent (nuevo)
babel-plugin-transform-function-sent: compila la propiedad metafunction.sent, usada dentro de funciones generadoras.
Entrada
function* generator() {
console.log("Sent", function.sent);
console.log("Yield", yield);
}
const iterator = generator();
iterator.next(1); // Logs "Sent 1"
iterator.next(2); // Logs "Yield 2"
Salida
let generator = _skipFirstGeneratorNext(function* () {
const _functionSent = yield;
console.log("Sent", _functionSent);
console.log("Yield", yield);
});
Etapa 2: export-ns-from
babel-plugin-transform-export-namespace: atajo para importar/reexportar un espacio de nombres. Separado del antiguotransform-export-extensionsque combinaba esta propuesta con otra.
Entrada
export * as ns from "mod";
Salida
import * as ns from "mod";
export {ns};
Etapa 1: export-default-from
babel-plugin-transform-export-default: atajo para importar/reexportar algo. Separado del antiguotransform-export-extensionsque combinaba esta propuesta con otra.
Entrada
export v from "mod";
Salida
import _v from "module";
export { _v as v };
Etapa 1: Encadenamiento opcional (nuevo)
babel-plugin-transform-optional-chaining: el operador (?.) permite acceder a propiedades de objetos anidados sin preocuparse por objetos intermedios indefinidos.
Entrada
a?.b = 42;
Salida
var _a;
(_a = a) == null ? void 0 : _a.b = 42;
ES2015: new.target
babel-plugin-transform-new-target: anteriormente no implementamos soporte paranew.target, pero ahora existe un plugin que se incluirá en los presets ES2015/env.
Ejemplo
// with a function
function Foo() {
console.log(new.target);
}
Foo(); // => undefined
new Foo(); // => Foo
// with classes
class Foo {
constructor() {
console.log(new.target);
}
}
class Bar extends Foo {
}
new Foo(); // => Foo
new Bar(); // => Bar
Entrada
class Foo {
constructor() {
new.target;
}
test() {
new.target;
}
}
Salida
class Foo {
constructor() {
this.constructor;
}
test() {
void 0;
}
}
🚀 Nueva característica
.babelrc.js
Los archivos de configuración *.js son comunes en el ecosistema JavaScript. ESLint y Webpack permiten archivos .eslintrc.js y webpack.config.js, respectivamente.
Escribir archivos de configuración en JavaScript permite configuraciones dinámicas, posibilitando un único archivo que se adapte programáticamente a diferentes entornos.
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
var plugins = [];
if (env === 'production') {
plugins.push.apply(plugins, ["a-super-cool-babel-plugin"]);
}
module.exports = { plugins };
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
module.exports = {
plugins: [
env === 'production' && "another-super-cool-babel-plugin"
].filter(Boolean)
};
Esto se hacía previamente mediante la opción de configuración env, que ahora está obsoleta. Consulta abajo para más detalles.
TypeScript
¡Ahora puedes usar babel-preset-typescript para que Babel elimine tipos, similar a cómo funciona babel-preset-flow!
{
"presets": ["typescript"]
}
Estamos trabajando con el equipo de TypeScript en una guía para configurar TypeScript y Babel, que debería completarse antes del lanzamiento oficial de la versión 7.0. En resumen, configuras TS con --noEmit o lo usas en modo editor/watch para poder usar preset-env y otros plugins de Babel.
Anotación "Pura" en transformaciones específicas para minificadores
Tras #6209, las clases ES6 transpiladas incluirán un comentario /*#__PURE__*/ que minificadores como Uglify y babel-minify pueden usar para eliminación de código muerto. Estas anotaciones podrían extenderse también a nuestras funciones auxiliares.
Entrada
class C {
m(x) {
return 'a';
}
}
Salida
var C = /*#__PURE__*/ function () {
function C() {
_classCallCheck(this, C)
}
C.prototype.m = function m(x) {
return 'a';
};
return C;
}();
😎 Otros Cambios Rompedores
Eliminación de babel-preset-flow de babel-preset-react
Este cambio fue importante porque recibimos muchas quejas de usuarios que no usaban tipos/flow y terminaban escribiendo JS inválido sin errores de sintaxis por usar el preset de react.
Además, ahora tenemos el preset de TypeScript, por lo que ya no tenía sentido incluir flow en el preset de react.
Integraciones
Paquetes como grunt-babel, gulp-babel, rollup-plugin-babel, etc. solían tener babel-core como dependencia.
Tras la v7, planeamos mover babel-core a peerDependency como hace babel-loader. Esto evita que estos paquetes necesiten versiones mayores cuando la API de babel-core no cambia. Por eso ya están publicados como 7.0.0, sin esperar más cambios.
Meta
Eliminación de babel-runtime de nuestras dependencias (#5218)
Babel tiene pocas dependencias externas, pero en 6.x cada paquete dependía de babel-runtime para disponer de built-ins como Symbol, Map y Set sin polyfill. Al cambiar el soporte mínimo a Node v4 (que incluye estos built-ins nativamente), podemos eliminar esta dependencia.
Esto era problema en npm 2 (no recomendado con Babel 6) y yarn antiguo, pero no en npm 3 gracias a su deduplicación.
En Create React App el tamaño de node_modules cambiaba drásticamente al hoistear babel-runtime.
-
node_modulespara npm 3: ~120MB -
node_modulespara Yarn (≤0.21.0): ~518MB -
node_modulespara Yarn (≤0.21.0) conbabel-runtimehoisteado: ~157MB
Aunque esto se solucionó "upstream" con npm ≥3/yarn moderno, podemos contribuir eliminando nuestra dependencia de babel-runtime.
Publicación Independiente de Paquetes Experimentales (#5224)
Mencioné esto en El Estado de Babel en la sección
Versioning. Issue en GitHub
Quizás recuerdes que después de Babel 6, Babel se convirtió en un conjunto de paquetes npm con su propio ecosistema de presets y plugins personalizados.
Sin embargo, desde entonces siempre hemos usado un sistema de versionado "fijo/sincronizado" (para que ningún paquete esté en v7.0 o superior). Cuando hacemos un nuevo lanzamiento como v6.23.0, solo los paquetes con código actualizado se publican con la nueva versión, mientras que los demás permanecen igual. Esto funciona en la práctica principalmente porque usamos ^ en nuestros paquetes.
Desafortunadamente, este sistema requiere que todas las versiones mayores se lancen para todos los paquetes cuando uno solo lo necesita. Esto implica hacer muchos cambios rupturistas pequeños (innecesarios) o agrupar muchos cambios rupturistas en un único lanzamiento. En su lugar, queremos diferenciar entre paquetes experimentales (Stage 0, etc.) y el resto (es2015).
Esto significa que planeamos hacer incrementos de versión mayor para cualquier plugin de propuestas experimentales cuando la especificación cambie, en lugar de esperar a actualizar todo Babel. Cualquier cosa < Stage 4 podría tener cambios rupturistas mediante incrementos de versión mayor, igual que los presets Stage si no los eliminamos por completo.
Por ejemplo:
Imagina que usas preset-env (que se mantiene actualizado e incluye es2015, es2016, es2017) + un plugin experimental. También decides usar object-rest-spread porque es genial.
{
"presets": ["env"],
"plugins": ["transform-object-rest-spread"]
}
Si la especificación de una propuesta experimental cambia, deberíamos poder hacer un cambio rupturista e incrementar solo la versión mayor de ese plugin. Como solo afecta a ese plugin, no perjudica nada más y los usuarios pueden actualizar cuando puedan. Solo queremos asegurarnos de que actualicen a la última versión de las propuestas experimentales cuando sea posible, y proporcionar herramientas para automatizarlo si es razonable.
💀 Posibles Deprecaciones
Deprecar la opción "env" en .babelrc
.babelrcbabel/babel#5276 EDIT: Cambiamos el comportamiento para hacerlo más intuitivo y no lo eliminamos.
La opción de configuración "env" (no confundir con babel-preset-env) ha generado confusión entre nuestros usuarios, como muestran los numerosos issues reportados.
El comportamiento actual fusiona los valores de configuración con los de nivel superior, lo cual no siempre es intuitivo. Los desarrolladores terminan dejando vacío el nivel superior y duplicando todos los presets/plugins en entornos separados.
Para eliminar la confusión (y ayudar a usuarios avanzados), consideramos eliminar por completo la opción env y recomendar el formato de configuración JS propuesto (ver abajo).
Deprecar presets ES20xx (hecho)
Ya deprecamos preset-latest hace tiempo, y ES2016/ES2017 antes Es molesto crear un preset anual (paquete/dependencia extra, problemas con npm package squatting a menos que usemos scoped packages)
¿Por qué los desarrolladores deberían decidir qué preset anual usar? Si deprecamos estos presets, todos pueden usar babel-preset-env, que se actualiza automáticamente según los cambios en la especificación.
🤔 Preguntas
Deprecar/Renombrar/Eliminar presets Stage X (hecho)
EDIT: Lo hicimos y escribimos un post completo para explicarlo.
Muchos en la comunidad (y en el TC39) han expresado preocupaciones sobre los presets de etapa X. Creo que los añadí principalmente para facilitar la migración de Babel 5 a Babel 6 (antes existía una opción "stage").
Aunque queremos una herramienta fácil de usar, resulta que muchas empresas y desarrolladores usan estos presets de "JavaScript aún no oficial" constantemente, incluso en producción. "Stage 0" no transmite el mismo mensaje que babel-preset-dont-use-this-stage-0.
Ariya hizo recientemente una encuesta reveladora que ilustra este punto
Los desarrolladores generalmente desconocen qué características pertenecen a cada versión de JavaScript (y no deberían necesitar saberlo). Pero se convierte en problema cuando creemos que "características" que son meras propuestas ya forman parte del estándar.
Muchos proyectos open source (¡incluyendo Babel mismo 😝!), tutoriales y charlas técnicas usan stage-0. React promueve JSX, propiedades de clase (ahora etapa 3), rest/spread de objetos (etapa 3), y todos asumimos que es JavaScript estándar porque Babel lo compila. Quizá eliminar esta abstracción ayudaría a comprender mejor los compromisos al usar plugins de etapa X.
Además, mantener tu propio preset parece más sencillo que actualizar constantemente el preset de etapa.
Suele verse: "Quiero object rest, que es stage 2, así que habilito stage 2". Pero ahora tienen activadas otras características experimentales que quizá desconozcan y no necesiten. Además, al cambiar las etapas, quienes no usan shrinkwrap o yarn reciben nuevas funcionalidades sin saberlo. Si se cancela una propuesta, podría desaparecer. @glenjamin
Uso de paquetes con ámbito npm (implementado, @babel/x)
Thoughts on @babeljs using npm scoped packages for 7.0?
— Henry Zhu (@left_pad) January 18, 2017
¿Parece que la mayoría que comprendió los paquetes con ámbito estaba a favor?
Ventajas
- Evita conflictos por nombres de paquetes (motivo original de esta propuesta).
Muchos nombres están ocupados (preset-es2016, preset-es2017, 2020, 2040, etc.). Puedes solicitar transferencias, pero es complicado y usuarios podrían confundir paquetes no oficiales.
Desventajas
-
Requiere migrar a nueva sintaxis
-
Soporte limitado en herramientas no-npm (lock-in)
-
Sin contadores de descargas a menos que usemos alias para nombres antiguos
Parece prudente posponerlo; al menos no es un cambio disruptivo pues solo altera nombres.
external-helpers, transform-runtime, babel-polyfill
EDIT: separamos el uso de @babel/runtime y core-js en transform-runtime
"regeneratorRuntime is not defined" - error frecuentemente reportado.
Necesitamos mejores soluciones para built-ins/polyfills.
-
Desarrolladores desconocen qué es regenerator-runtime; solo quieren usar generadores/funciones async.
-
Muchos no entienden por qué se necesita un runtime o por qué Babel no compila
Promise,Object.assignu otros built-ins. -
Confusión entre el plugin
transform-runtimey el runtimebabel-runtime. -
Quejas sobre tamaño de código:
babel-polyfillincluye todos los polyfills (aunque ahora existeuseBuiltIns) y pocos conocenexternal-helpers.
¿Podemos combinar/reemplazar estos paquetes y tener una experiencia más sencilla por defecto?
¿Qué sigue?
Queremos que la comunidad actualice y comparta sus comentarios/reportes. Probablemente habrá mucha actividad inicial que puede ser abrumadora, así que tengan paciencia con nosotros. Agradeceríamos ayuda para priorizar incidencias, escribir documentación/guias de actualización/consejos, y crear codemods que faciliten las migraciones. Como Babel impacta gran parte del ecosistema JavaScript, actualizar podría implicar dependencias con otros plugins comunitarios en npm, no solo un paquete. No vamos a esperar pasivamente un mes esperando que la gente migre: hay mucho trabajo para evitar que la mitad de la comunidad siga en 6.x el próximo año. No queremos dejar proyectos (ni personas) atrás. Dígannos cómo podemos ayudar, y les pedimos que hagan lo mismo por nosotros y la comunidad.
Sostenibilidad del Proyecto
Un reconocimiento a mi equipo en Behance por permitirme dedicar tiempo laboral parcial a Babel; seguimos siendo prácticamente la única empresa que sostiene Babel durante horario de trabajo. Me alegra poder apoyar el proyecto laboralmente, no solo en tiempo libre, y espero que más mantenedores logren esto. (Ojalá seamos ejemplo de cómo empresas pueden apoyar proyectos open source que usan sin "poseer").
Aún no tenemos suficientes fondos en Open Collective para pagar tiempo completo: nuestra donación más alta es $750 de Webflow, y la mensual máxima es $100 de varios individuos/empresas. Necesitamos más apoyo corporativo, como hizo AMP/Google con @jridgewell (reciente incorporación que ya marca diferencia al dedicar horas laborales).
Pregunta si tu empresa puede patrocinar vía Open Collective. Cuéntanos qué falta o cómo involucrarte. Ni siquiera necesitas una razón específica: si te importa sostener este proyecto a futuro, simplemente conecta a tu equipo y participa.
Futuro
Tras 7.0: hay muchas líneas por explorar (planteadas hace años): separar recorrido de AST de los plugins (¿visitantes asíncronos?), AST inmutable, extensiones sintácticas. En infraestructura: integración con test262, pruebas smoke, mejor flujo GitHub de propuesta a transformación, infraestructura de codemods para actualizaciones automáticas, etc.
¡Sigue nuestras discusiones en babel/notes y participa!
¡Gracias!
Esperamos lanzar pronto la versión oficial, pero recuerden: el open source se sostiene con mantenimiento diario constante, no solo con ciclos de lanzamiento promocionados que abandonan a otros. Tomará algo más de tiempo mientras corregimos bugs y actualizamos el ecosistema.