@putout/printer 1.5.3 → 1.6.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.03.20, v1.6.0
2
+
3
+ feature:
4
+ - f0ef762 @putout/printer: add ability to override syntax visitors
5
+
6
+ 2023.03.20, v1.5.4
7
+
8
+ feature:
9
+ - 96d7056 @putout/printer: split linebreak(\s\n) and breakline(\n\s)
10
+
1
11
  2023.03.20, v1.5.3
2
12
 
3
13
  feature:
package/README.md CHANGED
@@ -32,6 +32,32 @@ const a = (b, c) => {
32
32
  `;
33
33
  ```
34
34
 
35
+ ## Overrides
36
+
37
+ When you need to extend syntax of `@putout/printer` just pass a function which receives:
38
+
39
+ - `path`, Babel Path
40
+ - `print`, a function to output result of printing into token array;
41
+
42
+ When `path` contains to dashes `__` and name, it is the same as: `print(path.get('right'))`, and this is
43
+ actually `traverse(path.get('right'))` shortened to simplify read and process.
44
+
45
+ Here is how you can override `AssignmentPattern`:
46
+
47
+ ```js
48
+ const ast = parse('const {a = 5} = b');
49
+
50
+ print(ast, {
51
+ AssignmentPattern(path, {print}) {
52
+ print(' /* [hello world] */= ');
53
+ print('__right');
54
+ },
55
+ });
56
+
57
+ // returns
58
+ 'const {a /* [hello world] */= 5} = b;\n';
59
+ ```
60
+
35
61
  ## License
36
62
 
37
63
  MIT
package/lib/printer.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const {tokenize} = require('./tokenize/tokenize');
4
4
  const {printTokens} = require('./print-tokens');
5
5
 
6
- module.exports.print = (ast) => {
7
- const tokens = tokenize(ast);
6
+ module.exports.print = (ast, overrides) => {
7
+ const tokens = tokenize(ast, overrides);
8
8
  return printTokens(tokens);
9
9
  };
@@ -4,31 +4,31 @@ const {isObjectProperty} = require('@babel/types');
4
4
  const {entries} = Object;
5
5
  const isForOf = ({parentPath}) => parentPath.isForOfStatement();
6
6
 
7
- module.exports.ArrayExpression = (path, {write, maybe, traverse}) => {
7
+ module.exports.ArrayExpression = (path, {print, maybe}) => {
8
8
  const elements = path.get('elements');
9
9
  const elementIsObject = isElementObject(path);
10
10
 
11
- write('[');
11
+ print('[');
12
12
  maybe.indent.inc(!elementIsObject);
13
13
 
14
14
  const isNewLine = !isNumbers(elements) && !isForOf(path) && isLastArg(path) && !isParentProperty(path);
15
15
  const n = elements.length - 1;
16
16
 
17
- maybe.write(isNewLine && elements.length, '\n');
17
+ maybe.print(isNewLine && elements.length, '\n');
18
18
 
19
19
  for (const [index, element] of entries(elements)) {
20
20
  maybe.indent(isNewLine);
21
21
 
22
- traverse(element);
22
+ print(element);
23
23
 
24
- maybe.write(isNewLine, ',\n');
25
- maybe.write(!isNewLine && index < n, ', ');
24
+ maybe.print(isNewLine, ',\n');
25
+ maybe.print(!isNewLine && index < n, ', ');
26
26
  }
27
27
 
28
28
  maybe.indent.dec(!elementIsObject);
29
29
  maybe.indent(elements.length && isNewLine);
30
30
 
31
- write(']');
31
+ print(']');
32
32
  };
33
33
 
34
34
  function isNumbers(elements) {
@@ -3,8 +3,8 @@
3
3
  const {entries} = Object;
4
4
  const isForOf = ({parentPath}) => parentPath.parentPath.parentPath.isForOfStatement();
5
5
 
6
- module.exports.ArrayPattern = (path, {write, indent, maybe, traverse}) => {
7
- write('[');
6
+ module.exports.ArrayPattern = (path, {indent, maybe, print}) => {
7
+ print('[');
8
8
 
9
9
  const elements = path.get('elements');
10
10
 
@@ -13,21 +13,21 @@ module.exports.ArrayPattern = (path, {write, indent, maybe, traverse}) => {
13
13
  const isNewLine = !isForOf(path) && elements.length > 2;
14
14
  const n = elements.length - 1;
15
15
 
16
- maybe.write(isNewLine && elements.length, '\n');
16
+ maybe.print(isNewLine && elements.length, '\n');
17
17
 
18
18
  for (const [index, element] of entries(elements)) {
19
19
  maybe.indent(isNewLine);
20
20
 
21
- traverse(element);
21
+ print(element);
22
22
 
23
- maybe.write(isNewLine, ',\n');
24
- maybe.write(!isNewLine && index < n, ', ');
23
+ maybe.print(isNewLine, ',\n');
24
+ maybe.print(!isNewLine && index < n, ', ');
25
25
  }
26
26
 
27
27
  indent.dec();
28
28
 
29
29
  maybe.indent(elements.length && isNewLine);
30
30
 
31
- write(']');
31
+ print(']');
32
32
  };
33
33
 
@@ -1,12 +1,18 @@
1
1
  'use strict';
2
2
 
3
- module.exports.AssignmentExpression = (path, {traverse, write}) => {
3
+ module.exports.AssignmentExpression = (path, {print}) => {
4
+ const {operator} = path.node;
5
+ const left = path.get('left');
6
+ const right = path.get('right');
7
+
4
8
  if (shouldAddNewLine(path))
5
- write('\n');
9
+ print('\n');
6
10
 
7
- traverse(path.get('left'));
8
- write(` ${path.node.operator} `);
9
- traverse(path.get('right'));
11
+ print(left);
12
+ print(' ');
13
+ print(operator);
14
+ print(' ');
15
+ print(right);
10
16
  };
11
17
 
12
18
  function shouldAddNewLine({parentPath}) {
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- module.exports.AssignmentPattern = (path, {write, traverse}) => {
4
- write(' = ');
5
- traverse(path.get('right'));
3
+ module.exports.AssignmentPattern = (path, {print}) => {
4
+ print(' = ');
5
+ print('__right');
6
6
  };
7
+
@@ -11,7 +11,7 @@ module.exports.CallExpression = (path, {indent, print, maybe}) => {
11
11
  const isParentCall = toLong(path) && path.parentPath.isCallExpression();
12
12
 
13
13
  if (shouldAddNewLine(path) && !isMarkedParentBefore(path) && !isMarkedPrevAfter(path.parentPath))
14
- print.linebreak();
14
+ print.breakline();
15
15
 
16
16
  print(path.get('callee'));
17
17
  print('(');
@@ -6,7 +6,7 @@ const isFirst = (path) => path.node === path.parentPath.node.body[0];
6
6
 
7
7
  module.exports.ClassDeclaration = (path, {maybe, print, indent}) => {
8
8
  if (!isFirst(path)) {
9
- print.linebreak();
9
+ print.breakline();
10
10
  }
11
11
 
12
12
  indent();
@@ -3,7 +3,7 @@
3
3
  const {isMarkedPrevAfter} = require('../mark');
4
4
  const isFirst = (path) => path.node === path.parentPath.node.body[0];
5
5
 
6
- module.exports.FunctionExpression = (path, {write, maybe, traverse}) => {
6
+ module.exports.FunctionExpression = (path, {print, maybe}) => {
7
7
  const {node} = path;
8
8
 
9
9
  const {
@@ -11,105 +11,105 @@ module.exports.FunctionExpression = (path, {write, maybe, traverse}) => {
11
11
  async,
12
12
  } = node;
13
13
 
14
- maybe.write(async, 'async ');
15
- write('function');
16
- maybe.write(generator, '*');
17
- write(' (');
14
+ maybe.print(async, 'async ');
15
+ print('function');
16
+ maybe.print(generator, '*');
17
+ print(' (');
18
18
 
19
19
  const params = path.get('params');
20
20
  const n = params.length;
21
21
 
22
22
  for (let i = 0; i < n; i++) {
23
- traverse(params[i]);
23
+ print(params[i]);
24
24
 
25
25
  if (i < n - 1)
26
- write(', ');
26
+ print(', ');
27
27
  }
28
28
 
29
- write(') ');
30
- traverse(path.get('body'));
29
+ print(') ');
30
+ print('__body');
31
31
  };
32
32
 
33
33
  module.exports.ArrowFunctionExpression = ArrowFunctionExpression;
34
- function ArrowFunctionExpression(path, {write, maybe, traverse}) {
34
+ function ArrowFunctionExpression(path, {print, maybe}) {
35
35
  const {async} = path.node;
36
- maybe.write(async, 'async ');
36
+ maybe.print(async, 'async ');
37
37
 
38
- write('(');
38
+ print('(');
39
39
 
40
40
  const params = path.get('params');
41
41
  const n = params.length;
42
42
 
43
43
  for (let i = 0; i < n; i++) {
44
- traverse(params[i]);
44
+ print(params[i]);
45
45
 
46
46
  if (i < n - 1)
47
- write(', ');
47
+ print(', ');
48
48
  }
49
49
 
50
- write(') => ');
51
- traverse(path.get('body'));
50
+ print(') => ');
51
+ print('__body');
52
52
  }
53
53
 
54
- module.exports.ObjectMethod = (path, {write, traverse}) => {
55
- traverse(path.get('key'));
56
- write('(');
54
+ module.exports.ObjectMethod = (path, {print}) => {
55
+ print(path.get('key'));
56
+ print('(');
57
57
 
58
58
  const params = path.get('params');
59
59
  const n = params.length - 1;
60
60
 
61
61
  for (let i = 0; i <= n; i++) {
62
- traverse(params[i]);
62
+ print(params[i]);
63
63
 
64
64
  if (i < n)
65
- write(', ');
65
+ print(', ');
66
66
  }
67
67
 
68
- write(') ');
69
- traverse(path.get('body'));
68
+ print(') ');
69
+ print(path.get('body'));
70
70
  };
71
71
 
72
- module.exports.FunctionDeclaration = (path, {write, maybe, traverse}) => {
72
+ module.exports.FunctionDeclaration = (path, {print, maybe}) => {
73
73
  const {async} = path.node;
74
74
 
75
75
  if (!isFirst(path) && !isMarkedPrevAfter(path))
76
- write('\n');
76
+ print('\n');
77
77
 
78
- maybe.write(async, 'async ');
78
+ maybe.print(async, 'async ');
79
79
 
80
- write('function ');
81
- traverse(path.get('id'));
82
- write('(');
80
+ print('function ');
81
+ print(path.get('id'));
82
+ print('(');
83
83
 
84
84
  const params = path.get('params');
85
85
  const n = params.length - 1;
86
86
 
87
87
  for (let i = 0; i <= n; i++) {
88
- traverse(params[i]);
88
+ print(params[i]);
89
89
 
90
90
  if (i < n)
91
- write(', ');
91
+ print(', ');
92
92
  }
93
93
 
94
- write(') ');
95
- traverse(path.get('body'));
94
+ print(') ');
95
+ print(path.get('body'));
96
96
  };
97
97
 
98
- module.exports.ClassMethod = (path, {write, traverse}) => {
99
- traverse(path.get('key'));
100
- write('(');
98
+ module.exports.ClassMethod = (path, {print}) => {
99
+ print(path.get('key'));
100
+ print('(');
101
101
 
102
102
  const params = path.get('params');
103
103
  const n = params.length;
104
104
 
105
105
  for (let i = 0; i < n; i++) {
106
- traverse(params[i]);
106
+ print(params[i]);
107
107
 
108
108
  if (i < n - 1)
109
- write(', ');
109
+ print(', ');
110
110
  }
111
111
 
112
- write(') ');
113
- traverse(path.get('body'));
112
+ print(') ');
113
+ print(path.get('body'));
114
114
  };
115
115
 
@@ -14,7 +14,10 @@ module.exports.BlockStatement = (path, {indent, maybe, print}) => {
14
14
  if (body.length > 1 || isFirstStatement(path))
15
15
  print.newline();
16
16
 
17
- body.forEach(print);
17
+ for (const element of body) {
18
+ print(element);
19
+ }
20
+
18
21
  indent.dec();
19
22
 
20
23
  maybe.indent(body.length);
@@ -7,9 +7,9 @@ const {
7
7
  } = require('../mark');
8
8
  const {isNext} = require('../is');
9
9
 
10
- module.exports.ExpressionStatement = (path, {write, indent, traverse}) => {
10
+ module.exports.ExpressionStatement = (path, {write, indent, traverse, print}) => {
11
11
  if (isCoupleLinesExpression(path) && !isFirst(path) && shouldAddNewLine(path) && !isMarkedPrevAfter(path)) {
12
- write.linebreak();
12
+ print.breakline();
13
13
  markBefore(path);
14
14
  }
15
15
 
@@ -62,3 +62,4 @@ function shouldAddNewLine(path) {
62
62
 
63
63
  return true;
64
64
  }
65
+
@@ -3,31 +3,32 @@
3
3
  const {isMarkedPrevAfter} = require('../mark');
4
4
  const {isFirst} = require('../is');
5
5
 
6
- module.exports.ForOfStatement = (path, {write, indent, traverse}) => {
6
+ module.exports.ForOfStatement = (path, {indent, print}) => {
7
7
  if (!isFirst(path) && !isMarkedPrevAfter(path)) {
8
- write.indent();
9
- write.newline();
8
+ print.indent();
9
+ print.newline();
10
10
  }
11
11
 
12
12
  indent();
13
- write('for (');
14
- traverse(path.get('left'));
15
- write(' of ');
16
- traverse(path.get('right'));
17
- write(')');
13
+ print('for (');
14
+ print('__left');
15
+ print(' of ');
16
+ print('__right');
17
+ print(')');
18
18
 
19
19
  const bodyPath = path.get('body');
20
20
 
21
21
  if (bodyPath.isExpressionStatement()) {
22
22
  indent.inc();
23
- write.newline();
24
- traverse(bodyPath);
23
+ print.newline();
24
+ print(bodyPath);
25
25
  indent.dec();
26
- write.newline();
26
+ print.newline();
27
27
  }
28
28
 
29
29
  if (bodyPath.isBlockStatement()) {
30
- write(' ');
31
- traverse(bodyPath);
30
+ print(' ');
31
+ print(bodyPath);
32
32
  }
33
33
  };
34
+
@@ -16,7 +16,7 @@ module.exports.TryStatement = (path, {print, maybe}) => {
16
16
  print.newline();
17
17
  }
18
18
 
19
- maybe.print.linebreak(isNext(path));
19
+ maybe.print.breakline(isNext(path));
20
20
  };
21
21
 
22
22
  module.exports.CatchClause = (path, {print}) => {
@@ -13,7 +13,7 @@ const isNextAssign = (path) => {
13
13
 
14
14
  module.exports.VariableDeclaration = (path, {maybe, print}) => {
15
15
  if (!isFirst(path) && shouldAddNewLine(path) && !hasPrevNewline(path))
16
- print.linebreak();
16
+ print.breakline();
17
17
 
18
18
  const isParentBlock = /Program|BlockStatement/.test(path.parentPath.type);
19
19
 
@@ -31,8 +31,7 @@ module.exports.VariableDeclaration = (path, {maybe, print}) => {
31
31
  const is = isNext(path) && isCoupleLines(path) && !isNextAssign(path);
32
32
 
33
33
  if (is) {
34
- print.indent();
35
- print.newline();
34
+ print.linebreak();
36
35
  markAfter(path);
37
36
  }
38
37
  };
@@ -22,7 +22,10 @@ const traversers = {
22
22
  ...literals,
23
23
  };
24
24
 
25
- module.exports.tokenize = (ast) => {
25
+ const GET = '__';
26
+ const get = (path, command) => path.get(command.replace(GET, ''));
27
+
28
+ module.exports.tokenize = (ast, overrides) => {
26
29
  const tokens = [];
27
30
  const debug = createDebug(tokens);
28
31
  const write = (value) => {
@@ -37,7 +40,7 @@ module.exports.tokenize = (ast) => {
37
40
  const maybeIndentInc = (a) => a && indent.inc();
38
41
  const maybeIndentDec = (a) => a && indent.dec();
39
42
  const maybeNewline = (a) => a && newline();
40
- const maybeLinebreak = (a) => a && linebreak();
43
+ const maybeBreakline = (a) => a && breakline();
41
44
 
42
45
  let i = 0;
43
46
  const incIndent = () => ++i;
@@ -55,6 +58,13 @@ module.exports.tokenize = (ast) => {
55
58
  });
56
59
 
57
60
  const linebreak = () => {
61
+ tokens.push({
62
+ type: TYPES.NEWLINE,
63
+ value: `${printIndent(i)}\n`,
64
+ });
65
+ };
66
+
67
+ const breakline = () => {
58
68
  tokens.push({
59
69
  type: TYPES.NEWLINE,
60
70
  value: `\n${printIndent(i)}`,
@@ -72,22 +82,17 @@ module.exports.tokenize = (ast) => {
72
82
  indent,
73
83
  newline,
74
84
  linebreak,
85
+ breakline,
75
86
  });
76
- const print = (maybeLine) => {
77
- if (isString(maybeLine))
78
- return write(maybeLine);
79
-
80
- return traverse(maybeLine);
81
- };
82
87
 
83
- const maybePrint = (a, b) => a && print(b);
88
+ //const maybePrint = (a, b) => a && print(b);
84
89
 
85
90
  const maybe = {
86
91
  write: maybeWrite,
87
92
  indent: maybeIndent,
88
93
  markBefore: maybeMarkBefore,
89
94
  markAfter: maybeMarkAfter,
90
- print: maybePrint,
95
+ //print: maybePrint,
91
96
  };
92
97
 
93
98
  assign(maybe.indent, {
@@ -95,11 +100,7 @@ module.exports.tokenize = (ast) => {
95
100
  dec: maybeIndentDec,
96
101
  });
97
102
 
98
- assign(print, write);
99
- assign(maybePrint, {
100
- newline: maybeNewline,
101
- linebreak: maybeLinebreak,
102
- });
103
+ //assign(print, write);
103
104
 
104
105
  const printer = {
105
106
  incIndent,
@@ -111,7 +112,11 @@ module.exports.tokenize = (ast) => {
111
112
  maybeIndent,
112
113
  traverse,
113
114
  maybe,
114
- print,
115
+ };
116
+
117
+ const currentTraversers = {
118
+ ...traversers,
119
+ ...overrides,
115
120
  };
116
121
 
117
122
  babelTraverse(ast, {
@@ -123,11 +128,27 @@ module.exports.tokenize = (ast) => {
123
128
 
124
129
  function traverse(path) {
125
130
  const {type} = path;
126
- const currentTraverse = traversers[type];
131
+ const currentTraverse = currentTraversers[type];
127
132
 
128
133
  if (!path.node)
129
134
  return;
130
135
 
136
+ const print = createPrint(path, {write, traverse});
137
+ assign(print, write);
138
+ assign(printer, {
139
+ print,
140
+ });
141
+
142
+ const maybePrint = (a, b) => a && print(b);
143
+
144
+ assign(maybePrint, {
145
+ newline: maybeNewline,
146
+ breakline: maybeBreakline,
147
+ });
148
+ assign(printer.maybe, {
149
+ print: maybePrint,
150
+ });
151
+
131
152
  if (!currentTraverse)
132
153
  throw Error(`Node type '${type}' is not supported yet: '${path}'`);
133
154
 
@@ -150,3 +171,21 @@ function printIndent(i) {
150
171
  return result;
151
172
  }
152
173
 
174
+ /*
175
+ const createCompute = (path, {traverse}) => (maybeLine) => {
176
+ if (maybeLine.startsWith(GET))
177
+ return traverse(get(path, maybeLine));
178
+ };
179
+ */
180
+ const createPrint = (path, {traverse, write}) => (maybeLine) => {
181
+ if (maybeLine === path)
182
+ return null;
183
+
184
+ if (!isString(maybeLine))
185
+ return traverse(maybeLine);
186
+
187
+ if (maybeLine.startsWith(GET))
188
+ return traverse(get(path, maybeLine));
189
+
190
+ return write(maybeLine);
191
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "1.5.3",
3
+ "version": "1.6.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",