Language-tools: 言語サーバーでのアプローチの概要

作成日 2020年03月24日  ·  37コメント  ·  ソース: sveltejs/language-tools

このスレッドは、svelteファイル用の言語サーバーを実装する方法の概要を説明することを目的としています。 推奨されるソリューションについての議論は、ここでも行うことができます。

言語サーバーの現在の状態

現在、構文の強調表示といくつかの基本的なオートコンプリートは、svelteファイルに対して機能します。
ただし、リッチインテリセンスは提供されておらず、ワークスペース内のすべてのtypescriptファイルを認識しておらず、svelteコンポーネントに関するタイプ情報もありません。
特別なsvelte構文が強調表示されているが、インテリセンスが機能していないhtml部分についても同じことが言えます。 デフォルトで特別な構文を知らないvscode-html-languageserviceが使用されます。
現在の言語サーバーは、ファイルを解析し、「このimgタグにalt属性がありません」などの診断を取得するためにsvelteコンパイラを使用します。
より詳細な分析:コメント

既存のアプローチ/ソリューション

免責事項:私は既存のソリューションの作成者ではありませんが、コードを確認しました。 情報の一部が間違っているか、十分に詳細でない場合、または解決策がない場合は、コメントしてください。調整します。

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

htmlparser2を使用して、
espreeを使用して、
結果をJSON形式で提供します。

https://github.com/ArdenIvanov/svelte-intellisenseは、これを使用してsvelteファイルを解析します。 結果を使用して、いくつかの追加の動作を添付します。

これはjavascriptパーサーを使用するため、typescriptでは機能しません。

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-filesを作成します。 次に、typescriptの言語サービスを使用しますが、要求をプロキシします。元のファイルパスを、生成されたtsxファイルへのファイルパスに置き換えます。 したがって、typescript言語サーバーは生成されたtsxファイルをチェックします。 結果は、正しいドキュメント位置を取得するために変換されます。

https://github.com/halfnelson/svelte-type-checker-vscodeは、svelte2tsx-libraryを使用する別の言語サーバーです。

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

実装者のコメントからの説明:

コンパイラ:

  1. コンパイラはすべてのソースファイルを読み取ります
  2. スクリプトタグの内容以外はすべて破棄されます
  3. スクリプトのソースコードは、有効なJSファイルと宣言ファイルにコンパイルされます。
  4. 手順2に戻り、スクリプトタグのTSソースをコンパイル済みのJSに置き換えます。
  5. Svelteコンパイラはソースファイル全体をコンパイルします
  6. 上記のステップの出力コンパイルはキャッシュされ、HTMLASTがタイプチェッカーで使用されます。

エクスポートされた変数と関数をSvelteスクリプト内から使用し、ランタイムSvelteComponentを拡張するクラスを含む宣言ファイルとして生成します。

タイプチェッカー:

  1. タイプチェッカーは、キャッシュされたAST内のすべてのコンポーネントを検索します。
  2. テンプレート内のすべてのコンポーネントがSvelteComponentを拡張する有効なクラスであることを確認します
  3. 有効な宣言、プロパティなどをチェックします。

その他のアプローチ

このスレッドのコメントを参照してください。

最も参考になるコメント

よし! 先に進んで、いくつかのPRを作成し、コードの一部を再構築して準備し、個々のアイテムの作業を並列化できるようにします。

全てのコメント37件

では、Svelteパーサーを使用しているものはありませんか?

svelte2tsxは、スタイル/スクリプトタグを取り除き、svelteコンパイラを使用してhtmlx部分を解析します。
svelte-tsは、2番目のステップとしてsvelteコンパイラーを使用します。
現在の言語サーバーは、解析にsvelteコンパイラーを使用していません。
最初の投稿を更新します。

