@putout/printer 1.9.1 → 1.11.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
+ 2023.03.29, v1.11.0
2
+
3
+ feature:
4
+ - a2250dd @putout/printer: add newline at eof
5
+ - c1fb26f @putout/printer: use '__array.entries()' instead of 'entries(__array)'
6
+
7
+ 2023.03.28, v1.10.0
8
+
9
+ feature:
10
+ - c2f1d12 @putout/printer: add new pluginObject type of plugin
11
+
1
12
  2023.03.28, v1.9.1
2
13
 
3
14
  feature:
package/README.md CHANGED
@@ -60,7 +60,7 @@ print(ast, {
60
60
  });
61
61
 
62
62
  // returns
63
- 'const {a /* [hello world] */= 5} = b;';
63
+ 'const {a /* [hello world] */= 5} = b;\n';
64
64
  ```
65
65
 
66
66
  ## License
@@ -9,4 +9,3 @@ module.exports.cook = (tokens) => {
9
9
 
10
10
  return cookedTokens;
11
11
  };
12
-
@@ -5,61 +5,59 @@ const {
5
5
  hasPrevNewline,
6
6
  } = require('../mark');
7
7
 
8
- module.exports.OptionalCallExpression = (path, {indent, print, maybe}) => {
9
- return CallExpression(path, {
10
- indent,
11
- print,
12
- maybe,
13
- });
14
- };
15
-
16
8
  const {entries} = Object;
17
9
 
18
- module.exports.CallExpression = CallExpression;
19
-
20
- function CallExpression(path, {indent, print, maybe}) {
21
- const isParentCall = toLong(path) && path.parentPath.isCallExpression();
22
-
23
- if (shouldAddNewlineBefore(path)) {
10
+ const CallExpression = {
11
+ beforeIf(path) {
12
+ return isNewLineBefore(path) && !isMarkedParentBefore(path) && !hasPrevNewline(path.parentPath);
13
+ },
14
+ before(path, {print}) {
24
15
  print.breakline();
25
- }
26
-
27
- print('__callee');
28
-
29
- if (path.node.optional)
30
- print('?.');
31
-
32
- print('(');
33
-
34
- const args = path.get('arguments');
35
- const n = args.length - 1;
36
-
37
- maybe.indent.inc(isParentCall);
38
-
39
- for (const [i, arg] of entries(args)) {
40
- if (isParentCall) {
41
- print.newline();
42
- indent();
43
- }
16
+ },
17
+ print(path, {indent, print, maybe}) {
18
+ const isParentCall = toLong(path) && path.parentPath.isCallExpression();
19
+
20
+ print('__callee');
44
21
 
45
- print(arg);
22
+ if (path.node.optional)
23
+ print('?.');
24
+
25
+ print('(');
26
+
27
+ const args = path.get('arguments');
28
+ const n = args.length - 1;
29
+
30
+ maybe.indent.inc(isParentCall);
31
+
32
+ for (const [i, arg] of entries(args)) {
33
+ if (isParentCall) {
34
+ print.newline();
35
+ indent();
36
+ }
37
+
38
+ print(arg);
39
+
40
+ if (isParentCall) {
41
+ print(',');
42
+ continue;
43
+ }
44
+
45
+ maybe.print(i < n, ', ');
46
+ }
46
47
 
47
48
  if (isParentCall) {
48
- print(',');
49
- continue;
49
+ indent.dec();
50
+ print.newline();
51
+ print.indent();
50
52
  }
51
53
 
52
- maybe.print(i < n, ', ');
53
- }
54
-
55
- if (isParentCall) {
56
- indent.dec();
57
- print.newline();
58
- print.indent();
59
- }
60
-
61
- print(')');
62
- }
54
+ print(')');
55
+ },
56
+ };
57
+
58
+ module.exports.OptionalCallExpression = CallExpression;
59
+
60
+ module.exports.CallExpression = CallExpression;
63
61
 
64
62
  function isNewLineBefore({parentPath}) {
65
63
  if (!parentPath.isExpressionStatement())
@@ -92,7 +90,3 @@ function toLong(path) {
92
90
 
93
91
  return false;
94
92
  }
95
-
96
- function shouldAddNewlineBefore(path) {
97
- return isNewLineBefore(path) && !isMarkedParentBefore(path) && !hasPrevNewline(path.parentPath);
98
- }
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const {entries} = Object;
4
-
5
3
  module.exports.NewExpression = (path, {indent, print, maybe}) => {
6
4
  indent();
7
5
  print('new ');
@@ -12,7 +10,7 @@ module.exports.NewExpression = (path, {indent, print, maybe}) => {
12
10
 
13
11
  const n = args.length - 1;
14
12
 
15
- for (const [i, arg] of entries(args)) {
13
+ for (const [i, arg] of args.entries()) {
16
14
  print(arg);
17
15
  maybe.print(i < n, ', ');
18
16
  }
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const {entries} = Object;
4
3
  const isForOf = (path) => path.parentPath?.parentPath?.parentPath?.isForOfStatement();
5
4
 
6
5
  module.exports.ObjectPattern = (path, {indent, print, maybe}) => {
@@ -13,7 +12,7 @@ module.exports.ObjectPattern = (path, {indent, print, maybe}) => {
13
12
  const is = !isForOf(path) && !path.parentPath.isFunction() && n && checkLength(properties);
14
13
  maybe.print(is, '\n');
15
14
 
16
- for (const [i, property] of entries(properties)) {
15
+ for (const [i, property] of properties.entries()) {
17
16
  if (property.isRestElement()) {
18
17
  print(property);
19
18
  continue;
@@ -3,17 +3,27 @@
3
3
  const isParentProgram = (path) => path.parentPath?.isProgram();
4
4
  const isParentBlock = (path) => path.parentPath.isBlockStatement();
5
5
  const isNext = (path) => path.getNextSibling().node;
6
+ const isNextParent = (path) => path.parentPath.getNextSibling().node;
7
+ const isLast = (path) => isParentProgram(path) && !isNext(path);
6
8
 
7
9
  module.exports.isFirst = (path) => path.node === path.parentPath.node.body[0];
8
10
  module.exports.isPrevBody = (path) => path.getPrevSibling().isBlockStatement();
9
11
  module.exports.isNext = isNext;
12
+ module.exports.isNextParent = isNextParent;
10
13
  module.exports.isParentProgram = isParentProgram;
11
14
  module.exports.isParentBlock = isParentBlock;
12
- module.exports.isLast = (path) => isParentProgram(path) && !isNext(path);
15
+ module.exports.isLast = isLast;
16
+ module.exports.isParentLast = (path) => isLast(path.parentPath);
17
+ module.exports.isCoupleLines = isCoupleLines;
18
+ module.exports.isNextCoupleLines = isNextCoupleLines;
13
19
 
14
- module.exports.isCoupleLines = (path) => {
20
+ function isCoupleLines(path) {
15
21
  const start = path.node?.loc?.start?.line;
16
22
  const end = path.node?.loc?.end?.line;
17
23
 
18
24
  return end > start;
19
- };
25
+ }
26
+
27
+ function isNextCoupleLines(path) {
28
+ return isCoupleLines(path.getNextSibling());
29
+ }
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const isFn = (a) => typeof a === 'function';
4
+
3
5
  const {
4
6
  isProgram,
5
7
  isFile,
@@ -13,3 +15,31 @@ const maybeProgram = (ast) => isProgram(ast) ? ast : Program([
13
15
  ]);
14
16
 
15
17
  module.exports.maybeFile = (ast) => isFile(ast) ? ast : File(maybeProgram(ast));
18
+
19
+ module.exports.maybePlugin = (plugin, path, printer) => {
20
+ if (isFn(plugin))
21
+ return plugin(path, printer);
22
+
23
+ return objectPlugin(plugin, path, printer);
24
+ };
25
+
26
+ function objectPlugin(plugin, path, printer) {
27
+ const {
28
+ print,
29
+ split,
30
+ before = split,
31
+ beforeIf,
32
+ after = split,
33
+ afterIf,
34
+ } = plugin;
35
+
36
+ if (beforeIf?.(path, printer)) {
37
+ before(path, printer);
38
+ }
39
+
40
+ print(path, printer);
41
+
42
+ if (afterIf?.(path, printer)) {
43
+ after(path, printer);
44
+ }
45
+ }
@@ -3,37 +3,43 @@
3
3
  const {
4
4
  isNext,
5
5
  isParentProgram,
6
+ isLast,
7
+
6
8
  } = require('../is');
7
9
 
8
10
  const isFirstStatement = (path) => path.get('body.0')?.isStatement();
9
11
 
10
- module.exports.BlockStatement = (path, {indent, maybe, print}) => {
11
- const body = path.get('body');
12
-
13
- if (path.parentPath.isBlockStatement())
14
- indent();
15
-
16
- indent.inc();
17
- print('{');
18
-
19
- if (body.length > 1 || isFirstStatement(path))
12
+ module.exports.BlockStatement = {
13
+ print(path, {indent, maybe, print}) {
14
+ const body = path.get('body');
15
+
16
+ if (path.parentPath.isBlockStatement())
17
+ indent();
18
+
19
+ indent.inc();
20
+ print('{');
21
+
22
+ if (body.length > 1 || isFirstStatement(path))
23
+ print.newline();
24
+
25
+ for (const element of body) {
26
+ print(element);
27
+ }
28
+
29
+ indent.dec();
30
+ maybe.indent(body.length);
31
+ print('}');
32
+
33
+ if (path.parentPath.isObjectMethod()) {
34
+ print(',');
35
+ }
36
+ },
37
+ afterIf(path) {
38
+ return shouldAddNewlineAfter(path);
39
+ },
40
+ after(path, {print}) {
20
41
  print.newline();
21
-
22
- for (const element of body) {
23
- print(element);
24
- }
25
-
26
- indent.dec();
27
- maybe.indent(body.length);
28
- print('}');
29
-
30
- if (path.parentPath.isObjectMethod()) {
31
- print(',');
32
- }
33
-
34
- if (shouldAddNewlineAfter(path)) {
35
- print.newline();
36
- }
42
+ },
37
43
  };
38
44
 
39
45
  function shouldAddNewlineAfter(path) {
@@ -48,6 +54,9 @@ function shouldAddNewlineAfter(path) {
48
54
  if (isTry(path) || /FunctionExpression/.test(path.parentPath.type))
49
55
  return false;
50
56
 
57
+ if (isLast(path))
58
+ return false;
59
+
51
60
  return true;
52
61
  }
53
62
 
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ isNext,
5
+ isParentBlock,
6
+ isNextParent,
7
+ } = require('../is');
8
+
9
+ module.exports.BreakStatement = {
10
+ split(path, {print}) {
11
+ print.newline();
12
+ },
13
+ print(path, {print, indent}) {
14
+ indent();
15
+ print('break;');
16
+ },
17
+ afterIf(path) {
18
+ if (!isNext(path) && !isParentBlock(path) && !isNextParent(path))
19
+ return false;
20
+
21
+ return true;
22
+ },
23
+ };
@@ -1,15 +1,13 @@
1
1
  'use strict';
2
2
 
3
- const {
4
- markBefore,
5
- hasPrevNewline,
6
- } = require('../mark');
7
-
8
3
  const {
9
4
  isNext,
10
5
  isParentProgram,
11
6
  isLast,
12
7
  isParentBlock,
8
+ isCoupleLines,
9
+ isNextCoupleLines,
10
+ isParentLast,
13
11
  } = require('../is');
14
12
 
15
13
  const isNextDifferent = (path) => {
@@ -21,33 +19,29 @@ const isNextDifferent = (path) => {
21
19
  return next.node.expression.type !== path.node.expression.type;
22
20
  };
23
21
 
24
- module.exports.ExpressionStatement = (path, {indent, print, maybe}) => {
25
- if (isCoupleLinesExpression(path) && !isFirst(path) && shouldAddNewlineBefore(path) && !hasPrevNewline(path)) {
26
- print.breakline();
27
- markBefore(path);
28
- }
29
-
30
- indent();
31
- print('__expression');
32
- print(';');
33
-
34
- let wasNewline = false;
35
-
36
- if (shouldBreakline(path)) {
37
- print.newline();
38
- maybe.indent(isNext(path));
22
+ module.exports.ExpressionStatement = {
23
+ print(path, {indent, print, maybe, store}) {
24
+ indent();
25
+ print('__expression');
26
+ print(';');
39
27
 
40
- wasNewline = true;
41
- }
42
-
43
- if (shouldAddNewLineAfter(path)) {
28
+ if (shouldBreakline(path)) {
29
+ print.newline();
30
+ maybe.indent(isNext(path));
31
+ store(true);
32
+ }
33
+ },
34
+ afterIf(path) {
35
+ return shouldAddNewLineAfter(path);
36
+ },
37
+ after(path, {print, maybe, store}) {
44
38
  print.newline();
45
- maybe.markAfter(wasNewline, path);
46
- }
39
+ maybe.markAfter(store(), path);
40
+ },
47
41
  };
48
42
 
49
43
  function shouldBreakline(path) {
50
- if (isLast(path) || isLast(path.parentPath))
44
+ if (isLast(path) || isParentLast(path))
51
45
  return false;
52
46
 
53
47
  if (!isNext(path) && isParentBlock(path))
@@ -56,17 +50,20 @@ function shouldBreakline(path) {
56
50
  if (path.parentPath.get('body') === path)
57
51
  return true;
58
52
 
59
- if (isStrictMode(path) || isCoupleLinesExpression(path))
53
+ if (isStrictMode(path))
60
54
  return true;
61
55
 
62
56
  if (isNext(path) && isNextDifferent(path) && path.parentPath.node.body?.length > 2)
63
57
  return true;
64
58
 
59
+ if (isCoupleLines(path) || isNextCoupleLines(path))
60
+ return true;
61
+
65
62
  return false;
66
63
  }
67
64
 
68
65
  function shouldAddNewLineAfter(path) {
69
- if (isLast(path))
66
+ if (isLast(path) || isParentLast(path))
70
67
  return false;
71
68
 
72
69
  if (isParentBlock(path) && !isParentProgram(path))
@@ -78,13 +75,6 @@ function shouldAddNewLineAfter(path) {
78
75
  return false;
79
76
  }
80
77
 
81
- function isCoupleLinesExpression(path) {
82
- const start = path.node.loc?.start?.line;
83
- const end = path.node.loc?.end?.line;
84
-
85
- return end > start;
86
- }
87
-
88
78
  function isStrictMode(path) {
89
79
  const expressionPath = path.get('expression');
90
80
 
@@ -95,22 +85,3 @@ function isStrictMode(path) {
95
85
 
96
86
  return value === 'use strict';
97
87
  }
98
-
99
- function isFirst(path) {
100
- return path.node === path.parentPath.node.body[0];
101
- }
102
-
103
- function shouldAddNewlineBefore(path) {
104
- const prev = path.getPrevSibling();
105
-
106
- if (prev.isVariableDeclaration())
107
- return false;
108
-
109
- if (prev.isIfStatement())
110
- return false;
111
-
112
- if (isStrictMode(prev))
113
- return false;
114
-
115
- return true;
116
- }
@@ -1,36 +1,42 @@
1
1
  'use strict';
2
2
 
3
- module.exports.ForStatement = (path, {print, maybe, indent}) => {
4
- const {
5
- test,
6
- update,
7
- body,
8
- } = path.node;
9
-
10
- print('for (');
11
- print('__init');
12
- print(';');
13
- maybe.print(test, ' ');
14
- print('__test');
15
- print(';');
16
- maybe.print(update, ' ');
17
- print('__update');
18
- print(')');
19
-
20
- if (body.body) {
21
- print(' ');
22
- print('__body');
23
- print.newline();
3
+ const {isLast} = require('../is');
4
+
5
+ module.exports.ForStatement = {
6
+ print(path, {print, maybe, indent}) {
7
+ const {
8
+ test,
9
+ update,
10
+ body,
11
+ } = path.node;
24
12
 
25
- return;
26
- }
27
-
28
- if (!body.body) {
29
- print.newline();
30
- indent.inc();
31
- print('__body');
32
- indent.dec();
13
+ print('for (');
14
+ print('__init');
15
+ print(';');
16
+ maybe.print(test, ' ');
17
+ print('__test');
18
+ print(';');
19
+ maybe.print(update, ' ');
20
+ print('__update');
21
+ print(')');
22
+
23
+ if (body.body) {
24
+ print(' ');
25
+ print('__body');
26
+ } else {
27
+ print.newline();
28
+ indent.inc();
29
+ print('__body');
30
+ indent.dec();
31
+ }
32
+ },
33
+ after(path, {print}) {
33
34
  print.newline();
34
- }
35
+ },
36
+ afterIf(path) {
37
+ if (isLast(path))
38
+ return false;
39
+
40
+ return true;
41
+ },
35
42
  };
36
-
@@ -1,20 +1,19 @@
1
1
  'use strict';
2
2
 
3
3
  const {markAfter} = require('../mark');
4
- const {entries} = Object;
5
4
 
6
5
  module.exports.ImportDeclaration = (path, {print, maybe}) => {
7
6
  const specifiers = path.get('specifiers');
8
7
  print('import ');
9
8
 
10
- for (const [index, spec] of entries(specifiers)) {
9
+ for (const [index, spec] of specifiers.entries()) {
11
10
  if (spec.isImportDefaultSpecifier()) {
12
11
  print(spec.get('local'));
13
12
  continue;
14
13
  }
15
14
 
16
15
  if (spec.isImportSpecifier()) {
17
- maybe.print(Number(index), ', ');
16
+ maybe.print(index, ', ');
18
17
  print('{');
19
18
  print(spec.get('imported'));
20
19
  print('}');
@@ -15,6 +15,7 @@ const {WhileStatement} = require('./while-statement');
15
15
  const {SwitchStatement} = require('./switch-statement');
16
16
  const {ForInStatement} = require('./for-in-statement');
17
17
  const {ExportDefaultDeclaration} = require('./export-default-declaration');
18
+ const {BreakStatement} = require('./break-statement');
18
19
 
19
20
  module.exports = {
20
21
  ...importDeclarations,
@@ -31,14 +32,11 @@ module.exports = {
31
32
  DebuggerStatement,
32
33
  Program(path, {print}) {
33
34
  path.get('body').forEach(print);
35
+ print.newline();
34
36
  },
35
37
  SwitchStatement,
36
38
  ...TryStatements,
37
- BreakStatement(path, {print, indent}) {
38
- indent();
39
- print('break;');
40
- print.newline();
41
- },
39
+ BreakStatement,
42
40
  ContinueStatement(path, {indent, print}) {
43
41
  indent();
44
42
  print('continue;');
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ const fullstore = require('fullstore');
4
+
3
5
  const isObject = (a) => a && typeof a === 'object';
4
6
  const babelTraverse = require('@babel/traverse').default;
5
7
  const expressions = require('./expressions');
6
8
  const statements = require('./statements');
7
9
  const literals = require('./literals');
8
10
  const {TYPES} = require('../types');
9
- const {maybeFile} = require('./maybe');
11
+ const {maybeFile, maybePlugin} = require('./maybe');
10
12
 
11
13
  const {
12
14
  createDebug,
@@ -137,6 +139,7 @@ module.exports.tokenize = (ast, overrides = {}) => {
137
139
  debug,
138
140
  traverse,
139
141
  maybe,
142
+ store: fullstore(),
140
143
  };
141
144
 
142
145
  const currentTraversers = {
@@ -187,8 +190,9 @@ module.exports.tokenize = (ast, overrides = {}) => {
187
190
  throw Error(`Node type '${type}' is not supported yet: '${path}'`);
188
191
 
189
192
  parseLeadingComments(path, printer);
190
- currentTraverse(path, printer);
193
+ maybePlugin(currentTraverse, path, printer);
191
194
  parseTrailingComments(path, printer);
195
+
192
196
  debug(path.type);
193
197
  }
194
198
 
@@ -220,3 +224,4 @@ const createPrint = (path, {traverse, write}) => (maybeLine) => {
220
224
 
221
225
  return write(maybeLine);
222
226
  };
227
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "1.9.1",
3
+ "version": "1.11.0",
4
4
  "type": "commonjs",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "Easiest possible opinionated Babel AST printer made with ❤️ to use in 🐊Putout",
@@ -30,6 +30,7 @@
30
30
  "@babel/parser": "^7.19.0",
31
31
  "@babel/traverse": "^7.21.2",
32
32
  "@babel/types": "^7.21.3",
33
+ "fullstore": "^3.0.0",
33
34
  "just-snake-case": "^3.2.0"
34
35
  },
35
36
  "keywords": [