Saltar al contenido principal

7.4.0 Publicado: core-js 3, métodos privados estáticos y aplicación parcial

· 13 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 publicamos Babel 7.4.0!

Esta versión incluye soporte para TypeScript 3.4, la propuesta de aplicación parcial en llamadas a funciones y métodos privados estáticos.

Hemos añadido soporte para expresiones entre paréntesis significativas en @babel/parser, ¡y además lo hemos hecho más compatible con la especificación que nunca!

Por último pero no menos importante, tanto @babel/preset-env como @babel/transform-runtime ahora soportan core-js@3, ¡y @babel/template tiene una nueva y dulce sintaxis!

Puedes leer el changelog completo en GitHub.


Un agradecimiento especial a todos los nuevos contribuidores de Babel 😊. Desde que empezamos a generar los changelogs de versiones usando GitHub Actions no habíamos tenido la oportunidad de agradecerles en cada lanzamiento, ¡pero desde Babel 7.3.3 ha habido mucha gente!

Muchas características de esta versión se han desarrollado en colaboración con nuestros patrocinadores. Bloomberg ha contribuido con soporte para un nuevo tipo de elemento privado en cada lanzamiento desde la 7.0 (7.1, 7.2, 7.3), ¡y ahora han implementado métodos privados estáticos! Solo quedan pendientes los getters y setters privados estáticos.

De manera similar, Trivago (un patrocinador de Base Support en OpenCollective) se encargó de la implementación del plugin de aplicación parcial.

Durante el último mes, hemos estado experimentando con trabajar de forma más directa con empresas en varias características/optimizaciones que beneficiarían a la comunidad: RunKit ha estado patrocinando a Nicolò para implementar soporte de placeholders en @babel/template.

Cuando gestionas un gran proyecto de código abierto, no todo es código: necesitamos administrar servidores, integración continua, cuentas de redes sociales y... ¡muchas contraseñas! Realmente agradecemos a 1Password por aceptarnos en su programa de soporte para open source y proporcionarnos una cuenta gratuita de 1Password Teams.

Si tú o tu empresa quieren apoyar a Babel y la evolución de JavaScript pero no están seguros de cómo, pueden donar a través de OpenCollective o, 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 para financiar nuestros esfuerzos y mantener el código. ¡Contáctate con Henry en henry@babeljs.io si quieres conversar más!

