@putout/printer 1.9.0 → 1.10.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,15 @@
1
+ 2023.03.28, v1.10.0
2
+
3
+ feature:
4
+ - c2f1d12 @putout/printer: add new pluginObject type of plugin
5
+
6
+ 2023.03.28, v1.9.1
7
+
8
+ feature:
9
+ - 063bb53 @putout/printer: improve support of AST witout parent File
10
+ - d6fd09d @putout/printer: add support of trailingComments
11
+ - a323b20 @putout/printer: shouldAddNewlineAfter
12
+
1
13
  2023.03.27, v1.9.0
2
14
 
3
15
  feature:
@@ -3,7 +3,7 @@
3
3
  module.exports.cook = (tokens) => {
4
4
  const cookedTokens = [];
5
5
 
6
- for (const [i, {value}] of tokens.entries()) {
6
+ for (const {value} of tokens) {
7
7
  cookedTokens.push(value);
8
8
  }
9
9
 
package/lib/printer.js CHANGED
@@ -5,5 +5,7 @@ const {printTokens} = require('./print-tokens');
5
5
 
6
6
  module.exports.print = (ast, overrides) => {
7
7
  const tokens = tokenize(ast, overrides);
8
- return printTokens(tokens);
8
+ const result = printTokens(tokens);
9
+
10
+ return result;
9
11
  };
@@ -3,15 +3,11 @@
3
3
  const {isFirst} = require('./is');
4
4
  const {markBefore} = require('./mark');
5
5
 
6
- module.exports.parseComments = (path, {print, indent, maybe}) => {
6
+ module.exports.parseLeadingComments = (path, {print, indent}) => {
7
7
  const {leadingComments} = path.node;
8
8
 
9
- if (leadingComments)
10
- parseLeadingComments(path, {print, indent, maybe});
11
- };
12
-
13
- function parseLeadingComments(path, {print, indent}) {
14
- const {leadingComments} = path.node;
9
+ if (!leadingComments || !leadingComments.length)
10
+ return;
15
11
 
16
12
  if (shouldAddNewlineBefore(path))
17
13
  print.linebreak();
@@ -22,6 +18,7 @@ function parseLeadingComments(path, {print, indent}) {
22
18
  if (type === 'CommentLine') {
23
19
  print(`//${value}`);
24
20
  print.newline();
21
+
25
22
  continue;
26
23
  }
27
24
 
@@ -36,9 +33,24 @@ function parseLeadingComments(path, {print, indent}) {
36
33
  continue;
37
34
  }
38
35
  }
39
- }
36
+ };
37
+
38
+ module.exports.parseTrailingComments = (path, {print}) => {
39
+ const {trailingComments} = path.node;
40
+
41
+ if (!trailingComments || !trailingComments.length)
42
+ return;
43
+
44
+ for (const {type, value} of trailingComments) {
45
+ if (type === 'CommentLine') {
46
+ print.space();
47
+ print(`//${value}`);
48
+
49
+ continue;
50
+ }
51
+ }
52
+ };
40
53
 
41
54
  function shouldAddNewlineBefore(path) {
42
55
  return path.isStatement() && !isFirst(path);
43
56
  }
44
-
@@ -1,10 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ const {stringify} = JSON;
4
+
3
5
  const {TYPES} = require('../types');
4
6
  const toSnakeCase = require('just-snake-case');
5
7
  const {
6
8
  LOG,
7
9
  LOG_ALL,
10
+ LOG_TOKENS,
8
11
  DEBUG,
9
12
  } = process.env;
10
13
  const {codeFrameColumns} = require('@babel/code-frame');
@@ -19,23 +22,30 @@ module.exports.createDebug = (tokens) => (a) => {
19
22
  });
20
23
  };
21
24
 
