ソースを参照

Merge pull request #7 from alpinejs/master

update
Ryan Chandler 4 年 前
コミット
6b4e1aead0

+ 750 - 0
README.es.md

@@ -0,0 +1,750 @@
+# Alpine.js
+
+![npm bundle size](https://img.shields.io/bundlephobia/minzip/alpinejs)
+![npm version](https://img.shields.io/npm/v/alpinejs)
+[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://alpinejs.codewithhugo.com/chat/)
+
+Alpine.js ofrece las propiedades reactivas y declarativas de grandes *frameworks* como Vue o React con un coste mucho menor.
+
+Mantiene el DOM, pudiendo mejorar y perfeccionar el comportamiento como más convenga.
+
+Podríamos considerarlo como un [Tailwind](https://tailwindcss.com/) para JavaScript.
+
+> Nota: La sintaxis de esta herramienta está mayormente inspirada por [Vue](https://vuejs.org/) (y por extensión, de [Angular](https://angularjs.org/))). Estaré agradecido eternamente por lo que han aportado al desarrollo web.
+
+## Instalación
+
+**Desde CDN:** Añade el siguiente script al final de tu sección `<head>`.
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
+```
+
+Eso es todo. Se inicializará solo.
+
+Para entornos de producción, se recomienda especificar una número de versión en concreto en el enlace para evitar comportamientos inesperados que puedan romper las nuevas versiones. Por ejemplo, para usar la versión `2.7.0` (la última):
+```html
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.min.js" defer></script>
+```
+
+**Desde NPM:** Instalar el paquete desde NPM.
+```js
+npm i alpinejs
+```
+
+Incluir en tu script.
+```js
+import 'alpinejs'
+```
+
+**Para soporte en IE11** Utilizar los siguientes scripts.
+```html
+<script type="module" src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js"></script>
+<script nomodule src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine-ie11.min.js" defer></script>
+```
+
+El patrón de arriba es el [module/nomodule pattern](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/), que resultará en un empaquetado moderno cargado automáticamente en navegadores modernos, y el empaquetado para IE11 cargado automáticamente en IE11 y otros navegadores de legado.
+
+## Usar
+
+*Desplegable/Modal*
+```html
+<div x-data="{ open: false }">
+    <button @click="open = true">Abrir Desplegable</button>
+
+    <ul
+        x-show="open"
+        @click.away="open = false"
+    >
+        Cuerpo del Desplegable
+    </ul>
+</div>
+```
+
+*Pestañas*
+```html
+<div x-data="{ tab: 'foo' }">
+    <button :class="{ 'active': tab === 'foo' }" @click="tab = 'foo'">Foo</button>
+    <button :class="{ 'active': tab === 'bar' }" @click="tab = 'bar'">Bar</button>
+
+    <div x-show="tab === 'foo'">Pestaña Foo</div>
+    <div x-show="tab === 'bar'">Pestaña Bar</div>
+</div>
+```
+
+También se puede utilizar para fines no-triviales:
+*Pre-fetching del contenido de un desplegable HTML al pasar el cursor por encima*
+```html
+<div x-data="{ open: false }">
+    <button
+        @mouseenter.once="
+            fetch('/dropdown-partial.html')
+                .then(response => response.text())
+                .then(html => { $refs.dropdown.innerHTML = html })
+        "
+        @click="open = true"
+    >Mostrar Desplegable</button>
+
+    <div x-ref="dropdown" x-show="open" @click.away="open = false">
+        Cargando Ruleta...
+    </div>
+</div>
+```
+
+## Aprender
+
+Hay 14 directivas disponibles:
+
+| Directiva | Descripción |
+| --- | --- |
+| [`x-data`](#x-data) | Declara un nuevo *scope* del componente. |
+| [`x-init`](#x-init) | Ejecuta una expresión cuando un componente se inicializa. |
+| [`x-show`](#x-show) | Alterna `display: none;` en el elemento dependiendo de la expresión booleana (true o false). |
+| [`x-bind`](#x-bind) | Asigna el valor de un atributo a partir de el resultado de una expresión de JS. |
+| [`x-on`](#x-on) | Adjunta un evento *listener* al elemento. Ejecuta una expresión de JS cuando se emite el evento. |
+| [`x-model`](#x-model) | Añade *"two-way data binding"* al elemento. Mantiene la entrada del elemento sincronizado con los datos del componente. |
+| [`x-text`](#x-text) | Funciona similar a `x-bind`, pero actualiza el `innerText` del elemento. |
+| [`x-html`](#x-html) | Funciona similar a `x-bind`, pero actualiza el `innerHTML` del elemento. |
+| [`x-ref`](#x-ref) | Forma conveniente de extraer elementos crudos del DOM del componente. |
+| [`x-if`](#x-if) | Elimina totalmente un elemento del DOM. Debe ser utilizado en una etiqueta `<template>`. |
+| [`x-for`](#x-for) | Crea nuevos nodos en el DOM por cada elemento en un arreglo. Debe ser utilizado en una etiqueta `<template>`. |
+| [`x-transition`](#x-transition) | Directivas para aplicar clases a varias etapas de la transición del elemento. |
+| [`x-spread`](#x-spread) | Permite hacer *bind* de un objeto de las directivas de Alpine a un elemento para mejor reusabilidad. |
+| [`x-cloak`](#x-cloak) | Este atributo se elimina cuando Alpine se inicializa. Útil para ocultar el DOM pre-inicializado. |
+
+Y 6 propiedades mágicas:
+
+| Propiedades Mágicas | Descripción |
+| --- | --- |
+| [`$el`](#el) | Extrae el componente raíz de un nodo del DOM. |
+| [`$refs`](#refs) | Extrae elementos del DOM marcados con `x-ref` dentro del componente. |
+| [`$event`](#event) | Extrae el objeto "Event" del navegador nativo de dentro de un evento *listener*. |
+| [`$dispatch`](#dispatch) | Crea un `CustomEvent` y hace *dispatch* utilizando `.dispatchEvent()` internamente. |
+| [`$nextTick`](#nexttick) | Ejecuta la expresión dada DESPUÉS que Alpine ha hecho los cambios reactivos en las actualizaciones del DOM. |
+| [`$watch`](#watch) | Ejecuta la *callback* provista cuando una propiedad del componente al cual se ha hecho `watch` cambia. |
+
+
+## Sponsors
+
+<img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS">
+
+**¿Quieres tu logo aquí? [Escríbe MP en Twitter](https://twitter.com/calebporzio)**
+
+## Proyectos de la Comunidad
+
+* [AlpineJS Weekly Newsletter](https://alpinejs.codewithhugo.com/newsletter/)
+* [Spruce (State Management)](https://github.com/ryangjchandler/spruce)
+* [Turbolinks Adapter](https://github.com/SimoTod/alpine-turbolinks-adapter)
+* [Alpine Magic Helpers](https://github.com/KevinBatdorf/alpine-magic-helpers)
+* [Awesome Alpine](https://github.com/ryangjchandler/awesome-alpine)
+
+### Directivas
+
+---
+
+### `x-data`
+
+**Ejemplo:** `<div x-data="{ foo: 'bar' }">...</div>`
+
+**Estructura:** `<div x-data="[object literal]">...</div>`
+
+`x-data` declara un nuevo *scope* del componente. Indica al *framework* que debe inicializar un nuevo componente con el objeto especificado.
+
+Es análogo a la propiedad `data` de un componente en Vue.
+
+**Extraer Lógica del Componente**
+
+Se pueden extraer datos (y comportamiento) en funciones reutilizables:
+
+```html
+<div x-data="dropdown()">
+    <button x-on:click="open">Abrir</button>
+
+    <div x-show="isOpen()" x-on:click.away="close">
+        // Desplegable
+    </div>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            show: false,
+            open() { this.show = true },
+            close() { this.show = false },
+            isOpen() { return this.show === true },
+        }
+    }
+</script>
+```
+
+> **Para usuarios de bundler**, notad que Alpine.js accede a funciones que están en el *scope* global (`window`), es necesario asignar explicitamente las funciones a `window` para poder usarlas con `x-data`. Por ejemplo, `window.dropdown = function () {}` (eso pasa porque Webpack, Rollup, Parcel etc. pone las funciones que defines en el *scope* del módulo y no de `window`).
+
+
+También puedes mezclar múltiples tipos de datos usadndo desestructuración de objetos:
+
+```html
+<div x-data="{...dropdown(), ...tabs()}">
+```
+
+---
+
+### `x-init`
+**Ejemplo:** `<div x-data="{ foo: 'bar' }" x-init="foo = 'baz'"></div>`
+
+**Estructura:** `<div x-data="..." x-init="[expression]"></div>`
+
+`x-init` ejecuta una expresión cuando se inicializa un componente.
+
+Si deseas ejecutar código DESPUÉS que Alpine haga las actualizaciones iniciales al DOM (similar al *hook* de `mounted()` en VueJS), puedes devolver un *callback* en `x-init`, y se ejecutará después:
+
+`x-init="() => { // aquí tenemos acceso al estado de post-inicialización del DOM // }"`
+
+---
+
+### `x-show`
+**Ejemplo:** `<div x-show="open"></div>`
+
+**Estructura:** `<div x-show="[expression]"></div>`
+
+`x-show` alterna el estilo `display: none;` del elemento dependiendo de si la expresión evalúa a `true` o `false`.
+
+**x-show.transition**
+
+`x-show.transition` es una API de conveniencia para hacer `x-show`s más agradables utilizando transiciones de CSS.
+
+```html
+<div x-show.transition="open">
+    Estos contenidos entraran y saldrán de transición.
+</div>
+```
+
+| Directiva | Descripción |
+| --- | --- |
+| `x-show.transition` | A simultaneous fade and scale. (opacity, scale: 0.95, timing-function: cubic-bezier(0.4, 0.0, 0.2, 1), duration-in: 150ms, duration-out: 75ms)
+| `x-show.transition.in` | Only transition in. |
+| `x-show.transition.out` | Only transition out. |
+| `x-show.transition.opacity` | Only use the fade. |
+| `x-show.transition.scale` | Only use the scale. |
+| `x-show.transition.scale.75` | Customize the CSS scale transform `transform: scale(.75)`. |
+| `x-show.transition.duration.200ms` | Sets the "in" transition to 200ms. The out will be set to half that (100ms). |
+| `x-show.transition.origin.top.right` | Customize the CSS transform origin `transform-origin: top right`. |
+| `x-show.transition.in.duration.200ms.out.duration.50ms` | Different durations for "in" and "out". |
+
+> Nota: Todas esos modificadores de transiciones se pueden usar conjuntamente con cualquiera de los otros. Esto es posible (aunque ridículo lol): `x-show.transition.in.duration.100ms.origin.top.right.opacity.scale.85.out.duration.200ms.origin.bottom.left.opacity.scale.95`
+
+> Nota: `x-show` espera a que cualquier hijo acabe de salir de la transición. Si quieres evitar este comportamiento, añade el modificador `.immediate`:
+```html
+<div x-show.immediate="open">
+    <div x-show.transition="open">
+</div>
+```
+---
+
+### `x-bind`
+
+> Nota: Eres libre de usar la sintaxis abreviada ":": `:type="..."`
+
+**Ejemplo:** `<input x-bind:type="inputType">`
+
+**Estructura:** `<input x-bind:[attribute]="[expression]">`
+
+`x-bind` asigna el valor de un atributo como el resultado de evaluar una expresión de Javascript. La expresión tiene acceso a todos las claves del objeto de datos del componente, y se actualizará cada vez que se actualizan los datos.
+
+> Nota: *bindings* de atributos SÓLO se actualizan cuando se actualizan las dependencias. El *framework* es lo suficientemente inteligente para observar cambios en los datos y detectar que *bindings* se encargan de esos.
+
+**`x-bind` para atributos de clase**
+
+`x-bind` se comporta un poco distinto cuando hacemos *binding* de un atributo `class`.
+
+Para clases, es necesario pasar un objeto cuyas claves sean los nombres de la clase, y los valores sean expresiones booleanas que determinan si las clases se aplican o no.
+
+Por ejemplo:
+`<div x-bind:class="{ 'hidden': foo }"></div>`
+
+En este ejemplo, la clase "hidden" solo se aplicará cuando el valor del atributo `foo` sea `true`.
+
+**`x-bind` para atributos booleanos**
+
+`x-bind` da soporte a atributos booleanos del mismo modo que funciona para atributos valuables, utilizando una variable como condicion o cualquier expresión de JavaScript que resuelve a `true` o `false`.
+
+Por ejemplo:
+```html
+<!-- Given: -->
+<button x-bind:disabled="myVar">Hazme click</button>
+
+<!-- When myVar == true: -->
+<button disabled="disabled">Hazme click</button>
+
+<!-- When myVar == false: -->
+<button>Click me</button>
+```
+
+Esto añadirá o eliminará el atributo `disabled` cuando `myVar` sea `true` o `false` respectivamente.
+
+Se soportan atributos booleanos de la [especificación de HTML](https://html.spec.whatwg.org/multipage/indices.html#attributes-3:boolean-attribute), por ejemplo `disabled`, `readonly`, `required`, `checked`, `hidden`, `selected`, `open`, etc.
+
+**Modificador `.camel`**
+**Ejemplo:** `<svg x-bind:view-box.camel="viewBox">`
+
+El modificador `camel` hace *binding* del equivalente al nombre del atributo en *camel case*. En el ejemplo de arriba, el valor de `viewBox` se asignará al atributo `viewBox` y no al atributo `view-box`.
+
+---
+
+### `x-on`
+
+> Nota: Eres libre de usar la sintaxis abreviada "@": `@click="..."`
+
+**Ejemplo:** `<button x-on:click="foo = 'bar'"></button>`
+
+**Structure:** `<button x-on:[event]="[expression]"></button>`
+
+`x-on` adjunta un evento *listener* al elemento en el cual se declara. Cuando se emite el evento, se ejecuta la expresion de JavaScript especificada.
+
+Si cualquier dato es modificado en la expresión, otros atributos de elementos "vinculados" con dicho dato, se actualizarán.
+
+> Nota: También se puede especificar el nombre de una función de JavaScript
+
+**Ejemplo:** `<button x-on:click="myFunction"></button>`
+
+Eso es equivalente a: `<button x-on:click="myFunction($event)"></button>`
+
+**Modificador `keydown`**
+
+**Ejemplo:** `<input type="text" x-on:keydown.escape="open = false">`
+
+Puedes especificar teclas en conreto a escuchar utilizando modificadores *keydown* anexados a la directiva `x-on:keydown`. Nótese que los modificadores son versiones *kebab-cased* de los valores de `Event.key`.
+
+Ejemplos: `enter`, `escape`, `arrow-up`, `arrow-down`
+
+> Nota: También puedes escuchar combinaciones de teclas de sistema como: `x-on:keydown.cmd.enter="foo"`
+
+**Modificador `.away`**
+
+**Ejemplo:** `<div x-on:click.away="showModal = false"></div>`
+
+Cuando el modificador `.away` está presente, el evento solo se ejecutara cuando el evento se origina de una fuente distinta al propio elemento o sus hijos.
+
+**Modificador `.prevent`**
+**Ejemplo:** `<input type="checkbox" x-on:click.prevent>`
+
+Añadir `.prevent` en un *listener* de eventos llama a `preventDefault` sobre el evento disparado. En el ejemplo de arriba, esto significa que realmente la casilla no se marcará cuando el usuario haga click en ella.
+
+**Modificador `.stop`**
+**Ejemplo:** `<div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>`
+
+Añadir `.stop` en un *listener* de eventos llama a `stopPropagation` sobre el evento disparado. En el ejemplo de arriba, esto significa que el evento de "click" no saltará hacia el `<div>` exterior. En otras palabras, cuando un usuario pulse el botón, no se asignará `'bar'` a `foo`.
+
+**Modificador `.self`**
+**Ejemplo:** `<div x-on:click.self="foo = 'bar'"><button></button></div>`
+
+Añadir `.self` en un *listener* de eventos hará que el evento solo se dispare si `$event.target` es el propio elemento. En el ejemplo de arriba, esto significa que el evento de "click" que propaga el evento hacia el `<div>`exterior **no** correrá el código indicado.
+
+**Modificador `.window`**
+**Ejemplo:** `<div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>`
+
+Añadir `.window` en un *listener* de eventos instalará el *listener* en el objeto global `window` y no en el nodo del DOM en el que se declara. Esto es útil cuando quieres modificar el estado de un componente cuando algo cambia en `window`, como un evento de redimensión. En este ejemplo, cuando la ventana supera los 768 píxeles de anchura, cierra el modal/desplegable, y en el caso contrario mantiene el mismo estado.
+
+>Nota: También se puede usar el modificador `.document` para adjuntar *listeners* a `document` y no en `window`
+
+**Modificador `.once`**
+**Ejemplo:** `<button x-on:mouseenter.once="fetchSomething()"></button>`
+
+Añadir el modificador `.once` en un *listener* de eventos asegura que el *listener* solo se ejecute una sola vez. Esto es útil para tareas que solo quieres realizar una vez, como un *fetch* parcial de un HTML o similares.
+
+**Modificador `.passive`**
+**Ejemplo:** `<button x-on:mousedown.passive="interactive = true"></button>`
+
+Añadir el modificador `.passive` en un *listener* de eventos hará que el *listener* sea pasivo, lo que significa que `preventDefault()` no funcionará en ninguno de los eventos procesados. Esto puede ayudar, por ejemplo, con el buen desempeño del desplazamiento en dispositivos táctiles.
+
+**Modificador `.debounce`**
+**Ejemplo:** `<input x-on:input.debounce="fetchSomething()">`
+
+El modificador `debounce` permite hacer "debounce" de un evento. En otras palabras, la respuesta al evento NO se ejecutará hasta que haya pasado una cierta cantidad de tiempo desde que el evento se lanzó por última vez. Cuando está listo para ser llamado, se ejecutará la última respuesta.
+
+El valor de "espera" por defecto es de 250 milisegundos.
+
+Para personalizar este valor, es posible especificar una cifra en concreto de la siguiente forma:
+
+```
+<input x-on:input.debounce.750="fetchSomething()">
+<input x-on:input.debounce.750ms="fetchSomething()">
+```
+
+**Modificador `.camel`**
+**Ejemplo:** `<input x-on:event-name.camel="doSomething()">`
+
+El modificador `camel` hace *binding* del equivalente al nombre del evento en *camel case*. En el ejemplo de arriba, la expresión se evaluara cuando se dispare el evento `eventName`.
+
+---
+
+### `x-model`
+**Ejemplo:** `<input type="text" x-model="foo">`
+
+**Estructura:** `<input type="text" x-model="[data item]">`
+
+`x-model` añade *"two-way data binding"* en un elemento. En otras palabras, el valor del elemento de entrada estara sincronizado con el valor del dato en el componente.
+
+> Nota: `x-model` es lo suficientemente inteligente para detectar cambios en inputs de texto, checkboxes, radio buttons, radio buttons, textareas, selects, y multiple selects. Debería comportarse [igual que lo hace Vue](https://vuejs.org/v2/guide/forms.html) en esos escenarios.
+
+**Modificador `.number`**
+**Ejemplo:** `<input x-model.number="age">`
+
+El modificador `number` convierte el valor de entrada a un número. En caso que no se pueda convertir a número, devuelve el valor original.
+
+**Modificador `.debounce`**
+**Ejemplo:** `<input x-model.debounce="search">`
+
+El modificador `debounce` permite añadir "debounce" en la actualización de un valor. En otras palabras, la respuesta al evento NO se ejecutará hasta que haya pasado una cierta cantidad de tiempo desde que se disparó el último evento. Cuando la respuesta está lista para ser llamada, se ejecutará la respuesta al último evento.
+
+El valor de "espera" por defecto es de 250 milisegundos.
+
+Para personalizar este valor, es posible especificar una cifra en concreto de la siguiente forma:
+
+```
+<input x-model.debounce.750="search">
+<input x-model.debounce.750ms="search">
+```
+
+---
+
+### `x-text`
+**Ejemplo:** `<span x-text="foo"></span>`
+
+**Estructura:** `<span x-text="[expression]"`
+
+`x-text` funciona similar a `x-bind`, pero actualiza el `innerText` del elemento en lugar del valor del atributo.
+
+---
+
+### `x-html`
+**Ejemplo:** `<span x-html="foo"></span>`
+
+**Estructura:** `<span x-html="[expression]"`
+
+`x-html` funciona similar a `x-bind`, pero actualiza el `innerHTML` del elemento en lugar del valor del atributo.
+
+> :warning: **Utiliza solo contenido confiable y no elementos introducidos por el usuario.** :warning:
+>
+> Renderizar HTML de terceros dinamicamente puede facilmente llevarnos a vulnerabilidades [XSS](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting).
+
+---
+
+### `x-ref`
+**Ejemplo:** `<div x-ref="foo"></div><button x-on:click="$refs.foo.innerText = 'bar'"></button>`
+
+**Estructura:** `<div x-ref="[ref name]"></div><button x-on:click="$refs.[ref name].innerText = 'bar'"></button>`
+
+`x-ref` permite una forma conveniente de extraer elementos crudos del DOM del componente. Colocando el atributo `x-ref` en un elemento, pasa a estar disponible para todas las respuestas de eventos dentro de un objeto llamado `$refs`.
+
+Esta es una alternativa útil para evitar tener ids y utilizar `document.querySelector` en todos lados.
+
+> Nota: También se puede hacer *bind* dinámico de valores para x-ref: `<span :x-ref="item.id"></span>` en caso de ser necesario.
+
+---
+
+### `x-if`
+**Ejemplo:** `<template x-if="true"><div>Some Element</div></template>`
+
+**Estructura:** `<template x-if="[expression]"><div>Some Element</div></template>`
+
+En casos donde `x-show` no es suficiente (`x-show` pone el elemento con `display: none` si es false) `x-if` se puede utilizar para eliminar un elemento del DOM completamente.
+
+Es importante que `x-if` se use en una etiqueta `<template></template>` porque Alpine no utiliza un DOM virtual. Esta implementación permite a Alpine a ser robusto usando el DOM real y hacer su magia.
+
+> Nota: `x-if` debe tener un único elemento raíz dentro de la etiqueta `<template></template>`.
+
+> Nota: Cuando se usa `template` dentro de una etiqueta `svg`, es necesario añadir un [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) que debe ejecutarse antes que Alpine.js se inicialice.
+
+---
+
+### `x-for`
+**Ejemplo:**
+```html
+<template x-for="item in items" :key="item">
+    <div x-text="item"></div>
+</template>
+```
+
+> Nota: El *binding* `:key` es opcional, pero es ALTAMENTE recomendado.
+
+`x-for` está disponible para casos donde se requiere de crear nuevos nodos en el DOM por cada elemento en un arreglo. Actua similar a `v-for` en Vue, con la exepción que es necesario usarlo con una etiqueta `template` y no un elemento cualquiera.
+
+Si quieres acceder al indice actual de la iteración, utiliza la siguiente sintaxis:
+
+```html
+<template x-for="(item, index) in items" :key="index">
+    <!-- También se puede referenciar a "index" dentro de la iteración si es necesario. -->
+    <div x-text="index"></div>
+</template>
+```
+
+> Nota: `x-for` debe tener un único elemento raíz dentro de la etiqueta `<template></template>`.
+
+> Nota: Cuando se usa `template` dentro de una etiqueta `svg`, es necesario añadir un [polyfill](https://github.com/alpinejs/alpine/issues/637#issuecomment-654856538) que debe ejecutarse antes que Alpine.js se inicialice.
+
+
+#### Anidando `x-for`s
+Se pueden anidar bucles `x-for`, pero se debe envolver cada bucle en un elemento. Por ejemplo:
+
+```html
+<template x-for="item in items">
+    <div>
+        <template x-for="subItem in item.subItems">
+            <div x-text="subItem"></div>
+        </template>
+    </div>
+</template>
+```
+
+---
+
+### `x-transition`
+**Ejemplo:**
+```html
+<div
+    x-show="open"
+    x-transition:enter="transition ease-out duration-300"
+    x-transition:enter-start="opacity-0 transform scale-90"
+    x-transition:enter-end="opacity-100 transform scale-100"
+    x-transition:leave="transition ease-in duration-300"
+    x-transition:leave-start="opacity-100 transform scale-100"
+    x-transition:leave-end="opacity-0 transform scale-90"
+>...</div>
+```
+
+```html
+<template x-if="open">
+    <div
+        x-transition:enter="transition ease-out duration-300"
+        x-transition:enter-start="opacity-0 transform scale-90"
+        x-transition:enter-end="opacity-100 transform scale-100"
+        x-transition:leave="transition ease-in duration-300"
+        x-transition:leave-start="opacity-100 transform scale-100"
+        x-transition:leave-end="opacity-0 transform scale-90"
+    >...</div>
+</template>
+```
+
+> El ejemplo de arriba utiliza clases de [Tailwind CSS](https://tailwindcss.com)
+
+Alpine ofrece 6 formas distintas de directivas de transición para aplicar clases en varias etapas de transición de un elemento, entre los estados "hidden" y "shown". Estas directivas funcionan con ambos `x-show` y `x-if`.
+
+Estas, funcionan exactamente igual que las directivas de transición de VueJS con la diferencia de que tienen distintos nombres y más sensibles:
+
+| Directiva | Descripción |
+| --- | --- |
+| `:enter` | Se aplica durante toda la fase de entrada. |
+| `:enter-start` | Se añade antes que el elemento se inserte y se elimina un fotograma después de que se inserte el elemento. |
+| `:enter-end` | Añadido un fotograma después se inserir el elemento (al mismo tiempo que se elimina `enter-start`), y se elimina cuando la transición/animación finaliza. |
+| `:leave` | Aplicado durante toda la fase de abandono. |
+| `:leave-start` | Añadido inmediatamente cuando se dispara el abandono de la transición, y eliminado después de un fotograma. |
+| `:leave-end` | Añadido un fotograma después de que se dispare el dejar la transición (al mismo tiempo que se elimina `leave-start`), y se elimina cuando la transición/animación finaliza.
+
+---
+
+### `x-spread`
+**Ejemplo:**
+```html
+<div x-data="dropdown()">
+    <button x-spread="trigger">Abrir Desplegable</button>
+
+    <span x-spread="dialogue">Desplegar Contenidos</span>
+</div>
+
+<script>
+    function dropdown() {
+        return {
+            open: false,
+            trigger: {
+                ['@click']() {
+                    this.open = true
+                },
+            },
+            dialogue: {
+                ['x-show']() {
+                    return this.open
+                },
+                ['@click.away']() {
+                    this.open = false
+                },
+            }
+        }
+    }
+</script>
+```
+
+`x-spread` permite extraer los *bindings* de Alpine de un elemento en un objeto reutilizable.
+
+Las claves del objeto son las directivas (puede ser cualquiera, incluyendo modificadores), y los valores son *callbacks* a evaluar por Alpine.
+
+> Note: La única anomalía con x-spread ocurre cuando se usa junto a `x-for`. Cuando la directiva a extender es `x-for`, es necesario retornar una expresion en formato de *string* en el *callback*. Por ejemplo: `['x-for']() { return 'item in items' }`.
+
+---
+
+### `x-cloak`
+**Ejemplo:** `<div x-data="{}" x-cloak></div>`
+
+Los atributos de `x-cloak` se eliminan de los elementos cuando Alpine se inicializa. Esto es util para ocultar elementos pre-inicializados del DOM. Es recomendado añadir el siguiente estilo global para que esto funcione:
+
+```html
+<style>
+    [x-cloak] { display: none; }
+</style>
+```
+
+### Propiedades Mágicas
+
+> Con la excepción de `$el`, las propiedades mágicas **no están disponibles junto a `x-data`** ya que el componente aún no ha sido inicializado.
+
+---
+
+### `$el`
+**Ejemplo:**
+```html
+<div x-data>
+    <button @click="$el.innerHTML = 'foo'">Reemplázame con "foo"</button>
+</div>
+```
+
+`$el` es una propiedad mágica que puede ser utilizada para extraer el nodo DOM del componente raíz.
+
+### `$refs`
+**Ejemplo:**
+```html
+<span x-ref="foo"></span>
+
+<button x-on:click="$refs.foo.innerText = 'bar'"></button>
+```
+
+`$refs` es una propiedad mágica que puede ser utilizada para extraer elementos DOM marcados con `x-ref` dentro del componente. Esto es útil cuando es necesario manipular manualmente elementos del DOM.
+
+---
+
+### `$event`
+**Ejemplo:**
+```html
+<input x-on:input="alert($event.target.value)">
+```
+
+`$event` es una propiedad mágica que puede ser utilizada junto un a un *listener* de eventos para extraer el objeto nativo "Event" del navegador.
+
+> Nota: La propiedad $event sólo está disponible en expresiones del DOM.
+
+Si se requiere acceder a $event dentro de una función de JavaScript puedes pasar el objecto directamente como parámetro:
+
+`<button x-on:click="myFunction($event)"></button>`
+
+---
+
+### `$dispatch`
+**Ejemplo:**
+```html
+<div @custom-event="console.log($event.detail.foo)">
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+    <!-- Al hacer click, hará console.log de "bar" -->
+</div>
+```
+
+**Nota en la Propagación de Eventos**
+
+Nótese que, por el [event bubbling](https://en.wikipedia.org/wiki/Event_bubbling), cuando se necesita capturar eventos enviados desde nodes que están anidado bajo el mismo nivel de jerarquía, es necesario usar el modificador [`.window`](https://github.com/alpinejs/alpine#x-on):
+
+**Ejemplo:**
+
+```html
+<div x-data>
+    <span @custom-event="console.log($event.detail.foo)"></span>
+    <button @click="$dispatch('custom-event', { foo: 'bar' })">
+<div>
+```
+
+> Esto no funcionará porqué cuando se dispara `custom-event`, se propagará hacia el antepasado común, el `div`.
+
+**_Dispatching_ Componentes**
+
+También se puede aprovechar la técnica anterior para hacer que los componentes se comuniquen entre ellos:
+
+**Ejemplo:**
+
+```html
+<div x-data @custom-event.window="console.log($event.detail)"></div>
+
+<button x-data @click="$dispatch('custom-event', 'Hello World!')">
+<!-- Al hacer click, hará console.log de "Hello World!". -->
+```
+
+`$dispatch` es un atajo para crear un evento personalizado `CustomEvent` y enviarlo utilizando `.dispatchEvent()` internamente. Hay muchos casos de uso buenos en donde se requiere pasar los datos entre componentes utilizando eventos personalizados. [Leer esto](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events) para mas información sobre el sistema de `CustomEvent` en los navegadores.
+
+Nótese que cualquier dato que se pasa como segundo parametro de `$dispatch('some-event', { some: 'data' })`, pasa a estar disponible a través de la propiedad "detail" de los nuevos eventos: `$event.detail.some`. Añadir datos de eventos personalizados a la propiedad `.detail` es la práctica estándar para usar `CustomEvent` en navegadores. [Leer esto](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail) para mas información.
+
+También se puede utilizar `$dispatch()` para disparar actualizaciones de los datos para bindings con `x-model`. Por ejemplo:
+
+```html
+<div x-data="{ foo: 'bar' }">
+    <span x-model="foo">
+        <button @click="$dispatch('input', 'baz')">
+        <!-- Después de pulsar el botón, `x-model` captura el evento "input", y actualiza foo a "baz". -->
+    </span>
+</div>
+```
+
+> Nota: La propiedad $dispatch sólo está disponible en expresiones del DOM.
+
+Si necesitas acceder a $dispatch desde dentro de una función de JavaScript, puedes pasarlo como parámetro:
+
+`<button x-on:click="myFunction($dispatch)"></button>`
+
+---
+
+### `$nextTick`
+**Ejemplo:**
+```html
+<div x-data="{ fruit: 'apple' }">
+    <button
+        x-on:click="
+            fruit = 'pear';
+            $nextTick(() => { console.log($event.target.innerText) });
+        "
+        x-text="fruit"
+    ></button>
+</div>
+```
+
+`$nextTick` es una propiedad mágica que permite ejecutar la expresión indicada sólo DESPUÉS que Alpine haga las actualizaciones reactivas del DOM. Esto es útil para las veces que se necesita interactuar con el DOM DESPUÉS que se reflejen todas las actualizaciones que has hecho de los datos.
+
+---
+
+### `$watch`
+**Ejemplo:**
+```html
+<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">
+    <button @click="open = ! open">Toggle Open</button>
+</div>
+```
+
+Puedes hacer "watch" a la propiedad de un componente con el método mágico `$watch`. En el ejemplo de arriba, cuando se pulsa el botón y `open` cambia, la retrollamada provista ejecutara el `console.log` con el nuevo valor.
+
+## Seguridad
+Si encuentras una brecha de seguridad, por favor envía un email a [calebporzio@gmail.com]()
+
+Alpine depende de una implementación personalizada utilizando el objeto `Function` para evaluar las directivas. A pesar de ser más seguro que `eval()`, su uso está prohibido en algunos entornos, tels como Google Chrome App, utilizando Content Security Policy restrictivas (CSP).
+
+Si utilizas Alpine en un sitio web que maneja datos sensibles y requiere [CSP](https://csp.withgoogle.com/docs/strict-csp.html), necesitas incluir `unsafe-eval` en tu política. Una política robusta configurada correctamente ayudará a proteger a tus usuarios cuando utilizan datos personales o financieros.
+
+Ya que la política se aplica a todos los scripts de tu página, es improtante que otras bibliotecas externas incluidas en el sitio web sean revisadas cuidadosamente para asegurar que son confiables y que no intrudicen ninguna vulnerabilidad de Cross Site Scripting ni usando `eval()`ni manipulando el DOM para inyectar código malicioso en tu página.
+
+## V3 Roadmap
+* Migrar de `x-ref` a `ref` para paridad con Vue?
+* Añadir `Alpine.directive()`
+* Añadir `Alpine.component('foo', {...})` (Con el método mágico `__init()`)
+* Enviar eventos de Alpine para "loaded", "transition-start", etc... ([#299](https://github.com/alpinejs/alpine/pull/299)) ?
+* Eliminar síntaxis de "object" (y array) de `x-bind:class="{ 'foo': true }"` ([#236](https://github.com/alpinejs/alpine/pull/236) para añadir soporte a sintaxis de objeto para el atributo `style`)
+* Mejorar `x-for` para reactividad con mutaciones ([#165](https://github.com/alpinejs/alpine/pull/165))
+* Añadir soporte "deep watching" en V3 ([#294](https://github.com/alpinejs/alpine/pull/294))
+* Añadir atajo `$el`
+* Cambiar `@click.away` a `@click.outside`?
+
+## Licencia
+
+Copyright © 2019-2020 Caleb Porzio y colaboradores
+
+Licenciado bajo la licencia MIT, ve [LICENSE.md](LICENSE.md) para más detalles.

+ 2 - 2
README.ja.md

@@ -14,7 +14,7 @@ DOM を保持し、適切な動作を施すことができます。
 
 **CDNより:** `<head>` セクションの最後に次のスクリプトを追加します。
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.6.0/dist/alpine.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.js" defer></script>
 ```
 
 それだけです。初期は自身で行われます。
@@ -120,7 +120,7 @@ IE11 では、ポリフィルを提供する必要があります。次のスク
 
 **例:** `<div x-data="{ foo: 'bar' }">...</div>`
 
-**構造:** `<div x-data="[JSON data object]">...</div>`
+**構造:** `<div x-data="[object literal]">...</div>`
 
 `x-data` は新しいコンポーネントスコープを宣言します。フレームワークに、データオブジェクトを使用して新しいコンポーネントを初期化するよう指示します。
 

+ 18 - 7
README.md

@@ -16,10 +16,11 @@ Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.
 
 | Language | Link for documentation |
 | --- | --- |
-| Japanese | [**日本語ドキュメント**](./README.ja.md) | 
-| Chinese Traditional | [**繁體中文說明文件**](./README.zh-TW.md) | 
-| Russian | [**Документация на русском**](./README.ru.md) | 
-| Portuguese | [**Documentação em Português**](./README.pt.md) | 
+| Japanese | [**日本語ドキュメント**](./README.ja.md) |
+| Chinese Traditional | [**繁體中文說明文件**](./README.zh-TW.md) |
+| Russian | [**Документация на русском**](./README.ru.md) |
+| Portuguese | [**Documentação em Português**](./README.pt.md) |
+| Spanish | [**Documentación en Español**](./README.es.md) |
 
 ## Install
 
@@ -31,9 +32,9 @@ Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.
 That's it. It will initialize itself.
 
 For production environments, it's recommended to pin a specific version number in the link to avoid unexpected breakage from newer versions.
-For example, to use version `2.6.0` (latest):
+For example, to use version `2.7.0` (latest):
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.6.0/dist/alpine.min.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.min.js" defer></script>
 ```
 
 **From NPM:** Install the package from NPM.
@@ -155,7 +156,7 @@ And 6 magic properties:
 
 **Example:** `<div x-data="{ foo: 'bar' }">...</div>`
 
-**Structure:** `<div x-data="[JSON data object]">...</div>`
+**Structure:** `<div x-data="[object literal]">...</div>`
 
 `x-data` declares a new component scope. It tells the framework to initialize a new component with the following data object.
 
@@ -506,6 +507,16 @@ You can nest `x-for` loops, but you MUST wrap each loop in an element. For examp
 </template>
 ```
 
+#### Iterating over a range
+
+Alpine supports the `i in n` syntax, where `n` is an integer, allowing you to iterate over a fixed range of elements.
+
+```html
+<template x-for="i in 10">
+    <span x-text="i"></span>
+</template>
+```
+
 ---
 
 ### `x-transition`

+ 3 - 3
README.pt.md

@@ -23,10 +23,10 @@ Pensem nisso como o [Tailwind](https://tailwindcss.com/) para JavaScript.
 E é isso. Ele vai se inicializar.
 
 Para ambiente de produção, é recomendado fixar o número da versão específico no link para evitar problemas inesperadas das versões mais recentes.
-Por exemplo, para usar a versão `2.6.0`:
+Por exemplo, para usar a versão `2.7.0`:
 
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.6.0/dist/alpine.min.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.min.js" defer></script>
 ```
 
 **Via NPM:** Instale o pacote pelo NPM.
@@ -159,7 +159,7 @@ E 6 propriedades mágicas:
 
 **Exemplo:** `<div x-data="{ foo: 'bar' }">...</div>`
 
-**Estrutura:** `<div x-data="[JSON data object]">...</div>`
+**Estrutura:** `<div x-data="[object literal]">...</div>`
 
 `x-data` declara um novo scope do componente. Diz à estrutura para inicializar um novo componente com o seguinte objeto de dados.
 

+ 2 - 2
README.ru.md

@@ -22,9 +22,9 @@ Alpine.js предлагает вам реактивность и деклара
 Вот и всё. Он инициализируется самостоятельно.
 
 Для продакшн-окружения, рекомедуется использовать ссылку с конкретным номером версии, чтобы избежать неожиданных поломок после выпуска новых версий.
-Например, чтобы использовать версию `2.6.0`:
+Например, чтобы использовать версию `2.7.0`:
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.6.0/dist/alpine.min.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.min.js" defer></script>
 ```
 
 **С помощью NPM:** Установите пакет из NPM.

+ 2 - 2
README.zh-TW.md

@@ -22,9 +22,9 @@ Alpine.js 提供了 Vue 與 React 等大框架的互動式與宣告式的功能
 就這樣。Alpine.js 會自行初始化。
 
 在正式環境中,建議在連結中固定特定版本,以避免新版本使功能無法使用。
-如,要使用 `2.6.0` 版則可以這樣寫:
+如,要使用 `2.7.0` 版則可以這樣寫:
 ```html
-<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.6.0/dist/alpine.min.js" defer></script>
+<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.min.js" defer></script>
 ```
 
 **使用 NPM:** 從 NPM 安裝套件。

+ 31 - 5
dist/alpine-ie11.js

@@ -3613,6 +3613,21 @@
     }
   });
 
+  var nativeReverse = [].reverse;
+  var test$1 = [1, 2];
+
+  // `Array.prototype.reverse` method
+  // https://tc39.github.io/ecma262/#sec-array.prototype.reverse
+  // fix for Safari 12.0 bug
+  // https://bugs.webkit.org/show_bug.cgi?id=188794
+  _export({ target: 'Array', proto: true, forced: String(test$1) === String(test$1.reverse()) }, {
+    reverse: function reverse() {
+      // eslint-disable-next-line no-self-assign
+      if (isArray(this)) this.length = this.length;
+      return nativeReverse.call(this);
+    }
+  });
+
   var $some = arrayIteration.some;
 
 
@@ -5715,7 +5730,7 @@
     if (el.tagName.toLowerCase() !== 'template') {
       console.warn("Alpine: [".concat(directive, "] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#").concat(directive));
     } else if (el.content.childElementCount !== 1) {
-      console.warn("Alpine: <template> tag with [".concat(directive, "] encountered with multiple element roots. Make sure <template> only has a single child node."));
+      console.warn("Alpine: <template> tag with [".concat(directive, "] encountered with multiple element roots. Make sure <template> only has a single child element."));
     }
   }
   function kebabCase(subject) {
@@ -6361,10 +6376,21 @@
   }
 
   function evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, el, iteratorNames, extraVars) {
+    var _this4 = this;
+
     var ifAttribute = getXAttrs(el, component, 'if')[0];
 
     if (ifAttribute && !component.evaluateReturnExpression(el, ifAttribute.expression)) {
       return [];
+    } // This adds support for the `i in n` syntax.
+
+
+    if (isNumeric(iteratorNames.items)) {
+      return Array.from(Array(parseInt(iteratorNames.items, 10)).keys(), function (i) {
+        _newArrowCheck(this, _this4);
+
+        return i + 1;
+      }.bind(this));
     }
 
     return component.evaluateReturnExpression(el, iteratorNames.items, extraVars);
@@ -6397,12 +6423,12 @@
     var nextElementFromOldLoop = currentEl.nextElementSibling && currentEl.nextElementSibling.__x_for_key !== undefined ? currentEl.nextElementSibling : false;
 
     var _loop = function _loop() {
-      var _this4 = this;
+      var _this5 = this;
 
       var nextElementFromOldLoopImmutable = nextElementFromOldLoop;
       var nextSibling = nextElementFromOldLoop.nextElementSibling;
       transitionOut(nextElementFromOldLoop, function () {
-        _newArrowCheck(this, _this4);
+        _newArrowCheck(this, _this5);
 
         nextElementFromOldLoopImmutable.remove();
       }.bind(this), component);
@@ -6536,7 +6562,7 @@
       output = '';
     }
 
-    el.innerText = output;
+    el.textContent = output;
   }
 
   function handleHtmlDirective(component, el, expression, extraVars) {
@@ -7662,7 +7688,7 @@
   }();
 
   var Alpine = {
-    version: "2.6.0",
+    version: "2.7.0",
     pauseMutationObserver: false,
     magicProperties: {},
     onComponentInitializeds: [],

+ 8 - 3
dist/alpine.js

@@ -74,7 +74,7 @@
     if (el.tagName.toLowerCase() !== 'template') {
       console.warn(`Alpine: [${directive}] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#${directive}`);
     } else if (el.content.childElementCount !== 1) {
-      console.warn(`Alpine: <template> tag with [${directive}] encountered with multiple element roots. Make sure <template> only has a single child node.`);
+      console.warn(`Alpine: <template> tag with [${directive}] encountered with multiple element roots. Make sure <template> only has a single child element.`);
     }
   }
   function kebabCase(subject) {
@@ -577,6 +577,11 @@
 
     if (ifAttribute && !component.evaluateReturnExpression(el, ifAttribute.expression)) {
       return [];
+    } // This adds support for the `i in n` syntax.
+
+
+    if (isNumeric(iteratorNames.items)) {
+      return Array.from(Array(parseInt(iteratorNames.items, 10)).keys(), i => i + 1);
     }
 
     return component.evaluateReturnExpression(el, iteratorNames.items, extraVars);
@@ -711,7 +716,7 @@
       output = '';
     }
 