core-js 3 (#7646)

Hemos recibido muchos elogios por nuestro trabajo en @babel/preset-env, pero gran parte de ese crédito realmente corresponde al increíble trabajo de Denis. Él mantiene core-js, que proporciona todos los polyfills cargados por @babel/polyfill, @babel/runtime y @babel/preset-env.

Acabamos de lanzar core-js@3, que incluye muchas características nuevas: puedes leer sobre ellas en "core-js@3, babel and a look into the future". Además de todas las nuevas propuestas, ahora es posible aplicar polyfills a métodos de instancia usando @babel/plugin-transform-runtime, permitiendo su uso en navegadores antiguos sin contaminar el entorno global:

JavaScript
// 'foo' could be either a string or an array, or a custom object
foo.includes("a");

// ⮕

import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";

_includesInstanceProperty(foo).call(foo, "a");

@babel/preset-env y @babel/plugin-transform-runtime ahora admiten polyfills para propuestas: solo necesitas usar corejs: { version: 3, proposals: true } en lugar de corejs: 3 en tu configuración. ¡Recuerda que las propuestas de ECMAScript son inherentemente inestables y podrían cambiar en core-js@4!

Anteriormente, @babel/preset-env dependía completamente de datos de compat-table para determinar qué polyfills cargar para un entorno específico. core-js@3 introduce su propio conjunto de datos de compatibilidad con una suite de pruebas exhaustiva que debería proporcionar polyfills mucho más precisos.

Migración desde core-js@2

Dado que las versiones 2 y 3 de core-js son incompatibles entre sí (¡no queremos romper tu código!), la versión 3 no está habilitada por defecto.

  • If you are using @babel/preset-env, you need to enable the corejs: 3 option:

    JavaScript
    presets: [
    ["@babel/preset-env", {
    useBuiltIns: "usage", // or "entry"
    corejs: 3,
    }]
    ]

    Don't forget to update your installed version of core-js!

    npm install --save core-js@3

    When using core-js 3, the useBuiltIns: "entry" option not only transforms import "core-js" imports, but also regenerator-runtime/runtime and all the nested core-js entry points.

    For example, when targeting Chrome 72, it will apply this transformation:

    Input
    JavaScript
    import "core-js/es";
    import "core-js/proposals/set-methods";
    import "core-js/features/set/map";
    Output
    JavaScript
    import "core-js/modules/es.array.unscopables.flat";
    import "core-js/modules/es.array.unscopables.flat-map";
    import "core-js/modules/es.object.from-entries";
    import "core-js/modules/esnext.set.difference";
    import "core-js/modules/esnext.set.intersection";
    import "core-js/modules/esnext.set.is-disjoint-from";
    import "core-js/modules/esnext.set.is-subset-of";
    import "core-js/modules/esnext.set.is-superset-of";
    import "core-js/modules/esnext.set.map";
    import "core-js/modules/esnext.set.symmetric-difference";
    import "core-js/modules/esnext.set.union";

    Unlike when using core-js 2, it doesn't transform @babel/polyfill imports because when used directly that package loads version 2 of core-js.

  • Si usas @babel/plugin-transform-runtime, necesitas usar la opción corejs: 3:

    JavaScript
    plugins: [
    ["@babel/transform-runtime", {
    corejs: 3,
    }]
    ]

    Puedes eliminar @babel/runtime-corejs2, pero debes instalar @babel/runtime-corejs3:

    npm remove @babel/runtime-corejs2
    npm install --save @babel/runtime-corejs3
  • @babel/polyfill no es un plugin ni un preset, sino un paquete de runtime: si añadiéramos una opción para cambiar entre core-js@2 y core-js@3, ambas versiones del paquete tendrían que incluirse en tu bundle. Por esta razón, decidimos marcarlo como obsoleto: ahora deberías cargar core-js para los polyfills, y regenerator-runtime/runtime si estás transformando generadores:

    JavaScript
    // before
    import "@babel/polyfill";

    // after
    import "core-js/stable";
    import "regenerator-runtime/runtime";

    Esto te permite cargar cualquier versión que desees y actualizar ambos paquetes de forma independiente.

    Si te interesa, puedes revisar el código fuente antiguo de @babel/polyfill para core-js@2: packages/babel-polyfill/src/index.js.

Aplicación Parcial (#9343 y #9474)

Esta versión incluye soporte tanto en @babel/parser como en la transformación para la propuesta de aplicación parcial, actualmente en Etapa 1 (última presentación en julio de 2018). Todo el trabajo de implementación fue realizado por Behrang Yarahmadi, patrocinado por Trivago.

Esta nueva característica permite vincular algunos argumentos y el receptor this de funciones, similar al método existente Function#bind pero con menos limitaciones.

JavaScript
const half = divide(?, 2); // half = num => divide(num, 3)
half(6); // 3


element.addEventListener(
"click",
// handleEvent will be called with the correct "this".
this.handleEvent("click", ?) // e => this.handleEvent("click", e)
);

También es muy útil junto con la propuesta del operador pipeline (especialmente al usar las variantes "minimal" o "F-sharp"), ya que evita el uso de muchas funciones flecha:

JavaScript
let newScore = player.score
|> add(?, 7)
|> clamp(0, 100, ?);

// Without this proposal:
let newScore = player.score
|> (_ => add(_, 7))
|> (_ => clamp(0, 100, _));

Puedes probarlo añadiendo @babel/plugin-proposal-partial-application a tu configuración, o activando el preset stage 1 en el REPL online!

nota

Aunque la propuesta también describe aplicación parcial para literales de plantilla etiquetados, esto no se implementó porque probablemente se eliminará.

Métodos privados estáticos (#9446)

JavaScript
class Person {
static #is(obj) {
return obj instanceof Person;
}

constructor(name) {
if (Person.#is(name)) {
throw "It is already a person!";
}
}
}

¡Nuevamente gracias a Tim (Bloomberg) por implementar esta propuesta!

Si ya usas métodos privados de instancia, puedes usar esta nueva característica sin configuración adicional. De lo contrario, añade @babel/plugin-proposal-private-methods a tu lista de plugins. En el REPL online está activado con el preset stage-3.

¡El soporte para características privadas de clases está a solo un paso de completarse! 😄

Class PrivateInstanceStatic
Fields
class A { #a = 1 }
7.0.07.1.0
Methods
class A { #a() {} }
7.2.07.4.0
Accessors
class A { get #a() {} }
7.3.0✖️

Soporte para TypeScript 3.4 RC (#9529 y #9534)

TypeScript 3.4 RC se lanzó hace unos días, ¡y gracias a Tan Li Hau ya es compatible con Babel!

Hay dos nuevas características para anotaciones de tipos: contextos const (que marcan un objeto como "inmutable profundo") y el modificador readonly para arreglos y tuplas.

JavaScript
const student = {
name: "Joe Blogs",
marks: [25, 23, 30]
} as const;

const vowels: readonly string[] = ["a", "e", "i", "o", "u"];

Ten en cuenta que TypeScript 3.4 RC no es una versión estable, por lo que deberías esperar hasta el lanzamiento oficial de TypeScript 3.4: puedes suscribirte al blog de TypeScript para recibir notificaciones cuando esté disponible. 🙂

Expresiones entre paréntesis (#8025)

Los paréntesis generalmente no son significativos para los compiladores de JavaScript o generadores de código: solo son "pistas" que indican al parser que ciertos nodos tienen diferente precedencia a la predeterminada:

Code1 + 2 * 3 / 1 + (2 * 3)(1 + 2) * 3
AST structure

Cuando se genera el AST, la precedencia de las operaciones se determina por la estructura del árbol y no por los paréntesis originales: por esta razón Babel no los rastreaba.

Al imprimir un AST, @babel/generator no tiene conocimiento sobre el formato original y solo genera paréntesis donde son necesarios.

Existen situaciones donde esto causa problemas a los usuarios. Por ejemplo, al usar Google Closure Compiler, los paréntesis marcan expresiones de conversión de tipo:

JavaScript
/** @type {!MyType} */ (valueExpression)

Ya teníamos un nodo ParenthesizedExpression para representar paréntesis, pero nunca lo generaba @babel/parser y solo podía inyectarse mediante plugins personalizados. Gracias al trabajo de Erik Arvidsson, ¡ahora puedes usar la opción createParenthesizedExpressions en el parser para rastrearlos automáticamente!

Code1 + (2 * 3)(1 + 2) * 3
AST structure

Cumplimiento de especificaciones en @babel/parser

Daniel está haciendo que @babel/parser cumpla cada vez más con la especificación ECMAScript: ahora pasa el 98.97% de las pruebas en la suite Test262. 😎

Esta versión hace que @babel/parser conozca las reglas de alcance de JavaScript: ahora sabe qué variables están declaradas, si hay conflictos, si están elevadas (hoisted) o no, y si una construcción sintáctica específica está permitida en su contexto.

Todos estos ejemplos inválidos ahora se reportan correctamente como errores, evitando la necesidad de invalidarlos manualmente en cada herramienta que usa @babel/parser:

JavaScript
let a, a; // Duplicate declaration 💥

if (foo) {
if (bar) { var b }
let b; // Duplicate declaration, again 💥
}

export { Foo }; // Error, Foo is not declared ❓

class C {
constructor() {
super(); // Super in a non-derived class 🤔
}

method(d, d) {} // Duplicate parameter 💥
}

Marcadores de posición en código (#9364)

El código no siempre está destinado a ser escrito directamente por humanos: ¿qué pasa si se necesita generar código usando una plantilla predefinida?

Las plantillas se usan frecuentemente para generar código HTML, ya sea con lenguajes como PHP o motores como Handlebars:

<!-- PHP -->
<section>
<h1><?= $title ?></h1>
<main><?= $body ?></main>
</section>

<!-- Handlebars -->
<section>
<h1>{{ title }}</h1>
<main>{{ body }}</main>
</section>

Si has desarrollado algún plugin de Babel, probablemente usaste @babel/template: una utilidad que permite hacer lo mismo pero generando código JavaScript:

JavaScript
const buildAssign = template`
var NAME = VALUE;
`;

const result = buildAssign({
NAME: varName,
VALUE: varValue,
});

Hasta ahora, @babel/template usaba identificadores en mayúsculas como "marcadores de posición" que luego debían reemplazarse. Aunque este enfoque funcionaba bien en muchos casos, tenía ciertas limitaciones:

  • Por defecto, todo identificador en mayúsculas se marcaba como placeholder y @babel/template arrojaba error si no se reemplazaba.

  • No era posible colocar un marcador donde no se permite un identificador, como en el cuerpo de una función o declaraciones exportadas.

Para resolver estos problemas, introdujimos un nuevo elemento sintáctico que puede reemplazar cualquier nodo: %%placeholder_name%%.

JavaScript
const buildLazyFn = template`
function %%name%%(%%params%%) {
return function () %%body%%;
}
`;

const result = buildLazyFn({
name: functionName,
params: functionParams,
body: functionBody,
});

Esta funcionalidad fue patrocinada por Stripe (a través de Runkit). Estamos experimentando con nuevas formas de patrocinio para Babel, y por primera vez una empresa financió directamente la implementación de una característica pagando a un miembro del equipo. Si tu empresa quiere patrocinar la implementación de una propuesta de ECMAScript o mejoras generales en Babel, ¡contáctanos!


Comentar en Twitter