You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TSSLint is probably the smallest linter implementation ever. Built on the TypeScript Language Server (`tsserver`), it provides a minimalist diagnostic extension interface with zero default rules, allowing developers to implement custom rules with minimal overhead.
14
+
A linter that runs inside `tsserver`. No separate process, no re-parsing, no ESTree conversion. It uses the TypeChecker your editor already has.
15
15
16
-
## Motivation
16
+
Zero built-in rules. You write what you need with the full TypeScript compiler API.
17
17
18
-
TSSLint is the **spiritual successor to TSLint**. We believe that **direct integration with native TypeScript APIs** is the most efficient way to lint TypeScript code.
18
+
## Why?
19
19
20
-
General-purpose linters like ESLint, while powerful, operate as separate processes and often need to re-initialize type-checking context. This leads to a significant pain point in large-scale projects: **editor lag during "Auto Fix on Save"**.
20
+
ESLint works, but it runs separately and sets up its own type-checking. On large projects, "Auto Fix on Save" gets laggy.
21
21
22
-
TSSLint solves this by running directly as a `tsserver` plugin. By sharing the existing `TypeChecker` and operating on the native TypeScript AST (without ESTree/ES parser conversion), TSSLint provides **near-instant diagnostics and fixes**.
23
-
24
-
## Key Features
25
-
26
-
***Project-Centric**: Treats the **Project (tsconfig)** as a first-class citizen, enabling efficient cross-file type analysis and superior Monorepo support.
27
-
***High Performance**: Runs as a `tsserver` plugin, sharing the existing `TypeChecker` to provide near-instant diagnostics without redundant parsing.
28
-
***Minimalist Implementation**: Probably the smallest linter ever. Zero built-in rules and minimal code overhead by leveraging native TypeScript infrastructure.
29
-
***Rule Traceability**: Built-in debugging support. Jump from a reported error directly to the exact line in your **rule's source code** that triggered it.
30
-
31
-
## How It Works
32
-
33
-
TSSLint integrates into `tsserver` via the TypeScript plugin system, leveraging the semantic information already computed by your editor. Operating at the project level ensures accurate and performant diagnostics.
Since TSSLint operates directly within `tsserver`, it supports any framework that integrates with the TypeScript plugin system.
42
-
43
-
Tools like **Vue Official (Volar)**, **MDX**, or **Astro** virtualize non-TypeScript files into virtual TypeScript source files for `tsserver`. TSSLint seamlessly accesses and lints the TypeScript code within these virtual files without any additional configuration.
22
+
TSSLint avoids this by running as a `tsserver` plugin. It reuses the existing TypeChecker, works on native TypeScript AST, and skips the parser conversion layer.
44
23
45
24
<palign="center">
46
-
<imgsrc="architecture_v2.png"alt="TSSLint Framework Support Diagram"width="700">
A minimal configuration looks like this. For a complete example, see the [vuejs/language-tools tsslint.config.ts](https://github.com/vuejs/language-tools/blob/master/tsslint.config.ts).
34
+
Create `tsslint.config.ts`:
60
35
61
36
```ts
62
37
import { defineConfig } from'@tsslint/config';
63
38
64
39
exportdefaultdefineConfig({
65
40
rules: {
66
-
//Define or import your rules here
41
+
// your rules here
67
42
},
68
43
});
69
44
```
70
45
71
-
### 3. Editor Integration
72
-
73
-
***VSCode**:
74
-
1. Install the [TSSLint extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint).
75
-
2. (Optional) If you encounter issues importing `tsslint.config.ts` due to Node.js version mismatches, you can configure `typescript.tsserver.nodePath` to point to a Node.js 23.6.0+ executable.
76
-
***Other Editors**: Configure TSSLint as a plugin in your `tsconfig.json`:
if (node.kind===ts.SyntaxKind.DebuggerStatement) {
96
-
report(
97
-
'Debugger statement is not allowed.',
98
-
node.getStart(file),
99
-
node.getEnd()
100
-
);
66
+
report('Debugger statement is not allowed.', node.getStart(file), node.getEnd());
101
67
}
102
68
ts.forEachChild(node, cb);
103
69
});
104
70
});
105
71
```
106
72
107
-
### Rule Caching Mechanism
108
-
109
-
110
-
TSSLint's high performance comes from its intelligent caching strategy, which automatically distinguishes between **Syntax-Aware** and **Type-Aware** rules.
111
-
112
-
All rule diagnostics are cached by default. The cache is automatically disabled for a rule in two scenarios:
73
+
For a real-world example, see [vuejs/language-tools tsslint.config.ts](https://github.com/vuejs/language-tools/blob/master/tsslint.config.ts).
113
74
114
-
1.**Type-Aware Detection**: If a rule accesses `RuleContext.program` (e.g., to check types), TSSLint detects it as Type-Aware. The cache for this rule is then automatically managed and invalidated to ensure accuracy.
115
-
2.**Manual Exclusion**: A rule can explicitly prevent a specific diagnostic from being cached by calling `report().withoutCache()`.
75
+
### Caching
116
76
117
-
This automatic differentiation maximizes performance for simple syntax rules while maintaining correctness for complex type-aware rules.
77
+
Diagnostics are cached by default. The cache invalidates automatically when:
118
78
119
-
### Rule Debugging & Traceability (The `.at()` Magic)
79
+
1. A rule accesses `RuleContext.program` (type-aware rules)
80
+
2. A rule calls `report().withoutCache()`
120
81
121
-
TSSLint is designed to make rule debugging trivial. Every time you call `report()`, TSSLint automatically captures the current JavaScript stack trace and attaches it to the diagnostic as **Related Information**.
82
+
### Debugging
122
83
123
-
This means: **You can click on the diagnostic in your editor and jump directly to the line in your rule's source code that triggered the report.**
84
+
Every `report()` captures a stack trace. Click the diagnostic in your editor to jump to the exact line in your rulethat triggered it.
The `.at()` method is generally not needed, but is provided for advanced scenarios where you wrap `report()` in a helper function and need to adjust the stack depth to point to the correct logic:
130
-
131
-
```ts
132
-
// Example of advanced usage to adjust stack depth
133
-
report('message', start, end)
134
-
.at(newError(), 2) // Adjusts the stack index to skip the helper function's frame
135
-
.withFix(...);
136
-
```
137
-
138
-
## CLI Usage
139
-
140
-
The `@tsslint/cli` package provides a command-line tool for CI/CD and build processes.
> TSSLint focuses on diagnostic fixes and does not include a built-in formatter. It is recommended to run a dedicated formatter like **Prettier**, **dprint**, or **oxfmt** after running TSSLint with `--fix`.
TSSLint only does diagnostics and fixes. Run Prettier or dprint after `--fix`.
164
101
165
-
exportdefaultdefineConfig({
166
-
rules: {
167
-
...
168
-
},
169
-
plugins: [
170
-
createIgnorePlugin('tsslint-ignore', true)
171
-
],
172
-
});
173
-
```
174
-
*Usage: Use `// tsslint-ignore` comments in your code.*
102
+
## Framework Support
175
103
176
-
### Ecosystem Integration
104
+
TSSLint works with Vue, MDX, Astro, and anything else that plugs into tsserver. These tools virtualize their files as TypeScript for tsserver. TSSLint just sees and lints that TypeScript.
177
105
178
-
TSSLint provides compatibility layers for existing linter ecosystems (ESLint, TSLint, and TSL). These integrations are coordinated through `@tsslint/config`, which acts as a bridge to load rules from other linters.
179
-
180
-
To use a compatibility layer, you must install the corresponding TSSLint compatibility package. If you wish to use the original linter's built-in rules, you must also install the original linter package itself.
You can load rules from ESLint and TSLint through compatibility layers.
185
113
186
-
First, install the TSSLint compatibility package for ESLint.
114
+
### ESLint
187
115
188
116
```bash
189
117
npm install @tsslint/compat-eslint --save-dev
118
+
npm install eslint --save-dev # optional, for built-in rules
119
+
npx tsslint-docgen # generates JSDoc for IDE support
190
120
```
191
121
192
-
If you want to use ESLint's built-in rules (e.g., `no-unused-vars`), you must also install `eslint` (optional):
193
-
194
-
```bash
195
-
npm install eslint --save-dev
196
-
```
197
-
198
-
**Type Definition Update:**
199
-
200
-
After installing the original linter package, run the following command to update JSDoc for built-in rules, enabling better IDE support:
201
-
202
-
```bash
203
-
npx tsslint-docgen
204
-
```
205
-
206
-
**Usage in `tsslint.config.ts`:**
207
-
208
-
Use `importESLintRules` to load rules. This function automatically resolves and loads rules from ESLint plugins (e.g., `@typescript-eslint/eslint-plugin`) by searching your `node_modules`. Plugin rules are identified by their prefix (e.g., `@typescript-eslint/`).
If you want to use TSLint's built-in rules, you need to install `tslint` (optional):
228
-
229
-
```bash
230
-
npm install tslint --save-dev
231
-
```
232
-
233
-
**Type Definition Update:**
234
-
235
-
After installing `tslint`, run the following command to update JSDoc for built-in rules:
135
+
### TSLint
236
136
237
137
```bash
138
+
npm install tslint --save-dev # optional, for built-in rules
238
139
npx tsslint-docgen
239
140
```
240
141
241
-
**Usage in `tsslint.config.ts`:**
242
-
243
-
Use `importTSLintRules` to load rules. This function automatically reads `rulesDirectory` from your `tslint.json` to support third-party TSLint plugins.
0 commit comments