-    el.innerText = output;
+    el.textContent = output;
   }
 
   function handleHtmlDirective(component, el, expression, extraVars) {
@@ -1775,7 +1780,7 @@
   }
 
   const Alpine = {
-    version: "2.6.0",
+    version: "2.7.0",
     pauseMutationObserver: false,
     magicProperties: {},
     onComponentInitializeds: [],

+ 11 - 0
examples/index.html

@@ -304,6 +304,17 @@
                     </td>
                 </tr>
 
+                <tr>
+                    <td>x-for over a range using <code>i in 10</code> syntax</td>
+                    <td>
+                        <div x-data>
+                            <template x-for="i in 10" :key="i">
+                                <span x-text="i"></span>
+                            </template>
+                        </div>
+                    </td>
+                </tr>
+
                 <tr>
                     <td>Transitions</td>
                     <td>

+ 180 - 333
package-lock.json

@@ -1,6 +1,6 @@
 {
     "name": "alpinejs",
-    "version": "2.5.0",
+    "version": "2.6.0",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
@@ -25,19 +25,19 @@
             }
         },
         "@babel/core": {
-            "version": "7.11.1",
-            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz",
-            "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==",
+            "version": "7.11.6",
+            "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz",
+            "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==",
             "dev": true,
             "requires": {
                 "@babel/code-frame": "^7.10.4",
-                "@babel/generator": "^7.11.0",
+                "@babel/generator": "^7.11.6",
                 "@babel/helper-module-transforms": "^7.11.0",
                 "@babel/helpers": "^7.10.4",
-                "@babel/parser": "^7.11.1",
+                "@babel/parser": "^7.11.5",
                 "@babel/template": "^7.10.4",
-                "@babel/traverse": "^7.11.0",
-                "@babel/types": "^7.11.0",
+                "@babel/traverse": "^7.11.5",
+                "@babel/types": "^7.11.5",
                 "convert-source-map": "^1.7.0",
                 "debug": "^4.1.0",
                 "gensync": "^1.0.0-beta.1",
@@ -58,12 +58,12 @@
                     }
                 },
                 "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+                    "version": "7.11.6",
