ts-graphviz 1.0.0 → 1.0.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/lib/ast/index.cjs CHANGED
@@ -5,8 +5,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var utils = require('#lib/utils');
6
6
  var common = require('#lib/common');
7
7
 
8
+ /**
9
+ * @group Create AST
10
+ */
8
11
  class Builder {
9
12
  options;
13
+ /** @internal */
10
14
  getLocation() {
11
15
  return this.options?.locationFunction?.() ?? null;
12
16
  }
@@ -24,6 +28,11 @@ class Builder {
24
28
  }
25
29
  }
26
30
 
31
+ /**
32
+ * @group Create AST
33
+ */
34
+ const createElement = Builder.prototype.createElement.bind(new Builder());
35
+
27
36
  const EOL = /\r?\n/;
28
37
  function joinBy(sep) {
29
38
  return (value) => value.join(sep);
@@ -258,16 +267,16 @@ const defaultPlugins$1 = [
258
267
  SubgraphPrintPlugin,
259
268
  ];
260
269
 
270
+ /**
271
+ * @group Convert AST to DOT
272
+ */
261
273
  class Printer {
262
274
  options;
275
+ /** @internal */
263
276
  #plugins = [...defaultPlugins$1];
264
277
  constructor(options = {}) {
265
278
  this.options = options;
266
279
  }
267
- use(plugin) {
268
- this.#plugins.unshift(plugin);
269
- return this;
270
- }
271
280
  print(ast) {
272
281
  const plugins = [...this.#plugins];
273
282
  const { indentSize = 2, indentStyle = 'space', endOfLine = 'lf' } = this.options;
@@ -289,547 +298,266 @@ class Printer {
289
298
  }
290
299
  }
291
300
 
292
- const createElement = Builder.prototype.createElement.bind(new Builder());
293
-
294
- function convertAttribute(key, value) {
295
- if (typeof value === 'string') {
296
- const trimmed = value.trim();
297
- const isHTMLLike = /^<.+>$/ms.test(trimmed);
298
- if (isHTMLLike) {
299
- return createElement(
300
- 'Attribute',
301
- {
302
- key: createElement('Literal', { value: key, quoted: false }, []),
303
- value: createElement('Literal', { value: trimmed.slice(1, trimmed.length - 1), quoted: 'html' }, []),
304
- },
305
- [],
306
- );
307
- } else {
308
- return createElement(
309
- 'Attribute',
310
- {
311
- key: createElement('Literal', { value: key, quoted: false }, []),
312
- value: createElement('Literal', { value: value, quoted: true }, []),
313
- },
314
- [],
315
- );
316
- }
301
+ /**
302
+ * Stringify Graphviz AST Node.
303
+ *
304
+ * @param ast Graphviz AST node.
305
+ * @returns DOT language string.
306
+ * @group Convert AST to DOT
307
+ */
308
+ function stringify(ast, options) {
309
+ const result = new Printer(options).print(ast);
310
+ if (!result) {
311
+ throw new Error();
317
312
  }
318
- return createElement(
319
- 'Attribute',
320
- {
321
- key: createElement('Literal', { value: key, quoted: false }, []),
322
- value: createElement('Literal', { value: String(value), quoted: false }, []),
323
- },
324
- [],
325
- );
313
+ return result;
326
314
  }
327
315
 
328
- function convertComment(value, kind) {
329
- return createElement(
330
- 'Comment',
331
- {
332
- kind: kind,
333
- value: value,
334
- },
335
- [],
336
- );
316
+ /* eslint-disable */
317
+ function peg$padEnd(str, targetLength, padString) {
318
+ padString = padString || ' ';
319
+ if (str.length > targetLength) {
320
+ return str;
321
+ }
322
+ targetLength -= str.length;
323
+ padString += padString.repeat(targetLength);
324
+ return str + padString.slice(0, targetLength);
337
325
  }
338
-
339
- function convertClusterChildren(context, model) {
340
- return Array.from(
341
- (function* () {
342
- for (const [key, value] of model.values) {
343
- yield convertAttribute(key, value);
326
+ class SyntaxError$1 extends Error {
327
+ static buildMessage(expected, found) {
328
+ function hex(ch) {
329
+ return ch.charCodeAt(0).toString(16).toUpperCase();
330
+ }
331
+ function literalEscape(s) {
332
+ return s
333
+ .replace(/\\/g, '\\\\')
334
+ .replace(/"/g, '\\"')
335
+ .replace(/\0/g, '\\0')
336
+ .replace(/\t/g, '\\t')
337
+ .replace(/\n/g, '\\n')
338
+ .replace(/\r/g, '\\r')
339
+ .replace(/[\x00-\x0F]/g, (ch) => '\\x0' + hex(ch))
340
+ .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => '\\x' + hex(ch));
341
+ }
342
+ function classEscape(s) {
343
+ return s
344
+ .replace(/\\/g, '\\\\')
345
+ .replace(/\]/g, '\\]')
346
+ .replace(/\^/g, '\\^')
347
+ .replace(/-/g, '\\-')
348
+ .replace(/\0/g, '\\0')
349
+ .replace(/\t/g, '\\t')
350
+ .replace(/\n/g, '\\n')
351
+ .replace(/\r/g, '\\r')
352
+ .replace(/[\x00-\x0F]/g, (ch) => '\\x0' + hex(ch))
353
+ .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => '\\x' + hex(ch));
354
+ }
355
+ function describeExpectation(expectation) {
356
+ switch (expectation.type) {
357
+ case 'literal':
358
+ return '"' + literalEscape(expectation.text) + '"';
359
+ case 'class':
360
+ const escapedParts = expectation.parts.map((part) => {
361
+ return Array.isArray(part) ? classEscape(part[0]) + '-' + classEscape(part[1]) : classEscape(part);
362
+ });
363
+ return '[' + (expectation.inverted ? '^' : '') + escapedParts + ']';
364
+ case 'any':
365
+ return 'any character';
366
+ case 'end':
367
+ return 'end of input';
368
+ case 'other':
369
+ return expectation.description;
344
370
  }
345
- for (const attrs of Object.values(model.attributes)) {
346
- if (attrs.size > 0) {
347
- if (attrs.comment) {
348
- yield convertComment(attrs.comment, context.commentKind);
371
+ }
372
+ function describeExpected(expected1) {
373
+ const descriptions = expected1.map(describeExpectation);
374
+ let i;
375
+ let j;
376
+ descriptions.sort();
377
+ if (descriptions.length > 0) {
378
+ for (i = 1, j = 1; i < descriptions.length; i++) {
379
+ if (descriptions[i - 1] !== descriptions[i]) {
380
+ descriptions[j] = descriptions[i];
381
+ j++;
349
382
  }
350
- yield context.convert(attrs);
351
383
  }
384
+ descriptions.length = j;
352
385
  }
353
- for (const node of model.nodes) {
354
- if (node.comment) {
355
- yield convertComment(node.comment, context.commentKind);
356
- }
357
- yield context.convert(node);
386
+ switch (descriptions.length) {
387
+ case 1:
388
+ return descriptions[0];
389
+ case 2:
390
+ return descriptions[0] + ' or ' + descriptions[1];
391
+ default:
392
+ return descriptions.slice(0, -1).join(', ') + ', or ' + descriptions[descriptions.length - 1];
358
393
  }
359
- for (const subgraph of model.subgraphs) {
360
- if (subgraph.comment) {
361
- yield convertComment(subgraph.comment, context.commentKind);
394
+ }
395
+ function describeFound(found1) {
396
+ return found1 ? '"' + literalEscape(found1) + '"' : 'end of input';
397
+ }
398
+ return 'Expected ' + describeExpected(expected) + ' but ' + describeFound(found) + ' found.';
399
+ }
400
+ message;
401
+ expected;
402
+ found;
403
+ location;
404
+ name;
405
+ constructor(message, expected, found, location) {
406
+ super();
407
+ this.message = message;
408
+ this.expected = expected;
409
+ this.found = found;
410
+ this.location = location;
411
+ this.name = 'SyntaxError';
412
+ if (typeof Object.setPrototypeOf === 'function') {
413
+ Object.setPrototypeOf(this, SyntaxError$1.prototype);
414
+ } else {
415
+ this.__proto__ = SyntaxError$1.prototype;
416
+ }
417
+ if (typeof Error.captureStackTrace === 'function') {
418
+ Error.captureStackTrace(this, SyntaxError$1);
419
+ }
420
+ }
421
+ format(sources) {
422
+ let str = 'Error: ' + this.message;
423
+ if (this.location) {
424
+ let src = null;
425
+ let k;
426
+ for (k = 0; k < sources.length; k++) {
427
+ if (sources[k].source === this.location.source) {
428
+ src = sources[k].text.split(/\r\n|\n|\r/g);
429
+ break;
362
430
  }
363
- yield context.convert(subgraph);
364
431
  }
365
- for (const edge of model.edges) {
366
- if (edge.comment) {
367
- yield convertComment(edge.comment, context.commentKind);
368
- }
369
- yield context.convert(edge);
432
+ let s = this.location.start;
433
+ let loc = this.location.source + ':' + s.line + ':' + s.column;
434
+ if (src) {
435
+ let e = this.location.end;
436
+ let filler = peg$padEnd('', s.line.toString().length, ' ');
437
+ let line = src[s.line - 1];
438
+ let last = s.line === e.line ? e.column : line.length + 1;
439
+ str +=
440
+ '\n --> ' +
441
+ loc +
442
+ '\n' +
443
+ filler +
444
+ ' |\n' +
445
+ s.line +
446
+ ' | ' +
447
+ line +
448
+ '\n' +
449
+ filler +
450
+ ' | ' +
451
+ peg$padEnd('', s.column - 1, ' ') +
452
+ peg$padEnd('', last - s.column, '^');
453
+ } else {
454
+ str += '\n at ' + loc;
370
455
  }
371
- })(),
372
- );
456
+ }
457
+ return str;
458
+ }
373
459
  }
374
-
375
- const AttributeListPrintPlugin = {
376
- match(model) {
377
- return model.$$type === 'AttributeList';
378
- },
379
- convert(context, model) {
380
- return createElement(
381
- 'AttributeList',
382
- {
383
- kind: model.$$kind,
384
- },
385
- model.values.map(([key, value]) => convertAttribute(key, value)),
386
- );
387
- },
388
- };
389
-
390
- const EdgeConvertPlugin = {
391
- match(model) {
392
- return model.$$type === 'Edge';
393
- },
394
- convert(context, model) {
395
- return createElement(
396
- 'Edge',
397
- {
398
- targets: model.targets.map((target) => {
399
- if (common.isNodeModel(target)) {
400
- return createElement(
401
- 'NodeRef',
402
- {
403
- id: createElement(
404
- 'Literal',
405
- {
406
- value: target.id,
407
- quoted: true,
408
- },
409
- [],
410
- ),
411
- },
412
- [],
413
- );
414
- } else if (common.isForwardRefNode(target)) {
415
- return createElement(
416
- 'NodeRef',
417
- {
418
- id: createElement(
419
- 'Literal',
420
- {
421
- value: target.id,
422
- quoted: true,
423
- },
424
- [],
425
- ),
426
- port: target.port
427
- ? createElement(
428
- 'Literal',
429
- {
430
- value: target.port,
431
- quoted: true,
432
- },
433
- [],
434
- )
435
- : undefined,
436
- compass: target.compass
437
- ? createElement(
438
- 'Literal',
439
- {
440
- value: target.compass,
441
- quoted: true,
442
- },
443
- [],
444
- )
445
- : undefined,
446
- },
447
- [],
448
- );
460
+ function peg$parse(input, options) {
461
+ options = options !== undefined ? options : {};
462
+ const peg$FAILED = {};
463
+ const peg$source = options.grammarSource;
464
+ const peg$startRuleFunctions = {
465
+ Dot: peg$parseDot,
466
+ Graph: peg$parseGraph,
467
+ Subgraph: peg$parseSubgraph,
468
+ Node: peg$parseNode,
469
+ Edge: peg$parseEdge,
470
+ AttributeList: peg$parseAttributeList,
471
+ Attribute: peg$parseAttribute,
472
+ ClusterStatements: peg$parseClusterStatements,
473
+ };
474
+ let peg$startRuleFunction = peg$parseDot;
475
+ const peg$c0 = function (v) {
476
+ return v;
477
+ };
478
+ const peg$c1 = function (c1, graph, c2) {
479
+ return b.createElement('Dot', {}, [...c1, graph, ...c2]);
480
+ };
481
+ const peg$c2 = 'strict';
482
+ const peg$c3 = peg$literalExpectation('strict', true);
483
+ const peg$c4 = 'graph';
484
+ const peg$c5 = peg$literalExpectation('graph', true);
485
+ const peg$c6 = 'digraph';
486
+ const peg$c7 = peg$literalExpectation('digraph', true);
487
+ const peg$c8 = '{';
488
+ const peg$c9 = peg$literalExpectation('{', false);
489
+ const peg$c10 = '}';
490
+ const peg$c11 = peg$literalExpectation('}', false);
491
+ const peg$c12 = function (_strict, _kind, id, children) {
492
+ const strict = !!_strict;
493
+ const kind = _kind.toLowerCase();
494
+ const directed = kind === 'digraph';
495
+ for (const edgeop of edgeops) {
496
+ if (directed) {
497
+ if (edgeop.operator !== '->') {
498
+ error(`In digraph, it's necessary to describe with "->" operator to create edge.`, edgeop.location);
499
+ }
500
+ } else {
501
+ if (edgeop.operator !== '--') {
502
+ error(`In graph, it's necessary to describe with "--" operator to create edge.`, edgeop.location);
503
+ }
504
+ }
505
+ }
506
+ return b.createElement(
507
+ 'Graph',
508
+ id !== null
509
+ ? {
510
+ id,
511
+ directed,
512
+ strict,
449
513
  }
450
- }),
514
+ : {
515
+ directed,
516
+ strict,
517
+ },
518
+ children,
519
+ );
520
+ };
521
+ const peg$c13 = ';';
522
+ const peg$c14 = peg$literalExpectation(';', false);
523
+ const peg$c15 = function (keyValue) {
524
+ return b.createElement(
525
+ 'Attribute',
526
+ {
527
+ ...keyValue,
451
528
  },
452
- [
453
- ...(model.attributes.comment ? [convertComment(model.attributes.comment, context.commentKind)] : []),
454
- ...model.attributes.values.map(([key, value]) => convertAttribute(key, value)),
455
- ],
529
+ [],
456
530
  );
457
- },
458
- };
459
-
460
- const GraphConvertPlugin = {
461
- match(model) {
462
- return model.$$type === 'Graph';
463
- },
464
- convert(context, model) {
465
- return createElement('Dot', {}, [
466
- ...(model.comment ? [convertComment(model.comment, context.commentKind)] : []),
467
- createElement(
468
- 'Graph',
469
- {
470
- directed: model.directed,
471
- strict: model.strict,
472
- id: model.id
473
- ? createElement(
474
- 'Literal',
475
- {
476
- value: model.id,
477
- quoted: true,
478
- },
479
- [],
480
- )
481
- : undefined,
482
- },
483
- convertClusterChildren(context, model),
484
- ),
485
- ]);
486
- },
487
- };
488
-
489
- const NodeConvertPlugin = {
490
- match(model) {
491
- return model.$$type === 'Node';
492
- },
493
- convert(context, model) {
494
- return createElement(
495
- 'Node',
531
+ };
532
+ const peg$c16 = 'node';
533
+ const peg$c17 = peg$literalExpectation('node', true);
534
+ const peg$c18 = 'edge';
535
+ const peg$c19 = peg$literalExpectation('edge', true);
536
+ const peg$c20 = function (_kind, children) {
537
+ return b.createElement(
538
+ 'AttributeList',
496
539
  {
497
- id: createElement(
498
- 'Literal',
499
- {
500
- value: model.id,
501
- quoted: true,
502
- },
503
- [],
504
- ),
540
+ kind: _kind.toLowerCase(),
505
541
  },
506
- [
507
- ...(model.attributes.comment ? [convertComment(model.attributes.comment, context.commentKind)] : []),
508
- ...model.attributes.values.map(([key, value]) => convertAttribute(key, value)),
509
- ],
542
+ children,
510
543
  );
511
- },
512
- };
513
-
514
- const SubgraphConvertPlugin = {
515
- match(model) {
516
- return model.$$type === 'Subgraph';
517
- },
518
- convert(context, model) {
519
- return createElement(
520
- 'Subgraph',
544
+ };
545
+ const peg$c21 = function (id, rhs, _children) {
546
+ // @ts-ignore
547
+ return b.createElement(
548
+ // @ts-ignore
549
+ 'Edge',
521
550
  {
522
- id: model.id
523
- ? createElement(
524
- 'Literal',
525
- {
526
- value: model.id,
527
- quoted: true,
528
- },
529
- [],
530
- )
531
- : undefined,
551
+ targets: [id, ...rhs],
532
552
  },
533
- convertClusterChildren(context, model),
553
+ _children ?? [],
534
554
  );
535
- },
536
- };
537
-
538
- const defaultPlugins = [
539
- AttributeListPrintPlugin,
540
- EdgeConvertPlugin,
541
- NodeConvertPlugin,
542
- GraphConvertPlugin,
543
- SubgraphConvertPlugin,
544
- ];
545
-
546
- class Converter {
547
- options;
548
- #plugins = [...defaultPlugins];
549
- constructor(options = {}) {
550
- this.options = options;
551
- }
552
- use(plugin) {
553
- this.#plugins.unshift(plugin);
554
- return this;
555
- }
556
- convert(model) {
557
- const plugins = [...this.#plugins];
558
- const { commentKind = 'Slash' } = this.options;
559
- const context = {
560
- commentKind,
561
- convert(m) {
562
- for (const plugin of plugins) {
563
- if (plugin.match(m)) {
564
- return plugin.convert(context, m);
565
- }
566
- }
567
- throw Error();
568
- },
569
- };
570
- return context.convert(model);
571
- }
572
- }
573
-
574
- /**
575
- * Stringify Graphviz AST Node.
576
- *
577
- * @param ast Graphviz AST node.
578
- * @returns DOT language string.
579
- */
580
- function stringify(ast, options) {
581
- const result = new Printer(options).print(ast);
582
- if (!result) {
583
- throw new Error();
584
- }
585
- return result;
586
- }
587
-
588
- /* eslint-disable */
589
- function peg$padEnd(str, targetLength, padString) {
590
- padString = padString || ' ';
591
- if (str.length > targetLength) {
592
- return str;
593
- }
594
- targetLength -= str.length;
595
- padString += padString.repeat(targetLength);
596
- return str + padString.slice(0, targetLength);
597
- }
598
- class SyntaxError extends Error {
599
- static buildMessage(expected, found) {
600
- function hex(ch) {
601
- return ch.charCodeAt(0).toString(16).toUpperCase();
602
- }
603
- function literalEscape(s) {
604
- return s
605
- .replace(/\\/g, '\\\\')
606
- .replace(/"/g, '\\"')
607
- .replace(/\0/g, '\\0')
608
- .replace(/\t/g, '\\t')
609
- .replace(/\n/g, '\\n')
610
- .replace(/\r/g, '\\r')
611
- .replace(/[\x00-\x0F]/g, (ch) => '\\x0' + hex(ch))
612
- .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => '\\x' + hex(ch));
613
- }
614
- function classEscape(s) {
615
- return s
616
- .replace(/\\/g, '\\\\')
617
- .replace(/\]/g, '\\]')
618
- .replace(/\^/g, '\\^')
619
- .replace(/-/g, '\\-')
620
- .replace(/\0/g, '\\0')
621
- .replace(/\t/g, '\\t')
622
- .replace(/\n/g, '\\n')
623
- .replace(/\r/g, '\\r')
624
- .replace(/[\x00-\x0F]/g, (ch) => '\\x0' + hex(ch))
625
- .replace(/[\x10-\x1F\x7F-\x9F]/g, (ch) => '\\x' + hex(ch));
626
- }
627
- function describeExpectation(expectation) {
628
- switch (expectation.type) {
629
- case 'literal':
630
- return '"' + literalEscape(expectation.text) + '"';
631
- case 'class':
632
- const escapedParts = expectation.parts.map((part) => {
633
- return Array.isArray(part) ? classEscape(part[0]) + '-' + classEscape(part[1]) : classEscape(part);
634
- });
635
- return '[' + (expectation.inverted ? '^' : '') + escapedParts + ']';
636
- case 'any':
637
- return 'any character';
638
- case 'end':
639
- return 'end of input';
640
- case 'other':
641
- return expectation.description;
642
- }
643
- }
644
- function describeExpected(expected1) {
645
- const descriptions = expected1.map(describeExpectation);
646
- let i;
647
- let j;
648
- descriptions.sort();
649
- if (descriptions.length > 0) {
650
- for (i = 1, j = 1; i < descriptions.length; i++) {
651
- if (descriptions[i - 1] !== descriptions[i]) {
652
- descriptions[j] = descriptions[i];
653
- j++;
654
- }
655
- }
656
- descriptions.length = j;
657
- }
658
- switch (descriptions.length) {
659
- case 1:
660
- return descriptions[0];
661
- case 2:
662
- return descriptions[0] + ' or ' + descriptions[1];
663
- default:
664
- return descriptions.slice(0, -1).join(', ') + ', or ' + descriptions[descriptions.length - 1];
665
- }
666
- }
667
- function describeFound(found1) {
668
- return found1 ? '"' + literalEscape(found1) + '"' : 'end of input';
669
- }
670
- return 'Expected ' + describeExpected(expected) + ' but ' + describeFound(found) + ' found.';
671
- }
672
- message;
673
- expected;
674
- found;
675
- location;
676
- name;
677
- constructor(message, expected, found, location) {
678
- super();
679
- this.message = message;
680
- this.expected = expected;
681
- this.found = found;
682
- this.location = location;
683
- this.name = 'SyntaxError';
684
- if (typeof Object.setPrototypeOf === 'function') {
685
- Object.setPrototypeOf(this, SyntaxError.prototype);
686
- } else {
687
- this.__proto__ = SyntaxError.prototype;
688
- }
689
- if (typeof Error.captureStackTrace === 'function') {
690
- Error.captureStackTrace(this, SyntaxError);
691
- }
692
- }
693
- format(sources) {
694
- let str = 'Error: ' + this.message;
695
- if (this.location) {
696
- let src = null;
697
- let k;
698
- for (k = 0; k < sources.length; k++) {
699
- if (sources[k].source === this.location.source) {
700
- src = sources[k].text.split(/\r\n|\n|\r/g);
701
- break;
702
- }
703
- }
704
- let s = this.location.start;
705
- let loc = this.location.source + ':' + s.line + ':' + s.column;
706
- if (src) {
707
- let e = this.location.end;
708
- let filler = peg$padEnd('', s.line.toString().length, ' ');
709
- let line = src[s.line - 1];
710
- let last = s.line === e.line ? e.column : line.length + 1;
711
- str +=
712
- '\n --> ' +
713
- loc +
714
- '\n' +
715
- filler +
716
- ' |\n' +
717
- s.line +
718
- ' | ' +
719
- line +
720
- '\n' +
721
- filler +
722
- ' | ' +
723
- peg$padEnd('', s.column - 1, ' ') +
724
- peg$padEnd('', last - s.column, '^');
725
- } else {
726
- str += '\n at ' + loc;
727
- }
728
- }
729
- return str;
730
- }
731
- }
732
- function peg$parse(input, options) {
733
- options = options !== undefined ? options : {};
734
- const peg$FAILED = {};
735
- const peg$source = options.grammarSource;
736
- const peg$startRuleFunctions = {
737
- Dot: peg$parseDot,
738
- Graph: peg$parseGraph,
739
- Subgraph: peg$parseSubgraph,
740
- Node: peg$parseNode,
741
- Edge: peg$parseEdge,
742
- AttributeList: peg$parseAttributeList,
743
- Attribute: peg$parseAttribute,
744
- ClusterStatements: peg$parseClusterStatements,
745
- };
746
- let peg$startRuleFunction = peg$parseDot;
747
- const peg$c0 = function (v) {
748
- return v;
749
- };
750
- const peg$c1 = function (c1, graph, c2) {
751
- return b.createElement('Dot', {}, [...c1, graph, ...c2]);
752
- };
753
- const peg$c2 = 'strict';
754
- const peg$c3 = peg$literalExpectation('strict', true);
755
- const peg$c4 = 'graph';
756
- const peg$c5 = peg$literalExpectation('graph', true);
757
- const peg$c6 = 'digraph';
758
- const peg$c7 = peg$literalExpectation('digraph', true);
759
- const peg$c8 = '{';
760
- const peg$c9 = peg$literalExpectation('{', false);
761
- const peg$c10 = '}';
762
- const peg$c11 = peg$literalExpectation('}', false);
763
- const peg$c12 = function (_strict, _kind, id, children) {
764
- const strict = !!_strict;
765
- const kind = _kind.toLowerCase();
766
- const directed = kind === 'digraph';
767
- for (const edgeop of edgeops) {
768
- if (directed) {
769
- if (edgeop.operator !== '->') {
770
- error(`In digraph, it's necessary to describe with "->" operator to create edge.`, edgeop.location);
771
- }
772
- } else {
773
- if (edgeop.operator !== '--') {
774
- error(`In graph, it's necessary to describe with "--" operator to create edge.`, edgeop.location);
775
- }
776
- }
777
- }
778
- return b.createElement(
779
- 'Graph',
780
- id !== null
781
- ? {
782
- id,
783
- directed,
784
- strict,
785
- }
786
- : {
787
- directed,
788
- strict,
789
- },
790
- children,
791
- );
792
- };
793
- const peg$c13 = ';';
794
- const peg$c14 = peg$literalExpectation(';', false);
795
- const peg$c15 = function (keyValue) {
796
- return b.createElement(
797
- 'Attribute',
798
- {
799
- ...keyValue,
800
- },
801
- [],
802
- );
803
- };
804
- const peg$c16 = 'node';
805
- const peg$c17 = peg$literalExpectation('node', true);
806
- const peg$c18 = 'edge';
807
- const peg$c19 = peg$literalExpectation('edge', true);
808
- const peg$c20 = function (_kind, children) {
809
- return b.createElement(
810
- 'AttributeList',
811
- {
812
- kind: _kind.toLowerCase(),
813
- },
814
- children,
815
- );
816
- };
817
- const peg$c21 = function (id, rhs, _children) {
818
- // @ts-ignore
819
- return b.createElement(
820
- // @ts-ignore
821
- 'Edge',
822
- {
823
- targets: [id, ...rhs],
824
- },
825
- _children ?? [],
826
- );
827
- };
828
- const peg$c22 = function (id, _children) {
829
- return b.createElement(
830
- 'Node',
831
- {
832
- id,
555
+ };
556
+ const peg$c22 = function (id, _children) {
557
+ return b.createElement(
558
+ 'Node',
559
+ {
560
+ id,
833
561
  },
834
562
  _children ?? [],
835
563
  );
@@ -2766,10 +2494,10 @@ function peg$parse(input, options) {
2766
2494
  peg$maxFailExpected.push(expected1);
2767
2495
  }
2768
2496
  function peg$buildSimpleError(message, location1) {
2769
- return new SyntaxError(message, [], '', location1);
2497
+ return new SyntaxError$1(message, [], '', location1);
2770
2498
  }
2771
2499
  function peg$buildStructuredError(expected1, found, location1) {
2772
- return new SyntaxError(SyntaxError.buildMessage(expected1, found), expected1, found, location1);
2500
+ return new SyntaxError$1(SyntaxError$1.buildMessage(expected1, found), expected1, found, location1);
2773
2501
  }
2774
2502
  function peg$parseDot() {
2775
2503
  let s0, s1, s2, s3;
@@ -5528,13 +5256,301 @@ const parse$1 = peg$parse;
5528
5256
  function parse(input, options) {
5529
5257
  return parse$1(input, options);
5530
5258
  }
5259
+ /**
5260
+ * @group Convert DOT to AST
5261
+ */
5262
+ const SyntaxError = SyntaxError$1;
5531
5263
 
5264
+ function convertAttribute(key, value) {
5265
+ if (typeof value === 'string') {
5266
+ const trimmed = value.trim();
5267
+ const isHTMLLike = /^<.+>$/ms.test(trimmed);
5268
+ if (isHTMLLike) {
5269
+ return createElement(
5270
+ 'Attribute',
5271
+ {
5272
+ key: createElement('Literal', { value: key, quoted: false }, []),
5273
+ value: createElement('Literal', { value: trimmed.slice(1, trimmed.length - 1), quoted: 'html' }, []),
5274
+ },
5275
+ [],
5276
+ );
5277
+ } else {
5278
+ return createElement(
5279
+ 'Attribute',
5280
+ {
5281
+ key: createElement('Literal', { value: key, quoted: false }, []),
5282
+ value: createElement('Literal', { value: value, quoted: true }, []),
5283
+ },
5284
+ [],
5285
+ );
5286
+ }
5287
+ }
5288
+ return createElement(
5289
+ 'Attribute',
5290
+ {
5291
+ key: createElement('Literal', { value: key, quoted: false }, []),
5292
+ value: createElement('Literal', { value: String(value), quoted: false }, []),
5293
+ },
5294
+ [],
5295
+ );
5296
+ }
5297
+
5298
+ function convertComment(value, kind) {
5299
+ return createElement(
5300
+ 'Comment',
5301
+ {
5302
+ kind: kind,
5303
+ value: value,
5304
+ },
5305
+ [],
5306
+ );
5307
+ }
5308
+
5309
+ function convertClusterChildren(context, model) {
5310
+ return Array.from(
5311
+ (function* () {
5312
+ for (const [key, value] of model.values) {
5313
+ yield convertAttribute(key, value);
5314
+ }
5315
+ for (const attrs of Object.values(model.attributes)) {
5316
+ if (attrs.size > 0) {
5317
+ if (attrs.comment) {
5318
+ yield convertComment(attrs.comment, context.commentKind);
5319
+ }
5320
+ yield context.convert(attrs);
5321
+ }
5322
+ }
5323
+ for (const node of model.nodes) {
5324
+ if (node.comment) {
5325
+ yield convertComment(node.comment, context.commentKind);
5326
+ }
5327
+ yield context.convert(node);
5328
+ }
5329
+ for (const subgraph of model.subgraphs) {
5330
+ if (subgraph.comment) {
5331
+ yield convertComment(subgraph.comment, context.commentKind);
5332
+ }
5333
+ yield context.convert(subgraph);
5334
+ }
5335
+ for (const edge of model.edges) {
5336
+ if (edge.comment) {
5337
+ yield convertComment(edge.comment, context.commentKind);
5338
+ }
5339
+ yield context.convert(edge);
5340
+ }
5341
+ })(),
5342
+ );
5343
+ }
5344
+
5345
+ const AttributeListPrintPlugin = {
5346
+ match(model) {
5347
+ return model.$$type === 'AttributeList';
5348
+ },
5349
+ convert(context, model) {
5350
+ return createElement(
5351
+ 'AttributeList',
5352
+ {
5353
+ kind: model.$$kind,
5354
+ },
5355
+ model.values.map(([key, value]) => convertAttribute(key, value)),
5356
+ );
5357
+ },
5358
+ };
5359
+
5360
+ const EdgeConvertPlugin = {
5361
+ match(model) {
5362
+ return model.$$type === 'Edge';
5363
+ },
5364
+ convert(context, model) {
5365
+ return createElement(
5366
+ 'Edge',
5367
+ {
5368
+ targets: model.targets.map((target) => {
5369
+ if (common.isNodeModel(target)) {
5370
+ return createElement(
5371
+ 'NodeRef',
5372
+ {
5373
+ id: createElement(
5374
+ 'Literal',
5375
+ {
5376
+ value: target.id,
5377
+ quoted: true,
5378
+ },
5379
+ [],
5380
+ ),
5381
+ },
5382
+ [],
5383
+ );
5384
+ } else if (common.isForwardRefNode(target)) {
5385
+ return createElement(
5386
+ 'NodeRef',
5387
+ {
5388
+ id: createElement(
5389
+ 'Literal',
5390
+ {
5391
+ value: target.id,
5392
+ quoted: true,
5393
+ },
5394
+ [],
5395
+ ),
5396
+ port: target.port
5397
+ ? createElement(
5398
+ 'Literal',
5399
+ {
5400
+ value: target.port,
5401
+ quoted: true,
5402
+ },
5403
+ [],
5404
+ )
5405
+ : undefined,
5406
+ compass: target.compass
5407
+ ? createElement(
5408
+ 'Literal',
5409
+ {
5410
+ value: target.compass,
5411
+ quoted: true,
5412
+ },
5413
+ [],
5414
+ )
5415
+ : undefined,
5416
+ },
5417
+ [],
5418
+ );
5419
+ }
5420
+ }),
5421
+ },
5422
+ [
5423
+ ...(model.attributes.comment ? [convertComment(model.attributes.comment, context.commentKind)] : []),
5424
+ ...model.attributes.values.map(([key, value]) => convertAttribute(key, value)),
5425
+ ],
5426
+ );
5427
+ },
5428
+ };
5429
+
5430
+ const GraphConvertPlugin = {
5431
+ match(model) {
5432
+ return model.$$type === 'Graph';
5433
+ },
5434
+ convert(context, model) {
5435
+ return createElement('Dot', {}, [
5436
+ ...(model.comment ? [convertComment(model.comment, context.commentKind)] : []),
5437
+ createElement(
5438
+ 'Graph',
5439
+ {
5440
+ directed: model.directed,
5441
+ strict: model.strict,
5442
+ id: model.id
5443
+ ? createElement(
5444
+ 'Literal',
5445
+ {
5446
+ value: model.id,
5447
+ quoted: true,
5448
+ },
5449
+ [],
5450
+ )
5451
+ : undefined,
5452
+ },
5453
+ convertClusterChildren(context, model),
5454
+ ),
5455
+ ]);
5456
+ },
5457
+ };
5458
+
5459
+ const NodeConvertPlugin = {
5460
+ match(model) {
5461
+ return model.$$type === 'Node';
5462
+ },
5463
+ convert(context, model) {
5464
+ return createElement(
5465
+ 'Node',
5466
+ {
5467
+ id: createElement(
5468
+ 'Literal',
5469
+ {
5470
+ value: model.id,
5471
+ quoted: true,
5472
+ },
5473
+ [],
5474
+ ),
5475
+ },
5476
+ [
5477
+ ...(model.attributes.comment ? [convertComment(model.attributes.comment, context.commentKind)] : []),
5478
+ ...model.attributes.values.map(([key, value]) => convertAttribute(key, value)),
5479
+ ],
5480
+ );
5481
+ },
5482
+ };
5483
+
5484
+ const SubgraphConvertPlugin = {
5485
+ match(model) {
5486
+ return model.$$type === 'Subgraph';
5487
+ },
5488
+ convert(context, model) {
5489
+ return createElement(
5490
+ 'Subgraph',
5491
+ {
5492
+ id: model.id
5493
+ ? createElement(
5494
+ 'Literal',
5495
+ {
5496
+ value: model.id,
5497
+ quoted: true,
5498
+ },
5499
+ [],
5500
+ )
5501
+ : undefined,
5502
+ },
5503
+ convertClusterChildren(context, model),
5504
+ );
5505
+ },
5506
+ };
5507
+
5508
+ const defaultPlugins = [
5509
+ AttributeListPrintPlugin,
5510
+ EdgeConvertPlugin,
5511
+ NodeConvertPlugin,
5512
+ GraphConvertPlugin,
5513
+ SubgraphConvertPlugin,
5514
+ ];
5515
+
5516
+ /**
5517
+ * @group Convert Model to AST
5518
+ */
5519
+ class FromModelConverter {
5520
+ options;
5521
+ /** @hidden */
5522
+ #plugins = [...defaultPlugins];
5523
+ constructor(options = {}) {
5524
+ this.options = options;
5525
+ }
5526
+ convert(model) {
5527
+ const plugins = [...this.#plugins];
5528
+ const { commentKind = 'Slash' } = this.options;
5529
+ const context = {
5530
+ commentKind,
5531
+ convert(m) {
5532
+ for (const plugin of plugins) {
5533
+ /* */
5534
+ if (plugin.match(m)) {
5535
+ return plugin.convert(context, m);
5536
+ }
5537
+ }
5538
+ throw Error();
5539
+ },
5540
+ };
5541
+ return context.convert(model);
5542
+ }
5543
+ }
5544
+
5545
+ /**
5546
+ * @group Convert Model to AST
5547
+ */
5532
5548
  function fromModel(model, options) {
5533
- return new Converter(options).convert(model);
5549
+ return new FromModelConverter(options).convert(model);
5534
5550
  }
5535
5551
 
5536
5552
  exports.Builder = Builder;
5537
- exports.Converter = Converter;
5553
+ exports.FromModelConverter = FromModelConverter;
5538
5554
  exports.Printer = Printer;
5539
5555
  exports.SyntaxError = SyntaxError;
5540
5556
  exports.createElement = createElement;