Language-tools: Resumen de enfoques en un servidor de idiomas

Creado en 24 mar. 2020  ·  37Comentarios  ·  Fuente: sveltejs/language-tools

Este hilo está destinado a una descripción general de los enfoques sobre cómo implementar un servidor de idiomas para archivos esbeltos. Aquí también se puede discutir una solución preferida.

Estado actual del servidor de idiomas

Actualmente, el resaltado de sintaxis y algunas funciones básicas de autocompletado funcionan para archivos esbeltos.
Sin embargo, no se proporciona intellisense enriquecido, no conoce todos los archivos mecanografiados en el espacio de trabajo y tampoco tiene información de tipo sobre los componentes esbeltos.
Lo mismo es cierto para la parte html donde se resalta la sintaxis esbelta especial pero intellisense no funciona. Se utiliza el vscode-html-languageservice que no conoce la sintaxis especial por defecto.
El servidor de idioma actual usa el compilador svelte para analizar archivos y obtener diagnósticos como "sin atributo alt en esta etiqueta img".
Un análisis más en profundidad: comentario

Enfoques / soluciones existentes

Descargo de responsabilidad: no soy el autor de ninguna de las soluciones existentes, sin embargo, miré el código. Si parte de la información es incorrecta o no es lo suficientemente detallada, o le falta una solución, no dude en comentar, la ajustaré.

https://github.com/alexprey/sveltedoc-parser

Utiliza htmlparser2 para analizar la parte html de un archivo esbelto. Utiliza ganchos del analizador html para agregar análisis personalizados y extraer información relevante.
Utiliza espree para analizar las partes del script de un archivo esbelto. Luego recorre el AST para extraer información relevante.
Proporciona los resultados en formato JSON.

https://github.com/ArdenIvanov/svelte-intellisense lo usa para analizar archivos esbeltos. Utiliza el resultado para adjuntar algún comportamiento adicional.

Dado que utiliza un analizador de JavaScript, no funcionará con mecanografiado.

https://github.com/halfnelson/svelte2tsx

Utiliza el compilador de svelte para analizar la parte HTMLx de un componente esbelto. Luego transforma el HTMLx más la parte del script original en un archivo tsx con un transformador autoescrito.

https://github.com/simlrh/svelte-language-server/blob/feature/extend-ts-support/src/plugins/ts-svelte/service.ts (una bifurcación de svelte-language-server) lo usa para cree archivos shadow-tsx de archivos svelte. Luego usa el servicio de lenguaje mecanografiado, pero envía las solicitudes por proxy: reemplaza la ruta del archivo original con la ruta del archivo al archivo tsx generado. Por lo tanto, el servidor de idioma mecanografiado verifica los archivos tsx generados. Los resultados se vuelven a transformar para obtener las posiciones correctas de los documentos.

https://github.com/halfnelson/svelte-type-checker-vscode es otro servidor de idiomas que utiliza la biblioteca svelte2tsx.

https://github.com/marcus-sa/svelte-ts

Descripción tomada del comentario del

Compilador:

  1. El compilador lee todos los archivos fuente.
  2. Todo, excepto el contenido de las etiquetas de secuencia de comandos, se desecha.
  3. El código fuente del script se compila en archivos JS y de declaración válidos.
  4. Vuelve al paso 2, donde reemplaza la fuente de TS en las etiquetas de script con el JS compilado
  5. El compilador de Svelte compila todo el archivo fuente
  6. La compilación de salida del paso anterior se almacena en caché, donde luego se usa HTML AST en el verificador de tipos.

Consume las variables y funciones exportadas desde el interior de un script Svelte y luego las genera como archivos de declaración que contienen una clase que amplía el tiempo de ejecución SvelteComponent.

Comprobador de tipos:

  1. El verificador de tipo encuentra todos los componentes en el AST almacenado en caché.
  2. Comprueba que todos los componentes de la plantilla son una clase válida que amplía SvelteComponent
  3. Verificaciones de declaraciones válidas, propiedades, etc.

Otros enfoques

Ver comentarios de este hilo.

Comentario más útil

¡Vale genial! Seguiré adelante y haré algunos RP reestructurando parte del código y preparándolo para que luego podamos paralelizar el trabajo en elementos individuales.

Todos 37 comentarios

¿Entonces ninguno de ellos está usando el analizador Svelte?

