Babel Types by example

BabelJS es una herramienta que siempre me ha interesado. Originalmente 6to5, transformaba Javascript «nuevo» en Javascript «antiguo». Esta era su funcion principal para que la tarea del desarrollador no fuese tan dura al tener que duplicar implementación y aun así soportar navegadores menos actualizados (todos sabemos de quien estamos hablando …).

BABEL

Desde eso ha llovido mucho. Hoy soporta Typescript, JSX y la casi por nadie utilizada sintaxis de Flow. Así como una millonada de plugins disponibles en npm hacen que desarrollar con tu sabor favorito de JS sea más fácil que nunca.

Para progamar un plugin de Babel la propia librería esta dividida en distintos paquetes para ayudar al desarrollo. Aunque para ello tienes que conocer bien la sintaxis de JS, así como las otras sintaxis que también soporta.

Una función es una pieza de código que puede ser llamada en otra parte invocandola con parentesis.

function foo() {
    // 
}

const bar = function () { /* ...*/ }

const biz = () => 24;

Pero no es lo mismo si esta función está declarada directamente, o asignada a una variable, o asignada a una variable y además es una arrow function.

El analizador sintactico (parser) de babel transforma el codigo a una estructura de árbol para transformarla más fácilmente. Este árbol es simplemente un objeto con otros objetos anidados. La estructura se puede visualizar muy bien en ASTExplorer, que es un proyectazo muy util para entender lo que pasa por detrás de la transpilación.

Y aquí llega la parte aun más interesante. El tipo de objeto creado para repesentar una función será una FunctionDeclaration, pero si la estamos asignando es un FunctionExpression. Y bueno para la arrow function es una ArrowFunctionExpression.

Porque sí, no son lo mismo, el programa no sería representado de una manera unívoca si no tuviesemos estos distintos tipos. Todos los tipos que existen en babel están documentados en babel-types, que es el paquete de la librería con funciones para crear y comprobar tipos. Así se facilita mucho el desarrollo de un plugin.

En la documentación oficial explican como se debe usar cada metodo para crear un tipo y que parametros necesita. También si tiene algun alias, si el tipo es de Flow, Typescript o JSX y eso está geniar pero … no es muy didactica.

Para hacer más facil la comprensión de a que se refiere el nombre de cada tipo he publicado una documentación extendida, babel-types-by-example, con uno o varios ejemplos del elemento resaltado en un ejemplo de código.

Documentación de un jsxAttribute en @babel/types by example

Cómo se puede ver, cada tipo es una unidad comprensible por el parser de babel.

Espero que el pequeño «highlight» en cada uno de los elementos sea de ayuda. Para mi, el hecho de encotrar cada uno de los tipos dentro del código está siendo una formación genial en el lenguaje. El proyecto y la documentación de los ejemplos es un trabajo todabía en progreso, pero ya va tomando forma.

No pretende ser una documentación alternativa, ya que el script para generarla se basa en la oficial. Actualmente en versión 7 y pronto en 8 que está en beta. Si el lector tiene feedback sobre el proyecto o quiere contribuir toda ayuda es bienvenida.

Bun va a cambiar las cosas?

Bun bun.sh de la empresa Oven genial juego de palabras no se si tanto como la herramienta que están haciendo.

Bun es Node.js + npm + bundler es una herramienta que llevo probando unos meses para hacer experimentos aqui y allá y la verdad es que es impresionante lo bien que va.

Alguien podria decir que es el nuevo Deno, pero Deno no es package.json compatible, y Bun si lo es, y a demás tiene soporte nativo para JSX y Typescript. Aunque por ahora lo haga igual que Babel.js, solamente eliminando el tipado y ejecutando JS. Solo eso ya es un avance.

Aquí dejo una charla bastante curiosa de Ryan Dahl de porqué empezó con Deno. Tiene un tiempo pero está muy interesante:

