@graphty/graph-io 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +250 -28
- package/dist/chunks/children-CL3Cy0ez.js +238 -0
- package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
- package/dist/chunks/escape-DyI8JofU.js +938 -0
- package/dist/chunks/escape-DyI8JofU.js.map +1 -0
- package/dist/chunks/importer-CQnJuWJw.js +2987 -0
- package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
- package/dist/chunks/importer-CpCpfbxr.js +2015 -0
- package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
- package/dist/chunks/importer-DbnGYr3_.js +2342 -0
- package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
- package/dist/chunks/importer-GozH8DkN.js +3050 -0
- package/dist/chunks/importer-GozH8DkN.js.map +1 -0
- package/dist/chunks/records-CGpxszm1.js +605 -0
- package/dist/chunks/records-CGpxszm1.js.map +1 -0
- package/dist/chunks/text-CajMdVFy.js +189 -0
- package/dist/chunks/text-CajMdVFy.js.map +1 -0
- package/dist/chunks/writer-DxSKC7TL.js +2842 -0
- package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
- package/dist/csv.d.ts +1 -0
- package/dist/csv.js +1702 -0
- package/dist/csv.js.map +1 -0
- package/dist/dot.d.ts +1 -0
- package/dist/dot.js +8 -0
- package/dist/dot.js.map +1 -0
- package/dist/gexf.d.ts +1 -0
- package/dist/gexf.js +3466 -0
- package/dist/gexf.js.map +1 -0
- package/dist/gml.d.ts +1 -0
- package/dist/gml.js +2647 -0
- package/dist/gml.js.map +1 -0
- package/dist/graph-io.d.ts +1 -0
- package/dist/graph-io.js +790 -0
- package/dist/graph-io.js.map +1 -0
- package/dist/graphml.d.ts +1 -0
- package/dist/graphml.js +8 -0
- package/dist/graphml.js.map +1 -0
- package/dist/json.d.ts +1 -0
- package/dist/json.js +11 -0
- package/dist/json.js.map +1 -0
- package/dist/neo4j.d.ts +1 -0
- package/dist/neo4j.js +2046 -0
- package/dist/neo4j.js.map +1 -0
- package/dist/pajek.d.ts +1 -0
- package/dist/pajek.js +8 -0
- package/dist/pajek.js.map +1 -0
- package/dist/src/children.d.ts +134 -0
- package/dist/src/children.d.ts.map +1 -0
- package/dist/src/children.js +274 -0
- package/dist/src/children.js.map +1 -0
- package/dist/src/common/attributes.d.ts +229 -0
- package/dist/src/common/attributes.d.ts.map +1 -0
- package/dist/src/common/attributes.js +368 -0
- package/dist/src/common/attributes.js.map +1 -0
- package/dist/src/common/codes.d.ts +105 -0
- package/dist/src/common/codes.d.ts.map +1 -0
- package/dist/src/common/codes.js +107 -0
- package/dist/src/common/codes.js.map +1 -0
- package/dist/src/common/declared-types.d.ts +84 -0
- package/dist/src/common/declared-types.d.ts.map +1 -0
- package/dist/src/common/declared-types.js +326 -0
- package/dist/src/common/declared-types.js.map +1 -0
- package/dist/src/common/direction.d.ts +206 -0
- package/dist/src/common/direction.d.ts.map +1 -0
- package/dist/src/common/direction.js +370 -0
- package/dist/src/common/direction.js.map +1 -0
- package/dist/src/common/escape.d.ts +92 -0
- package/dist/src/common/escape.d.ts.map +1 -0
- package/dist/src/common/escape.js +212 -0
- package/dist/src/common/escape.js.map +1 -0
- package/dist/src/common/export.d.ts +249 -0
- package/dist/src/common/export.d.ts.map +1 -0
- package/dist/src/common/export.js +594 -0
- package/dist/src/common/export.js.map +1 -0
- package/dist/src/common/format.d.ts +59 -0
- package/dist/src/common/format.d.ts.map +1 -0
- package/dist/src/common/format.js +106 -0
- package/dist/src/common/format.js.map +1 -0
- package/dist/src/common/ids.d.ts +83 -0
- package/dist/src/common/ids.d.ts.map +1 -0
- package/dist/src/common/ids.js +158 -0
- package/dist/src/common/ids.js.map +1 -0
- package/dist/src/common/input.d.ts +100 -0
- package/dist/src/common/input.d.ts.map +1 -0
- package/dist/src/common/input.js +335 -0
- package/dist/src/common/input.js.map +1 -0
- package/dist/src/common/lists.d.ts +34 -0
- package/dist/src/common/lists.d.ts.map +1 -0
- package/dist/src/common/lists.js +185 -0
- package/dist/src/common/lists.js.map +1 -0
- package/dist/src/common/options.d.ts +108 -0
- package/dist/src/common/options.d.ts.map +1 -0
- package/dist/src/common/options.js +265 -0
- package/dist/src/common/options.js.map +1 -0
- package/dist/src/common/report.d.ts +187 -0
- package/dist/src/common/report.d.ts.map +1 -0
- package/dist/src/common/report.js +274 -0
- package/dist/src/common/report.js.map +1 -0
- package/dist/src/common/temporal.d.ts +71 -0
- package/dist/src/common/temporal.d.ts.map +1 -0
- package/dist/src/common/temporal.js +266 -0
- package/dist/src/common/temporal.js.map +1 -0
- package/dist/src/common/text.d.ts +104 -0
- package/dist/src/common/text.d.ts.map +1 -0
- package/dist/src/common/text.js +255 -0
- package/dist/src/common/text.js.map +1 -0
- package/dist/src/common/weights.d.ts +77 -0
- package/dist/src/common/weights.d.ts.map +1 -0
- package/dist/src/common/weights.js +156 -0
- package/dist/src/common/weights.js.map +1 -0
- package/dist/src/common/writer.d.ts +51 -0
- package/dist/src/common/writer.d.ts.map +1 -0
- package/dist/src/common/writer.js +108 -0
- package/dist/src/common/writer.js.map +1 -0
- package/dist/src/common/xml.d.ts +245 -0
- package/dist/src/common/xml.d.ts.map +1 -0
- package/dist/src/common/xml.js +942 -0
- package/dist/src/common/xml.js.map +1 -0
- package/dist/src/formats/csv/exporter.d.ts +70 -0
- package/dist/src/formats/csv/exporter.d.ts.map +1 -0
- package/dist/src/formats/csv/exporter.js +682 -0
- package/dist/src/formats/csv/exporter.js.map +1 -0
- package/dist/src/formats/csv/header.d.ts +66 -0
- package/dist/src/formats/csv/header.d.ts.map +1 -0
- package/dist/src/formats/csv/header.js +152 -0
- package/dist/src/formats/csv/header.js.map +1 -0
- package/dist/src/formats/csv/importer.d.ts +82 -0
- package/dist/src/formats/csv/importer.d.ts.map +1 -0
- package/dist/src/formats/csv/importer.js +849 -0
- package/dist/src/formats/csv/importer.js.map +1 -0
- package/dist/src/formats/csv/index.d.ts +60 -0
- package/dist/src/formats/csv/index.d.ts.map +1 -0
- package/dist/src/formats/csv/index.js +63 -0
- package/dist/src/formats/csv/index.js.map +1 -0
- package/dist/src/formats/csv/records.d.ts +188 -0
- package/dist/src/formats/csv/records.d.ts.map +1 -0
- package/dist/src/formats/csv/records.js +702 -0
- package/dist/src/formats/csv/records.js.map +1 -0
- package/dist/src/formats/csv/values.d.ts +105 -0
- package/dist/src/formats/csv/values.d.ts.map +1 -0
- package/dist/src/formats/csv/values.js +192 -0
- package/dist/src/formats/csv/values.js.map +1 -0
- package/dist/src/formats/dot/exporter.d.ts +52 -0
- package/dist/src/formats/dot/exporter.d.ts.map +1 -0
- package/dist/src/formats/dot/exporter.js +836 -0
- package/dist/src/formats/dot/exporter.js.map +1 -0
- package/dist/src/formats/dot/importer.d.ts +102 -0
- package/dist/src/formats/dot/importer.d.ts.map +1 -0
- package/dist/src/formats/dot/importer.js +1291 -0
- package/dist/src/formats/dot/importer.js.map +1 -0
- package/dist/src/formats/dot/index.d.ts +7 -0
- package/dist/src/formats/dot/index.d.ts.map +1 -0
- package/dist/src/formats/dot/index.js +7 -0
- package/dist/src/formats/dot/index.js.map +1 -0
- package/dist/src/formats/dot/names.d.ts +29 -0
- package/dist/src/formats/dot/names.d.ts.map +1 -0
- package/dist/src/formats/dot/names.js +28 -0
- package/dist/src/formats/dot/names.js.map +1 -0
- package/dist/src/formats/dot/tokenizer.d.ts +114 -0
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
- package/dist/src/formats/dot/tokenizer.js +341 -0
- package/dist/src/formats/dot/tokenizer.js.map +1 -0
- package/dist/src/formats/gexf/exporter.d.ts +56 -0
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
- package/dist/src/formats/gexf/exporter.js +1395 -0
- package/dist/src/formats/gexf/exporter.js.map +1 -0
- package/dist/src/formats/gexf/importer.d.ts +73 -0
- package/dist/src/formats/gexf/importer.d.ts.map +1 -0
- package/dist/src/formats/gexf/importer.js +1880 -0
- package/dist/src/formats/gexf/importer.js.map +1 -0
- package/dist/src/formats/gexf/index.d.ts +96 -0
- package/dist/src/formats/gexf/index.d.ts.map +1 -0
- package/dist/src/formats/gexf/index.js +97 -0
- package/dist/src/formats/gexf/index.js.map +1 -0
- package/dist/src/formats/gexf/schema.d.ts +135 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -0
- package/dist/src/formats/gexf/schema.js +323 -0
- package/dist/src/formats/gexf/schema.js.map +1 -0
- package/dist/src/formats/gml/exporter.d.ts +69 -0
- package/dist/src/formats/gml/exporter.d.ts.map +1 -0
- package/dist/src/formats/gml/exporter.js +1093 -0
- package/dist/src/formats/gml/exporter.js.map +1 -0
- package/dist/src/formats/gml/importer.d.ts +66 -0
- package/dist/src/formats/gml/importer.d.ts.map +1 -0
- package/dist/src/formats/gml/importer.js +1331 -0
- package/dist/src/formats/gml/importer.js.map +1 -0
- package/dist/src/formats/gml/index.d.ts +85 -0
- package/dist/src/formats/gml/index.d.ts.map +1 -0
- package/dist/src/formats/gml/index.js +88 -0
- package/dist/src/formats/gml/index.js.map +1 -0
- package/dist/src/formats/gml/syntax.d.ts +186 -0
- package/dist/src/formats/gml/syntax.d.ts.map +1 -0
- package/dist/src/formats/gml/syntax.js +467 -0
- package/dist/src/formats/gml/syntax.js.map +1 -0
- package/dist/src/formats/graphml/constants.d.ts +169 -0
- package/dist/src/formats/graphml/constants.d.ts.map +1 -0
- package/dist/src/formats/graphml/constants.js +165 -0
- package/dist/src/formats/graphml/constants.js.map +1 -0
- package/dist/src/formats/graphml/exporter.d.ts +34 -0
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
- package/dist/src/formats/graphml/exporter.js +1176 -0
- package/dist/src/formats/graphml/exporter.js.map +1 -0
- package/dist/src/formats/graphml/importer.d.ts +31 -0
- package/dist/src/formats/graphml/importer.d.ts.map +1 -0
- package/dist/src/formats/graphml/importer.js +1607 -0
- package/dist/src/formats/graphml/importer.js.map +1 -0
- package/dist/src/formats/graphml/index.d.ts +8 -0
- package/dist/src/formats/graphml/index.d.ts.map +1 -0
- package/dist/src/formats/graphml/index.js +8 -0
- package/dist/src/formats/graphml/index.js.map +1 -0
- package/dist/src/formats/graphml/tree.d.ts +72 -0
- package/dist/src/formats/graphml/tree.d.ts.map +1 -0
- package/dist/src/formats/graphml/tree.js +290 -0
- package/dist/src/formats/graphml/tree.js.map +1 -0
- package/dist/src/formats/json/dialect.d.ts +125 -0
- package/dist/src/formats/json/dialect.d.ts.map +1 -0
- package/dist/src/formats/json/dialect.js +262 -0
- package/dist/src/formats/json/dialect.js.map +1 -0
- package/dist/src/formats/json/exporter.d.ts +89 -0
- package/dist/src/formats/json/exporter.d.ts.map +1 -0
- package/dist/src/formats/json/exporter.js +1358 -0
- package/dist/src/formats/json/exporter.js.map +1 -0
- package/dist/src/formats/json/importer.d.ts +108 -0
- package/dist/src/formats/json/importer.d.ts.map +1 -0
- package/dist/src/formats/json/importer.js +1838 -0
- package/dist/src/formats/json/importer.js.map +1 -0
- package/dist/src/formats/json/index.d.ts +8 -0
- package/dist/src/formats/json/index.d.ts.map +1 -0
- package/dist/src/formats/json/index.js +8 -0
- package/dist/src/formats/json/index.js.map +1 -0
- package/dist/src/formats/neo4j/exporter.d.ts +68 -0
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
- package/dist/src/formats/neo4j/exporter.js +1055 -0
- package/dist/src/formats/neo4j/exporter.js.map +1 -0
- package/dist/src/formats/neo4j/header.d.ts +52 -0
- package/dist/src/formats/neo4j/header.d.ts.map +1 -0
- package/dist/src/formats/neo4j/header.js +131 -0
- package/dist/src/formats/neo4j/header.js.map +1 -0
- package/dist/src/formats/neo4j/importer.d.ts +73 -0
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
- package/dist/src/formats/neo4j/importer.js +932 -0
- package/dist/src/formats/neo4j/importer.js.map +1 -0
- package/dist/src/formats/neo4j/index.d.ts +79 -0
- package/dist/src/formats/neo4j/index.d.ts.map +1 -0
- package/dist/src/formats/neo4j/index.js +83 -0
- package/dist/src/formats/neo4j/index.js.map +1 -0
- package/dist/src/formats/pajek/exporter.d.ts +58 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
- package/dist/src/formats/pajek/exporter.js +825 -0
- package/dist/src/formats/pajek/exporter.js.map +1 -0
- package/dist/src/formats/pajek/importer.d.ts +88 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -0
- package/dist/src/formats/pajek/importer.js +1047 -0
- package/dist/src/formats/pajek/importer.js.map +1 -0
- package/dist/src/formats/pajek/index.d.ts +7 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -0
- package/dist/src/formats/pajek/index.js +7 -0
- package/dist/src/formats/pajek/index.js.map +1 -0
- package/dist/src/formats/pajek/syntax.d.ts +112 -0
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
- package/dist/src/formats/pajek/syntax.js +269 -0
- package/dist/src/formats/pajek/syntax.js.map +1 -0
- package/dist/src/index.d.ts +35 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/registry.d.ts +207 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +481 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/sniff.d.ts +104 -0
- package/dist/src/sniff.d.ts.map +1 -0
- package/dist/src/sniff.js +357 -0
- package/dist/src/sniff.js.map +1 -0
- package/dist/src/types.d.ts +238 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +29 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +122 -7
- package/src/children.ts +335 -0
- package/src/common/attributes.ts +520 -0
- package/src/common/codes.ts +153 -0
- package/src/common/declared-types.ts +374 -0
- package/src/common/direction.ts +518 -0
- package/src/common/escape.ts +231 -0
- package/src/common/export.ts +817 -0
- package/src/common/format.ts +111 -0
- package/src/common/ids.ts +176 -0
- package/src/common/input.ts +378 -0
- package/src/common/lists.ts +196 -0
- package/src/common/options.ts +377 -0
- package/src/common/report.ts +352 -0
- package/src/common/temporal.ts +302 -0
- package/src/common/text.ts +294 -0
- package/src/common/weights.ts +202 -0
- package/src/common/writer.ts +123 -0
- package/src/common/xml.ts +1053 -0
- package/src/formats/csv/exporter.ts +894 -0
- package/src/formats/csv/header.ts +172 -0
- package/src/formats/csv/importer.ts +1104 -0
- package/src/formats/csv/index.ts +88 -0
- package/src/formats/csv/records.ts +813 -0
- package/src/formats/csv/values.ts +224 -0
- package/src/formats/dot/exporter.ts +1014 -0
- package/src/formats/dot/importer.ts +1549 -0
- package/src/formats/dot/index.ts +7 -0
- package/src/formats/dot/names.ts +40 -0
- package/src/formats/dot/tokenizer.ts +384 -0
- package/src/formats/gexf/exporter.ts +1696 -0
- package/src/formats/gexf/importer.ts +2333 -0
- package/src/formats/gexf/index.ts +142 -0
- package/src/formats/gexf/schema.ts +361 -0
- package/src/formats/gml/exporter.ts +1404 -0
- package/src/formats/gml/importer.ts +1591 -0
- package/src/formats/gml/index.ts +128 -0
- package/src/formats/gml/syntax.ts +545 -0
- package/src/formats/graphml/constants.ts +225 -0
- package/src/formats/graphml/exporter.ts +1458 -0
- package/src/formats/graphml/importer.ts +2027 -0
- package/src/formats/graphml/index.ts +8 -0
- package/src/formats/graphml/tree.ts +318 -0
- package/src/formats/json/dialect.ts +317 -0
- package/src/formats/json/exporter.ts +1616 -0
- package/src/formats/json/importer.ts +2271 -0
- package/src/formats/json/index.ts +8 -0
- package/src/formats/neo4j/exporter.ts +1287 -0
- package/src/formats/neo4j/header.ts +156 -0
- package/src/formats/neo4j/importer.ts +1220 -0
- package/src/formats/neo4j/index.ts +116 -0
- package/src/formats/pajek/exporter.ts +1000 -0
- package/src/formats/pajek/importer.ts +1311 -0
- package/src/formats/pajek/index.ts +7 -0
- package/src/formats/pajek/syntax.ts +307 -0
- package/src/index.ts +244 -0
- package/src/registry.ts +617 -0
- package/src/sniff.ts +397 -0
- package/src/types.ts +262 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Id, label and value escaping for the text formats (design section 8.5), one function per
|
|
3
|
+
* syntax, with the inverse where the importer needs it: XML attribute and element text (GEXF,
|
|
4
|
+
* GraphML), GML quoted strings (`"` and `&` and non-ASCII as `&#NN;` character entities, the
|
|
5
|
+
* NetworkX convention), DOT ids (bare when the grammar allows, quoted with `\"` otherwise), RFC
|
|
6
|
+
* 4180 CSV cells and Pajek labels.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { GraphFormatError } from "@graphty/graph-format";
|
|
10
|
+
|
|
11
|
+
import { hasIllegalXmlChar } from "./xml.js";
|
|
12
|
+
|
|
13
|
+
const XML_TEXT_SPECIAL = /[&<>\r]/g;
|
|
14
|
+
const XML_ATTR_SPECIAL = /[&<>"\t\n\r]/g;
|
|
15
|
+
const XML_REPLACEMENTS: Readonly<Record<string, string>> = {
|
|
16
|
+
"&": "&",
|
|
17
|
+
"<": "<",
|
|
18
|
+
">": ">",
|
|
19
|
+
'"': """,
|
|
20
|
+
"\t": "	",
|
|
21
|
+
"\n": " ",
|
|
22
|
+
"\r": " ",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Escape text for an XML element body: `&`, `<`, `>` and a carriage return (which XML
|
|
27
|
+
* end-of-line handling would turn into a line feed on every read) as character references. A
|
|
28
|
+
* character XML 1.0 forbids (a C0 control other than tab / LF / CR, U+FFFE, U+FFFF, a lone
|
|
29
|
+
* surrogate) has no XML spelling at all and is E_COLUMN_TYPE; the exporters' check() counts them.
|
|
30
|
+
* @param text - the text
|
|
31
|
+
* @returns the escaped text
|
|
32
|
+
*/
|
|
33
|
+
export function escapeXmlText(text: string): string {
|
|
34
|
+
if (hasIllegalXmlChar(text)) {
|
|
35
|
+
throw illegalXml(text);
|
|
36
|
+
}
|
|
37
|
+
return text.replace(XML_TEXT_SPECIAL, (c) => XML_REPLACEMENTS[c]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Escape text for a double-quoted XML attribute value: `&`, `<`, `>`, `"` and the whitespace
|
|
42
|
+
* characters attribute normalisation would fold; a character XML 1.0 forbids is E_COLUMN_TYPE.
|
|
43
|
+
* @param text - the text
|
|
44
|
+
* @returns the escaped text
|
|
45
|
+
*/
|
|
46
|
+
export function escapeXmlAttribute(text: string): string {
|
|
47
|
+
if (hasIllegalXmlChar(text)) {
|
|
48
|
+
throw illegalXml(text);
|
|
49
|
+
}
|
|
50
|
+
return text.replace(XML_ATTR_SPECIAL, (c) => XML_REPLACEMENTS[c]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The error of a text no XML 1.0 document can carry.
|
|
55
|
+
* @param text - the text
|
|
56
|
+
* @returns the error
|
|
57
|
+
*/
|
|
58
|
+
function illegalXml(text: string): GraphFormatError {
|
|
59
|
+
return new GraphFormatError("E_COLUMN_TYPE", "the text holds a character XML 1.0 cannot carry", {
|
|
60
|
+
reason: "xml illegal char",
|
|
61
|
+
length: text.length,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const GML_SPECIAL = /["&]|[^ -~]/gu;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Quote a GML string value: double quotes around the text with `"`, `&` and every character
|
|
69
|
+
* outside printable ASCII written as `&#NN;` (NetworkX's writer convention, which its reader and
|
|
70
|
+
* Gephi's decode).
|
|
71
|
+
* A lone surrogate cannot be written and is E_COLUMN_TYPE.
|
|
72
|
+
* @param text - the text
|
|
73
|
+
* @returns the quoted GML string
|
|
74
|
+
*/
|
|
75
|
+
export function quoteGmlString(text: string): string {
|
|
76
|
+
const escaped = text.replace(GML_SPECIAL, (c) => {
|
|
77
|
+
const code = c.codePointAt(0);
|
|
78
|
+
if (code === undefined) {
|
|
79
|
+
return c;
|
|
80
|
+
}
|
|
81
|
+
if (code >= 0xd800 && code <= 0xdfff) {
|
|
82
|
+
throw new GraphFormatError("E_COLUMN_TYPE", "a lone surrogate cannot be written as a GML string", {
|
|
83
|
+
reason: "lone surrogate",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return `&#${code};`;
|
|
87
|
+
});
|
|
88
|
+
return `"${escaped}"`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const GML_ENTITY = /&(#[0-9]+|#x[0-9a-fA-F]+|amp|quot|lt|gt|apos);/g;
|
|
92
|
+
const GML_NAMED: Readonly<Record<string, string>> = { amp: "&", quot: '"', lt: "<", gt: ">", apos: "'" };
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Decode the character entities of a GML string body (the inverse of quoteGmlString on the text
|
|
96
|
+
* between the quotes). An unknown entity is left as written.
|
|
97
|
+
* @param body - the text between the quotes
|
|
98
|
+
* @returns the decoded text
|
|
99
|
+
*/
|
|
100
|
+
export function decodeGmlString(body: string): string {
|
|
101
|
+
return body.replace(GML_ENTITY, (whole, entity: string) => {
|
|
102
|
+
if (entity.startsWith("#x")) {
|
|
103
|
+
return String.fromCodePoint(Number.parseInt(entity.slice(2), 16));
|
|
104
|
+
}
|
|
105
|
+
if (entity.startsWith("#")) {
|
|
106
|
+
return String.fromCodePoint(Number.parseInt(entity.slice(1), 10));
|
|
107
|
+
}
|
|
108
|
+
return GML_NAMED[entity] ?? whole;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const DOT_NUMERAL = /^-?(\.[0-9]+|[0-9]+(\.[0-9]*)?)$/;
|
|
113
|
+
const DOT_KEYWORDS: ReadonlySet<string> = new Set(["node", "edge", "graph", "digraph", "subgraph", "strict"]);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Whether a text is a DOT ID that needs no quotes: an alphanumeric identifier not starting with
|
|
117
|
+
* a digit, or a numeral, and not a keyword (case-insensitive).
|
|
118
|
+
* @param text - the text
|
|
119
|
+
* @returns true when the text can be written bare
|
|
120
|
+
*/
|
|
121
|
+
export function isBareDotId(text: string): boolean {
|
|
122
|
+
if (DOT_KEYWORDS.has(text.toLowerCase())) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return isDotIdentifier(text) || DOT_NUMERAL.test(text);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Whether a text is a DOT identifier: a letter, underscore or any character of code 128 and
|
|
130
|
+
* above, followed by letters, digits, underscores or such characters.
|
|
131
|
+
* @param text - the text
|
|
132
|
+
* @returns true for a bare identifier
|
|
133
|
+
*/
|
|
134
|
+
function isDotIdentifier(text: string): boolean {
|
|
135
|
+
if (text.length === 0) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
for (let i = 0; i < text.length; i++) {
|
|
139
|
+
const c = text.charCodeAt(i);
|
|
140
|
+
const letter = (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95 || c >= 128;
|
|
141
|
+
const digit = c >= 48 && c <= 57;
|
|
142
|
+
if (!letter && (i === 0 || !digit)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Whether a text can be written as a DOT ID at all. Graphviz's scanner consumes a backslash pair
|
|
151
|
+
* `\\` as one unit and `\"` as an escaped quote, left to right, so a backslash that precedes a
|
|
152
|
+
* double quote or ends the text cannot be written: the written `\\"` reads as a pair and a closing
|
|
153
|
+
* quote, and a trailing backslash escapes the closing quote. Every other text is writable;
|
|
154
|
+
* quoteDotId() writes it.
|
|
155
|
+
* @param text - the id, name or value text
|
|
156
|
+
* @returns true when quoteDotId(text) reads back as `text`
|
|
157
|
+
*/
|
|
158
|
+
export function isWritableDotText(text: string): boolean {
|
|
159
|
+
return !text.endsWith("\\") && !text.includes('\\"');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Write a DOT ID: bare when the grammar allows, otherwise double-quoted with `"` escaped as `\"`.
|
|
164
|
+
* In DOT quoted strings the dyad `\"` is the only escape; every other character, a backslash and
|
|
165
|
+
* a raw line break included, is kept as written, so nothing else is rewritten. A text ending in a
|
|
166
|
+
* backslash has no DOT spelling (isWritableDotText); the caller refuses it before writing.
|
|
167
|
+
* @param text - the id or label text
|
|
168
|
+
* @returns the DOT ID
|
|
169
|
+
*/
|
|
170
|
+
export function quoteDotId(text: string): string {
|
|
171
|
+
if (isBareDotId(text)) {
|
|
172
|
+
return text;
|
|
173
|
+
}
|
|
174
|
+
return `"${text.replace(/"/g, '\\"')}"`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Write a set CSV cell per RFC 4180: quoted with `"` doubled when the text contains the
|
|
179
|
+
* delimiter, a quote, a CR or LF, or leading / trailing whitespace; bare otherwise. The empty
|
|
180
|
+
* string is written as the quoted empty cell `""`, so the importers can tell a set empty string
|
|
181
|
+
* (and an empty-string node id, legal under design section 4.1) from an unset cell, which every
|
|
182
|
+
* exporter writes as nothing between the delimiters.
|
|
183
|
+
* @param text - the cell text of a set cell
|
|
184
|
+
* @param delimiter - the field delimiter (default ",")
|
|
185
|
+
* @returns the cell as written
|
|
186
|
+
*/
|
|
187
|
+
export function quoteCsvCell(text: string, delimiter = ","): string {
|
|
188
|
+
if (text.length === 0) {
|
|
189
|
+
return '""';
|
|
190
|
+
}
|
|
191
|
+
if (
|
|
192
|
+
!text.includes(delimiter) &&
|
|
193
|
+
!text.includes('"') &&
|
|
194
|
+
!text.includes("\n") &&
|
|
195
|
+
!text.includes("\r") &&
|
|
196
|
+
text === text.trim()
|
|
197
|
+
) {
|
|
198
|
+
return text;
|
|
199
|
+
}
|
|
200
|
+
return `"${text.replace(/"/g, '""')}"`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Whether a text can be a Pajek label: Pajek has no escape mechanism inside its double-quoted
|
|
205
|
+
* labels, so a text containing a double quote, a CR or an LF cannot be written.
|
|
206
|
+
* @param text - the label text
|
|
207
|
+
* @returns true when quotePajekLabel() can write it
|
|
208
|
+
*/
|
|
209
|
+
export function isPajekLabel(text: string): boolean {
|
|
210
|
+
return !text.includes('"') && !text.includes("\n") && !text.includes("\r");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Write a Pajek label: bare when it is a single run of non-space, non-quote characters, otherwise
|
|
215
|
+
* double-quoted. E_UNSUPPORTED (reason "pajek label") for a text isPajekLabel() rejects; the
|
|
216
|
+
* exporter's check() counts those.
|
|
217
|
+
* @param text - the label text
|
|
218
|
+
* @returns the label as written
|
|
219
|
+
*/
|
|
220
|
+
export function quotePajekLabel(text: string): string {
|
|
221
|
+
if (!isPajekLabel(text)) {
|
|
222
|
+
throw new GraphFormatError("E_UNSUPPORTED", "a Pajek label cannot contain a double quote or a line break", {
|
|
223
|
+
reason: "pajek label",
|
|
224
|
+
value: text,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (text.length > 0 && !/[\s"]/.test(text)) {
|
|
228
|
+
return text;
|
|
229
|
+
}
|
|
230
|
+
return `"${text}"`;
|
|
231
|
+
}
|