svelte2tsx elimina las etiquetas de estilo / script y luego usa el compilador svelte para analizar la parte htmlx.
svelte-ts usa el compilador svelte como segundo paso.
El servidor de idioma actual no usa el compilador svelte para analizar.
Actualizaré la publicación inicial.

No estaba seguro de si debería compartir esto, pero una parte podría ser útil. El año pasado (julio / agosto de 2019) comencé a jugar con la verificación de tipos de Svelte, sabiendo que estaba haciendo trabajos desechables solo para aprender. Admite una cantidad desconocida de una solución completa, tal vez el 30%, y tiene algunas limitaciones (más sobre esto más adelante). No hay pruebas ni documentación externa porque siempre lo he considerado un callejón sin salida.

La compilación de Svelte funciona como lo hace hoy con un preprocesador TS sin control de tipos. Si quisiera tipos en su marcado HTMLx, no solo en su etiqueta de script, necesitaría un preprocesador para manejar eso, o Svelte necesitaría manejar TS internamente.

El comprobador de tipos es un paso de compilación independiente que utiliza el código TypeScript sin preprocesar de los bloques script y el marcado AST de svelte.compile para crear un archivo TypeScript de Svelte virtual únicamente con fines de comprobación de tipos. (aquí es donde el complemento Rollup crea el archivo virtual y luego lo verifica ) Es similar a la estrategia de @halfnelson , pero diferente en que produce TS normal, no TSX, por lo que en el marcado no intenta verificar las etiquetas HTML y atributos, solo lo que hay dentro de las etiquetas de bigote y los componentes de Svelte. Transforma los componentes de Svelte en constructores que obtienen new ed con sus accesorios. Llegué a la mitad del apoyo a muchas de las construcciones de Svelte antes de darme cuenta de que me dejé llevar más de lo que pretendía, y el progreso adicional debería ser Ingeniería seria.

La API del compilador de TypeScript se utiliza para construir un programa en la memoria y verificar el tipo de Svelte TS virtual. A diferencia de Svelte, su visión del mundo son programas completos, no archivos individuales. En ese momento, parecía que sería sencillo agregar compilación incremental y subprocesos de trabajo como optimizaciones futuras.

El archivo virtual de Svelte TS se genera con un mapa fuente que luego se utiliza para mapear los diagnósticos de TypeScript de nuevo a la fuente Svelte original . Fue genial ver que los errores de tipo apuntan a la fuente de Svelte, pero no tenía confianza en mi enfoque en general.

Un problema que recuerdo que no pude resolver fue con los diagnósticos que no eran lo suficientemente específicos en algunos casos. Un ejemplo de IIRC fue con la verificación de tipos de accesorios del constructor de componentes Svelte: VSCode de alguna manera apuntaría a errores de accesorios individuales, pero los diagnósticos de TS que estaba obteniendo apuntaban solo al constructor, no a la propiedad del problema.

Si revisa el código, tenga en cuenta que las partes son un desastre y algunos comentarios pueden estar desactualizados. Originalmente usé magic-string y en algún momento cambié a source-map . ¡Y recuerde que fue un esfuerzo condenado al fracaso desde el principio! Tenga en cuenta que todo esto está en la rama ts de ese repositorio, no en master .

Aquí están los 4 archivos mencionados anteriormente:

Investigué un poco el código fuente del servidor de idiomas esbelto actual. Aquí están mis pensamientos sobre la parte mecanografiada:

Visión general

El servidor de idiomas funciona ampliamente así:

  • DocumentManager maneja todos los documentos que se abren.
  • TypescriptPlugin registra en DocumentManager para ser invocado en eventos del servidor de idiomas ("doHover", etc.). No se registra directamente: se envuelve con wrapFragmentPlugin lo que garantiza que TypescriptPlugin solo obtenga el contenido dentro de la etiqueta script . El mapeo de posiciones (del mouse / cursor de texto) se realiza en wrapFragmentPlugin para garantizar que las informaciones se muestren en la posición correcta.
  • TypescriptPlugin usa service , un contenedor de servicios de lenguaje mecanografiado. Básicamente, delega todos los eventos a este servidor de idiomas, con algunos mapeos para adherirse a los tipos de retorno del método.
  • service toma el documento para trabajar también como un método createDocument de TypescriptPlugin . service mantiene su propio mapa de documentos que en este momento son básicamente solo un delegado al documento de DocumentsManager . Cada vez que se invoca service desde TypescriptPlugin , service actualiza su mapa de documentos con el documento dado (después de envolverlo). createDocument se invocaría si el servicio de idiomas abre un documento que aún no es parte del mapa de documentos, pero esto nunca sucederá ya que el servicio de idiomas mecanografiado no encuentra otros módulos esbeltos (consulte la siguiente sección).

