microsoft/TypeScript
Project system
How tsserver decides which files belong to which compilation, manages the open-file lifecycle, and serves the right project for each editor query.
Source
Almost entirely under src/server/:
| File | Lines | Role |
|---|---|---|
editorServices.ts |
5,727 | The ProjectService — the project graph manager |
project.ts |
3,227 | Project base + ConfiguredProject, InferredProject, ExternalProject, AutoImportProviderProject |
scriptInfo.ts |
700 | Per-file editor state |
scriptVersionCache.ts |
800 | Versioned text snapshots with edit-history compression |
packageJsonCache.ts |
100 | Per-server package.json cache |
moduleSpecifierCache.ts |
130 | Auto-import path memoisation |
Purpose
tsc works on one program at a time. tsserver typically owns several:
- A project per
tsconfig.jsonthe user has opened (aConfiguredProject). - A loose-files project for
.ts/.jsfiles outside any tsconfig (InferredProject). - A project per Visual Studio "external" project descriptor (
ExternalProject). - A derived auto-import provider per project (
AutoImportProviderProject) that scansnode_modulesfor completion candidates.
The project service decides which project owns each open file, when to load and unload projects, when to reload tsconfig.json, when to create the auto-import provider, and how to share state across projects.
Key abstractions
| Symbol | File | Role |
|---|---|---|
ProjectService |
editorServices.ts |
The graph manager — every command goes through it |
Project (abstract) |
project.ts |
One compilation; owns a LanguageService |
ConfiguredProject |
project.ts |
Backed by a tsconfig.json/jsconfig.json |
InferredProject |
project.ts |
Files not in any config |
ExternalProject |
project.ts |
Driven by openExternalProject request from VS-style hosts |
AutoImportProviderProject |
project.ts |
A read-only scan over a project's node_modules for auto-import candidates |
ScriptInfo |
scriptInfo.ts |
A file in the editor — open/closed, snapshot, attached projects |
ScriptVersionCache |
scriptVersionCache.ts |
Edit-history compression so version diffs are cheap |
WildcardWatcher, ChangedFilesWatcher |
editorServices.ts |
File-system watchers wired into project invalidation |
How it works
graph TD
Editor["Editor"] -->|open foo.ts| Session["Session"]
Session --> PS["ProjectService"]
PS --> Find["find tsconfig from foo.ts upward"]
Find --> Has{"tsconfig found?"}
Has -->|yes| CP["ConfiguredProject created/reused"]
Has -->|no| IP["InferredProject"]
CP --> SI["ScriptInfo attached"]
IP --> SI
SI --> LS["Project.languageService"]
LS --> Program["program.ts"]
PS -->|background| AIP["AutoImportProviderProject (per ConfiguredProject)"]When a file is opened:
ProjectService.openClientFileis invoked.- The service walks parents looking for
tsconfig.json/jsconfig.json. If found, it creates or reuses aConfiguredProject; otherwise it picks anInferredProject. - A
ScriptInfois created for the file (or fetched if already present), and attached to the project. - The project's
LanguageServicebecomes the answer for any future query about that file.
When a tsconfig file changes (or a directory it references adds files):
- The service invalidates the cached project graph.
- The next query rebuilds the project's program with
oldProgramreuse. - Affected
ScriptInfomappings update.
ScriptInfo and snapshots
ScriptInfo represents a file's editor-side state. Every open file has one. Important fields:
fileName,path(canonical),containingProjects: Project[].textStorage— aScriptVersionCacheof edit history.isOpen— was the editor told to track edits to this file? Closed files are still served from disk.
ScriptVersionCache stores compressed edit histories so the server can produce a snapshot for any version cheaply. This is what powers getQuickInfoAtPosition from a "history" cursor — tsserver can ask for the file as it was three edits ago without re-walking the entire text.
AutoImportProviderProject
When a ConfiguredProject becomes large, naively walking its node_modules for every completion is too slow. The service derives an AutoImportProviderProject — a tightly scoped program that includes only package.json-listed dependency index.d.ts entry points — and uses it to populate completion auto-imports asynchronously. The behaviour is tuned by compilerOptions.maximumHoverLength, disableAutoImports, and similar flags.
Diagnostics scheduling
tsserver debounces diagnostic computation. When the user types, the server schedules a geterr request after a short delay; if more typing happens it pushes the deadline. When the deadline fires, the server walks open files in priority order (most recently focused first), asks the project for diagnostics, and pushes events back to the editor.
Watch wiring
The project service installs file watchers for:
- Each project's source files and tsconfig.
- Each project's
package.jsonfiles. node_modules/directories that participate in resolution.- Glob patterns from
tsconfig.json'sinclude/exclude(resolved to wildcard watchers).
watchGuard (see apps/watch-guard) is consulted before turning on recursive watching on Windows.
Integration points
- Receives commands from
src/server/session.ts. - Owns the
LanguageServiceper project (see systems/language-service). - Coordinates with
typingsInstallerfor ATA (see systems/automatic-type-acquisition). - Surfaces
Projectevents through the session for editor consumption.
Entry points for modification
- Project-graph rules —
editorServices.ts, specificallyopenClientFile,closeClientFile,findOrCreateConfigFileForOpenFile,assignProjectToOpenedScriptInfo,cleanupProjectsAndScriptInfos. - Project subclasses —
project.ts. - Snapshot / version-cache changes —
scriptVersionCache.ts.
See apps/tsserver for the wrapping process and systems/language-service for what each project ultimately serves.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.