Language-tools: Présentation des approches sur un serveur de langage

Créé le 24 mars 2020  ·  37Commentaires  ·  Source: sveltejs/language-tools

Ce fil est destiné à un aperçu des approches sur la façon d'implémenter un serveur de langue pour les fichiers sveltes. Une discussion sur une solution préférée peut également être effectuée ici.

État actuel du serveur de langue

Actuellement, la coloration syntaxique et certains autocomplétion de base fonctionnent pour les fichiers sveltes.
Cependant, l'intellisense riche n'est pas fourni, ne connaît pas tous les fichiers dactylographiés dans l'espace de travail et n'a également aucune information de type sur les composants sveltes.
La même chose est vraie pour la partie html où la syntaxe svelte spéciale est mise en évidence mais intellisense ne fonctionne pas. Le vscode-html-languageservice est utilisé qui ne connaît pas la syntaxe spéciale par défaut.
Le serveur de langue actuel utilise le compilateur svelte pour analyser les fichiers et obtenir des diagnostics tels que "pas d'attribut alt sur cette balise img".
Une analyse plus approfondie : Commentaire

Approches/solutions existantes

Avis de non-responsabilité : je ne suis l'auteur d'aucune des solutions existantes, j'ai cependant examiné le code. Si certaines informations sont erronées ou pas assez détaillées, ou qu'il vous manque une solution, n'hésitez pas à commenter, je les ajusterai.

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

Utilise htmlparser2 pour analyser la partie html d'un fichier svelte. Utilise les hooks de l'analyseur html pour ajouter une analyse personnalisée et extraire les informations pertinentes.
Utilise espree pour analyser les parties de script d'un fichier svelte. Ensuite, marche l'AST pour extraire les informations pertinentes.
Fournit les résultats au format JSON.

https://github.com/ArdenIvanov/svelte-intellisense l' utilise pour analyser des fichiers sveltes. Il utilise le résultat pour attacher un comportement supplémentaire.

Comme cela utilise un analyseur javascript, cela ne fonctionnera pas avec dactylographié.

https://github.com/halfnelson/svelte2tsx

Utilise le compilateur de svelte pour analyser la partie HTMLx d'un composant svelte. Transforme ensuite le HTMLx ainsi que la partie de script d'origine en un fichier tsx avec un transformateur auto-écrit.

https://github.com/simlrh/svelte-language-server/blob/feature/extend-ts-support/src/plugins/ts-svelte/service.ts (un fork de svelte-language-server) l'utilise pour créer des shadow-tsx-files de svelte-files. Il utilise ensuite le service de langue de typescript, mais transmet les requêtes par procuration : il remplace le chemin d'accès au fichier d'origine par le chemin d'accès au fichier tsx généré. Le serveur de langage dactylographié vérifie donc les fichiers tsx générés. Les résultats sont retransformés pour obtenir les positions correctes des documents.

https://github.com/halfnelson/svelte-type-checker-vscode est un autre serveur de langue utilisant la svelte2tsx-library.

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

Description tirée du commentaire de l'

Compilateur:

  1. Le compilateur lit tous les fichiers sources
  2. Tout sauf le contenu des balises de script est jeté
  3. Le code source du script est compilé dans des fichiers JS et de déclaration valides.
  4. Revient à l'étape 2, où il remplace la source TS dans les balises de script par le JS compilé
  5. Le compilateur Svelte compile l'intégralité du fichier source
  6. La compilation de sortie de l'étape ci-dessus est mise en cache, où l'AST HTML est ensuite utilisé dans le vérificateur de type.

Il consomme les variables et fonctions exportées à l'intérieur d'un script Svelte, puis les génère en tant que fichiers de déclaration contenant une classe qui étend le runtime SvelteComponent.

Vérificateur de type :

  1. Le vérificateur de type trouve tous les composants dans l'AST mis en cache.
  2. Vérifie que tous les composants du modèle sont une classe valide qui étend SvelteComponent
  3. Vérifie les déclarations valides, les propriétés, etc.

Autres approches

Voir les commentaires de ce fil.

Commentaire le plus utile

