flatlint 1.19.0 → 1.21.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 CHANGED
@@ -1,3 +1,13 @@
1
+ 2025.01.03, v1.21.0
2
+
3
+ feature:
4
+ - d13da41 flatlint: add-missing-squire-brace: empty array
5
+
6
+ 2025.01.03, v1.20.0
7
+
8
+ feature:
9
+ - a6d8bd6 remove-useless-comma: use __args
10
+
1
11
  2025.01.02, v1.19.0
2
12
 
3
13
  feature:
package/README.md CHANGED
@@ -151,6 +151,10 @@ it can be only one quote `'__a` - this is valid, since **FlatLint** is tokens ba
151
151
  Collects everything that looks like array elements, it can start from squire brace `[__array;`, but that's not important
152
152
  to end with it, since it used to fix error patterns.
153
153
 
154
+ ### `__args`
155
+
156
+ Collects arguments of function when exists.
157
+
154
158
  ### `__expr`
155
159
 
156
160
  Collects everything that looks like expression.
@@ -0,0 +1,34 @@
1
+ import {
2
+ closeRoundBrace,
3
+ closeSquareBrace,
4
+ NOT_OK,
5
+ OK,
6
+ OpenRoundBrace,
7
+ } from '#types';
8
+ import {equal} from './equal.js';
9
+
10
+ export const collectArgs = ({currentTokenIndex, tokens, nextTemplateToken = closeRoundBrace}) => {
11
+ const n = tokens.length;
12
+ let index = currentTokenIndex;
13
+
14
+ if (equal(tokens[index - 1], OpenRoundBrace) && equal(tokens[index], closeRoundBrace))
15
+ return [NOT_OK];
16
+
17
+ for (; index < n; index++) {
18
+ const token = tokens[index];
19
+
20
+ if (equal(token, closeRoundBrace))
21
+ break;
22
+
23
+ if (equal(token, nextTemplateToken))
24
+ break;
25
+
26
+ if (equal(token, closeSquareBrace))
27
+ break;
28
+ }
29
+
30
+ return [
31
+ OK,
32
+ --index,
33
+ ];
34
+ };
@@ -1,26 +1,34 @@
1
1
  import {
2
- CloseRoundBrace,
3
- Punctuator,
2
+ closeRoundBrace,
3
+ closeSquareBrace,
4
+ semicolon,
5
+ OK,
6
+ NOT_OK,
4
7
  } from '#types';
5
8
  import {equal} from './equal.js';
6
9
 
