@putout/printer 1.74.0 → 1.76.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,13 @@
1
+ 2023.04.28, v1.76.0
2
+
3
+ feature:
4
+ - a515fbc @putout/printer: improve support of newlines in with comments
5
+
6
+ 2023.04.27, v1.75.0
7
+
8
+ feature:
9
+ - 0560c33 @putout/printer: MemberExpression: simplify chain
10
+
1
11
  2023.04.26, v1.74.0
2
12
 
3
13
  feature:
@@ -22,8 +22,6 @@ const isStringAndString = ([a, b]) => isStringLiteral(a) && isStringLiteral(b);
22
22
  const isIdentifierAndIdentifier = ([a, b]) => isIdentifier(a) && isIdentifier(b);
23
23
  const isArrayParent = (path) => path.parentPath.isArrayExpression();
24
24
 
25
- const isTwoElementReturn = (path, {elements}) => path.parentPath.isReturnStatement() && elements.length === 2;
26
-
27
25
  const isTwoLongStrings = ([a, b]) => {
28
26
  const LONG_STRING = 20;
29
27
 
@@ -207,10 +205,10 @@ function isNewlineBetweenElements(path, {elements}) {
207
205
  if (isOneSimple(path))
208
206
  return false;
209
207
 
210
- if (isCallInsideArrow(path))
208
+ if (elements.length === 2 && isIdentifierAndIdentifier(elements))
211
209
  return false;
212
210
 
213
- if (isTwoElementReturn(path, {elements}))
211
+ if (isCallInsideArrow(path))
214
212
  return false;
215
213
 
216
214
  if (isOneElementCall(path, {elements}))
@@ -31,8 +31,7 @@ function CallExpression(path, {indent, print, maybe, traverse}) {
31
31
  const isObject = arg.isObjectExpression();
32
32
 
33
33
  if (isParentCall && !isObject && n) {
34
- print.newline();
35
- indent();
34
+ print.breakline();
36
35
  }
37
36
 
38
37
  print(arg);
@@ -0,0 +1,75 @@
1
+ 'use strict';
2
+
3
+ const {assign} = Object;
4
+
5
+ module.exports.chain = (path) => {
6
+ const all = [
7
+ ...down(path),
8
+ ...up(path),
9
+ ];
10
+
11
+ const properties = all.slice(0, -1);
12
+ const root = all.at(-1);
13
+
14
+ return [root, properties];
15
+ };
16
+
17
+ function down(path) {
18
+ const properties = [];
19
+
20
+ let current = path.get('object');
21
+
22
+ while (!current.isIdentifier()) {
23
+ const isFn = current.isCallExpression();
24
+ const prop = build(current);
25
+
26
+ if (isFn)
27
+ current = current.get('callee');
28
+
29
+ properties.unshift(prop);
30
+
31
+ if (!current.isMemberExpression())
32
+ break;
33
+
34
+ current = current.get('object');
35
+ }
36
+
37
+ return properties;
38
+ }
39
+
40
+ function up(current) {
41
+ const properties = [];
42
+
43
+ while (current.isMemberExpression()) {
44
+ current = current.parentPath;
45
+
46
+ if (current.isCallExpression()) {
47
+ properties.push(build(current));
48
+ current = current.parentPath;
49
+ }
50
+
51
+ if (!current.isMemberExpression())
52
+ break;
53
+ }
54
+
55
+ properties.push({
56
+ type: current.type,
57
+ });
58
+
59
+ return properties;
60
+ }
61
+
62
+ function build(path) {
63
+ const prop = {
64
+ type: path.type,
65
+ };
66
+
67
+ if (path.isCallExpression()) {
68
+ assign(prop, {
69
+ args: path.node.arguments.length,
70
+ name: path.node.callee.property?.name || '',
71
+ });
72
+ }
73
+
74
+ return prop;
75
+ }
@@ -31,7 +31,9 @@ function classVisitor(path, {print, indent, maybe}) {
31
31
 
32
32
  if (node.implements) {
33
33
  print(' implements ');
34
- path.get('implements').forEach(print);
34
+ path
35
+ .get('implements')
36
+ .forEach(print);
35
37
  }
36
38
 
37
39
  if (node.superClass) {
@@ -1,18 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const {
4
- isIfStatement,
5
- isIdentifier,
6
- isThisExpression,
7
4
  isUnaryExpression,
5
+ isArrowFunctionExpression,
6
+ isLogicalExpression,
8
7
  } = require('@babel/types');
9
8
 
10
- const {
11
- compare,
12
- getTemplateValues,
13
- } = require('@putout/compare');
9
+ const {chain} = require('./chain/chain');
14
10
 
15
- module.exports.MemberExpression = (path, {print, indent, maybe, traverse}) => {
11
+ module.exports.MemberExpression = (path, {print, maybe, traverse}) => {
16
12
  const {computed} = path.node;
17
13
  const object = path.get('object');
18
14
  const isObjectAwait = object.isAwaitExpression();
@@ -29,12 +25,12 @@ module.exports.MemberExpression = (path, {print, indent, maybe, traverse}) => {
29
25
  return;
30
26
  }
31
27
 
32
- const isChain = isLooksLikeChain(path);
28
+ const isChain = likeChain(path);
29
+
33
30
  maybe.indent.inc(isChain);
34
31
 
35
32
  if (isChain) {
36
- print.newline();
37
- indent(isChain);
33
+ print.breakline();
38
34
  }
39
35
 
40
36
  print('.');
@@ -58,66 +54,25 @@ module.exports.OptionalMemberExpression = (path, {print}) => {
58
54
  print('__property');
59
55
  };
60
56
 
61
- function isLooksLikeChain(path) {
62
- const {parentPath} = path;
63
-
64
- if (path.find(isIfStatement))
65
- return false;
66
-
67
- if (compare(parentPath.parentPath.parentPath, '__a.__b(__x).__c(__y)'))
68
- return true;
69
-
70
- if (compare(parentPath, '__a.__b(__x).__c(__y)'))
71
- return true;
72
-
73
- if (path.find(isUnaryExpression))
74
- return false;
75
-
76
- const isMember = ({parentPath}) => parentPath.parentPath.isMemberExpression();
77
- const isExpression = ({parentPath}) => parentPath.parentPath.isExpressionStatement();
78
- const itMember = isMember(path);
79
- const itExpression = isExpression(path);
80
- const callee = parentPath.get('callee');
81
-
82
- if (parentPath.isLiteral())
83
- return false;
84
-
85
- if (!itMember && !path.parentPath.isExpressionStatement() && !parentPath.isCallExpression())
86
- return false;
87
-
88
- if (!parentPath.isCallExpression())
89
- return false;
90
-
91
- if (callee !== path)
92
- return false;
93
-
94
- if (compare(path.parentPath, '__a.__b(__args)[__c]'))
95
- return false;
96
-
97
- if (compare(path.parentPath.parentPath, '__a.__b().__c()'))
98
- return false;
99
-
100
- if (compare(path.parentPath.parentPath, '(__args) => __b.__c(__args).__d()'))
101
- return false;
57
+ const isCall = (a) => a.type === 'CallExpression';
58
+
59
+ function likeChain(path) {
60
+ const [root, properties] = chain(path);
102
61
 
103
- if (compare(parentPath, '__a.__b.__c(__args)') && !itMember)
62
+ if (isUnaryExpression(root))
104
63
  return false;
105
64
 
106
- if (compare(parentPath, '__a.__b.__c = __d'))
65
+ if (isArrowFunctionExpression(root))
107
66
  return false;
108
67
 
109
- if (compare(parentPath, '(__args) => __a.__b(__args).__c'))
68
+ if (isLogicalExpression(root))
110
69
  return false;
111
70
 
112
- const {__a, __b} = getTemplateValues(parentPath, '__a.__b(__args)');
113
- const aType = __a?.type;
114
- const bType = __b?.type;
115
-
116
- if (aType === bType && isIdentifier(__a) && itExpression)
117
- return false;
71
+ const calls = properties.filter(isCall);
72
+ const [firstCall] = calls;
118
73
 
119
- if (isThisExpression(__a) && isIdentifier(__b) && itExpression)
74
+ if (calls.length === 2 && !firstCall.name)
120
75
  return false;
121
76
 
122
- return itExpression;
77
+ return calls.length > 1;
123
78
  }
@@ -7,7 +7,8 @@ const {
7
7
  noTrailingComment,
8
8
  isNewlineBetweenSiblings,
9
9
  noLeadingComment,
10
- noComment,
10
+ hasLeadingComment,
11
+ exists,
11
12
  } = require('../is');
12
13
 
13
14
  const {isFunction} = require('@babel/types');
@@ -52,8 +53,11 @@ module.exports.ObjectExpression = (path, {print, maybe, indent}) => {
52
53
  }
53
54
 
54
55
  print(property);
55
- maybe.print.newline(manyLines && noTrailingComment(property) && noComment(property));
56
- maybe.print.linebreak(isNewlineBetweenSiblings(property));
56
+
57
+ if (!hasNextLeadingComment(property)) {
58
+ maybe.print.newline(manyLines && noTrailingComment(property));
59
+ maybe.print.linebreak(isNewlineBetweenSiblings(property));
60
+ }
57
61
  }
58
62
 
59
63
  indent.dec();
@@ -63,6 +67,15 @@ module.exports.ObjectExpression = (path, {print, maybe, indent}) => {
63
67
  maybe.print(parens, ')');
64
68
  };
65
69
 
70
+ const hasNextLeadingComment = (path) => {
71
+ const next = path.getNextSibling();
72
+
73
+ if (!exists(next))
74
+ return false;
75
+
76
+ return hasLeadingComment(next);
77
+ };
78
+
66
79
  function shouldAddNewline(path) {
67
80
  if (!path.parentPath.isLogicalExpression())
68
81
  return false;
@@ -85,7 +85,7 @@ module.exports.satisfy = (conditions) => (path) => {
85
85
  };
86
86
 
87
87
  module.exports.hasTrailingComment = (path) => path.node?.trailingComments?.length;
88
+ module.exports.hasLeadingComment = (path) => path.node?.leadingComments?.length;
88
89
 
89
90
  module.exports.noTrailingComment = (path) => !path.node.trailingComments?.length;
90
- module.exports.noComment = (path) => !path.node.comments?.length;
91
91
  module.exports.noLeadingComment = (path) => !path.node.leadingComments?.length;
@@ -21,4 +21,3 @@ module.exports.JSXAttribute = {
21
21
  indent.dec();
22
22
  },
23
23
  };
24
-
@@ -9,16 +9,14 @@ module.exports.JSXText = (path, {write, indent}) => {
9
9
 
10
10
  if (isSpacesOnly && hasNext) {
11
11
  indent.inc();
12
- write.newline();
13
- write.indent();
12
+ write.breakline();
14
13
  indent.dec();
15
14
 
16
15
  return;
17
16
  }
18
17
 
19
18
  if (isSpacesOnly) {
20
- write.newline();
21
- write.indent();
19
+ write.breakline();
22
20
 
23
21
  return;
24
22
  }
@@ -9,12 +9,30 @@ const {
9
9
  satisfy,
10
10
  noTrailingComment,
11
11
  hasTrailingComment,
12
+ exists,
12
13
  } = require('../is');
13
14
 
15
+ const satisfyAfter = satisfy([
16
+ isParentBlockOrNotLastOrParentLast,
17
+ isParentBlock,
18
+ isNext,
19
+ isNextUp,
20
+ ]);
21
+
14
22
  const isNextLiteral = (path) => {
15
23
  const next = path.getNextSibling();
24
+
25
+ if (!exists(next))
26
+ return false;
27
+
16
28
  const expression = next.get('expression');
17
29
 
30
+ if (expression.isTemplateLiteral())
31
+ return true;
32
+
33
+ if (expression.isArrayExpression())
34
+ return true;
35
+
18
36
  return expression.isLiteral();
19
37
  };
20
38
 
@@ -36,15 +54,22 @@ module.exports.ExpressionStatement = {
36
54
  store(true);
37
55
  }
38
56
  },
39
- afterSatisfy: () => [
40
- isParentBlockOrNotLastOrParentLast,
41
- isParentBlock,
42
- isNext,
43
- isNextUp,
44
- ],
45
- after(path, {print, maybe, store}) {
57
+ afterIf: (path) => {
46
58
  if (hasTrailingComment(path) && isNextLiteral(path))
47
- return;
59
+ return false;
60
+
61
+ if (satisfyAfter(path))
62
+ return true;
63
+
64
+ if (hasTrailingComment(path) && isLast(path))
65
+ return true;
66
+
67
+ return false;
68
+ },
69
+ after(path, {print, maybe, store}) {
70
+ if (hasTrailingComment(path) && isLast(path)) {
71
+ print.breakline();
72
+ }
48
73
 
49
74
  print.newline();
50
75
  maybe.markAfter(store(), path);
@@ -8,7 +8,9 @@ module.exports.ForInStatement = (path, {print}) => {
8
8
  print('__right');
9
9
  print(')');
10
10
 
11
- if (path.get('body').isBlockStatement())
11
+ if (path
12
+ .get('body')
13
+ .isBlockStatement())
12
14
  print.space();
13
15
 
14
16
  print('__body');
@@ -64,7 +64,9 @@ module.exports.ImportDeclaration = {
64
64
  if (isLast(path))
65
65
  return false;
66
66
 
67
- if (path.getNextSibling().isImportDeclaration())
67
+ if (path
68
+ .getNextSibling()
69
+ .isImportDeclaration())
68
70
  return false;
69
71
 
70
72
  return true;
@@ -20,7 +20,9 @@ module.exports.SwitchStatement = {
20
20
  print(switchCase.get('test'));
21
21
  print(':');
22
22
 
23
- const isBlock = switchCase.get('consequent.0').isBlockStatement();
23
+ const isBlock = switchCase
24
+ .get('consequent.0')
25
+ .isBlockStatement();
24
26
 
25
27
  maybe.indent.inc(!isBlock);
26
28
  maybe.print.newline(!isBlock);
@@ -125,5 +125,7 @@ const isNextAssign = (path) => {
125
125
  if (parentPath.isBlockStatement() && parentPath.node.body.length < 3)
126
126
  return false;
127
127
 
128
- return nextPath.get('expression').isAssignmentExpression();
128
+ return nextPath
129
+ .get('expression')
130
+ .isAssignmentExpression();
129
131
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "1.74.0",
3
+ "version": "1.76.0",
4
4
  "type": "commonjs",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "Simplest possible opinionated Babel AST printer fro 🐊Putout",