これを共有すべきかどうかはわかりませんでしたが、その一部が役立つかもしれません。 昨年(2019年7月/ 8月)私は、学ぶためだけに使い捨ての仕事をしていることを知って、Svelteのタイプチェックをいじり始めました。 未知の量の完全なソリューション(おそらく30%)をサポートし、いくつかの制限があります(これらについては後で詳しく説明します)。 私はいつも行き止まりだと思っていたので、テストや外部ドキュメントはありません。

Svelteコンパイルは、タイプチェックを行わないTSプリプロセッサを使用した場合と同じように実行されます。 スクリプトタグだけでなく、HTMLxマークアップに型が必要な場合は、それを処理するためのプリプロセッサが必要です。そうでない場合、SvelteはTSを内部で処理する必要があります。

型チェッカーから非前処理活字体のコードを使用して別のビルドステップでscriptブロックからのマークアップAST svelte.compileするために仮想スヴェルトtypescriptファイルを作成する目的で型チェックのためだけに。 (ロールアップのプラグインはどこここにある仮想ファイルを作成して、それをtypechecks )それはとてもマークアップで、それはです。TypeCheck HTMLタグにしようとしない、@halfnelsonの戦略が、それは通常のTSを生成すること異なるではなく、TSXに類似だと属性、口ひげタグとSvelteコンポーネント内のもののみ。 Svelteコンポーネントを、小道具でnewを取得するコンストラクターに変換します。 意図した以上に夢中になっていることに気付く前に、Svelteの多くの構成をサポートする途中でしたが、さらなる進歩は深刻なエンジニアリングになるはずです。

TypeScriptコンパイラAPIは、メモリ内にプログラムチェックするために使用されます。 Svelteとは異なり、その世界観はプログラム全体であり、個々のファイルではありません。 当時、将来の最適化としてインクリメンタルコンパイルとワーカースレッドを追加するのは簡単なようでした。

仮想SvelteTSファイルはソースマップを使用して生成され、 TypeScript診断を元のSvelteソースます。 タイプエラーがSvelteソースを指しているのを見るのはかなりクールでしたが、全体的なアプローチに自信がありませんでした。

私が理解できなかったことを覚えている1つの問題は、場合によっては十分に具体的でない診断に関するものでした。 IIRCの例は、Svelteコンポーネントコンストラクターの小道具のタイプチェックでした-VSCodeはどういうわけか個々の小道具エラーを指していましたが、私が得たTS診断は、問題のプロパティではなく、コンストラクターのみを指していました。

コードを確認する場合は、パーツが混乱していて、一部のコメントが古くなっている可能性があることに注意してください。 私はもともとmagic-stringを使用していましたが、ある時点でsource-map 。 そして、それは最初から運命の努力だったことを忘れないでください! これらはすべて、 masterではなく、そのリポジトリのtsブランチにあることに注意してください。

上記で参照されている4つのファイルは次のとおりです。

私は現在のsvelte言語サーバーのソースコードを掘り下げました。 これがそのタイプスクリプト部分についての私の考えです:

概要

言語サーバーは、次のように広く機能します。

  • DocumentManagerは、開かれるすべてのドキュメントを処理します。
  • TypescriptPluginDocumentManagerに登録され、言語サーバーイベント(「doHover」など)で呼び出されます。 直接登録されません。 wrapFragmentPluginでラップされ、 TypescriptPluginscriptタグ内のコンテンツのみを取得するようにします。 (マウス/テキストカーソルの)位置のマッピングはwrapFragmentPlugin行われ、情報が正しい位置に表示されるようにします。
  • TypescriptPluginは、typescript言語サービスラッパーであるservice使用します。 基本的に、すべてのイベントをこの言語サーバーに委任し、メソッドの戻り型に準拠するためのマッピングをいくつか行います。
  • serviceは、 TypescriptPluginからのcreateDocumentメソッドと同様に、ドキュメントを処理します。 serviceは、現時点では基本的にDocumentsManagerのドキュメントの代理人にすぎない独自のドキュメントマップを保持しています。 serviceTypescriptPluginから呼び出されるたびに、 serviceは、指定されたドキュメントでドキュメントマップを更新します(ラップした後)。 createDocumentは、言語サービスがまだドキュメントマップの一部ではないドキュメントを開いた場合に呼び出されますが、typescript言語サービスは他のsvelteモジュールを検出しないため、これは発生しません(次のセクションを参照)。