+                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+                    "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
                     "dev": true,
                     "requires": {
-                        "@babel/types": "^7.11.0",
+                        "@babel/types": "^7.11.5",
                         "jsesc": "^2.5.1",
                         "source-map": "^0.5.0"
                     }
@@ -115,9 +115,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -132,26 +132,26 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
+                    "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
                     "dev": true,
                     "requires": {
                         "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
+                        "@babel/generator": "^7.11.5",
                         "@babel/helper-function-name": "^7.10.4",
                         "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
+                        "@babel/parser": "^7.11.5",
+                        "@babel/types": "^7.11.5",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0",
                         "lodash": "^4.17.19"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -202,9 +202,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -231,9 +231,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -332,9 +332,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -349,9 +349,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -430,9 +430,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -447,9 +447,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -460,119 +460,24 @@
             }
         },
         "@babel/helper-explode-assignable-expression": {
-            "version": "7.10.4",
-            "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz",
-            "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==",
+            "version": "7.11.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.11.4.tgz",
+            "integrity": "sha512-ux9hm3zR4WV1Y3xXxXkdG/0gxF9nvI0YVmKVhvK9AfMoaQkemL3sJpXw+Xbz65azo8qJiEz2XVDUpK3KYhH3ZQ==",
             "dev": true,
             "requires": {
-                "@babel/traverse": "^7.10.4",
                 "@babel/types": "^7.10.4"
             },
             "dependencies": {
-                "@babel/code-frame": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
-                    "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/highlight": "^7.10.4"
-                    }
-                },
-                "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.11.0",
-                        "jsesc": "^2.5.1",
-                        "source-map": "^0.5.0"
-                    }
-                },
-                "@babel/helper-function-name": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
-                    "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-get-function-arity": "^7.10.4",
-                        "@babel/template": "^7.10.4",
-                        "@babel/types": "^7.10.4"
-                    }
-                },
-                "@babel/helper-get-function-arity": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
-                    "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.10.4"
-                    }
-                },
-                "@babel/helper-split-export-declaration": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
-                    "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.11.0"
-                    }
-                },
                 "@babel/helper-validator-identifier": {
                     "version": "7.10.4",
                     "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
                     "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
                     "dev": true
                 },
