|
1 | 1 | # TSSLint |
2 | 2 |
|
3 | | -<p> |
| 3 | +<p align="center"> |
| 4 | + <img src="logo.png" alt="TSSLint Logo" width="200"> |
| 5 | +</p> |
| 6 | + |
| 7 | +<p align="center"> |
4 | 8 | <a href="https://npmjs.com/package/@tsslint/core"><img src="https://badgen.net/npm/v/@tsslint/core" alt="npm package"></a> |
5 | | - <a href="https://deepwiki.com/johnsoncodehk/tsslint"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> |
| 9 | + <a href="https://discord.gg/NpdmPEUNjE"><img src="https://img.shields.io/discord/854968233938354226?color=7289DA&label=discord" alt="Discord"></a> |
6 | 10 | <a href="https://github.com/johnsoncodehk/tsslint/tree/master/LICENSE"><img src="https://img.shields.io/github/license/johnsoncodehk/tsslint.svg?labelColor=18181B&color=1584FC" alt="License"></a> |
| 11 | + <a href="https://deepwiki.com/johnsoncodehk/tsslint"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> |
7 | 12 | </p> |
8 | 13 |
|
9 | | -> A lightweight inspection tool that seamlessly integrates with TypeScript Language Server |
10 | | -
|
11 | | -TSSLint is not your typical linter. Its main purpose is to expose the TypeScript Language Server diagnostic interface, allowing you to add your own diagnostic rules without additional overhead to creating a TypeChecker. |
12 | | - |
13 | | -Discord Server: https://discord.gg/NpdmPEUNjE |
14 | | - |
15 | | -Special thanks to @basarat for transferring the `tsl` package name. |
16 | | - |
17 | | -## Packages |
| 14 | +TSSLint is a minimalist diagnostic extension interface built on the TypeScript Language Server (`tsserver`). It provides zero default rules, allowing developers to implement custom rules that complement TypeScript's native checks with minimal overhead. |
18 | 15 |
|
19 | | -This repository is a monorepo that we manage using [Lerna-Lite](https://github.com/lerna-lite/lerna-lite). That means that we actually publish several packages to npm from the same codebase, including: |
| 16 | +## Motivation |
20 | 17 |
|
21 | | -- [`cli`](packages/cli): This package provides the command line interface for TSSLint. |
22 | | -- [`config`](packages/config): This package allows you to define and build configuration files for TSSLint. |
23 | | -- [`core`](packages/core): This is the core package for TSSLint, which provides the main functionality of the tool. |
24 | | -- [`typescript-plugin`](packages/typescript-plugin): This package integrates TSSLint with the TypeScript language server. |
25 | | -- [`vscode`](packages/vscode): This package is a Visual Studio Code extension that integrates TSSLint into the editor. |
| 18 | +TSSLint was created to solve a specific pain point: **editor lag during "Auto Fix on Save"**. |
26 | 19 |
|
27 | | -## Why TSSLint? |
| 20 | +In large-scale TypeScript projects, traditional linters (like ESLint) often cause noticeable delays when performing auto-fixes on save, as they frequently need to re-initialize type-checking or run in separate processes. By running directly as a `tsserver` plugin and sharing the existing type-checking context, TSSLint provides near-instant diagnostics and fixes, ensuring a smooth and uninterrupted development experience. |
28 | 21 |
|
29 | | -The performance of TypeScript in code editors has always been a crucial concern. Most TypeScript tools integrate TypeScript libraries to enable type checking and query code types through the LanguageService or TypeChecker API. |
| 22 | +## Key Features |
30 | 23 |
|
31 | | -However, for complex types or large codebases, the tsserver process can consume significant memory and CPU resources. When linter tools integrate with TypeScript and create their own LanguageService instances, memory and CPU usage can continue to increase. In some cases, this has caused projects to experience long save times when codeActionOnSave is enabled in VSCode. |
| 24 | +* **Zero Assumptions**: Comes with no built-in rules. It does not enforce any specific coding style or patterns, leaving full control to the developer. |
| 25 | +* **High Performance**: Runs as a `tsserver` plugin, sharing the existing `TypeChecker` instance to avoid redundant parsing and type-checking. |
| 26 | +* **Low Noise**: Violations are reported as "Message" diagnostics, ensuring they don't interfere with actual compiler errors or warnings. |
| 27 | +* **Direct AST Access**: Rule authoring uses native TypeScript APIs directly, without unnecessary abstraction layers. |
32 | 28 |
|
33 | | -TSSLint aims to seamlessly integrate with tsserver to minimize unnecessary overhead and provide linting capabilities on top of it. |
| 29 | +## How It Works |
34 | 30 |
|
35 | | -## Features |
| 31 | +TSSLint integrates into `tsserver` via the TypeScript plugin system, leveraging the semantic information already computed by your editor. |
36 | 32 |
|
37 | | -- Integration with tsserver to minimize semantic linting overhead in IDEs. |
38 | | -- Writing config in typescript. |
39 | | -- Direct support for meta framework files based on TS Plugin without a parser. (e.g., Vue, MDX) |
40 | | -- Pure ESM. |
41 | | -- Designed to allow simple, direct access to rule source code without an intermediary layer. |
42 | | - |
43 | | -## Usage |
44 | | - |
45 | | -To enable TSSLint in VSCode, follow these steps: |
46 | | - |
47 | | -1. Install the [TSSLint VSCode Extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint) |
48 | | -2. Add the `@tsslint/config` dependency to your project. |
49 | | - ```json |
50 | | - { |
51 | | - "devDependencies": { |
52 | | - "@tsslint/config": "latest" |
53 | | - } |
54 | | - } |
55 | | - ``` |
56 | | -3. Create the `tsslint.config.ts` config file: |
57 | | - ```js |
58 | | - import { defineConfig } from '@tsslint/config'; |
59 | | - |
60 | | - export default defineConfig({ |
61 | | - rules: { |
62 | | - // ... your rules |
63 | | - }, |
64 | | - }); |
65 | | - ``` |
66 | | - |
67 | | -### Create a Rule |
68 | | - |
69 | | -To create a rule, you need to define a function that receives the context of the current diagnostic task. Within this function, you can call `report()` to report an error. |
70 | | - |
71 | | -As an example, let's create a `no-console` rule under `[project root]/rules/`. |
| 33 | +<p align="center"> |
| 34 | + <img src="architecture.png" alt="TSSLint Architecture Diagram" width="700"> |
| 35 | +</p> |
72 | 36 |
|
73 | | -Here's the code for `[project root]/rules/noConsoleRule.ts`: |
| 37 | +## Getting Started |
74 | 38 |
|
75 | | -```js |
76 | | -import { defineRule } from '@tsslint/config'; |
| 39 | +### 1. Install |
77 | 40 |
|
78 | | -export function create() { |
79 | | - return defineRule(({ typescript: ts, file, report }) => { |
80 | | - ts.forEachChild(file, function cb(node) { |
81 | | - if ( |
82 | | - ts.isPropertyAccessExpression(node) && |
83 | | - ts.isIdentifier(node.expression) && |
84 | | - node.expression.text === 'console' |
85 | | - ) { |
86 | | - report( |
87 | | - `Calls to 'console.x' are not allowed.`, |
88 | | - node.parent.getStart(file), |
89 | | - node.parent.getEnd() |
90 | | - ).withFix( |
91 | | - 'Remove this console expression', |
92 | | - () => [{ |
93 | | - fileName: file.fileName, |
94 | | - textChanges: [{ |
95 | | - newText: '/* deleted */', |
96 | | - span: { |
97 | | - start: node.parent.getStart(file), |
98 | | - length: node.parent.getWidth(file), |
99 | | - }, |
100 | | - }], |
101 | | - }] |
102 | | - ); |
103 | | - } |
104 | | - ts.forEachChild(node, cb); |
105 | | - }); |
106 | | - }); |
107 | | -} |
| 41 | +```bash |
| 42 | +npm install @tsslint/config --save-dev |
108 | 43 | ``` |
109 | 44 |
|
110 | | -Then add it to the `tsslint.config.ts` config file. |
| 45 | +### 2. Configure `tsslint.config.ts` |
111 | 46 |
|
112 | | -```diff |
| 47 | +```ts |
113 | 48 | import { defineConfig } from '@tsslint/config'; |
114 | 49 |
|
115 | 50 | export default defineConfig({ |
116 | | - rules: { |
117 | | -+ 'no-console': (await import('./rules/noConsoleRule.ts')).create(), |
118 | | - }, |
| 51 | + rules: { |
| 52 | + // Define or import your rules here |
| 53 | + }, |
119 | 54 | }); |
120 | 55 | ``` |
121 | 56 |
|
122 | | -After saving the config file, you will notice that `console.log` is now reporting errors in the editor. The error message will also display the specific line of code where the error occurred. Clicking on the error message will take you to line 11 in `noConsoleRule.ts`, where the `report()` code is located. |
123 | | - |
124 | | -> Full example: https://github.com/johnsoncodehk/tsslint/tree/master/fixtures/define-a-rule |
125 | | -
|
126 | | -### Modify the Error |
127 | | - |
128 | | -While you cannot directly configure the severity of a rule, you can modify the reported errors through the `resolveDiagnostics()` API in the config file. This allows you to customize the severity of specific rules and even add additional errors. |
129 | | - |
130 | | -Here's an example of changing the severity of the `no-console` rule from Warning to Error in the `tsslint.config.ts` file: |
131 | | - |
132 | | -```js |
133 | | -import { defineConfig } from '@tsslint/config'; |
134 | | -import noConsoleRule from './rules/noConsoleRule.ts'; |
| 57 | +### 3. Editor Integration |
| 58 | + |
| 59 | +* **VSCode**: |
| 60 | + 1. Install the [TSSLint extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint). |
| 61 | + 2. Since TSSLint requires Node.js 23.6.0+ to import `tsslint.config.ts`, and VSCode (v1.108.0) currently bundles Node.js 22.21.1, you must configure `typescript.tsserver.nodePath` to point to a Node.js 23.6.0+ executable: |
| 62 | + ```json |
| 63 | + { |
| 64 | + "typescript.tsserver.nodePath": "/path/to/node-23.6.0" |
| 65 | + } |
| 66 | + ``` |
| 67 | +* **Other Editors**: Configure TSSLint as a plugin in your `tsconfig.json`: |
| 68 | + ```json |
| 69 | + { |
| 70 | + "compilerOptions": { |
| 71 | + "plugins": [{ "name": "@tsslint/typescript-plugin" }] |
| 72 | + } |
| 73 | + } |
| 74 | + ``` |
| 75 | + |
| 76 | +## Rule Example |
| 77 | + |
| 78 | +```ts |
| 79 | +// rules/no-debugger.ts |
| 80 | +import { defineRule } from '@tsslint/config'; |
135 | 81 |
|
136 | | -export default defineConfig({ |
137 | | - rules: { |
138 | | - 'no-console': noConsoleRule |
139 | | - }, |
140 | | - plugins: [ |
141 | | - ({ typescript: ts }) => ({ |
142 | | - resolveDiagnostics(file, diagnostics) { |
143 | | - for (const diagnostic of diagnostics) { |
144 | | - if (diagnostic.code === 'no-console') { |
145 | | - diagnostic.category = ts.DiagnosticCategory.Error; |
146 | | - } |
147 | | - } |
148 | | - return diagnostics; |
149 | | - }, |
150 | | - }), |
151 | | - ], |
| 82 | +export default defineRule(({ typescript: ts, file, report }) => { |
| 83 | + ts.forEachChild(file, function cb(node) { |
| 84 | + if (node.kind === ts.SyntaxKind.DebuggerStatement) { |
| 85 | + report( |
| 86 | + 'Debugger statement is not allowed.', |
| 87 | + node.getStart(file), |
| 88 | + node.getEnd() |
| 89 | + ); |
| 90 | + } |
| 91 | + ts.forEachChild(node, cb); |
| 92 | + }); |
152 | 93 | }); |
153 | 94 | ``` |
154 | 95 |
|
155 | 96 | ## CLI Usage |
156 | 97 |
|
157 | | -The `@tsslint/cli` package provides a command-line interface for running the TSSLint tool across your TypeScript projects. It can be used by running the `tsslint` command in your terminal. |
| 98 | +The `@tsslint/cli` package provides a command-line tool for CI/CD and build processes. |
| 99 | + |
| 100 | +```bash |
| 101 | +# Lint a project |
| 102 | +npx tsslint --project path/to/tsconfig.json |
158 | 103 |
|
159 | | -Here is a basic example of how to use it: |
| 104 | +# Auto-fix violations |
| 105 | +npx tsslint --project path/to/tsconfig.json --fix |
160 | 106 |
|
161 | | -```sh |
162 | | -npx tsslint --project path/to/your/tsconfig.json |
| 107 | +# Lint multiple projects |
| 108 | +npx tsslint --project 'packages/*/tsconfig.json' --vue-project 'apps/web/tsconfig.json' |
163 | 109 | ``` |
164 | 110 |
|
165 | | -This command will run the linter on the TypeScript project defined by the provided `tsconfig.json` file. Any linting errors will be output to the console. |
| 111 | +> [!TIP] |
| 112 | +> 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`. |
| 113 | +
|
| 114 | + |
| 115 | +## Extensions |
166 | 116 |
|
167 | | -If you want to automatically fix any fixable linting errors, you can use the `--fix` option: |
| 117 | +### Ignoring Rules |
| 118 | +```ts |
| 119 | +import { defineConfig, createIgnorePlugin } from '@tsslint/config'; |
168 | 120 |
|
169 | | -```sh |
170 | | -npx tsslint --project path/to/your/tsconfig.json --fix |
| 121 | +export default defineConfig({ |
| 122 | + plugins: [ |
| 123 | + createIgnorePlugin('tsslint-ignore', true) |
| 124 | + ], |
| 125 | +}); |
171 | 126 | ``` |
| 127 | +*Usage: Use `// tsslint-ignore` comments in your code.* |
172 | 128 |
|
173 | | -This will run the linter and automatically apply any fixes that are available. |
| 129 | +### Ecosystem Integration |
| 130 | +* **ESLint**: Convert rules via `@tsslint/eslint`. |
| 131 | +* **TSLint**: Convert rules via `@tsslint/tslint`. |
174 | 132 |
|
175 | | -You can also lint multiple projects at once: |
| 133 | +## Technical Notes |
176 | 134 |
|
177 | | -```sh |
178 | | -npx tsslint --project packages/*/tsconfig.json |
179 | | -npx tsslint --project {packages/pkg-a/tsconfig.json,packages/pkg-b/tsconfig.json} |
180 | | -``` |
| 135 | +* **Node.js**: Requires 23.6.0+ (v3.0+). |
| 136 | +* **TypeScript**: Incompatible with `typescript-go` (v7) as it does not support Language Service Plugins. |
| 137 | + |
| 138 | +## License |
181 | 139 |
|
182 | | -This command will run the linter on all TypeScript projects located in the subdirectories of the `packages` directory. Each subdirectory should contain a `tsconfig.json` file defining a TypeScript project. Any linting errors will be output to the console. |
183 | | - |
184 | | -### Linting Different Project Types |
185 | | - |
186 | | -TSSLint also supports linting different types of projects, such as Vue, Vue Vine, MDX, and Astro. You can specify the project type using the relevant flags: |
187 | | - |
188 | | -- **Vue projects**: |
189 | | - ```sh |
190 | | - npx tsslint --vue-project path/to/vue/tsconfig.json |
191 | | - ``` |
192 | | -- **Vue Vine projects**: |
193 | | - ```sh |
194 | | - npx tsslint --vue-vine-project path/to/vue-vine/tsconfig.json |
195 | | - ``` |
196 | | -- **MDX projects**: |
197 | | - ```sh |
198 | | - npx tsslint --mdx-project path/to/mdx/tsconfig.json |
199 | | - ``` |
200 | | -- **Astro projects**: |
201 | | - ```sh |
202 | | - npx tsslint --astro-project path/to/astro/tsconfig.json |
203 | | - ``` |
204 | | -- **TS Macro projects**: |
205 | | - ```sh |
206 | | - npx tsslint --ts-macro-project path/to/ts-macro/tsconfig.json |
207 | | - ``` |
208 | | - |
209 | | -This allows flexibility in linting different project structures while maintaining the same CLI workflow. |
| 140 | +[MIT](LICENSE) |
0 commit comments