現在の欠点と改善のためのアイデア

  • scriptタグはtypescript言語サービスにそのまま与えられるため、特別なsvelte構文($)を処理することはできません。
  • typescript言語サービスは、現在開いているsvelteファイルで参照されている他のファイルを検索しようとします。 .ts / .jsファイルの場合、これは機能しますが、 .svelteファイルの場合は機能しません。 理由:typescript言語サービスは.svelte終わりを知らないので、それが通常のtypescriptファイルであると想定し、 ../Component.svelte.tsようなファイルを検索します。これは間違っています。 これを修正するには、 LanguageServiceHostメソッドresolveModuleNamesを実装し、すべての.svelte.tsファイルルックアップを.svelte再ルーティングする必要があります。 次に、言語サービスはすべてのsvelteファイルをウォークします。 注:これを実装するには、次のバグも修正する必要があります。typescript言語サービスはすべてのファイルをウォークするため、 TypescriptPluginからcreateDocumentを呼び出します。 ただし、これは現在、scriptタグ内のコンテンツだけでなく、ドキュメント全体を返します。 これはwrapFragmentPlugin内のバグでopenDocumentラップしていません。
  • svelteコンポーネントのインポート名をクリックして「gotosvelte file」などのより高度な機能を実装したり、typescriptを$記号でグロッグしたりするには、プリプロセッサを追加する必要があります。 このプリプロセッサはどういうわけかtypescriptの不足している部分を追加します。 理想的には、すべての追加のものを既存のコードの上に配置できるため、位置のマッピングは、オフセットを使用して位置をマッピングするのと同じくらい簡単です。 ただし、これには別の課題があります。1つはsvelteファイル全体からscriptタグへ、もう1つはscriptタグから前処理されたtypescriptコードへの2つの位置マッピングがあります。 これが進むべき道なのか、それともTypescriptPlugin (およびその他)がドキュメントを取得する方法を作り直す必要があるのか​​、現時点ではわかりません。スクリプトタグの抽出と前処理の両方を1つのステップで実行する必要があるかもしれません。 。 これは、 wrapFragmentPlugin変更または完全に置き換えることを意味します。 リワークのもう1つの論拠は、「未使用の関数」などを正しく判別するには、html部分を考慮に入れる必要があるため、scriptタグでは不十分であるということです。

考え?

resolveModuleNames、TypeScriptチームメンバーがVeturに対して行った方法と、Svelteに対して行った同様のバージョンがあります。これには、機能した代替戦略について言及したコメントが含まれていますが、Veturのコピーの方が優れているようです。

@dummdidumm技術リーダーとして行動し、これらのことを特定の問題に分解してもよろしいですか? 私は開発時間をボランティアで提供できれば幸いですが、これは圧倒的なものであり、明確に定義された小さな問題でうまく機能します。 実行する必要のある大きなアーキテクチャの変更がない限り(いいえのように見えますか?)

基本的には、たくさんの問題を切り開いて、人々がそれらを主張し、これに関する非公式の技術リーダーとして管理できるようにします。 私はそれをすることを申し出ますが、正直なところ、まだ痛みを感じていませんハハ

https://github.com/sveltejs/svelte/issues/4518を閲覧している他の人のためのortaの元のLSP投稿にもリンクしてい

確かに私はこれを手伝いたいのですが、今のところ私は言語サーバーに非常に興奮し、コードを掘り下げていくつかの提案をしているランダムな男です😄コードに慣れるにはもう少し時間が必要です、そのアーキテクチャと一般的なlsp。 しかし、私は間違いなく物事を前進させたいと思っています。 @ortaあなたはこのレポの