D'accord, super! Je vais aller de l'avant et faire quelques PR en restructurant une partie du code et en le préparant afin que nous puissions ensuite paralléliser le travail sur des éléments individuels.

Tous les 37 commentaires

Donc aucun d'entre eux n'utilise l'analyseur Svelte ?

svelte2tsx supprime les balises style/script, puis utilise le compilateur svelte pour analyser la partie htmlx.
svelte-ts utilise le compilateur svelte comme deuxième étape.
Le serveur de langue actuel n'utilise pas le compilateur svelte pour l'analyse.
Je vais mettre à jour le post initial.

Je ne savais pas si je devais partager cela, mais une partie pourrait être utile. L'année dernière (juillet/août 2019), j'ai commencé à jouer avec la vérification de type Svelte, sachant que je faisais du travail jetable juste pour apprendre. Il prend en charge une quantité inconnue d'une solution complète, peut-être 30%, et il a quelques limitations (plus sur celles-ci plus tard). Il n'y a pas de tests ou de documentation externe car j'ai toujours considéré que c'était une impasse.

La compilation svelte se déroule comme elle le fait aujourd'hui avec un préprocesseur TS sans vérification de type. Si vous vouliez des types dans votre balisage HTMLx, pas seulement votre balise de script, vous auriez besoin d'un préprocesseur pour gérer cela, ou Svelte aurait besoin de gérer TS en interne.

