flatlint 1.11.0 β†’ 1.13.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.13.0
2
+
3
+ feature:
4
+ - f24e4d3 flatlint: add support of __array
5
+ - 0b2d275 flatlint: compare: __array: add support
6
+
7
+ 2024.12.31, v1.12.0
8
+
9
+ feature:
10
+ - dedb945 flatlint: add-missing-quote: improve
11
+
1
12
  2024.12.31, v1.11.0
2
13
 
3
14
  feature:
package/README.md CHANGED
@@ -28,6 +28,15 @@ npm i flatlint
28
28
 
29
29
  </details>
30
30
 
31
+ <details><summary>Convert <code>from</code> to <code>require</code></summary>
32
+
33
+ ```diff
34
+ -const a = from 'a';
35
+ +const a = require('a');
36
+ ```
37
+
38
+ </details>
39
+
31
40
  <details><summary>add missing round braces in if statement</summary>
32
41
 
33
42
  ```diff
@@ -81,6 +90,24 @@ npm i flatlint
81
90
 
82
91
  </details>
83
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
+ - βœ…it may not be valid **JavaScript**, it can be couple tokens that can be fixed;
99
+ - βœ…it counts each symbol as a token;
100
+
101
+ ### `__a`
102
+
103
+ From `__a` to `__z` is usually identifiers, but can also be strings if used with quotes `'__a'` they can be single or double,
104
+ it can be only one quote `'__a` - this is valid, since **FlatLint** is tokens based.
105
+
106
+ ### `__array`
107
+
108
+ Collects everything that looks like array elements, it can start from squire brace `[__array;`, but that's not important
109
+ to end with it, since it used to fix error patterns.
110
+
84
111
  ## API
85
112
 