Deficiencias actuales e ideas de mejora.

  • Dado que la etiqueta de script se proporciona tal cual al servicio de lenguaje mecanografiado, no puede tratar con una sintaxis esbelta especial ($).
  • El servicio de lenguaje mecanografiado intenta buscar otros archivos a los que se hace referencia en el archivo svelte abierto actualmente. Para archivos .ts / .js esto funciona, para archivos .svelte no. Razón: El servicio de lenguaje mecanografiado no conoce el final .svelte , por lo que simplemente asume que es un archivo mecanografiado normal y busca archivos como ../Component.svelte.ts , lo cual es incorrecto. Para solucionar este problema, debemos implementar el método resolveModuleNames en LanguageServiceHost y redirigir todas las búsquedas de archivos .svelte.ts a .svelte . Luego, el servicio de idiomas recorrerá todos los archivos esbeltos. NOTA: Para implementar esto, también necesitamos corregir el siguiente error: Dado que el servicio de lenguaje mecanografiado ahora recorrería todos los archivos, invocaría createDocument desde TypescriptPlugin . Pero esto actualmente devuelve todo el documento, no solo el contenido dentro de la etiqueta del script. Este es un error dentro de wrapFragmentPlugin no envuelve openDocument .
  • Para implementar funciones más avanzadas como "ir al archivo svelte" haciendo clic en el nombre de importación del componente svelte, o hacer que el mecanografíe grog los signos $, necesitamos agregar un preprocesador. Este preprocesador de alguna manera agregaría las partes faltantes para el mecanografiado. Idealmente, todas las cosas adicionales se pueden poner encima del código existente, de modo que mapear las posiciones sea tan fácil como mapear las posiciones con compensaciones. Sin embargo, esto plantea otro desafío: ahora tendríamos dos asignaciones de posición: una desde el archivo esbelto completo a la etiqueta de secuencia de comandos, y otra desde la etiqueta de secuencia de comandos al código mecanografiado preprocesado. No estoy seguro en este punto si este es el camino a seguir o si deberíamos volver a trabajar cómo TypescriptPlugin (y otros) obtienen su documento; tal vez tanto la extracción de la etiqueta del script como el preprocesamiento deberían realizarse en un solo paso . Esto significaría cambiar o reemplazar completamente el wrapFragmentPlugin . Otro argumento para volver a trabajar es que, para determinar correctamente cosas como "función no utilizada", tenemos que tener en cuenta la parte html, por lo que la etiqueta de script no es suficiente.

¿Pensamientos?

En resolveModuleNames , así es como un miembro del equipo de TypeScript lo hizo para Vetur , y aquí hay una versión similar que hice para Svelte, que incluye un comentario que menciona una estrategia alternativa que funcionó, pero copiar Vetur parecía mejor.

@dummdidumm, ¿te importaría actuar como líder tecnológico y dividir estas cosas en problemas específicos? Estaría feliz de ser voluntario en tiempo de desarrollo, pero encuentro mucho de esto abrumador y funcionaría mejor en problemas pequeños y definidos. A menos que haya un gran cambio arquitectónico que deba hacerse (¿parece que no?)

Básicamente, simplemente abra un montón de problemas y deje que la gente los reclame y los administre como nuestro líder tecnológico informal en esto. Me ofrecería hacerlo pero, honestamente, todavía no he sentido el dolor jaja

también enlazando a la publicación LSP original de orta para otros que están navegando https://github.com/sveltejs/svelte/issues/4518

Seguro que me encantaría ayudar en esto, pero ahora mismo soy un tipo cualquiera que está muy entusiasmado con el servidor de idiomas, investigando el código y haciendo algunas sugerencias 😄 También necesito algo más de tiempo para familiarizarme con el código, su arquitectura y lsp en general. Pero definitivamente estoy dispuesto a impulsar las cosas. @orta ya que eres el mantenedor de este repositorio, ¿qué opinas de esto?

👋 Sí, los dos somos personas al azar a las que les importa, ni siquiera tengo una base de código esbelta para desencadenar mi picazón de "esto debería ser mejor". Por lo tanto, estoy muy feliz de ver cualquier movimiento, @dummdidumm , aléjese y lo respaldaré y trataré de asegurarme de que todo funcione sin problemas.