Por lo que sé Oven levantaon 7 millones de dolares de financiación para hacer Bun, nada mal. Son aun una startup pequeña pero promete mucho. No son mucha gente, aun están contratando programadores de Zig/C++, porque sí este nuevo JS engine está escrito en Zig, un lenguaje de proposito general y de bajo nivel del cual no conozco ningun otro projecto importante que lo use … ahora hay uno. Pero si tienes curiosidad hay un awesome zig con bastantes cosas.

Todo eso muy bien, pero ¿Va a cambiar Bun las cosas en el mundo JS?

Aun no está claro. Es un buen proyecto con el que jugar y experimentar. Quizas también para tooling, pero hay que tener en cuenta que de los 7897 commits en el momento de escribir esto, 5896 son de Jarred Sumner, el fundador de Oven y creador inicial de Bun.

Por ahora solo hay 7 developers con más de 100 commits, eso nos dice que aun no hay mucha comunidad. Son los empleados de Oven los que principalment contribuyen. Eso no está del todo mal, el proyecto es aun muy nuevo (2 años y medio es poco para un motor de JS de proposito general) y es razonable que esto sea así por ahora.

Como curiosidad me alegra saber que Colin McDonnel (colinhacks) el creador de zod y de la version inicial de tRCP es Developer Advocate en Oven. De hecho es uno de los top 7 devs en Bun. Es bueno para el proyecto, que un developer con bastante notoriedad esté tan involucrado.

Unselect KendoUI Treeview (de verdad)

La mayoría de soluciones para deseleccionar un nodo de un árbol de Kendo UI no me han funcionado. O no funcionan, o dejan el widget en un estado ‘bastardo’ que no permite continuar con el funcionamiento esperado.
Desde la docu oficial dan una solución … que lamentablemente no funciona:

//Unselect all nodes
var treeView = $("#treeView").data("kendoTreeView");
treeView.select($());

Al final casi que ha sido más fácil destripar un poco la libraria. Si tienes instanciado un treeview y devuelves la función select esto es lo que obtienes;

/*
* var tree = $("#treeview").data("kendoTreeView");
* console.log(tree.select);
*/
function (n){var i=this,r=i.element;return arguments.length?(n=e(n,r).closest(H),n.length&&(r.find(".k-state-selected").each(function(){var e=i.dataItem(this);e.set("selected",!1),delete e.selected}),i.dataItem(n).set("selected",!0)),t):r.find(".k-state-selected").closest(H)}

El método .select() es un getsetter dependiendo del arguments.length, bueno pues hay que mirar la parte donde si que le pasas parámetros (ya que primero deselecciona antes de volver a seleccionar).

Deseleccionar los elementos de un árbol de kendo

No podiemos solo eliminar la clase ‘.k-state-selected’ porque en Kendo UI siempre hay un dataSource que mantiene al widget.
Hay que recuperar el dataItem y decirle “deseleccionate”, o lo que viene siendo lo mismo .set(“selected”, false)

false a.k.a. !1

$("#treeview").kendoTreeView();

