This page documents how tsserver manages project lifecycles through ProjectService, the three primary project types (ConfiguredProject, InferredProject, ExternalProject), and how individual files are tracked via ScriptInfo. It covers the file-open/close lifecycle, project structure updates, and how each project is associated with a LanguageService instance.
For the tsserver protocol and how clients communicate with these projects, see page 5.1. For Automatic Type Acquisition (ATA), which runs alongside project management to install @types packages, see page 5.3.
When an editor client connects to tsserver, it does not create language services directly. Instead, it interacts with ProjectService, which decides which project each open file belongs to, creates or tears down projects as needed, and keeps each project's compiler state up to date. A project is the unit of compilation inside tsserver: it owns a Program, a LanguageService, and a set of ScriptInfo objects representing its source files.
Project management lives in two primary files:
| File | Role |
|---|---|
src/server/editorServices.ts | ProjectService class — central coordinator |
src/server/project.ts | Project base class and all concrete project types |
src/server/scriptInfo.ts | ScriptInfo — per-file state tracked by ProjectService |
src/server/session.ts | Session — bridges protocol messages to ProjectService calls |
Five project kinds are distinguished by the ProjectKind enum in src/server/project.ts.
Project Kind Summary
| Kind | Class | Trigger |
|---|---|---|
Configured | ConfiguredProject | A tsconfig.json or jsconfig.json was found |
Inferred | InferredProject | No config file; editor opened a loose file |
External | ExternalProject | Client explicitly created the project via protocol |
AutoImportProvider | AutoImportProviderProject | Background project for auto-import from node_modules |
Auxiliary | AuxiliaryProject | Internal auxiliary use (e.g., nightly features) |
Backed by a tsconfig.json (or jsconfig.json). ProjectService searches ancestor directories for a config file when a file is opened. The parsed ParsedCommandLine from the config drives the compiler options and the file inclusion list. File system watchers monitor the config file and any wildcard include/exclude patterns so that the project automatically updates when files are added or removed.
Created for files that have no discoverable tsconfig.json. Each such file group is placed in an inferred project. The compiler options are either default values or those supplied by the client via the setCompilerOptionsForInferredProjects protocol message. Multiple files can share an InferredProject when they are opened together without a config context.
Created on behalf of the editor client, which supplies the full file list and compiler options via the openExternalProject / openExternalProjects protocol commands. Build systems and IDE plugins that maintain their own project model use this type. The client is responsible for calling closeExternalProject when the project is no longer needed.
A background project that shadows a root project. It expands the set of available symbols for auto-import completions by including packages present in node_modules that are not in the root project's compilation. It is created lazily and is not associated with an editor session directly.
Sources: src/server/project.ts1-100 src/server/editorServices.ts145-200
Project class hierarchy
Sources: src/server/project.ts1-150
ProjectService (defined in src/server/editorServices.ts) is the single stateful object that owns all projects and all open files.
| Field | Type | Purpose |
|---|---|---|
openFiles | Map<Path, NormalizedPath | undefined> | All currently-open files with their project root hint |
configuredProjects | Map<NormalizedPath, ConfiguredProject> | Active configured projects keyed by config file path |
inferredProjects | InferredProject[] | All active inferred projects |
externalProjects | ExternalProject[] | All active external projects |
filenameToScriptInfo | Map<Path, ScriptInfo> | All known ScriptInfo objects |
documentRegistry | DocumentRegistry | Shared registry that avoids re-parsing source files |
The documentRegistry is shared across all projects so that SourceFile objects are reused when the same file appears in multiple projects.
ProjectService raises events that Session forwards to the editor client:
| Constant | Value | When |
|---|---|---|
ProjectsUpdatedInBackgroundEvent | "projectsUpdatedInBackground" | Background project graph refresh completed |
ProjectLoadingStartEvent | "projectLoadingStart" | Project is about to load files |
ProjectLoadingFinishEvent | "projectLoadingFinish" | Project finished loading |
LargeFileReferencedEvent | "largeFileReferenced" | A file exceeds maxFileSize (4 MB) |
ConfigFileDiagEvent | "configFileDiag" | Diagnostics in a tsconfig.json |
ProjectLanguageServiceStateEvent | "projectLanguageServiceState" | Language service enabled/disabled |
Sources: src/server/editorServices.ts198-210
ScriptInfo (in src/server/scriptInfo.ts) represents a single file as seen by ProjectService. It is the bridge between the raw file on disk and the IScriptSnapshot that the compiler consumes.
Key responsibilities of ScriptInfo:
ScriptKind (TS, JS, TSX, JSX, JSON, …).TextChange arrays from the editor using applyChanges.containingProjects) that include this file.IScriptSnapshot used by the compiler host.When a file is open in an editor, the ScriptInfo uses the in-memory buffer. When not open, it reads from disk through the ServerHost.
Sources: src/server/scriptInfo.ts1-50
Data flow: from open-file request to LanguageService
Sources: src/server/editorServices.ts1-210 src/server/project.ts1-100 src/server/session.ts1-100 src/server/scriptInfo.ts1-50
When the editor sends an open request, Session calls ProjectService.openClientFile(fileName, fileContent, scriptKind, projectRootPath).
ProjectService looks up or creates a ScriptInfo for the path.ProjectService walks ancestor directories looking for tsconfig.json or jsconfig.json using findConfigFile. If found, it returns or creates a ConfiguredProject.InferredProject.project.updateGraph() is called to refresh the Program so it includes the new file.openClientFile returns the ConfiguredProject or InferredProject that now owns the file, and any config file diagnostics.When the editor sends a close request, Session calls ProjectService.closeClientFile(fileName).
ScriptInfo is marked as not open; its content reverts to being read from disk.ProjectService checks whether any projects still need the file (via other open files) and may delete orphaned inferred projects.The maxProgramSizeForNonTsFiles limit (20 MB, set in editorServices.ts) prevents InferredProject from loading a large number of JavaScript files that would make the compiler too slow.
Sources: src/server/editorServices.ts198-201 src/server/project.ts1-50
When source files or config files change on disk, ProjectService uses file system watchers (via ServerHost) to detect the changes and schedule a project graph update.
| What is watched | Triggers |
|---|---|
tsconfig.json content | Compiler options changed; re-parse config and rebuild |
| Wildcard include directories | Files added/removed matching include/exclude globs |
| Individual source files | File content changes when file is not open in editor |
node_modules | Package changes that affect module resolution |
package.json files | Dependency changes affecting ATA and module resolution |
Changes are batched using ThrottledOperations to avoid redundant updates when many files change simultaneously (e.g., a git checkout).
When a project's updateGraph() is triggered:
Program is rebuilt using createProgram with the current file set and options.ConfiguredProjects or removed from stale ones.ProjectsUpdatedInBackgroundEvent is emitted so the client can re-request diagnostics.Sources: src/server/editorServices.ts120-195 src/server/project.ts1-100
Each Project instance creates exactly one LanguageService by calling createLanguageService(projectAsHost, documentRegistry, languageServiceMode).
Project itself implements LanguageServiceHost, providing getScriptFileNames(), getScriptVersion(), getScriptSnapshot(), getCompilationSettings(), etc. to the language service.DocumentRegistry is passed in so SourceFile ASTs are shared when the same file (at the same version) appears in multiple projects.languageServiceMode is set to LanguageServiceMode.Semantic for full projects, or LanguageServiceMode.Syntactic when a project is in a degraded state (e.g., it exceeds the program size limit for non-TS files).Project host protocol
Sources: src/server/project.ts1-50 src/services/services.ts1-50
For InferredProjects, there is no tsconfig.json to supply compiler options. The client may set default options using the setCompilerOptionsForInferredProjects protocol command. ProjectService stores these in:
These are managed by the compilerOptionsForInferredProjects and compilerOptionsForInferredProjectsPerProjectRoot maps inside ProjectService.
Sources: src/server/editorServices.ts200-560 src/server/protocol.ts1-50
ProjectService file and project lifecycle
Sources: src/server/editorServices.ts198-560 src/server/project.ts1-150 src/server/scriptInfo.ts1-50
Refresh this wiki
This wiki was recently refreshed. Please wait 4 days to refresh again.