flatlint 1.27.0 → 1.29.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,15 @@
1
+ 2025.01.09, v1.29.0
2
+
3
+ feature:
4
+ - 8d308ac flatlint: remove-useless-comma: curly
5
+ - f070e0c flatlint: convert-coma-to-semicolon: return
6
+
7
+ 2025.01.08, v1.28.0
8
+
9
+ feature:
10
+ - b1e6b43 remove-useless-comma: improve
11
+ - 3ebb93d add-missing-semicolon: improve
12
+
1
13
  2025.01.08, v1.27.0
2
14
 
3
15
  feature:
package/README.md CHANGED
@@ -24,6 +24,11 @@ npm i flatlint
24
24
  ```diff
25
25
  -const a = 5,
26
26
  +const a = 5;
27
+
28
+ function x() {
29
+ - return m,
30
+ + return m;
31
+ }
27
32
  ```
28
33
 
29
34
  </details>
@@ -106,6 +111,17 @@ const a = {
106
111
 
107
112
  </details>
108
113
 
114
+ <details><summary>remove useless comma</summary>
115
+
116
+ ```diff
117
+ function x() {
118
+ return m;
119
+ -},
120
+ +}
121
+ ```
122
+
123
+ </details>
124
+
109
125
  <details><summary>add missing quote</summary>
110
126
 
111
127
  ```diff
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  closeRoundBrace,
3
+ comma,
3
4
  openRoundBrace,
4
5
  semicolon,
5
6
  } from '#types';
@@ -15,6 +16,9 @@ export const collectExpression = ({currentTokenIndex, tokens, nextTemplateToken
15
16
  if (equal(token, semicolon))
16
17
  break;
17
18
 
19
+ if (equal(token, comma))
20
+ break;
21
+
18
22
  if (equal(token, nextTemplateToken))
19
23
  break;
20
24
 
@@ -3,6 +3,8 @@ import {
3
3
  isTemplateArgsToken,
4
4
  isTemplateArrayToken,
5
5
  isTemplateExpressionToken,
6
+ OK,
7
+ NOT_OK,
6
8
  } from '#types';
7
9
  import {collectArray} from './collect-array.js';
8
10
  import {collectExpression} from './collect-expression.js';
@@ -25,9 +27,10 @@ export const compare = (source, template, {index = 0} = {}) => {
25
27
  let start = 0;
26
28
  let end = 0;
27
29
  let delta = 0;
28
- let skip = 0;
29
30
 
30
31
  for (; index < n; index++) {
32
+ let skip = 0;
33
+
31
34
  for (let templateIndex = 0; templateIndex < templateTokensLength; templateIndex++) {
32
35
  let currentTokenIndex = index + templateIndex - skip;
33
36
 
@@ -58,7 +61,7 @@ export const compare = (source, template, {index = 0} = {}) => {
58
61
  nextTemplateToken: templateTokens[templateIndex + 1],
59
62
  });
60
63
 
61
- if (n === indexOfExpressionEnd) {
64
+ if (indexOfExpressionEnd >= n - 1) {
62
65
  end = indexOfExpressionEnd;
63
66
  continue;
64
67
  }
@@ -90,10 +93,14 @@ export const compare = (source, template, {index = 0} = {}) => {
90
93
  }
91
94
 
92
95
  if (isEqual)
93
- return [true, start, ++end];
96
+ return [
97
+ OK,
98
+ start,
99
+ ++end,
100
+ ];
94
101
  }
95
102
 
96
- return [false];
103
+ return [NOT_OK];
97
104
  };
98
105
 
99
106
  const comparators = [
@@ -0,0 +1,77 @@
1
+ import {traverse} from '#traverser';
2
+ import {prepare} from '#parser';
3
+ import {
4
+ is,
5
+ isTemplateArgs,
6
+ isTemplateArray,
7
+ isTemplateExpression,
8
+ } from '#types';
9
+ import {collectArray} from './collect-array.js';
10
+ import {collectExpression} from './collect-expression.js';
11
+ import {collectArgs} from './collect-args.js';
12
+
13
+ const {entries} = Object;
14
+
15
+ export function findVarsWays(tokens) {
16
+ const ways = {};
17
+
18
+ traverse(tokens, {
19
+ Identifier({value, index}) {
20
+ if (is(value))
21
+ ways[value] = index;
22
+ },
23
+ StringLiteral({value, index}) {
24
+ if (is(value))
25
+ ways[value] = index;
26
+ },
27
+ });
28
+
29
+ return ways;
30
+ }
31
+
32
+ export function getValues(tokens, waysFrom) {
33
+ const values = {};
34
+
35
+ for (const [name, index] of entries(waysFrom)) {
36
+ let end = index;
37
+ let ok = true;
38
+
39
+ if (isTemplateArray(name))
40
+ [ok, end] = collectArray({
41
+ currentTokenIndex: index,
42
+ tokens,
43
+ });
44
+ else if (isTemplateExpression(name))
45
+ end = collectExpression({
46
+ currentTokenIndex: index,
47
+ tokens,
48
+ });
49
+ else if (isTemplateArgs(name))
50
+ [ok, end] = collectArgs({
51
+ currentTokenIndex: index,
52
+ tokens,
53
+ });
54
+
55
+ if (!ok) {
56
+ values[name] = [];
57
+ continue;
58
+ }
59
+
60
+ values[name] = tokens.slice(index, end + 1);
61
+ }
62
+
63
+ return values;
64
+ }
65
+
66
+ export function setValues({to, waysTo, values}) {
67
+ for (const [name, index] of entries(waysTo)) {
68
+ to.splice(index, 1, ...values[name]);
69
+ }
70
+ }
71
+
72
+ export function getCurrentValues({from, start, end, tokens}) {
73
+ const current = tokens.slice(start, end);
74
+ const waysFrom = findVarsWays(prepare(from));
75
+
76
+ return getValues(current, waysFrom);
77
+ }
@@ -1,9 +1,22 @@
1
- import {closeRoundBrace} from '#types';
1
+ import {
2
+ closeRoundBrace,
3
+ isPunctuator,
4
+ } from '#types';
2
5
 
3
6
  export const report = () => 'Add missing round braces';
4
7
 
5
8
  export const match = () => ({
6
- '__a(__args': (vars, path) => !path.isNextPunctuator(closeRoundBrace),
9
+ '__a(__args': (vars, path) => {
10
+ if (path.isCurrentPunctuator(closeRoundBrace))
11
+ return false;
12
+
13
+ for (const token of path.getAllNext()) {
14
+ if (isPunctuator(token, closeRoundBrace))
15
+ return false;
16
+ }
17
+
18
+ return true;
19
+ },
7
20
  });
8
21
 
9
22
  export const replace = () => ({
@@ -17,7 +17,10 @@ export const replace = () => ({
17
17
  });
18
18
 
19
19
  function check(vars, path) {
20
- if (path.isEndsWithPunctuator(comma))
20
+ if (path.isNextPunctuator(comma))
21
+ return false;
22
+
23
+ if (path.isNextPunctuator(semicolon))
21
24
  return false;
22
25
 
23
26
  if (path.isPrevIdentifier('function'))
@@ -1,4 +1,19 @@
1
+ import {colon, isPunctuator} from '#types';
2
+
1
3
  export const report = () => 'Use semicolon instead of trailing comma';
4
+ export const match = () => ({
5
+ '__a(__args),': (vars, path) => {
6
+ for (const token of path.getAllPrev()) {
7
+ if (isPunctuator(token, colon))
8
+ return false;
9
+ }
10
+
11
+ return true;
12
+ },
13
+ });
14
+
2
15
  export const replace = () => ({
3
16
  'const __a = __b,': 'const __a = __b;',
17
+ '__a(__args),': '__a(__args);',
18
+ 'return __expr,': 'return __expr;',
4
19
  });
@@ -0,0 +1,36 @@
1
+ import {
2
+ isIdentifier,
3
+ isPunctuator,
4
+ openCurlyBrace,
5
+ openSquireBrace,
6
+ } from '#types';
7
+
8
+ export const report = () => 'Remove useless coma';
9
+
10
+ export const match = () => ({
11
+ '__a(__args) {},': (vars, path) => {
12
+ for (const token of path.getAllPrev()) {
13
+ if (isIdentifier(token, 'class'))
14
+ return true;
15
+ }
16
+
17
+ return false;
18
+ },
19
+ '},': (vars, path) => {
20
+ for (const token of path.getAllPrev()) {
21
+ if (isPunctuator(token, openSquireBrace))
22
+ return false;
23
+ }
24
+
25
+ if (path.isNextIdentifier('const'))
26
+ return true;
27
+
28
+ return !path.isNextPunctuator(openCurlyBrace);
29
+ },
30
+ });
31
+
32
+ export const replace = () => ({
33
+ '__a(__args) {},': '__a(__args) {}',
34
+ '__a(),': '__a()',
35
+ '},': '}',
36
+ });
package/lib/plugins.js CHANGED
@@ -2,20 +2,22 @@ import * as wrapAssignmentInParens from './plugins/wrap-assignment-in-parens/ind
2
2
  import * as addMissingRoundBraces from './plugins/add-missing-round-braces/index.js';
3
3
  import * as addMissingSquireBrace from './plugins/add-missing-square-brace/index.js';
4
4
  import * as addMissingQuote from './plugins/add-missing-quote/index.js';
5
+ import * as addMissingSemicolon from './plugins/add-missing-semicolon/index.js';
5
6
  import * as convertCommaToSemicolon from './plugins/convert-comma-to-semicolon/index.js';
6
7
  import * as convertFromToRequire from './plugins/convert-from-to-require/index.js';
7
8
  import * as removeUselessRoundBrace from './plugins/remove-useless-round-brace/index.js';
9
+ import * as removeUselessComma from './plugins/remove-useless-comma/index.js';
8
10
  import * as addConstToExport from './plugins/add-const-to-export/index.js';
9
- import * as addMissingSemicolon from './plugins/add-missing-semicolon/index.js';
10
11
 
11
12
  export const plugins = [
12
13
  ['wrap-assignment-in-parens', wrapAssignmentInParens],
13
14
  ['add-missing-round-braces', addMissingRoundBraces],
14
15
  ['add-missing-squire-brace', addMissingSquireBrace],
15
- ['add-missing-quote', addMissingQuote],
16
16
  ['add-missing-semicolon', addMissingSemicolon],
17
+ ['add-missing-quote', addMissingQuote],
17
18
  ['add-const-to-export', addConstToExport],
18
19
  ['convert-comma-to-semicolon', convertCommaToSemicolon],
19
20
  ['convert-from-to-require', convertFromToRequire],
20
21
  ['remove-useless-round-brace', removeUselessRoundBrace],
22
+ ['remove-useless-comma', removeUselessComma],
21
23
  ];
@@ -1,10 +1,6 @@
1
- const isString = (a) => typeof a === 'string';
2
1
  const getValue = (token) => token.value;
3
2
 
4
3
  export const print = (tokens) => {
5
- if (isString(tokens))
6
- return tokens;
7
-
8
4
  return tokens
9
5
  .map(getValue)
10
6
  .join('');
@@ -2,9 +2,17 @@ import {
2
2
  isIdentifier,
3
3
  isNewLine,
4
4
  isPunctuator,
5
+ isWhiteSpace,
5
6
  } from '#types';
6
7
 
7
8
  export const createPath = ({tokens, start, end}) => ({
9
+ tokens,
10
+ start,
11
+ end,
12
+ isNextIdentifier: createIsNextIdentifier({
13
+ tokens,
14
+ end,
15
+ }),
8
16
  getAllPrev: createGetAllPrev({
9
17
  tokens,
10
18
  start,
@@ -21,28 +29,30 @@ export const createPath = ({tokens, start, end}) => ({
21
29
  tokens,
22
30
  start,
23
31
  }),
24
- isEndsWithPunctuator: createIsCurrentPunctuator({
25
- tokens,
26
- end,
27
- }),
28
- isCurrentPunctuator: createIsCurrentPunctuator({
32
+ isCurrentPunctuator: createIsNextPunctuator({
29
33
  tokens,
30
- end,
34
+ end: end - 1,
31
35
  }),
32
36
  });
33
37
 
34
- const createIsCurrentPunctuator = ({tokens, end}) => (punctuator) => {
35
- const current = tokens[end - 1];
38
+ const createIsNextIdentifier = ({tokens, end}) => (value) => {
39
+ const current = tokens[end];
36
40
 
37
- return isPunctuator(current, punctuator);
41
+ if (!current)
42
+ return false;
43
+
44
+ return isIdentifier(current, value);
38
45
  };
39
46
 
40
47
  const createIsNextPunctuator = ({tokens, end}) => (punctuator) => {
41
- const current = tokens[end];
48
+ let current = tokens[end];
42
49
 
43
50
  if (!current)
44
51
  return false;
45
52
 
53
+ if (isWhiteSpace(current))
54
+ current = tokens[end + 1];
55
+
46
56
  return isPunctuator(current, punctuator);
47
57
  };
48
58
 
@@ -1,17 +1,12 @@
1
1
  import debug from 'debug';
2
2
  import {compare} from '#compare';
3
3
  import {prepare} from '#parser';
4
- import {traverse} from '#traverser';
5
4
  import {
6
- is,
7
- isTemplateArgs,
8
- isTemplateArray,
9
- isTemplateExpression,
10
- } from '#types';
11
- import {collectArray} from '../compare/collect-array.js';
12
- import {collectExpression} from '../compare/collect-expression.js';
5
+ findVarsWays,
6
+ getCurrentValues,
7
+ setValues,
8
+ } from '#compare/values';
13
9
  import {createPath} from './path.js';
14
- import {collectArgs} from '../compare/collect-args.js';
15
10
 
16
11
  const returns = (a) => () => a;
17
12
  const {entries} = Object;
@@ -20,6 +15,7 @@ const log = debug('flatlint');
20
15
  export const replace = (tokens, {fix, rule, plugin}) => {
21
16
  const places = [];
22
17
  let isFixed = false;
18
+
23
19
  const match = plugin.match?.() ?? returns({});
24
20
 
25
21
  for (const [from, to] of entries(plugin.replace())) {
@@ -91,68 +87,3 @@ export const replace = (tokens, {fix, rule, plugin}) => {
91
87
 
92
88
  return [isFixed, places];
93
89
  };
94
-
95
- function findVarsWays(tokens) {
96
- const ways = {};
97
-
98
- traverse(tokens, {
99
- Identifier({value, index}) {
100
- if (is(value))
101
- ways[value] = index;
102
- },
103
- StringLiteral({value, index}) {
104
- if (is(value))
105
- ways[value] = index;
106
- },
107
- });
108
-
109
- return ways;
110
- }
111
-
112
- function getValues(tokens, waysFrom) {
113
- const values = {};
114
-
115
- for (const [name, index] of entries(waysFrom)) {
116
- let end = index;
117
- let ok = true;
118
-
119
- if (isTemplateArray(name))
120
- [ok, end] = collectArray({
121
- currentTokenIndex: index,
122
- tokens,
123
- });
124
- else if (isTemplateExpression(name))
125
- end = collectExpression({
126
- currentTokenIndex: index,
127
- tokens,
128
- });
129
- else if (isTemplateArgs(name))
130
- [ok, end] = collectArgs({
131
- currentTokenIndex: index,
132
- tokens,
133
- });
134
-
135
- if (!ok) {
136
- values[name] = [];
137
- continue;
138
- }
139
-
140
- values[name] = tokens.slice(index, end + 1);
141
- }
142
-
143
- return values;
144
- }
145
-
146
- function setValues({to, waysTo, values}) {
147
- for (const [name, index] of entries(waysTo)) {
148
- to.splice(index, 1, ...values[name]);
149
- }
150
- }
151
-
152
- function getCurrentValues({from, start, end, tokens}) {
153
- const current = tokens.slice(start, end);
154
-
155
- const waysFrom = findVarsWays(prepare(from));
156
-
157
- return getValues(current, waysFrom);
158
- }
@@ -82,6 +82,11 @@ export const closeRoundBrace = Punctuator(')');
82
82
  export const closeSquareBrace = Punctuator(']');
83
83
  export const semicolon = Punctuator(';');
84
84
  export const comma = Punctuator(',');
85
+ export const colon = Punctuator(':');
86
+ export const openCurlyBrace = Punctuator('{');
87
+ export const closeCurlyBrace = Punctuator('}');
88
+ export const openSquireBrace = Punctuator('[');
89
+ export const closeSquireBrace = Punctuator(']');
85
90
 
86
91
  export const OK = true;
87
92
  export const NOT_OK = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatlint",
3
- "version": "1.27.0",
3
+ "version": "1.29.0",
4
4
  "description": "JavaScript tokens-based linter",
5
5
  "main": "lib/flatlint.js",
6
6
  "type": "module",
@@ -26,6 +26,9 @@
26
26
  "#compare": {
27
27
  "default": "./lib/compare/compare.js"
28
28
  },
29
+ "#compare/values": {
30
+ "default": "./lib/compare/values.js"
31
+ },
29
32
  "#runner": {
30
33
  "default": "./lib/runner/runner.js"
31
34
  },
@@ -1,18 +0,0 @@
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.getAllPrev()) {
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
- });