//Unselect
$("button#deselect").click(function(){
  var tree=$("#treeview").data("kendoTreeView");

  $("#treeview")
    .find(".k-state-selected")
    .each(
      function () {
        var e = tree.dataItem(this);
        e.set("selected", !1);
        delete e.selected;
    });
});
[iframe src=»http://jsbin.com/ebejad/3″ width=»400px» height=»300px» ]

Unselect en Kendo UI TreeView
Hay que tener en cuenta que en Kendo UI se suele seguir el patrón MVVM para tener sincronizado la interfaz con los datos que son representados.

LET – variables locales al ámbito de bloque

Let es una de las muchas mejoras previstas para la versión 6 de EcmaScript que la mayoría de navegadores/máquinas virtuales JS no soportan.

No es una aportación sustancial al lenguaje. Principalmente está descrita para alejar a los nuevos programadores de las malas prácticas que el lenguaje ofrece a los noobs.

SpiderMonkey (JS de Firefox) implementa en gran medida esta funcionalidad. A partir de la versión 1.7 de JavaScript podemos usar estas y otras mejoras, pero siempre es necesario indicar que versión de javascript se debe ejecutar.

Esta instrucción nos permite jugar con variables locales. Esto no es novedad en javascript, lo novedoso es que su ámbito local es el bloque de código, no la función.

En mis pruebas he comprobado que Firefox acepta el type del script como texto o aplicación. Este es el type recomendado, pero ambos funcionan.

<script type="text/javascript;version=1.7">...</script>
<script type="application/javascript;version=1.7">...</script>

-Let como Declaración-

En el primer console.log baz no se le ha asignado valor, pero dentro de ese ámbito ha sido reservada ese identificador.

<script type="text/javascript;version=1.7">
function yeah(){
    var foo="aaa";
    {
        console.log(foo, baz);// aaa, undefined
        let baz="zzz";
        console.log(foo, baz);// aaa, zzz
    }
    console.log(foo, baz);// ReferenceError: baz is not defined
}

yeah();
</script>

En el ámbito de la función no existe la variable baz puesto que es local al bloque. Aunque podemos acceder a las variables de este ámbito de función y de bloque, estos no son el mismo. Con la sentencia Let restringimos el acceso de ámbito al bloque. Con la sentencia var solo se restringe a la función.

Redeclaraciones

No están permitidas, así de simple. Este es un punto de rigidez mayor que con Var donde están completamente permitidas.

En el siguiente ejemplo mientras que con foo no tenemos problemas, con let no están permitidas múltiples declaraciones. Sin mostrar por consola ninguna línea anterior se nos devuelve un error de tipos:

TypeError: redeclaration of variable baz

<script type="text/javascript;version=1.7">
function yeah(){
    var foo="aaa";
    var foo="bbb";
    {
        var foo="ccc";
        let baz="zzz";
        console.log(foo, baz);
        let baz="_";//TypeError: redeclaration of variable baz
    }
    console.log(foo, baz);
}

yeah();
</script>

Sucede de la misma manera si declaramos baz en un bloque interior pero con la declaración var, con lo que las dos variables estarían en conflicto.

<script type="text/javascript;version=1.7">
function yeah(){
    var foo="aaa";
    var foo="bbb";
    {
        var foo="ccc";
        let baz="zzz";
        console.log(foo, baz);
        {
            var baz="_";//TypeError: redeclaration of variable baz
        }
    }
    console.log(foo, baz);
}

yeah();
</script>

Sacando el segundo baz a otro bloque ya no encontramos ningún problema en la nueva declaración. Estamos en otro bloque con distinto ámbito de bloque.

<script type="text/javascript;version=1.7">
function yeah(){
    var foo="aaa";
    var foo="bbb";
    {
        var foo="ccc";
        let baz="zzz";
        console.log(foo, baz);// ccc, zzz
    }
    console.log(foo, baz);// ccc, undefined
    let baz="_";
    console.log(foo, baz);// ccc, _
}

yeah();
</script>

Mezclando variables globales, locales y locales al bloque

Veamos el comportamiento a través de bloques y funciones. Modificamos una variable global en la línea 2 de la función:

<script type="text/javascript;version=1.7">
var a="aaa";
function yeah(){
    console.log(a);// aaa
    a="bbb";
    {
        console.log(a);// undefined
        let a="zzz";
        console.log(a);// zzz
    }
    console.log(a);// bbb
}

yeah();
console.log(a);// bbb
</script>

Dentro del bloque la variable ‘a’ global existe, pero también existe otra variable local a la función llamada ‘a’, solo que aun no se le ha asignado ningún valor.

Una vez asignado el string “zzz” se puede mostrar, pero al ser local al bloque no se puede trabajar con ella fuera del bloque.

Es importante tener en cuenta que un bloque viene definido solamente por llaves ( { } ). Aunque ciertas expresiones como if y for puedan tener efecto para sentencia siguiente sin bloque (se puede entender que está dentro de un bloque indirectamente) el caso de Let es estricto en el bloque definido por llaves.

<script type="text/javascript;version=1.7">
if (false) var x = 9; // legal
if (false) let x = 9; // SyntaxError: let declaration not directly within block
</script>

-Let como Expresión-

Una expresión let limita el alcance de la variable declarada a sólo esa expresión.

De esta manera podemos usar let para usar una variable en una sentencia solamente.

var a = 5;
let(a = 6) alert(a); // 6
alert(a); // 5

Claro que como las funciones son instrucciones, podemos utilizar una para ejecutar todo el código que queramos con determinado valor local

<script type="text/javascript;version=1.7">
var a = 5;
let(a = 6) (function(){
    console.log(a);// 6
    a+=100;
    console.log(a);// 106
})();
console.log(a); // 5
</script>

Pero todos los valores declarados con let seguirán siendo locales a esa expresión, sin existir fuera de ella.

<script type="text/javascript;version=1.7">
var a = 5;
let( a = 6, b=2000 )(function(){
    console.log(a);// 6
    a+=100 + b;
    console.log(a);// 2106
})();
console.log(a); // 5
console.log(b); // ReferenceError: b is not defined
</script>

El uso de Let como expresión de esta última manera lleva de forma natural a pensar en usarla como una sentencia más.

-Let como Sentencia-

La sintaxis es muy parecida a la expresión, y tiene precedentes en otras sentencias como for, while, if, … así que en definitiva podemos decir que es una expresión más que genera un ámbito local.

<script type="text/javascript;version=1.7">
var global_a = 10;
var global_b =20
let( a = global_a, b=2000 ){
    console.log(a);// 10
    a+=100 + b;
    console.log(a);// 2110
    global_b = a/2;
};
console.log(global_a); // 10
console.log(global_b); // 1055
console.log(a); // ReferenceError: a is not defined
</script>

así con Let creamos un ámbito en el que existen variables privadas y locales para ese ámbito.

-Let con otras sentencias-

Ya que tenemos una intrucción que declara variables locales al bloque, porqué no combinarla con las que ya existen.

Si declaramos variables con Let en un for en la parte de inicialización obtendremos variables locales al for.

<script type="text/javascript;version=1.7">
for(let i=0; i<5; i++){
    //...
    console.log(i);// 0 1 2 3 4
}
console.log(i); // ReferenceError: i is not defined
</script>

Así i desaparece al acabar el for, que es cuando acaba el bloque de la sentencia.

Lo mismo sucede en el caso de for..in y for..of para el uso de Let.

En conclusion:

Muchas veces la forma de programar en JS es como es porque sólo define ámbito una función. Usando let podemos jugar con los ámbitos de bloque como en otros lenguajes clásicos. Pero esto no le quita a var sus propiedades tradicionales de definicion de variables.

-Problemas con Let-

Redeclaraciones de parámetros

La redeclaración de un parámetro de la función con Let y con var es un tema pendiente. Las redeclaraciónes con var sobreescriben el valor del parámetro, si, pero las redeclaraciones con var están permitidas en cualquier bloque y función tantas veces como se desee. Con let no es igual.

La redeclaracion de un parámetro con var y su posterior uso modifica el objeto arguments en la posición donde aparece la variable.

function fun(a, b, c){

    var b = 1000;
    return arguments;

}
console.log(fun("a1", "b2", "c3")); // ["a1", 1000, "c3"]

Existe una discusión en la comunidad de si debería ser igual con Let en este caso.

function f(a) {

    let a = 42;
    return arguments[0]

}
console.log(f(7));// PROBLEMA -> 42 ¿debería ser 7 o 42?

Los chicos de Mozilla dicen que sí, por eso en su implementación la redeclaración de parámetros funciona igual en let que en var.

Let en primer nivel

Este si que es un bug real de mozilla y es que Let en primer nivel (nivel de función) se comporta como Var. Permite redeclaraciones, cosa que no debería ser posible. En mozilla tienen documentado el bug (https://bugzilla.mozilla.org/show_bug.cgi?id=589199) y está en sus planes de para completar la implementación de ecmascript 6