-                "@babel/highlight": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
-                    "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.10.4",
-                        "chalk": "^2.0.0",
-                        "js-tokens": "^4.0.0"
-                    }
-                },
-                "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
-                    "dev": true
-                },
-                "@babel/template": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
-                    "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/code-frame": "^7.10.4",
-                        "@babel/parser": "^7.10.4",
-                        "@babel/types": "^7.10.4"
-                    }
-                },
-                "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
-                        "@babel/helper-function-name": "^7.10.4",
-                        "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
-                        "debug": "^4.1.0",
-                        "globals": "^11.1.0",
-                        "lodash": "^4.17.19"
-                    }
-                },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -644,9 +549,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -672,9 +577,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -753,9 +658,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -770,9 +675,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -798,9 +703,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -826,15 +731,14 @@
             }
         },
         "@babel/helper-remap-async-to-generator": {
-            "version": "7.10.4",
-            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz",
-            "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==",
+            "version": "7.11.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.11.4.tgz",
+            "integrity": "sha512-tR5vJ/vBa9wFy3m5LLv2faapJLnDFxNWff2SAYkSE4rLUdbp7CdObYFgI7wK4T/Mj4UzpjPwzR8Pzmr5m7MHGA==",
             "dev": true,
             "requires": {
                 "@babel/helper-annotate-as-pure": "^7.10.4",
                 "@babel/helper-wrap-function": "^7.10.4",
                 "@babel/template": "^7.10.4",
-                "@babel/traverse": "^7.10.4",
                 "@babel/types": "^7.10.4"
             },
             "dependencies": {
@@ -847,46 +751,6 @@
                         "@babel/highlight": "^7.10.4"
                     }
                 },
-                "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.11.0",
-                        "jsesc": "^2.5.1",
-                        "source-map": "^0.5.0"
-                    }
-                },
-                "@babel/helper-function-name": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
-                    "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/helper-get-function-arity": "^7.10.4",
-                        "@babel/template": "^7.10.4",
-                        "@babel/types": "^7.10.4"
-                    }
-                },
-                "@babel/helper-get-function-arity": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
-                    "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.10.4"
-                    }
-                },
-                "@babel/helper-split-export-declaration": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
-                    "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/types": "^7.11.0"
-                    }
-                },
                 "@babel/helper-validator-identifier": {
                     "version": "7.10.4",
                     "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
@@ -905,9 +769,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -921,27 +785,10 @@
                         "@babel/types": "^7.10.4"
                     }
                 },
-                "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
-                    "dev": true,
-                    "requires": {
-                        "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
-                        "@babel/helper-function-name": "^7.10.4",
-                        "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
-                        "debug": "^4.1.0",
-                        "globals": "^11.1.0",
-                        "lodash": "^4.17.19"
-                    }
-                },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -973,12 +820,12 @@
                     }
                 },
                 "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+                    "version": "7.11.6",
+                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+                    "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
                     "dev": true,
                     "requires": {
-                        "@babel/types": "^7.11.0",
+                        "@babel/types": "^7.11.5",
                         "jsesc": "^2.5.1",
                         "source-map": "^0.5.0"
                     }
@@ -1030,9 +877,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -1047,26 +894,26 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
+                    "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
                     "dev": true,
                     "requires": {
                         "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
+                        "@babel/generator": "^7.11.5",
                         "@babel/helper-function-name": "^7.10.4",
                         "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
+                        "@babel/parser": "^7.11.5",
+                        "@babel/types": "^7.11.5",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0",
                         "lodash": "^4.17.19"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -1113,9 +960,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -1130,9 +977,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -1158,9 +1005,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -1220,12 +1067,12 @@
                     }
                 },
                 "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+                    "version": "7.11.6",
+                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+                    "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
                     "dev": true,
                     "requires": {
-                        "@babel/types": "^7.11.0",
+                        "@babel/types": "^7.11.5",
                         "jsesc": "^2.5.1",
                         "source-map": "^0.5.0"
                     }
@@ -1277,9 +1124,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -1294,26 +1141,26 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
+                    "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
                     "dev": true,
                     "requires": {
                         "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
+                        "@babel/generator": "^7.11.5",
                         "@babel/helper-function-name": "^7.10.4",
                         "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
+                        "@babel/parser": "^7.11.5",
+                        "@babel/types": "^7.11.5",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0",
                         "lodash": "^4.17.19"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -1344,12 +1191,12 @@
                     }
                 },
                 "@babel/generator": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
