@putout/plugin-putout 29.25.0 → 29.26.1

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.
@@ -88,7 +88,7 @@ const checkArgs = (names, push) => (path) => {
88
88
  name,
89
89
  });
90
90
  },
91
- ['__a(__args)']: (path) => {
91
+ '__a(__args)': (path) => {
92
92
  const {callee} = path.node;
93
93
  const {name} = callee;
94
94
 
@@ -4,9 +4,14 @@ const {
4
4
  objectPattern,
5
5
  isIdentifier,
6
6
  objectProperty,
7
+ isObjectProperty,
8
+ isObjectExpression,
7
9
  } = types;
8
10
 
9
- const {getBindingPath} = operator;
11
+ const {
12
+ getBindingPath,
13
+ getTemplateValues,
14
+ } = operator;
10
15
 
11
16
  const SHORTHAND = true;
12
17
  const COMPUTED = false;
@@ -41,11 +46,20 @@ export const fix = ({path, node}) => {
41
46
  export const traverse = ({push}) => ({
42
47
  '(__a, path) => __b': process(push),
43
48
  '(__a) => __b': process(push),
44
- '() => __a': process(push),
49
+ '() => __b': process(push),
45
50
  });
46
51
 
47
52
  const process = (push) => (path) => {
48
- if (!path.parentPath.isObjectProperty())
53
+ const {parentPath} = path;
54
+ const {__b} = getTemplateValues(path, '(__args) => __b');
55
+
56
+ if (isObjectExpression(__b))
57
+ return;
58
+
59
+ if (!parentPath.isObjectProperty())
60
+ return;
61
+
62
+ if (!isReplacer(path))
49
63
  return;
50
64
 
51
65
  path.traverse({
@@ -66,3 +80,16 @@ const process = (push) => (path) => {
66
80
  },
67
81
  });
68
82
  };
83
+
84
+ function isReplacer({parentPath}) {
85
+ const currentPath = parentPath.parentPath.parentPath.parentPath;
86
+
87
+ if (!isObjectProperty(currentPath))
88
+ return true;
89
+
90
+ const {key} = currentPath.node;
91
+
92
+ return isIdentifier(key, {
93
+ name: 'replace',
94
+ });
95
+ }
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import * as sortReadmeFile from './sort-readme-file/index.js';
1
2
  import * as applyStringToPropertyKey from './apply-string-to-property-key/index.js';
2
3
  import * as convertTraverseToSuperTraverse from './convert-traverse-to-super-traverse/index.js';
3
4
  import * as applyDestructuringToOptions from './apply-destructuring-to-options/index.js';
@@ -176,4 +177,5 @@ export const rules = {
176
177
  'apply-destructuring-to-options': applyDestructuringToOptions,
177
178
  'convert-traverse-to-super-traverse': convertTraverseToSuperTraverse,
178
179
  'apply-string-to-property-key': applyStringToPropertyKey,
180
+ 'sort-readme-file': sortReadmeFile,
179
181
  };
@@ -0,0 +1,30 @@
1
+ import {operator} from 'putout';
2
+ import * as sortContents from './sort-contents/index.js';
3
+ import * as sortHeadings from './sort-headings/index.js';
4
+
5
+ const {
6
+ matchFiles,
7
+ getFilename,
8
+ getParentDirectory,
9
+ } = operator;
10
+
11
+ export const {
12
+ report,
13
+ scan,
14
+ fix,
15
+ } = matchFiles({
16
+ files: {
17
+ 'README.md': {
18
+ plugins: [
19
+ ['sort-contents', sortContents],
20
+ ['sort-headings', sortHeadings],
21
+ ],
22
+ },
23
+ },
24
+ exclude: (file) => {
25
+ const dir = getParentDirectory(file);
26
+ const dirname = getFilename(dir);
27
+
28
+ return !/plugin-.*$/.test(dirname);
29
+ },
30
+ });
@@ -0,0 +1,69 @@
1
+ import {operator} from 'putout';
2
+
3
+ const {compareAny} = operator;
4
+ const {entries} = Object;
5
+
6
+ export const report = () => `Sort 'contents'`;
7
+
8
+ export const fix = ({path, list}) => {
9
+ path.node.arguments = list;
10
+ };
11
+
12
+ export const traverse = ({listStore, push}) => ({
13
+ 'li("✅ ", link(__b, __c), ";")': (path) => {
14
+ const {parentPath} = path;
15
+ const prev = parentPath.getPrevSibling();
16
+
17
+ if (!compareAny(prev, [
18
+ `heading(2, 'Rules')`,
19
+ `heading(2, 'File rules')`,
20
+ ]))
21
+ return;
22
+
23
+ listStore(path);
24
+ },
25
+ 'Program': {
26
+ exit() {
27
+ const list = {};
28
+ const ids = {};
29
+
30
+ for (const {node, parentPath} of listStore()) {
31
+ const {line} = parentPath.node.loc.start;
32
+
33
+ ids[line] = parentPath;
34
+
35
+ list[line] = list[line] || [];
36
+ list[line].push(node);
37
+ }
38
+
39
+ const sortedList = {};
40
+ const resultList = [];
41
+
42
+ for (const [ul, currentList] of entries(list)) {
43
+ sortedList[ul] = currentList.toSorted(asc);
44
+
45
+ for (const [i, current] of sortedList[ul].entries()) {
46
+ if (current !== currentList[i]) {
47
+ resultList[ul] = sortedList[ul];
48
+ break;
49
+ }
50
+ }
51
+ }
52
+
53
+ for (const [id, list] of entries(resultList)) {
54
+ push({
55
+ path: ids[id],
56
+ list,
57
+ });
58
+ }
59
+ },
60
+ },
61
+ });
62
+
63
+ function asc(a, b) {
64
+ return getValue(a).localeCompare(getValue(b));
65
+ }
66
+
67
+ function getValue(node) {
68
+ return node.arguments[1].arguments[0].value;
69
+ }
@@ -0,0 +1,166 @@
1
+ import {operator, types} from 'putout';
2
+
3
+ const {
4
+ getTemplateValues,
5
+ replaceWithMultiple,
6
+ remove,
7
+ compare,
8
+ __markdown,
9
+ } = operator;
10
+
11
+ const {arrayExpression} = types;
12
+
13
+ const MAX_RULE_HEADING = 2;
14
+ const LINK = `li('✅ ', link(__a, __b), ';')`;
15
+ const LIST = 'ul(__args)';
16
+ const HEADING = 'heading(__a, __b)';
17
+ const compareOne = (a) => (b) => compare(b, a);
18
+
19
+ export const report = ({invalid}) => {
20
+ if (invalid.length)
21
+ return `Avoid using rules with heading level more then 2: '### ${invalid[0]}' -> '## ${invalid[0]}'`;
22
+
23
+ return `Sort 'contents'`;
24
+ };
25
+
26
+ export const fix = ({path, sorted, rulesHeadings, invalid}) => {
27
+ if (invalid.length)
28
+ return;
29
+
30
+ packHeadings(path.get('arguments.0.elements'));
31
+ const {elements} = path.node.arguments[0];
32
+
33
+ for (const [i, current] of rulesHeadings.entries()) {
34
+ elements.splice(current.key, 1, sorted[i].node);
35
+ }
36
+
37
+ extractHeadings(path.get('arguments.0.elements'));
38
+ };
39
+
40
+ export const traverse = ({push}) => ({
41
+ [__markdown](path) {
42
+ const elements = path.get('arguments.0.elements');
43
+ const rules = getRules(elements);
44
+
45
+ const rulesHeadings = getRulesHeadings({
46
+ elements,
47
+ rules,
48
+ });
49
+
50
+ const sorted = rulesHeadings.toSorted(asc);
51
+ let is = false;
52
+
53
+ for (const [index, heading] of rulesHeadings.entries()) {
54
+ if (heading !== sorted[index]) {
55
+ is = true;
56
+ break;
57
+ }
58
+ }
59
+
60
+ if (!is)
61
+ return;
62
+
63
+ const invalid = validateRulesHeadings({
64
+ rules,
65
+ elements,
66
+ });
67
+
68
+ push({
69
+ path,
70
+ rulesHeadings,
71
+ sorted,
72
+ invalid,
73
+ });
74
+ },
75
+ });
76
+
77
+ function validateRulesHeadings({rules, elements}) {
78
+ const invalid = [];
79
+
80
+ for (const element of elements) {
81
+ if (compare(element, HEADING)) {
82
+ const {__a, __b} = getTemplateValues(element, HEADING);
83
+ const {value} = __b;
84
+
85
+ if (!rules.has(value))
86
+ continue;
87
+
88
+ if (__a.value !== MAX_RULE_HEADING) {
89
+ invalid.push(value);
90
+ return invalid;
91
+ }
92
+ }
93
+ }
94
+
95
+ return invalid;
96
+ }
97
+
98
+ function asc(a, b) {
99
+ return getValue(a).localeCompare(getValue(b));
100
+ }
101
+
102
+ const getValue = (path) => path.node.arguments[1].value;
103
+
104
+ function packHeadings(elements) {
105
+ let argument;
106
+
107
+ for (const element of elements) {
108
+ if (isRuleHeading(element)) {
109
+ argument = arrayExpression([]);
110
+ element.node.arguments.push(argument);
111
+ continue;
112
+ }
113
+
114
+ argument.elements.push(element.node);
115
+ remove(element);
116
+ }
117
+ }
118
+
119
+ function extractHeadings(elements) {
120
+ for (const element of elements.filter(isRuleHeading)) {
121
+ const args = element.node.arguments;
122
+ const argument = args.pop();
123
+
124
+ replaceWithMultiple(element, [element, ...argument.elements]);
125
+ }
126
+ }
127
+
128
+ function isRuleHeading(element) {
129
+ if (!compare(element, 'heading(__args)'))
130
+ return false;
131
+
132
+ const {__a} = getTemplateValues(element, 'heading(__a, __b)');
133
+
134
+ return __a.value <= MAX_RULE_HEADING;
135
+ }
136
+
137
+ function getRules(elements) {
138
+ const rules = new Set();
139
+
140
+ for (const element of elements.filter(compareOne(LIST))) {
141
+ const args = element.node.arguments;
142
+
143
+ for (const li of args.filter(compareOne(LINK))) {
144
+ const {__a} = getTemplateValues(li, LINK);
145
+ rules.add(__a.value);
146
+ }
147
+ }
148
+
149
+ return rules;
150
+ }
151
+
152
+ function getRulesHeadings({rules, elements}) {
153
+ const headings = [];
154
+
155
+ for (const element of elements) {
156
+ if (compare(element, HEADING)) {
157
+ const {__b} = getTemplateValues(element, HEADING);
158
+ const {value} = __b;
159
+
160
+ if (rules.has(value))
161
+ headings.push(element);
162
+ }
163
+ }
164
+
165
+ return headings;
166
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/plugin-putout",
3
- "version": "29.25.0",
3
+ "version": "29.26.1",
4
4
  "type": "module",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "🐊Putout plugin helps with plugins development",