Le vérificateur de type est une étape de construction distincte qui utilise le code TypeScript non prétraité des blocs script et le balisage AST de svelte.compile pour créer un fichier TypeScript Svelte virtuel uniquement à des fins de vérification de type. (c'est ici que le plugin Rollup crée le fichier virtuel puis le type ) C'est similaire à la stratégie de new ed avec leurs accessoires. J'ai pris en charge de nombreuses constructions de Svelte avant de réaliser que je m'étais laissé emporter plus que je ne l'avais prévu, et que les progrès ultérieurs devraient être l'ingénierie sérieuse.

L'API du compilateur TypeScript est utilisée pour construire un programme en mémoire et vérifier le

Le fichier Svelte TS virtuel est généré avec une carte source qui est ensuite utilisée pour mapper les diagnostics TypeScript sur la source Svelte d'origine . C'était plutôt cool de voir les erreurs de type pointer vers la source Svelte, mais je n'avais aucune confiance dans mon approche globale.

Un problème dont je me souviens que je ne pouvais pas comprendre était lié aux diagnostics qui n'étaient pas assez spécifiques dans certains cas. IIRC, par exemple, consistait à vérifier le type d'accessoires de constructeur de composants Svelte - VSCode pointait d'une manière ou d'une autre vers des erreurs d'accessoires individuelles, mais les diagnostics TS que je recevais ne pointaient que vers le constructeur, pas la propriété du problème.

Si vous parcourez le code, sachez que certaines parties sont en désordre et que certains commentaires peuvent être obsolètes. J'ai initialement utilisé magic-string et à un moment donné, je suis passé à source-map . Et rappelez-vous que c'était une entreprise vouée à l'échec dès le début ! Notez que tout cela se trouve sur la branche ts de ce dépôt, et non sur master .

Voici les 4 fichiers référencés ci-dessus :

J'ai fait quelques recherches dans le code source du serveur de langage svelte actuel. Voici mes réflexions sur la partie tapuscrit :

Aperçu

Le serveur de langue fonctionne globalement comme ceci :

  • DocumentManager gère tous les documents qui sont ouverts.
  • TypescriptPlugin s'enregistre sur le DocumentManager pour être invoqué lors des événements du serveur de langue ("doHover", etc.). Il ne s'enregistre pas directement : il est enveloppé de wrapFragmentPlugin ce qui garantit que TypescriptPlugin n'obtient que le contenu à l'intérieur de la balise script . Le mappage des positions (de la souris/du curseur de texte) se fait en wrapFragmentPlugin pour s'assurer que les infos sont affichées à la bonne position.
  • TypescriptPlugin utilise service , un wrapper de service en langage dactylographié. Il délègue essentiellement tous les événements à ce serveur de langage, avec un certain mappage pour adhérer aux types de retour de méthode.
  • service utilise le document sur lequel travailler ainsi qu'une méthode createDocument partir de TypescriptPlugin . service conserve sa propre carte de documents qui ne sont pour le moment essentiellement qu'un délégué au document du DocumentsManager . Chaque fois que service est invoqué à partir de TypescriptPlugin , service met createDocument serait invoqué si le service de langue ouvrait un document qui ne fait pas encore partie de la carte des documents - mais cela n'arrivera jamais car le service de langue dactylographié ne trouve pas d'autres modules sveltes (voir la section suivante).

Lacunes actuelles et pistes d'amélioration

  • Étant donné que la balise de script est fournie telle quelle au service de langage dactylographié, elle ne peut pas gérer une syntaxe svelte spéciale ($).
  • Le service de langage dactylographié essaie de rechercher d'autres fichiers référencés dans le fichier svelte actuellement ouvert. Pour les fichiers .ts / .js , cela fonctionne, pour les fichiers .svelte , ce n'est pas le cas. Raison : le service de langage dactylographié ne connaît pas la fin .svelte , il suppose donc simplement qu'il s'agit d'un fichier dactylographié normal et recherche des fichiers comme ../Component.svelte.ts , ce qui est faux. Pour résoudre ce problème, nous devons implémenter la méthode resolveModuleNames sur LanguageServiceHost et rediriger toutes les recherches de fichiers .svelte.ts vers .svelte . Ensuite, le service de langue parcourra tous les fichiers sveltes. REMARQUE : Pour implémenter cela, nous devons également corriger le bogue suivant : puisque le service de langage dactylographié parcourrait désormais tous les fichiers, il invoquerait createDocument partir de TypescriptPlugin . Mais cela renvoie actuellement l'ensemble du document, pas seulement le contenu à l'intérieur de la balise de script. Il s'agit d'un bug dans le wrapFragmentPlugin n'encapsule pas openDocument .
  • Afin d'implémenter des fonctionnalités plus avancées telles que "aller au fichier svelte" en cliquant sur le nom d'importation du composant svelte, ou faire en sorte que le tapuscrit grog les signes $, nous devons ajouter un préprocesseur. Ce préprocesseur ajouterait en quelque sorte les parties manquantes pour le tapuscrit. Idéalement, tous les éléments supplémentaires peuvent être ajoutés au code existant, de sorte que le mappage des positions soit aussi simple que le mappage des positions avec des décalages. Cela pose cependant un autre défi : nous aurions maintenant deux mappages de position : un de l'ensemble du fichier svelte à la balise de script, et un de la balise de script au code dactylographié prétraité. Je ne sais pas à ce stade si c'est la voie à suivre ou si nous devons retravailler la façon dont TypescriptPlugin (et autres) obtient son document - peut-être que l'extraction de la balise de script et le prétraitement devraient être effectués en une seule étape . Cela signifierait changer ou remplacer complètement le wrapFragmentPlugin . Un autre argument pour retravailler c'est que, afin de déterminer correctement des choses comme "fonction inutilisée", nous devons prendre en compte la partie html, donc la balise script n'est pas suffisante.

Les pensées?

Sur resolveModuleNames , voici comment un membre de l'équipe TypeScript l'a fait pour Vetur , et voici une version similaire que j'ai faite pour Svelte, qui inclut un commentaire mentionnant une stratégie alternative qui a fonctionné, mais la copie de Vetur m'a semblé meilleure.

@dummdidumm, cela vous dérange-

Fondamentalement, ouvrez simplement un tas de problèmes et laissez les gens les revendiquer et les gérer en tant que responsable technique informel à ce sujet. Je proposerais de le faire mais honnêtement, je n'ai pas encore ressenti la douleur haha

également un lien vers le message LSP d'origine d'orta pour les autres utilisateurs qui parcourent https://github.com/sveltejs/svelte/issues/4518

Bien sûr, j'aimerais aider à ce sujet, mais pour le moment, je ne suis qu'un mec au hasard qui est très enthousiasmé par le serveur de langue, qui creuse dans le code et fait quelques suggestions 😄 J'ai également besoin de plus de temps pour me familiariser avec le code, son architecture et lsp en général. Mais je suis définitivement prêt à faire avancer les choses. @orta puisque vous êtes le mainteneur de ce dépôt, qu'en pensez-vous ?

👋 Ouais, nous sommes tous les deux des gens aléatoires qui s'en soucient - je n'ai même pas de bases de code sveltes pour déclencher ma démangeaison "ça devrait être mieux". Donc, je suis très heureux de voir n'importe quel mouvement - @dummdidumm - repousser et je vais vous soutenir et essayer de m'assurer que tout se passe bien

D'accord, super! Je vais aller de l'avant et faire quelques PR en restructurant une partie du code et en le préparant afin que nous puissions ensuite paralléliser le travail sur des éléments individuels.

Peut-être pourrions-nous combiner @ryanatkn , l' approche de svelete-ts et l'approche variable d'eslint-plugin-svelte3 :

  1. Utilisez un prétraitement svelte pour prétraiter le script en js.
    Ici, nous pourrions simplement passer à la dernière ou à la prochaine version d'ES pour effectuer le moins de transformation possible.
  2. Laissez svelte compiler la source prétraitée.
  3. Nous pouvons ensuite injecter des variables de résultat où injected = true dans l'instance ts source ou ts AST, puis l'utiliser avec la carte source pour fournir une vérification de type et une saisie semi-automatique.

Le script peut avoir trois fragments : module <script context="module"> , instance et moustache (template). La partie moustache pourrait simplement être entièrement déversée dans un template.js pour le moment. Mais à long terme, nous pourrions refléter le modèle dans tsx ou plain ts comme l'approche de Ryanatkn.

Les variables de module, où module = true , seraient injectées dans template.js et instance mais pas l'inverse.

De cette façon, nous n'avons pas à réimplémenter la façon de trouver la variable réactive $: a = 1 ou le magasin à préfixe $ dans le script dactylographié AST. Ces variables injectées par le compilateur svelte dérivent de la déclaration de niveau supérieur et nous la transpilons dans le dernier ES. Donc, il ne devrait surtout pas être renommé par tapuscrit.

À propos de la variable réactive $: a elle pourrait être écrite comme ceci :

$: a = 1

```ts
soit un : nombre
$ : a = 1

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

Tous c'est des ts valides qui n'ont pas besoin de transformer avant de transpiler

Et magasin à préfixe $, nous pouvons créer une fonction générique comme svelte/store get pour extraire le type de magasin

/** injected */
let $store = getType(store)
  1. Utilisez un prétraitement svelte pour prétraiter le script en js.
    Ici, nous pourrions simplement passer à la dernière ou à la prochaine version d'ES pour effectuer le moins de transformation possible.
  2. Laissez svelte compiler la source prétraitée.
  3. Nous pouvons ensuite injecter des variables de résultat où injected = true dans l'instance ts source ou ts AST, puis l'utiliser avec la carte source pour fournir une vérification de type et une saisie semi-automatique.

Je pense que svelte-ts fait quelque chose comme ça. Cela semble être une bonne idée. Sur injected = true : Quel code a exactement cette propriété ?

Le script peut avoir trois fragments : module <script context="module"> , instance et moustache (template). La partie moustache pourrait simplement être entièrement déversée dans un template.js pour le moment. Mais à long terme, nous pourrions refléter le modèle dans tsx ou plain ts comme l'approche de Ryanatkn.

Les variables de module, où module = true , seraient injectées dans template.js et instance mais pas l'inverse.

De cette façon, nous n'avons pas à réimplémenter la façon de trouver la variable réactive $: a = 1 ou le magasin à préfixe $ dans le script dactylographié AST. Ces variables injectées par le compilateur svelte dérivent de la déclaration de niveau supérieur et nous la transpilons dans le dernier ES. Donc, il ne devrait surtout pas être renommé par tapuscrit.

Vous voulez donc diviser le fichier en un fichier virtuel .ts et un fichier .template ? Pourquoi ne pas avoir les parties de moustache au bas du fichier .ts ? De cette façon, nous nous débarrasserions également des avertissements de "méthode inutilisée". Sur la marche de l'AST : Oui, je pense que nous pouvons être intelligents ici et ignorer trop l'AST car tout ce qui est référencé dans le modèle doit être de niveau supérieur dans le script.

À propos de la variable réactive $: a elle pourrait être écrite comme ceci :

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

ou

$: a = someFunction(b) as Type

Tous c'est des ts valides qui n'ont pas besoin de transformer avant de transpiler

Est-il possible de déduire le type? Peut-être que c'est juste quelque chose que nous laissons tel quel et si l'utilisateur veut utiliser le dactylographe, il doit définir lui-même let a: number . Comme alternative, nous pourrions insérer let a; , mais ce serait alors any .

Et magasin à préfixe $, nous pouvons créer une fonction générique comme svelte/store get pour extraire le type de magasin

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

Ouais ça a l'air sympa. Une autre idée que j'ai eue était de construire des getters/setters.

Je pense que svelte-ts fait quelque chose comme ça. Cela semble être une bonne idée. Sur injected = true : Quel code a exactement cette propriété ?

Le résultat de la compilation a une propriété vars, qui est un tableau d'objets ayant la propriété suivante :
name, export_name, injecté, module, muté, réaffecté, referenced_from_script et inscriptible
vous pouvez voir svelte compiler l'api pour plus d'informations

Vous voulez donc diviser le fichier en un fichier .ts virtuel et un fichier .template ? Pourquoi ne pas avoir les parties de moustache au bas du fichier .ts ? De cette façon, nous nous débarrasserions également des avertissements de "méthode inutilisée".

C'est l'approche de eslint-plugin-svelte3 . De plus, j'ai juste pensé si nous voulions prendre en charge ts dans le modèle car il n'y a pas d'attribut pour spécifier la langue.

Est-il possible de déduire le type?

Étant donné que cette source virtuelle n'est utilisée que pour la vérification de type et la saisie semi-automatique, je suppose qu'elle peut être réalisée en copiant simplement la première instruction d'assignation pour l'initialiser, mais cela semble nécessiter plus de travail.

Est-il possible de déduire le type? Peut-être que c'est juste quelque chose que nous laissons tel quel et si l'utilisateur veut utiliser le dactylographe, il doit définir lui-même let a: number. Comme alternative, nous pourrions insérer let a;, mais alors ce serait n'importe lequel.

On dirait que l'inférence normale ne nous mène pas jusqu'ici. (Vouliez-vous dire l'inférence au moment de la compilation ? Je ne sais pas si cela pourrait fonctionner)

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

La raison en est que dans le flux de code linéaire, TypeScript peut voir que a n'a pas été réaffecté, mais les étendues de fonctions imbriquées n'ont pas cette garantie, et TypeScript se trompe du côté de la sécurité de type.

exemple de terrain de jeu

Exactement, c'étaient mes soucis avec ça. Mais comme l'a souligné @jasonulu123 , nous pourrions simplement copier la définition (tout ce qui suit le = ).

Avant

$: a = true;

Après

let a = true;
$: a = true;

Mais je ne sais pas si c'est faisable en toutes circonstances. Surtout avec de gros objets où quelqu'un implémente une interface, cela ne déduira pas tout comme l'utilisateur pourrait s'y attendre. De plus, les pâtes copieuses pourraient devenir assez grosses. Je suis donc toujours plus du côté de "laisser l'utilisateur le taper".

Merci d'avoir clarifié, j'ai mal compris ce que @jasonulu123 a dit. Il résout le problème de portée des fonctions imbriquées que j'ai mentionné.

Y a-t-il un exemple où ce n'est pas un bon comportement par défaut ? Il semble qu'il fasse la bonne chose ergonomique la plupart du temps, et d'autres cas peuvent être corrigés manuellement. Voici quelques exemples.

Mhm je suppose que tu as raison. C'est la chose la plus ergonomique. Ce qui m'inquiète, c'est si le type inféré est faux, par exemple la propriété est string au lieu d'une union définie de chaînes ( 'a' | 'b' ). Mais comme vous l'avez dit, ces cas ne sont pas si courants et l'utilisateur peut toujours le saisir manuellement.

Il y a une partie de moi qui est comme: "super, la syntaxe let explicite fonctionne à merveille" mais cela finirait par ne pas fonctionner avec le support JS prêt à l'emploi

Que diriez-vous simplement de remplacer la première étiquette pour laisser :

$: a = 1
$: a = 2

se transformer en

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

Je repense un peu et ne trouve pas l'avantage de "copier la définition" sur "remplacer $: par let ". Pouvez-vous imaginer un inconvénient à remplacer $: par let ?

À propos de la différence de type inférée par rapport à ce que l'utilisateur attend, pouvons-nous fournir des actions de refactorisation pour le convertir en définition manuelle ? Cette action invitera l'utilisateur à saisir manuellement la variable comme ceci , ou à refactoriser son type inféré si possible.

avant

$: a = 'const1'

après

let a : AskUserForType
$: a = 'const1'

Pense aussi aux choses que tu soulignes, Appris quelque chose

Ha, idée intelligente, remplacez simplement le $: par un let . Je ne connais pas non plus de situations où cela finirait mal.
On pourrait aussi penser à toujours faire la transformation, pas seulement pour le premier label. Ensuite, il y aurait une erreur "ne peut pas redéclarer" qui indique à l'utilisateur "woops, j'ai défini cette variable deux fois" - ou existe-t-il des situations où cela serait viable et non une erreur ?

Bien, je ne vois aucune raison pour laquelle le remplacement de l'étiquette échouerait non plus. (puisque break $ ne fonctionne jamais dans les devoirs, n'est-ce pas ?)

Pour redéclarer la même variable réactive, il semble que Svelte le permette et cela fonctionne dans l'ordre comme vous vous en doutez. ( voir cet exemple de repl ) Je ne me vois pas personnellement utiliser ce modèle intentionnellement et je voudrais l'erreur, mais c'est dans le domaine de l'interprétation de la sémantique de Svelte, alors ne me croyez pas sur parole!

OK donc redéclarer c'est un modèle qui fonctionne réellement. Je dirais que nous ferions mieux de l'autoriser alors - ou de créer un indicateur de fonctionnalité "redéclarer non autorisé" plus tard qui est désactivé par défaut.

@halfnelson a créé un serveur de langue basé sur son approche svelte2tsx dans https://github.com/halfnelson/svelte-type-checker-vscode . J'aime vraiment à quel point tout cela fonctionne déjà bien, mais je préférerais toujours ne pas utiliser tsx comme sortie intermédiaire car je crains que cela ne soit un peu trop lent et ait des limites - mais ce n'est que mon intuition. Pourtant, nous pourrions tirer beaucoup de svelte2tsx : comment le flux d'analyse-compilation-transformation est effectué, ainsi que la vaste suite de tests. Comment les autres voient-ils l'approche consistant à transformer le code en tsx ?

J'ai pensé à quoi pourraient ressembler d'autres transformations. Voici quelques exemples. Commentaires bienvenus.

A quoi ressemble une définition de composant

Original:

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

    export let todo: string;

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

Converti:

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);
}

Explication:
D'une manière ou d'une autre, récupérez tous les accessoires (facile) et les événements envoyés (difficiles) et ajoutez-les à un seul gros objet props. L'ajout d'événements avec on: -prefix devrait minimiser les collisions et faciliter la mise en œuvre des suggestions de saisie semi-automatique.

(dans les exemples suivants, la définition du composant est omise par souci de concision)

Utiliser la variable dans le modèle

Original:

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

Converti:

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

Explication:
Ajoutez des éléments arbitraires const , peut-être préfixés par un caractère Unicode obscur, pour vérifier les utilisations correctes des variables et pour faire disparaître l'avertissement "inutilisé". Peut-être que nous devrons alors réfléchir à la façon de nous débarrasser de l'avertissement _bla inutilisé ...

Utiliser l'écouteur d'événements natif

Original:

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

Converti:

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

Explication:
Lorsque vous utilisez des écouteurs d'événement natifs, créez l'élément correspondant, puis ajoutez un écouteur d'événement. De cette façon, nous tirons parti des addEventListener -typings qui ont des surcharges pour à peu près tous les auditeurs : .addEventListener('click', ...); -> l'événement est de type MouseEvent . Ce modèle serait utilisé après avoir vérifié si l'élément est un autre composant svelte qui a l'événement défini (voir aussi l'exemple suivant). Inconvénients : et si Svelte était utilisé dans un environnement non Web (Nativescript) ?

Utiliser un autre composant

Original:

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

Converti:

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

Explication:
Chaque composant reçoit une définition de classe, nous instancions donc simplement cette classe.

Chaque boucle

Original:

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

Converti:

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

Explication:
forEach semble être le plus facile à convertir. À l'intérieur, nous pouvons appliquer récursivement toutes les autres fonctionnalités.

Si

Original:

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

Converti:

if (x === 1) {

} else if (x === 2) {

} else {

}

Explication:
Assez explicite.

Pièces manquantes

  • emplacements
  • composants spéciaux svelte:x
  • d'autres directives d'élément/composant telles que use:action , transition , bind
  • $-syntaxe

Qu'en pensez-vous? Ces transformations sont-elles viables et réalisables ? Ai-je oublié quelque chose?

À propos de la classe de composants, je préférerais étendre la propre classe SvelteComponent de svelte, mais je ne sais pas si c'est facile à implémenter ou non

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 () => { }
    }
}

et importer ces interfaces

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>>
}

Noté $$slots est probablement l'api interne de svelte, je viens de le creuser à partir du code compilé

La raison en est qu'il peut ensuite être utilisé pour émettre des .d.ts qui pourraient être utilisés pour taper vanilla ts/js.
Aussi parce que, bien qu'assez étrange, c'est une syntaxe valide :

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

Directives d'élément

Transition , use:action et d'autres directives d'élément pourraient être transformées comme

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

Attendre

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

à

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

lier:ceci

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

à

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

nécessaire de l'envelopper dans le rappel car il n'est pas immédiatement disponible

context="module"

C'est un peu délicat

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

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

devrait être compilé pour

import _ from "lodash"

let count = 1;

// any block is ok
{
    let b;
{

Et aussi si l'utilisateur utilise d'une manière ou d'une autre un langage différent dans deux scripts, comme js dans le module et ts dans l'instance ?

Joli, ça a l'air bien. Pour "Utiliser la variable dans le modèle" const _bla = bla.bla; pourrait être juste bla.bla; laissant TypeScript faire sa vérification sans avoir besoin de filtrer le diagnostic "var inutilisé". Je ne peux pas penser à des cas de coin où cela ne fonctionne pas.

Certains membres de l'équipe Vue explorent un plugin de serveur de langage dactylographié - https://github.com/znck/vue-developer-experience

C'est un angle intéressant car aujourd'hui ces expériences vont de l'extérieur à TypeScript (comme le fait vetur, car il exécute son propre tsserver-ish, ainsi que les LSP html/css/etc) ou essayez-vous d'aller de l'intérieur de TypeScript et de travailler vers l'extérieur ( par exemple cette proposition) où vous manipulez le TSServer pour croire effectivement que les fichiers .vue sont du TypeScript légitime en masquant le code non-TS.

Bien qu'aujourd'hui, cela ne puisse fonctionner qu'en ayant des correctifs à taper

C'est une approche intéressante. Mais comment les fonctionnalités telles que la saisie semi-automatique pour html ou css fonctionneraient-elles alors ? Devrait-on implémenter tout cela "à la main" et ne plus compter sur les serveurs de langue prêts à l'emploi, ou est-ce que je manque quelque chose ?

Voyez-vous des avantages à en faire un par rapport à l'autre ?

Tout sauf le support JS/TS serait géré par un LSP vscode séparé. Ce serait comme si cette extension supprimait toute prise en charge de JS/TS et ne gérait que le reste, alors le plugin dactylographié gérerait le reste.

Avantageusement, vous obtenez tous les outils TS "gratuitement" dans ce cas, passez au symbole, aux mappages de fichiers, etc.

Je ne le recommande probablement pas, aujourd'hui, il nécessite des forks ou des correctifs de TypeScript pour fonctionner, ce qui est une barrière assez élevée à l'entrée - et l'équipe TypeScript ne sait toujours pas si/quand les plugins du compilateur pourraient avoir ce type d'accès.

Merci pour les idées. Je dirais nous avec l'approche actuelle alors (de l'extérieur vers l'intérieur).

Si nous appliquons l'approche « créer un fichier ts virtuel » discutée ci-dessus, comment pourrions-nous implémenter cela et conserver les mappages de source ?

Aujourd'hui, c'est facile car il s'agit simplement de "trouver le début du script, trouver la fin du script", "extraire la partie du script" et pour mapper "ajouter le décalage du fichier svelte aux positions".
Si nous modifions la partie script maintenant, nous avons besoin d'un mappage plus sophistiqué :

Original

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

Mappé :

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

Des idées? Je n'ai jamais fait de mappage de source auparavant.

Certains membres de l'équipe Vue explorent un plugin de serveur de langage dactylographié - https://github.com/znck/vue-developer-experience

C'est un angle intéressant car aujourd'hui ces expériences vont de l'extérieur à TypeScript (comme le fait vetur, car il exécute son propre tsserver-ish, ainsi que les LSP html/css/etc) ou essayez-vous d'aller de l'intérieur de TypeScript et de travailler vers l'extérieur ( par exemple cette proposition) où vous manipulez le TSServer pour croire effectivement que les fichiers .vue sont du TypeScript légitime en masquant le code non-TS.

Bien qu'aujourd'hui, cela ne puisse fonctionner qu'en ayant des correctifs à taper

Le correctif PR de suppression de TypeScript est fusionné.
https://github.com/znck/vue-developer-experience/pull/7

Alors ... @orta cela signifie que les inconvénients concernant "non officiel" ne sont plus le cas? Ou est-ce encore un peu officieux, pas à travers un patch mais à travers une configuration de package.json vous devez connaître et que vous pouvez modifier à tout moment ?

Autre sujet : pour le moment, je pense que nous sommes toujours en discussion sur la meilleure façon d'aborder cela, et si nous décidons, cela prendra un certain temps pour le mettre en œuvre. Que pensez-vous d'utiliser svelte2tsx en attendant ? Cela pourrait servir de très bon point de départ, nous pourrions faire des tests autour de cela, puis nous verrons soit « ok, cela fonctionne vraiment très bien, ne le changeons pas » ou nous en éloignerons progressivement. Les pensées?

Oui, il y avait deux façons de modifier le flux par défaut de TS, il corrige toujours TypeScript - mais au moment de l'

Je serais un mauvais membre de l'équipe de base si je le recommandais ;)

Essayer svelte2ts semble être une bonne piste d'exploration !

Vous pouvez voir à quoi cela ressemblerait avec svelte2tsx en utilisant ce plugin vscode : https://marketplace.visualstudio.com/items?itemName=halfnelson.svelte-type-checker-vscode avec Svelte Beta (fonctionne mieux si vous désactivez la version bêta de svelte options de dactylographie afin que vous n'obteniez pas le double des indices :) )

Même si je n'utilise pas svelte2tsx, je vois une discussion sur les transformations à apporter au code JS de svelte pour que le dactylographe reste heureux. La suite de tests pour svelte2tsx couvre non seulement les modifications à apporter au modèle, mais également la balise de script, qui si vous optez pour une seule solution de balise de script pourrait être un bon point de départ : https://github.com/halfnelson /svelte2tsx/tree/master/test/svelte2tsx/samples

le htmlx2jsx est le dossier que les tests de changement de modèle (htmlx), tandis que les échantillons svelte2tsx sont les modifications spécifiques sveltes nécessaires pour l'abus spécial de la syntaxe js de svelte :)

Je viens de découvrir ce dépôt https://github.com/pivaszbs/svelte-autoimport . Cela vaut peut-être la peine d'examiner si nous voulons importer intellisense indépendamment de svelte2tsx .

Je pense que nous sommes à un point où la plupart des orientations générales de ce projet sont triées et semblent bien fonctionner. Je vais vous donner une semaine pour donner votre avis, mais je suis assez heureux que nous n'ayons pas besoin de repenser l'architecture maintenant.

On dirait que personne n'a d'objection, alors je vais fermer celui-ci. Merci à tous d'avoir discuté de ça !

Cette page vous a été utile?
0 / 5 - 0 notes