@ts-graphviz/ast 3.0.7 → 3.0.8
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 +10 -0
- package/README.md +2 -1
- package/lib/ast.js +90 -43
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @ts-graphviz/ast
|
|
2
2
|
|
|
3
|
+
## 3.0.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1763](https://github.com/ts-graphviz/ts-graphviz/pull/1763) [`24cc46e`](https://github.com/ts-graphviz/ts-graphviz/commit/24cc46ebf0078440fa498f89b21e7224725c16b6) Thanks [@kamiazya](https://github.com/kamiazya)! - Options are read from their own properties only, and values are always written as DOT text:
|
|
8
|
+
|
|
9
|
+
- `toStream` / `toFile`, `parse`, `stringify`, `fromModel` / `toModel`, `fromDot` and `toDot` read only the options their caller set. `toStream` / `toFile` also pass Node.js only the options and environment they set.
|
|
10
|
+
- Attribute keys and values, IDs, ports, compass points and comments are written so that DOT reads them back as the same text. A string of the form `<…>` is written as an HTML-like label only when it is well-formed; otherwise it is written as a quoted string.
|
|
11
|
+
- Writing attribute values and reading block comments are faster on large inputs.
|
|
12
|
+
|
|
3
13
|
## 3.0.7
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ This package contains the module for processing the DOT language at the **A**bst
|
|
|
22
22
|
|
|
23
23
|
[](https://github.com/ts-graphviz/ts-graphviz)
|
|
24
24
|
[](https://www.npmjs.com/package/ts-graphviz)
|
|
25
|
-
[](https://ts-graphviz.github.io/
|
|
25
|
+
[](https://ts-graphviz.github.io/api/)
|
|
26
26
|
[](https://deepwiki.com/ts-graphviz/ts-graphviz)
|
|
27
27
|
|
|
28
28
|
[](https://github.com/sponsors/ts-graphviz)
|
|
@@ -175,6 +175,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
175
175
|
<td align="center" valign="top" width="14.28%"><a href="http://www.walterra.dev"><img src="https://avatars.githubusercontent.com/u/230104?v=4?s=100" width="100px;" alt="Walter Rafelsberger"/><br /><sub><b>Walter Rafelsberger</b></sub></a><br /><a href="#question-walterra" title="Answering Questions">💬</a></td>
|
|
176
176
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/grsjst"><img src="https://avatars.githubusercontent.com/u/4739018?v=4?s=100" width="100px;" alt="grsjst"/><br /><sub><b>grsjst</b></sub></a><br /><a href="https://github.com/ts-graphviz/ts-graphviz/issues?q=author%3Agrsjst" title="Bug reports">🐛</a></td>
|
|
177
177
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stephenirven"><img src="https://avatars.githubusercontent.com/u/4293560?v=4?s=100" width="100px;" alt="Steve"/><br /><sub><b>Steve</b></sub></a><br /><a href="https://github.com/ts-graphviz/ts-graphviz/issues?q=author%3Astephenirven" title="Bug reports">🐛</a></td>
|
|
178
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/btomaj"><img src="https://avatars.githubusercontent.com/u/2638986?v=4?s=100" width="100px;" alt="poetry by"/><br /><sub><b>poetry by</b></sub></a><br /><a href="https://github.com/ts-graphviz/ts-graphviz/issues?q=author%3Abtomaj" title="Bug reports">🐛</a></td>
|
|
178
179
|
</tr>
|
|
179
180
|
</tbody>
|
|
180
181
|
</table>
|
package/lib/ast.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { isNodeModel, isForwardRefNode, createModelsContext } from "@ts-graphviz/common";
|
|
2
|
+
function ownOptions(options) {
|
|
3
|
+
const own = /* @__PURE__ */ Object.create(null);
|
|
4
|
+
if (options != null) {
|
|
5
|
+
const descriptors = Object.getOwnPropertyDescriptors(options);
|
|
6
|
+
for (const key of Object.getOwnPropertyNames(descriptors)) {
|
|
7
|
+
const descriptor = descriptors[key];
|
|
8
|
+
own[key] = "value" in descriptor ? descriptor.value : descriptor.get?.call(options);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return own;
|
|
12
|
+
}
|
|
2
13
|
class ASTNodeCountExceededError extends Error {
|
|
3
14
|
/**
|
|
4
15
|
* Constructor
|
|
@@ -22,7 +33,8 @@ class Builder {
|
|
|
22
33
|
*/
|
|
23
34
|
constructor(options) {
|
|
24
35
|
this.options = options;
|
|
25
|
-
this.
|
|
36
|
+
this.options = ownOptions(options);
|
|
37
|
+
this.maxNodes = this.options.maxASTNodes ?? DEFAULT_MAX_AST_NODES;
|
|
26
38
|
}
|
|
27
39
|
nodeCount = 0;
|
|
28
40
|
maxNodes;
|
|
@@ -465,7 +477,7 @@ function peg$parse(input, options) {
|
|
|
465
477
|
"Comment",
|
|
466
478
|
{
|
|
467
479
|
kind: "Block",
|
|
468
|
-
value: dedent(v.join("").replace(
|
|
480
|
+
value: dedent(v.join("").replace(/^[ \t]*\*/gm, ""))
|
|
469
481
|
},
|
|
470
482
|
[]
|
|
471
483
|
);
|
|
@@ -2700,8 +2712,10 @@ function peg$parse(input, options) {
|
|
|
2700
2712
|
const matches = str.match(/\n([\t ]+|(?!\s).)/g);
|
|
2701
2713
|
if (matches) {
|
|
2702
2714
|
const indentLengths = matches.map((match) => match.match(/[\t ]/g)?.length ?? 0);
|
|
2715
|
+
let indent = Infinity;
|
|
2716
|
+
for (const length of indentLengths) if (length < indent) indent = length;
|
|
2703
2717
|
const pattern = new RegExp(`
|
|
2704
|
-
[ ]{${
|
|
2718
|
+
[ ]{${indent}}`, "g");
|
|
2705
2719
|
return str.replace(pattern, "\n");
|
|
2706
2720
|
}
|
|
2707
2721
|
return str;
|
|
@@ -2763,7 +2777,7 @@ function parse(input, options) {
|
|
|
2763
2777
|
maxEdgeChainDepth,
|
|
2764
2778
|
maxInputSize,
|
|
2765
2779
|
maxASTNodes
|
|
2766
|
-
} = options
|
|
2780
|
+
} = ownOptions(options);
|
|
2767
2781
|
const inputSizeLimit = maxInputSize ?? DEFAULT_MAX_INPUT_SIZE;
|
|
2768
2782
|
if (inputSizeLimit > 0) {
|
|
2769
2783
|
const inputBytes = new TextEncoder().encode(input).length;
|
|
@@ -2830,13 +2844,13 @@ const AttributePrintPlugin = {
|
|
|
2830
2844
|
};
|
|
2831
2845
|
const ZERO_WIDTH_SPACE = "";
|
|
2832
2846
|
function escapeComment(value, kind) {
|
|
2833
|
-
let escaped = value.replace(/\0/g, "");
|
|
2847
|
+
let escaped = String(value).replace(/\0/g, "");
|
|
2834
2848
|
if (kind === "Block") {
|
|
2835
2849
|
escaped = escaped.replace(/\*\//g, `*${ZERO_WIDTH_SPACE}/`);
|
|
2836
2850
|
}
|
|
2837
2851
|
return escaped;
|
|
2838
2852
|
}
|
|
2839
|
-
const EOL_PATTERN = /\r
|
|
2853
|
+
const EOL_PATTERN = /\r\n|\r|\n/;
|
|
2840
2854
|
const paddingMap = {
|
|
2841
2855
|
Block: " * ",
|
|
2842
2856
|
Macro: "# ",
|
|
@@ -2910,14 +2924,27 @@ const GraphPrintPlugin = {
|
|
|
2910
2924
|
yield "}";
|
|
2911
2925
|
}
|
|
2912
2926
|
};
|
|
2913
|
-
|
|
2914
|
-
const
|
|
2915
|
-
"
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2927
|
+
function escape(value) {
|
|
2928
|
+
const text = String(value);
|
|
2929
|
+
let out = "";
|
|
2930
|
+
let backslashes = 0;
|
|
2931
|
+
for (const ch of text) {
|
|
2932
|
+
if (ch === "\0") continue;
|
|
2933
|
+
if (ch === "\\") {
|
|
2934
|
+
backslashes++;
|
|
2935
|
+
out += ch;
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
if (ch === "\n" || ch === "\r") {
|
|
2939
|
+
if (backslashes % 2 === 1) out += "\\";
|
|
2940
|
+
out += ch === "\n" ? String.raw`\n` : String.raw`\r`;
|
|
2941
|
+
} else {
|
|
2942
|
+
if (ch === '"' && backslashes % 2 === 0) out += "\\";
|
|
2943
|
+
out += ch;
|
|
2944
|
+
}
|
|
2945
|
+
backslashes = 0;
|
|
2946
|
+
}
|
|
2947
|
+
return backslashes % 2 === 1 ? `${out}\\` : out;
|
|
2921
2948
|
}
|
|
2922
2949
|
const LiteralPrintPlugin = {
|
|
2923
2950
|
match(ast) {
|
|
@@ -2971,11 +2998,11 @@ const NodeRefPrintPlugin = {
|
|
|
2971
2998
|
},
|
|
2972
2999
|
*print(context, ast) {
|
|
2973
3000
|
yield* context.print(ast.id);
|
|
2974
|
-
if (ast.port) {
|
|
3001
|
+
if (Object.hasOwn(ast, "port") && ast.port) {
|
|
2975
3002
|
yield ":";
|
|
2976
3003
|
yield* context.print(ast.port);
|
|
2977
3004
|
}
|
|
2978
|
-
if (ast.compass) {
|
|
3005
|
+
if (Object.hasOwn(ast, "compass") && ast.compass) {
|
|
2979
3006
|
yield ":";
|
|
2980
3007
|
yield* context.print(ast.compass);
|
|
2981
3008
|
}
|
|
@@ -3017,6 +3044,7 @@ class Printer {
|
|
|
3017
3044
|
*/
|
|
3018
3045
|
constructor(options = {}) {
|
|
3019
3046
|
this.options = options;
|
|
3047
|
+
this.options = ownOptions(options);
|
|
3020
3048
|
}
|
|
3021
3049
|
/** @internal */
|
|
3022
3050
|
#plugins = [...defaultPlugins$2];
|
|
@@ -3090,15 +3118,32 @@ function stringify(ast, options) {
|
|
|
3090
3118
|
}
|
|
3091
3119
|
return result;
|
|
3092
3120
|
}
|
|
3121
|
+
function isBalancedMarkup(markup) {
|
|
3122
|
+
let depth = 0;
|
|
3123
|
+
for (const ch of markup) {
|
|
3124
|
+
if (ch === "<") depth++;
|
|
3125
|
+
else if (ch === ">" && --depth < 0) return false;
|
|
3126
|
+
}
|
|
3127
|
+
return depth === 0 && !markup.includes("\0");
|
|
3128
|
+
}
|
|
3129
|
+
function keyLiteral(createElement2, key) {
|
|
3130
|
+
const bare = typeof key === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key) && !/^(?:node|edge|graph|digraph|subgraph|strict)$/i.test(key);
|
|
3131
|
+
return createElement2(
|
|
3132
|
+
"Literal",
|
|
3133
|
+
{ value: key, quoted: !bare },
|
|
3134
|
+
[]
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
const NUMERAL = /^-?(?:\.\d+|\d+(?:\.\d*)?)$/;
|
|
3093
3138
|
function convertAttribute(createElement2, key, value) {
|
|
3094
3139
|
if (typeof value === "string") {
|
|
3095
3140
|
const trimmed = value.trim();
|
|
3096
|
-
const isHTMLLike =
|
|
3141
|
+
const isHTMLLike = trimmed.length > 2 && trimmed.startsWith("<") && trimmed.endsWith(">") && isBalancedMarkup(trimmed.slice(1, -1));
|
|
3097
3142
|
if (isHTMLLike) {
|
|
3098
3143
|
return createElement2(
|
|
3099
3144
|
"Attribute",
|
|
3100
3145
|
{
|
|
3101
|
-
key: createElement2
|
|
3146
|
+
key: keyLiteral(createElement2, key),
|
|
3102
3147
|
value: createElement2(
|
|
3103
3148
|
"Literal",
|
|
3104
3149
|
{ value: trimmed.slice(1, -1), quoted: "html" },
|
|
@@ -3111,21 +3156,19 @@ function convertAttribute(createElement2, key, value) {
|
|
|
3111
3156
|
return createElement2(
|
|
3112
3157
|
"Attribute",
|
|
3113
3158
|
{
|
|
3114
|
-
key: createElement2
|
|
3159
|
+
key: keyLiteral(createElement2, key),
|
|
3115
3160
|
value: createElement2("Literal", { value, quoted: true }, [])
|
|
3116
3161
|
},
|
|
3117
3162
|
[]
|
|
3118
3163
|
);
|
|
3119
3164
|
}
|
|
3165
|
+
const text = String(value);
|
|
3166
|
+
const bare = typeof value === "boolean" || typeof value === "number" && NUMERAL.test(text);
|
|
3120
3167
|
return createElement2(
|
|
3121
3168
|
"Attribute",
|
|
3122
3169
|
{
|
|
3123
|
-
key: createElement2
|
|
3124
|
-
value: createElement2(
|
|
3125
|
-
"Literal",
|
|
3126
|
-
{ value: String(value), quoted: false },
|
|
3127
|
-
[]
|
|
3128
|
-
)
|
|
3170
|
+
key: keyLiteral(createElement2, key),
|
|
3171
|
+
value: createElement2("Literal", { value: text, quoted: !bare }, [])
|
|
3129
3172
|
},
|
|
3130
3173
|
[]
|
|
3131
3174
|
);
|
|
@@ -3209,6 +3252,9 @@ const AttributeListPlugin = {
|
|
|
3209
3252
|
);
|
|
3210
3253
|
}
|
|
3211
3254
|
};
|
|
3255
|
+
function ownField(target, field) {
|
|
3256
|
+
return Object.hasOwn(target, field) ? target[field] : void 0;
|
|
3257
|
+
}
|
|
3212
3258
|
const EdgePlugin$1 = {
|
|
3213
3259
|
match(model) {
|
|
3214
3260
|
return model.$$type === "Edge";
|
|
@@ -3247,18 +3293,18 @@ const EdgePlugin$1 = {
|
|
|
3247
3293
|
},
|
|
3248
3294
|
[]
|
|
3249
3295
|
),
|
|
3250
|
-
port: target
|
|
3296
|
+
port: ownField(target, "port") ? createElement2(
|
|
3251
3297
|
"Literal",
|
|
3252
3298
|
{
|
|
3253
|
-
value: target
|
|
3299
|
+
value: ownField(target, "port"),
|
|
3254
3300
|
quoted: true
|
|
3255
3301
|
},
|
|
3256
3302
|
[]
|
|
3257
3303
|
) : void 0,
|
|
3258
|
-
compass: target
|
|
3304
|
+
compass: ownField(target, "compass") ? createElement2(
|
|
3259
3305
|
"Literal",
|
|
3260
3306
|
{
|
|
3261
|
-
value: target
|
|
3307
|
+
value: ownField(target, "compass"),
|
|
3262
3308
|
quoted: true
|
|
3263
3309
|
},
|
|
3264
3310
|
[]
|
|
@@ -3298,18 +3344,18 @@ const EdgePlugin$1 = {
|
|
|
3298
3344
|
},
|
|
3299
3345
|
[]
|
|
3300
3346
|
),
|
|
3301
|
-
port: n
|
|
3347
|
+
port: ownField(n, "port") ? createElement2(
|
|
3302
3348
|
"Literal",
|
|
3303
3349
|
{
|
|
3304
|
-
value: n
|
|
3350
|
+
value: ownField(n, "port"),
|
|
3305
3351
|
quoted: true
|
|
3306
3352
|
},
|
|
3307
3353
|
[]
|
|
3308
3354
|
) : void 0,
|
|
3309
|
-
compass: n
|
|
3355
|
+
compass: ownField(n, "compass") ? createElement2(
|
|
3310
3356
|
"Literal",
|
|
3311
3357
|
{
|
|
3312
|
-
value: n
|
|
3358
|
+
value: ownField(n, "compass"),
|
|
3313
3359
|
quoted: true
|
|
3314
3360
|
},
|
|
3315
3361
|
[]
|
|
@@ -3428,6 +3474,7 @@ const defaultPlugins$1 = [
|
|
|
3428
3474
|
class FromModelConverter {
|
|
3429
3475
|
constructor(options = {}) {
|
|
3430
3476
|
this.options = options;
|
|
3477
|
+
this.options = ownOptions(options);
|
|
3431
3478
|
}
|
|
3432
3479
|
/** @hidden */
|
|
3433
3480
|
#plugins = [...defaultPlugins$1];
|
|
@@ -3512,21 +3559,20 @@ function collectAttributes(attrs) {
|
|
|
3512
3559
|
return acc;
|
|
3513
3560
|
}, {});
|
|
3514
3561
|
}
|
|
3562
|
+
function toEdgeTarget(t) {
|
|
3563
|
+
return {
|
|
3564
|
+
id: t.id.value,
|
|
3565
|
+
port: Object.hasOwn(t, "port") ? t.port?.value : void 0,
|
|
3566
|
+
compass: Object.hasOwn(t, "compass") ? t.compass?.value : void 0
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3515
3569
|
function convertToEdgeTargetTuple(edge) {
|
|
3516
3570
|
return edge.targets.map((t) => {
|
|
3517
3571
|
switch (t.type) {
|
|
3518
3572
|
case "NodeRef":
|
|
3519
|
-
return
|
|
3520
|
-
id: t.id.value,
|
|
3521
|
-
port: t.port?.value,
|
|
3522
|
-
compass: t.compass?.value
|
|
3523
|
-
};
|
|
3573
|
+
return toEdgeTarget(t);
|
|
3524
3574
|
case "NodeRefGroup":
|
|
3525
|
-
return t.children.map(
|
|
3526
|
-
id: t2.id.value,
|
|
3527
|
-
port: t2.port?.value,
|
|
3528
|
-
compass: t2.compass?.value
|
|
3529
|
-
}));
|
|
3575
|
+
return t.children.map(toEdgeTarget);
|
|
3530
3576
|
}
|
|
3531
3577
|
});
|
|
3532
3578
|
}
|
|
@@ -3647,6 +3693,7 @@ const defaultPlugins = [
|
|
|
3647
3693
|
class ToModelConverter {
|
|
3648
3694
|
constructor(options = {}) {
|
|
3649
3695
|
this.options = options;
|
|
3696
|
+
this.options = ownOptions(options);
|
|
3650
3697
|
}
|
|
3651
3698
|
/** @hidden */
|
|
3652
3699
|
plugins = [
|