@putout/printer 1.5.4 → 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,8 @@
1
+ 2023.03.20, v1.6.0
2
+
3
+ feature:
4
+ - f0ef762 @putout/printer: add ability to override syntax visitors
5
+
1
6
  2023.03.20, v1.5.4
2
7
 
3
8
  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
+
@@ -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,15 @@ 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
87
 
91
- const maybePrint = (a, b) => a && print(b);
88
+ //const maybePrint = (a, b) => a && print(b);
92
89
 
93
90
  const maybe = {
94
91
  write: maybeWrite,
95
92
  indent: maybeIndent,
96
93
  markBefore: maybeMarkBefore,
97
94
  markAfter: maybeMarkAfter,
98
- print: maybePrint,
95
+ //print: maybePrint,
99
96
  };
100
97
 
101
98
  assign(maybe.indent, {
@@ -103,11 +100,7 @@ module.exports.tokenize = (ast) => {
103
100
  dec: maybeIndentDec,
104
101
  });
105
102
 
106
- assign(print, write);
107
- assign(maybePrint, {
108
- newline: maybeNewline,
109
- breakline: maybeBreakline,
110
- });
103
+ //assign(print, write);
111
104
 
112
105
  const printer = {
113
106
  incIndent,
@@ -119,7 +112,11 @@ module.exports.tokenize = (ast) => {
119
112
  maybeIndent,
120
113
  traverse,
121
114
  maybe,
122
- print,
115
+ };
116
+
117
+ const currentTraversers = {
118
+ ...traversers,
119
+ ...overrides,
123
120
  };
124
121
 
125
122
  babelTraverse(ast, {
@@ -131,11 +128,27 @@ module.exports.tokenize = (ast) => {
131
128
 
132
129
  function traverse(path) {
133
130
  const {type} = path;
134
- const currentTraverse = traversers[type];
131
+ const currentTraverse = currentTraversers[type];
135
132
 
136
133
  if (!path.node)
137
134
  return;
138
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
+
139
152
  if (!currentTraverse)
140
153
  throw Error(`Node type '${type}' is not supported yet: '${path}'`);
141
154
 
@@ -158,3 +171,21 @@ function printIndent(i) {
158
171
  return result;
159
172
  }
160
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.4",
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",