@putout/printer 18.7.3 → 18.7.5
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 +13 -0
- package/lib/tokenize/expressions/array-expression/indent.js +1 -3
- package/lib/tokenize/expressions/array-expression/newline.js +2 -5
- package/lib/tokenize/expressions/class/class.js +1 -2
- package/lib/tokenize/expressions/logical-expression/chain.js +6 -8
- package/lib/tokenize/expressions/logical-expression/logical-expression.js +21 -17
- package/lib/tokenize/expressions/member-expression/is-looks-like-chain.js +54 -38
- package/lib/tokenize/expressions/member-expression/member-expressions.js +0 -1
- package/lib/tokenize/statements/block-statement/block-statement.js +3 -5
- package/lib/tokenize/statements/block-statement/is-call-inside-chain.js +11 -23
- package/lib/tokenize/statements/switch-statement/switch-statement.js +0 -1
- package/lib/tokenize/type-checker/comparators.js +7 -4
- package/lib/tokenize/type-checker/parsers.js +25 -1
- package/lib/tokenize/type-checker/type-checker.js +1 -1
- package/package.json +1 -1
package/ChangeLog
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
2026.03.16, v18.7.5
|
|
2
|
+
|
|
3
|
+
feature:
|
|
4
|
+
- f0cb1bfa @putout/printer: BlockStatement: isCallInsideChain: simplify
|
|
5
|
+
- 246851f5 @putout/printer: MemberExpression: simplify
|
|
6
|
+
- 3fd6353a @putout/printer: MemberExpression: simplify
|
|
7
|
+
- 2ddd2e69 @putout/printer: LogicalExpression: simplify
|
|
8
|
+
|
|
9
|
+
2026.03.16, v18.7.4
|
|
10
|
+
|
|
11
|
+
feature:
|
|
12
|
+
- a03e0d22 @putout/printer: type-checker: comparison
|
|
13
|
+
|
|
1
14
|
2026.03.15, v18.7.3
|
|
2
15
|
|
|
3
16
|
feature:
|
|
@@ -98,12 +98,10 @@ export function isArrayInsideArray(path) {
|
|
|
98
98
|
return length <= 3 && length !== 1;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
const isTwo = (a) => a === 2;
|
|
102
|
-
|
|
103
101
|
export const isHideIndent = createTypeChecker([
|
|
104
102
|
['-: -> !', isInsideArray],
|
|
105
103
|
['-: parentPath -> !', isStringAndArray],
|
|
106
|
-
['+: parentPath.node.elements.length',
|
|
104
|
+
['+: parentPath.node.elements.length', '=', 2],
|
|
107
105
|
]);
|
|
108
106
|
|
|
109
107
|
const isLastElementObjectExpression = ({node}) => isObjectExpression(node.elements.at(-1));
|
|
@@ -162,10 +162,8 @@ const hasObjects = (path) => {
|
|
|
162
162
|
return literals.length;
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
-
const isLessThenTwo = (a) => a < 2;
|
|
166
|
-
|
|
167
165
|
const isMoreThenMaxElementLengthInOneLine = createTypeChecker([
|
|
168
|
-
['-: node.elements.length',
|
|
166
|
+
['-: node.elements.length', '<', 2],
|
|
169
167
|
['-: -> !', isInsideCall],
|
|
170
168
|
['-', hasObjects],
|
|
171
169
|
['+', isMoreThenMaxLiteralLength],
|
|
@@ -192,11 +190,10 @@ const isElementsMoreThenMaxWithFirstString = createTypeChecker([
|
|
|
192
190
|
['+', isElementsMoreThenMax],
|
|
193
191
|
]);
|
|
194
192
|
|
|
195
|
-
const isOne = (a) => a === 1;
|
|
196
193
|
const isBody = ({node, parentPath}) => node === parentPath.node.body;
|
|
197
194
|
|
|
198
195
|
const isBodyWithOneElement = createTypeChecker([
|
|
199
|
-
['-: node.elements.length -> !',
|
|
196
|
+
['-: node.elements.length -> !', '=', 1],
|
|
200
197
|
['+', isBody],
|
|
201
198
|
]);
|
|
202
199
|
|
|
@@ -19,11 +19,10 @@ const afterIf = createTypeChecker([
|
|
|
19
19
|
]);
|
|
20
20
|
|
|
21
21
|
const {isFunction} = types;
|
|
22
|
-
const isLessThenTwo = (a) => a < 2;
|
|
23
22
|
|
|
24
23
|
const isSpaceBeforeImplements = createTypeChecker([
|
|
25
24
|
['+: node.typeParameters -> -'],
|
|
26
|
-
['+: node.typeParameters.params.length',
|
|
25
|
+
['+: node.typeParameters.params.length', '<', 2],
|
|
27
26
|
]);
|
|
28
27
|
|
|
29
28
|
const classVisitor = maybeDecorators((path, printer, semantics) => {
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {createTypeChecker} from '#type-checker';
|
|
2
3
|
|
|
3
|
-
const {
|
|
4
|
-
isLogicalExpression,
|
|
5
|
-
isReturnStatement,
|
|
6
|
-
isVariableDeclarator,
|
|
7
|
-
} = types;
|
|
4
|
+
const {isLogicalExpression} = types;
|
|
8
5
|
|
|
9
|
-
export const isRootOk = (
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export const isRootOk = createTypeChecker([
|
|
7
|
+
'+: -> ReturnStatement',
|
|
8
|
+
'+: -> VariableDeclarator',
|
|
9
|
+
]);
|
|
12
10
|
|
|
13
11
|
export const chain = (path) => {
|
|
14
12
|
const [downCount] = down(path);
|
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
import {maybeParens} from '#maybe-parens';
|
|
2
|
+
import {createTypeChecker} from '#type-checker';
|
|
2
3
|
import {chain, isRootOk} from './chain.js';
|
|
3
4
|
|
|
5
|
+
const condition = createTypeChecker([
|
|
6
|
+
'+: parentPath -> UnaryExpression',
|
|
7
|
+
'+: parentPath -> AwaitExpression',
|
|
8
|
+
]);
|
|
9
|
+
|
|
4
10
|
export const LogicalExpression = maybeParens({
|
|
5
|
-
condition
|
|
6
|
-
if (path.parentPath.isUnaryExpression())
|
|
7
|
-
return true;
|
|
8
|
-
|
|
9
|
-
return path.parentPath.isAwaitExpression();
|
|
10
|
-
},
|
|
11
|
+
condition,
|
|
11
12
|
print(path, {print, maybe}, semantics) {
|
|
13
|
+
const {operator} = path.node;
|
|
14
|
+
|
|
12
15
|
print('__left');
|
|
13
16
|
|
|
14
|
-
const needNewLine =
|
|
17
|
+
const needNewLine = isNewline(path, semantics);
|
|
15
18
|
|
|
16
19
|
maybe.indent.inc(needNewLine);
|
|
17
20
|
needNewLine ? print.breakline() : print.space();
|
|
18
21
|
maybe.indent.dec(needNewLine);
|
|
19
22
|
|
|
20
|
-
print(
|
|
23
|
+
print(operator);
|
|
21
24
|
print.space();
|
|
22
25
|
print('__right');
|
|
23
26
|
},
|
|
24
27
|
});
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
const callWithRoot = (fn) => (path, {maxLogicalsInOneLine}) => {
|
|
27
30
|
const [root, count] = chain(path);
|
|
28
31
|
|
|
29
|
-
if (!
|
|
32
|
+
if (!fn(root))
|
|
30
33
|
return false;
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
return count > maxLogicalsInOneLine;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const isNewline = createTypeChecker([
|
|
39
|
+
['-: -> !', callWithRoot(isRootOk)],
|
|
40
|
+
['+: node.operator ->', '=', '||'],
|
|
41
|
+
['+: node.operator ->', '=', '&&'],
|
|
42
|
+
]);
|
|
@@ -1,20 +1,49 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
-
import {
|
|
2
|
+
import {createTypeChecker} from '#type-checker';
|
|
3
3
|
import {chain} from './chain.js';
|
|
4
4
|
|
|
5
|
+
const hasPropertyWithComment = (properties) => {
|
|
6
|
+
return properties.find(hasComment);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const isPathGet = ([property]) => {
|
|
10
|
+
return isCallExpression(property, {
|
|
11
|
+
name: 'get',
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
5
15
|
const {
|
|
6
|
-
isUnaryExpression,
|
|
7
16
|
isIfStatement,
|
|
8
17
|
isCallExpression,
|
|
9
18
|
isIdentifier,
|
|
10
19
|
} = types;
|
|
11
20
|
|
|
12
|
-
const
|
|
21
|
+
const isPathFirstArg = ({node, parentPath}) => {
|
|
22
|
+
const [first] = parentPath.node.arguments;
|
|
23
|
+
return node === first;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function isPathLastArg({node, parentPath}) {
|
|
27
|
+
const last = parentPath.node.arguments.at(-1);
|
|
28
|
+
return node === last;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isFirstArgOfCall = createTypeChecker([
|
|
32
|
+
['-: parentPath -> !CallExpression'],
|
|
33
|
+
['+', isPathFirstArg],
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const isLastArgInCall = createTypeChecker([
|
|
37
|
+
['-: parentPath -> !CallExpression'],
|
|
38
|
+
['+', isPathLastArg],
|
|
39
|
+
]);
|
|
40
|
+
|
|
13
41
|
const isCall = (a) => a.type === 'CallExpression';
|
|
14
42
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
43
|
+
const callWithRoot = (fn) => (a, {root}) => fn(root);
|
|
44
|
+
const isExcludedFromChain = createTypeChecker([
|
|
45
|
+
'+: -> UnaryExpression',
|
|
46
|
+
'+: -> IfStatement',
|
|
18
47
|
]);
|
|
19
48
|
|
|
20
49
|
const hasComment = ({type}) => type === 'CommentLine';
|
|
@@ -32,51 +61,38 @@ const isInsideMemberCall = (path) => {
|
|
|
32
61
|
return isCallExpression(path.parentPath.parentPath);
|
|
33
62
|
};
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!isCallExpression(parentPath))
|
|
39
|
-
return false;
|
|
40
|
-
|
|
41
|
-
return path === parentPath.get('arguments').at(-1);
|
|
42
|
-
}
|
|
64
|
+
const callWithProperties = (fn) => (a, {properties}) => fn(properties);
|
|
65
|
+
const isFindUpIf = (path) => path.find(isIfUp);
|
|
43
66
|
|
|
44
|
-
|
|
45
|
-
const [root, properties] = chain(path);
|
|
46
|
-
|
|
47
|
-
if (isInsideMemberCall(path))
|
|
48
|
-
return false;
|
|
49
|
-
|
|
50
|
-
if (isExcludedFromChain(root))
|
|
51
|
-
return false;
|
|
52
|
-
|
|
53
|
-
if (isPathGet(properties))
|
|
54
|
-
return false;
|
|
55
|
-
|
|
56
|
-
if (properties.find(hasComment))
|
|
57
|
-
return true;
|
|
58
|
-
|
|
59
|
-
if (path.find(isIfUp))
|
|
60
|
-
return false;
|
|
61
|
-
|
|
67
|
+
const checkCallsCount = (path, {properties}) => {
|
|
62
68
|
const calls = properties.filter(isCall);
|
|
63
69
|
const [firstCall] = calls;
|
|
64
70
|
|
|
65
71
|
if (calls.length === 2 && !firstCall.name)
|
|
66
72
|
return false;
|
|
67
73
|
|
|
68
|
-
if (isArgOfCall(path))
|
|
69
|
-
return false;
|
|
70
|
-
|
|
71
74
|
return calls.length > 1;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
export const isLooksLikeChain = (path) => {
|
|
78
|
+
const [root, properties] = chain(path);
|
|
79
|
+
|
|
80
|
+
return isLikeChain(path, {
|
|
81
|
+
root,
|
|
82
|
+
properties,
|
|
77
83
|
});
|
|
78
84
|
};
|
|
79
85
|
|
|
86
|
+
const isLikeChain = createTypeChecker([
|
|
87
|
+
['-', isInsideMemberCall],
|
|
88
|
+
['-', callWithRoot(isExcludedFromChain)],
|
|
89
|
+
['-', callWithProperties(isPathGet)],
|
|
90
|
+
['+', callWithProperties(hasPropertyWithComment)],
|
|
91
|
+
['-', isFindUpIf],
|
|
92
|
+
['-', isFirstArgOfCall],
|
|
93
|
+
['+', checkCallsCount],
|
|
94
|
+
]);
|
|
95
|
+
|
|
80
96
|
const isIfUp = (path) => {
|
|
81
97
|
const ifPath = path.find(isIfStatement);
|
|
82
98
|
let is = false;
|
|
@@ -6,16 +6,14 @@ import {getDirectives} from './get-directives.js';
|
|
|
6
6
|
import {isCallInsideChain} from './is-call-inside-chain.js';
|
|
7
7
|
import {shouldAddNewlineAfter} from './block-statement-newline.js';
|
|
8
8
|
|
|
9
|
-
const isThree = (a) => a === 3;
|
|
10
|
-
|
|
11
9
|
const isInsideArrayTupleOfThree = createTypeChecker([
|
|
12
10
|
['-: parentPath.parentPath -> !ArrayExpression'],
|
|
13
|
-
['+: parentPath.parentPath.node.elements.length',
|
|
11
|
+
['+: parentPath.parentPath.node.elements.length', '=', 3],
|
|
14
12
|
]);
|
|
15
13
|
|
|
16
14
|
const hasDirectives = (a) => getDirectives(a).length;
|
|
17
15
|
|
|
18
|
-
const
|
|
16
|
+
const isNewlineAfterOpenCurlyBrace = createTypeChecker([
|
|
19
17
|
['+', hasDirectives],
|
|
20
18
|
['+: node.body.length -> +'],
|
|
21
19
|
]);
|
|
@@ -45,7 +43,7 @@ export const BlockStatement = {
|
|
|
45
43
|
maybe.indent.inc(!insideArray);
|
|
46
44
|
write('{');
|
|
47
45
|
|
|
48
|
-
if (
|
|
46
|
+
if (isNewlineAfterOpenCurlyBrace(path))
|
|
49
47
|
write.newline();
|
|
50
48
|
|
|
51
49
|
for (const directive of directives) {
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import {types} from '@putout/babel';
|
|
2
|
+
import {createTypeChecker} from '#type-checker';
|
|
2
3
|
import {isLooksLikeChain} from '../../expressions/member-expression/is-looks-like-chain.js';
|
|
3
4
|
|
|
4
|
-
const {
|
|
5
|
-
isReturnStatement,
|
|
6
|
-
isExpressionStatement,
|
|
7
|
-
isMemberExpression,
|
|
8
|
-
isCallExpression,
|
|
9
|
-
} = types;
|
|
5
|
+
const {isCallExpression} = types;
|
|
10
6
|
|
|
11
7
|
export const isCallInsideChain = (path) => {
|
|
12
8
|
if (!isCallExpression(path.parentPath.parentPath))
|
|
@@ -27,21 +23,13 @@ export const isCallInsideChain = (path) => {
|
|
|
27
23
|
return isLooksLikeChain(calleeMember);
|
|
28
24
|
};
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return isExpressionStatement(path.parentPath.parentPath);
|
|
35
|
-
}
|
|
26
|
+
const isTopMemberInsideCall = createTypeChecker([
|
|
27
|
+
'-: -> !MemberExpression',
|
|
28
|
+
'+: parentPath.parentPath -> ExpressionStatement',
|
|
29
|
+
]);
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (isReturnStatement(parentPath))
|
|
44
|
-
return true;
|
|
45
|
-
|
|
46
|
-
return isExpressionStatement(parentPath);
|
|
47
|
-
}
|
|
31
|
+
const isTopCall = createTypeChecker([
|
|
32
|
+
'-: -> !CallExpression',
|
|
33
|
+
'+: parentPath -> ReturnStatement',
|
|
34
|
+
'+: parentPath -> ExpressionStatement',
|
|
35
|
+
]);
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
const isFn = (a) => typeof a === 'function';
|
|
2
2
|
const isString = (a) => typeof a === 'string';
|
|
3
|
+
const maybeNot = (not, a) => not ? !a : a;
|
|
3
4
|
|
|
4
5
|
export const equal = (not, a, b) => {
|
|
6
|
+
if (!a)
|
|
7
|
+
return maybeNot(not, false);
|
|
8
|
+
|
|
9
|
+
const {type} = a;
|
|
10
|
+
|
|
5
11
|
if (!isString(b))
|
|
6
12
|
return false;
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
return a !== b;
|
|
10
|
-
|
|
11
|
-
return a === b;
|
|
14
|
+
return maybeNot(not, type === b);
|
|
12
15
|
};
|
|
13
16
|
|
|
14
17
|
export const maybeCall = (fn, not, a, options) => {
|
|
@@ -30,7 +30,7 @@ export function parseTypeNames(typeNames) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (isArray(typeName)) {
|
|
33
|
-
tuples.push(typeName);
|
|
33
|
+
tuples.push(parseComparison(typeName));
|
|
34
34
|
continue;
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -45,3 +45,27 @@ export function parseTypeNames(typeNames) {
|
|
|
45
45
|
|
|
46
46
|
return tuples;
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
const equal = (a) => (b) => b === a;
|
|
50
|
+
const more = (a) => (b) => b > a;
|
|
51
|
+
const less = (a) => (b) => b < a;
|
|
52
|
+
const moreOrEqual = (a) => (b) => b >= a;
|
|
53
|
+
const lessOrEqual = (a) => (b) => b <= a;
|
|
54
|
+
|
|
55
|
+
const CMP = {
|
|
56
|
+
'=': equal,
|
|
57
|
+
'>': more,
|
|
58
|
+
'<': less,
|
|
59
|
+
'>=': moreOrEqual,
|
|
60
|
+
'<=': lessOrEqual,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
function parseComparison(typeName) {
|
|
64
|
+
if (typeName.length === 3) {
|
|
65
|
+
const [result, comparison, value] = typeName;
|
|
66
|
+
|
|
67
|
+
return [result, CMP[comparison](value)];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return typeName;
|
|
71
|
+
}
|
|
@@ -37,7 +37,7 @@ export const createTypeChecker = (typeNames, overrides = {}) => {
|
|
|
37
37
|
if (selector)
|
|
38
38
|
currentPath = jessy(selector, path);
|
|
39
39
|
|
|
40
|
-
if (
|
|
40
|
+
if (equal(not, currentPath, typeName))
|
|
41
41
|
return [index, result];
|
|
42
42
|
|
|
43
43
|
if (maybeCall(typeName, not, currentPath, options))
|
package/package.json
CHANGED