flatlint 1.18.0 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ChangeLog +10 -0
- package/README.md +15 -0
- package/lib/compare/collect-args.js +33 -0
- package/lib/compare/compare.js +19 -2
- package/lib/flatlint.js +1 -1
- package/lib/plugins/convert-comma-to-semicolon/index.js +15 -8
- package/lib/plugins/remove-useless-coma/index.js +18 -0
- package/lib/runner/path.js +6 -5
- package/lib/runner/replacer.js +24 -11
- package/lib/runner/runner.js +13 -9
- package/lib/types/types.js +14 -1
- package/package.json +1 -1
package/ChangeLog
CHANGED
package/README.md
CHANGED
|
@@ -121,6 +121,17 @@ const a = {
|
|
|
121
121
|
|
|
122
122
|
</details>
|
|
123
123
|
|
|
124
|
+
<details><summary>Remove useless coma</summary>
|
|
125
|
+
|
|
126
|
+
```diff
|
|
127
|
+
const a = class {
|
|
128
|
+
- b() {},
|
|
129
|
+
+ b() {}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
</details>
|
|
134
|
+
|
|
124
135
|
## Template literals
|
|
125
136
|
|
|
126
137
|
**FlatLint** uses language similar to 🐊[**PutoutScript**](https://github.com/coderaiser/putout/blob/master/docs/putout-script.md#-putoutscript).
|
|
@@ -140,6 +151,10 @@ it can be only one quote `'__a` - this is valid, since **FlatLint** is tokens ba
|
|
|
140
151
|
Collects everything that looks like array elements, it can start from squire brace `[__array;`, but that's not important
|
|
141
152
|
to end with it, since it used to fix error patterns.
|
|
142
153
|
|
|
154
|
+
### `__args`
|
|
155
|
+
|
|
156
|
+
Collects arguments of function when exists.
|
|
157
|
+
|
|
143
158
|
### `__expr`
|
|
144
159
|
|
|
145
160
|
Collects everything that looks like expression.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CloseRoundBrace,
|
|
3
|
+
OpenRoundBrace,
|
|
4
|
+
Punctuator,
|
|
5
|
+
} from '#types';
|
|
6
|
+
import {equal} from './equal.js';
|
|
7
|
+
|
|
8
|
+
export const collectArgs = ({currentTokenIndex, tokens, nextTemplateToken = Punctuator(')')}) => {
|
|
9
|
+
const n = tokens.length;
|
|
10
|
+
const brace = Punctuator(']');
|
|
11
|
+
let index = currentTokenIndex;
|
|
12
|
+
|
|
13
|
+
if (equal(tokens[index - 1], OpenRoundBrace) && equal(tokens[index], CloseRoundBrace))
|
|
14
|
+
return [false];
|
|
15
|
+
|
|
16
|
+
for (; index < n; index++) {
|
|
17
|
+
const token = tokens[index];
|
|
18
|
+
|
|
19
|
+
if (equal(token, CloseRoundBrace))
|
|
20
|
+
break;
|
|
21
|
+
|
|
22
|
+
if (equal(token, nextTemplateToken))
|
|
23
|
+
break;
|
|
24
|
+
|
|
25
|
+
if (equal(token, brace))
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return [
|
|
30
|
+
true,
|
|
31
|
+
--index,
|
|
32
|
+
];
|
|
33
|
+
};
|
package/lib/compare/compare.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {prepare} from '#parser';
|
|
2
2
|
import {
|
|
3
|
+
isTemplateArgsToken,
|
|
3
4
|
isTemplateArrayToken,
|
|
4
5
|
isTemplateExpressionToken,
|
|
5
6
|
} from '#types';
|
|
6
7
|
import {collectArray} from './collect-array.js';
|
|
7
8
|
import {collectExpression} from './collect-expression.js';
|
|
9
|
+
import {collectArgs} from './collect-args.js';
|
|
8
10
|
import {
|
|
9
11
|
equal,
|
|
10
12
|
equalAny,
|
|
@@ -23,10 +25,11 @@ export const compare = (source, template) => {
|
|
|
23
25
|
let start = 0;
|
|
24
26
|
let end = 0;
|
|
25
27
|
let delta = 0;
|
|
28
|
+
let skip = 0;
|
|
26
29
|
|
|
27
30
|
for (let index = 0; index < n; index++) {
|
|
28
31
|
for (let templateIndex = 0; templateIndex < templateTokensLength; templateIndex++) {
|
|
29
|
-
const currentTokenIndex = index + templateIndex;
|
|
32
|
+
const currentTokenIndex = index + templateIndex - skip;
|
|
30
33
|
|
|
31
34
|
if (currentTokenIndex > n) {
|
|
32
35
|
isEqual = false;
|
|
@@ -36,7 +39,21 @@ export const compare = (source, template) => {
|
|
|
36
39
|
const templateToken = templateTokens[templateIndex];
|
|
37
40
|
const currentToken = tokens[currentTokenIndex];
|
|
38
41
|
|
|
39
|
-
if (
|
|
42
|
+
if (isTemplateArgsToken(templateToken)) {
|
|
43
|
+
const [ok, end] = collectArgs({
|
|
44
|
+
currentTokenIndex,
|
|
45
|
+
tokens,
|
|
46
|
+
templateToken,
|
|
47
|
+
nextTemplateToken: templateTokens[templateIndex + 1],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (!ok) {
|
|
51
|
+
++skip;
|
|
52
|
+
} else {
|
|
53
|
+
delta = end - currentTokenIndex;
|
|
54
|
+
index = end - templateIndex;
|
|
55
|
+
}
|
|
56
|
+
} else if (isTemplateExpressionToken(templateToken)) {
|
|
40
57
|
const indexOfExpressionEnd = collectExpression({
|
|
41
58
|
currentTokenIndex,
|
|
42
59
|
tokens,
|
package/lib/flatlint.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
return 'Use semicolon instead of trailing comma';
|
|
3
|
-
}
|
|
1
|
+
import {isIdentifier} from '#types';
|
|
4
2
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
export const report = () => 'Use semicolon instead of trailing comma';
|
|
4
|
+
export const match = () => ({
|
|
5
|
+
'__a: __expr;': (vars, path) => {
|
|
6
|
+
for (const token of path.getPrev()) {
|
|
7
|
+
if (isIdentifier(token, 'interface'))
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return true;
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export const replace = () => ({
|
|
15
|
+
'const __a = __b,': 'const __a = __b;',
|
|
16
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {isIdentifier} from '#types';
|
|
2
|
+
|
|
3
|
+
export const report = () => 'Remove useless coma';
|
|
4
|
+
|
|
5
|
+
export const match = () => ({
|
|
6
|
+
'__a(__args) {},': (vars, path) => {
|
|
7
|
+
for (const token of path.getPrev()) {
|
|
8
|
+
if (isIdentifier(token, 'class'))
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return false;
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const replace = () => ({
|
|
17
|
+
'__a(__args) {},': '__a(__args) {}',
|
|
18
|
+
});
|
package/lib/runner/path.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export const createPath = ({tokens, start
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
1
|
+
export const createPath = ({tokens, start}) => ({
|
|
2
|
+
getPrev: createGetPrev({
|
|
3
|
+
tokens,
|
|
4
|
+
start,
|
|
5
|
+
}),
|
|
6
|
+
});
|
|
6
7
|
|
|
7
8
|
function createGetPrev({tokens, start}) {
|
|
8
9
|
return function*() {
|
package/lib/runner/replacer.js
CHANGED
|
@@ -3,42 +3,42 @@ import {prepare} from '#parser';
|
|
|
3
3
|
import {traverse} from '#traverser';
|
|
4
4
|
import {
|
|
5
5
|
is,
|
|
6
|
+
isTemplateArgs,
|
|
6
7
|
isTemplateArray,
|
|
7
8
|
isTemplateExpression,
|
|
8
9
|
} from '#types';
|
|
9
10
|
import {collectArray} from '../compare/collect-array.js';
|
|
10
11
|
import {collectExpression} from '../compare/collect-expression.js';
|
|
11
12
|
import {createPath} from './path.js';
|
|
13
|
+
import {collectArgs} from '../compare/collect-args.js';
|
|
12
14
|
|
|
13
15
|
const returns = (a) => () => a;
|
|
14
16
|
const {entries} = Object;
|
|
15
17
|
|
|
16
18
|
export const match = (tokens, {plugin}) => {
|
|
17
19
|
const match = plugin.match ?? returns([]);
|
|
20
|
+
let result = true;
|
|
18
21
|
|
|
19
22
|
for (const [from, fn] of entries(match())) {
|
|
20
|
-
const [
|
|
21
|
-
|
|
22
|
-
if (!ok)
|
|
23
|
-
continue;
|
|
24
|
-
|
|
23
|
+
const [, start, end] = compare(tokens, from);
|
|
25
24
|
const current = tokens.slice(start, end);
|
|
26
25
|
|
|
27
26
|
const waysFrom = findVarsWays(prepare(from));
|
|
28
27
|
const values = getValues(current, waysFrom);
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
result = fn(values, createPath({
|
|
31
30
|
tokens,
|
|
32
31
|
start,
|
|
33
32
|
end,
|
|
34
33
|
}));
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
return
|
|
36
|
+
return result;
|
|
38
37
|
};
|
|
39
38
|
|
|
40
39
|
export const replace = (tokens, {fix, rule, plugin}) => {
|
|
41
40
|
const places = [];
|
|
41
|
+
let isFixed = false;
|
|
42
42
|
|
|
43
43
|
for (const [from, to] of entries(plugin.replace())) {
|
|
44
44
|
const [ok, start, end] = compare(tokens, from);
|
|
@@ -53,6 +53,7 @@ export const replace = (tokens, {fix, rule, plugin}) => {
|
|
|
53
53
|
start,
|
|
54
54
|
end,
|
|
55
55
|
});
|
|
56
|
+
isFixed = true;
|
|
56
57
|
continue;
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -67,7 +68,7 @@ export const replace = (tokens, {fix, rule, plugin}) => {
|
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
return places;
|
|
71
|
+
return [isFixed, places];
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
function runReplace(tokens, {from, to, start, end}) {
|
|
@@ -113,16 +114,29 @@ function getValues(tokens, waysFrom) {
|
|
|
113
114
|
for (const [name, index] of entries(waysFrom)) {
|
|
114
115
|
let end = index;
|
|
115
116
|
|
|
116
|
-
if (isTemplateArray(name))
|
|
117
|
+
if (isTemplateArray(name)) {
|
|
117
118
|
end = collectArray({
|
|
118
119
|
currentTokenIndex: index,
|
|
119
120
|
tokens,
|
|
120
121
|
});
|
|
121
|
-
else if (isTemplateExpression(name))
|
|
122
|
+
} else if (isTemplateExpression(name)) {
|
|
122
123
|
end = collectExpression({
|
|
123
124
|
currentTokenIndex: index,
|
|
124
125
|
tokens,
|
|
125
126
|
});
|
|
127
|
+
} else if (isTemplateArgs(name)) {
|
|
128
|
+
let ok = false;
|
|
129
|
+
|
|
130
|
+
[ok, end] = collectArgs({
|
|
131
|
+
currentTokenIndex: index,
|
|
132
|
+
tokens,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (!ok) {
|
|
136
|
+
values[name] = [];
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
126
140
|
|
|
127
141
|
values[name] = tokens.slice(index, end + 1);
|
|
128
142
|
}
|
|
@@ -135,4 +149,3 @@ function setValues({to, waysTo, values}) {
|
|
|
135
149
|
to.splice(index, 1, ...values[name]);
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
|
-
|
package/lib/runner/runner.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import {match, replace} from './replacer.js';
|
|
2
2
|
|
|
3
|
-
export const run = (tokens, {fix, fixCount = 10, plugins}) => {
|
|
3
|
+
export const run = (tokens, {fix, fixCount = fix ? 10 : 1, plugins}) => {
|
|
4
4
|
const places = [];
|
|
5
5
|
|
|
6
6
|
while (--fixCount >= 0) {
|
|
7
|
+
const fixed = [];
|
|
8
|
+
|
|
7
9
|
for (const {rule, plugin} of plugins) {
|
|
8
|
-
if (match(tokens, {plugin}))
|
|
9
|
-
|
|
10
|
+
if (match(tokens, {plugin})) {
|
|
11
|
+
const [isFixed, newPlaces] = replace(tokens, {
|
|
10
12
|
fix,
|
|
11
13
|
rule,
|
|
12
14
|
plugin,
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
break;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
fixed.push(isFixed);
|
|
18
|
+
places.push(...newPlaces);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
21
|
+
|
|
22
|
+
if (!fixed.filter(Boolean).length)
|
|
23
|
+
break;
|
|
20
24
|
}
|
|
21
25
|
|
|
22
|
-
return
|
|
26
|
+
return places;
|
|
23
27
|
};
|
package/lib/types/types.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const isString = (a) => typeof a === 'string';
|
|
2
2
|
|
|
3
3
|
export const isWhiteSpace = ({type}) => type === 'WhiteSpace';
|
|
4
|
-
export const isIdentifier = (
|
|
4
|
+
export const isIdentifier = (token, value = token.value) => {
|
|
5
|
+
const {type} = token;
|
|
6
|
+
const is = type === 'IdentifierName';
|
|
7
|
+
|
|
8
|
+
if (!is)
|
|
9
|
+
return false;
|
|
10
|
+
|
|
11
|
+
return value === token.value;
|
|
12
|
+
};
|
|
13
|
+
|
|
5
14
|
export const isStringLiteral = ({type}) => type === 'StringLiteral';
|
|
6
15
|
export const isNumericLiteral = ({type}) => type === 'NumericLiteral';
|
|
7
16
|
export const isNewLine = ({type}) => type === 'LineTerminatorSequence';
|
|
@@ -32,11 +41,13 @@ const ANY = '__';
|
|
|
32
41
|
const QUOTE = /^['"]$/;
|
|
33
42
|
const ARRAY = '__array';
|
|
34
43
|
const EXPR = '__expr';
|
|
44
|
+
const ARGS = '__args';
|
|
35
45
|
|
|
36
46
|
const ALL = [
|
|
37
47
|
ANY,
|
|
38
48
|
LINKED_NODE,
|
|
39
49
|
ARRAY,
|
|
50
|
+
ARGS,
|
|
40
51
|
EXPR,
|
|
41
52
|
];
|
|
42
53
|
|
|
@@ -51,8 +62,10 @@ export const isId = (a) => LINKED_NODE.test(a);
|
|
|
51
62
|
export const isQuote = (a) => QUOTE.test(a);
|
|
52
63
|
export const isTemplateArray = (a) => a === ARRAY;
|
|
53
64
|
export const isTemplateExpression = (a) => a === EXPR;
|
|
65
|
+
export const isTemplateArgs = (a) => a === ARGS;
|
|
54
66
|
export const isTemplateArrayToken = (a) => isIdentifier(a) && isTemplateArray(a.value);
|
|
55
67
|
export const isTemplateExpressionToken = (a) => isIdentifier(a) && isTemplateExpression(a.value);
|
|
68
|
+
export const isTemplateArgsToken = (a) => isIdentifier(a) && isTemplateArgs(a.value);
|
|
56
69
|
|
|
57
70
|
export const OpenRoundBrace = Punctuator('(');
|
|
58
71
|
export const CloseRoundBrace = Punctuator(')');
|