flatlint 1.12.0 β†’ 1.14.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,14 @@
1
+ 2025.01.01, v1.14.0
2
+
3
+ feature:
4
+ - 2361298 flatlint: __expr
5
+
6
+ 2025.01.01, v1.13.0
7
+
8
+ feature:
9
+ - f24e4d3 flatlint: add support of __array
10
+ - 0b2d275 flatlint: compare: __array: add support
11
+
1
12
  2024.12.31, v1.12.0
2
13
 
3
14
  feature:
package/README.md CHANGED
@@ -90,6 +90,29 @@ npm i flatlint
90
90
 
91
91
  </details>
92
92
 
93
+ ## Template literals
94
+
95
+ **FlatLint** uses language similar to 🐊[**PutoutScript**](https://github.com/coderaiser/putout/blob/master/docs/putout-script.md#-putoutscript).
96
+
97
+ It can look similar, but has a couple differences:
98
+
99
+ - βœ…it may not be valid **JavaScript**, it can be couple tokens that can be fixed;
100
+ - βœ…it counts each symbol as a token;
101
+
102
+ ### `__a`
103
+
104
+ From `__a` to `__z` is usually identifiers, but can also be strings if used with quotes `'__a'` they can be single or double,
105
+ it can be only one quote `'__a` - this is valid, since **FlatLint** is tokens based.
106
+
107
+ ### `__array`
108
+
109
+ Collects everything that looks like array elements, it can start from squire brace `[__array;`, but that's not important
110
+ to end with it, since it used to fix error patterns.
111
+
112
+ ### `__expr`
113
+
114
+ Collects everything that looks like expression.
115
+
93
116
  ## API
94
117
 
95
118
  ```js
@@ -0,0 +1,20 @@
1
+ import {Punctuator} from '#types';
2
+ import {equal} from './equal.js';
3
+
4
+ export const collectArray = ({currentTokenIndex, tokens, nextTemplateToken}) => {
5
+ const n = tokens.length;
6
+ const brace = Punctuator(']');
7
+ let index = currentTokenIndex;
8
+
9
+ for (; index < n; index++) {
10
+ const token = tokens[index];
11
+
12
+ if (equal(token, nextTemplateToken))
13
+ break;
14
+
15
+ if (equal(token, brace))
16
+ break;
17
+ }
18
+
19
+ return --index;
20
+ };
@@ -0,0 +1,28 @@
1
+ import {Punctuator} from '#types';
2
+ import {equal} from './equal.js';
3
+
4
+ export const collectExpression = ({currentTokenIndex, tokens, nextTemplateToken}) => {
5
+ const n = tokens.length;
6
+ const closeBrace = Punctuator(')');
7
+ const openBrace = Punctuator('(');
8
+ const semicolon = Punctuator(';');
9
+ let index = currentTokenIndex;
10
+
11
+ for (; index < n; index++) {
12
+ const token = tokens[index];
13
+
14
+ if (equal(token, semicolon))
15
+ break;
16
+
17
+ if (equal(token, nextTemplateToken))
18
+ break;
19
+
20
+ if (equal(token, openBrace))
21
+ break;
22
+
23
+ if (equal(token, closeBrace))
24
+ break;
25
+ }
26
+
27
+ return --index;
28
+ };
@@ -1,11 +1,17 @@
1
1
  import {prepare} from '#parser';
2
2
  import {
3
- isId,
4
- isIdentifier,
5
- isPunctuator,
6
- isQuote,
7
- isStringLiteral,
3
+ isTemplateArrayToken,
4
+ isTemplateExpressionToken,
8
5
  } from '#types';
6
+ import {collectArray} from './collect-array.js';
7
+ import {collectExpression} from './collect-expression.js';
8
+ import {
9
+ equal,
10
+ equalAny,
11
+ equalId,
12
+ equalQuote,
13
+ equalStr,
14
+ } from './equal.js';
9
15
 
10
16
  export const compare = (source, template) => {
11
17
  const templateTokens = prepare(template);
@@ -16,26 +22,41 @@ export const compare = (source, template) => {
16
22
  let isEqual = false;
17
23
  let start = 0;
18
24
  let end = 0;
25
+ let delta = 0;
19
26
 
20
27
  for (let index = 0; index < n; index++) {
21
28
  for (let templateIndex = 0; templateIndex < templateTokensLength; templateIndex++) {
22
29
  const currentTokenIndex = index + templateIndex;
23
-
24
- if (currentTokenIndex > n) {
25
- isEqual = false;
26
- break;
27
- }
28
-
29
30
  const templateToken = templateTokens[templateIndex];
30
31
  const currentToken = tokens[currentTokenIndex];
31
32
 
32
- if (!compareAll(currentToken, templateToken)) {
33
+ if (isTemplateExpressionToken(templateToken)) {
34
+ const indexOfExpressionEnd = collectExpression({
35
+ currentTokenIndex,
36
+ tokens,
37
+ templateToken,
38
+ nextTemplateToken: templateTokens[templateIndex + 1],
39
+ });
40
+
41
+ delta = indexOfExpressionEnd - currentTokenIndex;
42
+ index = indexOfExpressionEnd - templateIndex;
43
+ } else if (isTemplateArrayToken(templateToken)) {
44
+ const indexOfArrayEnd = collectArray({
45
+ currentTokenIndex,
46
+ tokens,
47
+ templateToken,
48
+ nextTemplateToken: templateTokens[templateIndex + 1],
49
+ });
50
+
51
+ delta = indexOfArrayEnd - currentTokenIndex;
52
+ index = indexOfArrayEnd - templateIndex;
53
+ } else if (!compareAll(currentToken, templateToken)) {
33
54
  isEqual = false;
34
55
  break;
35
56
  }
36
57
 
37
58
  isEqual = true;
38
- start = index;
59
+ start = index - delta;
39
60
  end = currentTokenIndex;
40
61
  }
41
62
 
@@ -62,41 +83,3 @@ function compareAll(a, b) {
62
83
 
63
84
  return false;
64
85
  }
65
-
66
- function equal(a, b) {
67
- return a.type === b.type && a.value === b.value;
68
- }
69
-
70
- function equalAny(a, b) {
71
- if (!isIdentifier(b))
72
- return false;
73
-
74
- if (isPunctuator(a))
75
- return false;
76
-
77
- return b.value === '__';
78
- }
79
-
80
- function equalQuote(a, b) {
81
- if (!isPunctuator(a))
82
- return false;
83
-
84
- return isQuote(b.value);
85
- }
86
-
87
- function equalStr(a, b) {
88
- if (!isStringLiteral(a))
89
- return false;
90
-
91
- return isId(b.value);
92
- }
93
-
94
- function equalId(a, b) {
95
- if (!isIdentifier(b))
96
- return false;
97
-
98
- if (isPunctuator(a))
99
- return false;
100
-
101
- return isId(b.value);
102
- }
@@ -0,0 +1,45 @@
1
+ import {
2
+ isId,
3
+ isIdentifier,
4
+ isPunctuator,
5
+ isQuote,
6
+ isStringLiteral,
7
+ } from '#types';
8
+
9
+ export function equal(a, b) {
10
+ return a.type === b.type && a.value === b.value;
11
+ }
12
+
13
+ export function equalAny(a, b) {
14
+ if (!isIdentifier(b))
15
+ return false;
16
+
17
+ if (isPunctuator(a))
18
+ return false;
19
+
20
+ return b.value === '__';
21
+ }
22
+
23
+ export function equalQuote(a, b) {
24
+ if (!isPunctuator(a))
25
+ return false;
26
+
27
+ return isQuote(b.value);
28
+ }
29
+
30
+ export function equalStr(a, b) {
31
+ if (!isStringLiteral(a))
32
+ return false;
33
+
34
+ return isId(b.value);
35
+ }
36
+
37
+ export function equalId(a, b) {
38
+ if (!isIdentifier(b))
39
+ return false;
40
+
41
+ if (isPunctuator(a))
42
+ return false;
43
+
44
+ return isId(b.value);
45
+ }
@@ -4,9 +4,6 @@ export function report() {
4
4
 
5
5
  export function replace() {
6
6
  return {
7
- '["__a";': '["__a"];',
8
- '[__a;': '[__a];',
9
- '["__a", "__b";': '["__a", "__b"];',
10
- '[__a, __b;': '[__a, __b];',
7
+ '[__array;': '[__array];',
11
8
  };
12
9
  }
@@ -1,7 +1,6 @@
1
1
  export const report = () => 'Remove useless round brace';
2
2
 
3
3
  export const replace = () => ({
4
- 'const __a = __b)': 'const __a = __b',
5
- 'const __a = "__b")': 'const __a = "__b"',
4
+ 'const __a = __expr);': 'const __a = __expr;',
6
5
  'from "__b")': 'from "__b"',
7
6
  });
@@ -1,8 +1,16 @@
1
- import {is} from '#types';
2
- import {prepare} from '#parser';
3
1
  import {compare} from '#compare';
2
+ import {prepare} from '#parser';
4
3
  import {traverse} from '#traverser';
4
+ import {
5
+ is,
6
+ isTemplateArray,
7
+ isTemplateExpression,
8
+ Punctuator,
9
+ } from '#types';
10
+ import {collectArray} from '../compare/collect-array.js';
11
+ import {collectExpression} from '../compare/collect-expression.js';
5
12
 
13
+ const {isArray} = Array;
6
14
  const {entries} = Object;
7
15
 
8
16
  export const replace = (tokens, {fix, rule, plugin}) => {
@@ -79,6 +87,38 @@ function getValues(tokens, waysFrom) {
79
87
  const values = {};
80
88
 
81
89
  for (const [name, index] of entries(waysFrom)) {
90
+ if (isTemplateArray(name)) {
91
+ const endOfArray = collectArray({
92
+ currentTokenIndex: index,
93
+ tokens,
94
+ nextTemplateToken: Punctuator(';'),
95
+ });
96
+
97
+ if (index === endOfArray) {
98
+ values[name] = tokens[index];
99
+ continue;
100
+ }
101
+
102
+ values[name] = tokens.slice(index, endOfArray + 1);
103
+ continue;
104
+ }
105
+
106
+ if (isTemplateExpression(name)) {
107
+ const endOfArray = collectExpression({
108
+ currentTokenIndex: index,
109
+ tokens,
110
+ nextTemplateToken: Punctuator(';'),
111
+ });
112
+
113
+ if (index === endOfArray) {
114
+ values[name] = tokens[index];
115
+ continue;
116
+ }
117
+
118
+ values[name] = tokens.slice(index, endOfArray + 1);
119
+ continue;
120
+ }
121
+
82
122
  values[name] = tokens[index];
83
123
  }
84
124
 
@@ -87,6 +127,13 @@ function getValues(tokens, waysFrom) {
87
127
 
88
128
  function setValues({to, waysTo, values}) {
89
129
  for (const [name, index] of entries(waysTo)) {
90
- to[index] = values[name];
130
+ const current = values[name];
131
+
132
+ if (!isArray(current)) {
133
+ to[index] = values[name];
134
+ continue;
135
+ }
136
+
137
+ to.splice(index, 1, ...values[name]);
91
138
  }
92
139
  }
@@ -3,7 +3,7 @@ import {replace} from './replacer.js';
3
3
  export const run = (tokens, {fix, fixCount = 10, plugins}) => {
4
4
  const places = [];
5
5
 
6
- while (--fixCount) {
6
+ while (--fixCount >= 0) {
7
7
  for (const {rule, plugin} of plugins) {
8
8
  places.push(...replace(tokens, {
9
9
  fix,
@@ -11,8 +11,10 @@ export const run = (tokens, {fix, fixCount = 10, plugins}) => {
11
11
  plugin,
12
12
  }));
13
13
 
14
- if (places.length)
15
- return [places];
14
+ if (!fix) {
15
+ fixCount = 0;
16
+ break;
17
+ }
16
18
  }
17
19
  }
18
20
 
@@ -30,8 +30,15 @@ export const is = (str, array = ALL) => {
30
30
  const LINKED_NODE = /^__[a-z]$/;
31
31
  const ANY = '__';
32
32
  const QUOTE = /^['"]$/;
33
+ const ARRAY = '__array';
34
+ const EXPR = '__expr';
33
35
 
34
- const ALL = [ANY, LINKED_NODE];
36
+ const ALL = [
37
+ ANY,
38
+ LINKED_NODE,
39
+ ARRAY,
40
+ EXPR,
41
+ ];
35
42
 
36
43
  function check(str, item) {
37
44
  if (isString(item))
@@ -42,3 +49,7 @@ function check(str, item) {
42
49
 
43
50
  export const isId = (a) => LINKED_NODE.test(a);
44
51
  export const isQuote = (a) => QUOTE.test(a);
52
+ export const isTemplateArray = (a) => a === ARRAY;
53
+ export const isTemplateExpression = (a) => a === EXPR;
54
+ export const isTemplateArrayToken = (a) => isIdentifier(a) && isTemplateArray(a.value);
55
+ export const isTemplateExpressionToken = (a) => isIdentifier(a) && isTemplateExpression(a.value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatlint",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "JavaScript tokens-based linter",
5
5
  "main": "lib/flatlint.js",
6
6
  "type": "module",