Skip to content

Commit 7ead22a

Browse files
committed
chore: remove unused architecture and traceability images from the repository
1 parent 4566dcd commit 7ead22a

4 files changed

Lines changed: 195 additions & 49 deletions

File tree

README.md

Lines changed: 195 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,91 @@
1111
<a href="https://deepwiki.com/johnsoncodehk/tsslint"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a>
1212
</p>
1313

14-
A linter that runs inside `tsserver`. No separate process, no re-parsing, no ESTree conversion. It uses the TypeChecker your editor already has.
14+
A linter that runs as a `tsserver` plugin. It reuses the TypeChecker your editor already has — no second process, no AST conversion, no duplicated type-checking.
1515

16-
Zero built-in rules. You write what you need with the full TypeScript compiler API.
16+
Zero built-in rules. Rules are plain functions over the TypeScript compiler API.
1717

1818
## Why?
1919

20-
ESLint works, but it runs separately and sets up its own type-checking. On large projects, "Auto Fix on Save" gets laggy.
20+
ESLint runs in its own process and builds its own type information. On large projects this makes "Auto Fix on Save" slow.
2121

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.
22+
TSSLint piggybacks on `tsserver`. Diagnostics show up in the same path TypeScript errors do, using the same `Program` instance.
2323

24-
<p align="center">
25-
<img src="architecture.png" alt="TSSLint Architecture" width="700">
26-
</p>
24+
```
25+
Traditional TSSLint
26+
─────────── ───────
27+
28+
┌─────┐ ┌─────┐
29+
│ IDE │ │ IDE │
30+
└──┬──┘ └──┬──┘
31+
│ │
32+
┌───┴────┐ ▼
33+
▼ ▼ ┌─────────────────┐
34+
┌──────┐ ┌──────┐ │ tsserver │
35+
│ ts- │ │linter│ │ ┌───────────┐ │
36+
│server│ │ │ │ │TypeChecker│ │
37+
│ │ │ │ │ └─────┬─────┘ │
38+
│ Type │ │ Type │ │ │ │
39+
│ Chk. │ │ Chk. │ │ ┌─────▼─────┐ │
40+
└──────┘ └──────┘ │ │ TSSLint │ │
41+
│ └───────────┘ │
42+
✗ two type-checkers └─────────────────┘
43+
two parses ✓ one shared pass
44+
```
45+
46+
## How it compares
47+
48+
TSLint (TS-AST, deprecated 2019) → ESLint took over via `typescript-eslint` → TSSLint revives the in-process TS-AST approach as a `tsserver` plugin (2023).
49+
50+
```
51+
2013 2019 2023
52+
│ │ │
53+
│ │ │
54+
TSLint ─────●━━━━━━━━━━━✗ deprecated │
55+
╲ │
56+
╲ │
57+
ESLint ─────●━━━━━━━━━━━━━━━━╲━━━━━━━━━━━━━━━━━━━▶ (active)
58+
╲ │
59+
╲ │
60+
TSSLint ╲────────────●━━▶ (tsserver plugin,
61+
revives TS-AST)
62+
```
63+
64+
| | ESLint | TSLint | Oxlint | TSSLint |
65+
|---|---|---|---|---|
66+
| Runtime | Node, separate process | Node, separate process | Rust, separate process | Node, in `tsserver` |
67+
| AST | ESTree | TS AST | Native Rust AST | TS AST |
68+
| Type-aware rules | Yes (its own `Program`) | Yes (its own `Program`) | Yes (via `tsgolint`, alpha) | Yes (shared `TypeChecker`) |
69+
| Built-in rules | Many | Deprecated | Subset of ESLint (+ JS plugins, alpha) | Zero (imports ESLint / TSLint / TSL) |
70+
| Status | Active standard | Deprecated 2019 | Active | Active |
71+
72+
**Pick by need.** Largest ecosystem → ESLint. Fastest standalone runtime → Oxlint. Type-aware without duplicate type-checking → TSSLint.
2773

2874
## Setup
2975

3076
```bash
3177
npm install @tsslint/config --save-dev
3278
```
3379

34-
Create `tsslint.config.ts`:
80+
`tsslint.config.ts`:
3581

3682
```ts
3783
import { defineConfig } from '@tsslint/config';
3884

3985
export default defineConfig({
4086
rules: {
41-
// your rules here
87+
// your rules
4288
},
4389
});
4490
```
4591

46-
**VSCode**: Install the [TSSLint extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint).
92+
**VSCode**: install [the extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint).
4793

48-
**Other editors**: Add the plugin to `tsconfig.json`:
94+
**Other editors**: install the plugin and register it in `tsconfig.json`:
95+
96+
```bash
97+
npm install @tsslint/typescript-plugin --save-dev
98+
```
4999

50100
```json
51101
{
@@ -55,78 +105,156 @@ export default defineConfig({
55105
}
56106
```
57107

58-
## Writing Rules
108+
## Writing rules
109+
110+
A rule is a function. It receives the TypeScript module, the current `Program`, the `SourceFile`, and a `report()` callback.
59111