👋ええ、私たちはどちらも気にかけるランダムな人々です-私は「これはもっと良いはずです」というかゆみを引き起こすための洗練されたコードベースさえ持っていません。 だから、どんな動きも見てとてもうれしいです- @ dummdidumm-押しのけて、あなたをバックアップして、すべてがスムーズに実行されることを確認します

よし! 先に進んで、いくつかのPRを作成し、コードの一部を再構築して準備し、個々のアイテムの作業を並列化できるようにします。

たぶん、 @ ryanatknsvelete-tsのアプローチとeslint-plugin-svelte3の可変アプローチを組み合わせることができます。

  1. svelte preprocessを使用して、スクリプトをjsに前処理します。
    ここでは、ESの最新バージョンまたは次のバージョンにトランパイルして、変換をできるだけ少なくすることができます。
  2. 前処理されたソースをsvelteでコンパイルしましょう。
  3. 次に、 injected = true結果変数をインスタンスtssourceまたはtsASTに挿入し、それをソースマップで使用して、型チェックとオートコンプリートを提供できます。

スクリプトには、モジュール<script context="module"> 、インスタンス、口ひげ(テンプレート)の3つのフラグメントを含めることができます。 口ひげの部分は、今のところすべてtemplate.jsにダンプすることができます。 しかし、長期的には、テンプレートをtsxまたはryanatknのアプローチのようなプレーンなtsに反映することができます。

モジュール変数( module = true )は、template.jsとインスタンスに挿入されますが、その逆はありません。

このようにして、typescript ASTでリアクティブ変数$: a = 1または$プレフィックス付きストアを見つける方法を再実装する必要はありません。 svelteコンパイラによって注入されたこれらの変数はトップレベルのステートメントから派生しており、最新のESにトランスパイルしています。 したがって、ほとんどの場合、typescriptで名前を変更するべきではありません。

反応変数$: aについては、次のように記述できます。

$: a = 1