¡Vale genial! Seguiré adelante y haré algunos RP reestructurando parte del código y preparándolo para que luego podamos paralelizar el trabajo en elementos individuales.

Tal vez podríamos combinar @ryanatkn , el enfoque de svelete-ts y el enfoque de variable de eslint-plugin-svelte3 :

  1. Utilice un preproceso esbelto para preprocesar el script en js.
    Aquí podríamos simplemente transpilar a la última o próxima versión de ES para hacer la menor transformación posible.
  2. Deje que svelte compile la fuente preprocesada.
  3. Luego podemos inyectar variables de resultado donde injected = true en la instancia ts source o ts AST y luego usarlo con el mapa fuente para proporcionar verificación de tipo y autocompletado.

El script puede tener tres fragmentos: módulo <script context="module"> , instancia y bigote (plantilla). La parte del bigote podría simplemente volcarse en un template.js por ahora. Pero, a largo plazo, podríamos reflejar la plantilla en tsx o ts sencillos como el enfoque de ryanatkn.

Variables del módulo, donde module = true , se inyectarían en template.js y en la instancia, pero no al revés.

De esta manera, no tenemos que volver a implementar cómo encontrar la variable reactiva $: a = 1 o $ -prefixed store en AST mecanografiado. Estas variables inyectadas por el compilador esbelto se derivan de una declaración de nivel superior y la estamos transpilando al último ES. Por lo tanto, la mayoría de las veces no debería cambiarse el nombre por mecanografiado.

Acerca de la variable reactiva $: a podría escribirse así:

$: a = 1

ts
deja un: número
$: a = 1

