リモート開発と GitHub Codespaces のサポート
Visual Studio Code リモート開発 を使用すると、仮想または物理的な別のマシン上にあるソースコードやランタイム環境を透過的に操作できます。GitHub Codespaces は、VS Code とブラウザベースのエディターの両方からアクセスできる、マネージド型のクラウドホスト環境でこれらの機能を拡張するサービスです。
パフォーマンスを確保するために、リモート開発と GitHub Codespaces の両方で、特定の VS Code 拡張機能をリモートで透過的に実行します。ただし、これは拡張機能の動作方法に微妙な影響を与える可能性があります。多くの拡張機能は変更なしで動作しますが、拡張機能がすべての環境で適切に動作するように変更が必要になる場合があります。ただし、これらの変更は多くの場合、ごくわずかです。
この記事では、拡張機能の作成者がリモート開発と Codespaces について知っておく必要のあることをまとめています。これには、拡張機能の アーキテクチャ、リモートワークスペースまたは Codespaces での 拡張機能のデバッグ方法、および 拡張機能が正常に動作しない場合の対処法 に関する推奨事項が含まれます。
アーキテクチャと拡張機能の種類
リモート開発または Codespaces を可能な限り透過的にユーザーが利用できるようにするために、VS Code は 2 種類の拡張機能を区別しています。
-
UI 拡張機能: これらの拡張機能は VS Code のユーザーインターフェイスに貢献し、常にユーザーのローカルマシンで実行されます。UI 拡張機能は、リモートワークスペース内のファイルに直接アクセスしたり、そのワークスペースまたはマシンにインストールされているスクリプト/ツールを実行したりすることはできません。UI 拡張機能の例としては、テーマ、スニペット、言語文法、キーマップなどがあります。
-
ワークスペース拡張機能: これらの拡張機能は、ワークスペースがあるマシンと同じマシン上で実行されます。ローカルワークスペースでは、ワークスペース拡張機能はローカルマシンで実行されます。リモートワークスペースまたは Codespaces を使用している場合、ワークスペース拡張機能はリモートマシン/環境で実行されます。ワークスペース拡張機能は、ワークスペース内のファイルにアクセスして、リッチでマルチファイルな言語サービス、デバッガーサポートを提供したり、ワークスペース内の複数のファイルに対して複雑な操作を実行したりできます (直接またはスクリプト/ツールを呼び出すことによって)。ワークスペース拡張機能は UI の変更に重点を置いていませんが、エクスプローラー、ビュー、その他の UI 要素も提供できます。
ユーザーが拡張機能をインストールすると、VS Code はその種類に基づいて正しい場所に自動的にインストールします。拡張機能がどちらの種類としても実行できる場合、VS Code は状況に最適なものを選択しようとします。UI 拡張機能は VS Code の ローカル拡張機能ホスト で実行され、ワークスペース拡張機能はリモートワークスペースに存在する場合は小さな VS Code サーバー 内にある リモート拡張機能ホスト で実行され、それ以外の場合はローカルに存在する場合は VS Code のローカル拡張機能ホストで実行されます。最新の VS Code クライアント機能を利用できるようにするには、サーバーが VS Code クライアントバージョンと正確に一致する必要があります。したがって、コンテナ内、リモート SSH ホスト上、Codespaces を使用する場合、または Windows Subsystem for Linux (WSL) でフォルダーを開くと、リモート開発または GitHub Codespaces 拡張機能によってサーバーが自動的にインストール (または更新) されます。(VS Code はサーバーの起動と停止も自動的に管理するため、ユーザーはその存在に気づきません。)
VS Code API は、UI 拡張機能とワークスペース拡張機能の両方から呼び出された場合に、正しいマシン (ローカルまたはリモート) で自動的に実行されるように設計されています。ただし、拡張機能が VS Code によって提供されていない API (Node API の使用やシェルスクリプトの実行など) を使用している場合、リモートで実行すると正常に動作しない可能性があります。拡張機能のすべての機能がローカルワークスペースとリモートワークスペースの両方で適切に動作することをテストすることをお勧めします。
拡張機能のデバッグ
拡張機能の開発バージョンをリモート環境にインストール してテストできますが、問題が発生した場合は、リモート環境で拡張機能を直接デバッグすることをお勧めします。このセクションでは、GitHub Codespaces、ローカルコンテナ、SSH ホスト、または WSL で拡張機能を編集、起動、デバッグする方法について説明します。
通常、テストの最適な出発点は、ポートアクセスを制限するリモート環境 (たとえば、Codespaces、コンテナ、または制限的なファイアウォールを備えたリモート SSH ホスト) を使用することです。これらの環境で動作する拡張機能は、WSL のように制限の少ない環境でも動作する傾向があるためです。
GitHub Codespaces でのデバッグ
GitHub Codespaces プレビューで拡張機能をデバッグすることは、VS Code と Codespaces ブラウザベースのエディターの両方をテストとトラブルシューティングに使用できるため、優れた出発点となります。必要に応じて、カスタム開発コンテナ を使用することもできます。
次の手順に従ってください。
-
GitHub 上の拡張機能を含むリポジトリに移動し、codespace で開いて、ブラウザベースのエディターで作業します。必要に応じて、VS Code で codespace を開く こともできます。
-
GitHub Codespaces のデフォルトイメージにはほとんどの拡張機能に必要な前提条件がすべて含まれているはずですが、新しい VS Code ターミナルウィンドウで (たとえば、
yarn install
またはsudo apt-get
を使用して) その他の必要な依存関係をインストールできます (⌃⇧` (Windows, Linux Ctrl+Shift+`))。 -
最後に、F5 を押すか、実行とデバッグ ビューを使用して、codespace 内で拡張機能を起動します。
注: 表示されるウィンドウで拡張機能のソースコードフォルダーを開くことはできませんが、codespace 内のサブフォルダーまたは他の場所を開くことはできます。
表示される拡張機能開発ホストウィンドウには、デバッガーがアタッチされた codespace で実行されている拡張機能が含まれます。
カスタム開発コンテナでのデバッグ
次の手順に従ってください。
-
ローカルで開発コンテナを使用するには、Dev Containers 拡張機能をインストールして構成 し、ファイル > 開く... / フォルダーを開く... を使用して、VS Code でローカルにソースコードを開きます。代わりに Codespaces を使用するには、GitHub 上の拡張機能を含むリポジトリに移動し、codespace で開いて、ブラウザベースのエディターで作業します。必要に応じて、VS Code で codespace を開く こともできます。
-
コマンドパレット (F1) から Dev Containers: 開発コンテナ構成ファイルを追加... または Codespaces: 開発コンテナ構成ファイルを追加... を選択し、必要なコンテナ構成ファイルを追加するために Node.js & TypeScript (または TypeScript を使用していない場合は Node.js) を選択します。
-
オプション: このコマンドの実行後、
.devcontainer
フォルダーの内容を変更して、追加のビルドまたはランタイム要件を含めることができます。詳細については、詳細な 開発コンテナの作成 ドキュメントを参照してください。 -
Dev Containers: コンテナで再度開く または Codespaces: 開発コンテナ構成ファイルを追加... を実行すると、すぐに VS Code がコンテナをセットアップして接続します。これで、ローカルの場合と同じように、コンテナ内からソースコードを開発できるようになります。
-
新しい VS Code ターミナルウィンドウで (⌃⇧` (Windows, Linux Ctrl+Shift+`))
yarn install
またはnpm install
を実行して、Linux バージョンの Node.js ネイティブ依存関係がインストールされていることを確認します。他の OS またはランタイム依存関係をインストールすることもできますが、コンテナを再構築する場合にも利用できるように、これらを.devcontainer/Dockerfile
に追加することをお勧めします。 -
最後に、F5 を押すか、実行とデバッグ ビューを使用して、この同じコンテナ内で拡張機能を起動し、デバッガーをアタッチします。
注: 表示されるウィンドウで拡張機能のソースコードフォルダーを開くことはできませんが、コンテナ内のサブフォルダーまたは他の場所を開くことはできます。
表示される拡張機能開発ホストウィンドウには、ステップ 2 で定義したコンテナで実行されている拡張機能と、アタッチされたデバッガーが含まれます。
SSH を使用したデバッグ
次の手順に従います。
-
Remote - SSH 拡張機能をインストールして構成 した後、VS Code のコマンドパレット (F1) から Remote-SSH: ホストに接続... を選択してホストに接続します。
-
接続したら、ファイル > 開く... / フォルダーを開く... を使用して拡張機能のソースコードを含むリモートフォルダーを選択するか、コマンドパレット (F1) から Git: クローン を選択してクローンし、リモートホストで開きます。
-
新しい VS Code ターミナルウィンドウで (たとえば、
yarn install
またはapt-get
を使用して) 不足している可能性のある必要な依存関係をインストールします (⌃⇧` (Windows, Linux Ctrl+Shift+`))。 -
最後に、F5 を押すか、実行とデバッグ ビューを使用して、リモートホスト上で拡張機能を起動し、デバッガーをアタッチします。
注: 表示されるウィンドウで拡張機能のソースコードフォルダーを開くことはできませんが、SSH ホスト上のサブフォルダーまたは他の場所を開くことはできます。
表示される拡張機能開発ホストウィンドウには、SSH ホスト上で実行されている拡張機能と、アタッチされたデバッガーが含まれます。
WSL を使用したデバッグ
次の手順に従ってください。
-
WSL 拡張機能をインストールして構成 した後、VS Code のコマンドパレット (F1) から WSL: 新しいウィンドウ を選択します。
-
表示される新しいウィンドウで、ファイル > 開く... / フォルダーを開く... を使用して拡張機能のソースコードを含むリモートフォルダーを選択するか、コマンドパレット (F1) から Git: クローン を選択してクローンし、WSL で開きます。
ヒント:
/mnt/c
フォルダーを選択すると、Windows 側にあるクローンされたソースコードにアクセスできます。 -
新しい VS Code ターミナルウィンドウで (たとえば、
apt-get
を使用して) 不足している可能性のある必要な依存関係をインストールします (⌃⇧` (Windows, Linux Ctrl+Shift+`))。少なくとも、yarn install
またはnpm install
を実行して、Linux バージョンのネイティブ Node.js 依存関係が利用可能であることを確認する必要があります。 -
最後に、F5 を押すか、実行とデバッグ ビューを使用して、ローカルの場合と同様に拡張機能を起動し、デバッガーをアタッチします。
注: 表示されるウィンドウで拡張機能のソースコードフォルダーを開くことはできませんが、WSL 内のサブフォルダーまたは他の場所を開くことはできます。
表示される拡張機能開発ホストウィンドウには、WSL で実行されている拡張機能と、アタッチされたデバッガーが含まれます。
拡張機能の開発バージョンのインストール
VS Code が SSH ホスト、コンテナ内、WSL 内、または GitHub Codespaces 経由で拡張機能を自動的にインストールする場合、Marketplace バージョンが使用されます (ローカルマシンに既にインストールされているバージョンではありません)。
これはほとんどの場合理にかなっていますが、デバッグ環境をセットアップせずに、テスト用に未公開バージョンの拡張機能を使用 (または共有) したい場合があります。未公開バージョンの拡張機能をインストールするには、拡張機能を VSIX
としてパッケージ化し、実行中のリモート環境に既に接続されている VS Code ウィンドウに手動でインストールできます。
次の手順に従ってください。
- これが公開されている拡張機能である場合は、
settings.json
に"extensions.autoUpdate": false
を追加して、最新の Marketplace バージョンへの自動更新を防ぐことができます。 - 次に、
vsce package
を使用して拡張機能を VSIX としてパッケージ化します。 - codespace、Dev Containers、SSH ホスト、または WSL 環境 に接続します。
- 拡張機能ビューの その他のアクション (
...
) メニューで使用できる VSIX からインストール... コマンドを使用して、この特定のウィンドウ (ローカルウィンドウではない) に拡張機能をインストールします。 - プロンプトが表示されたらリロードします。
ヒント: インストール後、開発者: 実行中の拡張機能を表示 コマンドを使用して、VS Code が拡張機能をローカルで実行しているかリモートで実行しているかを確認できます。
リモート拡張機能での依存関係の処理
拡張機能は、API について他の拡張機能に依存関係を持つことができます。例:
- 拡張機能は、
activate
関数から API をエクスポートできます。 - この API は、同じ拡張機能ホストで実行されているすべての拡張機能で使用できるようになります。
- コンシューマー拡張機能は、
package.json
で、extensionDependencies
プロパティを使用して、提供拡張機能に依存していることを宣言します。
すべての拡張機能がローカルで実行され、同じ拡張機能ホストを共有している場合、拡張機能の依存関係は正常に機能します。
リモートシナリオを扱う場合、リモートで実行されている拡張機能がローカルで実行されている拡張機能に拡張機能の依存関係を持っている可能性があります。たとえば、ローカル拡張機能は、リモート拡張機能の機能に不可欠なコマンドを公開します。この場合、リモート拡張機能はローカル拡張機能を extensionDependency
として宣言することをお勧めしますが、問題は、拡張機能が 2 つの異なる拡張機能ホストで実行されることです。つまり、プロバイダーからの API はコンシューマーでは利用できません。したがって、提供拡張機能は、拡張機能の package.json
で "api": "none"
を使用して、API をエクスポートする機能を完全に放棄する必要があります。拡張機能は、VS Code コマンド (非同期) を使用して通信できます。
これは提供拡張機能に対する不必要に厳格な制約のように思えるかもしれませんが、"api": "none"
を使用する拡張機能は、activate
メソッドから API を返す機能を放棄するだけです。他の拡張機能ホストで実行されるコンシューマー拡張機能は、引き続きそれらに依存関係を持つことができ、アクティブ化されます。
一般的な問題
VS Code の API は、拡張機能がどこにあるかに関係なく、適切な場所で自動的に実行されるように設計されています。これを念頭に置いて、予期しない動作を回避するのに役立つ API がいくつかあります。
不正な実行場所
拡張機能が期待どおりに機能しない場合は、間違った場所で実行されている可能性があります。最も一般的なのは、UI 拡張機能がワークスペース拡張機能として誤って扱われている場合、またはその逆の場合です。コマンドパレット (F1) から 開発者: 実行中の拡張機能を表示 コマンドを使用して、拡張機能が実行されている場所を確認できます。
開発者: 実行中の拡張機能を表示 コマンドで、UI 拡張機能がワークスペース拡張機能として誤って扱われているか、またはその逆になっていることが示されている場合は、package.json の extensionKind
プロパティを 拡張機能の種類セクション で説明されているように設定してみてください。
Azure Databases 拡張機能を UI 拡張機能 (ワークスペースのデフォルトではなく) に強制し、Remote - SSH: 構成ファイルの編集 拡張機能をワークスペース拡張機能 (UI のデフォルトではなく) に強制する場合は、remote.extensionKind
設定 で拡張機能の種類を変更する効果をすばやく テスト できます。この設定は、拡張機能 ID から拡張機能の種類へのマップです。たとえば、次のように設定します。
{
"remote.extensionKind": {
"ms-azuretools.vscode-cosmosdb": ["ui"],
"ms-vscode-remote.remote-ssh-edit": ["workspace"]
}
}
remote.extensionKind
を使用すると、公開されているバージョンの拡張機能を、package.json
を変更して再構築することなく、すばやくテストできます。
拡張機能のデータまたは状態の永続化
場合によっては、拡張機能で settings.json
や個別のワークスペース構成ファイル (たとえば .eslintrc
) に属さない状態情報を永続化する必要がある場合があります。この問題を解決するために、VS Code はアクティベーション中に拡張機能に渡される vscode.ExtensionContext
オブジェクトに役立つストレージプロパティのセットを提供します。拡張機能が既にこれらのプロパティを利用している場合、実行場所に関係なく機能し続けるはずです。
ただし、拡張機能が現在の VS Code パス規則 (たとえば ~/.vscode
) や特定の OS フォルダー (たとえば Linux 上の ~/.config/Code
) の存在に依存してデータを永続化している場合は、問題が発生する可能性があります。幸いなことに、拡張機能を更新してこれらの課題を回避することは簡単です。
単純なキーと値のペアを永続化する場合は、vscode.ExtensionContext.workspaceState
または vscode.ExtensionContext.globalState
をそれぞれ使用して、ワークスペース固有またはグローバルな状態情報を保存できます。データがキーと値のペアよりも複雑な場合は、globalStorageUri
および storageUri
プロパティは、ファイル内のグローバルなワークスペース固有の情報を読み書きするために使用できる「安全な」URI を提供します。
API を使用するには:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.persistWorkspaceData', async () => {
if (!context.storageUri) {
return;
}
// Create the extension's workspace storage folder if it doesn't already exist
try {
// When folder doesn't exist, and error gets thrown
await vscode.workspace.fs.stat(context.storageUri);
} catch {
// Create the extension's workspace storage folder
await vscode.workspace.fs.createDirectory(context.storageUri)
}
const workspaceData = vscode.Uri.joinPath(context.storageUri, 'workspace-data.json');
const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
vscode.workspace.fs.writeFile(workspaceData, writeData);
}
));
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.persistGlobalData', async () => {
if (!context.globalStorageUri) {
return;
}
// Create the extension's global (cross-workspace) folder if it doesn't already exist
try {
// When folder doesn't exist, and error gets thrown
await vscode.workspace.fs.stat(context.globalStorageUri);
} catch {
await vscode.workspace.fs.createDirectory(context.globalStorageUri)
}
const workspaceData = vscode.Uri.joinPath(context.globalStorageUri, 'global-data.json');
const writeData = new TextEncoder().encode(JSON.stringify({ now: Date.now() }));
vscode.workspace.fs.writeFile(workspaceData, writeData);
));
}
マシン間でユーザーのグローバル状態を同期する
拡張機能が異なるマシン間で一部のユーザー状態を保持する必要がある場合は、vscode.ExtensionContext.globalState.setKeysForSync
を使用して 設定同期 に状態を提供します。これは、複数のマシンで同じウェルカムページまたは更新ページをユーザーに表示しないようにするのに役立ちます。
拡張機能の機能 トピックには、setKeysforSync
の使用例があります。
シークレットの永続化
拡張機能でパスワードやその他のシークレットを永続化する必要がある場合は、Visual Studio Code の SecretStorage API を使用することをお勧めします。これは、暗号化によって保護されたファイルシステムにテキストを安全に保存する方法を提供します。たとえば、デスクトップでは、Electron の safeStorage API を使用して、シークレットをファイルシステムに保存する前に暗号化します。API は常にシークレットをクライアント側に保存しますが、拡張機能がどこで実行されているかに関係なくこの API を使用して、同じシークレット値を取得できます。
注: この API は、パスワードとシークレットを永続化するための推奨される方法です。
vscode.ExtensionContext.workspaceState
またはvscode.ExtensionContext.globalState
を使用してシークレットを保存するべきではありません。これらの API はデータをプレーンテキストで保存するためです。
例を次に示します。
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// ...
const myApiKey = context.secrets.get('apiKey');
// ...
context.secrets.delete('apiKey');
// ...
context.secrets.store('apiKey', myApiKey);
}
クリップボードの使用
歴史的に、拡張機能の作成者は、clipboardy
などの Node.js モジュールを使用してクリップボードを操作してきました。残念ながら、これらのモジュールをワークスペース拡張機能で使用すると、ユーザーのローカルクリップボードではなく、リモートクリップボードが使用されます。VS Code クリップボード API は、この問題を解決します。これは、呼び出す拡張機能の種類に関係なく、常にローカルで実行されます。
拡張機能で VS Code クリップボード API を使用するには:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.clipboardIt', async () => {
// Read from clipboard
const text = await vscode.env.clipboard.readText();
// Write to clipboard
await vscode.env.clipboard.writeText(
`It looks like you're copying "${text}". Would you like help?`
);
})
);
}
ローカルブラウザまたはアプリケーションで何かを開く
プロセスを生成したり、opn
のようなモジュールを使用して特定の URI のブラウザまたはその他のアプリケーションを起動したりすることは、ローカルシナリオではうまく機能しますが、ワークスペース拡張機能はリモートで実行されるため、アプリケーションが間違った側で起動する可能性があります。VS Code リモート開発は、既存の拡張機能が機能するように、opn
node モジュールを 部分的に シムします。URI を指定してモジュールを呼び出すと、VS Code はクライアント側で URI のデフォルトアプリケーションを表示します。ただし、オプションはサポートされておらず、child_process
オブジェクトが返されないため、これは完全な実装ではありません。
サードパーティの node モジュールに依存する代わりに、拡張機能は vscode.env.openExternal
メソッドを利用して、指定された URI のローカルオペレーティングシステムでデフォルトで登録されたアプリケーションを起動することをお勧めします。さらに良いことに、vscode.env.openExternal
は 自動 localhost ポートフォワーディングを実行します! これを使用して、リモートマシンまたは codespace 上のローカル Web サーバーをポイントし、そのポートが外部的にブロックされている場合でもコンテンツを提供できます。
注: 現在、Codespaces ブラウザベースのエディターの転送メカニズムは、http および https リクエスト のみをサポートしています。ただし、VS Code から codespace に接続する場合は、任意の TCP 接続を操作できます。
vscode.env.openExternal
API を使用するには:
import * as vscode from 'vscode';
export async function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('myAmazingExtension.openExternal', () => {
// Example 1 - Open the VS Code homepage in the default browser.
vscode.env.openExternal(vscode.Uri.parse('https://vscode.dokyumento.jp'));
// Example 2 - Open an auto-forwarded localhost HTTP server.
vscode.env.openExternal(vscode.Uri.parse('http://localhost:3000'));
// Example 3 - Open the default email application.
vscode.env.openExternal(vscode.Uri.parse('mailto:<fill in your email here>'));
})
);
}
localhost のフォワーディング
vscode.env.openExternal
の localhost フォワーディングメカニズムは便利ですが、新しいブラウザウィンドウまたはアプリケーションを実際に起動せずに何かをフォワーディングしたい状況もあるかもしれません。これが、vscode.env.asExternalUri
API が役立つ場所です。
注: 現在、Codespaces ブラウザベースのエディターの転送メカニズムは、http および https リクエスト のみをサポートしています。ただし、VS Code から codespace に接続する場合は、任意の TCP 接続を操作できます。
vscode.env.asExternalUri
API を使用するには:
import * as vscode from 'vscode';
import { getExpressServerPort } from './server';
export async function activate(context: vscode.ExtensionContext) {
const dynamicServerPort = await getWebServerPort();
context.subscriptions.push(vscode.commands.registerCommand('myAmazingExtension.forwardLocalhost', async () =>
// Make the port available locally and get the full URI
const fullUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`http://localhost:${dynamicServerPort}`));
// ... do something with the fullUri ...
}));
}
API によって返される URI は localhost をまったく参照していない可能性がある ことに注意することが重要です。したがって、URI を全体として使用する必要があります。これは、localhost を使用できない Codespaces ブラウザベースのエディターでは特に重要です。
コールバックと URI ハンドラー
vscode.window.registerUriHandler
API を使用すると、拡張機能はカスタム URI を登録できます。ブラウザで開くと、拡張機能でコールバック関数が起動します。URI ハンドラーを登録する一般的なユースケースは、OAuth 2.0 認証プロバイダー (たとえば、Azure AD) を使用したサービスサインインを実装する場合です。ただし、外部アプリケーションまたはブラウザが拡張機能に情報を送信する場合など、あらゆるシナリオで使用できます。
VS Code のリモート開発および Codespaces 拡張機能は、URI が実際に実行されている場所 (ローカルまたはリモート) に関係なく、URI の拡張機能への受け渡しを透過的に処理します。ただし、Codespaces ブラウザベースのエディターでは、vscode://
URI は機能しません。これらの URI をブラウザのようなもので開くと、ブラウザベースのエディターではなく、ローカルの VS Code クライアントに URI を渡そうとするためです。幸いなことに、これは vscode.env.asExternalUri
API を使用することで簡単に修正できます。
vscode.window.registerUriHandler
と vscode.env.asExternalUri
を組み合わせて、OAuth 認証コールバックの例を配線してみましょう。
import * as vscode from 'vscode';
// This is ${publisher}.${name} from package.json
const extensionId = 'my.amazing-extension';
export async function activate(context: vscode.ExtensionContext) {
// Register a URI handler for the authentication callback
vscode.window.registerUriHandler({
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
// Add your code for what to do when the authentication completes here.
if (uri.path === '/auth-complete') {
vscode.window.showInformationMessage('Sign in successful!');
}
}
});
// Register a sign in command
context.subscriptions.push(
vscode.commands.registerCommand(`${extensionId}.signin`, async () => {
// Get an externally addressable callback URI for the handler that the authentication provider can use
const callbackUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`${vscode.env.uriScheme}://${extensionId}/auth-complete`)
);
// Add your code to integrate with an authentication provider here - we'll fake it.
vscode.env.clipboard.writeText(callbackUri.toString());
await vscode.window.showInformationMessage(
'Open the URI copied to the clipboard in a browser window to authorize.'
);
})
);
}
このサンプルを VS Code で実行すると、認証プロバイダーのコールバックとして使用できる vscode://
または vscode-insiders://
URI が配線されます。Codespaces ブラウザベースのエディターで実行すると、コード変更や特別な条件なしで https://*.github.dev
URI が配線されます。
OAuth はこのドキュメントの範囲外ですが、このサンプルを実際の認証プロバイダーに適合させた場合、プロバイダーの前にプロキシサービスを構築する必要がある場合があることに注意してください。これは、すべてのプロバイダーが vscode://
コールバック URI を許可しているわけではなく、HTTPS 経由のコールバックに対してワイルドカードホスト名を許可していないプロバイダーもあるためです。また、コールバックのセキュリティを向上させるために、可能な限り OAuth 2.0 Authorization Code with PKCE フロー (たとえば、Azure AD は PKCE をサポートしています) を使用することをお勧めします。
リモートまたは Codespaces ブラウザエディターで実行する場合の動作のばらつき
場合によっては、ワークスペース拡張機能でリモートで実行する場合の動作を変更する必要がある場合があります。また、Codespaces ブラウザベースのエディターで実行する場合の動作を変更したい場合もあります。VS Code は、これらの状況を検出するための 3 つの API を提供します: vscode.env.uiKind
、extension.extensionKind
、および vscode.env.remoteName
。
次に、3 つの API を次のように使用できます。
import * as vscode from 'vscode';
export async function activate(context: vscode.ExtensionContext) {
// extensionKind returns ExtensionKind.UI when running locally, so use this to detect remote
const extension = vscode.extensions.getExtension('your.extensionId');
if (extension.extensionKind === vscode.ExtensionKind.Workspace) {
vscode.window.showInformationMessage('I am running remotely!');
}
// Codespaces browser-based editor will return UIKind.Web for uiKind
if (vscode.env.uiKind === vscode.UIKind.Web) {
vscode.window.showInformationMessage('I am running in the Codespaces browser editor!');
}
// VS Code will return undefined for remoteName if working with a local workspace
if (typeof vscode.env.remoteName === 'undefined') {
vscode.window.showInformationMessage('Not currently connected to a remote workspace.');
}
}
コマンドを使用した拡張機能間の通信
一部の拡張機能は、他の拡張機能で使用することを目的とした API をアクティベーションの一部として返します (vscode.extension.getExtension(extensionName).exports
経由)。これらは、関係するすべての拡張機能が同じ側 (すべて UI 拡張機能またはすべてワークスペース拡張機能) にある場合は機能しますが、UI 拡張機能とワークスペース拡張機能の間では機能しません。
幸いなことに、VS Code は、実行されたコマンドを場所に関係なく、正しい拡張機能に自動的にルーティングします。影響を心配することなく、(他の拡張機能によって提供されるものを含む) 任意のコマンドを自由に呼び出すことができます。
相互に対話する必要がある拡張機能のセットがある場合、プライベートコマンドを使用して機能を公開すると、予期しない影響を回避するのに役立ちます。ただし、パラメーターとして渡すオブジェクトは送信前に「文字列化」(JSON.stringify
) されるため、オブジェクトに循環参照を含めることはできず、反対側では「プレーンな古い JavaScript オブジェクト」として終わります。
例:
import * as vscode from 'vscode';
export async function activate(context: vscode.ExtensionContext) {
// Register the private echo command
const echoCommand = vscode.commands.registerCommand(
'_private.command.called.echo',
(value: string) => {
return value;
}
);
context.subscriptions.push(echoCommand);
}
コマンドの操作の詳細については、コマンド API ガイド を参照してください。
Webview API の使用
クリップボード API と同様に、Webview API は、ワークスペース拡張機能から使用する場合でも、常にユーザーのローカルマシンまたはブラウザで実行されます。これは、多くの webview ベースの拡張機能が、リモートワークスペースまたは Codespaces で使用する場合でも、そのまま動作するはずであることを意味します。ただし、リモートで実行した場合に webview 拡張機能が適切に動作するように、注意すべき点がいくつかあります。
常に asWebviewUri を使用する
拡張機能リソースを管理するには、asWebviewUri
API を使用する必要があります。vscode-resource://
URI をハードコーディングする代わりにこの API を使用することは、Codespaces ブラウザベースのエディターが拡張機能で動作するようにするために必要です。詳細については、Webview API ガイドを参照してください。簡単な例を次に示します。
コンテンツで API を次のように使用できます。
// Create the webview
const panel = vscode.window.createWebviewPanel(
'catWebview',
'Cat Webview',
vscode.ViewColumn.One
);
// Get the content Uri
const catGifUri = panel.webview.asWebviewUri(
vscode.Uri.joinPath(context.extensionUri, 'media', 'cat.gif')
);
// Reference it in your content
panel.webview.html = `<!DOCTYPE html>
<html>
<body>
<img src="${catGifUri}" width="300" />
</body>
</html>`;
動的な webview コンテンツにはメッセージパッシング API を使用する
VS Code webview には、ローカル Web サーバーを使用せずに webview コンテンツを動的に更新できる メッセージパッシング API が含まれています。拡張機能が webview コンテンツを更新するために対話したいローカル Web サービスを実行している場合でも、HTML コンテンツから直接ではなく、拡張機能自体からこれを行うことができます。
これは、リモート開発と GitHub Codespaces にとって重要なパターンであり、webview コードが VS Code と Codespaces ブラウザベースのエディターの両方で動作するようにします。
localhost Web サーバーの代わりにメッセージパッシングを使用する理由
代替パターンは、iframe
で Web コンテンツを提供するか、webview コンテンツに localhost サーバーと直接対話させることです。残念ながら、デフォルトでは、webview 内の localhost
は開発者のローカルマシンに解決されます。これは、リモートで実行されているワークスペース拡張機能の場合、作成する webview が拡張機能によって生成されたローカルサーバーにアクセスできないことを意味します。マシンの IP を使用した場合でも、接続するポートは通常、クラウド VM またはコンテナでデフォルトでブロックされます。これが VS Code で動作した場合でも、Codespaces ブラウザベースのエディターでは動作しません。
Remote - SSH 拡張機能を使用する場合の問題の図を次に示しますが、Dev Containers と GitHub Codespaces にも問題が存在します。
可能であれば、これを避ける必要があります。拡張機能が大幅に複雑になるためです。メッセージパッシング API を使用すると、これらの種類の頭痛の種なしで、同じタイプのユーザーエクスペリエンスを実現できます。拡張機能自体はリモート側の VS Code サーバーで実行されるため、拡張機能が webview から渡されたメッセージの結果として起動する Web サーバーと透過的に対話できます。
webview から localhost を使用するための回避策
何らかの理由で メッセージパッシング API を使用できない場合は、VS Code のリモート開発および GitHub Codespaces 拡張機能で動作する 2 つのオプションがあります。
各オプションを使用すると、webview コンテンツは VS Code が VS Code サーバーと通信するために使用するのと同じチャネルを介してルーティングできます。たとえば、Remote - SSH の前のセクションの図を更新すると、次のようになります。
オプション 1 - asExternalUri を使用する
VS Code 1.40 では、拡張機能がローカルの http
および https
リクエストをプログラムでリモート転送できるようにする vscode.env.asExternalUri
API が導入されました。この同じ API を使用して、拡張機能が VS Code で実行されている場合に、webview から localhost
Web サーバーへのリクエストを転送できます。
API を使用して iframe の完全な URI を取得し、HTML に追加します。また、webview でスクリプトを有効にし、CSP を HTML コンテンツに追加する必要があります。
// Use asExternalUri to get the URI for the web server
const dynamicWebServerPort = await getWebServerPort();
const fullWebServerUri = await vscode.env.asExternalUri(
vscode.Uri.parse(`http://localhost:${dynamicWebServerPort}`)
);
// Create the webview
const panel = vscode.window.createWebviewPanel(
'asExternalUriWebview',
'asExternalUri Example',
vscode.ViewColumn.One,
{
enableScripts: true
}
);
const cspSource = panel.webview.cspSource;
panel.webview.html = `<!DOCTYPE html>
<head>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; frame-src ${fullWebServerUri} ${cspSource} https:; img-src ${cspSource} https:; script-src ${cspSource}; style-src ${cspSource};"
/>
</head>
<body>
<!-- All content from the web server must be in an iframe -->
<iframe src="${fullWebServerUri}">
</body>
</html>`;
上記の例の iframe
で提供される HTML コンテンツは、localhost
をハードコーディングするのではなく、相対パスを使用する必要がある ことに注意してください。
オプション 2 - ポートマッピングを使用する
Codespaces のブラウザーベースのエディターをサポートしない場合は、webview API で利用可能な portMapping
オプションを使用できます。(この方法は、VS Code クライアントからの Codespaces でも機能しますが、ブラウザーでは機能しません)。
ポートマッピングを使用するには、webview を作成する際に portMapping
オブジェクトを渡します。
const LOCAL_STATIC_PORT = 3000;
const dynamicServerPort = await getWebServerPort();
// Create webview and pass portMapping in
const panel = vscode.window.createWebviewPanel(
'remoteMappingExample',
'Remote Mapping Example',
vscode.ViewColumn.One,
{
portMapping: [
// This maps localhost:3000 in the webview to the web server port on the remote host.
{ webviewPort: LOCAL_STATIC_PORT, extensionHostPort: dynamicServerPort }
]
}
);
// Reference the port in any full URIs you reference in your HTML.
panel.webview.html = `<!DOCTYPE html>
<body>
<!-- This will resolve to the dynamic server port on the remote machine -->
<img src="http://localhost:${LOCAL_STATIC_PORT}/canvas.png">
</body>
</html>`;
この例では、リモートとローカルの両方の場合において、http://localhost:3000
へのリクエストはすべて、Express.js Web サーバーが実行されている動的ポートに自動的にマッピングされます。
ネイティブ Node.js モジュールの使用
VS Code 拡張機能にバンドルされている(または動的に取得される)ネイティブモジュールは、Electron の electron-rebuild
を使用して再コンパイルする必要があります。ただし、VS Code Server は標準(非 Electron)バージョンの Node.js を実行するため、リモートで使用するとバイナリが失敗する可能性があります。
この問題を解決するには
- VS Code に同梱されている Node.js の「modules」バージョン用に、バイナリ(Electron と標準 Node.js の両方)の両方のセットを含める(または動的に取得する)ようにしてください。
- 拡張機能がリモートで実行されているかローカルで実行されているかに基づいて正しいバイナリをセットアップするために、
vscode.extensions.getExtension('your.extensionId').extensionKind === vscode.ExtensionKind.Workspace
であるかどうかを確認してください。 - 同様のロジックに従う ことで、非 x86_64 ターゲットと Alpine Linux のサポートを同時に追加することもできます。
VS Code が使用する「modules」バージョンは、**ヘルプ > 開発者ツール** に移動し、コンソールで process.versions.modules
と入力することで確認できます。ただし、ネイティブモジュールがさまざまな Node.js 環境でシームレスに動作するようにするには、サポートしたいすべての可能な Node.js「modules」バージョンとプラットフォーム(Electron Node.js、公式 Node.js Windows/Darwin/Linux、すべてのバージョン)に対してネイティブモジュールをコンパイルすることをお勧めします。node-tree-sitter モジュールは、これをうまく実行しているモジュールの良い例です。
非 x86_64 ホストまたは Alpine Linux コンテナのサポート
拡張機能が純粋に JavaScript/TypeScript で記述されている場合、他のプロセッサアーキテクチャまたは musl
ベースの Alpine Linux のサポートを拡張機能に追加するために何もする必要はないかもしれません。
ただし、拡張機能が Debian 9 以降、Ubuntu 16.04 以降、または RHEL / CentOS 7 以降のリモート SSH ホスト、コンテナー、または WSL で動作するが、サポートされている非 x86_64 ホスト(ARMv7l など)または Alpine Linux コンテナーで失敗する場合、拡張機能には、これらのアーキテクチャ/オペレーティングシステムで失敗する x86_64 glibc
固有のネイティブコードまたはランタイムが含まれている可能性があります。
たとえば、拡張機能には、ネイティブモジュールまたはランタイムの x86_64 コンパイルバージョンのみが含まれている場合があります。Alpine Linux の場合、含まれているネイティブコードまたはランタイムは、Alpine Linux(musl
)および他のディストリビューション(glibc
)で libc
がどのように実装されているかの根本的な違いのために動作しない可能性があります。
この問題を解決するには
-
コンパイル済みコードを動的に取得している場合は、
process.arch
を使用して非 x86_64 ターゲットを検出し、適切なアーキテクチャ用にコンパイルされたバージョンをダウンロードすることで、サポートを追加できます。代わりに、拡張機能内にサポートされているすべてのアーキテクチャ用のバイナリを含める場合は、このロジックを使用して正しいものを利用できます。 -
Alpine Linux の場合、
await fs.exists('/etc/alpine-release')
を使用してオペレーティングシステムを検出し、再度musl
ベースのオペレーティングシステム用の正しいバイナリをダウンロードまたは使用できます。 -
これらのプラットフォームをサポートしない場合は、代わりに同じロジックを使用して適切なエラーメッセージを提供できます。
一部のサードパーティ npm モジュールには、この問題を引き起こす可能性のあるネイティブコードが含まれていることに注意することが重要です。そのため、場合によっては、追加のコンパイルターゲットを追加するために npm モジュールの作成者と協力する必要がある場合があります。
Electron モジュールの使用を避ける
拡張機能 API によって公開されていない組み込みの Electron または VS Code モジュールに依存することは便利な場合がありますが、VS Code Server は標準(非 Electron)バージョンの Node.js を実行することに注意することが重要です。これらのモジュールは、リモートで実行すると見つからなくなります。それらを動作させるための特定のコードが配置されている例外がいくつかあります。
これらの問題を回避するには、ベースの Node.js モジュールまたは拡張機能 VSIX のモジュールを使用してください。どうしても Electron モジュールを使用する必要がある場合は、モジュールが見つからない場合のフォールバックを必ず用意してください。
以下の例では、Electron original-fs
node モジュールが見つかった場合はそれを使用し、そうでない場合はベースの Node.js fs
モジュールにフォールバックします。
function requireWithFallback(electronModule: string, nodeModule: string) {
try {
return require(electronModule);
} catch (err) {}
return require(nodeModule);
}
const fs = requireWithFallback('original-fs', 'fs');
可能な限り、これらの状況を避けるようにしてください。
既知の問題
ワークスペース拡張機能に追加機能が追加されることで解決できる拡張機能の問題がいくつかあります。次の表は、検討中の既知の問題のリストです。
問題 | 説明 |
---|---|
ワークスペース拡張機能から接続されたデバイスにアクセスできない | ローカルに接続されたデバイスにアクセスする拡張機能は、リモートで実行されている場合はそれらに接続できません。これを克服する 1 つのアプローチは、接続されたデバイスにアクセスし、リモート拡張機能も呼び出すことができるコマンドを提供するコンパニオン UI 拡張機能を作成することです。 もう 1 つのアプローチはリバーストンネリングであり、これはVS Code リポジトリの issue で追跡されています。 |
質問とフィードバック
- ヒントとコツ または FAQ を参照してください。
- Stack Overflow で回答を検索してください。
- 機能を投票するか、新しい機能をリクエスト し、既存の issue を検索するか、問題を報告 してください。
- 他のユーザーが使用できるように、開発コンテナテンプレート または 機能 を作成してください。
- ドキュメント または VS Code に貢献してください。
- 詳細については、CONTRIBUTING ガイドを参照してください。