`` `ts
:番号をしましょう
$:a = 1

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

トランスパイルする前に変換する必要がないのはすべて有効なtsです

そして、$ -prefixedストアは、我々はほっそり/ストアなどの一般的な機能を作成することができますGETを抽出ストアタイプに

/** injected */
let $store = getType(store)
  1. svelte preprocessを使用して、スクリプトをjsに前処理します。
    ここでは、ESの最新バージョンまたは次のバージョンにトランパイルして、変換をできるだけ少なくすることができます。
  2. 前処理されたソースをsvelteでコンパイルしましょう。
  3. 次に、 injected = true結果変数をインスタンスtssourceまたはtsASTに挿入し、それをソースマップで使用して、型チェックとオートコンプリートを提供できます。

svelte-tsはそのようなことをしていると思います。 良い考えのようです。 injected = true :このプロパティを正確に持っているコードは何ですか?

スクリプトには、モジュール<script context="module"> 、インスタンス、口ひげ(テンプレート)の3つのフラグメントを含めることができます。 口ひげの部分は、今のところすべてtemplate.jsにダンプすることができます。 しかし、長期的には、テンプレートをtsxまたはryanatknのアプローチのようなプレーンなtsに反映することができます。

モジュール変数( module = true )は、template.jsとインスタンスに挿入されますが、その逆はありません。

このようにして、typescript ASTでリアクティブ変数$: a = 1または$プレフィックス付きストアを見つける方法を再実装する必要はありません。 svelteコンパイラによって注入されたこれらの変数はトップレベルのステートメントから派生しており、最新のESにトランスパイルしています。 したがって、ほとんどの場合、typescriptで名前を変更するべきではありません。

それで、ファイルを仮想.tsファイルと.templateファイルに分割したいですか? .tsファイルの下部に口ひげの部分がないのはなぜですか? このようにして、「未使用の方法」(警告)も取り除くことができます。 ASTを歩くことについて:はい、私たちはここで賢くなり、テンプレートで参照されるすべてのものがスクリプトのトップレベルでなければならないので、ASTの調査をスキップしすぎることができると思います。

反応変数$: aについては、次のように記述できます。

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

また

$: a = someFunction(b) as Type

トランスパイルする前に変換する必要がないのはすべて有効なtsです

タイプを推測することは可能ですか? たぶん、これは私たちがそのままにしておくものであり、ユーザーがtypescriptを使用したい場合は、自分でlet a: numberを定義する必要があります。 別の方法として、 let a;挿入することもできますが、その場合はanyます。

そして、$ -prefixedストアは、我々はほっそり/ストアなどの一般的な機能を作成することができますGETを抽出ストアタイプに

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

ええ、これはいいようです。 私が持っていた別のアイデアは、ゲッター/セッターを構築することでした。

svelte-tsはそのようなことをしていると思います。 良い考えのようです。 injected = true :このプロパティを正確に持っているコードは何ですか?

コンパイル結果には、次のプロパティを持つオブジェクトの配列であるプロパティvarsがあります。
name、export_name、injected、module、mutated、reassigned、referred_from_script、およびwritable
詳細については、svelte compileapiを参照してください。

それで、ファイルを仮想.tsファイルと.templateファイルに分割したいですか? .tsファイルの下部に口ひげの部分がないのはなぜですか? このようにして、「未使用の方法」(警告)も取り除くことができます。

これはeslint-plugin-svelte3のアプローチです。 また、言語を指定する属性がないため、テンプレートでtsをサポートしたいかどうかも考えました。

タイプを推測することは可能ですか?

この仮想ソースは型チェックとオートコンプリートにのみ使用されるため、最初のassignステートメントをコピーして初期化するだけで実現できると思いますが、さらに作業が必要なようです。

タイプを推測することは可能ですか? たぶん、これは私たちがそのままにしておくものであり、ユーザーがtypescriptを使用したい場合は、let a:numberを自分で定義する必要があります。 別の方法として、let a;を挿入することもできますが、その場合は任意になります。

通常の推論では、そこまで到達できないようです。 (コンパイル時の推論を意味しましたか?それが機能するかどうかはわかりません)

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が言ったことを誤解し

それが適切なデフォルトの動作ではない場合の例はありますか? ほとんどの場合、人間工学的に正しいことをしているようですが、他の場合は手動で修正できます。 ここにいくつかの例があります。

うーん、あなたが正しいと思います。 それは最も人間工学的なものです。 私が心配しているのは、推論された型が間違っているかどうかです。たとえば、プロパティは、定義された文字列の和集合( 'a' | 'b' )ではなくstringです。 しかし、あなたが言ったように、これらのケースはそれほど一般的ではなく、ユーザーはそれを手動で入力することができます。

私の中には、「素晴らしい、明示的なlet構文は素晴らしい動作をする」というような部分がありますが、それは、そのままではJSサポートで機能しなくなるでしょう。

最初のラベルを置き換えるだけでどうでしょうか。

$: a = 1
$: a = 2

に変換を取得します

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

少し考え直してみると、「 $:let置き換える」よりも「コピー定義」の利点が見つかりません。 $:letに置き換えることの不利な点について考えてみてください。

ユーザーが期待したものとの推定タイプの違いについて、手動定義に変換するためのリファクタリングアクションを提供できますか? このアクションにより、ユーザーはこのように変数を手動で入力するか、可能であれば推定された型にリファクタリングするように求められます。

$: a = 'const1'

let a : AskUserForType
$: a = 'const1'

また、あなたが指摘したことについて考え、何かを学びました👍

賢い考えですが、 $:let完全に置き換えます。 また、それが悪い結果になる状況もわかりません。
また、最初のレーベルだけでなく、常に変革を行うことも考えられます。 次に、「再宣言できません」(ユーザーに「うわー、この変数を2回定義しました」と示唆するエラー)がありますか、それともこれが実行可能で間違いではない状況がありますか?

いいですね、ラベルの交換も失敗する理由は考えられません。 ( break $は割り当て内では機能しないので、そうですか?)

同じリアクティブ変数を再宣言する場合、Svelteがこれを許可しているように見え、期待どおりに機能します。 (このreplの例を参照してください)私は個人的にこのパターンを意図的に使用しているとは思わず、エラーが必要ですが、Svelteのセマンティクスを解釈する領域にあるため、私の言葉を信じないでください!

OK、それを再宣言するのは実際に機能するパターンです。 その後、それを許可するか、「再宣言は許可されていません」という機能フラグを後で作成する方がよいと思います。これはデフォルトでオフになっています。

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

説明:
どういうわけか、すべての小道具(簡単)とディスパッチされたイベント(難しい)を取得し、それらを1つの大きな小道具オブジェクトに追加します。 on:プレフィックスを付けてイベントを追加すると、衝突が最小限に抑えられ、オートコンプリートの提案が簡単に実装できるようになります。

(以下の例では、簡潔にするためにコンポーネント定義は省略されています)

テンプレートで変数を使用する

オリジナル:

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

変換された:

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

説明:
任意のconst要素を追加し、変数の正しい使用法を確認し、「未使用」の警告をなくすために、あいまいなUnicode文字を前に付けることもできます。 たぶん、未使用の_blaを取り除く方法を考える必要があるかもしれません

ネイティブイベントリスナーを使用する

オリジナル:

<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タイプを利用します。 .addEventListener('click', ...); ->イベントのタイプはMouseEventです。 このパターンは、要素がイベントが定義されている別のsvelteコンポーネントであるかどうかを確認した後に使用されます(次の例も参照)。 欠点:Svelteが非Web環境(Nativescript)で使用されている場合はどうなりますか?

他のコンポーネントを使用する

オリジナル:

<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であることに注意してください、私はコンパイルされたコードからそれを掘り出します

これは、バニラts / jsの入力に使用できる.d.tsを発行するために使用できるためです。
また、十分に奇妙ですが、これは有効な構文であるためです。

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

bind:this

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

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

すぐには利用できないため、コールバックでラップする必要がありました

context = "module"

これは少し注意が必要です

<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のように、2つのスクリプトで異なる言語を使用している場合はどうなりますか?

いいですね。 「テンプレートで変数を使用する」の場合、 const _bla = bla.bla;bla.bla; 、「未使用の変数」診断を除外せずにTypeScriptにチェックを実行させることができます。 これがうまくいかないコーナーケースは考えられません。

Vueチームの一部はtypescript言語サーバープラグインを調査しています-https ://github.com/znck/vue-developer-experience

今日、これらのエクスペリエンスはTypeScriptの外部から(veturのように、独自のtsserver-ishやhtml / css / etc LSPを実行するため)、TypeScriptの内部から外部に向かって作業するので、興味深い角度です(たとえば、この提案)では、TSServerを操作して、非TSコードをマスクすることにより、.vueファイルが正当なTypeScriptであると効果的に信じます。

今日では、typescriptへのパッチを用意することによってのみ機能しますが

それは興味深いアプローチです。 しかし、htmlやcssの自動補完などの機能はどのように機能しますか? これをすべて「手作業で」実装する必要があり、すぐに使用できる言語サーバーに依存する必要はありませんか、それとも何かが足りないのでしょうか。

やり直すことの利点はありますか?

JS / TSサポート以外は、個別のvscodeLSPによって処理されます。 この拡張機能がJS / TSのすべてのサポートを削除し、残りのみを処理する場合、typescriptプラグインが残りを処理するようになります。

この場合、すべてのTSツールを「無料」で入手でき、シンボル、ファイルマッピングなどにジャンプできます。

私はおそらくそれをお勧めしません、今日それはエントリへのかなり高い障壁であるTypeScriptのフォークまたはパッチを必要とします-そしてTypeScriptチームはコンパイラプラグインがその種のアクセスを持っているかどうか/いつできるかまだわかりません。

洞察に感謝します。 そのときは、現在のアプローチ(アウトサイドイン)を使用します。

上記の「仮想tsファイルの作成」アプローチを採用する場合、これをどのように実装し、ソースマッピングを維持するのでしょうか。

今日では、「スクリプトの開始を検索、スクリプトの終了を検索」、「スクリプト部分を抽出」、「svelteファイルのオフセットを位置に追加」をマッピングするだけなので簡単です。
ここでスクリプト部分を変更する場合は、より高度なマッピングが必要です。

オリジナル

<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チームの一部はtypescript言語サーバープラグインを調査しています-https ://github.com/znck/vue-developer-experience

今日、これらのエクスペリエンスはTypeScriptの外部から(veturのように、独自のtsserver-ishやhtml / css / etc LSPを実行するため)、TypeScriptの内部から外部に向かって作業するので、興味深い角度です(たとえば、この提案)では、TSServerを操作して、非TSコードをマスクすることにより、.vueファイルが正当なTypeScriptであると効果的に信じます。

今日では、typescriptへのパッチを用意することによってのみ機能しますが

TypeScriptパッチの削除PRがマージされます。
https://github.com/znck/vue-developer-experience/pull/7

だから... @ ortaこれは、「非公式」に関する不利な点がもはやpackage.jsonを介して、まだ非公式なものですか?

別のトピック:現時点では、これに最善のアプローチをとる方法についてまだ話し合っていると感じています。決定した場合、実装には時間がかかるでしょう。 それまでの間、 svelte2tsxを使用することについてどう思いますか? これは非常に良い出発点として役立つ可能性があり、その周りでいくつかのテストを行うことができます。その後、「これは実際にうまく機能します。変更しないでください」と表示されるか、徐々に移行します。 考え?

はい、TSのデフォルトフローを編集する方法は2つありました。それでもTypeScriptにパッチを適用していますが、実行時にTSの関数を置き換えることによって。 プラグインの開発がまだロードマップにないことを考えると、それは合理的だと思います。

私がそれを推薦した場合、私は悪いコアチームメンバーになるでしょう;)

svelte2ts試してみるのは、探索するのに良い方法のように思えます。

このvscodeプラグインを使用してsvelte2tsxでどのようになるかを確認できます: https ://marketplace.visualstudio.com/items?itemName = halfnelson.svelte-type-checker-vscodeとSvelteベータ(svelteベータを無効にすると最適に機能します)タイプスクリプトオプションなので、ヒントが2倍にならないようにします:))

svelte2tsxを使用していなくても、typescriptを満足させるために、svelteのJSコードに対してどの変換を行う必要があるかについての議論があります。 svelte2tsxのテストスイートは、テンプレートに加える必要のある変更だけでなく、スクリプトタグもカバーします。スクリプトタグのみのソリューションを使用する場合は、 https。 / svelte2tsx / tree / master / test / svelte2tsx / samples

htmlx2jsxはテンプレート変更テスト(htmlx)のフォルダーであり、 svelte2tsxサンプルはsvelteによるjs構文の特別な悪用に必要なsvelte固有の変更です:)

このリポジトリhttps://github.com/pivaszbs/svelte-autoimportを発見しました。 svelte2tsx依存しないインポートインテリセンスが必要かどうかを調べる価値があるかもしれません。

このプロジェクトの一般的な方向性のほとんどが整理され、うまく機能しているように見える段階にあると思います。 フィードバックのためにこれを1週間提供しますが、今はアーキテクチャを再考する必要がないことを非常に嬉しく思います。

誰も異議を唱えていないようですので、これを閉じます。 これについて議論してくれてありがとう!

このページは役に立ちましたか?
0 / 5 - 0 評価