links-notation 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,9 +36,12 @@ class Link {
36
36
  return value && typeof value.toLinkOrIdString === "function" ? value.toLinkOrIdString() : String(value);
37
37
  }
38
38
  static escapeReference(reference) {
39
- if (!reference || reference.trim() === "") {
39
+ if (reference === null || reference === undefined) {
40
40
  return "";
41
41
  }
42
+ if (reference === "") {
43
+ return '""';
44
+ }
42
45
  const hasSingleQuote = reference.includes("'");
43
46
  const hasDoubleQuote = reference.includes('"');
44
47
  const needsQuoting = reference.includes(":") || reference.includes("(") || reference.includes(")") || reference.includes(" ") || reference.includes("\t") || reference.includes(`
@@ -378,9 +381,9 @@ function peg$parse(input, options) {
378
381
  document: peg$parsedocument
379
382
  };
380
383
  let peg$startRuleFunction = peg$parsedocument;
381
- const peg$c0 = ":";
382
- const peg$c1 = "(";
383
- const peg$c2 = ")";
384
+ const peg$c0 = "(";
385
+ const peg$c1 = ")";
386
+ const peg$c2 = ":";
384
387
  const peg$c3 = '"';
385
388
  const peg$c4 = "'";
386
389
  const peg$c5 = "`";
@@ -392,9 +395,9 @@ function peg$parse(input, options) {
392
395
  const peg$e0 = peg$classExpectation([" ", "\t"], false, false, false);
393
396
  const peg$e1 = peg$classExpectation(["\r", `
394
397
  `], false, false, false);
395
- const peg$e2 = peg$literalExpectation(":", false);
396
- const peg$e3 = peg$literalExpectation("(", false);
397
- const peg$e4 = peg$literalExpectation(")", false);
398
+ const peg$e2 = peg$literalExpectation("(", false);
399
+ const peg$e3 = peg$literalExpectation(")", false);
400
+ const peg$e4 = peg$literalExpectation(":", false);
398
401
  const peg$e5 = peg$literalExpectation('"', false);
399
402
  const peg$e6 = peg$anyExpectation();
400
403
  const peg$e7 = peg$literalExpectation("'", false);
@@ -405,17 +408,13 @@ function peg$parse(input, options) {
405
408
  const peg$e11 = peg$classExpectation([" ", "\t", `
406
409
  `, "\r", "(", ":", ")"], true, false, false);
407
410
  function peg$f0() {
408
- indentationStack = [0];
409
- baseIndentation = null;
410
- return true;
411
+ return resetState();
411
412
  }
412
413
  function peg$f1(links) {
413
414
  return links;
414
415
  }
415
416
  function peg$f2() {
416
- indentationStack = [0];
417
- baseIndentation = null;
418
- return true;
417
+ return resetState();
419
418
  }
420
419
  function peg$f3() {
421
420
  return [];
@@ -431,7 +430,7 @@ function peg$parse(input, options) {
431
430
  return l;
432
431
  }
433
432
  function peg$f7(e, l) {
434
- return { id: e.id, values: e.values, children: l };
433
+ return Object.assign({}, e, { children: l });
435
434
  }
436
435
  function peg$f8(e) {
437
436
  return e;
@@ -457,27 +456,28 @@ function peg$parse(input, options) {
457
456
  function peg$f15(vl) {
458
457
  return vl;
459
458
  }
460
- function peg$f16(value) {
461
- return value;
459
+ function peg$f16(body) {
460
+ exitNestedContext();
461
+ return body;
462
462
  }
463
- function peg$f17(list) {
464
- return list;
463
+ function peg$f17(l) {
464
+ return { nested: l };
465
+ }
466
+ function peg$f18() {
467
+ return { nested: [] };
468
+ }
469
+ function peg$f19() {
470
+ return enterNestedContext();
465
471
  }
466
- function peg$f18(value) {
472
+ function peg$f20(value) {
467
473
  return value;
468
474
  }
469
- function peg$f19(list) {
475
+ function peg$f21(list) {
470
476
  return list;
471
477
  }
472
- function peg$f20(id, v) {
478
+ function peg$f22(id, v) {
473
479
  return { id, values: v };
474
480
  }
475
- function peg$f21(id, v) {
476
- return { id, values: v };
477
- }
478
- function peg$f22(v) {
479
- return { values: v };
480
- }
481
481
  function peg$f23(v) {
482
482
  return { values: v };
483
483
  }
@@ -565,6 +565,9 @@ function peg$parse(input, options) {
565
565
  function peg$f44(spaces) {
566
566
  return checkIndentation(spaces);
567
567
  }
568
+ function peg$f45() {
569
+ return isInsideNestedContext();
570
+ }
568
571
  let peg$currPos = options.peg$currPos | 0;
569
572
  let peg$savedPos = peg$currPos;
570
573
  const peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -924,7 +927,7 @@ function peg$parse(input, options) {
924
927
  function peg$parsereferenceOrLink() {
925
928
  let s0, s1;
926
929
  s0 = peg$currPos;
927
- s1 = peg$parsemultiLineAnyLink();
930
+ s1 = peg$parsenestedGroup();
928
931
  if (s1 !== peg$FAILED) {
929
932
  peg$savedPos = s0;
930
933
  s1 = peg$f9(s1);
@@ -944,7 +947,7 @@ function peg$parse(input, options) {
944
947
  function peg$parseanyLink() {
945
948
  let s0, s1, s2;
946
949
  s0 = peg$currPos;
947
- s1 = peg$parsemultiLineAnyLink();
950
+ s1 = peg$parsenestedGroup();
948
951
  if (s1 !== peg$FAILED) {
949
952
  s2 = peg$parseeol();
950
953
  if (s2 !== peg$FAILED) {
@@ -978,14 +981,6 @@ function peg$parse(input, options) {
978
981
  }
979
982
  return s0;
980
983
  }
981
- function peg$parsemultiLineAnyLink() {
982
- let s0;
983
- s0 = peg$parsemultiLineValueLink();
984
- if (s0 === peg$FAILED) {
985
- s0 = peg$parsemultiLineLink();
986
- }
987
- return s0;
988
- }
989
984
  function peg$parsesingleLineAnyLink() {
990
985
  let s0, s1, s2;
991
986
  s0 = peg$currPos;
@@ -1022,32 +1017,97 @@ function peg$parse(input, options) {
1022
1017
  }
1023
1018
  return s0;
1024
1019
  }
1025
- function peg$parsemultiLineValueAndWhitespace() {
1026
- let s0, s1, s2;
1020
+ function peg$parsenestedGroup() {
1021
+ let s0, s1, s2, s3;
1027
1022
  s0 = peg$currPos;
1028
- s1 = peg$parsereferenceOrLink();
1023
+ if (input.charCodeAt(peg$currPos) === 40) {
1024
+ s1 = peg$c0;
1025
+ peg$currPos++;
1026
+ } else {
1027
+ s1 = peg$FAILED;
1028
+ if (peg$silentFails === 0) {
1029
+ peg$fail(peg$e2);
1030
+ }
1031
+ }
1029
1032
  if (s1 !== peg$FAILED) {
1030
- s2 = peg$parse_();
1031
- peg$savedPos = s0;
1032
- s0 = peg$f16(s1);
1033
+ s2 = peg$parseENTER_NESTED_CONTEXT();
1034
+ if (s2 !== peg$FAILED) {
1035
+ s3 = peg$parsenestedGroupBody();
1036
+ if (s3 !== peg$FAILED) {
1037
+ peg$savedPos = s0;
1038
+ s0 = peg$f16(s3);
1039
+ } else {
1040
+ peg$currPos = s0;
1041
+ s0 = peg$FAILED;
1042
+ }
1043
+ } else {
1044
+ peg$currPos = s0;
1045
+ s0 = peg$FAILED;
1046
+ }
1033
1047
  } else {
1034
1048
  peg$currPos = s0;
1035
1049
  s0 = peg$FAILED;
1036
1050
  }
1037
1051
  return s0;
1038
1052
  }
1039
- function peg$parsemultiLineValues() {
1040
- let s0, s1, s2, s3;
1053
+ function peg$parsenestedGroupBody() {
1054
+ let s0, s1, s2, s3, s4;
1041
1055
  s0 = peg$currPos;
1042
- s1 = peg$parse_();
1043
- s2 = [];
1044
- s3 = peg$parsemultiLineValueAndWhitespace();
1045
- while (s3 !== peg$FAILED) {
1046
- s2.push(s3);
1047
- s3 = peg$parsemultiLineValueAndWhitespace();
1056
+ s1 = peg$parseskipEmptyLines();
1057
+ s2 = peg$parselinks();
1058
+ if (s2 !== peg$FAILED) {
1059
+ s3 = peg$parse_();
1060
+ if (input.charCodeAt(peg$currPos) === 41) {
1061
+ s4 = peg$c1;
1062
+ peg$currPos++;
1063
+ } else {
1064
+ s4 = peg$FAILED;
1065
+ if (peg$silentFails === 0) {
1066
+ peg$fail(peg$e3);
1067
+ }
1068
+ }
1069
+ if (s4 !== peg$FAILED) {
1070
+ peg$savedPos = s0;
1071
+ s0 = peg$f17(s2);
1072
+ } else {
1073
+ peg$currPos = s0;
1074
+ s0 = peg$FAILED;
1075
+ }
1076
+ } else {
1077
+ peg$currPos = s0;
1078
+ s0 = peg$FAILED;
1079
+ }
1080
+ if (s0 === peg$FAILED) {
1081
+ s0 = peg$currPos;
1082
+ s1 = peg$parse_();
1083
+ if (input.charCodeAt(peg$currPos) === 41) {
1084
+ s2 = peg$c1;
1085
+ peg$currPos++;
1086
+ } else {
1087
+ s2 = peg$FAILED;
1088
+ if (peg$silentFails === 0) {
1089
+ peg$fail(peg$e3);
1090
+ }
1091
+ }
1092
+ if (s2 !== peg$FAILED) {
1093
+ peg$savedPos = s0;
1094
+ s0 = peg$f18();
1095
+ } else {
1096
+ peg$currPos = s0;
1097
+ s0 = peg$FAILED;
1098
+ }
1099
+ }
1100
+ return s0;
1101
+ }
1102
+ function peg$parseENTER_NESTED_CONTEXT() {
1103
+ let s0;
1104
+ peg$savedPos = peg$currPos;
1105
+ s0 = peg$f19();
1106
+ if (s0) {
1107
+ s0 = undefined;
1108
+ } else {
1109
+ s0 = peg$FAILED;
1048
1110
  }
1049
- peg$savedPos = s0;
1050
- s0 = peg$f17(s2);
1051
1111
  return s0;
1052
1112
  }
1053
1113
  function peg$parsesingleLineValueAndWhitespace() {
@@ -1057,7 +1117,7 @@ function peg$parse(input, options) {
1057
1117
  s2 = peg$parsereferenceOrLink();
1058
1118
  if (s2 !== peg$FAILED) {
1059
1119
  peg$savedPos = s0;
1060
- s0 = peg$f18(s2);
1120
+ s0 = peg$f20(s2);
1061
1121
  } else {
1062
1122
  peg$currPos = s0;
1063
1123
  s0 = peg$FAILED;
@@ -1079,7 +1139,7 @@ function peg$parse(input, options) {
1079
1139
  }
1080
1140
  if (s1 !== peg$FAILED) {
1081
1141
  peg$savedPos = s0;
1082
- s1 = peg$f19(s1);
1142
+ s1 = peg$f21(s1);
1083
1143
  }
1084
1144
  s0 = s1;
1085
1145
  return s0;
@@ -1092,78 +1152,19 @@ function peg$parse(input, options) {
1092
1152
  if (s2 !== peg$FAILED) {
1093
1153
  s3 = peg$parse__();
1094
1154
  if (input.charCodeAt(peg$currPos) === 58) {
1095
- s4 = peg$c0;
1155
+ s4 = peg$c2;
1096
1156
  peg$currPos++;
1097
1157
  } else {
1098
1158
  s4 = peg$FAILED;
1099
1159
  if (peg$silentFails === 0) {
1100
- peg$fail(peg$e2);
1160
+ peg$fail(peg$e4);
1101
1161
  }
1102
1162
  }
1103
1163
  if (s4 !== peg$FAILED) {
1104
1164
  s5 = peg$parsesingleLineValues();
1105
1165
  if (s5 !== peg$FAILED) {
1106
1166
  peg$savedPos = s0;
1107
- s0 = peg$f20(s2, s5);
1108
- } else {
1109
- peg$currPos = s0;
1110
- s0 = peg$FAILED;
1111
- }
1112
- } else {
1113
- peg$currPos = s0;
1114
- s0 = peg$FAILED;
1115
- }
1116
- } else {
1117
- peg$currPos = s0;
1118
- s0 = peg$FAILED;
1119
- }
1120
- return s0;
1121
- }
1122
- function peg$parsemultiLineLink() {
1123
- let s0, s1, s2, s3, s4, s5, s6, s7, s8;
1124
- s0 = peg$currPos;
1125
- if (input.charCodeAt(peg$currPos) === 40) {
1126
- s1 = peg$c1;
1127
- peg$currPos++;
1128
- } else {
1129
- s1 = peg$FAILED;
1130
- if (peg$silentFails === 0) {
1131
- peg$fail(peg$e3);
1132
- }
1133
- }
1134
- if (s1 !== peg$FAILED) {
1135
- s2 = peg$parse_();
1136
- s3 = peg$parsereference();
1137
- if (s3 !== peg$FAILED) {
1138
- s4 = peg$parse_();
1139
- if (input.charCodeAt(peg$currPos) === 58) {
1140
- s5 = peg$c0;
1141
- peg$currPos++;
1142
- } else {
1143
- s5 = peg$FAILED;
1144
- if (peg$silentFails === 0) {
1145
- peg$fail(peg$e2);
1146
- }
1147
- }
1148
- if (s5 !== peg$FAILED) {
1149
- s6 = peg$parsemultiLineValues();
1150
- s7 = peg$parse_();
1151
- if (input.charCodeAt(peg$currPos) === 41) {
1152
- s8 = peg$c2;
1153
- peg$currPos++;
1154
- } else {
1155
- s8 = peg$FAILED;
1156
- if (peg$silentFails === 0) {
1157
- peg$fail(peg$e4);
1158
- }
1159
- }
1160
- if (s8 !== peg$FAILED) {
1161
- peg$savedPos = s0;
1162
- s0 = peg$f21(s3, s6);
1163
- } else {
1164
- peg$currPos = s0;
1165
- s0 = peg$FAILED;
1166
- }
1167
+ s0 = peg$f22(s2, s5);
1167
1168
  } else {
1168
1169
  peg$currPos = s0;
1169
1170
  s0 = peg$FAILED;
@@ -1184,48 +1185,11 @@ function peg$parse(input, options) {
1184
1185
  s1 = peg$parsesingleLineValues();
1185
1186
  if (s1 !== peg$FAILED) {
1186
1187
  peg$savedPos = s0;
1187
- s1 = peg$f22(s1);
1188
+ s1 = peg$f23(s1);
1188
1189
  }
1189
1190
  s0 = s1;
1190
1191
  return s0;
1191
1192
  }
1192
- function peg$parsemultiLineValueLink() {
1193
- let s0, s1, s2, s3, s4;
1194
- s0 = peg$currPos;
1195
- if (input.charCodeAt(peg$currPos) === 40) {
1196
- s1 = peg$c1;
1197
- peg$currPos++;
1198
- } else {
1199
- s1 = peg$FAILED;
1200
- if (peg$silentFails === 0) {
1201
- peg$fail(peg$e3);
1202
- }
1203
- }
1204
- if (s1 !== peg$FAILED) {
1205
- s2 = peg$parsemultiLineValues();
1206
- s3 = peg$parse_();
1207
- if (input.charCodeAt(peg$currPos) === 41) {
1208
- s4 = peg$c2;
1209
- peg$currPos++;
1210
- } else {
1211
- s4 = peg$FAILED;
1212
- if (peg$silentFails === 0) {
1213
- peg$fail(peg$e4);
1214
- }
1215
- }
1216
- if (s4 !== peg$FAILED) {
1217
- peg$savedPos = s0;
1218
- s0 = peg$f23(s2);
1219
- } else {
1220
- peg$currPos = s0;
1221
- s0 = peg$FAILED;
1222
- }
1223
- } else {
1224
- peg$currPos = s0;
1225
- s0 = peg$FAILED;
1226
- }
1227
- return s0;
1228
- }
1229
1193
  function peg$parseindentedIdLink() {
1230
1194
  let s0, s1, s2, s3, s4;
1231
1195
  s0 = peg$currPos;
@@ -1233,12 +1197,12 @@ function peg$parse(input, options) {
1233
1197
  if (s1 !== peg$FAILED) {
1234
1198
  s2 = peg$parse__();
1235
1199
  if (input.charCodeAt(peg$currPos) === 58) {
1236
- s3 = peg$c0;
1200
+ s3 = peg$c2;
1237
1201
  peg$currPos++;
1238
1202
  } else {
1239
1203
  s3 = peg$FAILED;
1240
1204
  if (peg$silentFails === 0) {
1241
- peg$fail(peg$e2);
1205
+ peg$fail(peg$e4);
1242
1206
  }
1243
1207
  }
1244
1208
  if (s3 !== peg$FAILED) {
@@ -1767,6 +1731,9 @@ function peg$parse(input, options) {
1767
1731
  }
1768
1732
  if (s2 === peg$FAILED) {
1769
1733
  s2 = peg$parseeof();
1734
+ if (s2 === peg$FAILED) {
1735
+ s2 = peg$parsenestedGroupEnd();
1736
+ }
1770
1737
  }
1771
1738
  if (s2 !== peg$FAILED) {
1772
1739
  s1 = [s1, s2];
@@ -1777,6 +1744,48 @@ function peg$parse(input, options) {
1777
1744
  }
1778
1745
  return s0;
1779
1746
  }
1747
+ function peg$parsenestedGroupEnd() {
1748
+ let s0, s1, s2, s3;
1749
+ s0 = peg$currPos;
1750
+ peg$savedPos = peg$currPos;
1751
+ s1 = peg$f45();
1752
+ if (s1) {
1753
+ s1 = undefined;
1754
+ } else {
1755
+ s1 = peg$FAILED;
1756
+ }
1757
+ if (s1 !== peg$FAILED) {
1758
+ s2 = peg$currPos;
1759
+ peg$silentFails++;
1760
+ if (input.charCodeAt(peg$currPos) === 41) {
1761
+ s3 = peg$c1;
1762
+ peg$currPos++;
1763
+ } else {
1764
+ s3 = peg$FAILED;
1765
+ if (peg$silentFails === 0) {
1766
+ peg$fail(peg$e3);
1767
+ }
1768
+ }
1769
+ peg$silentFails--;
1770
+ if (s3 !== peg$FAILED) {
1771
+ peg$currPos = s2;
1772
+ s2 = undefined;
1773
+ } else {
1774
+ s2 = peg$FAILED;
1775
+ }
1776
+ if (s2 !== peg$FAILED) {
1777
+ s1 = [s1, s2];
1778
+ s0 = s1;
1779
+ } else {
1780
+ peg$currPos = s0;
1781
+ s0 = peg$FAILED;
1782
+ }
1783
+ } else {
1784
+ peg$currPos = s0;
1785
+ s0 = peg$FAILED;
1786
+ }
1787
+ return s0;
1788
+ }
1780
1789
  function peg$parseeof() {
1781
1790
  let s0, s1;
1782
1791
  s0 = peg$currPos;
@@ -1863,6 +1872,30 @@ function peg$parse(input, options) {
1863
1872
  }
1864
1873
  let indentationStack = [0];
1865
1874
  let baseIndentation = null;
1875
+ let contextStack = [];
1876
+ function resetState() {
1877
+ indentationStack = [0];
1878
+ baseIndentation = null;
1879
+ contextStack = [];
1880
+ return true;
1881
+ }
1882
+ function enterNestedContext() {
1883
+ contextStack.push({ indentationStack, baseIndentation });
1884
+ indentationStack = [0];
1885
+ baseIndentation = null;
1886
+ return true;
1887
+ }
1888
+ function exitNestedContext() {
1889
+ const saved = contextStack.pop();
1890
+ if (saved) {
1891
+ indentationStack = saved.indentationStack;
1892
+ baseIndentation = saved.baseIndentation;
1893
+ }
1894
+ return true;
1895
+ }
1896
+ function isInsideNestedContext() {
1897
+ return contextStack.length > 0;
1898
+ }
1866
1899
  function setBaseIndentation(spaces) {
1867
1900
  if (baseIndentation === null) {
1868
1901
  baseIndentation = spaces.length;
@@ -1890,6 +1923,24 @@ function peg$parse(input, options) {
1890
1923
  function getCurrentIndentation() {
1891
1924
  return indentationStack[indentationStack.length - 1];
1892
1925
  }
1926
+ function isSubstantiveBody(content) {
1927
+ let depth = 0;
1928
+ let hasVisible = false;
1929
+ for (const c of content) {
1930
+ if (c === "(") {
1931
+ depth++;
1932
+ } else if (c === ")") {
1933
+ depth--;
1934
+ if (depth < 0) {
1935
+ return false;
1936
+ }
1937
+ }
1938
+ if (!/\s/.test(c)) {
1939
+ hasVisible = true;
1940
+ }
1941
+ }
1942
+ return hasVisible && depth === 0;
1943
+ }
1893
1944
  function parseQuotedStringAt(inputStr, startPos, quoteChar) {
1894
1945
  if (startPos >= inputStr.length || inputStr[startPos] !== quoteChar) {
1895
1946
  return null;
@@ -1900,6 +1951,8 @@ function peg$parse(input, options) {
1900
1951
  quoteCount++;
1901
1952
  pos++;
1902
1953
  }
1954
+ const isEvenRun = quoteCount % 2 === 0;
1955
+ const emptyReference = isEvenRun ? { value: "", length: quoteCount } : null;
1903
1956
  const closeSeq = quoteChar.repeat(quoteCount);
1904
1957
  const escapeSeq = quoteChar.repeat(quoteCount * 2);
1905
1958
  let content = "";
@@ -1912,6 +1965,9 @@ function peg$parse(input, options) {
1912
1965
  if (inputStr.substr(pos, quoteCount) === closeSeq) {
1913
1966
  const afterClose = pos + quoteCount;
1914
1967
  if (afterClose >= inputStr.length || inputStr[afterClose] !== quoteChar) {
1968
+ if (isEvenRun && !isSubstantiveBody(content)) {
1969
+ return emptyReference;
1970
+ }
1915
1971
  return {
1916
1972
  value: content,
1917
1973
  length: afterClose - startPos
@@ -1921,7 +1977,7 @@ function peg$parse(input, options) {
1921
1977
  content += inputStr[pos];
1922
1978
  pos++;
1923
1979
  }
1924
- return null;
1980
+ return emptyReference;
1925
1981
  }
1926
1982
  let parsedValue = null;
1927
1983
  let parsedLength = 0;
@@ -1988,7 +2044,7 @@ class Parser {
1988
2044
  if (item === null || item === undefined)
1989
2045
  return;
1990
2046
  if (item.children && item.children.length > 0) {
1991
- if (item.id && (!item.values || item.values.length === 0)) {
2047
+ if (item.id !== undefined && item.id !== null && (!item.values || item.values.length === 0)) {
1992
2048
  const childValues = item.children.map((child) => {
1993
2049
  if (child.values && child.values.length === 1) {
1994
2050
  return this.transformLink(child.values[0]);
@@ -2041,21 +2097,37 @@ class Parser {
2041
2097
  combined._isFromPathCombination = true;
2042
2098
  return combined;
2043
2099
  }
2100
+ transformNested(nested) {
2101
+ const nestedLinks = [];
2102
+ for (const item of nested) {
2103
+ if (item !== null && item !== undefined) {
2104
+ this.collectLinks(item, [], nestedLinks);
2105
+ }
2106
+ }
2107
+ const wrapsSingleGroup = nested.length === 1 && nested[0] && nested[0].nested !== undefined;
2108
+ if (nestedLinks.length === 1 && !wrapsSingleGroup) {
2109
+ return nestedLinks[0];
2110
+ }
2111
+ return new Link(null, nestedLinks);
2112
+ }
2044
2113
  transformLink(item) {
2045
2114
  if (item === null || item === undefined)
2046
2115
  return null;
2047
2116
  if (item instanceof Link) {
2048
2117
  return item;
2049
2118
  }
2119
+ if (item.nested !== undefined) {
2120
+ return this.transformNested(item.nested);
2121
+ }
2050
2122
  if (item.id !== undefined && !item.values && !item.children) {
2051
2123
  return new Link(item.id);
2052
2124
  }
2053
2125
  if (item.values && Array.isArray(item.values)) {
2054
- const link = new Link(item.id || null, []);
2126
+ const link = new Link(item.id ?? null, []);
2055
2127
  link.values = item.values.map((v) => this.transformLink(v));
2056
2128
  return link;
2057
2129
  }
2058
- return new Link(item.id || null, []);
2130
+ return new Link(item.id ?? null, []);
2059
2131
  }
2060
2132
  }
2061
2133
  // src/FormatOptions.js
@@ -2087,10 +2159,10 @@ class FormatOptions {
2087
2159
  class FormatConfig extends FormatOptions {
2088
2160
  }
2089
2161
  export {
2090
- formatLinks,
2091
- Parser,
2092
- LinksGroup,
2093
- Link,
2162
+ FormatConfig,
2094
2163
  FormatOptions,
2095
- FormatConfig
2164
+ Link,
2165
+ LinksGroup,
2166
+ Parser,
2167
+ formatLinks
2096
2168
  };
@@ -0,0 +1,34 @@
1
+ export default [
2
+ {
3
+ ignores: ['dist/', 'node_modules/', 'src/parser-generated.js'],
4
+ },
5
+ {
6
+ languageOptions: {
7
+ ecmaVersion: 'latest',
8
+ sourceType: 'module',
9
+ globals: {
10
+ // Node.js globals
11
+ process: 'readonly',
12
+ Buffer: 'readonly',
13
+ __dirname: 'readonly',
14
+ __filename: 'readonly',
15
+ global: 'readonly',
16
+ // Test framework globals
17
+ describe: 'readonly',
18
+ test: 'readonly',
19
+ it: 'readonly',
20
+ expect: 'readonly',
21
+ beforeEach: 'readonly',
22
+ afterEach: 'readonly',
23
+ beforeAll: 'readonly',
24
+ afterAll: 'readonly',
25
+ },
26
+ },
27
+ rules: {
28
+ 'no-unused-vars': 'warn',
29
+ 'no-undef': 'error',
30
+ semi: ['error', 'always'],
31
+ quotes: ['warn', 'single', { avoidEscape: true }],
32
+ },
33
+ },
34
+ ];
@@ -0,0 +1,43 @@
1
+ // A document written for an object with empty keys, the case that motivated
2
+ // https://github.com/link-foundation/links-notation/issues/288
3
+ //
4
+ // `{"": {"": 1}}` is written as `(o: ("" (o: ("" 1))))`. Every reference in it
5
+ // reads back as itself, so encoders that produce empty keys round trip.
6
+ //
7
+ // Usage: node js/examples/empty_reference.js
8
+
9
+ import { Parser, formatLinks } from '../src/index.js';
10
+
11
+ const parser = new Parser();
12
+
13
+ const source = '(o: ("" (o: ("" 1))))';
14
+
15
+ console.log('Source: ', source);
16
+
17
+ const links = parser.parse(source);
18
+
19
+ // Show every reference between angle brackets, so an empty one stays visible.
20
+ function render(node) {
21
+ if (!node.values || node.values.length === 0) {
22
+ return `<${node.id ?? ''}>`;
23
+ }
24
+ const head =
25
+ node.id === null || node.id === undefined ? '' : `<${node.id}>: `;
26
+ return `(${head}${node.values.map(render).join(' ')})`;
27
+ }
28
+
29
+ console.log('Parsed as: ', links.map(render).join('\n'));
30
+
31
+ const formatted = formatLinks(links);
32
+ console.log('Formatted: ', formatted);
33
+ console.log(
34
+ 'Round trips: ',
35
+ formatLinks(parser.parse(formatted)) === formatted
36
+ );
37
+
38
+ // A bare delimiter pair is one empty reference, and two in a row stay separate.
39
+ for (const example of ['(a "" b)', '(a "" "" b)', '(a " " b)', '(a ""x"" b)']) {
40
+ console.log(
41
+ `${example.padEnd(14)} => ${parser.parse(example).map(render).join(' ')}`
42
+ );
43
+ }