Language-tools: 语言服务器上的方法概述

创建于 2020-03-24  ·  37评论  ·  资料来源: sveltejs/language-tools

此线程旨在概述有关如何为 svelte 文件实现语言服务器的方法。 也可以在此处讨论首选解决方案。

语言服务器的当前状态

当前语法高亮和一些基本的自动完成功能适用于 svelte 文件。
然而,没有提供丰富的智能感知,不知道工作区中的所有打字稿文件,也没有关于 svelte 组件的类型信息。
对于 html 部分也是如此,其中突出显示了特殊的 svelte 语法但智能感知不起作用。 使用 vscode-html-language 服务,默认情况下不知道特殊语法。
当前的语言服务器确实使用 svelte 编译器来解析文件并获取诸如“此 img 标签上没有 alt 属性”之类的诊断信息。
更深入的分析:评论

现有方法/解决方案

免责声明:我不是任何现有解决方案的作者,但是我确实查看了代码。 如果某些信息有错误或不够详细,或者您缺少解决方案,请随时发表评论,我会进行调整。

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

使用htmlparser2解析 svelte 文件的 html 部分。 使用html解析器的钩子添加自定义解析并提取相关信息。
使用espree解析 svelte 文件的脚本部分。 然后走AST提取相关信息。
以 JSON 格式提供结果。

https://github.com/ArdenIvanov/svelte-intellisense使用它来解析 svelte 文件。 它使用结果附加一些额外的行为。

由于这使用了 javascript 解析器,因此它不适用于打字稿。

https://github.com/halfnelson/svelte2tsx

使用 svelte 的编译器来解析 svelte 组件的 HTMLx 部分。 然后用自己编写的转换器将HTMLx加上原脚本部分转换成tsx文件。

https://github.com/simlrh/svelte-language-server/blob/feature/extend-ts-support/src/plugins/ts-svelte/service.ts(svelte-language-server 的一个分支)使用它来创建 svelte 文件的 shadow-tsx 文件。 然后它使用 typescript 的语言服务,但代理请求:它将原始文件路径替换为生成的 tsx 文件的文件路径。 因此,打字稿语言服务器会检查生成的 tsx 文件。 结果被转换回以获得正确的文档位置。

https://github.com/halfnelson/svelte-type-checker-vscode是另一种使用 svelte2tsx-library 的语言服务器。

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

取自实施者评论的描述:

编译器:

  1. 编译器读取所有源文件
  2. 除了脚本标签的内容之外的所有东西都被扔掉了
  3. 脚本源代码被编译为有效的 JS 和声明文件。
  4. 跳回第 2 步,用编译后的 JS 替换 script 标签中的 TS 源
  5. Svelte 编译器编译整个源文​​件
  6. 上述步骤的输出编译被缓存,然后在类型检查器中使用 HTML AST。

它使用从 Svelte 脚本内部导出的变量和函数,然后将其生成为包含扩展运行时 SvelteComponent 的类的声明文件。

类型检查器:

  1. 类型检查器在缓存的 AST 中查找所有组件。
  2. 检查模板中的所有组件是否是扩展 SvelteComponent 的有效类
  3. 检查有效的声明、属性等。

其他方法

请参阅此线程的评论。

最有用的评论

太好了! 我会继续做一些 PR 来重组一些代码并准备它,这样我们就可以并行处理单个项目。

所有37条评论

所以他们都没有使用 Svelte 解析器?

svelte2tsx 去掉样式/脚本标签,然后使用 svelte 编译器解析 htmlx 部分。
svelte-ts 使用 svelte 编译器作为第二步。
当前语言服务器不使用 svelte 编译器进行解析。
我会更新最初的帖子。

我不确定是否应该分享这个,但其中的某些部分可能有用。 去年(2019 年 7 月/8 月),我开始尝试打字检查 Svelte,因为我知道我在做一次性工作只是为了学习。 它支持一些未知数量的完整解决方案,可能是 30%,并且它有一些限制(稍后会详细介绍)。 没有任何测试或外部文档,因为我一直认为这是一个死胡同。

Svelte 编译就像今天一样,使用非类型检查 TS 预处理器。 如果您希望在 HTMLx 标记中使用类型,而不仅仅是脚本标记,则需要一个预处理器来处理它,否则 Svelte 需要在内部处理 TS。