22
- module.exports.createLog = ({newline = '\n', store = createStore()} = {}) => (chunk) => {
25
+ module.exports.createLog = ({newline = '\n', store = createStore()} = {}) => ({type, value}) => {
26
+ if (LOG_TOKENS) {
27
+ console.log(codeFrameColumns(stringify({type, value}), {}, {
28
+ highlightCode: true,
29
+ }));
30
+ return;
31
+ }
32
+
23
33
  if (LOG_ALL) {
24
- console.log(codeFrameColumns(chunk, {}, {
34
+ console.log(codeFrameColumns(value, {}, {
25
35
  highlightCode: true,
26
36
  }));
27
37
  return;
28
38
  }
29
39
 
30
40
  if (LOG) {
31
- if (chunk === newline) {
41
+ if (value === newline) {
32
42
  console.log(codeFrameColumns(store(), {}, {
33
43
  highlightCode: true,
34
44
  }));
35
45
  return;
36
46
  }
37
47
 
38
- store(chunk);
48
+ store(value);
39
49
  }
40
50
  };
41
51
 
@@ -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 (isNewLineBefore(path) && !isMarkedParentBefore(path) && !hasPrevNewline(path.parentPath)) {
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();
44
19
 
45
- print(arg);
20
+ print('__callee');
21
+
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())
@@ -1,14 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const {entries} = Object;
4
-
5
- const isFirst = (path) => path.node === path.parentPath.node.body[0];
6
-
7
4
  module.exports.ClassDeclaration = (path, {maybe, print, indent}) => {
8
- if (!isFirst(path)) {
9
- print.breakline();
10
- }
11
-
12
5
  indent();
13
6
  print('class ');
14
7
  print('__id');
@@ -4,13 +4,19 @@ const functions = require('./functions');
4
4
  const unaryExpressions = require('./unary-expressions');
5
5
  const memberExpressions = require('./member-expressions');
6
6
  const {ClassDeclaration} = require('./class-declaration');
7
+
7
8
  const {
8
9
  CallExpression,
9
10
  OptionalCallExpression,
10
11
  } = require('./call-expression');
11
12
 
12
13
  const {NewExpression} = require('./new-expression');
13
- const {ObjectExpression} = require('./object-expression');
14
+
15
+ const {
16
+ ObjectExpression,
17
+ ObjectProperty,
18
+ } = require('./object-expression');
19
+
14
20
  const {ObjectPattern} = require('./object-pattern');
15
21
  const {AssignmentExpression} = require('./assignment-expression');
16
22
  const {ArrayExpression} = require('./array-expression');
@@ -20,10 +26,12 @@ const {RestElement} = require('./rest-element');
20
26
  const {SpreadElement} = require('./spread-element');
21
27
  const {SequenceExpression} = require('./sequence-expression');
22
28
  const {TaggedTemplateExpression} = require('./tagged-template-expression');
29
+
23
30
  const {
24
31
  BinaryExpression,
25
32
  LogicalExpression,
26
33
  } = require('./binary-expression');
34
+
27
35
  const {ConditionalExpression} = require('./conditional-expression');
28
36
 
29
37
  module.exports = {
@@ -42,6 +50,7 @@ module.exports = {
42
50
  LogicalExpression,
43
51
  OptionalCallExpression,
44
52
  ObjectExpression,
53
+ ObjectProperty,
45
54
  ObjectPattern,
46
55
  RestElement,
47
56
  SpreadElement,
@@ -3,6 +3,7 @@
3
3
  const {isCoupleLines} = require('../is');
4
4
  const isBodyOfArrow = (path) => path.parentPath.node.body === path.node;
5
5
  const isLogical = (path) => path.get('argument').isLogicalExpression();
6
+
6
7
  const isForOf = (path) => {
7
8
  if (path.parentPath.isForOfStatement())
8
9
  return true;
@@ -20,7 +21,6 @@ module.exports.ObjectExpression = (path, {print, maybe, indent}) => {
20
21
 
21
22
  maybe.print(parens, '(');
22
23
  print('{');
23
-
24
24
  maybe.print(manyLines, '\n');
25
25
 
26
26
  for (const property of properties) {
@@ -36,8 +36,6 @@ module.exports.ObjectExpression = (path, {print, maybe, indent}) => {
36
36
  continue;
37
37
  }
38
38
 
39
- const {shorthand, computed} = property.node;
40
-
41
39
  maybe.indent(manyLines);
42
40
 
43
41
  if (property.isObjectMethod()) {
@@ -45,25 +43,36 @@ module.exports.ObjectExpression = (path, {print, maybe, indent}) => {
45
43
  continue;
46
44
  }
47
45
 
48
- maybe.print(computed, '[');
49
- print(property.get('key'));
50
- maybe.print(computed, ']');
51
-
52
- if (!shorthand) {
53
- print(': ');
54
- print(property.get('value'));
55
- }
56
-
57
- maybe.print(manyLines, ',\n');
46
+ print(property);
47
+ maybe.print.newline(manyLines);
58
48
  }
59
49
 
60
50
  indent.dec();
61
-
62
51
  maybe.indent(manyLines);
63
52
  print('}');
64
53
  maybe.print.newline(path.parentPath.isLogicalExpression());
65
54
  maybe.print(parens, ')');
66
55
  };
56
+
57
+ module.exports.ObjectProperty = (path, {print, maybe}) => {
58
+ const {
59
+ shorthand,
60
+ computed,
61
+ } = path.node;
62
+ const manyLines = !isOneLine(path.parentPath);
63
+
64
+ maybe.print(computed, '[');
65
+ print('__key');
66
+ maybe.print(computed, ']');
67
+
68
+ if (!shorthand) {
69
+ print(': ');
70
+ print('__value');
71
+ }
72
+
73
+ maybe.print(manyLines, ',');
74
+ };
75
+
67
76
  function isOneLine(path) {
68
77
  const isCall = path.parentPath.isCallExpression();
69
78
  const {length} = path.get('properties');
@@ -1,15 +1,29 @@
1
1
  'use strict';
2
2
 
3
+ const isParentProgram = (path) => path.parentPath?.isProgram();
4
+ const isParentBlock = (path) => path.parentPath.isBlockStatement();
5
+ const isNext = (path) => path.getNextSibling().node;
6
+ const isNextParent = (path) => path.parentPath.getNextSibling().node;
7
+ const isLast = (path) => isParentProgram(path) && !isNext(path);
8
+
3
9
  module.exports.isFirst = (path) => path.node === path.parentPath.node.body[0];
4
10
  module.exports.isPrevBody = (path) => path.getPrevSibling().isBlockStatement();
5
- module.exports.isNext = (path) => path.getNextSibling().node;
6
- module.exports.isProgramParent = (path) => path.parentPath?.isProgram();
7
- module.exports.isNextParent = (path) => path.parentPath.getNextSibling().node;
8
- module.exports.isNextBlock = (path) => path.getNextSibling().isBlockStatement();
11
+ module.exports.isNext = isNext;
12
+ module.exports.isNextParent = isNextParent;
13
+ module.exports.isParentProgram = isParentProgram;
14
+ module.exports.isParentBlock = isParentBlock;
15
+ module.exports.isLast = isLast;
16
+ module.exports.isParentLast = (path) => isLast(path.parentPath);
17
+ module.exports.isCoupleLines = isCoupleLines;
18
+ module.exports.isNextCoupleLines = isNextCoupleLines;
9
19
 
10
- module.exports.isCoupleLines = (path) => {
20
+ function isCoupleLines(path) {
11
21
  const start = path.node?.loc?.start?.line;
12
22
  const end = path.node?.loc?.end?.line;
13
23
 
14
24
  return end > start;
15
- };
25
+ }
26
+
27
+ function isNextCoupleLines(path) {
28
+ return isCoupleLines(path.getNextSibling());
29
+ }
@@ -4,24 +4,24 @@ const {TemplateLiteral} = require('./template-literal');
4
4
 
5
5
  module.exports = {
6
6
  TemplateLiteral,
7
- NumericLiteral(path, {print}) {
7
+ NumericLiteral(path, {write}) {
8
8
  const {
9
9
  raw,
10
10
  extra,
11
11
  } = path.node;
12
12
 
13
- print(raw || extra.raw);
13
+ write(raw || extra.raw);
14
14
  },
15
- BooleanLiteral(path, {print}) {
16
- print(path.node.value);
15
+ BooleanLiteral(path, {write}) {
16
+ write(path.node.value);
17
17
  },
18
- StringLiteral(path, {print}) {
18
+ StringLiteral(path, {write}) {
19
19
  const {
20
20
  raw,
21
21
  value,
22
22
  } = path.node;
23
23
 
24
- print(raw || `'${value}'`);
24
+ write(raw || `'${value}'`);
25
25
  },
26
26
  Identifier(path, {write}) {
27
27
  write(path.node.name);
@@ -29,7 +29,10 @@ module.exports = {
29
29
  RegExpLiteral(path, {print}) {
30
30
  print(path.node.raw);
31
31
  },
32
- NullLiteral(path, {print}) {
33
- print('null');
32
+ NullLiteral(path, {write}) {
33
+ write('null');
34
+ },
35
+ MetaProperty(path, {write}) {
36
+ write('import.meta');
34
37
  },
35
38
  };
@@ -5,6 +5,7 @@ const WATER_MARK_AFTER = '__putout_newline_after';
5
5
 
6
6
  module.exports.markBefore = markBefore;
7
7
  module.exports.markAfter = markAfter;
8
+ module.exports.maybeMarkAfter = (a, path) => a && markAfter(path);
8
9
 
9
10
  function markBefore(path) {
10
11
  path[WATER_MARK_BEFORE] = true;
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ const isFn = (a) => typeof a === 'function';
4
+
5
+ const {
6
+ isProgram,
7
+ isFile,
8
+ File,
9
+ ExpressionStatement,
10
+ Program,
11
+ } = require('@babel/types');
12
+
13
+ const maybeProgram = (ast) => isProgram(ast) ? ast : Program([
14
+ ExpressionStatement(ast),
15
+ ]);
16
+
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
+ }
@@ -2,8 +2,9 @@
2
2
 
3
3
  const {
4
4
  isNext,
5
- isProgramParent,
5
+ isParentProgram,
6
6
  } = require('../is');
7
+
7
8
  const isFirstStatement = (path) => path.get('body.0')?.isStatement();
8
9
 
9
10
  module.exports.BlockStatement = (path, {indent, maybe, print}) => {
@@ -38,7 +39,7 @@ module.exports.BlockStatement = (path, {indent, maybe, print}) => {
38
39
  function shouldAddNewlineAfter(path) {
39
40
  const {parentPath} = path;
40
41
 
41
- if (!isNext(path) && !isNext(path.parentPath) && isProgramParent(path.parentPath))
42
+ if (!isNext(path) && !isNext(path.parentPath) && isParentProgram(path.parentPath))
42
43
  return false;
43
44
 
44
45
  if (parentPath.isStatement() && !path.node.body.length && !isNext(parentPath))
@@ -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,19 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const {isFirst} = require('../is');
4
-
5
3
  module.exports.ExportDefaultDeclaration = (path, {print}) => {
6
- if (shouldAddNewlineBefore(path))
7
- print.newline();
8
-
9
4
  print('export default ');
10
5
  print('__declaration');
11
6
  print(';');
12
7
  };
13
-
14
- function shouldAddNewlineBefore(path) {
15
- if (isFirst(path))
16
- return false;
17
-
18
- return true;
19
- }
@@ -1,53 +1,78 @@
1
1
  'use strict';
2
2
 
3
- const {
4
- markBefore,
5
- markAfter,
6
- hasPrevNewline,
7
- } = require('../mark');
8
-
9
3
  const {
10
4
  isNext,
11
- isProgramParent,
12
- isNextParent,
13
-
5
+ isParentProgram,
6
+ isLast,
7
+ isParentBlock,
8
+ isCoupleLines,
9
+ isNextCoupleLines,
10
+ isParentLast,
14
11
  } = require('../is');
15
12
 
16
- module.exports.ExpressionStatement = (path, {indent, print}) => {
17
- if (isCoupleLinesExpression(path) && !isFirst(path) && shouldAddNewlineBefore(path) && !hasPrevNewline(path)) {
18
- print.breakline();
19
- markBefore(path);
20
- }
13
+ const isNextDifferent = (path) => {
14
+ const next = path.getNextSibling();
21
15
 
22
- indent();
23
- print('__expression');
24
- print(';');
25
-
26
- if (isNext(path) || isNextParent(path) || path.parentPath.isBlockStatement() && !isProgramParent(path)) {
27
- print.newline();
28
- }
16
+ if (!next.isExpressionStatement())
17
+ return true;
29
18
 
30
- if (shouldAddNewLineAfter(path)) {
19
+ return next.node.expression.type !== path.node.expression.type;
20
+ };
21
+
22
+ module.exports.ExpressionStatement = {
23
+ print(path, {indent, print, maybe, store}) {
24
+ indent();
25
+ print('__expression');
26
+ print(';');
27
+
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}) {
31
38
  print.newline();
32
- markAfter(path);
33
- }
39
+ maybe.markAfter(store(), path);
40
+ },
34
41
  };
35
42
 
36
- function shouldAddNewLineAfter(path) {
37
- if (!isNext(path))
43
+ function shouldBreakline(path) {
44
+ if (isLast(path) || isParentLast(path))
38
45
  return false;
39
46
 
40
- if (isStrictMode(path) || isCoupleLinesExpression(path))
47
+ if (!isNext(path) && isParentBlock(path))
48
+ return false;
49
+
50
+ if (path.parentPath.get('body') === path)
51
+ return true;
52
+
53
+ if (isStrictMode(path))
54
+ return true;
55
+
56
+ if (isNext(path) && isNextDifferent(path) && path.parentPath.node.body?.length > 2)
57
+ return true;
58
+
59
+ if (isCoupleLines(path) || isNextCoupleLines(path))
41
60
  return true;
42
61
 
43
62
  return false;
44
63
  }
45
64
 
46
- function isCoupleLinesExpression(path) {
47
- const start = path.node.loc?.start?.line;
48
- const end = path.node.loc?.end?.line;
65
+ function shouldAddNewLineAfter(path) {
66
+ if (isLast(path) || isParentLast(path))
67
+ return false;
68
+
69
+ if (isParentBlock(path) && !isParentProgram(path))
70
+ return true;
71
+
72
+ if (isNext(path))
73
+ return true;
49
74
 
50
- return end > start;
75
+ return false;
51
76
  }
52
77
 
53
78
  function isStrictMode(path) {
@@ -60,22 +85,3 @@ function isStrictMode(path) {
60
85
 
61
86
  return value === 'use strict';
62
87
  }
63
-
64
- function isFirst(path) {
65
- return path.node === path.parentPath.node.body[0];
66
- }
67
-
68
- function shouldAddNewlineBefore(path) {
69
- const prev = path.getPrevSibling();
70
-
71
- if (prev.isVariableDeclaration())
72
- return false;
73
-
74
- if (prev.isIfStatement())
75
- return false;
76
-
77
- if (isStrictMode(prev))
78
- return false;
79
-
80
- return true;
81
- }
@@ -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
-
@@ -4,9 +4,8 @@ const {hasPrevNewline, markAfter} = require('../mark');
4
4
  const {isFirst} = require('../is');
5
5
 
6
6
  module.exports.IfStatement = (path, {indent, print}) => {
7
- if (!isFirst(path) && !hasPrevNewline(path)) {
8
- print.indent();
9
- print.newline();
7
+ if (shouldAddNewlineBefore(path)) {
8
+ print.linebreak();
10
9
  }
11
10
 
12
11
  indent();
@@ -31,6 +30,7 @@ module.exports.IfStatement = (path, {indent, print}) => {
31
30
  print(' else ');
32
31
  print(alternate);
33
32
  } else if (alternate.node) {
33
+ print.breakline();
34
34
  print('else');
35
35
  print.newline();
36
36
  indent.inc();
@@ -48,3 +48,6 @@ module.exports.IfStatement = (path, {indent, print}) => {
48
48
  markAfter(path);
49
49
  };
50
50
 
51
+ function shouldAddNewlineBefore(path) {
52
+ return !isFirst(path) && !hasPrevNewline(path);
53
+ }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const {markAfter} = require('../mark');
3
4
  const {entries} = Object;
4
5
 
5
6
  module.exports.ImportDeclaration = (path, {print, maybe}) => {
@@ -27,5 +28,16 @@ module.exports.ImportDeclaration = (path, {print, maybe}) => {
27
28
  print('__source');
28
29
  print(';');
29
30
  print.newline();
31
+
32
+ if (shouldAddNewlineAfter(path)) {
33
+ print.newline();
34
+ markAfter(path);
35
+ }
30
36
  };
31
37
 
38
+ function shouldAddNewlineAfter(path) {
39
+ if (path.getNextSibling().isImportDeclaration())
40
+ return false;
41
+
42
+ return true;
43
+ }
@@ -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,
@@ -34,11 +35,7 @@ module.exports = {
34
35
  },
35
36
  SwitchStatement,
36
37
  ...TryStatements,
37
- BreakStatement(path, {print, indent}) {
38
- indent();
39
- print('break;');
40
- print.newline();
41
- },
38
+ BreakStatement,
42
39
  ContinueStatement(path, {indent, print}) {
43
40
  indent();
44
41
  print('continue;');
@@ -3,9 +3,8 @@
3
3
  const {
4
4
  isNext,
5
5
  isCoupleLines,
6
- isNextBlock,
7
-
8
6
  } = require('../is');
7
+
9
8
  const {
10
9
  hasPrevNewline,
11
10
  markAfter,
@@ -26,6 +25,7 @@ module.exports.VariableDeclaration = (path, {maybe, print}) => {
26
25
  maybe.print(initPath.node, ' = ');
27
26
  print(initPath);
28
27
  maybe.print(isParentBlock(path), ';');
28
+
29
29
  let wasNewline = false;
30
30
 
31
31
  if (isParentBlock(path) && isNext(path)) {
@@ -44,9 +44,6 @@ function shouldAddNewlineAfter(path) {
44
44
  if (!isNext(path) && path.parentPath.isBlockStatement())
45
45
  return true;
46
46
 
47
- if (isNextBlock(path))
48
- return false;
49
-
50
47
  if (isLast(path))
51
48
  return false;
52
49
 
@@ -61,7 +58,6 @@ function shouldAddNewlineAfter(path) {
61
58
 
62
59
  return false;
63
60
  }
64
-
65
61
  const isLast = (path) => path.parentPath.isProgram() && !isNext(path);
66
62
 
67
63
  function shouldAddNewlineBefore(path) {
@@ -81,15 +77,6 @@ function shouldAddNewlineBefore(path) {
81
77
 
82
78
  if (isCoupleLines(path))
83
79
  return true;
84
-
85
- const nextPath = path.getNextSibling();
86
- const nextNextPath = nextPath.getNextSibling();
87
- const twoNext = nextPath.node && nextNextPath.node;
88
-
89
- if (!twoNext)
90
- return false;
91
-
92
- return true;
93
80
  }
94
81
 
95
82
  function isFirst(path) {
@@ -103,4 +90,3 @@ const isNextAssign = (path) => {
103
90
 
104
91
  return nextPath.get('expression').isAssignmentExpression();
105
92
  };
106
-
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const {isLast} = require('../is');
4
+
3
5
  module.exports.WhileStatement = (path, {print, indent}) => {
4
6
  print('while (');
5
7
  print('__test');
@@ -15,5 +17,14 @@ module.exports.WhileStatement = (path, {print, indent}) => {
15
17
  indent.dec();
16
18
  }
17
19
 
18
- print.linebreak();
20
+ if (shouldAddNewlineAfter(path)) {
21
+ print.linebreak();
22
+ }
19
23
  };
24
+
25
+ function shouldAddNewlineAfter(path) {
26
+ if (isLast(path))
27
+ return false;
28
+
29
+ return true;
30
+ }
@@ -1,19 +1,30 @@
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 {createDebug, createLog} = require('./debug');
11
+ const {maybeFile, maybePlugin} = require('./maybe');
12
+
13
+ const {
14
+ createDebug,
15
+ createLog,
16
+ } = require('./debug');
10
17
 
11
18
  const {
12
19
  maybeMarkAfter,
13
20
  maybeMarkBefore,
14
21
  } = require('./mark');
15
22
 
16
- const {parseComments} = require('./comments');
23
+ const {
24
+ parseLeadingComments,
25
+ parseTrailingComments,
26
+ } = require('./comments');
27
+
17
28
  const isString = (a) => typeof a === 'string';
18
29
  const {assign} = Object;
19
30
 
@@ -32,13 +43,11 @@ function initFormat(format) {
32
43
  indent: ' ',
33
44
  };
34
45
  }
35
-
36
46
  const createAddToken = (tokens) => {
37
47
  const log = createLog();
48
+
38
49
  return (token) => {
39
- const {value} = token;
40
-
41
- log(value);
50
+ log(token);
42
51
  tokens.push(token);
43
52
  };
44
53
  };
@@ -130,6 +139,7 @@ module.exports.tokenize = (ast, overrides = {}) => {
130
139
  debug,
131
140
  traverse,
132
141
  maybe,
142
+ store: fullstore(),
133
143
  };
134
144
 
135
145
  const currentTraversers = {
@@ -137,7 +147,7 @@ module.exports.tokenize = (ast, overrides = {}) => {
137
147
  ...overrides.visitors,
138
148
  };
139
149
 
140
- babelTraverse(ast, {
150
+ babelTraverse(maybeFile(ast), {
141
151
  Program(path) {
142
152
  traverse(path);
143
153
  path.stop();
@@ -179,8 +189,10 @@ module.exports.tokenize = (ast, overrides = {}) => {
179
189
  if (!currentTraverse)
180
190
  throw Error(`Node type '${type}' is not supported yet: '${path}'`);
181
191
 
182
- parseComments(path, printer);
183
- currentTraverse(path, printer);
192
+ parseLeadingComments(path, printer);
193
+ maybePlugin(currentTraverse, path, printer);
194
+ parseTrailingComments(path, printer);
195
+
184
196
  debug(path.type);
185
197
  }
186
198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "1.9.0",
3
+ "version": "1.10.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": [
@@ -49,7 +50,6 @@
49
50
  "eslint-plugin-putout": "^17.0.0",
50
51
  "estree-to-babel": "^5.0.1",
51
52
  "just-kebab-case": "^4.2.0",
52
- "lerna": "^6.0.1",
53
53
  "madrun": "^9.0.0",
54
54
  "mock-require": "^3.0.3",
55
55
  "montag": "^1.0.0",
@@ -1,11 +1,21 @@
1
1
  'use strict';
2
2
 
3
+ const {assign} = Object;
4
+
3
5
  module.exports.report = () => `Use print('__path') instead of path.get(__path)`;
4
6
 
5
7
  module.exports.replace = () => ({
6
8
  'print(path.get(__a))': ({__a}) => {
7
- __a.value = '__' + __a.value;
9
+ const {
10
+ raw,
11
+ value,
12
+ } = __a;
13
+
14
+ assign(__a, {
15
+ value: `__${value}`,
16
+ raw: raw.replace(value, `__${value}`),
17
+ });
18
+
8
19
  return 'print(__a)';
9
20
  },
10
21
  });
11
-