86
113
  ```js
@@ -0,0 +1,21 @@
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
+ };
21
+
@@ -1,11 +1,13 @@
1
1
  import {prepare} from '#parser';
2
+ import {isTemplateArrayToken} from '#types';
3
+ import {collectArray} from './collect-array.js';
2
4
  import {
3
- isId,
4
- isIdentifier,
5
- isPunctuator,
6
- isQuote,
7
- isStringLiteral,
8
- } from '#types';
5
+ equal,
6
+ equalAny,
7
+ equalId,
8
+ equalQuote,
9
+ equalStr,
10
+ } from './equal.js';
9
11
 
10
12
  export const compare = (source, template) => {
11
13
  const templateTokens = prepare(template);
@@ -16,26 +18,31 @@ export const compare = (source, template) => {
16
18
  let isEqual = false;
17
19
  let start = 0;
18
20
  let end = 0;
21
+ let delta = 0;
19
22
 
20
23
  for (let index = 0; index < n; index++) {
21
24
  for (let templateIndex = 0; templateIndex < templateTokensLength; templateIndex++) {
22
25
  const currentTokenIndex = index + templateIndex;
23
-
24
- if (currentTokenIndex > n) {
25
- isEqual = false;
26
- break;
27
- }
28
-
29
26
  const templateToken = templateTokens[templateIndex];
30
27
  const currentToken = tokens[currentTokenIndex];
31
28
 
32
- if (!compareAll(currentToken, templateToken)) {
29
+ if (isTemplateArrayToken(templateToken)) {
30
+ const indexOfArrayEnd = collectArray({
31
+ currentTokenIndex,
32
+ tokens,
33
+ templateToken,
34
+ nextTemplateToken: templateTokens[templateIndex + 1],
35
+ });
36
+
37
+ delta = indexOfArrayEnd - currentTokenIndex;
38
+ index = indexOfArrayEnd - templateIndex;
39
+ } else if (!compareAll(currentToken, templateToken)) {
33
40
  isEqual = false;
34
41
  break;
35
42
  }
36
43
 
37
44
  isEqual = true;
38
- start = index;
45
+ start = index - delta;
39
46
  end = currentTokenIndex;
40
47
  }
41
48
 
@@ -62,41 +69,3 @@ function compareAll(a, b) {
62
69
 
63
70
  return false;
64
71
  }
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
+ }
@@ -1,10 +1,6 @@
1
- export function report() {
2
- return 'Add missing quote';
3
- }
1
+ export const report = () => 'Add missing quote';
4
2
 
5
- export function replace() {
6
- return {
7
- 'const __a = "__b': 'const __a = "__b"',
8
- '__a("__b)': '__a("__b")',
9
- };
10
- }
3
+ export const replace = () => ({
4
+ 'const __a = "__b;': 'const __a = "__b";',
5
+ '__a("__b)': '__a("__b")',
6
+ });
@@ -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
  }
@@ -0,0 +1,5 @@
1
+ export const report = () => `Use 'require' instead of 'from'`;
2
+
3
+ export const replace = () => ({
4
+ '= from "__a"': '= require("__a")',
5
+ });
@@ -1,11 +1,7 @@
1
- export function report() {
2
- return 'Remove useless round brace';
3
- }
1
+ export const report = () => 'Remove useless round brace';
4
2
 
5
- export function replace() {
6
- return {
7
- 'const __a = __b)': 'const __a = __b',
8
- 'const __a = "__b")': 'const __a = "__b"',
9
- 'from "__b")': 'from "__b"',
10
- };
11
- }
3
+ export const replace = () => ({
4
+ 'const __a = __b)': 'const __a = __b',
5
+ 'const __a = "__b")': 'const __a = "__b"',
6
+ 'from "__b")': 'from "__b"',
7
+ });
package/lib/plugins.js CHANGED
@@ -3,13 +3,17 @@ import * as addMissingRoundBraces from './plugins/add-missing-round-braces/index
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
5
  import * as convertCommaToSemicolon from './plugins/convert-comma-to-semicolon/index.js';
6
+ import * as convertFromToRequire from './plugins/convert-from-to-require/index.js';
6
7
  import * as removeUselessRoundBrace from './plugins/remove-useless-round-brace/index.js';
8
+ import * as addConstToExport from './plugins/add-const-to-export/index.js';
7
9
 
8
10
  export const plugins = [
9
11
  ['wrap-assignment-in-parens', wrapAssignmentInParens],
10
12
  ['add-missing-round-braces', addMissingRoundBraces],
11
13
  ['add-missing-squire-brace', addMissingSquireBrace],
12
14
  ['add-missing-quote', addMissingQuote],
15
+ ['add-const-to-export', addConstToExport],
13
16
  ['convert-comma-to-semicolon', convertCommaToSemicolon],
17
+ ['convert-from-to-require', convertFromToRequire],
14
18
  ['remove-useless-round-brace', removeUselessRoundBrace],
15
19
  ];
@@ -1,8 +1,14 @@
1
- import {is} from '#types';
1
+ import {
2
+ is,
3
+ isTemplateArray,
4
+ Punctuator,
5
+ } from '#types';
2
6
  import {prepare} from '#parser';
3
7
  import {compare} from '#compare';
4
8
  import {traverse} from '#traverser';
9
+ import {collectArray} from '../compare/collect-array.js';
5
10
 
11
+ const {isArray} = Array;
6
12
  const {entries} = Object;
7
13
 
8
14
  export const replace = (tokens, {fix, rule, plugin}) => {
@@ -79,6 +85,23 @@ function getValues(tokens, waysFrom) {
79
85
  const values = {};
80
86
 
81
87
  for (const [name, index] of entries(waysFrom)) {
88
+ if (isTemplateArray(name)) {
89
+ debugger;
90
+ const endOfArray = collectArray({
91
+ currentTokenIndex: index,
92
+ tokens,
93
+ nextTemplateToken: Punctuator(';'),
94
+ });
95
+
96
+ if (index === endOfArray) {
97
+ values[name] = tokens[index];
98
+ continue;
99
+ }
100
+
101
+ values[name] = tokens.slice(index, endOfArray + 1);
102
+ continue;
103
+ }
104
+
82
105
  values[name] = tokens[index];
83
106
  }
84
107
 
@@ -87,6 +110,14 @@ function getValues(tokens, waysFrom) {
87
110
 
88
111
  function setValues({to, waysTo, values}) {
89
112
  for (const [name, index] of entries(waysTo)) {
90
- to[index] = values[name];
113
+ const current = values[name];
114
+
115
+ if (!isArray(current)) {
116
+ to[index] = values[name];
117
+ continue;
118
+ }
119
+
120
+ to.splice(index, 1, ...values[name]);
91
121
  }
92
122
  }
123
+
@@ -1,14 +1,22 @@
1
1
  import {replace} from './replacer.js';
2
2
 
3
- export const run = (tokens, {fix, plugins}) => {
3
+ export const run = (tokens, {fix, fixCount = 10, plugins}) => {
4
+ debugger;
4
5
  const places = [];
5
6
 
6
- for (const {rule, plugin} of plugins) {
7
- places.push(...replace(tokens, {
8
- fix,
9
- rule,
10
- plugin,
11
- }));
7
+ while (--fixCount >= 0) {
8
+ for (const {rule, plugin} of plugins) {
9
+ places.push(...replace(tokens, {
10
+ fix,
11
+ rule,
12
+ plugin,
13
+ }));
14
+
15
+ if (!fix || places.length) {
16
+ fixCount = 0;
17
+ break;
18
+ }
19
+ }
12
20
  }
13
21
 
14
22
  return [places];
@@ -30,8 +30,13 @@ 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';
33
34
 
34
- const ALL = [ANY, LINKED_NODE];
35
+ const ALL = [
36
+ ANY,
37
+ LINKED_NODE,
38
+ ARRAY,
39
+ ];
35
40
 
36
41
  function check(str, item) {
37
42
  if (isString(item))
@@ -42,3 +47,5 @@ function check(str, item) {
42
47
 
43
48
  export const isId = (a) => LINKED_NODE.test(a);
44
49
  export const isQuote = (a) => QUOTE.test(a);
50
+ export const isTemplateArray = (a) => a === ARRAY;
51
+ export const isTemplateArrayToken = (a) => isIdentifier(a) && isTemplateArray(a.value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatlint",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "JavaScript tokens-based linter",
5
5
  "main": "lib/flatlint.js",
6
6
  "type": "module",