类型检查器是一个单独的构建步骤,它使用来自script块的未预处理的 TypeScript 代码和来自svelte.compile的标记 AST创建一个虚拟的 Svelte TypeScript 文件,仅用于类型检查。 (这里是 Rollup 插件创建虚拟文件然后检查它的地方)它类似于@halfnelson的策略,但不同之处在于它生成普通的 TS,而不是 TSX,所以在标记中它不会尝试对 HTML 标签进行类型检查和属性,只有 mustache 标签和 Svelte 组件中的内容。 它将 Svelte 组件转换为构造函数,并使用它们的 props 获取new ed。 我通过支持 Svelte 的许多构造得到了中途的支持,然后才意识到我超出了我的预期,进一步的进展应该是 Serious Engineering。

TypeScript 编译器 API 用于在内存中构建程序并对虚拟 Svelte TS 进行类型检查。 与 Svelte 不同的是,它的世界观是整个程序,而不是单个文件。 当时,添加增量编译和工作线程作为未来的优化似乎很简单。

虚拟 Svelte TS 文件使用源映射生成,然后用于将TypeScript 诊断映射回原始 Svelte 源。 看到类型错误指向 Svelte 源代码非常酷,但我对我的整体方法没有信心。

我记得我无法弄清楚的一个问题是在某些情况下诊断不够具体。 IIRC 的一个例子是类型检查 Svelte 组件构造函数道具 - VSCode 会以某种方式指向单个道具错误,但我得到的 TS 诊断仅指向构造函数,而不是问题属性。

如果您仔细查看代码,请注意部分是一团糟,有些注释可能已经过时。 我最初使用magic-string并在某些时候改为source-map 。 请记住,这从一开始就注定失败! 请注意,所有这些都在该回购的ts分支上,而不是master

以下是上面引用的 4 个文件:

我对当前 svelte 语言服务器的源代码进行了一些挖掘。 以下是我对打字稿部分的看法:

概述

语言服务器大致是这样工作的:

  • DocumentManager处理所有打开的文档。
  • TypescriptPluginDocumentManager以在语言服务器事件(“doHover”等)上调用。 它不直接注册:它用wrapFragmentPlugin包裹,确保TypescriptPlugin只获取script标签内的内容。 位置(鼠标/文本光标)的映射在wrapFragmentPlugin以确保信息显示在正确的位置。
  • TypescriptPlugin使用service ,一个打字稿语言服务包装器。 它基本上将所有事件委托给该语言服务器,并带有一些映射以遵守方法返回类型。
  • service将文档和TypescriptPlugincreateDocument方法一起处理。 service保留他自己的文档地图,目前基本上只是DocumentsManager文档的代表。 每次service从调用TypescriptPluginservice更新其文件映射给定文件(其包装后)。 createDocument将在语言服务打开一个不属于文档映射的文档时被调用——但这永远不会发生,因为 typescript 语言服务没有找到其他 svelte 模块(见下一节)。

目前存在的不足和改进思路

  • 由于脚本标记按原样提供给打字稿语言服务,因此它无法处理特殊的细长语法 ($)。
  • 打字稿语言服务尝试查找当前打开的 svelte 文件中引用的其他文件。 对于.ts / .js文件这是有效的,对于.svelte文件则无效。 原因:打字稿语言服务不知道.svelte结尾,所以它只是假设它是一个普通的打字稿文件并搜索像../Component.svelte.ts这样的文件,这是错误的。 为了解决这个问题,我们需要在LanguageServiceHost resolveModuleNames上实现方法LanguageServiceHost并将所有.svelte.ts文件查找重新路由到.svelte 。 然后语言服务将遍历所有细长的文件。 注意:为了实现这一点,我们还需要修复以下错误:由于打字稿语言服务现在会遍历所有文件,它会从TypescriptPlugin调用createDocument TypescriptPlugin 。 但这目前返回整个文档,而不仅仅是脚本标签内的内容。 这是wrapFragmentPlugin一个错误,没有包装openDocument
  • 为了通过单击 svelte 组件导入名称来实现更高级的功能,例如“转到 svelte 文件”,或者让 typescript grog 带有 $ 符号,我们需要添加一个预处理器。 这个预处理器会以某种方式为打字稿添加缺失的部分。 理想情况下,所有额外的东西都可以放在现有代码之上,这样映射位置就像用偏移量映射位置一样简单。 这带来了另一个挑战:我们现在有两个位置映射:一个从整个 svelte 文件到脚本标签,一个从脚本标签到预处理的打字稿代码。 我现在不确定这是否是可行的方法,或者我们是否应该重新设计TypescriptPlugin (和其他人)获取其文档的方式 - 也许脚本标签的提取和预处理都应该在一个步骤中完成. 这意味着更改或完全替换wrapFragmentPlugin 。 另一个返工的论点是,为了正确确定诸如“未使用的函数”之类的东西,我们必须将 html 部分考虑在内,因此脚本标记是不够的。

