VS Codeのアーキテクチャ:5,600ファイルのTypeScriptコードベースを読み解く
前提知識
- ›TypeScript の基礎知識(ジェネリクス、モジュール)
- ›VS Code をユーザーとして使ったことがある
VS Codeのアーキテクチャ:5,600ファイルのTypeScriptコードベースを読み解く
VS Code は現存する最大規模のオープンソース TypeScript プロジェクトのひとつです。約5,700のソースファイル、数百のサービス、そしてデスクトップ(Electron)・Webブラウザ・リモートのヘッドレス環境という3つの実行環境を、すべて単一のコードベースで支えています。これだけの規模があれば、コードナビゲーションは悪夢になりかねません。しかし実際にはそうなっていません。その理由は、慣例ベースの厳格なアーキテクチャが徹底されており、ファイルパスを見ただけでそのファイルの役割が推測できるからです。この記事では、そのメンタルモデルを解説します。
リポジトリの全体像:5,700ファイルには何が入っているのか
src/ の詳細へ入る前に、トップレベルのディレクトリ構成を把握しておきましょう。リポジトリはいくつかの主要ディレクトリから成り、それぞれ明確な役割を持っています。
| ディレクトリ | 役割 |
|---|---|
src/ |
すべてのアプリケーションソースコード — エディター、ワークベンチ、プラットフォームサービス、エントリーポイント |
extensions/ |
VS Code に同梱されるビルトイン拡張機能(TypeScript、Git、Markdown、テーマなど) |
build/ |
ビルドツール — Gulpタスク、レイヤーチェッカー、コード品質スクリプト、パッケージング |
cli/ |
Rust製の VS Code CLI(code tunnel、リモートサーバー管理) |
test/ |
インテグレーションテストとスモークテスト(ユニットテストは src/ 内のソースと同じ場所に置かれる) |
resources/ |
プラットフォーム固有のリソース — アイコン、シェルスクリプト、.desktop ファイル |
私たちが主に関心を持つコードのほぼすべては src/vs/ 以下にあります。エディター、ワークベンチ、すべてのプラットフォームサービス、そしてそれらをつなぐグルーコードがここに集約されています。src/vs/ の外にあるのは、インフラかバンドル済みの拡張機能です。
Tips: VS Code の特定の機能がどのように動いているか調べるなら、まず
src/vs/workbench/contrib/を起点にしましょう。ターミナル、SCM、デバッガー、チャットなど、ユーザーが目にするほぼすべての機能が、自己完結したコントリビューションとしてここに収められています。
四つの柱:base、platform、editor、workbench
src/vs/ ディレクトリは4つの主要レイヤーで構成されており、下のレイヤーの上に上のレイヤーが積み重なる構造になっています。
graph BT
A["<b>base</b><br/>Generic utilities, data structures,<br/>UI primitives"] --> B["<b>platform</b><br/>Service interfaces, DI, configuration,<br/>files, logging, storage"]
B --> C["<b>editor</b><br/>Monaco editor — text model,<br/>view, contributions"]
C --> D["<b>workbench</b><br/>Full IDE shell — layout, parts,<br/>extensions, contrib features"]
style A fill:#e8f5e9
style B fill:#e3f2fd
style C fill:#fff3e0
style D fill:#fce4ec
依存関係のルールは厳格で、常に一方向です。
-
base/は VS Code 固有の依存を一切持ちません。汎用的な TypeScript ユーティリティが揃っています。データ構造(LinkedList、Graph、TernarySearchTree)や非同期プリミティブ(Barrier、Throttler、RunOnceScheduler)が含まれます。Disposableライフサイクルパターン、Event/Emitterシステム、DOM ヘルパー、IPC 抽象化なども揃っています。独立した npm パッケージとして切り出せるほど汎用的です。 -
platform/はbase/の上に構築され、横断的なサービスインターフェースをすべて定義しています。依存性注入、設定管理、ファイルシステム、ロギング、ストレージ、テレメトリー、テーマ、拡張機能の管理などがここで定義されており、エディターとワークベンチが依存するコントラクトとなっています。 -
editor/はbase/とplatform/の上に構築されています。これが Monaco エディターです。独自のコントリビューションシステム、テキストモデル(piece table)、ビューレイヤー、40以上のビルトイン機能を備えたスタンドアロンの埋め込み可能なコードエディターです。Monaco は npm で独立して配布されており、Monaco Editor Playground でも使われています。 -
workbench/は三つすべてのレイヤーの上に構築されています。これが完全な IDE です。シェルレイアウト、サイドバー、パネル、アクティビティバー、ステータスバー、エディターグループ、そしてターミナル・デバッガー・SCM・検索・チャット/AI・拡張機能マーケットプレイスといったすべての主要機能がここに属します。Monaco を「テキストエディター」から「IDE」へと昇華させるレイヤーです。
レイヤリングシステム:common、browser、node、electron-*
各柱の内部では、さらにサブディレクトリの命名規則によって実行環境ごとにコードが分割されています。
graph LR
subgraph "Environment Layers"
COMMON["<b>common/</b><br/>Pure TypeScript<br/>No DOM, no Node"]
BROWSER["<b>browser/</b><br/>DOM APIs allowed<br/>Runs in browser or Electron renderer"]
NODE["<b>node/</b><br/>Node.js APIs allowed<br/>Server-side only"]
EM["<b>electron-main/</b><br/>Electron main process APIs"]
EB["<b>electron-browser/</b><br/>Electron renderer + Node integration"]
end
COMMON --> BROWSER
COMMON --> NODE
COMMON --> EM
COMMON --> EB
NODE --> EM
BROWSER --> EB
このルールは制約マトリクスとして整理できます。
| レイヤー | インポート可能 | インポート不可 |
|---|---|---|
common/ |
common/ のみ |
browser/、node/、electron-* |
browser/ |
common/、browser/ |
node/、electron-* |
node/ |
common/、node/ |
browser/、electron-* |
electron-main/ |
common/、node/、electron-main/ |
browser/、electron-browser/ |
electron-browser/ |
common/、browser/、electron-browser/ |
node/、electron-main/ |
この設計こそが、VS Code の Web 版(vscode.dev)を実現している核心です。common/ と browser/ のコードは Node.js や Electron への依存を持たないため、純粋なブラウザ環境でも動作します。node/ と electron-* レイヤーは、デスクトップアプリのビルド時にのみ含まれます。
Tips: デスクトップと Web の両方で動くコードを書くときは、
common/かbrowser/に置きましょう。誤って Node.js の API をインポートしてしまった場合は、レイヤーチェッカーが検出してくれます。
ビルド時の自動検証
これらの規則は単なるガイドラインではなく、自動化ツールによって強制されています。主な検証機構は build/checker/layersChecker.ts で、ファイルパスに対するグロブベースのルールを定義し、使用を禁止する型を指定しています。
flowchart TD
A["layersChecker.ts"] --> B["Load TypeScript program<br/>from src/tsconfig.json"]
B --> C["For each source file,<br/>match against RULES"]
C --> D{Rule matched?}
D -->|Skip test files| E["Continue"]
D -->|Check rule| F["Walk AST identifiers"]
F --> G{Type in<br/>disallowedTypes?}
G -->|Yes| H["Report violation<br/>Exit code 1"]
G -->|No| E
このチェッカーは TypeScript の AST に含まれるすべての識別子を走査します。対象となるのは NativeParsedArgs、INativeEnvironmentService、INativeHostService といった型です。これらは common/ で定義されていますが、セマンティクス的にはネイティブ限定の概念を表します。こうした型が common/、browser/、worker/ のコードに現れていないかを検証するのです。
build/checker/layersChecker.ts#L26-L35
もう一つの検証機構は、レイヤーごとの TypeScript 設定ファイルです。build/checker/tsconfig.browser.json は common/ と browser/ のソースファイルのみを対象とし、"types": [] を指定することで Node.js の型定義を完全に除外しています。ブラウザレイヤーのコードから fs や child_process を参照しようとすると、カスタムチェッカーを使うまでもなく TypeScript 自身がエラーを報告します。
flowchart LR
subgraph "Layer Enforcement"
LC["layersChecker.ts<br/>(AST type scanning)"]
TC["tsconfig.browser.json<br/>(TypeScript type exclusion)"]
CI["CI Pipeline"]
end
LC --> CI
TC --> CI
CI -->|Fail on violation| BLOCK["PR Blocked"]
electron-main/ レイヤーには固有のルールもあります。ipcMain の直接使用を禁止し、より安全な validatedIpcMain ラッパーの使用を強制します。これはプラットフォームのレイヤリングだけでなく、プロジェクト固有のセキュリティ規約もチェッカーで強制できることを示す好例です。
バレルファイルと読み込まれるコード
数千のファイルと厳格なレイヤリングがある中で、VS Code はどのサービスや機能をどのプラットフォーム向けに読み込むかをどう管理しているのでしょうか。その答えがバレルファイルです。インポートのみを列挙した大きなモジュールで、マニフェストとして機能します。
重要なバレルファイルは三つあります。
-
src/vs/workbench/workbench.common.main.ts— デスクトップと Web で共有されるすべてのコードをインポートします。エディターコア、ワークベンチのアクション、API の拡張ポイント、ワークベンチのパーツ、プラットフォーム非依存のサービスが含まれます。 -
src/vs/workbench/workbench.desktop.main.ts— まずworkbench.common.main.tsをインポートし、その上に Electron 固有のサービスを追加します。ネイティブファイルダイアログ、ネイティブメニュー、デスクトップのライフサイクルサービス、ネイティブクリップボード、暗号化などが該当します。 -
src/vs/workbench/workbench.web.main.ts— こちらもworkbench.common.main.tsをインポートし、ブラウザ固有の実装を追加します。Web 検索サービス、ブラウザ用テキストファイルサービス、Web キーボードレイアウト、ブラウザのライフサイクル、Web ベースの拡張機能管理などが含まれます。
graph TD
COMMON["workbench.common.main.ts<br/><i>Editor core, shared services,<br/>all contrib features</i>"]
DESKTOP["workbench.desktop.main.ts<br/><i>Electron services:<br/>native dialogs, menus, lifecycle</i>"]
WEB["workbench.web.main.ts<br/><i>Browser services:<br/>web search, browser lifecycle</i>"]
DESKTOP --> COMMON
WEB --> COMMON
style COMMON fill:#e8f5e9
style DESKTOP fill:#e3f2fd
style WEB fill:#fff3e0
このパターンは非常にエレガントです。共通のバレルファイルが機能の同一性を保証し、各プラットフォームのバレルファイルが環境に応じた実装に差し替える構造になっています。デスクトップ版と Web 版の VS Code の違いを知りたければ、二つのプラットフォームバレルファイルを diff するだけで一目瞭然です。
ディレクトリマップ:src/vs/ を素早く把握する
重要なサブディレクトリをまとめたクイックリファレンスです。
| パス | 内容 |
|---|---|
src/vs/base/common/ |
Disposable、Event/Emitter、データ構造、非同期ユーティリティ |
src/vs/base/browser/ |
DOM ヘルパー、UI コンポーネント(grid、tree、list、sash) |
src/vs/base/parts/ipc/ |
あらゆるトランスポートに対応した IPC チャネル抽象化 |
src/vs/platform/instantiation/ |
依存性注入 — createDecorator、InstantiationService |
src/vs/platform/files/ |
ファイルシステムサービスのインターフェースとプロバイダー |
src/vs/platform/configuration/ |
設定(Settings)システム |
src/vs/editor/common/ |
テキストモデル(piece table)、言語モード、カーソルロジック |
src/vs/editor/browser/ |
エディタービュー、GPU レンダリング、ウィジェットシステム |
src/vs/editor/contrib/ |
40以上のエディター機能:検索、折り畳み、ホバー、補完候補など |
src/vs/workbench/browser/ |
ワークベンチシェル:レイアウト、パーツ、Workbench クラス |
src/vs/workbench/contrib/ |
主要 IDE 機能:ターミナル、デバッガー、SCM、チャット、検索 |
src/vs/workbench/api/ |
拡張機能ホストプロトコルと vscode.* API の実装 |
src/vs/workbench/services/ |
ワークベンチレベルのサービス:拡張機能、テーマ、ライフサイクル |
src/vs/code/electron-main/ |
Electron メインプロセス:CodeMain、CodeApplication |
src/vs/code/electron-browser/ |
Electron プリロードスクリプト |
src/vs/server/ |
リモート開発サーバー |
次のステップ
アーキテクチャの全体像が掴めたところで、次の記事では src/main.ts から最初のエディターウィンドウが表示されるまでの道筋をたどります。Electron のメインプロセスからレンダラーへ、プロセスの境界をまたぎながらブートシーケンスを追い、今回解説したレイヤリングシステムが各プロセスでどのサービスのインスタンス化を決定するかを見ていきましょう。