60112
```ts
61113
import { defineRule } from '@tsslint/config';
62114

63115
export default defineRule(({ typescript: ts, file, report }) => {
64-
ts.forEachChild(file, function cb(node) {
116+
ts.forEachChild(file, function visit(node) {
65117
if (node.kind === ts.SyntaxKind.DebuggerStatement) {
66118
report('Debugger statement is not allowed.', node.getStart(file), node.getEnd());
67119
}
68-
ts.forEachChild(node, cb);
120+
ts.forEachChild(node, visit);
69121
});
70122
});
71123
```
72124

73-
For a real-world example, see [vuejs/language-tools tsslint.config.ts](https://github.com/vuejs/language-tools/blob/master/tsslint.config.ts).
125+
Touch `program` only when you need type information — rules that don't are cached aggressively (see [Caching](#caching)).
126+
127+
### Severity, fixes, refactors
128+
129+
`report()` returns a chainable reporter:
130+
131+
```ts
132+
report('No console.', node.getStart(file), node.getEnd())
133+
.asError() // default is Message; also: asWarning(), asSuggestion()
134+
.withDeprecated() // strikethrough
135+
.withUnnecessary() // faded
136+
.withFix('Remove call', () => [
137+
{ fileName: file.fileName, textChanges: [{ span: { start, length }, newText: '' }] },
138+
])
139+
.withRefactor('Wrap in if (DEBUG)', () => [/* ... */]);
140+
```
141+
142+
`withFix` runs automatically as a quick fix; `withRefactor` shows up under the editor's refactor menu (user-initiated).
143+
144+
### Real-world example
145+
146+
[vuejs/language-tools tsslint.config.ts](https://github.com/vuejs/language-tools/blob/master/tsslint.config.ts).
147+
148+
### Organizing rules
149+
150+
Rules can nest; the path becomes the rule id:
151+
152+
```ts
153+
defineConfig({
154+
rules: {
155+
style: {
156+
'no-debugger': debuggerRule, // reported as "style/no-debugger"
157+
},
158+
},
159+
});
160+
```
161+
162+
`defineConfig` also accepts an array — each entry can scope rules with `include` / `exclude` minimatch patterns.
74163

75164
### Caching
76165

77-
Diagnostics are cached by default. The cache invalidates automatically when:
166+
By default, rules run in a syntax-only mode and their diagnostics are cached on disk under `os.tmpdir()/tsslint-cache/`. Cache is keyed by file mtime.
167+
168+
The moment a rule reads `ctx.program`, it switches to type-aware mode for that file and skips the cache (type information depends on more than one file's mtime). To opt a single diagnostic out of caching without going type-aware, call `.withoutCache()` on the reporter.
78169

79-
1. A rule accesses `RuleContext.program` (type-aware rules)
80-
2. A rule calls `report().withoutCache()`
170+
Pass `--force` to the CLI to ignore the cache.
81171

82172
### Debugging
83173

84-
Every `report()` captures a stack trace. Click the diagnostic in your editor to jump to the exact line in your rule that triggered it.
174+
Every `report()` captures a stack trace. The diagnostic carries a "Related Information" link back to the exact line in your rule that triggered it — ⌘-click in the editor to jump there:
85175

86-
<p align="center">
87-
<img src="traceability.png" alt="Rule Traceability" width="700">
88-
</p>
176+
```
177+
src/index.ts:3:1
178+
3 │ debugger;
179+
│ ~~~~~~~~~ Debugger statement is not allowed. (tsslint)
180+
│ ↳ rules/no-debugger.ts:5:7 ⌘-click to open
181+
```
89182

90183
## CLI
91184

185+
```bash
186+
npm install @tsslint/cli --save-dev
187+
```
188+
92189
```bash
93190
npx tsslint --project tsconfig.json
94191
npx tsslint --project tsconfig.json --fix
95-
npx tsslint --project packages/*/tsconfig.json
192+
npx tsslint --project 'packages/*/tsconfig.json' --filter 'src/**/*.ts'
96193
```
97194

98-
Run `npx tsslint --help` for all options.
195+
Flags:
99196

100-
TSSLint only does diagnostics and fixes. Run Prettier or dprint after `--fix`.
197+
| Flag | |
198+
|---|---|
199+
| `--project <glob...>` | TypeScript projects to lint |
200+
| `--vue-project <glob...>` | Vue projects |
201+
| `--vue-vine-project <glob...>` | Vue Vine projects |
202+
| `--mdx-project <glob...>` | MDX projects |
203+
| `--astro-project <glob...>` | Astro projects |
204+
| `--ts-macro-project <glob...>` | TS Macro projects |
205+
| `--filter <glob...>` | Restrict to matching files |
206+
| `--fix` | Apply fixes |
207+
| `--force` | Ignore cache |
208+
| `--failures-only` | Only print diagnostics that affect exit code |
209+
| `-h`, `--help` | |
101210

102-
## Framework Support
211+
TSSLint produces diagnostics and edits — it does not format. Run dprint or Prettier after `--fix`.
103212

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.
213+
## Framework support
105214

106-
<p align="center">
107-
<img src="architecture_v2.png" alt="Framework Support" width="700">
108-
</p>
215+
The `--*-project` flags wire in [Volar](https://volarjs.dev/) language plugins so framework files (Vue SFCs, MDX, Astro components, etc.) are virtualized as TypeScript before linting. Anything `tsserver` can see, TSSLint can lint.
109216

110-
## Using ESLint/TSLint Rules
217+
```
218+
.vue ──┐
219+
.mdx ──┤ ┌──────────────┐ ┌──────────────────┐
220+
.astro──┼───▶│ Framework │───▶│ tsserver │───▶ diagnostics
221+
.ts ──┘ │ adapters │ │ │ in editor
222+
│ │ │ TypeChecker │
223+
│ ─▶ virtual │ │ + │
224+
│ TS file │ │ TSSLint plugin │
225+
└──────────────┘ └──────────────────┘
226+
```
111227

112-
You can load rules from ESLint and TSLint through compatibility layers.
228+
Each flag resolves the language plugin from your project's `node_modules`, so you must install the corresponding package:
229+
230+
| Flag | Required package(s) |
231+
|---|---|
232+
| `--vue-project` | `@vue/language-core` or `vue-tsc` |
233+
| `--vue-vine-project` | `@vue-vine/language-service` or `vue-vine-tsc` |
234+
| `--mdx-project` | `@mdx-js/language-service` |
235+
| `--astro-project` | `@astrojs/ts-plugin` |
236+
| `--ts-macro-project` | `@ts-macro/language-plugin` or `@ts-macro/tsc` |
237+
238+
## Importing ESLint, TSLint, or TSL rules
113239

114240
### ESLint
115241

116242
```bash
117243
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
244+
npm install @typescript-eslint/eslint-plugin --save-dev # for @typescript-eslint/* rules
245+
npx tsslint-docgen # generates JSDoc for IDE autocomplete
120246
```
121247

248+
For each non-built-in rule (`<plugin>/<rule>`), install the matching ESLint plugin (`eslint-plugin-<plugin>` or `@scope/eslint-plugin`).
249+
122250
```ts
123251
import { defineConfig, importESLintRules } from '@tsslint/config';
124252

125253
export default defineConfig({
126254
rules: {
127255
...await importESLintRules({
128256
'no-unused-vars': true,
129-
'@typescript-eslint/no-explicit-any': true,
257+
'@typescript-eslint/no-explicit-any': 'warn',
130258
}),
131259
},
132260
});
@@ -135,19 +263,17 @@ export default defineConfig({
135263
### TSLint
136264

137265
```bash
138-
npm install tslint --save-dev # optional, for built-in rules
266+
npm install tslint --save-dev # required for built-in rules
139267
npx tsslint-docgen
140268
```
141269

142270
```ts
143271
import { defineConfig, importTSLintRules } from '@tsslint/config';
144272

145273
export default defineConfig({
146-
rules: {
147-
...await importTSLintRules({
148-
'no-console': true,
149-
}),
150-
},
274+
rules: await importTSLintRules({
275+
'no-console': true,
276+
}),
151277
});
152278
```
153279

@@ -166,23 +292,43 @@ export default defineConfig({
166292
});
167293
```
168294

169-
## Ignoring Rules
295+
## Plugins
296+
297+
Plugins can rewrite rules per file, filter diagnostics, and inject code fixes. Three are bundled:
170298

171299
```ts
172-
import { defineConfig, createIgnorePlugin } from '@tsslint/config';
300+
import {
301+
defineConfig,
302+
createIgnorePlugin,
303+
createCategoryPlugin,
304+
createDiagnosticsPlugin,
305+
} from '@tsslint/config';
306+
import ts from 'typescript';
173307

174308
export default defineConfig({
175-
rules: { ... },
176-
plugins: [createIgnorePlugin('tsslint-ignore', true)],
309+
rules: { /* ... */ },
310+
plugins: [
311+
// // tsslint-ignore [rule-id] — single-line, or *-start / *-end pairs
312+
createIgnorePlugin('tsslint-ignore', /* report unused */ true),
313+
314+
// Override severity by rule-id pattern
315+
createCategoryPlugin({
316+
'style/*': ts.DiagnosticCategory.Warning,
317+
}),
318+
319+
// Forward TypeScript's own diagnostics through the same pipeline
320+
createDiagnosticsPlugin('semantic'),
321+
],
177322
});
178323
```
179324

180-
Then use `// tsslint-ignore` comments in your code.
325+
Build your own with the `Plugin` type from `@tsslint/types`.
181326

182-
## Notes
327+
## Requirements
183328

184-
- Requires Node.js 22.6.0+
185-
- Not compatible with typescript-go (v7) - it doesn't support Language Service Plugins
329+
- Node.js **22.6.0+** (uses `--experimental-strip-types` to load `tsslint.config.ts` directly — no transpile step)
330+
- Any TypeScript version with Language Service Plugin support
331+
- Not compatible with `typescript-go` (v7), which does not yet support Language Service Plugins
186332

187333
## License
188334

architecture.png

-4.61 MB
Binary file not shown.

architecture_v2.png

-4.32 MB
Binary file not shown.

traceability.png

-5.16 MB
Binary file not shown.

0 commit comments

Comments
 (0)