想法?

resolveModuleNames ,以下是 TypeScript 团队成员为 Vetur 所做的这是我为 Svelte 所做

@dummdidumm你介意充当技术负责人并将这些事情分解为特定问题吗? 我很乐意为开发时间提供志愿服务,但我发现这种情况很多,而且在小的、明确的问题上会更好地工作。 除非需要进行一些重大的架构更改(看起来没有?)

基本上只是打开一堆问题,让人们声称并管理它们作为我们的非正式技术负责人。 我愿意这样做,但老实说只是还没有感觉到痛苦,哈哈

还为正在浏览的其他人链接到 orta 的原始 LSP 帖子https://github.com/sveltejs/svelte/issues/4518

当然我很乐意在这方面提供帮助,但现在我只是一个对语言服务器非常兴奋的随机家伙,深入研究代码并提出一些建议😄我还需要更多时间来熟悉代码,它的架构和一般的 lsp。 但我绝对愿意推动事情向前发展。 @orta既然你是这个 repo 的维护者,你怎么看?

👋 是的,我们都是随机的人,只是关心 - 我什至没有任何简洁的代码库来触发我的“这应该更好”之痒。 所以,我很高兴看到任何运动 - @dummdidumm - 推开我会支持你并确保一切顺利进行

太好了! 我会继续做一些 PR 来重组一些代码并准备它,这样我们就可以并行处理单个项目。

也许我们可以结合@ryanatknsvelete-ts的方法和eslint-plugin-svelte3的可变方法:

  1. 使用svelte preprocess将脚本预处理为 js。
    在这里,我们可以转译到最新或下一个版本的 ES,以尽可能少地进行转换。
  2. 让 svelte编译预处理过的源代码。
  3. 然后我们可以将injected = true结果变量注入到实例 ts 源或 ts AST 中,然后将其与源映射一起使用以提供类型检查和自动完成。

该脚本可以包含三个片段:模块<script context="module"> 、实例和小胡子(模板)。 小胡子部分现在可以全部转储到 template.js 中。 但从长远来看,我们可以将模板反映到 tsx 或像 ryanatkn 的方法一样的普通 ts 中。

模块变量,其中module = true ,将被注入到 template.js 和实例中,而不是相反。

通过这种方式,我们不必重新实现如何在 typescript AST 中查找反应变量$: a = 1或 $-prefixed store。 svelte 编译器注入的这些变量来自顶级语句,我们正在将其转换为最新的 ES。 所以它不应该被打字稿重命名。

关于反应变量$: a可以这样写:

$: a = 1

