Skip to content

Commit 0c7ae18

Browse files
committed
First commit
0 parents  commit 0c7ae18

20 files changed

+1175
-0
lines changed

.editorconfig

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = tab
6+
indent_size = 4
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = false

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
.DS_Store
4+
*.log

.npmignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
!dist/
2+
*.log
3+
npm-debug.log*
4+
tsconfig.json
5+
tslint.json
6+
test

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2019 Jens Duttke
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
[![npm version](https://badge.fury.io/js/tslint-filter.svg)](https://badge.fury.io/js/tslint-filter)
2+
[![Known Vulnerabilities](https://snyk.io/test/github/jens-duttke/tslint-filter/badge.svg?targetFile=package.json)](https://snyk.io/test/github/jens-duttke/tslint-filter?targetFile=package.json)
3+
4+
# TSLint-Filter
5+
Suppress and modify TSLint warnings, before they get returned to the console or your code editor.
6+
7+
Many TSLint rules are very limited by their configurability, and some rules looks like they are not thought to the end.
8+
9+
For example, I want to prevent the usage of "I" as prefix for interface names. The TSLint rule for that is called "interface-name".
10+
Unfortunately, this rule also shows an error for "I18N", which is an absolutely valid interface name for me.
11+
12+
Or, in my React projects I want to get a warning, if I forgot to specify a Components class method as `private` or `public` using the "member-access" rule. But for the React methods `componentDidMount`, `render`, `componentDidUpdate` etc. I don't want to specify that, because they are always public.
13+
Unfortunately, by now, it's not possible to specify a whitelist here.
14+
15+
Or, I want to prefer conditional expressions for small, simple alignments, but "prefer-conditional-expression" also complains about complex statements, which wouldn't be easy readable in a single line, because this line would have a size of 300 characters or more.
16+
Why isn't there a way to show the warning only, if the conditional expression would be a ...let's say... less-than-120-chars-one-liner?
17+
Using TSLint Filter, you have to possibility to easily extend existing rules and suppress specific warnings, based on regular expressions.
18+
19+
It's even possible to use integer ranges in these regular expression, to filter by a range of numbers in the error message.
20+
21+
**Table of Contents**
22+
23+
- [Installation](#installation)
24+
- [Basic Usage](#basic-usage)
25+
- [Extended usage](#extended-usage)
26+
- [Location of rule directories](#location-of-rule-directories)
27+
- [Rule file names](#rule-file-names)
28+
29+
## Installation
30+
31+
Install with npm
32+
33+
```sh
34+
npm install tslint-filter --save-dev
35+
```
36+
37+
## Basic Usage
38+
39+
Since TSLint does not provide an easy way to modify warnings before they get returned, we need to create own rules, with TSLint-Filter as wrapper for the orignal rule.
40+
41+
But that's very easy:
42+
43+
Create a folder for custom rules in your project folder
44+
45+
In this folder create a JavaScript file like:
46+
```javascript
47+
module.exports = require('tslint-filter')('tslint/lib/rules/memberAccessRule');
48+
```
49+
"tslint/lib/rules/memberAccessRule" is the name of the original rule, which you want to extend.
50+
51+
You can name the file to whatever you want, but it must end with "Rule.js". I prefer th use the name of the original rule, and prefix it with "___", so in this case "___memberAccessRule.js".
52+
53+
In your `tslint.json` add the folder to the "rulesDirectory" section:
54+
```json
55+
{
56+
"rulesDirectory": [
57+
"script/custom-tslint-rules"
58+
],
59+
"rules": {
60+
...
61+
}
62+
}
63+
```
64+
65+
Now, instead of using the rule "member-access", I'm able to use the rule "___member-access".
66+
The last parameter **must** be always an array with regular expressions. Warnings which match these expressions will be ignored.
67+
68+
```json
69+
"___member-access": [true, [
70+
"'(getDerivedStateFromProps|componentDidMount|shouldComponentUpdate|render|getSnapshotBeforeUpdate|componentDidUpdate|componentWillUnmount)'"
71+
]],
72+
```
73+
74+
## Extended usage
75+
76+
Beside simply ignoring warnings, you can also manipulate them. You can change the message, implement a fix or whatever you like.
77+
78+
For example, we want to extend the "interface-name" rule, to allow the interface name "I18N", even if it starts with "I".
79+
Unfortunately, the message of this rule does not provide the name of the interface, so first, we have to include the name into the message:
80+
```javascript
81+
const Utils = require('tsutils');
82+
83+
module.exports = require('tslint-filter')('tslint/lib/rules/interfaceNameRule', {
84+
modifyFailure (failure) {
85+
const node = Utils.getTokenAtPosition(failure.sourceFile, failure.getStartPosition().getPosition());
86+
87+
if (node.escapedText) {
88+
failure.failure = `Interface name "${node.escapedText}" must not have an "I" prefix`;
89+
}
90+
91+
return failure;
92+
}
93+
});
94+
```
95+
96+
Now you can ignore interface names, starting with "I" followed by an digit:
97+
```json
98+
"___interface-name": [true, "never-prefix", [
99+
"Interface name \"I[\\d]"
100+
]],
101+
```
102+
103+
104+
For example the "prefer-conditional-expression" rule could be extended to show the approximated number of characters you could save, and also the approximated size if you write the statement as conditional expression:
105+
106+
```javascript
107+
const Utils = require('tsutils');
108+
109+
module.exports = require('tslint-filter')('tslint/lib/rules/preferConditionalExpressionRule', {
110+
modifyFailure (failure) {
111+
const match = failure.getFailure().match(/'([^\0]+)'/);
112+
113+
if (match !== null) {
114+
const node = Utils.getTokenAtPosition(failure.sourceFile, failure.getStartPosition().getPosition());
115+
const parent = node.parent;
116+
117+
const originalSize = (parent.end - parent.pos);
118+
119+
const assigneeLength = match[1].length;
120+
const expressionLength = parent.expression.end - parent.expression.pos;
121+
const thenStatementLength = parent.thenStatement.getText().replace(/^{?[\s\n]+|[\s\n]+}?$/g, '').length;
122+
const elseStatementLength = parent.elseStatement.getText().replace(/^{?[\s\n]+|[\s\n]+}?$/g, '').length;
123+
124+
// That's only an approximated size, depending on the wrapping characters
125+
newLength = expressionLength + thenStatementLength + elseStatementLength - assigneeLength + 1;
126+
127+
if (newLength > originalSize) {
128+
return;
129+
}
130+
131+
failure.failure = `${failure.failure} (save about ${originalSize - newLength} characters, conditional expression size would be about ${newLength} characters)`
132+
}
133+
134+
return failure;
135+
}
136+
});
137+
```
138+
Save this file under the name "___preferConditionalExpressionRule.js" in your custom rule folder.
139+
140+
Now you can use this pattern, to prevent warnings, where the conditional expression size would be 120 characters or more:
141+
```json
142+
"___prefer-conditional-expression": [true, "check-else-if", [
143+
"conditional expression size would be about [120...]"
144+
]],
145+
```
146+
147+
Ranges can be specified with:
148+
| RegExp character sets | Meaning
149+
|---|---
150+
| `[-5...5]` | Any integer number from -5 to 5
151+
| `[...100]` | Any integer number from -999999999999999 to 100
152+
| `[10...]` | Any integer number from 10 to 999999999999999
153+
| `[...]` | Any integer number from -999999999999999 to 999999999999999, but if possible you should prefer `-?\d+`
154+
155+
`-999999999999999` and `999999999999999` are required, because the expression is converted into a valid RegExp, and here we always need to specify a range.
156+
These numbers are choosen because they are very near to Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER, but the RegExp representation is still very short.
157+
158+
## Location of rule directories
159+
160+
> It's an open to-do to determine the directory and rule file name automatically, based on the `rulesDirectory`, but I havn't found an easy way to do that yet.
161+
162+
So, for now, it's required to specify the whole path to the rule, instead of just using the rule name.
163+
164+
| Rule Package | Directory | Number of rules\*
165+
|---|---|---:
166+
| [tslint](https://www.npmjs.com/package/tslint) | tslint/lib/rules/ | 152
167+
| [tslint-microsoft-contrib](https://www.npmjs.com/package/tslint-microsoft-contrib) | tslint-microsoft-contrib/ | 93
168+
| [tslint-sonarts](https://www.npmjs.com/package/tslint-sonarts) | tslint-sonarts/lib/rules/ | 62
169+
| [tslint-eslint-rules](https://www.npmjs.com/package/tslint-eslint-rules) | tslint-eslint-rules/dist/rules/ | 38
170+
| [rxjs-tslint-rules](https://www.npmjs.com/package/rxjs-tslint-rules) | rxjs-tslint-rules/dist/rules/ | 37
171+
| [tslint-consistent-codestyle](https://www.npmjs.com/package/tslint-consistent-codestyle) | tslint-consistent-codestyle/rules/ | 19
172+
| [tslint-config-security](https://www.npmjs.com/package/tslint-config-security) | tslint-config-security/dist/rules/ | 16
173+
| [tslint-immutable](https://www.npmjs.com/package/tslint-immutable) | tslint-immutable/rules/ | 15
174+
| [tslint-misc-rules](https://www.npmjs.com/package/tslint-misc-rules) | tslint-misc-rules/rules/ | 15
175+
| [tslint-react](https://www.npmjs.com/package/tslint-react) | tslint-react/rules/ | 15
176+
| [tslint-clean-code](https://www.npmjs.com/package/tslint-clean-code) | tslint-clean-code/dist/src/ | 12
177+
| [tslint-stencil](https://www.npmjs.com/package/tslint-stencil) | tslint-stencil/rules/ | 7
178+
| [vrsource-tslint-rules](https://www.npmjs.com/package/vrsource-tslint-rules) | vrsource-tslint-rules/rules/ | 7
179+
| [rxjs-tslint](https://www.npmjs.com/package/rxjs-tslint) | rxjs-tslint/ | 4
180+
| [tslint-jasmine-rules](https://www.npmjs.com/package/tslint-jasmine-rules) | tslint-jasmine-rules/dist/ | 3
181+
| [tslint-defocus](https://www.npmjs.com/package/tslint-defocus) | tslint-defocus/dist/ | 1
182+
| [tslint-lines-between-class-members](https://www.npmjs.com/package/tslint-lines-between-class-members) | tslint-lines-between-class-members/ | 1
183+
| [tslint-no-unused-expression-chai](https://www.npmjs.com/package/tslint-no-unused-expression-chai) | tslint-no-unused-expression-chai/rules/ | 1
184+
| [tslint-origin-ordered-imports-rule](https://www.npmjs.com/package/tslint-origin-ordered-imports-rule) | tslint-origin-ordered-imports-rule/dist/ | 1
185+
| [tslint-plugin-prettier](https://www.npmjs.com/package/tslint-plugin-prettier) | tslint-plugin-prettier/rules/ | 1
186+
187+
\* as of 2019-01-16. List ordered by number of rules.
188+
189+
## Rule file names
190+
191+
Dashes in the file names are converted to camel case, but leading and trailing dashes are keeped. "Rule" is appeneded
192+
193+
So, the rule name `-ab-cd-ef-` is located in the file `-abCdEf-Rule`.

0 commit comments

Comments
 (0)