```ts
$: a = someFunction(b) as Type

Todos son ts válidos que no necesitan transformarse antes de transpile

Y $ -prefixed store podemos crear una función genérica como svelte / store get to extract store type

/** injected */
let $store = getType(store)
  1. Utilice un preproceso esbelto para preprocesar el script en js.
    Aquí podríamos simplemente transpilar a la última o próxima versión de ES para hacer la menor transformación posible.
  2. Deje que svelte compile la fuente preprocesada.
  3. Luego podemos inyectar variables de resultado donde injected = true en la instancia ts source o ts AST y luego usarlo con el mapa fuente para proporcionar verificación de tipo y autocompletado.

Creo que svelte-ts hace algo así. Parece una buena idea. En injected = true : ¿Qué código tiene exactamente esta propiedad?

El script puede tener tres fragmentos: módulo <script context="module"> , instancia y bigote (plantilla). La parte del bigote podría simplemente volcarse en un template.js por ahora. Pero, a largo plazo, podríamos reflejar la plantilla en tsx o ts sencillos como el enfoque de ryanatkn.

Variables del módulo, donde module = true , se inyectarían en template.js y en la instancia, pero no al revés.

De esta manera, no tenemos que volver a implementar cómo encontrar la variable reactiva $: a = 1 o $ -prefixed store en AST mecanografiado. Estas variables inyectadas por el compilador esbelto se derivan de una declaración de nivel superior y la estamos transpilando al último ES. Por lo tanto, la mayoría de las veces no debería cambiarse el nombre por mecanografiado.

Entonces, ¿desea dividir el archivo en un archivo virtual .ts y un archivo .template ? ¿Por qué no tener las partes del bigote en la parte inferior del archivo .ts ? De esta manera también nos desharíamos de las advertencias de "método no utilizado". Sobre caminar el AST: Sí, creo que podemos ser inteligentes aquí y saltarnos demasiado el AST porque todo lo que se hace referencia en la plantilla debe ser de nivel superior en el script.

Acerca de la variable reactiva $: a podría escribirse así:

$: a = 1
let a: number
$: a = 1

o

$: a = someFunction(b) as Type

Todos son ts válidos que no necesitan transformarse antes de transpile

¿Es posible inferir el tipo? Quizás esto es algo que dejamos como está y si el usuario quiere usar mecanografiado, tiene que definir let a: number él mismo. Como alternativa, podríamos insertar let a; , pero luego sería any .

Y $ -prefixed store podemos crear una función genérica como svelte / store get to extract store type

/** injected */
let $store = getType(store)

Sí, esto parece agradable. Otra idea que tuve fue la de construir getters / setters.

Creo que svelte-ts hace algo así. Parece una buena idea. En injected = true : ¿Qué código tiene exactamente esta propiedad?

El resultado de la compilación tiene una propiedad vars, que es una matriz de objeto que tiene la siguiente propiedad:
nombre, export_name, inyectado, módulo, mutado, reasignado, referenciado_de_script y escribible
puedes ver svelte compile api para más información

Entonces, ¿desea dividir el archivo en un archivo .ts virtual y un archivo .template? ¿Por qué no tener las partes del bigote en la parte inferior del archivo .ts? De esta manera también nos desharíamos de las advertencias de "método no utilizado".

Este es el enfoque de eslint-plugin-svelte3 . También pensé si queremos admitir ts en la plantilla porque no hay ningún atributo para especificar el idioma.

¿Es posible inferir el tipo?

Dado que esta fuente virtual solo se usa para la verificación de tipo y el autocompletado, supongo que se puede lograr con solo copiar la primera instrucción de asignación para inicializarla, pero parece que se necesita más trabajo.

¿Es posible inferir el tipo? Quizás esto es algo que dejamos como está y si el usuario quiere usar mecanografiado, tiene que definir let a: number él mismo. Como alternativa, podríamos insertar let a ;, pero luego sería any.

Parece que la inferencia normal no nos lleva hasta allí. (¿te refieres a la inferencia en tiempo de compilación? No estoy seguro de si eso podría funcionar)

let a; // type is "any" initially
$: a = 5;
a; // type is now "number", good
const cb = () => a; // type is implicitly "any", noo

La razón es que en el flujo de código lineal, TypeScript puede ver que a no se reasignó, pero los ámbitos de función anidados no tienen esa garantía, y TypeScript se equivoca en el lado de la seguridad de tipos.

ejemplo de patio de recreo

Exactamente, estas eran mis preocupaciones con eso. Pero como señaló @ jasonlyu123 , podríamos simplemente copiar la definición (todo después de = ).

Antes

$: a = true;

Después

let a = true;
$: a = true;

Pero no sé si eso es factible en todas las circunstancias. Especialmente con objetos grandes donde alguien implementa una interfaz, esto no inferirá todo como el usuario podría esperar. Además, la pasta de copia podría ser bastante grande. Así que todavía estoy más del lado de "dejar que el usuario lo escriba".

Gracias por aclarar, entendí mal lo que dijo

¿Hay algún ejemplo de cuando ese no sea un buen comportamiento predeterminado? Parece que hace lo ergonómico correcto la mayor parte del tiempo, y otros casos se pueden arreglar manualmente. He aquí algunos ejemplos.

Mhm supongo que tienes razón. Es lo más ergonómico. Lo que me preocupa es si el tipo inferido es incorrecto, por ejemplo, la propiedad es string lugar de una unión definida de cadenas ( 'a' | 'b' ). Pero como dijiste, estos casos no son tan comunes y el usuario aún puede escribirlos manualmente.

Hay una parte de mí que es como: "genial, la sintaxis explícita de let funciona de maravilla", pero eso no funcionaría con el soporte de JS listo para usar.

¿Qué tal simplemente reemplazar la primera etiqueta para permitir:

$: a = 1
$: a = 2

conseguir transformar a

/** injected  */
let a = 1
$: a = 2

Lo reconsidero un poco y no puedo encontrar la ventaja de "copiar definición" sobre "reemplazar $: con let ". ¿Pueden pensar en alguna desventaja de reemplazar $: con let ?

Acerca de la diferencia de tipo inferido con lo que esperaba el usuario, ¿podemos proporcionar acciones de refactorización para convertirlo en definición manual? Esta acción le pedirá al usuario que escriba manualmente la variable como esta , o refactorice a su tipo inferido si es posible.

antes de

$: a = 'const1'

después

let a : AskUserForType
$: a = 'const1'

También piensa en las cosas que señala, aprendió algo 👍

Ja, idea inteligente, simplemente reemplace completamente el $: por un let . Tampoco conozco situaciones en las que eso terminaría mal.
También podríamos pensar en hacer siempre la transformación, no solo para la primera etiqueta. Entonces, habría un error de "no se puede volver a declarar" que sugiere al usuario "woops, definí esta variable dos veces", o ¿hay situaciones en las que esto sería viable y no un error?

Bien, no puedo pensar en ninguna razón por la que el reemplazo de la etiqueta tampoco podría fallar. (ya que break $ nunca funciona dentro de las asignaciones, ¿verdad?)

Para volver a declarar la misma variable reactiva, parece que Svelte lo permite y funciona en el orden esperado. ( ver este ejemplo de respuesta ) Personalmente no me veo usando este patrón intencionalmente y querría el error, pero está en el territorio de interpretar la semántica de Svelte, ¡así que no confíe en mi palabra!

Está bien, así que redeclararlo es un patrón que realmente funciona. Yo diría que es mejor que lo permitamos entonces, o que hagamos una marca de característica de "redeclarar no permitido" más tarde, que está desactivada de forma predeterminada.

@halfnelson ha creado un servidor de idiomas basado en su enfoque svelte2tsx en https://github.com/halfnelson/svelte-type-checker-vscode . Realmente me encanta lo bien que funciona todo esto, pero aún así preferiría no usar tsx como una salida intermedia porque me temo que podría ser demasiado lento y tener algunas limitaciones, pero esa es solo mi intuición. Aún así, podríamos sacar mucho de svelte2tsx: cómo se realiza el flujo de análisis-compilación-transformación, y también el extenso conjunto de pruebas. ¿Cómo ven los demás el enfoque de transformar el código en tsx?

Pensé en cómo podrían verse otras transformaciones. Aquí hay unos ejemplos. Comentarios bienvenidos.

Cómo se ve la definición de un componente

Original:

<script>
    import { createEventDispatcher } from 'svelte';
    const dispatch = createEventDispatcher();

    export let todo: string;

    function doneChange() {
        dispatch('doneChange', true);
    }
</script>

Convertido:

export default class Component {
   constructor(props: {todo: string; 'on:doneChange': (evt: CustomEvent<boolean>) => void}) {}
}

import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();

export let todo: string;

function doneChange() {
    dispatch('doneChange', !todo.done);
}

Explicación:
De alguna manera obtenga todos los accesorios (fácil) y los eventos enviados (difíciles) y agréguelos a un objeto grande de accesorios. Agregar eventos con on: -prefix debería minimizar las colisiones y facilitar la implementación de las sugerencias de autocompletar.

(en los siguientes ejemplos, la definición de componente se omite por brevedad)

Usar variable en plantilla

Original:

<script>const bla = {bla: 'bla'};</script>
<p>{bla.bla}</p>

Convertido:

const bla = 'bla';
const _bla = bla.bla;

Explicación:
Agregue elementos const arbitrarios, tal vez con el prefijo de algún carácter unicode oscuro, para verificar el uso correcto de las variables y también para hacer desaparecer la advertencia de "no usado". Quizás entonces debamos pensar en cómo deshacernos de la advertencia _bla utilizada ...

Utilice un detector de eventos nativo

Original:

<script>function bla() {return true;}</script>
<button on:click={bla}>bla</button>

Convertido:

function bla() {return true;}
const btn = document.createElement('button');
btn.addEventListener('click', bla);

Explicación:
Cuando utilice detectores de eventos nativos, cree el elemento correspondiente y luego agregue un detector de eventos. De esta manera aprovechamos los tipos addEventListener que tienen sobrecargas para casi todos los oyentes: .addEventListener('click', ...); -> el evento es del tipo MouseEvent . Este patrón se usaría después de haber verificado si el elemento es otro componente esbelto que tiene el evento definido (ver también el siguiente ejemplo). Deficiencias: ¿Qué pasa si Svelte se utiliza en un entorno no web (Nativescript)?

Utilice otro componente

Original:

<script>
import Bla from './bla.svelte';
function blub() {}
</script>
<Bla bla={1} on:blubb={blub} />

Convertido:

import Bla from './bla.svelte';
function blub() {}
const bla = new Bla({bla: 1, 'on:blubb': blub});

Explicación:
Cada componente obtiene una definición de clase, por lo que simplemente instanciamos esa clase.

Cada bucle

Original:

{#each todos as todo,i (todo.id)}
    <p>{todo.text}</p>
{/each}

Convertido:

todos.forEach((todo, i) => {
    const _todoText = todo.text;
});

Explicación:
forEach parece el más fácil de convertir. En el interior, podemos aplicar de forma recursiva todas las demás funciones.

Si

Original:

{#if x === 1}
    <p>if</p>
{else if x === 2}
    <p>elseif</p>
{else}
    <p>else</p>
{/if}

Convertido:

if (x === 1) {

} else if (x === 2) {

} else {

}

Explicación:
Bastante autoexplicativo.

Piezas perdidas

  • ranuras
  • componentes svelte:x
  • otras directivas de elemento / componente como use:action , transition , bind
  • $ -sintaxis

¿Qué piensan ustedes? ¿Son estas transformaciones viables y factibles? ¿Me he perdido algo?

Acerca de la clase de componente, preferiría extender la propia clase SvelteComponent de svelte, pero no sé si esto es fácil de implementar o no

import { SvelteComponent } from "svelte";

export default class Component extends SvelteComponent {
    constructor(options: ComponentOption<{ propA: string }>) {
        super(options)
    }

    $on(
        event: 'input',
        callback: (event: CustomEvent<string>) => void
    ): () => void
    $on(
        event: 'click',
        callback: (event: CustomEvent<number>) => void
    ): () => void 
    $on(
        event: string,
        callback: (event: CustomEvent<any>) => void
    ): () => void {
        return () => { }
    }
}

e importar esta interfaz

interface ComponentOption<TProps, TSlot = undefined> {
    target: Element,
    props: TProps | SlotProp<TSlot>
}

type SlotOption<T> = [unknown, unknown, unknown]

interface SlotProp<TSlot> {
    $$slots: Record<string, SlotOption<TSlot>>
}

Notado que $$slots es probablemente la api interna de svelte, simplemente la excavo desde el código compilado

La razón de esto es que luego se puede usar para emitir .d.ts que se podría usar para escribir vanilla ts / js.
También porque, aunque es bastante extraño, esta es una sintaxis válida:

<script>
onMount(() => {
    new Component({
        target: document.getElementById('foo')
    })
})
</script>

Directivas de elementos

Transition , use:action y otras directivas de elementos podrían transformarse como

fade(null as Element, {  } /* option in attribute*/)

Esperar

{#await promise}
    <p>...waiting</p>
{:then number}
    <p>The number is {number}</p>
{:catch error}
    <p style="color: red">{error.message}</p>
{/await}

para

    promise.then(number => {  number })
    promise.catch(error => { error.message })

enlazar: esto

<script>
let a;
</script>
<div bind:this={a}><div>

para

let a;
onMount(() => {
 a = document.createElement('div')
})

necesitaba envolverlo en devolución de llamada porque no está disponible de inmediato

context = "módulo"

Esto es un poco complicado

<script context="module">
 let count = 1;
</script>

<script>
import _ from "lodash"
let b;
</script>

debe ser compilado para

import _ from "lodash"

let count = 1;

// any block is ok
{
    let b;
{

¿Y también qué pasa si el usuario de alguna manera usa un lenguaje diferente en dos secuencias de comandos, como js en el módulo y ts en la instancia?

Bien, luciendo bien. Para "Usar variable en la plantilla" const _bla = bla.bla; podría ser solo bla.bla; dejar que TypeScript haga su verificación sin necesidad de filtrar el diagnóstico de "var no utilizada". No puedo pensar en ningún caso de esquina en el que esto no funcione.

Algunos miembros del equipo de Vue están explorando un complemento de servidor de idioma mecanografiado: https://github.com/znck/vue-developer-experience

Es un ángulo interesante ya que hoy en día estas experiencias van desde fuera de TypeScript y dentro (como vetur, ya que ejecuta su propio tsserver-ish, así como html / css / etc LSP) o intenta ir desde dentro de TypeScript y trabajar hacia afuera ( por ejemplo, esta propuesta) donde manipula el TSServer para creer efectivamente que los archivos .vue son TypeScript legítimo al enmascarar el código que no es TS.

Aunque hoy solo puede funcionar con parches para mecanografiar

Ese es un enfoque interesante. Pero, ¿cómo funcionarían entonces funciones como el autocompletado para html o css? ¿Tendría que implementar todo esto "a mano" y no depender más de los servidores de idiomas listos para usar, o me estoy perdiendo algo?

¿Ves alguna ventaja de hacer uno tras otro?

Cualquier cosa menos el soporte de JS / TS sería manejado por un vscode LSP separado. Sería como si esta extensión eliminara todo el soporte para JS / TS y solo manejara el resto, entonces el complemento mecanografiado se encargaría del resto.

En cuanto a las ventajas, obtiene todas las herramientas de TS "gratis" en este caso, salte al símbolo, asignaciones de archivos, etc.

Probablemente no lo recomiendo, hoy en día requiere bifurcaciones o parches de TypeScript para funcionar, lo cual es una barrera de entrada bastante alta, y el equipo de TypeScript aún no está seguro de si / cuándo los complementos del compilador podrían tener ese tipo de acceso.

Gracias por las ideas. Diría que estamos con el enfoque actual entonces (de afuera hacia adentro).

Si seguimos el enfoque de "hacer un archivo ts virtual" discutido anteriormente, ¿cómo implementaríamos esto y mantendríamos las asignaciones de fuentes?

Hoy en día, es fácil porque es simplemente "buscar el inicio del script, encontrar el final del script", "extraer parte del script" y para mapear "agregar el desplazamiento del archivo esbelto a las posiciones".
Si cambiamos la parte del script ahora, necesitamos un mapeo más sofisticado:

Original

<script lang="typescript">
  let a = 1;
  $: b = a + 1;
</script>
<p>{b}</p>

Mapeado:

export default class Bla extends SvelteComponent { ... } // <- prepended, no mapping needed as far as I know
let a = 1; // <- untouched
let b = a + 1; // <- modified in place. How to map? Do we need to adjust all following code positions now to have a new offset/position?
b; // <- appended. Needs mapping from {b} inside svelte-mustache-tag to here. We can get the original position from the html ast which is part of the output of svelte.parse

¿Algunas ideas? Nunca antes hice cosas de mapeo de fuentes.

Algunos miembros del equipo de Vue están explorando un complemento de servidor de idioma mecanografiado: https://github.com/znck/vue-developer-experience

Es un ángulo interesante ya que hoy en día estas experiencias van desde fuera de TypeScript y dentro (como vetur, ya que ejecuta su propio tsserver-ish, así como html / css / etc LSP) o intenta ir desde dentro de TypeScript y trabajar hacia afuera ( por ejemplo, esta propuesta) donde manipula el TSServer para creer efectivamente que los archivos .vue son TypeScript legítimo al enmascarar el código que no es TS.

Aunque hoy solo puede funcionar con parches para mecanografiar

Se fusiona el parche de eliminación de TypeScript PR.
https://github.com/znck/vue-developer-experience/pull/7

Entonces ... @orta, ¿ esto significa que las desventajas con respecto a lo "no oficial" ya no son el caso? ¿O todavía no es oficial, simplemente no a través de un parche, sino a través de una configuración de package.json que debe conocer y que podría cambiar en cualquier momento?

Otro tema: en este momento siento que todavía estamos discutiendo cómo abordar esto mejor, y si decidimos, llevará algún tiempo implementarlo. ¿Qué piensan ustedes de usar svelte2tsx mientras tanto? Podría servir como un muy buen punto de partida, podríamos hacer algunas pruebas y luego ver "ok, esto realmente funciona muy bien, no lo cambiemos" o migrar gradualmente. ¿Pensamientos?

Sí, había dos formas de editar el flujo predeterminado de TS, todavía está parcheando TypeScript, pero en tiempo de

Sin embargo, sería un mal miembro del equipo central si lo recomendara;)

¡Probar svelte2ts parece una buena vía de exploración!

Puede ver cómo sería con svelte2tsx usando este complemento de vscode: https://marketplace.visualstudio.com/items?itemName=halfnelson.svelte-type-checker-vscode junto con Svelte Beta (funciona mejor si deshabilita las versiones beta svelte opciones de mecanografiado para que no obtenga el doble de pistas :))

Incluso si no utilizo svelte2tsx, veo una discusión sobre qué transformaciones deben realizarse en el código JS de svelte para mantener feliz a la escritura mecanografiada. El conjunto de pruebas para svelte2tsx cubre no solo los cambios que se deben realizar en la plantilla, sino también la etiqueta de script, que si busca una solución de solo etiqueta de script podría ser un buen punto de partida: https://github.com/halfnelson / svelte2tsx / tree / master / test / svelte2tsx / samples

htmlx2jsx es la carpeta donde la plantilla cambia las pruebas (htmlx), mientras que las muestras svelte2tsx son los cambios específicos necesarios para el abuso especial de svelte de la sintaxis js :)

Acabo de descubrir este repositorio https://github.com/pivaszbs/svelte-autoimport . Quizás valga la pena investigar si queremos importar intellisense independiente de svelte2tsx .

Creo que estamos en un punto en el que la mayor parte de la dirección general de este proyecto está ordenada y parece estar funcionando perfectamente. Le daré una semana para recibir comentarios, pero estoy muy feliz de que no necesitemos repensarlo arquitectónicamente ahora.

Parece que nadie tiene objeciones, así que cerraré esta. ¡Gracias a todos por discutir esto!

¿Fue útil esta página
0 / 5 - 0 calificaciones