-                    "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+                    "version": "7.11.6",
+                    "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+                    "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
                     "dev": true,
                     "requires": {
-                        "@babel/types": "^7.11.0",
+                        "@babel/types": "^7.11.5",
                         "jsesc": "^2.5.1",
                         "source-map": "^0.5.0"
                     }
@@ -1401,9 +1248,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -1418,26 +1265,26 @@
                     }
                 },
                 "@babel/traverse": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
-                    "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
+                    "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
                     "dev": true,
                     "requires": {
                         "@babel/code-frame": "^7.10.4",
-                        "@babel/generator": "^7.11.0",
+                        "@babel/generator": "^7.11.5",
                         "@babel/helper-function-name": "^7.10.4",
                         "@babel/helper-split-export-declaration": "^7.11.0",
-                        "@babel/parser": "^7.11.0",
-                        "@babel/types": "^7.11.0",
+                        "@babel/parser": "^7.11.5",
+                        "@babel/types": "^7.11.5",
                         "debug": "^4.1.0",
                         "globals": "^11.1.0",
                         "lodash": "^4.17.19"
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -1935,9 +1782,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -2059,9 +1906,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -2076,9 +1923,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -2255,9 +2102,9 @@
                     }
                 },
                 "@babel/parser": {
-                    "version": "7.11.3",
-                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
-                    "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+                    "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
                     "dev": true
                 },
                 "@babel/template": {
@@ -2272,9 +2119,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -2471,9 +2318,9 @@
                     "dev": true
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -2650,9 +2497,9 @@
             }
         },
         "@babel/preset-env": {
-            "version": "7.11.0",
-            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz",
-            "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==",
+            "version": "7.11.5",
+            "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz",
+            "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==",
             "dev": true,
             "requires": {
                 "@babel/compat-data": "^7.11.0",
@@ -2717,7 +2564,7 @@
                 "@babel/plugin-transform-unicode-escapes": "^7.10.4",
                 "@babel/plugin-transform-unicode-regex": "^7.10.4",
                 "@babel/preset-modules": "^0.1.3",
-                "@babel/types": "^7.11.0",
+                "@babel/types": "^7.11.5",
                 "browserslist": "^4.12.0",
                 "core-js-compat": "^3.6.2",
                 "invariant": "^2.2.2",
@@ -2774,9 +2621,9 @@
                     }
                 },
                 "@babel/types": {
-                    "version": "7.11.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
-                    "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+                    "version": "7.11.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+                    "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
                     "dev": true,
                     "requires": {
                         "@babel/helper-validator-identifier": "^7.10.4",
@@ -2787,9 +2634,9 @@
             }
         },
         "@babel/preset-modules": {
-            "version": "0.1.3",
-            "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz",
-            "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==",
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz",
+            "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==",
             "dev": true,
             "requires": {
                 "@babel/helper-plugin-utils": "^7.0.0",
@@ -4625,13 +4472,13 @@
             }
         },
         "browserslist": {
-            "version": "4.14.0",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz",
-            "integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==",
+            "version": "4.14.1",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.1.tgz",
+            "integrity": "sha512-zyBTIHydW37pnb63c7fHFXUG6EcqWOqoMdDx6cdyaDFriZ20EoVxcE95S54N+heRqY8m8IUgB5zYta/gCwSaaA==",
             "dev": true,
             "requires": {
-                "caniuse-lite": "^1.0.30001111",
-                "electron-to-chromium": "^1.3.523",
+                "caniuse-lite": "^1.0.30001124",
+                "electron-to-chromium": "^1.3.562",
                 "escalade": "^3.0.2",
                 "node-releases": "^1.1.60"
             }
@@ -4687,9 +4534,9 @@
             "dev": true
         },
         "caniuse-lite": {
-            "version": "1.0.30001114",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001114.tgz",
-            "integrity": "sha512-ml/zTsfNBM+T1+mjglWRPgVsu2L76GAaADKX5f4t0pbhttEp0WMawJsHDYlFkVZkoA+89uvBRrVrEE4oqenzXQ==",
+            "version": "1.0.30001125",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001125.tgz",
+            "integrity": "sha512-9f+r7BW8Qli917mU3j0fUaTweT3f3vnX/Lcs+1C73V+RADmFme+Ih0Br8vONQi3X0lseOe6ZHfsZLCA8MSjxUA==",
             "dev": true
         },
         "capture-exit": {
@@ -5146,9 +4993,9 @@
             }
         },
         "electron-to-chromium": {
-            "version": "1.3.533",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz",
-            "integrity": "sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A==",
+            "version": "1.3.564",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.564.tgz",
+            "integrity": "sha512-fNaYN3EtKQWLQsrKXui8mzcryJXuA0LbCLoizeX6oayG2emBaS5MauKjCPAvc29NEY4FpLHIUWiP+Y0Bfrs5dg==",
             "dev": true
         },
         "element-closest": {
@@ -9065,9 +8912,9 @@
             }
         },
         "node-releases": {
-            "version": "1.1.60",
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz",
-            "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==",
+            "version": "1.1.61",
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz",
+            "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==",
             "dev": true
         },
         "normalize-package-data": {
@@ -9898,9 +9745,9 @@
             }
         },
         "rollup-plugin-terser": {
-            "version": "7.0.0",
-            "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.0.tgz",
-            "integrity": "sha512-p/N3lLiFusCjYTLfVkoaiRTOGr5AESEaljMPH12MhOtoMkmTBhIAfuadrcWy4am1U0vU4WTxO9fi0K09O4CboQ==",
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
+            "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
             "dev": true,
             "requires": {
                 "@babel/code-frame": "^7.10.4",
@@ -9959,18 +9806,18 @@
                     "dev": true
                 },
                 "supports-color": {
-                    "version": "7.1.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
-                    "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
                     "dev": true,
                     "requires": {
                         "has-flag": "^4.0.0"
                     }
                 },
                 "terser": {
-                    "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz",
-                    "integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==",
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz",
+                    "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==",
                     "dev": true,
                     "requires": {
                         "commander": "^2.20.0",

+ 4 - 4
package.json

@@ -1,7 +1,7 @@
 {
     "main": "dist/alpine.js",
     "name": "alpinejs",
-    "version": "2.6.0",
+    "version": "2.7.0",
     "repository": {
         "type": "git",
         "url": "git://github.com/alpinejs/alpine.git"
@@ -15,8 +15,8 @@
     "author": "Caleb Porzio",
     "license": "MIT",
     "devDependencies": {
-        "@babel/core": "^7.11.1",
-        "@babel/preset-env": "^7.11.0",
+        "@babel/core": "^7.11.6",
+        "@babel/preset-env": "^7.11.5",
         "@rollup/plugin-commonjs": "^11.1.0",
         "@rollup/plugin-multi-entry": "^3.0.1",
         "@rollup/plugin-replace": "^2.3.3",
@@ -39,7 +39,7 @@
         "rollup-plugin-filesize": "^6.2.1",
         "rollup-plugin-node-resolve": "^5.2.0",
         "rollup-plugin-strip-code": "^0.2.7",
-        "rollup-plugin-terser": "^7.0.0",
+        "rollup-plugin-terser": "^7.0.2",
         "shim-selected-options": "^1.0.1"
     },
     "dependencies": {}

+ 6 - 1
src/directives/for.js

@@ -1,4 +1,4 @@
-import { transitionIn, transitionOut, getXAttrs, warnIfMalformedTemplate } from '../utils'
+import { transitionIn, transitionOut, getXAttrs, warnIfMalformedTemplate, isNumeric } from '../utils'
 
 export function handleForDirective(component, templateEl, expression, initialUpdate, extraVars) {
     warnIfMalformedTemplate(templateEl, 'x-for')
@@ -92,6 +92,11 @@ function evaluateItemsAndReturnEmptyIfXIfIsPresentAndFalseOnElement(component, e
         return []
     }
 
+    // This adds support for the `i in n` syntax.
+    if (isNumeric(iteratorNames.items)) {
+        return Array.from(Array(parseInt(iteratorNames.items, 10)).keys(), i => i + 1)
+    }
+
     return component.evaluateReturnExpression(el, iteratorNames.items, extraVars)
 }
 

+ 1 - 1
src/directives/text.js

@@ -4,5 +4,5 @@ export function handleTextDirective(el, output, expression) {
         output = ''
     }
 
-    el.innerText = output
+    el.textContent = output
 }

+ 1 - 1
src/utils.js

@@ -24,7 +24,7 @@ export function warnIfMalformedTemplate(el, directive) {
     if (el.tagName.toLowerCase() !== 'template') {
         console.warn(`Alpine: [${directive}] directive should only be added to <template> tags. See https://github.com/alpinejs/alpine#${directive}`)
     } else if (el.content.childElementCount !== 1) {
-        console.warn(`Alpine: <template> tag with [${directive}] encountered with multiple element roots. Make sure <template> only has a single child node.`)
+        console.warn(`Alpine: <template> tag with [${directive}] encountered with multiple element roots. Make sure <template> only has a single child element.`)
     }
 }
 

+ 17 - 17
test/constructor.spec.js

@@ -32,7 +32,7 @@ test('auto-detect new components at the top level', async () => {
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }})
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('auto-detect nested new components at the top level', async () => {
@@ -67,7 +67,7 @@ test('auto-detect nested new components at the top level', async () => {
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }})
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('auto-detect new components and dont lose state of existing ones', async () => {
@@ -91,7 +91,7 @@ test('auto-detect new components and dont lose state of existing ones', async ()
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }})
 
-    await wait(() => { expect(document.querySelector('#A span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') })
 
     document.querySelector('#B').innerHTML = `
         <div x-data="{foo: 'baz'}">
@@ -109,8 +109,8 @@ test('auto-detect new components and dont lose state of existing ones', async ()
     ])
 
     await wait(() => {
-        expect(document.querySelector('#A span').innerText).toEqual('bar')
-        expect(document.querySelector('#B span').innerText).toEqual('baz')
+        expect(document.querySelector('#A span').textContent).toEqual('bar')
+        expect(document.querySelector('#B span').textContent).toEqual('baz')
     })
 })
 
@@ -135,7 +135,7 @@ test('auto-detect new components that are wrapped in non-new component tags', as
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' }})
 
-    await wait(() => { expect(document.querySelector('#A span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('#A span').textContent).toEqual('bar') })
 
     document.querySelector('#B').innerHTML = `
         <section>
@@ -155,8 +155,8 @@ test('auto-detect new components that are wrapped in non-new component tags', as
     ])
 
     await wait(() => {
-        expect(document.querySelector('#A span').innerText).toEqual('bar')
-        expect(document.querySelector('#B span').innerText).toEqual('baz')
+        expect(document.querySelector('#A span').textContent).toEqual('bar')
+        expect(document.querySelector('#B span').textContent).toEqual('baz')
     })
 })
 
@@ -179,7 +179,7 @@ test('auto-initialize new elements added to a component', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     document.querySelector('#target').innerHTML = `
         <span x-text="count"></span>
@@ -194,12 +194,12 @@ test('auto-initialize new elements added to a component', async () => {
         ] }
     ])
 
-    await wait(() => { expect(document.querySelector('#target span').innerText).toEqual(0) })
+    await wait(() => { expect(document.querySelector('#target span').textContent).toEqual('0') })
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
-    await wait(() => { expect(document.querySelector('#target span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
+    await wait(() => { expect(document.querySelector('#target span').textContent).toEqual('1') })
 })
 
 test('Alpine mutations don\'t trigger (like x-if and x-for) MutationObserver', async () => {
@@ -263,7 +263,7 @@ test('auto-detect x-data property changes at run-time', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     document.querySelector('div').setAttribute('x-data', '{ count: 1 }')
 
@@ -276,7 +276,7 @@ test('auto-detect x-data property changes at run-time', async () => {
         }
     ])
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 })
 
 test('can use $el when changing x-data property at run-time', async () => {
@@ -295,7 +295,7 @@ test('can use $el when changing x-data property at run-time', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('0')
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     document.querySelector('div').setAttribute('x-data', '{ count: $el.dataset.count }')
 
@@ -308,7 +308,7 @@ test('can use $el when changing x-data property at run-time', async () => {
         }
     ])
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('1') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 })
 
 test('nested components only get registered once on initialization', async () => {
@@ -355,7 +355,7 @@ test('can clone an existing component to a new element', async () => {
 
     Alpine.clone(document.querySelector('h1').__x, document.querySelector('h2'))
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 })
 
 test('x-attributes are matched exactly', async () => {

+ 1 - 1
test/custom-magic-properties.spec.js

@@ -13,5 +13,5 @@ test('can register custom magic properties', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 })

+ 20 - 20
test/data.spec.js

@@ -16,7 +16,7 @@ test('data manipulated on component object is reactive', async () => {
 
     document.querySelector('div').__x.$data.foo = 'baz'
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('baz') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('baz') })
 })
 
 test('x-data attribute value is optional', async () => {
@@ -28,7 +28,7 @@ test('x-data attribute value is optional', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('foo')
+    expect(document.querySelector('span').textContent).toEqual('foo')
 })
 
 test('x-data can use attributes from a reusable function', async () => {
@@ -45,7 +45,7 @@ test('x-data can use attributes from a reusable function', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 })
 
 test('x-data can use $el', async () => {
@@ -57,7 +57,7 @@ test('x-data can use $el', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('test')
+    expect(document.querySelector('span').textContent).toEqual('test')
 })
 
 test('functions in x-data are reactive', async () => {
@@ -69,11 +69,11 @@ test('functions in x-data are reactive', async () => {
     `
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('baz') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('baz') })
 })
 
 test('Proxies are not nested and duplicated when manipulating an array', async () => {
@@ -89,33 +89,33 @@ test('Proxies are not nested and duplicated when manipulating an array', async (
 
     // Before this fix: https://github.com/alpinejs/alpine/pull/141
     // This test would create exponentially slower performance and eventually stall out.
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
     document.querySelector('h1').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('foo') })
     document.querySelector('button').click()
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('component refresh one time per update whatever the number of mutations in the update', async () => {

+ 30 - 30
test/debounce.spec.js

@@ -20,21 +20,21 @@ test('x-on with debounce modifier', async () => {
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(249)
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(20)
 
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })
 
 test('x-on with debounce modifier and specified wait', async () => {
@@ -51,21 +51,21 @@ test('x-on with debounce modifier and specified wait', async () => {
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(99)
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(20)
 
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })
 
 test('x-model with debounce modifier', async () => {
@@ -78,25 +78,25 @@ test('x-model with debounce modifier', async () => {
 
     Alpine.start()
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' }})
+    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' } })
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' }})
-    expect(document.querySelector('span').innerText).toEqual('')
+    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' } })
+    expect(document.querySelector('span').textContent).toEqual('')
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' }})
-    expect(document.querySelector('span').innerText).toEqual('')
+    fireEvent.input(document.querySelector('input'), { target: { value: 'foo' } })
+    expect(document.querySelector('span').textContent).toEqual('')
 
     await timeout(249)
 
-    expect(document.querySelector('span').innerText).toEqual('')
+    expect(document.querySelector('span').textContent).toEqual('')
 
     await timeout(20)
 
-    expect(document.querySelector('span').innerText).toEqual('foo')
+    expect(document.querySelector('span').textContent).toEqual('foo')
 })
 
 test('x-on with debounce modifier (with "ms" suffix)', async () => {
@@ -109,25 +109,25 @@ test('x-on with debounce modifier (with "ms" suffix)', async () => {
 
     Alpine.start()
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(10)
 
-    fireEvent.input(document.querySelector('input'), { target: { value: 1 }})
-    expect(document.querySelector('span').innerText).toEqual(0)
+    fireEvent.input(document.querySelector('input'), { target: { value: 1 } })
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(99)
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     await timeout(20)
 
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })
 
 test('keydown with key modifier and debounce with 10ms wait', async () => {
@@ -144,14 +144,14 @@ test('keydown with key modifier and debounce with 10ms wait', async () => {
 
     await timeout(10)
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'a' })
     fireEvent.keyDown(document.querySelector('input'), { key: 'a' })
 
     await timeout(20)
 
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })
 
 test('keydown with debounce modifier and no specified wait', async () => {
@@ -164,12 +164,12 @@ test('keydown with debounce modifier and no specified wait', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'a' })
     fireEvent.keyDown(document.querySelector('input'), { key: 'a' })
 
     await timeout(270)
 
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })

+ 2 - 2
test/dispatch.spec.js

@@ -16,9 +16,9 @@ test('$dispatch', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('baz') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('baz') })
 })

+ 60 - 46
test/for.spec.js

@@ -19,14 +19,14 @@ test('x-for', async () => {
     Alpine.start()
 
     expect(document.querySelectorAll('span').length).toEqual(1)
-    expect(document.querySelectorAll('span')[0].innerText).toEqual('foo')
+    expect(document.querySelectorAll('span')[0].textContent).toEqual('foo')
 
     document.querySelector('button').click()
 
     await wait(() => { expect(document.querySelectorAll('span').length).toEqual(2) })
 
-    expect(document.querySelectorAll('span')[0].innerText).toEqual('foo')
-    expect(document.querySelectorAll('span')[1].innerText).toEqual('bar')
+    expect(document.querySelectorAll('span')[0].textContent).toEqual('foo')
+    expect(document.querySelectorAll('span')[1].textContent).toEqual('bar')
 })
 
 test('removes all elements when array is empty and previously had one item', async () => {
@@ -86,14 +86,14 @@ test('elements inside of loop are reactive', async () => {
     Alpine.start()
 
     expect(document.querySelectorAll('span').length).toEqual(1)
-    expect(document.querySelector('h1').innerText).toEqual('first')
-    expect(document.querySelector('h2').innerText).toEqual('bar')
+    expect(document.querySelector('h1').textContent).toEqual('first')
+    expect(document.querySelector('h2').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual('first')
-        expect(document.querySelector('h2').innerText).toEqual('baz')
+        expect(document.querySelector('h1').textContent).toEqual('first')
+        expect(document.querySelector('h2').textContent).toEqual('baz')
     })
 })
 
@@ -112,12 +112,12 @@ test('components inside of loop are reactive', async () => {
     Alpine.start()
 
     expect(document.querySelectorAll('div.child').length).toEqual(1)
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('bob')
+        expect(document.querySelector('span').textContent).toEqual('bob')
     })
 })
 
@@ -138,12 +138,12 @@ test('components inside a plain element of loop are reactive', async () => {
     Alpine.start()
 
     expect(document.querySelectorAll('ul').length).toEqual(1)
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('bob')
+        expect(document.querySelector('span').textContent).toEqual('bob')
     })
 })
 
@@ -209,8 +209,8 @@ test('can use index inside of loop', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual(0)
-    expect(document.querySelector('h2').innerText).toEqual(0)
+    expect(document.querySelector('h1').textContent).toEqual('0')
+    expect(document.querySelector('h2').textContent).toEqual('0')
 })
 
 test('can use third iterator param (collection) inside of loop', async () => {
@@ -227,8 +227,8 @@ test('can use third iterator param (collection) inside of loop', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual(["foo"])
-    expect(document.querySelector('h2').innerText).toEqual(["foo"])
+    expect(document.querySelector('h1').textContent).toEqual('foo')
+    expect(document.querySelector('h2').textContent).toEqual('foo')
 })
 
 test('can use x-if in conjunction with x-for', async () => {
@@ -278,15 +278,15 @@ test('listeners in loop get fresh iteration data even though they are only regis
 
     document.querySelector('span').click()
 
-    await wait(() => { expect(document.querySelector('h1').innerText).toEqual('foo') })
+    await wait(() => { expect(document.querySelector('h1').textContent).toEqual('foo') })
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 
     document.querySelector('span').click()
 
-    await wait(() => { expect(document.querySelector('h1').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('h1').textContent).toEqual('bar') })
 })
 
 test('nested x-for', async () => {
@@ -308,16 +308,16 @@ test('nested x-for', async () => {
     await wait(() => { expect(document.querySelectorAll('h1').length).toEqual(1) })
     await wait(() => { expect(document.querySelectorAll('h2').length).toEqual(2) })
 
-    expect(document.querySelectorAll('h2')[0].innerText).toEqual('bob')
-    expect(document.querySelectorAll('h2')[1].innerText).toEqual('lob')
+    expect(document.querySelectorAll('h2')[0].textContent).toEqual('bob')
+    expect(document.querySelectorAll('h2')[1].textContent).toEqual('lob')
 
     document.querySelector('button').click()
 
     await wait(() => { expect(document.querySelectorAll('h2').length).toEqual(3) })
 
-    expect(document.querySelectorAll('h2')[0].innerText).toEqual('bob')
-    expect(document.querySelectorAll('h2')[1].innerText).toEqual('lob')
-    expect(document.querySelectorAll('h2')[2].innerText).toEqual('law')
+    expect(document.querySelectorAll('h2')[0].textContent).toEqual('bob')
+    expect(document.querySelectorAll('h2')[1].textContent).toEqual('lob')
+    expect(document.querySelectorAll('h2')[2].textContent).toEqual('law')
 })
 
 test('x-for updates the right elements when new item are inserted at the beginning of the list', async () => {
@@ -343,9 +343,9 @@ test('x-for updates the right elements when new item are inserted at the beginni
 
     await wait(() => { expect(document.querySelectorAll('span').length).toEqual(3) })
 
-    expect(document.querySelectorAll('span')[0].innerText).toEqual('zero')
-    expect(document.querySelectorAll('span')[1].innerText).toEqual('one')
-    expect(document.querySelectorAll('span')[2].innerText).toEqual('two')
+    expect(document.querySelectorAll('span')[0].textContent).toEqual('zero')
+    expect(document.querySelectorAll('span')[1].textContent).toEqual('one')
+    expect(document.querySelectorAll('span')[2].textContent).toEqual('two')
 
     // Make sure states are preserved
     expect(document.querySelectorAll('span')[0].getAttribute('order')).toEqual(null)
@@ -371,10 +371,10 @@ test('nested x-for access outer loop variable', async () => {
     await wait(() => { expect(document.querySelectorAll('h1').length).toEqual(2) })
     await wait(() => { expect(document.querySelectorAll('h2').length).toEqual(4) })
 
-    expect(document.querySelectorAll('h2')[0].innerText).toEqual('foo: bob')
-    expect(document.querySelectorAll('h2')[1].innerText).toEqual('foo: lob')
-    expect(document.querySelectorAll('h2')[2].innerText).toEqual('baz: bab')
-    expect(document.querySelectorAll('h2')[3].innerText).toEqual('baz: lab')
+    expect(document.querySelectorAll('h2')[0].textContent).toEqual('foo: bob')
+    expect(document.querySelectorAll('h2')[1].textContent).toEqual('foo: lob')
+    expect(document.querySelectorAll('h2')[2].textContent).toEqual('baz: bab')
+    expect(document.querySelectorAll('h2')[3].textContent).toEqual('baz: lab')
 })
 
 test('nested x-for event listeners', async () => {
@@ -402,20 +402,20 @@ test('nested x-for event listeners', async () => {
     await wait(() => { expect(document.querySelectorAll('h1').length).toEqual(2) })
     await wait(() => { expect(document.querySelectorAll('h2').length).toEqual(4) })
 
-    expect(document.querySelectorAll('h2')[0].innerText).toEqual('foo: bob = 0')
-    expect(document.querySelectorAll('h2')[1].innerText).toEqual('foo: lob = 0')
-    expect(document.querySelectorAll('h2')[2].innerText).toEqual('baz: bab = 0')
-    expect(document.querySelectorAll('h2')[3].innerText).toEqual('baz: lab = 0')
+    expect(document.querySelectorAll('h2')[0].textContent).toEqual('foo: bob = 0')
+    expect(document.querySelectorAll('h2')[1].textContent).toEqual('foo: lob = 0')
+    expect(document.querySelectorAll('h2')[2].textContent).toEqual('baz: bab = 0')
+    expect(document.querySelectorAll('h2')[3].textContent).toEqual('baz: lab = 0')
 
     expect(document._alerts.length).toEqual(0)
 
     document.querySelectorAll('h2')[0].click()
 
     await wait(() => {
-        expect(document.querySelectorAll('h2')[0].innerText).toEqual('foo: bob = 1')
-        expect(document.querySelectorAll('h2')[1].innerText).toEqual('foo: lob = 0')
-        expect(document.querySelectorAll('h2')[2].innerText).toEqual('baz: bab = 0')
-        expect(document.querySelectorAll('h2')[3].innerText).toEqual('baz: lab = 0')
+        expect(document.querySelectorAll('h2')[0].textContent).toEqual('foo: bob = 1')
+        expect(document.querySelectorAll('h2')[1].textContent).toEqual('foo: lob = 0')
+        expect(document.querySelectorAll('h2')[2].textContent).toEqual('baz: bab = 0')
+        expect(document.querySelectorAll('h2')[3].textContent).toEqual('baz: lab = 0')
 
         expect(document._alerts.length).toEqual(1)
         expect(document._alerts[0]).toEqual('foo: bob = 1')
@@ -424,10 +424,10 @@ test('nested x-for event listeners', async () => {
     document.querySelectorAll('h2')[2].click()
 
     await wait(() => {
-        expect(document.querySelectorAll('h2')[0].innerText).toEqual('foo: bob = 1')
-        expect(document.querySelectorAll('h2')[1].innerText).toEqual('foo: lob = 0')
-        expect(document.querySelectorAll('h2')[2].innerText).toEqual('baz: bab = 1')
-        expect(document.querySelectorAll('h2')[3].innerText).toEqual('baz: lab = 0')
+        expect(document.querySelectorAll('h2')[0].textContent).toEqual('foo: bob = 1')
+        expect(document.querySelectorAll('h2')[1].textContent).toEqual('foo: lob = 0')
+        expect(document.querySelectorAll('h2')[2].textContent).toEqual('baz: bab = 1')
+        expect(document.querySelectorAll('h2')[3].textContent).toEqual('baz: lab = 0')
 
         expect(document._alerts.length).toEqual(2)
         expect(document._alerts[0]).toEqual('foo: bob = 1')
@@ -437,10 +437,10 @@ test('nested x-for event listeners', async () => {
     document.querySelectorAll('h2')[0].click()
 
     await wait(() => {
-        expect(document.querySelectorAll('h2')[0].innerText).toEqual('foo: bob = 2')
-        expect(document.querySelectorAll('h2')[1].innerText).toEqual('foo: lob = 0')
-        expect(document.querySelectorAll('h2')[2].innerText).toEqual('baz: bab = 1')
-        expect(document.querySelectorAll('h2')[3].innerText).toEqual('baz: lab = 0')
+        expect(document.querySelectorAll('h2')[0].textContent).toEqual('foo: bob = 2')
+        expect(document.querySelectorAll('h2')[1].textContent).toEqual('foo: lob = 0')
+        expect(document.querySelectorAll('h2')[2].textContent).toEqual('baz: bab = 1')
+        expect(document.querySelectorAll('h2')[3].textContent).toEqual('baz: lab = 0')
 
         expect(document._alerts.length).toEqual(3)
         expect(document._alerts[0]).toEqual('foo: bob = 1')
@@ -477,3 +477,17 @@ test('make sure new elements with different keys added to the beginning of a loo
 
     expect(clickCount).toEqual(2)
 })
+
+test('x-for over range using i in x syntax', async () => {
+    document.body.innerHTML = `
+        <div x-data>
+            <template x-for="i in 10">
+                <span x-text="i"></span>
+            </template>
+        </div>
+    `
+
+    Alpine.start()
+
+    expect(document.querySelectorAll('span').length).toEqual(10)
+})

+ 5 - 5
test/if.spec.js

@@ -41,7 +41,7 @@ test('elements inside x-if are still reactive', async () => {
     Alpine.start()
 
     expect(document.querySelector('h2')).toBeFalsy()
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('h1').click()
 
@@ -52,7 +52,7 @@ test('elements inside x-if are still reactive', async () => {
     document.querySelector('h2').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('baz')
+        expect(document.querySelector('span').textContent).toEqual('baz')
     })
 })
 
@@ -74,7 +74,7 @@ test('x-if works inside a loop', async () => {
     Alpine.start()
 
     expect(document.querySelectorAll('span').length).toEqual(1)
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 })
 
 test('event listeners are attached once', async () => {
@@ -89,11 +89,11 @@ test('event listeners are attached once', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual(1)
+        expect(document.querySelector('span').textContent).toEqual('1')
     })
 })

+ 7 - 7
test/lifecycle.spec.js

@@ -26,8 +26,8 @@ test('x-init from data function with callback return for "x-mounted" functionali
     var valueA
     var valueB
     window.setValueA = (el) => { valueA = el.innerHTML }
-    window.setValueB = (el) => { valueB = el.innerText }
-    window.data = function() {
+    window.setValueB = (el) => { valueB = el.textContent }
+    window.data = function () {
         return {
             foo: 'bar',
             init() {
@@ -63,11 +63,11 @@ test('callbacks registered within x-init can affect reactive data changes', asyn
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bob') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bob') })
 })
 
 test('callbacks registered within x-init callback can affect reactive data changes', async () => {
@@ -81,11 +81,11 @@ test('callbacks registered within x-init callback can affect reactive data chang
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bob') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bob') })
 })
 
 test('x-init is capable of dispatching an event', async () => {
@@ -100,6 +100,6 @@ test('x-init is capable of dispatching an event', async () => {
     Alpine.start()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('baz')
+        expect(document.querySelector('span').textContent).toEqual('baz')
     })
 })

+ 41 - 41
test/model.spec.js

@@ -109,7 +109,7 @@ test('x-model trims value if trim modifier is present', async () => {
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar   ' } })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('x-model updates value when updated via changed event when lazy modifier is present', async () => {
@@ -248,7 +248,7 @@ test('x-model binds select dropdown', async () => {
     expect(document.querySelectorAll('option')[0].selected).toEqual(false)
     expect(document.querySelectorAll('option')[1].selected).toEqual(true)
     expect(document.querySelectorAll('option')[2].selected).toEqual(false)
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     fireEvent.change(document.querySelector('select'), { target: { value: 'baz' } });
 
@@ -256,7 +256,7 @@ test('x-model binds select dropdown', async () => {
         expect(document.querySelectorAll('option')[0].selected).toEqual(false)
         expect(document.querySelectorAll('option')[1].selected).toEqual(false)
         expect(document.querySelectorAll('option')[2].selected).toEqual(true)
-        expect(document.querySelector('span').innerText).toEqual('baz')
+        expect(document.querySelector('span').textContent).toEqual('baz')
     })
 })
 
@@ -278,7 +278,7 @@ test('x-model binds multiple select dropdown', async () => {
     expect(document.querySelectorAll('option')[0].selected).toEqual(false)
     expect(document.querySelectorAll('option')[1].selected).toEqual(true)
     expect(document.querySelectorAll('option')[2].selected).toEqual(false)
-    expect(document.querySelector('span').innerText).toEqual(['bar'])
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelectorAll('option')[2].selected = true
     fireEvent.change(document.querySelector('select'));
@@ -287,7 +287,7 @@ test('x-model binds multiple select dropdown', async () => {
         expect(document.querySelectorAll('option')[0].selected).toEqual(false)
         expect(document.querySelectorAll('option')[1].selected).toEqual(true)
         expect(document.querySelectorAll('option')[2].selected).toEqual(true)
-        expect(document.querySelector('span').innerText).toEqual(['bar', 'baz'])
+        expect(document.querySelector('span').textContent).toEqual('bar,baz')
     })
 })
 
@@ -302,13 +302,13 @@ test('x-model binds nested keys', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('foo')
-    expect(document.querySelector('span').innerText).toEqual('foo')
+    expect(document.querySelector('span').textContent).toEqual('foo')
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' } })
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('bar')
-        expect(document.querySelector('span').innerText).toEqual('bar')
+        expect(document.querySelector('span').textContent).toEqual('bar')
     })
 })
 
@@ -323,13 +323,13 @@ test('x-model undefined nested model key defaults to empty string', async () =>
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('')
-    expect(document.querySelector('span').innerText).toEqual('')
+    expect(document.querySelector('span').textContent).toEqual('')
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' } })
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('bar')
-        expect(document.querySelector('span').innerText).toEqual('bar')
+        expect(document.querySelector('span').textContent).toEqual('bar')
     })
 })
 
@@ -344,12 +344,12 @@ test('x-model can listen for custom input event dispatches', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('baz')
+        expect(document.querySelector('span').textContent).toEqual('baz')
     })
 })
 
@@ -365,13 +365,13 @@ test('x-model bind color input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('#ff0000')
-    expect(document.querySelector('span').innerText).toEqual('#ff0000')
+    expect(document.querySelector('span').textContent).toEqual('#ff0000')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '#00ff00' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('#00ff00')
-        expect(document.querySelector('span').innerText).toEqual('#00ff00')
+        expect(document.querySelector('span').textContent).toEqual('#00ff00')
     })
 
 })
@@ -388,13 +388,13 @@ test('x-model bind button input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('foo')
-    expect(document.querySelector('span').innerText).toEqual('foo')
+    expect(document.querySelector('span').textContent).toEqual('foo')
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'bar' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('bar')
-        expect(document.querySelector('span').innerText).toEqual('bar')
+        expect(document.querySelector('span').textContent).toEqual('bar')
     })
 })
 
@@ -410,13 +410,13 @@ test('x-model bind date input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('2020-07-10')
-    expect(document.querySelector('span').innerText).toEqual('2020-07-10')
+    expect(document.querySelector('span').textContent).toEqual('2020-07-10')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '2021-01-01' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('2021-01-01')
-        expect(document.querySelector('span').innerText).toEqual('2021-01-01')
+        expect(document.querySelector('span').textContent).toEqual('2021-01-01')
     })
 })
 
@@ -432,13 +432,13 @@ test('x-model bind datetime-local input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('2020-01-01T20:00')
-    expect(document.querySelector('span').innerText).toEqual('2020-01-01T20:00')
+    expect(document.querySelector('span').textContent).toEqual('2020-01-01T20:00')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '2021-02-02T20:00' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('2021-02-02T20:00')
-        expect(document.querySelector('span').innerText).toEqual('2021-02-02T20:00')
+        expect(document.querySelector('span').textContent).toEqual('2021-02-02T20:00')
     })
 })
 
@@ -458,13 +458,13 @@ test('x-model bind month input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('2020-04')
-    expect(document.querySelector('span').innerText).toEqual('2020-04')
+    expect(document.querySelector('span').textContent).toEqual('2020-04')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '2021-05' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('2021-05')
-        expect(document.querySelector('span').innerText).toEqual('2021-05')
+        expect(document.querySelector('span').textContent).toEqual('2021-05')
     })
 })
 