7
- export const collectArray = ({currentTokenIndex, tokens, nextTemplateToken = Punctuator(';')}) => {
10
+ export const collectArray = ({currentTokenIndex, tokens, nextTemplateToken = semicolon}) => {
8
11
  const n = tokens.length;
9
- const brace = Punctuator(']');
10
12
  let index = currentTokenIndex;
11
13
 
14
+ if (equal(tokens[index], closeSquareBrace))
15
+ return [NOT_OK];
16
+
12
17
  for (; index < n; index++) {
13
18
  const token = tokens[index];
14
19
 
15
- if (equal(token, CloseRoundBrace))
20
+ if (equal(token, closeRoundBrace))
16
21
  break;
17
22
 
18
23
  if (equal(token, nextTemplateToken))
19
24
  break;
20
25
 
21
- if (equal(token, brace))
26
+ if (equal(token, closeSquareBrace))
22
27
  break;
23
28
  }
24
29
 
25
- return --index;
30
+ return [
31
+ OK,
32
+ --index,
33
+ ];
26
34
  };
@@ -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 (isTemplateExpressionToken(templateToken)) {
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,
@@ -47,15 +64,19 @@ export const compare = (source, template) => {
47
64
  delta = indexOfExpressionEnd - currentTokenIndex;
48
65
  index = indexOfExpressionEnd - templateIndex;
49
66
  } else if (isTemplateArrayToken(templateToken)) {
50
- const indexOfArrayEnd = collectArray({
67
+ const [ok, indexOfArrayEnd] = collectArray({
51
68
  currentTokenIndex,
52
69
  tokens,
53
70
  templateToken,
54
71
  nextTemplateToken: templateTokens[templateIndex + 1],
55
72
  });
56
73
 
57
- delta = indexOfArrayEnd - currentTokenIndex;
58
- index = indexOfArrayEnd - templateIndex;
74
+ if (!ok) {
75
+ ++skip;
76
+ } else {
77
+ delta = indexOfArrayEnd - currentTokenIndex;
78
+ index = indexOfArrayEnd - templateIndex;
79
+ }
59
80
  } else if (!compareAll(currentToken, templateToken)) {
60
81
  isEqual = false;
61
82
  break;
package/lib/flatlint.js CHANGED
@@ -10,7 +10,7 @@ export function lint(source, overrides = {}) {
10
10
 
11
11
  const tokens = parse(source);
12
12
 
13
- const [places] = run(tokens, {
13
+ const places = run(tokens, {
14
14
  plugins,
15
15
  fix,
16
16
  });
@@ -3,7 +3,7 @@ import {isIdentifier} from '#types';
3
3
  export const report = () => 'Remove useless coma';
4
4
 
5
5
  export const match = () => ({
6
- '__a() {},': (vars, path) => {
6
+ '__a(__args) {},': (vars, path) => {
7
7
  for (const token of path.getPrev()) {
8
8
  if (isIdentifier(token, 'class'))
9
9
  return true;
@@ -14,5 +14,5 @@ export const match = () => ({
14
14
  });
15
15
 
16
16
  export const replace = () => ({
17
- '__a() {},': '__a() {}',
17
+ '__a(__args) {},': '__a(__args) {}',
18
18
  });
@@ -1,8 +1,9 @@
1
- export const createPath = ({tokens, start, end}) => {
2
- return {
3
- getPrev: createGetPrev({tokens, start}),
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*() {
@@ -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 [ok, start, end] = compare(tokens, from);
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
- return fn(values, createPath({
29
+ result = fn(values, createPath({
31
30
  tokens,
32
31
  start,
33
32
  end,
34
33
  }));
35
34
  }
36
35
 
37
- return true;
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}) {
@@ -112,9 +113,10 @@ function getValues(tokens, waysFrom) {
112
113
 
113
114
  for (const [name, index] of entries(waysFrom)) {
114
115
  let end = index;
116
+ let ok = true;
115
117
 
116
118
  if (isTemplateArray(name))
117
- end = collectArray({
119
+ [ok, end] = collectArray({
118
120
  currentTokenIndex: index,
119
121
  tokens,
120
122
  });
@@ -123,6 +125,16 @@ function getValues(tokens, waysFrom) {
123
125
  currentTokenIndex: index,
124
126
  tokens,
125
127
  });
128
+ else if (isTemplateArgs(name))
129
+ [ok, end] = collectArgs({
130
+ currentTokenIndex: index,
131
+ tokens,
132
+ });
133
+
134
+ if (!ok) {
135
+ values[name] = [];
136
+ continue;
137
+ }
126
138
 
127
139
  values[name] = tokens.slice(index, end + 1);
128
140
  }
@@ -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
- places.push(...replace(tokens, {
10
+ if (match(tokens, {plugin})) {
11
+ const [isFixed, newPlaces] = replace(tokens, {
10
12
  fix,
11
13
  rule,
12
14
  plugin,
13
- }));
14
-
15
- if (!fix) {
16
- fixCount = 0;
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 [places];
26
+ return places;
23
27
  };
@@ -41,11 +41,13 @@ const ANY = '__';
41
41
  const QUOTE = /^['"]$/;
42
42
  const ARRAY = '__array';
43
43
  const EXPR = '__expr';
44
+ const ARGS = '__args';
44
45
 
45
46
  const ALL = [
46
47
  ANY,
47
48
  LINKED_NODE,
48
49
  ARRAY,
50
+ ARGS,
49
51
  EXPR,
50
52
  ];
51
53
 
@@ -60,8 +62,15 @@ export const isId = (a) => LINKED_NODE.test(a);
60
62
  export const isQuote = (a) => QUOTE.test(a);
61
63
  export const isTemplateArray = (a) => a === ARRAY;
62
64
  export const isTemplateExpression = (a) => a === EXPR;
65
+ export const isTemplateArgs = (a) => a === ARGS;
63
66
  export const isTemplateArrayToken = (a) => isIdentifier(a) && isTemplateArray(a.value);
64
67
  export const isTemplateExpressionToken = (a) => isIdentifier(a) && isTemplateExpression(a.value);
68
+ export const isTemplateArgsToken = (a) => isIdentifier(a) && isTemplateArgs(a.value);
65
69
 
66
70
  export const OpenRoundBrace = Punctuator('(');
67
- export const CloseRoundBrace = Punctuator(')');
71
+ export const closeRoundBrace = Punctuator(')');
72
+ export const closeSquareBrace = Punctuator(']');
73
+ export const semicolon = Punctuator(';');
74
+
75
+ export const OK = true;
76
+ export const NOT_OK = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatlint",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "JavaScript tokens-based linter",
5
5
  "main": "lib/flatlint.js",
6
6
  "type": "module",