@putout/printer 1.5.4 → 1.6.1

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.1
2
+
3
+ fix:
4
+ - 7e8d47c @putout/printer: visitors
5
+
6
+ 2023.03.20, v1.6.0
7
+
8
+ feature:
9
+ - f0ef762 @putout/printer: add ability to override syntax visitors
10
+
1
11
  2023.03.20, v1.5.4
2
12
 
3
13
  feature:
package/README.md CHANGED
@@ -32,6 +32,35 @@ 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
+ indent: ' ',
52
+ visitors: {
53
+ AssignmentPattern(path, {print}) {
54
+ print(' /* [hello world] */= ');
55
+ print('__right');
56
+ },
57
+ },
58
+ });
59
+
60
+ // returns
61
+ 'const {a /* [hello world] */= 5} = b;\n';
62
+ ```
63
+
35
64
  ## License
36
65
 
37
66
  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
+
@@ -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);
@@ -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
+
@@ -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) => {
@@ -81,21 +84,12 @@ module.exports.tokenize = (ast) => {
81
84
  linebreak,
82
85
  breakline,
83
86
  });
84
- const print = (maybeLine) => {
85
- if (isString(maybeLine))
86
- return write(maybeLine);
87
-
88
- return traverse(maybeLine);
89
- };
90
-
91
- const maybePrint = (a, b) => a && print(b);
92
87
 
93
88
  const maybe = {
94
89
  write: maybeWrite,
95
90
  indent: maybeIndent,
96
91
  markBefore: maybeMarkBefore,
97
92
  markAfter: maybeMarkAfter,
98
- print: maybePrint,
99
93
  };
100
94
 
101
95
  assign(maybe.indent, {
@@ -103,12 +97,6 @@ module.exports.tokenize = (ast) => {
103
97
  dec: maybeIndentDec,
104
98
  });
105
99
 
106
- assign(print, write);
107
- assign(maybePrint, {
108
- newline: maybeNewline,
109
- breakline: maybeBreakline,
110
- });
111
-
112
100
  const printer = {
113
101
  incIndent,
114
102
  decIndent,
@@ -119,7 +107,11 @@ module.exports.tokenize = (ast) => {
119
107
  maybeIndent,
120
108
  traverse,
121
109
  maybe,
122
- print,
110
+ };
111
+
112
+ const currentTraversers = {
113
+ ...traversers,
114
+ ...overrides.visitors,
123
115
  };
124
116
 
125
117
  babelTraverse(ast, {
@@ -131,11 +123,27 @@ module.exports.tokenize = (ast) => {
131
123
 
132
124
  function traverse(path) {
133
125
  const {type} = path;
134
- const currentTraverse = traversers[type];
126
+ const currentTraverse = currentTraversers[type];
135
127
 
136
128
  if (!path.node)
137
129
  return;
138
130
 
131
+ const print = createPrint(path, {write, traverse});
132
+ assign(print, write);
133
+ assign(printer, {
134
+ print,
135
+ });
136
+
137
+ const maybePrint = (a, b) => a && print(b);
138
+
139
+ assign(maybePrint, {
140
+ newline: maybeNewline,
141
+ breakline: maybeBreakline,
142
+ });
143
+ assign(printer.maybe, {
144
+ print: maybePrint,
145
+ });
146
+
139
147
  if (!currentTraverse)
140
148
  throw Error(`Node type '${type}' is not supported yet: '${path}'`);
141
149
 
@@ -158,3 +166,15 @@ function printIndent(i) {
158
166
  return result;
159
167
  }
160
168
 
169
+ const createPrint = (path, {traverse, write}) => (maybeLine) => {
170
+ if (maybeLine === path)
171
+ return null;
172
+
173
+ if (!isString(maybeLine))
174
+ return traverse(maybeLine);
175
+
176
+ if (maybeLine.startsWith(GET))
177
+ return traverse(get(path, maybeLine));
178
+
179
+ return write(maybeLine);
180
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "1.5.4",
3
+ "version": "1.6.1",
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",