```ts
让一个:数字
$: a = 1

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

在转译之前不需要转换的所有它都是有效的 ts

和 $-prefixed store 我们可以创建一个像 svelte/store get这样的通用函数来提取存储类型

/** injected */
let $store = getType(store)
  1. 使用svelte preprocess将脚本预处理为 js。
    在这里,我们可以转译到最新或下一个版本的 ES,以尽可能少地进行转换。
  2. 让 svelte编译预处理过的源代码。
  3. 然后我们可以将injected = true结果变量注入到实例 ts 源或 ts AST 中,然后将其与源映射一起使用以提供类型检查和自动完成。

我认为 svelte-ts 会做类似的事情。 似乎是个好主意。 在injected = true :究竟什么代码具有此属性?

该脚本可以包含三个片段:模块<script context="module"> 、实例和小胡子(模板)。 小胡子部分现在可以全部转储到 template.js 中。 但从长远来看,我们可以将模板反映到 tsx 或像 ryanatkn 的方法一样的普通 ts 中。

模块变量,其中module = true ,将被注入到 template.js 和实例中,而不是相反。

通过这种方式,我们不必重新实现如何在 typescript AST 中查找反应变量$: a = 1或 $-prefixed store。 svelte 编译器注入的这些变量来自顶级语句,我们正在将其转换为最新的 ES。 所以它不应该被打字稿重命名。

所以你想把文件分割成一个虚拟的.ts -file 和一个.template -file? 为什么不在.ts文件底部的小胡子部分? 这样我们也将摆脱“未使用的方法”-警告。 关于走 AST:是的,我认为我们可以在这里聪明一点,跳过过多地查看 AST,因为模板中引用的所有内容都必须是脚本中的顶级。

关于反应变量$: a可以这样写:

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

或者

$: a = someFunction(b) as Type

在转译之前不需要转换的所有它都是有效的 ts

是否可以推断类型? 也许这只是我们保持原样的东西,如果用户想要使用打字稿,他必须自己定义let a: number 。 作为替代方案,我们可以插入let a; ,但它会是any

和 $-prefixed store 我们可以创建一个像 svelte/store get这样的通用函数来提取存储类型

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

是的,这看起来不错。 我的另一个想法是构建 getter/setter。

我认为 svelte-ts 会做类似的事情。 似乎是个好主意。 在injected = true :究竟什么代码具有此属性?

编译结果有一个属性 vars,它是一个具有以下属性的对象数组:
name、export_name、injected、module、mutated、reassigned、referenced_from_script 和 writable
您可以查看 svelte compile api 以获取更多信息

所以你想把文件拆分成一个虚拟的 .ts 文件和一个 .template 文件? 为什么不在 .ts 文件底部放置小胡子部分? 这样我们也将摆脱“未使用的方法”-警告。

这是eslint-plugin-svelte3 的方法。 另外我只是想如果我们想在模板中支持 ts ,因为没有指定语言的属性。

是否可以推断类型?

由于这个虚拟源只用于类型检查和自动完成,我认为它可以通过复制第一个赋值语句来实现它,但它似乎需要更多的工作。

是否可以推断类型? 也许这只是我们保持原样的东西,如果用户想要使用打字稿,他必须自己定义 let a: number 。 作为替代方案,我们可以插入 let a;,但它会是 any。

看起来正常的推理并没有让我们一路走好。 (你的意思是编译时推断?不确定这是否可行)

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

原因是在线性代码流中,TypeScript 可以看到a没有被重新分配,但是嵌套的函数作用域没有这个保证,并且 TypeScript 在类型安全方面犯了错误。

游乐场示例

没错,这些就是我的担忧。 但正如@jasonlyu123指出的那样,我们可以复制定义( =之后的所有内容)。

$: a = true;

let a = true;
$: a = true;

但我不知道这是否适用于所有情况。 特别是对于有人实现接口的大型对象,这不会像用户期望的那样推断出所有内容。 此外,复制意大利面可能会变得很大。 所以我更倾向于“让用户输入”。

感谢您的澄清,我误解了@jasonlyu123所说的内容。 它修复了我提到的嵌套函数作用域问题。

有没有什么时候这不是好的默认行为的例子? 它似乎在大多数情况下都符合人体工程学,其他情况可以手动修复。 这里有一些例子。

嗯,我想你是对的。 这是最符合人体工程学的东西。 我担心的是推断类型是否错误,例如属性是string而不是定义的字符串联合( 'a' | 'b' )。 但是正如你所说,这些情况并不常见,然后用户仍然可以手动输入。

我的一部分是这样的:“太棒了,显式的 let 语法非常棒”,但这最终无法与开箱即用的 JS 支持一起使用

只替换第一个标签如何让:

$: a = 1
$: a = 2

转换为

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

我重新思考了一下,找不到“复制定义”相对于“用$:替换let ”的优势。 你们能想到用let替换$:有什么缺点吗?

关于推断类型与用户期望的差异,我们能否提供重构操作将其转换为手动定义? 此操作将提示用户像这样手动键入变量,或者尽可能重构为其推断的类型。

$: a = 'const1'

let a : AskUserForType
$: a = 'const1'

也为你指出的东西思考,学到东西👍

哈,聪明的主意,只需通过let完全替换$: let 。 我也不知道在任何情况下最终会变得糟糕。
我们还可以考虑始终进行转换,而不仅仅是针对第一个标签。 然后会有一个“无法重新声明” - 错误提示用户“糟糕,我定义了这个变量两次” - 或者在某些情况下这是可行的而不是错误的?

很好,我想不出任何更换标签会失败的原因。 (因为break $永远不会在作业中工作,对吗?)

为了重新声明相同的反应变量,看起来 Svelte 允许这样做,并且它按您期望的顺序工作。 (请参阅此 repl 示例)我个人并不认为自己是故意使用这种模式,并且希望出现错误,但它属于解释 Svelte 语义的领域,所以不要相信我的话!

好的,所以重新声明它是一种实际有效的模式。 我会说我们最好允许它 - 或者稍后设置“不允许重新声明” - 功能标志,默认情况下是关闭的。

@halfnelson基于他在https://github.com/halfnelson/svelte-type-checker-vscode 中的svelte2tsx 方法创建了一个语言服务器。 我真的很喜欢这一切已经很好了,但我仍然不想使用 tsx 作为中间输出,因为我担心它可能有点太慢并且有一些限制 - 但这只是我的直觉。 尽管如此,我们还是可以从 svelte2tsx 中学到很多东西:解析-编译-转换的流程是如何完成的,以及广泛的测试套件。 其他人如何看待将代码转换为 tsx 的方法?

我想过其他转换会是什么样子。 这里有些例子。 欢迎反馈。

组件定义的样子

原来的:

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

    export let todo: string;

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

转换:

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

解释:
以某种方式获取所有道具(简单)和调度事件(困难)并将它们添加到一个大道具对象中。 添加带有on: -prefix 的事件应该可以最大限度地减少冲突并使自动完成建议更容易实现。

(在以下示例中,为简洁起见省略了组件定义)

在模板中使用变量

原来的:

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

转换:

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

解释:
添加任意const元素,可能以一些晦涩的 unicode 字符为前缀,以检查变量的正确用法并使“未使用”警告消失。 也许我们然后需要考虑如何摆脱未使用的_bla -warning ...

使用本机事件侦听器

原来的:

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

转换:

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

解释:
使用本机事件监听器时,创建相应的元素,然后添加事件监听器。 通过这种方式,我们利用了addEventListener -typings 的优势,它对几乎每个侦听器都有重载: .addEventListener('click', ...); -> event is of type MouseEvent 。 在检查元素是否是另一个定义了事件的 svelte 组件后,将使用此模式(另请参见下一个示例)。 缺点:如果在非网络环境(Nativescript)中使用 Svelte 会怎样?

使用其他组件

原来的:

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

转换:

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

解释:
每个组件都有一个类定义,所以我们只是实例化那个类。

每个循环

原来的:

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

转换:

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

解释:
forEach似乎是最容易转换的。 在内部,我们可以递归地应用所有其他功能。

如果

原来的:

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

转换:

if (x === 1) {

} else if (x === 2) {

} else {

}

解释:
不言自明。

缺件

  • 插槽
  • 特殊的svelte:x组件
  • 其他元素/组件指令,例如use:actiontransitionbind
  • $-语法

你们有什么感想? 这些转变是否可行? 我错过了什么?

关于组件类我更喜欢扩展 svelte 自己的 SvelteComponent 类,但我不知道这是否容易实现

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

并导入这些接口

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

注意到$$slots可能是 svelte 的内部 api,我只是从编译代码中挖掘它

这样做的原因是它可以用来发出.d.ts可以用来输入 vanilla ts/js。
也因为,虽然很奇怪,但这是有效的语法:

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

元素指令

Transitionuse:action和其他元素指令可以像这样转换

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}

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

绑定:这个

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

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

需要将它包装在回调中,因为它不是立即可用的

上下文=“模块”

这有点棘手

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

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

应该编译为

import _ from "lodash"

let count = 1;

// any block is ok
{
    let b;
{

如果用户以某种方式在两个脚本中使用不同的语言,例如模块中的 js 和实例中的 ts 怎么办?

不错,看着不错对于“在模板中使用变量” const _bla = bla.bla;可能只是bla.bla;让 TypeScript 进行检查而无需过滤掉“未使用的变量”诊断。 我想不出任何不起作用的极端情况。

一些 Vue 团队正在探索打字稿语言服务器插件 - https://github.com/znck/vue-developer-experience

这是一个有趣的角度,因为今天这些经验来自 TypeScript 外部和内部(就像 vetur 一样,因为它运行自己的 tsserver-ish,以及 html/css/etc LSP),或者您是否尝试从 TypeScript 内部进入并向外工作(例如本提案),您可以在其中操作 TSServer,通过屏蔽非 TS 代码来有效地相信 .vue 文件是合法的 TypeScript。

虽然今天它只能通过打字稿打补丁来工作

这是一个有趣的方法。 但是,像 html 或 css 的自动完成功能这样的功能是如何工作的呢? 是否必须“手动”实现这一切,而不再依赖开箱即用的语言服务器,还是我错过了什么?

你看到一遍遍地做一个有什么好处吗?

除了 JS/TS 支持之外的任何东西都将由单独的 vscode LSP 处理。 就像如果这个扩展删除了对 JS/TS 的所有支持而只处理其余的,那么 typescript 插件将处理其余的。

在这种情况下,您可以“免费”获得所有 TS 工具,跳转到符号、文件映射等。

我可能不推荐它,今天它需要 TypeScript 的 fork 或补丁才能工作,这是一个相当高的进入门槛——而且 TypeScript 团队仍然不确定编译器插件是否/何时可以拥有这种访问权限。

感谢您的见解。 我会说我们采用当前的方法(从外到内)。

如果我们采用上面讨论的“制作虚拟 ts 文件”的方法,我们将如何实现这一点并保持源映射?

今天,这很容易,因为它只是“找到脚本的开头,找到脚本的结尾”,“提取脚本部分”和映射“将细长文件的偏移量添加到位置”。
如果我们现在改变脚本部分,我们需要一个更复杂的映射:

原来的

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

映射:

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

有任何想法吗? 我以前从未做过源映射的东西..

一些 Vue 团队正在探索打字稿语言服务器插件 - https://github.com/znck/vue-developer-experience

这是一个有趣的角度,因为今天这些经验来自 TypeScript 外部和内部(就像 vetur 一样,因为它运行自己的 tsserver-ish,以及 html/css/etc LSP),或者您是否尝试从 TypeScript 内部进入并向外工作(例如本提案),您可以在其中操作 TSServer,通过屏蔽非 TS 代码来有效地相信 .vue 文件是合法的 TypeScript。

虽然今天它只能通过打字稿打补丁来工作

移除 TypeScript 补丁 PR 已合并。
https://github.com/znck/vue-developer-experience/pull/7

所以...... @orta这意味着关于“非官方”的缺点不再如此? 或者它仍然是一种非官方的,只是不是通过补丁而是通过一些您必须知道并且可以随时更改的package.json配置?

另一个话题:目前我觉得我们仍在讨论如何最好地解决这个问题,如果我们决定,那么实施它需要一些时间。 你们觉得在此期间使用svelte2tsx怎么样? 它可以作为一个很好的起点,我们可以围绕它进行一些测试,然后我们要么看到“好吧,这确实很好用,我们不要改变它”,或者逐渐远离它。 想法?

是的,它有两种编辑 TS 的默认流程的方式,它仍在修补 TypeScript -但在运行时通过替换 TS 的功能。 我认为这是合理的,因为插件开发还没有在路线图上。

如果我推荐它,我会成为一个糟糕的核心团队成员;)

尝试svelte2ts听起来是一个很好的探索途径!

您可以使用此 vscode 插件查看 svelte2tsx 的效果: https ://marketplace.visualstudio.com/items?itemName

即使不使用 svelte2tsx,我也会看到关于需要对 svelte 的 JS 代码进行哪些转换以保持打字稿满意的讨论。 svelte2tsx 的测试套件不仅涵盖了需要对模板进行的更改,还涵盖了脚本标签,如果您要使用仅脚本标签的解决方案,这可能是一个很好的起点: https :

htmlx2jsx是模板更改测试 (htmlx) 的文件夹,而svelte2tsx示例是 svelte 对 js 语法的特殊滥用所需的 svelte 特定更改:)

刚刚发现这个 repo https://github.com/pivaszbs/svelte-autoimport 。 如果我们想要独立于svelte2tsx导入智能感知,也许值得研究。

我认为我们正处于对这个项目的大部分总体方向进行排序并且看起来很顺利的时候。 我会给这个一周的反馈,但我很高兴我们现在不需要在架构上重新考虑它。

似乎没有人反对,所以我要关闭这个。 谢谢大家讨论这个!

此页面是否有帮助?
0 / 5 - 0 等级