@@ -481,13 +481,13 @@ test('x-model bind number input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('11')
-    expect(document.querySelector('span').innerText).toEqual('11')
+    expect(document.querySelector('span').textContent).toEqual('11')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '2021' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('2021')
-        expect(document.querySelector('span').innerText).toEqual('2021')
+        expect(document.querySelector('span').textContent).toEqual('2021')
     })
 })
 
@@ -503,13 +503,13 @@ test('x-model bind password input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('SecretKey')
-    expect(document.querySelector('span').innerText).toEqual('SecretKey')
+    expect(document.querySelector('span').textContent).toEqual('SecretKey')
 
     fireEvent.input(document.querySelector('input'), { target: { value: 'NewSecretKey' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('NewSecretKey')
-        expect(document.querySelector('span').innerText).toEqual('NewSecretKey')
+        expect(document.querySelector('span').textContent).toEqual('NewSecretKey')
     })
 })
 
@@ -525,13 +525,13 @@ test('x-model bind range input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('10')
-    expect(document.querySelector('span').innerText).toEqual('10')
+    expect(document.querySelector('span').textContent).toEqual('10')
 
     fireEvent.input(document.querySelector('input'), { target: { value: '20' } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual('20')
-        expect(document.querySelector('span').innerText).toEqual('20')
+        expect(document.querySelector('span').textContent).toEqual('20')
     })
 })
 
