A TypeScript server plugin that limits the scope of tsserver to speed up the experience in code editors by processing only the open files and their dependencies.
Poor TypeScript performance in code editors and high memory usage is a common issue in large codebases, especially in monorepos. To mitigate this, the community has been tweaking configs, refactoring code and waiting for TypeScript 7. But what if we could get a visible performance boost by changing the way tsserver treats projects? Let’s take a look at the project example and see what happens after opening a single file.
workspaceA
- global.d.ts
- fileA.ts
- fileB.ts
- tsconfig.json
workspaceB
- fileC.ts
- fileD.ts
- tsconfig.json
workspaceA/tsconfig.json
is the following:
"compilerOptions" {
...
},
"include": ["."]
After opening workspaceA/fileA.ts
, tsserver processes and adds watchers for all files in workspaceA and their dependencies, so 4 files in total:
flowchart TD
subgraph workspaceA
A[fileA.ts]
B[fileB.ts]
D[global.d.ts]
end
subgraph workspaceB
C[fileC.ts]
E[fileD.ts]
end
B --> C
class E transparent;
class A chosen;
classDef transparent fill:#ffffff00,stroke:#999,stroke-width:1px,color:#999;
classDef chosen stroke-width:3px;
To illustrate the problem, here is a simple file without any TS dependencies: loading it in VSCode takes >5s because tsserver processes 6510 files. ts-scope-trimmer-plugin
changes this behavior by limiting the tsserver scope only to the open files, their dependencies and global types:
flowchart TD
subgraph workspaceA
A[fileA.ts]
B[fileB.ts]
D[global.d.ts]
end
subgraph workspaceB
C[fileC.ts]
E[fileD.ts]
end
B --> C
class E transparent;
class B transparent;
class C transparent;
class A chosen;
classDef transparent fill:#ffffff00,stroke:#999,stroke-width:1px,color:#999;
classDef chosen stroke-width:3px;
So opening the same file in VSCode takes around 500ms because tsserver loads only 183 files.
Warning
Features like "find all references" and "find usages" are not fully functional since they need the full project context. This seems like an acceptable trade-off, considering the opt-in nature of the plugin. See configuration for more details.
Add this plugin as a dev dependency to your project:
npm install ts-scope-trimmer-plugin --save-dev
Add this plugin to your tsconfig.json
:
{
"compilerOptions": {
"plugins": [
{
"name": "ts-scope-trimmer-plugin"
}
]
}
}
VSCode/Cursor/Windsurf only: make sure to use the workspace version of TypeScript.
Option | Type | Default | Description |
---|---|---|---|
enabled |
boolean | false |
Enable/disable the plugin |
alwaysInclude |
string[] | [] |
Glob patterns for files that should always be included, e.g. global types. Relative to the project root. Example |
debug |
boolean | false |
Enable/disable debug logging |
These options can be specified in tsconfig.json
(example) or in ts-scope-trimmer.json
(example) in your project root. The latter has higher priority and can be added to .gitignore
, so engineers can toggle the plugin locally on demand.
This plugin is tested with TypeScript >=5.0.0, Node.js >=18.0.0 and the following code editors:
- VSCode
- Cursor
- IntelliJ IDEA
- WebStorm
- Neovim
- Zed
Benchmark was performed in a test repo on this commit with TypeScript 5.9.2, VSCode 1.102.3 (default settings), Mac M1 Pro, 32GB RAM.
- ms: shows the time in milliseconds it takes to fully load the file (
updateOpen
event) - files: shows the number of files that tsserver processes
File | Before (ms) | After (ms) | Before (files) | After (files) |
---|---|---|---|---|
apps/crew/pages/_app.tsx | 5100 | 530 | 6510 | 187 |
apps/crew/pages/important-feature-0.tsx | 5100 | 1400 | 6510 | 1695 |
packages/crew/important-feature-0/src/lib/important-component-0/important-component-0.tsx | 1400 | 1300 | 1653 | 1402 |
The performance improvement varies by file: the fewer the dependencies it has, the greater the benefit.
Contributions are welcome! Please see CONTRIBUTING.md for details.