@ts-graphviz/ast 2.0.5 → 2.0.6-next-d7ff421ec861ca8fdede1a6bdf256f3455fb9797

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.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @ts-graphviz/ast
2
2
 
3
+ ## 2.0.6-next-d7ff421ec861ca8fdede1a6bdf256f3455fb9797
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1218](https://github.com/ts-graphviz/ts-graphviz/pull/1218) [`d7ff421`](https://github.com/ts-graphviz/ts-graphviz/commit/d7ff421ec861ca8fdede1a6bdf256f3455fb9797) Thanks [@kamiazya](https://github.com/kamiazya)! - Fix AST printing not to be destroyed during stringify even in HTML-Like format
8
+
9
+ - Updated dependencies [[`d7ff421`](https://github.com/ts-graphviz/ts-graphviz/commit/d7ff421ec861ca8fdede1a6bdf256f3455fb9797)]:
10
+ - @ts-graphviz/common@2.1.5-next-d7ff421ec861ca8fdede1a6bdf256f3455fb9797
11
+
3
12
  ## 2.0.5
4
13
 
5
14
  ### Patch Changes
package/lib/ast.cjs CHANGED
@@ -34,95 +34,57 @@ class Builder {
34
34
  }
35
35
  }
36
36
  const createElement = Builder.prototype.createElement.bind(new Builder());
37
- const EOL = /\r?\n/;
38
- function joinBy(sep) {
39
- return (value) => value.join(sep);
40
- }
41
- function wrapWith(wrapper) {
42
- return (value) => wrapper + value + wrapper;
43
- }
44
- function wrapByPair(l, r) {
45
- return (value) => l + value + r;
46
- }
47
- function leftPadWith(left) {
48
- return (value) => left + value;
49
- }
50
- const escape = (value) => value.replace(/(?<!\\)"|[\r\n]/g, escapeReplacer);
51
- function escapeReplacer(match) {
52
- switch (match) {
53
- case "\r":
54
- return "\\r";
55
- case "\n":
56
- return "\\n";
57
- default:
58
- return '\\"';
59
- }
60
- }
61
- const splitByLine = (value) => value.split(EOL);
62
- const indent = (style, size, eol) => common.pipe(
63
- splitByLine,
64
- common.map(leftPadWith(style === "space" ? " ".repeat(size) : "\n")),
65
- joinBy(eol)
66
- );
67
- const endOfLine = (eol) => {
68
- switch (eol) {
69
- case "crlf":
70
- return "\r\n";
71
- case "lf":
72
- return "\n";
73
- }
74
- };
75
37
  const AttributeListPrintPlugin = {
76
38
  match(ast) {
77
39
  return ast.type === "AttributeList";
78
40
  },
79
- print(context, ast) {
41
+ *print(context, ast) {
80
42
  if (ast.children.length === 0) {
81
- return `${ast.kind.toLocaleLowerCase()} [];`;
43
+ yield `${ast.kind.toLocaleLowerCase()} [];`;
44
+ } else {
45
+ yield `${ast.kind.toLocaleLowerCase()} [`;
46
+ yield* context.printChildren(ast.children);
47
+ yield "];";
82
48
  }
83
- const eol = endOfLine(context.endOfLine);
84
- return common.pipe(
85
- common.map(context.print),
86
- joinBy(eol),
87
- indent(context.indentStyle, context.indentSize, eol),
88
- wrapByPair(`${ast.kind.toLocaleLowerCase()} [${eol}`, `${eol}];`)
89
- )(ast.children);
90
49
  }
91
50
  };
92
51
  const AttributePrintPlugin = {
93
52
  match(ast) {
94
53
  return ast.type === "Attribute";
95
54
  },
96
- print(context, ast) {
97
- return `${context.print(ast.key)} = ${context.print(ast.value)};`;
55
+ *print(context, ast) {
56
+ yield* context.print(ast.key);
57
+ yield " = ";
58
+ yield* context.print(ast.value);
59
+ yield ";";
98
60
  }
99
61
  };
62
+ const EOL_PATTERN = /\r?\n/;
63
+ const paddingMap = {
64
+ Block: " * ",
65
+ Macro: "# ",
66
+ Slash: "// "
67
+ };
100
68
  const CommentPrintPlugin = {
101
69
  match(ast) {
102
70
  return ast.type === "Comment";
103
71
  },
104
- print(context, ast) {
105
- const eol = endOfLine(context.endOfLine);
106
- switch (ast.kind) {
107
- case "Block":
108
- return common.pipe(
109
- splitByLine,
110
- common.map(leftPadWith(" * ")),
111
- joinBy(eol),
112
- wrapByPair(`/**${eol}`, `${eol} */`)
113
- )(ast.value);
114
- case "Macro":
115
- return common.pipe(
116
- splitByLine,
117
- common.map(leftPadWith("# ")),
118
- joinBy(eol)
119
- )(ast.value);
120
- default:
121
- return common.pipe(
122
- splitByLine,
123
- common.map(leftPadWith("// ")),
124
- joinBy(eol)
125
- )(ast.value);
72
+ *print(context, ast) {
73
+ const padding = paddingMap[ast.kind];
74
+ if (ast.kind === "Block") {
75
+ yield* ["/**", context.EOL];
76
+ }
77
+ const lines = ast.value.split(EOL_PATTERN);
78
+ const lineLength = lines.length;
79
+ for (let i = 0; i < lineLength; i++) {
80
+ yield padding;
81
+ yield lines[i];
82
+ if (i < lineLength - 1) {
83
+ yield context.EOL;
84
+ }
85
+ }
86
+ if (ast.kind === "Block") {
87
+ yield* [context.EOL, " */"];
126
88
  }
127
89
  }
128
90
  };
@@ -130,73 +92,74 @@ const DotPrintPlugin = {
130
92
  match(ast) {
131
93
  return ast.type === "Dot";
132
94
  },
133
- print(context, ast) {
134
- return ast.children.map(context.print).join(endOfLine(context.endOfLine));
95
+ *print(context, ast) {
96
+ yield* context.join(ast.children, context.EOL);
135
97
  }
136
98
  };
137
99
  const EdgePrintPlugin = {
138
100
  match(ast) {
139
101
  return ast.type === "Edge";
140
102
  },
141
- print(context, ast) {
142
- const targets = common.pipe(
143
- common.map(context.print),
144
- joinBy(context.directed ? " -> " : " -- ")
145
- )(ast.targets);
103
+ *print(context, ast) {
104
+ yield* context.join(ast.targets, context.directed ? " -> " : " -- ");
146
105
  if (ast.children.length === 0) {
147
- return `${targets};`;
106
+ yield ";";
107
+ } else {
108
+ yield " [";
109
+ yield* context.printChildren(ast.children);
110
+ yield "];";
148
111
  }
149
- const eol = endOfLine(context.endOfLine);
150
- const contents = common.pipe(
151
- common.map(context.print),
152
- joinBy(eol),
153
- indent(context.indentStyle, context.indentSize, eol),
154
- wrapByPair(`[${eol}`, `${eol}];`)
155
- )(ast.children);
156
- return `${targets} ${contents}`;
157
112
  }
158
113
  };
159
114
  const GraphPrintPlugin = {
160
115
  match(ast) {
161
116
  return ast.type === "Graph";
162
117
  },
163
- print(context, ast) {
118
+ *print(context, ast) {
164
119
  context.directed = ast.directed;
165
- const parts = [];
166
120
  if (ast.strict) {
167
- parts.push("strict");
121
+ yield "strict ";
168
122
  }
169
- parts.push(ast.directed ? "digraph" : "graph");
123
+ yield ast.directed ? "digraph " : "graph ";
170
124
  if (ast.id) {
171
- parts.push(context.print(ast.id));
125
+ yield* context.print(ast.id);
126
+ yield " ";
172
127
  }
173
- if (ast.children.length === 0) {
174
- return `${parts.join(" ")} {}`;
128
+ yield "{";
129
+ if (ast.children.length >= 1) {
130
+ yield* context.printChildren(ast.children);
175
131
  }
176
- const eol = endOfLine(context.endOfLine);
177
- const contents = common.pipe(
178
- common.map(context.print),
179
- joinBy(eol),
180
- indent(context.indentStyle, context.indentSize, eol),
181
- wrapByPair(`{${eol}`, `${eol}}`)
182
- )(ast.children);
183
- return `${parts.join(" ")} ${contents}`;
132
+ yield "}";
184
133
  }
185
134
  };
186
- const quoteLiteralValue = common.pipe(escape, wrapWith('"'));
187
- const quoteHTMLLikeLiteralValue = wrapByPair("<", ">");
135
+ const escape = (value) => value.replace(/(?<!\\)"|[\r\n]/g, escapeReplacer);
136
+ const escapeMap = {
137
+ "\r": String.raw`\r`,
138
+ "\n": String.raw`\n`,
139
+ '"': String.raw`\"`
140
+ };
141
+ function escapeReplacer(match) {
142
+ return escapeMap[match];
143
+ }
188
144
  const LiteralPrintPlugin = {
189
145
  match(ast) {
190
146
  return ast.type === "Literal";
191
147
  },
192
- print(context, ast) {
148
+ *print(context, ast) {
193
149
  switch (ast.quoted) {
194
150
  case "html":
195
- return quoteHTMLLikeLiteralValue(ast.value);
151
+ yield "<";
152
+ yield ast.value;
153
+ yield ">";
154
+ return;
196
155
  case true:
197
- return quoteLiteralValue(ast.value);
156
+ yield '"';
157
+ yield escape(ast.value);
158
+ yield '"';
159
+ return;
198
160
  default:
199
- return escape(ast.value);
161
+ yield ast.value;
162
+ return;
200
163
  }
201
164
  }
202
165
  };
@@ -204,68 +167,57 @@ const NodePrintPlugin = {
204
167
  match(ast) {
205
168
  return ast.type === "Node";
206
169
  },
207
- print(context, ast) {
208
- const id = context.print(ast.id);
209
- if (ast.children.length === 0) {
210
- return `${id};`;
170
+ *print(context, ast) {
171
+ yield* context.print(ast.id);
172
+ if (ast.children.length >= 1) {
173
+ yield " [";
174
+ yield* context.printChildren(ast.children);
175
+ yield "]";
211
176
  }
212
- const eol = endOfLine(context.endOfLine);
213
- const contents = common.pipe(
214
- common.map(context.print),
215
- joinBy(eol),
216
- indent(context.indentStyle, context.indentSize, eol),
217
- wrapByPair(`[${eol}`, `${eol}];`)
218
- )(ast.children);
219
- return `${id} ${contents}`;
177
+ yield ";";
220
178
  }
221
179
  };
222
180
  const NodeRefGroupPrintPlugin = {
223
181
  match(ast) {
224
182
  return ast.type === "NodeRefGroup";
225
183
  },
226
- print(context, ast) {
227
- return common.pipe(
228
- common.map(context.print),
229
- joinBy(" "),
230
- wrapByPair("{", "}")
231
- )(ast.children);
184
+ *print(context, ast) {
185
+ yield "{";
186
+ yield* context.join(ast.children, " ");
187
+ yield "}";
232
188
  }
233
189
  };
234
190
  const NodeRefPrintPlugin = {
235
191
  match(ast) {
236
192
  return ast.type === "NodeRef";
237
193
  },
238
- print(context, ast) {
239
- const parts = [context.print(ast.id)];
194
+ *print(context, ast) {
195
+ yield* context.print(ast.id);
240
196
  if (ast.port) {
241
- parts.push(context.print(ast.port));
197
+ yield ":";
198
+ yield* context.print(ast.port);
242
199
  }
243
200
  if (ast.compass) {
244
- parts.push(context.print(ast.compass));
201
+ yield ":";
202
+ yield* context.print(ast.compass);
245
203
  }
246
- return parts.join(":");
247
204
  }
248
205
  };
249
206
  const SubgraphPrintPlugin = {
250
207
  match(ast) {
251
208
  return ast.type === "Subgraph";
252
209
  },
253
- print(context, ast) {
254
- const parts = ["subgraph"];
210
+ *print(context, ast) {
211
+ yield "subgraph";
255
212
  if (ast.id) {
256
- parts.push(context.print(ast.id));
213
+ yield " ";
214
+ yield* context.print(ast.id);
257
215
  }
258
- if (ast.children.length === 0) {
259
- return `${parts.join(" ")} {}`;
216
+ yield " {";
217
+ if (ast.children.length >= 1) {
218
+ yield* context.printChildren(ast.children);
260
219
  }
261
- const eol = endOfLine(context.endOfLine);
262
- const contents = common.pipe(
263
- common.map(context.print),
264
- joinBy(eol),
265
- indent(context.indentStyle, context.indentSize, eol),
266
- wrapByPair(`{${eol}`, `${eol}}`)
267
- )(ast.children);
268
- return `${parts.join(" ")} ${contents}`;
220
+ yield "}";
269
221
  }
270
222
  };
271
223
  const defaultPlugins$2 = [
@@ -296,27 +248,61 @@ class Printer {
296
248
  * @returns The DOT string generated from the ASTNode.
297
249
  */
298
250
  print(ast) {
251
+ return Array.from(this.toChunks(ast)).join("");
252
+ }
253
+ toChunks(ast) {
299
254
  const plugins = [...this.#plugins];
300
255
  const {
301
256
  indentSize = 2,
302
257
  indentStyle = "space",
303
- endOfLine: endOfLine2 = "lf"
258
+ endOfLine = "lf"
304
259
  } = this.options;
260
+ const EOL = endOfLine === "crlf" ? "\r\n" : "\n";
261
+ const PADDING = indentStyle === "space" ? " ".repeat(indentSize) : " ";
305
262
  const context = {
306
263
  directed: true,
307
- indentSize,
308
- indentStyle,
309
- endOfLine: endOfLine2,
310
- print(a) {
264
+ EOL,
265
+ *printChildren(children) {
266
+ yield* indent(function* () {
267
+ yield EOL;
268
+ yield* context.join(children, EOL);
269
+ });
270
+ yield EOL;
271
+ },
272
+ *print(a) {
311
273
  for (const plugin of plugins) {
312
274
  if (plugin.match(a)) {
313
- return plugin.print(context, a);
275
+ yield* plugin.print(this, a);
276
+ return;
277
+ }
278
+ }
279
+ throw new Error(
280
+ `No matching plugin found for AST node: ${JSON.stringify(a)}`
281
+ );
282
+ },
283
+ *join(array, separator) {
284
+ const childrenLength = array.length;
285
+ for (let i = 0; i < childrenLength; i++) {
286
+ yield* context.print(array[i]);
287
+ if (i < childrenLength - 1) {
288
+ yield separator;
314
289
  }
315
290
  }
316
- throw Error();
317
291
  }
318
292
  };
319
- return context.print(ast);
293
+ return {
294
+ [Symbol.iterator]: function* () {
295
+ yield* context.print(ast);
296
+ }
297
+ };
298
+ function* indent(tokens) {
299
+ for (const token of tokens()) {
300
+ yield token;
301
+ if (token === EOL) {
302
+ yield PADDING;
303
+ }
304
+ }
305
+ }
320
306
  }
321
307
  }
322
308
  function stringify(ast, options) {
package/lib/ast.d.ts CHANGED
@@ -736,15 +736,18 @@ declare interface OtherExpectation {
736
736
  * PrintContext interface provides an interface for printing an ASTNode with a set of options.
737
737
  * @group Convert AST to DOT
738
738
  */
739
- export declare interface PrintContext extends Required<PrintOptions> {
739
+ export declare interface PrintContext {
740
740
  /**
741
741
  * Indicates if the AST should be printed in a directed graph.
742
742
  */
743
743
  directed: boolean;
744
+ readonly EOL: string;
744
745
  /**
745
746
  * A function to print an ASTNode, taking in an ASTNode as an argument. Returns a string.
746
747
  */
747
- print(ast: ASTNode): string;
748
+ print(ast: ASTNode): Iterable<string>;
749
+ printChildren(children: ASTNode[]): Iterable<string>;
750
+ join(children: ASTNode[], separator: string): Iterable<string>;
748
751
  }
749
752
 
750
753
  /**
@@ -764,6 +767,7 @@ declare interface OtherExpectation {
764
767
  * @returns The DOT string generated from the ASTNode.
765
768
  */
766
769
  print(ast: ASTNode): string;
770
+ private toChunks;
767
771
  }
768
772
 
769
773
  /**
@@ -809,7 +813,7 @@ declare interface OtherExpectation {
809
813
  * @returns printed string
810
814
  * @memberof PrintPlugin
811
815
  */
812
- print(context: PrintContext, ast: T): string;
816
+ print(context: PrintContext, ast: T): Generator<string>;
813
817
  }
814
818
 
815
819
  /**
package/lib/ast.js CHANGED
@@ -1,4 +1,4 @@
1
- import { pipe, map, isNodeModel, isForwardRefNode, createModelsContext } from "@ts-graphviz/common";
1
+ import { isNodeModel, isForwardRefNode, createModelsContext } from "@ts-graphviz/common";
2
2
  class Builder {
3
3
  /**
4
4
  * Constructor of Builder
@@ -32,95 +32,57 @@ class Builder {
32
32
  }
33
33
  }
34
34
  const createElement = Builder.prototype.createElement.bind(new Builder());
35
- const EOL = /\r?\n/;
36
- function joinBy(sep) {
37
- return (value) => value.join(sep);
38
- }
39
- function wrapWith(wrapper) {
40
- return (value) => wrapper + value + wrapper;
41
- }
42
- function wrapByPair(l, r) {
43
- return (value) => l + value + r;
44
- }
45
- function leftPadWith(left) {
46
- return (value) => left + value;
47
- }
48
- const escape = (value) => value.replace(/(?<!\\)"|[\r\n]/g, escapeReplacer);
49
- function escapeReplacer(match) {
50
- switch (match) {
51
- case "\r":
52
- return "\\r";
53
- case "\n":
54
- return "\\n";
55
- default:
56
- return '\\"';
57
- }
58
- }
59
- const splitByLine = (value) => value.split(EOL);
60
- const indent = (style, size, eol) => pipe(
61
- splitByLine,
62
- map(leftPadWith(style === "space" ? " ".repeat(size) : "\n")),
63
- joinBy(eol)
64
- );
65
- const endOfLine = (eol) => {
66
- switch (eol) {
67
- case "crlf":
68
- return "\r\n";
69
- case "lf":
70
- return "\n";
71
- }
72
- };
73
35
  const AttributeListPrintPlugin = {
74
36
  match(ast) {
75
37
  return ast.type === "AttributeList";
76
38
  },
77
- print(context, ast) {
39
+ *print(context, ast) {
78
40
  if (ast.children.length === 0) {
79
- return `${ast.kind.toLocaleLowerCase()} [];`;
41
+ yield `${ast.kind.toLocaleLowerCase()} [];`;
42
+ } else {
43
+ yield `${ast.kind.toLocaleLowerCase()} [`;
44
+ yield* context.printChildren(ast.children);
45
+ yield "];";
80
46
  }
81
- const eol = endOfLine(context.endOfLine);
82
- return pipe(
83
- map(context.print),
84
- joinBy(eol),
85
- indent(context.indentStyle, context.indentSize, eol),
86
- wrapByPair(`${ast.kind.toLocaleLowerCase()} [${eol}`, `${eol}];`)
87
- )(ast.children);
88
47
  }
89
48
  };
90
49
  const AttributePrintPlugin = {
91
50
  match(ast) {
92
51
  return ast.type === "Attribute";
93
52
  },
94
- print(context, ast) {
95
- return `${context.print(ast.key)} = ${context.print(ast.value)};`;
53
+ *print(context, ast) {
54
+ yield* context.print(ast.key);
55
+ yield " = ";
56
+ yield* context.print(ast.value);
57
+ yield ";";
96
58
  }
97
59
  };
60
+ const EOL_PATTERN = /\r?\n/;
61
+ const paddingMap = {
62
+ Block: " * ",
63
+ Macro: "# ",
64
+ Slash: "// "
65
+ };
98
66
  const CommentPrintPlugin = {
99
67
  match(ast) {
100
68
  return ast.type === "Comment";
101
69
  },
102
- print(context, ast) {
103
- const eol = endOfLine(context.endOfLine);
104
- switch (ast.kind) {
105
- case "Block":
106
- return pipe(
107
- splitByLine,
108
- map(leftPadWith(" * ")),
109
- joinBy(eol),
110
- wrapByPair(`/**${eol}`, `${eol} */`)
111
- )(ast.value);
112
- case "Macro":
113
- return pipe(
114
- splitByLine,
115
- map(leftPadWith("# ")),
116
- joinBy(eol)
117
- )(ast.value);
118
- default:
119
- return pipe(
120
- splitByLine,
121
- map(leftPadWith("// ")),
122
- joinBy(eol)
123
- )(ast.value);
70
+ *print(context, ast) {
71
+ const padding = paddingMap[ast.kind];
72
+ if (ast.kind === "Block") {
73
+ yield* ["/**", context.EOL];
74
+ }
75
+ const lines = ast.value.split(EOL_PATTERN);
76
+ const lineLength = lines.length;
77
+ for (let i = 0; i < lineLength; i++) {
78
+ yield padding;
79
+ yield lines[i];
80
+ if (i < lineLength - 1) {
81
+ yield context.EOL;
82
+ }
83
+ }
84
+ if (ast.kind === "Block") {
85
+ yield* [context.EOL, " */"];
124
86
  }
125
87
  }
126
88
  };
@@ -128,73 +90,74 @@ const DotPrintPlugin = {
128
90
  match(ast) {
129
91
  return ast.type === "Dot";
130
92
  },
131
- print(context, ast) {
132
- return ast.children.map(context.print).join(endOfLine(context.endOfLine));
93
+ *print(context, ast) {
94
+ yield* context.join(ast.children, context.EOL);
133
95
  }
134
96
  };
135
97
  const EdgePrintPlugin = {
136
98
  match(ast) {
137
99
  return ast.type === "Edge";
138
100
  },
139
- print(context, ast) {
140
- const targets = pipe(
141
- map(context.print),
142
- joinBy(context.directed ? " -> " : " -- ")
143
- )(ast.targets);
101
+ *print(context, ast) {
102
+ yield* context.join(ast.targets, context.directed ? " -> " : " -- ");
144
103
  if (ast.children.length === 0) {
145
- return `${targets};`;
104
+ yield ";";
105
+ } else {
106
+ yield " [";
107
+ yield* context.printChildren(ast.children);
108
+ yield "];";
146
109
  }
147
- const eol = endOfLine(context.endOfLine);
148
- const contents = pipe(
149
- map(context.print),
150
- joinBy(eol),
151
- indent(context.indentStyle, context.indentSize, eol),
152
- wrapByPair(`[${eol}`, `${eol}];`)
153
- )(ast.children);
154
- return `${targets} ${contents}`;
155
110
  }
156
111
  };
157
112
  const GraphPrintPlugin = {
158
113
  match(ast) {
159
114
  return ast.type === "Graph";
160
115
  },
161
- print(context, ast) {
116
+ *print(context, ast) {
162
117
  context.directed = ast.directed;
163
- const parts = [];
164
118
  if (ast.strict) {
165
- parts.push("strict");
119
+ yield "strict ";
166
120
  }
167
- parts.push(ast.directed ? "digraph" : "graph");
121
+ yield ast.directed ? "digraph " : "graph ";
168
122
  if (ast.id) {
169
- parts.push(context.print(ast.id));
123
+ yield* context.print(ast.id);
124
+ yield " ";
170
125
  }
171
- if (ast.children.length === 0) {
172
- return `${parts.join(" ")} {}`;
126
+ yield "{";
127
+ if (ast.children.length >= 1) {
128
+ yield* context.printChildren(ast.children);
173
129
  }
174
- const eol = endOfLine(context.endOfLine);
175
- const contents = pipe(
176
- map(context.print),
177
- joinBy(eol),
178
- indent(context.indentStyle, context.indentSize, eol),
179
- wrapByPair(`{${eol}`, `${eol}}`)
180
- )(ast.children);
181
- return `${parts.join(" ")} ${contents}`;
130
+ yield "}";
182
131
  }
183
132
  };
184
- const quoteLiteralValue = pipe(escape, wrapWith('"'));
185
- const quoteHTMLLikeLiteralValue = wrapByPair("<", ">");
133
+ const escape = (value) => value.replace(/(?<!\\)"|[\r\n]/g, escapeReplacer);
134
+ const escapeMap = {
135
+ "\r": String.raw`\r`,
136
+ "\n": String.raw`\n`,
137
+ '"': String.raw`\"`
138
+ };
139
+ function escapeReplacer(match) {
140
+ return escapeMap[match];
141
+ }
186
142
  const LiteralPrintPlugin = {
187
143
  match(ast) {
188
144
  return ast.type === "Literal";
189
145
  },
190
- print(context, ast) {
146
+ *print(context, ast) {
191
147
  switch (ast.quoted) {
192
148
  case "html":
193
- return quoteHTMLLikeLiteralValue(ast.value);
149
+ yield "<";
150
+ yield ast.value;
151
+ yield ">";
152
+ return;
194
153
  case true:
195
- return quoteLiteralValue(ast.value);
154
+ yield '"';
155
+ yield escape(ast.value);
156
+ yield '"';
157
+ return;
196
158
  default:
197
- return escape(ast.value);
159
+ yield ast.value;
160
+ return;
198
161
  }
199
162
  }
200
163
  };
@@ -202,68 +165,57 @@ const NodePrintPlugin = {
202
165
  match(ast) {
203
166
  return ast.type === "Node";
204
167
  },
205
- print(context, ast) {
206
- const id = context.print(ast.id);
207
- if (ast.children.length === 0) {
208
- return `${id};`;
168
+ *print(context, ast) {
169
+ yield* context.print(ast.id);
170
+ if (ast.children.length >= 1) {
171
+ yield " [";
172
+ yield* context.printChildren(ast.children);
173
+ yield "]";
209
174
  }
210
- const eol = endOfLine(context.endOfLine);
211
- const contents = pipe(
212
- map(context.print),
213
- joinBy(eol),
214
- indent(context.indentStyle, context.indentSize, eol),
215
- wrapByPair(`[${eol}`, `${eol}];`)
216
- )(ast.children);
217
- return `${id} ${contents}`;
175
+ yield ";";
218
176
  }
219
177
  };
220
178
  const NodeRefGroupPrintPlugin = {
221
179
  match(ast) {
222
180
  return ast.type === "NodeRefGroup";
223
181
  },
224
- print(context, ast) {
225
- return pipe(
226
- map(context.print),
227
- joinBy(" "),
228
- wrapByPair("{", "}")
229
- )(ast.children);
182
+ *print(context, ast) {
183
+ yield "{";
184
+ yield* context.join(ast.children, " ");
185
+ yield "}";
230
186
  }
231
187
  };
232
188
  const NodeRefPrintPlugin = {
233
189
  match(ast) {
234
190
  return ast.type === "NodeRef";
235
191
  },
236
- print(context, ast) {
237
- const parts = [context.print(ast.id)];
192
+ *print(context, ast) {
193
+ yield* context.print(ast.id);
238
194
  if (ast.port) {
239
- parts.push(context.print(ast.port));
195
+ yield ":";
196
+ yield* context.print(ast.port);
240
197
  }
241
198
  if (ast.compass) {
242
- parts.push(context.print(ast.compass));
199
+ yield ":";
200
+ yield* context.print(ast.compass);
243
201
  }
244
- return parts.join(":");
245
202
  }
246
203
  };
247
204
  const SubgraphPrintPlugin = {
248
205
  match(ast) {
249
206
  return ast.type === "Subgraph";
250
207
  },
251
- print(context, ast) {
252
- const parts = ["subgraph"];
208
+ *print(context, ast) {
209
+ yield "subgraph";
253
210
  if (ast.id) {
254
- parts.push(context.print(ast.id));
211
+ yield " ";
212
+ yield* context.print(ast.id);
255
213
  }
256
- if (ast.children.length === 0) {
257
- return `${parts.join(" ")} {}`;
214
+ yield " {";
215
+ if (ast.children.length >= 1) {
216
+ yield* context.printChildren(ast.children);
258
217
  }
259
- const eol = endOfLine(context.endOfLine);
260
- const contents = pipe(
261
- map(context.print),
262
- joinBy(eol),
263
- indent(context.indentStyle, context.indentSize, eol),
264
- wrapByPair(`{${eol}`, `${eol}}`)
265
- )(ast.children);
266
- return `${parts.join(" ")} ${contents}`;
218
+ yield "}";
267
219
  }
268
220
  };
269
221
  const defaultPlugins$2 = [
@@ -294,27 +246,61 @@ class Printer {
294
246
  * @returns The DOT string generated from the ASTNode.
295
247
  */
296
248
  print(ast) {
249
+ return Array.from(this.toChunks(ast)).join("");
250
+ }
251
+ toChunks(ast) {
297
252
  const plugins = [...this.#plugins];
298
253
  const {
299
254
  indentSize = 2,
300
255
  indentStyle = "space",
301
- endOfLine: endOfLine2 = "lf"
256
+ endOfLine = "lf"
302
257
  } = this.options;
258
+ const EOL = endOfLine === "crlf" ? "\r\n" : "\n";
259
+ const PADDING = indentStyle === "space" ? " ".repeat(indentSize) : " ";
303
260
  const context = {
304
261
  directed: true,
305
- indentSize,
306
- indentStyle,
307
- endOfLine: endOfLine2,
308
- print(a) {
262
+ EOL,
263
+ *printChildren(children) {
264
+ yield* indent(function* () {
265
+ yield EOL;
266
+ yield* context.join(children, EOL);
267
+ });
268
+ yield EOL;
269
+ },
270
+ *print(a) {
309
271
  for (const plugin of plugins) {
310
272
  if (plugin.match(a)) {
311
- return plugin.print(context, a);
273
+ yield* plugin.print(this, a);
274
+ return;
275
+ }
276
+ }
277
+ throw new Error(
278
+ `No matching plugin found for AST node: ${JSON.stringify(a)}`
279
+ );
280
+ },
281
+ *join(array, separator) {
282
+ const childrenLength = array.length;
283
+ for (let i = 0; i < childrenLength; i++) {
284
+ yield* context.print(array[i]);
285
+ if (i < childrenLength - 1) {
286
+ yield separator;
312
287
  }
313
288
  }
314
- throw Error();
315
289
  }
316
290
  };
317
- return context.print(ast);
291
+ return {
292
+ [Symbol.iterator]: function* () {
293
+ yield* context.print(ast);
294
+ }
295
+ };
296
+ function* indent(tokens) {
297
+ for (const token of tokens()) {
298
+ yield token;
299
+ if (token === EOL) {
300
+ yield PADDING;
301
+ }
302
+ }
303
+ }
318
304
  }
319
305
  }
320
306
  function stringify(ast, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-graphviz/ast",
3
- "version": "2.0.5",
3
+ "version": "2.0.6-next-d7ff421ec861ca8fdede1a6bdf256f3455fb9797",
4
4
  "description": "Graphviz AST(Abstract Syntax Tree) Utilities",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/ts-graphviz/ts-graphviz#readme",
@@ -37,7 +37,7 @@
37
37
  "module": "lib/ast.js",
38
38
  "types": "lib/ast.d.ts",
39
39
  "dependencies": {
40
- "@ts-graphviz/common": "^2.1.4"
40
+ "@ts-graphviz/common": "^2.1.5-next-d7ff421ec861ca8fdede1a6bdf256f3455fb9797"
41
41
  },
42
42
  "devDependencies": {
43
43
  "peggy": "^4.0.3",