Este tópico se destina a uma visão geral das abordagens sobre como implementar um servidor de linguagem para arquivos svelte. A discussão sobre uma solução preferida também pode ser feita aqui.
Atualmente, o realce de sintaxe e alguns autocompletar básicos funcionam para arquivos esbeltos.
Entretanto, o rich intellisense não é fornecido, não sabe sobre todos os arquivos de texto digitados no espaço de trabalho e também não tem informações de tipo sobre os componentes esbeltos.
O mesmo é verdadeiro para a parte html onde a sintaxe svelte especial é destacada, mas o intellisense não está funcionando. O vscode-html-languageservice é usado, o qual não conhece a sintaxe especial por padrão.
O servidor de linguagem atual usa o compilador svelte para analisar arquivos e obter diagnósticos como "nenhum atributo alt nesta tag img".
Uma análise mais aprofundada: Comentário
Isenção de responsabilidade: não sou o autor de nenhuma das soluções existentes, no entanto, examinei o código. Se alguma das informações estiver incorreta ou não detalhada o suficiente, ou você estiver faltando uma solução, fique à vontade para comentar, eu irei ajustar.
Usa htmlparser2 para analisar a parte html de um arquivo svelte. Usa ganchos do analisador html para adicionar análise customizada e extrair informações relevantes.
Usa espree para analisar as partes do script de um arquivo esbelto. Em seguida, percorre o AST para extrair informações relevantes.
Fornece os resultados em um formato JSON.
https://github.com/ArdenIvanov/svelte-intellisense o usa para analisar arquivos svelte. Ele usa o resultado para anexar algum comportamento adicional.
Como ele usa um analisador javascript, não funcionará com o typescript.
Usa o compilador svelte para analisar a parte HTMLx de um componente svelte. Em seguida, transforma o HTMLx mais a parte do script original em um arquivo tsx com um transformador autoescrito.
https://github.com/simlrh/svelte-language-server/blob/feature/extend-ts-support/src/plugins/ts-svelte/service.ts (um fork do svelte-language-server) o usa para crie arquivos shadow-tsx de arquivos svelte. Em seguida, ele usa o serviço de linguagem do typescript, mas faz o proxy das solicitações: ele substitui o caminho do arquivo original pelo caminho do arquivo para o arquivo tsx gerado. O servidor de linguagem typescript, portanto, verifica os arquivos tsx gerados. Os resultados são transformados de volta para obter as posições corretas dos documentos.
https://github.com/halfnelson/svelte-type-checker-vscode é outro servidor de linguagem que usa a biblioteca svelte2tsx.
Descrição retirada do comentário do implementador :
Compilador:
Ele consome as variáveis e funções exportadas de dentro de um script Svelte e, em seguida, o gera como um arquivo de declaração que contém uma classe que estende o SvelteComponent de tempo de execução.
Typechecker:
Veja os comentários deste tópico.
Então nenhum deles está usando o analisador Svelte?
svelte2tsx remove as tags de estilo / script e usa o compilador svelte para analisar a parte htmlx.
svelte-ts usa o compilador svelte como uma segunda etapa.
O servidor de linguagem atual não usa o compilador svelte para análise.
Vou atualizar o post inicial.
Eu não tinha certeza se deveria compartilhar isso, mas parte disso pode ser útil. No ano passado (julho / agosto de 2019), comecei a brincar com a digitação de Svelte, sabendo que estava fazendo um trabalho inútil apenas para aprender. Ele suporta uma quantidade desconhecida de uma solução completa, talvez 30%, e tem algumas limitações (mais sobre isso depois). Não existem testes ou documentação externa porque sempre considerei isso um beco sem saída.
A compilação Svelte funciona como hoje com um pré-processador TS sem verificação de tipo. Se você quisesse tipos em sua marcação HTMLx, não apenas sua tag de script, você precisaria de um pré-processador para lidar com isso, ou o Svelte precisaria lidar com TS internamente.
O typechecker é uma etapa de construção separada que usa o código TypeScript não pré-processado de blocos script
e a marcação AST de svelte.compile
para criar um arquivo Svelte TypeScript virtual exclusivamente para fins de verificação de tipo. (aqui é onde o plugin Rollup cria o arquivo virtual e então o verifica ) É semelhante à estratégia de @halfnelson , mas diferente por produzir TS normal, não TSX, então na marcação ele não tenta digitar tags HTML e atributos, apenas o material dentro de tags de bigode e componentes Svelte. Ele transforma os componentes Svelte em construtores que ganham new
ed com seus adereços. Eu comecei a apoiar muitas das construções de Svelte antes de perceber que me empolguei mais do que pretendia, e o progresso futuro deveria ser Engenharia Séria.
A API do compilador TypeScript é usada para construir um programa na memória e verificar o
O arquivo Svelte TS virtual é gerado com um mapa de origem que é então usado para mapear os diagnósticos do TypeScript de volta para a fonte Svelte original . Foi muito bom ver os erros de tipo apontando para a fonte Svelte, mas eu não tinha confiança na minha abordagem geral.
Um problema que me lembro e que não consegui resolver foi com diagnósticos que não eram específicos o suficiente em alguns casos. Um exemplo do IIRC foi com a verificação de tipos de props do construtor de componente Svelte - VSCode de alguma forma apontaria para erros de prop individuais, mas os diagnósticos de TS que eu estava obtendo apontavam apenas para o construtor, não para a propriedade do problema.
Se você olhar o código, saiba que as partes estão uma bagunça e alguns comentários podem estar desatualizados. Eu originalmente usei magic-string
e em algum momento mudei para source-map
. E lembre-se de que foi um esforço fadado ao fracasso desde o início! Observe que tudo isso está no branch ts
desse repo, não master
.
Aqui estão os 4 arquivos mencionados acima:
Eu fiz algumas pesquisas no código-fonte do servidor de linguagem svelte atual. Aqui estão meus pensamentos sobre a parte datilografada dele:
O servidor de linguagem funciona amplamente assim:
DocumentManager
lida com todos os documentos que são abertos.TypescriptPlugin
registra-se em DocumentManager
para ser invocado em eventos de servidor de linguagem ("doHover" etc). Ele não se registra diretamente: ele é envolvido por wrapFragmentPlugin
que garante que TypescriptPlugin
apenas obtenha o conteúdo dentro da tag script
. O mapeamento das posições (do mouse / cursor de texto) é feito em wrapFragmentPlugin
para garantir que as informações sejam mostradas na posição correta.TypescriptPlugin
usa service
, um wrapper de serviço de linguagem typescript. Basicamente, delega todos os eventos a este servidor de linguagem, com algum mapeamento para aderir aos tipos de retorno do método.service
pega o documento para fazer o trabalho, bem como um método createDocument
de TypescriptPlugin
. service
mantém seu próprio mapa de documentos que no momento são basicamente apenas um delegado ao documento de DocumentsManager
. Sempre que service
é invocado a partir de TypescriptPlugin
, service
atualiza seu mapa de documentos com o documento fornecido (após empacotá-lo). createDocument
seria invocado se o serviço de linguagem abrir um documento que ainda não faz parte do mapa de documentos - mas isso nunca acontecerá, pois o serviço de linguagem de typecript não encontra outros módulos esbeltos (consulte a próxima seção)..ts
/ .js
arquivos isso funciona, para .svelte
arquivos não. Motivo: o serviço de linguagem datilografada não sabe sobre a terminação .svelte
, então ele apenas assume que é um arquivo datilografado normal e procura por arquivos como ../Component.svelte.ts
, o que está errado. Para corrigir isso, precisamos implementar o método resolveModuleNames
em LanguageServiceHost
e redirecionar todas as pesquisas de arquivo .svelte.ts
para .svelte
. Em seguida, o serviço de linguagem percorrerá todos os arquivos esbeltos. NOTA: Para implementar isso, também precisamos corrigir o seguinte bug: Como o serviço de linguagem de digitação agora percorreria todos os arquivos, ele invocaria createDocument
de TypescriptPlugin
. Mas isso atualmente retorna todo o documento, não apenas o conteúdo dentro da tag do script. Este é um bug dentro do wrapFragmentPlugin
não envolve openDocument
.TypescriptPlugin
(e outros) obtêm seu documento - talvez a extração da tag do script e o pré-processamento devam ser feitos em uma única etapa . Isso significaria mudar ou substituir totalmente wrapFragmentPlugin
. Outro argumento para retrabalho é que, para determinar corretamente coisas como "função não utilizada", temos que levar em conta a parte html, de modo que a tag de script não é suficiente.Pensamentos?
Em resolveModuleNames
, aqui está como um membro da equipe TypeScript fez isso para Vetur , e aqui está uma versão semelhante que fiz para Svelte, que inclui um comentário que menciona uma estratégia alternativa que funcionou, mas copiar Vetur parecia melhor.
@dummdidumm , você se importa em atuar como líder de tecnologia e revelar essas coisas para questões específicas? Eu ficaria feliz em oferecer tempo de devolução voluntário, mas considero muito disso opressor e funcionaria melhor em questões pequenas e definidas. A menos que haja alguma grande mudança arquitetônica que precise ser feita (parece que não?)
Basicamente, basta abrir um monte de problemas e deixar as pessoas reivindicá-los e gerenciá-los como nosso líder de tecnologia informal nisso. Eu me ofereceria para fazer isso, mas, honestamente, ainda não senti a dor ainda haha
também com link para a postagem LSP original da orta para outras pessoas que estão navegando em https://github.com/sveltejs/svelte/issues/4518
Claro, eu adoraria ajudar nisso, mas agora sou apenas um cara aleatório que está muito animado com o servidor de linguagem, pesquisando o código e fazendo algumas sugestões 😄 Também preciso de mais algum tempo para me familiarizar com o código, sua arquitetura e lsp em geral. Mas estou definitivamente disposto a levar as coisas adiante. @orta já que você é o mantenedor deste repositório, o que você acha disso?
👋 Sim, nós dois somos pessoas aleatórias que simplesmente se importam - eu nem mesmo tenho nenhuma base de código esbelta para acionar minha coceira de "isso deveria ser melhor". Então, estou muito feliz em ver qualquer movimento - @dummdidumm - afaste-se e eu irei apoiá-lo e tentar garantir que tudo corra bem
OK ótimo! Vou prosseguir e fazer alguns PRs, reestruturando parte do código e preparando-o para que possamos paralelizar o trabalho em itens individuais.
Talvez pudéssemos combinar @ryanatkn , a abordagem de svelete-ts e a abordagem de variável de eslint-plugin-svelte3 :
injected = true
na instância ts source ou ts AST e, em seguida, usá-lo com o mapa de origem para fornecer verificação de tipo e preenchimento automático.O script pode ter três fragmentos: módulo <script context="module">
, instância e bigode (modelo). A parte do bigode pode ser apenas despejada em um template.js por enquanto. Mas, a longo prazo, poderíamos refletir o modelo em tsx ou ts simples, como a abordagem de ryanatkn.
O módulo vars, onde module = true
, seria injetado no template.js e na instância, mas não o contrário.
Desta forma, não temos que reimplementar como encontrar a variável reativa $: a = 1
ou $ -prefixed store no typescript AST. Essas variáveis injetadas pelo compilador svelte derivam de uma instrução de nível superior e a estamos transpilando para o ES mais recente. Portanto, geralmente não deve ser renomeado por texto digitado.
Sobre a variável reativa $: a
ela poderia ser escrita assim:
$: a = 1
`` `ts
deixe um: número
$: a = 1
```ts
$: a = someFunction(b) as Type
Todos são ts válidos que não precisam ser transformados antes de transpilar
E US $ store -prefixed podemos criar uma função genérica como esbelto loja / get ao extrato tipo de armazenamento
/** injected */
let $store = getType(store)
- Use o pré - pré -processar o script em js.
Aqui, poderíamos simplesmente transpilar para a versão mais recente ou para a próxima versão do ES para fazer o mínimo de transformação possível.- Vamos compilar o código fonte pré-processado.
- Podemos então injetar vars de resultado onde
injected = true
na instância ts source ou ts AST e, em seguida, usá-lo com o mapa de origem para fornecer verificação de tipo e preenchimento automático.
Eu acho que svelte-ts faz algo assim. Parece uma boa ideia. Em injected = true
: qual código exatamente tem essa propriedade?
O script pode ter três fragmentos: módulo
<script context="module">
, instância e bigode (modelo). A parte do bigode pode ser apenas despejada em um template.js por enquanto. Mas, a longo prazo, poderíamos refletir o modelo em tsx ou ts simples, como a abordagem de ryanatkn.O módulo vars, onde
module = true
, seria injetado em template.js e instância, mas não o contrário.Desta forma, não temos que reimplementar como encontrar a variável reativa
$: a = 1
ou $ -prefixed store no typescript AST. Essas variáveis injetadas pelo compilador svelte derivam de uma instrução de nível superior e a estamos transpilando para o ES mais recente. Portanto, geralmente não deve ser renomeado por texto digitado.
Então você deseja dividir o arquivo em um .ts
-file virtual e um .template
-file? Por que não colocar as partes do bigode na parte inferior do arquivo .ts
? Dessa forma, também nos livraríamos dos avisos de "método não utilizado". Sobre a caminhada pelo AST: Sim, acho que podemos ser inteligentes aqui e pular muito a análise do AST porque tudo o que é referenciado no modelo deve ser de nível superior no script.
Sobre a variável reativa
$: a
ela poderia ser escrita assim:$: a = 1
let a: number $: a = 1
ou
$: a = someFunction(b) as Type
Todos são ts válidos que não precisam ser transformados antes de transpilar
É possível inferir o tipo? Talvez isso seja apenas algo que deixamos como está e se o usuário quiser usar o texto datilografado, ele deve definir let a: number
ele mesmo. Como alternativa, poderíamos inserir let a;
, mas então seria any
.
E US $ store -prefixed podemos criar uma função genérica como esbelto loja / get ao extrato tipo de armazenamento
/** injected */ let $store = getType(store)
Sim, isso parece bom. Outra ideia que tive foi construir getters / setters.
Eu acho que svelte-ts faz algo assim. Parece uma boa ideia. Em
injected = true
: Qual código exatamente tem essa propriedade?
O resultado da compilação possui uma propriedade vars, que é uma matriz de objeto que possui a seguinte propriedade:
nome, export_name, injetado, módulo, mutado, reatribuído, referenced_from_script e gravável
você pode ver a API de compilação svelte para mais informações
Então você deseja dividir o arquivo em um arquivo .ts virtual e um arquivo .template? Por que não colocar as partes do bigode na parte inferior do arquivo .ts? Dessa forma, também nos livraríamos dos avisos de "método não utilizado".
Esta é a abordagem de eslint-plugin-svelte3 . Também pensei se queremos suportar ts no template porque não há atributo para especificar o idioma.
É possível inferir o tipo?
Uma vez que esta fonte virtual é usada apenas para verificação de tipo e preenchimento automático, presumo que seja possível apenas copiar a primeira instrução de atribuição para inicializá-la, mas parece que precisa de mais trabalho.
É possível inferir o tipo? Talvez seja apenas algo que deixamos como está e se o usuário quiser usar o texto datilografado, ele mesmo deve definir let a: number. Como alternativa, poderíamos inserir let a ;, mas então seria qualquer.
Parece que a inferência normal não nos leva até lá. (você quis dizer inferência em tempo de compilação? não tenho certeza se isso poderia funcionar)
let a; // type is "any" initially
$: a = 5;
a; // type is now "number", good
const cb = () => a; // type is implicitly "any", noo
O motivo é que, no fluxo de código linear, o TypeScript pode ver que a
não foi reatribuído, mas os escopos de função aninhados não têm essa garantia, e o TypeScript erra no lado da segurança de tipo.
Exatamente, essas eram minhas preocupações com isso. Mas, como @ jasonlyu123 apontou, poderíamos apenas copiar a definição (tudo após =
).
Antes
$: a = true;
Depois de
let a = true;
$: a = true;
Mas não sei se isso é viável em todas as circunstâncias. Especialmente com objetos grandes onde alguém implementa uma interface, isso não inferirá tudo como o usuário pode esperar. Além disso, a massa de cópia pode ficar bem grande. Portanto, estou ainda mais a favor de "deixar o usuário digitar".
Obrigado por esclarecer, eu entendi mal o que @ jasonlyu123 disse. Ele corrige o problema de escopo de função aninhado que mencionei.
Existe um exemplo de quando esse não é um bom comportamento padrão? Parece que faz a coisa ergonômica certa na maioria das vezes, e outros casos podem ser consertados manualmente. Aqui estão alguns exemplos.
Mhm, acho que você está certo. É a coisa mais ergonômica. O que me preocupa é se o tipo inferido está errado, por exemplo propriedade é string
vez de uma união definida de strings ( 'a'
| 'b'
). Mas como você disse, esses casos não são tão comuns e o usuário ainda pode digitá-lo manualmente.
Há uma parte de mim que diz: "Ótimo, a sintaxe let explícita funciona muito bem", mas isso acabaria não funcionando com suporte a JS fora da caixa
Que tal substituir o primeiro rótulo para permitir:
$: a = 1
$: a = 2
obter a transformação para
/** injected */
let a = 1
$: a = 2
Eu repenso um pouco e não consigo encontrar a vantagem de "copiar definição" sobre "substituir $:
por let
". Vocês podem pensar em alguma desvantagem de substituir $:
por let
?
Sobre a diferença de tipo inferida em relação ao que o usuário esperava, podemos fornecer ações de refatoração para convertê-la em definição manual? Esta ação irá pedir usuário para tipo manual da variável como este , ou refatorar o seu tipo inferido, se possível.
antes
$: a = 'const1'
depois de
let a : AskUserForType
$: a = 'const1'
Também pensa nas coisas que você aponta, Aprendeu algo 👍
Ha, boa ideia, basta substituir totalmente $:
por um let
. Também não conheço nenhuma situação em que isso acabaria mal.
Também poderíamos pensar em fazer sempre a transformação, não só para a primeira gravadora. Em seguida, haveria um erro "não é possível redeclarar" que sugere ao usuário "uau, defini essa variável duas vezes" - ou há situações em que isso seria viável e não um erro?
Legal, também não consigo pensar em nenhuma razão para substituir o rótulo. (já que break $
nunca trabalha dentro de atribuições, certo?)
Para redeclarar a mesma variável reativa, parece que o Svelte permite isso e funciona na ordem que você esperava. ( veja este exemplo repl ) Eu pessoalmente não me vejo usando esse padrão intencionalmente e gostaria do erro, mas ele está no território de interpretar a semântica de Svelte, então não acredite na minha palavra!
OK, então redeclarar é um padrão que realmente funciona. Eu diria que é melhor permitirmos isso então - ou criar um sinalizador de recurso "redeclarar não permitido" posteriormente, que está desativado por padrão.
@halfnelson criou um servidor de linguagem baseado em sua abordagem svelte2tsx em https://github.com/halfnelson/svelte-type-checker-vscode . Eu realmente amo como tudo isso já funciona bem, mas ainda prefiro não usar tsx como uma saída intermediária porque temo que possa ser um pouco lento e ter algumas limitações - mas isso é apenas minha intuição. Ainda assim, poderíamos aprender muito com svelte2tsx: como o fluxo de análise-compilação-transformação é feito e também o amplo conjunto de testes. Como os outros veem a abordagem de transformar o código em tsx?
Pensei em como outras transformações poderiam ser. Aqui estão alguns exemplos. Feedback bem-vindo.
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);
}
Explicação:
De alguma forma, pegue todos os adereços (fácil) e eventos despachados (difícil) e adicione-os a um grande objeto de adereços. Adicionar eventos com on:
-prefix deve minimizar as colisões e tornar as sugestões de preenchimento automático mais fáceis de implementar.
(nos exemplos a seguir, a definição de componente é omitida por questões de brevidade)
Original:
<script>const bla = {bla: 'bla'};</script>
<p>{bla.bla}</p>
Convertido:
const bla = 'bla';
const _bla = bla.bla;
Explicação:
Adicione elementos const
arbitrários, talvez prefixados com algum caractere Unicode obscuro, para verificar os usos corretos das variáveis e também fazer com que o aviso "não utilizado" desapareça. Talvez precisemos pensar em como nos livrar do aviso _bla
utilizado ...
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);
Explicação:
Ao usar ouvintes de eventos nativos, crie o elemento correspondente e, em seguida, adicione um ouvinte de eventos. Desta forma, aproveitamos os addEventListener
-tipos que têm sobrecargas para praticamente todos os ouvintes: .addEventListener('click', ...);
-> evento é do tipo MouseEvent
. Este padrão seria usado depois de verificar se o elemento é outro componente esbelto que tem o evento definido (veja também o próximo exemplo). Deficiências: E se o Svelte for usado em um ambiente não-web (Nativescript)?
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});
Explicação:
Cada componente obtém uma definição de classe, então apenas instanciamos essa classe.
Original:
{#each todos as todo,i (todo.id)}
<p>{todo.text}</p>
{/each}
Convertido:
todos.forEach((todo, i) => {
const _todoText = todo.text;
});
Explicação:
forEach
parece ser o mais fácil de converter. Dentro, podemos aplicar recursivamente todos os outros recursos.
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 {
}
Explicação:
Bastante autoexplicativo.
svelte:x
use:action
, transition
, bind
O que é que vocês acham? Essas transformações são viáveis e realizáveis? Perdi algo?
Sobre a classe de componente, eu preferiria estender a classe SvelteComponent do próprio svelte, mas não sei se isso é fácil de implementar ou não
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 essas 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>>
}
Notado que $$slots
é provavelmente a API interna do svelte, acabei de explorá-la no código compilado
A razão para isso é que ele pode ser usado para emitir .d.ts
que poderia ser usado para digitar vanilla ts / js.
Também porque, embora estranho o suficiente, esta é uma sintaxe válida:
<script>
onMount(() => {
new Component({
target: document.getElementById('foo')
})
})
</script>
Transition
, use:action
e outras diretivas de elemento podem ser transformadas como
fade(null as Element, { } /* option in attribute*/)
{#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 })
<script>
let a;
</script>
<div bind:this={a}><div>
para
let a;
onMount(() => {
a = document.createElement('div')
})
precisava envolvê-lo no retorno de chamada porque não está imediatamente disponível
Isso é um pouco complicado
<script context="module">
let count = 1;
</script>
<script>
import _ from "lodash"
let b;
</script>
deve ser compilado para
import _ from "lodash"
let count = 1;
// any block is ok
{
let b;
{
E também se o usuário de alguma forma usar uma linguagem diferente em dois scripts, como js no módulo e ts na instância?
Legal, parece bom. Para "Usar variável no modelo" const _bla = bla.bla;
poderia ser apenas bla.bla;
permitindo que o TypeScript faça sua verificação sem precisar filtrar o diagnóstico "var não utilizado". Não consigo pensar em nenhum caso em que isso não funcione.
Alguns membros da equipe Vue estão explorando um plugin de servidor de linguagem typescript - https://github.com/znck/vue-developer-experience
É um ângulo interessante, pois hoje essas experiências vão de fora e dentro do TypeScript (como vetur faz, já que executa seu próprio tsserver-ish, bem como html / css / etc LSPs) ou você tenta ir de dentro do TypeScript e trabalhar para fora ( por exemplo, esta proposta) onde você manipula o TSServer para efetivamente acreditar que os arquivos .vue são TypeScript legítimos, mascarando o código não-TS.
Embora hoje só funcione com patches para datilografar
Essa é uma abordagem interessante. Mas como recursos como preenchimentos automáticos para html ou css funcionariam então? Seria preciso implementar tudo isso "manualmente" e não depender mais dos servidores de linguagem prontos para uso, ou estou perdendo alguma coisa?
Você vê alguma vantagem em fazer um ao longo do tempo?
Qualquer coisa, exceto o suporte JS / TS, seria tratada por um vscode LSP separado. Seria como se essa extensão removesse todo o suporte para JS / TS e cuidasse apenas do resto, então o plugin de digitação cuidaria do resto.
Em termos de vantagem, você obtém todas as ferramentas de TS "de graça" neste caso, pule para o símbolo, mapeamentos de arquivo, etc.
Provavelmente não o recomendo, hoje ele requer bifurcações ou patches do TypeScript para funcionar, o que é uma barreira muito alta para a entrada - e a equipe do TypeScript ainda não tem certeza se / quando os plug-ins do compilador poderiam ter esse tipo de acesso.
Obrigado pelos insights. Eu diria que estamos com a abordagem atual então (de fora para dentro).
Se seguirmos com a abordagem "criar um arquivo ts virtual" discutida acima, como a implementaríamos e manteríamos os mapeamentos de origem?
Hoje, é fácil porque é apenas "localizar o início do script, localizar o final do script", "extrair a parte do script" e para mapear "adicionar o deslocamento do arquivo esbelto às posições".
Se mudarmos a parte do script agora, precisamos de um mapeamento mais 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
Alguma ideia? Nunca fiz mapeamento de código-fonte antes ..
Alguns membros da equipe Vue estão explorando um plugin de servidor de linguagem typescript - https://github.com/znck/vue-developer-experience
É um ângulo interessante, pois hoje essas experiências vão de fora e dentro do TypeScript (como vetur faz, já que executa seu próprio tsserver-ish, bem como html / css / etc LSPs) ou você tenta ir de dentro do TypeScript e trabalhar para fora ( por exemplo, esta proposta) onde você manipula o TSServer para efetivamente acreditar que os arquivos .vue são TypeScript legítimos, mascarando o código não-TS.
Embora hoje só funcione com patches para datilografar
O patch PR de remoção do TypeScript é mesclado.
https://github.com/znck/vue-developer-experience/pull/7
Então ... @orta isso significa que as desvantagens em relação a "não oficial" não são mais o caso? Ou ainda é meio não oficial, não apenas por meio de um patch, mas por meio de alguma configuração package.json
você deve conhecer e pode alterar a qualquer momento?
Outro tópico: no momento, sinto que ainda estamos discutindo sobre como abordar isso da melhor maneira e, se decidirmos, vai levar algum tempo para implementá-lo. O que vocês acham de usar svelte2tsx
entretanto? Poderia servir como um ponto de partida muito bom, poderíamos fazer alguns testes em torno dele e então ver "ok, isso realmente funciona muito bem, não vamos alterá-lo" ou migrar gradualmente para longe dele. Pensamentos?
Sim, havia duas maneiras de editar o fluxo padrão do TS. Ele ainda está corrigindo o TypeScript - mas em tempo de
Eu seria um péssimo membro da equipe principal se o recomendasse;)
Experimentar svelte2ts
parece um bom caminho para a exploração!
Você pode ver como seria o svelte2tsx usando este plugin vscode: https://marketplace.visualstudio.com/items?itemName=halfnelson.svelte-type-checker-vscode junto com o Svelte Beta (funciona melhor se você desativar o svelte beta opções de texto datilografado para que você não receba o dobro das dicas :))
Mesmo se não estiver usando svelte2tsx, vejo uma discussão sobre quais transformações precisam ser feitas no código JS do svelte para manter o texto digitado feliz. O conjunto de testes para svelte2tsx cobre não apenas as mudanças necessárias para serem feitas no modelo, mas também a tag de script, que se você estiver indo para uma solução apenas de tag de script pode ser um bom ponto de partida: https://github.com/halfnelson / svelte2tsx / tree / master / test / svelte2tsx / samples
o htmlx2jsx
é a pasta que os testes de mudança de modelo (htmlx), enquanto os svelte2tsx
exemplos são as mudanças específicas do Svelte necessárias para o abuso especial da sintaxe js do Svelte :)
Acabei de descobrir este repo https://github.com/pivaszbs/svelte-autoimport . Talvez valha a pena verificar se queremos importar o intellisense independente de svelte2tsx
.
Acho que estamos em um ponto em que a maior parte da direção geral deste projeto está ordenada e parece estar funcionando perfeitamente. Vou dar uma semana para feedback, mas estou muito feliz por não precisarmos repensar arquitetonicamente agora.
Parece que ninguém tem objeções, então vou encerrar este. Obrigado a todos por discutir isso!
Comentários muito úteis
OK ótimo! Vou prosseguir e fazer alguns PRs, reestruturando parte do código e preparando-o para que possamos paralelizar o trabalho em itens individuais.