@@ -547,14 +547,14 @@ test('x-model bind search input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('')
-    expect(document.querySelector('span').innerText).toEqual('')
+    expect(document.querySelector('span').textContent).toEqual('')
 
     const newValue = 'Frontend Frameworks';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -570,14 +570,14 @@ test('x-model bind tel input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('+12345678901')
-    expect(document.querySelector('span').innerText).toEqual('+12345678901')
+    expect(document.querySelector('span').textContent).toEqual('+12345678901')
 
     const newValue = '+1239874560';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -593,14 +593,14 @@ test('x-model bind tel input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('+12345678901')
-    expect(document.querySelector('span').innerText).toEqual('+12345678901')
+    expect(document.querySelector('span').textContent).toEqual('+12345678901')
 
     const newValue = '+1239874560';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -616,14 +616,14 @@ test('x-model bind time input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('22:00')
-    expect(document.querySelector('span').innerText).toEqual('22:00')
+    expect(document.querySelector('span').textContent).toEqual('22:00')
 
     const newValue = '23:00';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -639,14 +639,14 @@ test('x-model bind time input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('22:00')
-    expect(document.querySelector('span').innerText).toEqual('22:00')
+    expect(document.querySelector('span').textContent).toEqual('22:00')
 
     const newValue = '23:00';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -662,14 +662,14 @@ test('x-model bind week input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('2020-W20')
-    expect(document.querySelector('span').innerText).toEqual('2020-W20')
+    expect(document.querySelector('span').textContent).toEqual('2020-W20')
 
     const newValue = '2020-W30';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 
@@ -685,14 +685,14 @@ test('x-model bind url input', async () => {
     Alpine.start()
 
     expect(document.querySelector('input').value).toEqual('https://example.com')
-    expect(document.querySelector('span').innerText).toEqual('https://example.com')
+    expect(document.querySelector('span').textContent).toEqual('https://example.com')
 
     const newValue = 'https://alpine.io';
     fireEvent.input(document.querySelector('input'), { target: { value: newValue } });
 
     await wait(() => {
         expect(document.querySelector('input').value).toEqual(newValue)
-        expect(document.querySelector('span').innerText).toEqual(newValue)
+        expect(document.querySelector('span').textContent).toEqual(newValue)
     })
 })
 

+ 4 - 4
test/mutations.spec.js

@@ -48,23 +48,23 @@ test('mutationObserver doesn\'t reset data when reparenting nested components',
 
     Alpine.start()
 
-    expect(document.querySelector('button').innerText).toEqual(1)
+    expect(document.querySelector('button').textContent).toEqual('1')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('button').innerText).toEqual(2) })
+    await wait(() => { expect(document.querySelector('button').textContent).toEqual('2') })
 
     // Move the component and run the observer
     document.getElementById('b').appendChild(document.querySelector('button'))
     runObservers.forEach(cb => {
         cb([
             {
-                target:  document.getElementById('b'),
+                target: document.getElementById('b'),
                 type: 'childList',
                 addedNodes: [ document.querySelector('button') ],
             }
         ])
     })
 
-    await wait(() => { expect(document.querySelector('button').innerText).toEqual(2) })
+    await wait(() => { expect(document.querySelector('button').textContent).toEqual('2') })
 })

+ 3 - 3
test/nesting.spec.js

@@ -19,13 +19,13 @@ test('can nest components', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await timeout(20)
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('can access parent properties after nested components', async () => {
@@ -41,5 +41,5 @@ test('can access parent properties after nested components', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 })

+ 7 - 7
test/next-tick.spec.js

@@ -10,17 +10,17 @@ test('$nextTick', async () => {
         <div x-data="{foo: 'bar'}">
             <span x-text="foo" x-ref="span"></span>
 
-            <button x-on:click="foo = 'baz'; $nextTick(() => {$refs.span.innerText = 'bob'})"></button>
+            <button x-on:click="foo = 'baz'; $nextTick(() => {$refs.span.textContent = 'bob'})"></button>
         </div>
     `
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
-    await wait(() => expect(document.querySelector('span').innerText).toEqual('bob'))
+    await wait(() => expect(document.querySelector('span').textContent).toEqual('bob'))
 })
 
 test('$nextTick waits for x-for to finish rendering', async () => {
@@ -38,11 +38,11 @@ test('$nextTick waits for x-for to finish rendering', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('p').innerText).toEqual(2)
+    expect(document.querySelector('p').textContent).toEqual('2')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('p').innerText).toEqual(3) })
+    await wait(() => { expect(document.querySelector('p').textContent).toEqual('3') })
 })
 
 test('$nextTick works with transition', async () => {
@@ -63,12 +63,12 @@ test('$nextTick works with transition', async () => {
     Alpine.start()
 
     await wait(() => {
-        expect(document.querySelector('h2').innerText).toEqual('none')
+        expect(document.querySelector('h2').textContent).toEqual('none')
     })
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('h2').innerText).toEqual('')
+        expect(document.querySelector('h2').textContent).toEqual('')
     })
 })

+ 28 - 28
test/on.spec.js

@@ -99,18 +99,18 @@ test('.self modifier', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('bar')
+        expect(document.querySelector('span').textContent).toEqual('bar')
     })
 
     document.querySelector('#selfTarget').click()
 
     await wait(() => {
-        expect(document.querySelector('span').innerText).toEqual('baz')
+        expect(document.querySelector('span').textContent).toEqual('baz')
     })
 })
 
@@ -249,23 +249,23 @@ test('keydown modifiers', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(2) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('2') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: ' ' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(4) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('4') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Spacebar' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(6) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('6') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Escape' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(7) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('7') })
 })
 
 test('keydown combo modifiers', async () => {
@@ -279,15 +279,15 @@ test('keydown combo modifiers', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(0) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('0') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Enter', metaKey: true })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 })
 
 test('keydown with specified key and stop modifier only stops for specified key', async () => {
@@ -303,16 +303,16 @@ test('keydown with specified key and stop modifier only stops for specified key'
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Escape' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
 
     await timeout(25)
-    expect(document.querySelector('span').innerText).toEqual(1)
+    expect(document.querySelector('span').textContent).toEqual('1')
 })
 
 test('click away', async () => {
@@ -320,10 +320,10 @@ test('click away', async () => {
     // make our own implementation using a specific class added to the class. Ugh.
     Object.defineProperties(window.HTMLElement.prototype, {
         offsetHeight: {
-          get: function() { return this.classList.contains('hidden') ? 0 : 1 }
+            get: function () { return this.classList.contains('hidden') ? 0 : 1 }
         },
         offsetWidth: {
-          get: function() { return this.classList.contains('hidden') ? 0 : 1 }
+            get: function () { return this.classList.contains('hidden') ? 0 : 1 }
         }
     });
 
@@ -467,13 +467,13 @@ test('allow method reference to be passed to listeners', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await new Promise(resolve => setTimeout(resolve, 1))
 
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 })
 
 test('event instance is passed to method reference', async () => {
@@ -486,13 +486,13 @@ test('event instance is passed to method reference', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual('bar')
+    expect(document.querySelector('span').textContent).toEqual('bar')
 
     document.querySelector('button').click()
 
     await new Promise(resolve => setTimeout(resolve, 1))
 
-    expect(document.querySelector('span').innerText).toEqual('baz')
+    expect(document.querySelector('span').textContent).toEqual('baz')
 })
 
 test('autocomplete event does not trigger keydown with modifier callback', async () => {
@@ -506,21 +506,21 @@ test('autocomplete event does not trigger keydown with modifier callback', async
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     const autocompleteEvent = new Event('keydown')
 
     fireEvent.keyDown(document.querySelector('input'), { key: 'Enter' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(0) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('0') })
 
     fireEvent.keyDown(document.querySelector('input'), { key: '?' })
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 
     fireEvent(document.querySelector('input'), autocompleteEvent)
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 })
 
 test('.camel modifier correctly binds event listener', async () => {
@@ -533,12 +533,12 @@ test('.camel modifier correctly binds event listener', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('p').innerText).toEqual('bar')
+    expect(document.querySelector('p').textContent).toEqual('bar')
 
     document.querySelector('button').click();
 
     await wait(() => {
-        expect(document.querySelector('p').innerText).toEqual('bob');
+        expect(document.querySelector('p').textContent).toEqual('bob');
     });
 })
 
@@ -552,11 +552,11 @@ test('.camel modifier correctly binds event listener with namespace', async () =
 
     Alpine.start()
 
-    expect(document.querySelector('p').innerText).toEqual('bar')
+    expect(document.querySelector('p').textContent).toEqual('bar')
 
     document.querySelector('button').click();
 
     await wait(() => {
-        expect(document.querySelector('p').innerText).toEqual('bob');
+        expect(document.querySelector('p').textContent).toEqual('bob');
     });
 })

+ 2 - 2
test/readonly.spec.js

@@ -18,7 +18,7 @@ test('read-only properties do not break the proxy', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(0)
+    expect(document.querySelector('span').textContent).toEqual('0')
 
     const input = document.querySelector('input')
     Object.defineProperty(input, 'files', {
@@ -27,5 +27,5 @@ test('read-only properties do not break the proxy', async () => {
 
     input.dispatchEvent(new Event('change'));
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1') })
 })

+ 6 - 6
test/ref.spec.js

@@ -10,22 +10,22 @@ test('can reference elements from event listeners', async () => {
         <div x-data="{}">
             <span x-ref="bob"></span>
 
-            <button x-on:click="$refs['bob'].innerText = 'lob'"></button>
+            <button x-on:click="$refs['bob'].textContent = 'lob'"></button>
         </div>
     `
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(undefined)
+    expect(document.querySelector('span').textContent).toEqual('')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('lob') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('lob') })
 })
 
 test('can reference elements from data object methods', async () => {
     document.body.innerHTML = `
-        <div x-data="{ foo() { this.$refs.bob.innerText = 'lob' } }">
+        <div x-data="{ foo() { this.$refs.bob.textContent = 'lob' } }">
             <span x-ref="bob"></span>
 
             <button x-on:click="foo()"></button>
@@ -34,9 +34,9 @@ test('can reference elements from data object methods', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('span').innerText).toEqual(undefined)
+    expect(document.querySelector('span').textContent).toEqual('')
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('lob') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('lob') })
 })

+ 3 - 3
test/spread.spec.js

@@ -62,9 +62,9 @@ test('x-spread supports x-for', async () => {
 
     Alpine.start()
 
-    expect(document.querySelectorAll('li')[0].innerText).toEqual('one')
-    expect(document.querySelectorAll('li')[1].innerText).toEqual('two')
-    expect(document.querySelectorAll('li')[2].innerText).toEqual('three')
+    expect(document.querySelectorAll('li')[0].textContent).toEqual('one')
+    expect(document.querySelectorAll('li')[1].textContent).toEqual('two')
+    expect(document.querySelectorAll('li')[2].textContent).toEqual('three')
 })
 
 test('x-spread syntax supports x-transition', async () => {

+ 1 - 1
test/strict-mode.spec.js

@@ -26,5 +26,5 @@ test('Proxy does not error in strict mode when reactivity is suspended', async (
 
     Alpine.start()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual(1200) })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('1200') })
 })

+ 18 - 4
test/text.spec.js

@@ -2,7 +2,7 @@ import Alpine from 'alpinejs'
 import { wait } from '@testing-library/dom'
 
 global.MutationObserver = class {
-    observe() {}
+    observe() { }
 }
 
 test('x-text on init', async () => {
@@ -14,7 +14,7 @@ test('x-text on init', async () => {
 
     Alpine.start()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
 })
 
 test('x-text on triggered update', async () => {
@@ -28,9 +28,23 @@ test('x-text on triggered update', async () => {
 
     Alpine.start()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('') })
 
     document.querySelector('button').click()
 
-    await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
+    await wait(() => { expect(document.querySelector('span').textContent).toEqual('bar') })
+})
+
+test('x-text on SVG elements', async () => {
+    document.body.innerHTML = `
+        <div x-data="{ foo: 'bar' }">
+            <svg>
+                <text x-text="foo"></text>
+            </svg>
+        </div>
+    `
+
+    Alpine.start()
+
+    await wait(() => { expect(document.querySelector('text').textContent).toEqual('bar') })
 })

+ 28 - 28
test/watch.spec.js

@@ -17,14 +17,14 @@ test('$watch', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual('bar')
-    expect(document.querySelector('h2').innerText).toEqual('lob')
+    expect(document.querySelector('h1').textContent).toEqual('bar')
+    expect(document.querySelector('h2').textContent).toEqual('lob')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual('baz')
-        expect(document.querySelector('h2').innerText).toEqual('baz')
+        expect(document.querySelector('h1').textContent).toEqual('baz')
+        expect(document.querySelector('h2').textContent).toEqual('baz')
     })
 })
 
@@ -42,14 +42,14 @@ test('$watch nested properties', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual('baz')
-    expect(document.querySelector('h2').innerText).toEqual('lob')
+    expect(document.querySelector('h1').textContent).toEqual('baz')
+    expect(document.querySelector('h2').textContent).toEqual('lob')
 
     document.querySelector('button').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual('law')
-        expect(document.querySelector('h2').innerText).toEqual('law')
+        expect(document.querySelector('h1').textContent).toEqual('law')
+        expect(document.querySelector('h2').textContent).toEqual('law')
     })
 })
 
@@ -71,56 +71,56 @@ test('$watch arrays', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual(['one'])
-    expect(document.querySelector('h2').innerText).toEqual('lob')
+    expect(document.querySelector('h1').textContent).toEqual('one')
+    expect(document.querySelector('h2').textContent).toEqual('lob')
 
     document.querySelector('#push').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual(['one','two'])
-        expect(document.querySelector('h2').innerText).toEqual(['one','two'])
+        expect(document.querySelector('h1').textContent).toEqual('one,two')
+        expect(document.querySelector('h2').textContent).toEqual('one,two')
     })
 
     document.querySelector('#pop').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual(['one'])
-        expect(document.querySelector('h2').innerText).toEqual(['one'])
+        expect(document.querySelector('h1').textContent).toEqual('one')
+        expect(document.querySelector('h2').textContent).toEqual('one')
     })
 
     document.querySelector('#unshift').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual(['zero','one'])
-        expect(document.querySelector('h2').innerText).toEqual(['zero','one'])
+        expect(document.querySelector('h1').textContent).toEqual('zero,one')
+        expect(document.querySelector('h2').textContent).toEqual('zero,one')
     })
 
     document.querySelector('#shift').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual(['one'])
-        expect(document.querySelector('h2').innerText).toEqual(['one'])
+        expect(document.querySelector('h1').textContent).toEqual('one')
+        expect(document.querySelector('h2').textContent).toEqual('one')
     })
 
     document.querySelector('#assign').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual([2,1,3])
-        expect(document.querySelector('h2').innerText).toEqual([2,1,3])
+        expect(document.querySelector('h1').textContent).toEqual('2,1,3')
+        expect(document.querySelector('h2').textContent).toEqual('2,1,3')
     })
 
     document.querySelector('#sort').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual([1,2,3])
-        expect(document.querySelector('h2').innerText).toEqual([1,2,3])
+        expect(document.querySelector('h1').textContent).toEqual('1,2,3')
+        expect(document.querySelector('h2').textContent).toEqual('1,2,3')
     })
 
     document.querySelector('#reverse').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual([3,2,1])
-        expect(document.querySelector('h2').innerText).toEqual([3,2,1])
+        expect(document.querySelector('h1').textContent).toEqual('3,2,1')
+        expect(document.querySelector('h2').textContent).toEqual('3,2,1')
     })
 })
 
@@ -136,13 +136,13 @@ test('$watch nested arrays', async () => {
 
     Alpine.start()
 
-    expect(document.querySelector('h1').innerText).toEqual(['one'])
-    expect(document.querySelector('h2').innerText).toEqual('lob')
+    expect(document.querySelector('h1').textContent).toEqual('one')
+    expect(document.querySelector('h2').textContent).toEqual('lob')
 
     document.querySelector('#push').click()
 
     await wait(() => {
-        expect(document.querySelector('h1').innerText).toEqual(['one','two'])
-        expect(document.querySelector('h2').innerText).toEqual(['one','two'])
+        expect(document.querySelector('h1').textContent).toEqual('one,two')
+        expect(document.querySelector('h2').textContent).toEqual('one,two')
     })
 })