@orkestrel/markdown 0.0.5 → 0.0.6

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.
@@ -862,15 +862,6 @@ function scanInline(source, from, to, depth = 0) {
862
862
  const nodes = [];
863
863
  let index = from;
864
864
  let pending = "";
865
- const flush = () => {
866
- if (pending.length > 0) {
867
- nodes.push({
868
- element: "text",
869
- value: pending
870
- });
871
- pending = "";
872
- }
873
- };
874
865
  while (index < to) {
875
866
  const character = source[index] ?? "";
876
867
  if (character === "\\" && index + 1 < to && isEscapable(source[index + 1] ?? "")) {
@@ -878,40 +869,51 @@ function scanInline(source, from, to, depth = 0) {
878
869
  index += 2;
879
870
  continue;
880
871
  }
872
+ let scanned;
873
+ let end = index;
881
874
  if (character === "`") {
882
875
  const span = scanCode(source, index, to);
883
876
  if (span) {
884
- flush();
885
- nodes.push({
877
+ scanned = {
886
878
  element: "codeSpan",
887
879
  value: span.value
888
- });
889
- index = span.end;
890
- continue;
880
+ };
881
+ end = span.end;
891
882
  }
892
883
  }
893
884
  if (character === "[") {
894
885
  const link = scanLink(source, index, to, depth);
895
886
  if (link) {
896
- flush();
897
- nodes.push(link.node);
898
- index = link.end;
899
- continue;
887
+ scanned = link.node;
888
+ end = link.end;
900
889
  }
901
890
  }
902
891
  if (character === "*" || character === "_") {
903
892
  const emphasis = scanEmphasis(source, index, to, depth);
904
893
  if (emphasis) {
905
- flush();
906
- nodes.push(emphasis.node);
907
- index = emphasis.end;
908
- continue;
894
+ scanned = emphasis.node;
895
+ end = emphasis.end;
909
896
  }
910
897
  }
898
+ if (scanned !== void 0) {
899
+ if (pending.length > 0) {
900
+ nodes.push({
901
+ element: "text",
902
+ value: pending
903
+ });
904
+ pending = "";
905
+ }
906
+ nodes.push(scanned);
907
+ index = end;
908
+ continue;
909
+ }
911
910
  pending += character;
912
911
  index += 1;
913
912
  }
914
- flush();
913
+ if (pending.length > 0) nodes.push({
914
+ element: "text",
915
+ value: pending
916
+ });
915
917
  return nodes;
916
918
  }
917
919
  /**
@@ -970,10 +972,7 @@ function sanitizeUrl(href) {
970
972
  * @remarks
971
973
  * Total: never throws. At {@link MAX_DEPTH} a value-bearing node (`text` / `codeSpan`)
972
974
  * degrades to its escaped `value`; any other node degrades to `''` instead of
973
- * recursing further, so pathologically deep input cannot exhaust the call stack. The
974
- * recursive engine and its per-shape sub-steps (inline concatenation, table cell,
975
- * tight list-item) are nested inner functions - the only exported surface is
976
- * `renderHTML` itself.
975
+ * recursing further, so pathologically deep input cannot exhaust the call stack.
977
976
  *
978
977
  * @param node - The AST node to render (a full document, or any sub-node)
979
978
  * @returns The rendered, XSS-safe HTML string
@@ -987,47 +986,158 @@ function sanitizeUrl(href) {
987
986
  * ```
988
987
  */
989
988
  function renderHTML(node) {
990
- function render(current, depth) {
991
- if (depth >= 64) return "value" in current && typeof current.value === "string" ? escapeHtml(current.value) : "";
989
+ const stack = [{
990
+ node,
991
+ depth: 0,
992
+ expanded: false,
993
+ count: 0
994
+ }];
995
+ const values = [];
996
+ while (stack.length > 0) {
997
+ const frame = stack.pop();
998
+ if (frame === void 0) continue;
999
+ const current = frame.node;
1000
+ if (!frame.expanded) {
1001
+ if (frame.depth >= 64) {
1002
+ values.push("value" in current && typeof current.value === "string" ? escapeHtml(current.value) : "");
1003
+ continue;
1004
+ }
1005
+ const children = [];
1006
+ let depth = frame.depth + 1;
1007
+ switch (current.element) {
1008
+ case "document":
1009
+ case "heading":
1010
+ case "paragraph":
1011
+ case "blockquote":
1012
+ for (const child of current.children) if (child !== void 0) children.push(child);
1013
+ break;
1014
+ case "listItem": {
1015
+ const only = current.children[0];
1016
+ if (current.children.length === 1 && only !== void 0 && only.element === "paragraph") {
1017
+ for (const child of only.children) if (child !== void 0) children.push(child);
1018
+ } else for (const child of current.children) if (child !== void 0) children.push(child);
1019
+ break;
1020
+ }
1021
+ case "emphasis":
1022
+ case "link":
1023
+ for (const child of current.children) if (child !== void 0) children.push(child);
1024
+ depth += 1;
1025
+ break;
1026
+ case "list":
1027
+ for (const child of current.items) if (child !== void 0) children.push(child);
1028
+ break;
1029
+ case "table":
1030
+ for (const cell of current.header) if (cell !== void 0) {
1031
+ for (const child of cell) if (child !== void 0) children.push(child);
1032
+ }
1033
+ for (const row of current.rows) if (row !== void 0) {
1034
+ for (const cell of row) if (cell !== void 0) {
1035
+ for (const child of cell) if (child !== void 0) children.push(child);
1036
+ }
1037
+ }
1038
+ depth += 1;
1039
+ break;
1040
+ }
1041
+ stack.push({
1042
+ ...frame,
1043
+ expanded: true,
1044
+ count: children.length
1045
+ });
1046
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1047
+ const child = children[index];
1048
+ if (child !== void 0) stack.push({
1049
+ node: child,
1050
+ depth,
1051
+ expanded: false,
1052
+ count: 0
1053
+ });
1054
+ }
1055
+ continue;
1056
+ }
1057
+ const children = frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count);
1058
+ let value = "";
992
1059
  switch (current.element) {
993
- case "document": return current.children.map((child) => render(child, depth + 1)).join("\n");
994
- case "heading": return `<h${current.level}>${renderInline(current.children, depth)}</h${current.level}>`;
995
- case "paragraph": return `<p>${renderInline(current.children, depth)}</p>`;
996
- case "thematicBreak": return "<hr>";
997
- case "blockquote": return `<blockquote>\n${current.children.map((child) => render(child, depth + 1)).join("\n")}\n</blockquote>`;
998
- case "codeBlock": return `<pre>${current.lang === void 0 ? "<code>" : `<code class="language-${escapeHtml(current.lang)}">`}${escapeHtml(current.code)}</code></pre>`;
1060
+ case "document":
1061
+ value = children.join("\n");
1062
+ break;
1063
+ case "heading":
1064
+ value = `<h${current.level}>${children.join("")}</h${current.level}>`;
1065
+ break;
1066
+ case "paragraph":
1067
+ value = `<p>${children.join("")}</p>`;
1068
+ break;
1069
+ case "thematicBreak":
1070
+ value = "<hr>";
1071
+ break;
1072
+ case "blockquote":
1073
+ value = `<blockquote>\n${children.join("\n")}\n</blockquote>`;
1074
+ break;
1075
+ case "codeBlock":
1076
+ value = `<pre>${current.lang === void 0 ? "<code>" : `<code class="language-${escapeHtml(current.lang)}">`}${escapeHtml(current.code)}</code></pre>`;
1077
+ break;
999
1078
  case "list": {
1000
- const items = current.items.map((item) => render(item, depth + 1)).join("\n");
1001
- if (!current.ordered) return `<ul>\n${items}\n</ul>`;
1002
- return `<ol${current.start !== 1 ? ` start="${current.start}"` : ""}>\n${items}\n</ol>`;
1079
+ const items = children.join("\n");
1080
+ if (!current.ordered) {
1081
+ value = `<ul>\n${items}\n</ul>`;
1082
+ break;
1083
+ }
1084
+ value = `<ol${current.start !== 1 ? ` start="${current.start}"` : ""}>\n${items}\n</ol>`;
1085
+ break;
1003
1086
  }
1004
- case "listItem": return `<li>${renderItem(current.children, depth)}</li>`;
1087
+ case "listItem":
1088
+ value = `<li>${children.join(current.children.length === 1 && current.children[0]?.element === "paragraph" ? "" : "\n")}</li>`;
1089
+ break;
1005
1090
  case "table": {
1006
- const head = `<tr>${current.header.map((cell, column) => renderCell("th", cell, current.align[column], depth)).join("")}</tr>`;
1007
- const body = current.rows.map((row) => `<tr>${row.map((cell, column) => renderCell("td", cell, current.align[column], depth)).join("")}</tr>`).join("\n");
1008
- return `<table>\n<thead>\n${head}\n</thead>${isNonEmptyArray(current.rows) ? `\n<tbody>\n${body}\n</tbody>` : ""}\n</table>`;
1091
+ let offset = 0;
1092
+ const header = [];
1093
+ for (const [column, cell] of current.header.entries()) {
1094
+ if (cell === void 0) continue;
1095
+ const align = current.align[column];
1096
+ const style = align === "left" || align === "right" || align === "center" ? ` style="text-align:${align}"` : "";
1097
+ let count = 0;
1098
+ for (const child of cell) if (child !== void 0) count += 1;
1099
+ header.push(`<th${style}>${children.slice(offset, offset + count).join("")}</th>`);
1100
+ offset += count;
1101
+ }
1102
+ const rows = [];
1103
+ for (const row of current.rows) {
1104
+ const cells = [];
1105
+ for (const [column, cell] of row.entries()) {
1106
+ if (cell === void 0) continue;
1107
+ const align = current.align[column];
1108
+ const style = align === "left" || align === "right" || align === "center" ? ` style="text-align:${align}"` : "";
1109
+ let count = 0;
1110
+ for (const child of cell) if (child !== void 0) count += 1;
1111
+ cells.push(`<td${style}>${children.slice(offset, offset + count).join("")}</td>`);
1112
+ offset += count;
1113
+ }
1114
+ rows.push(`<tr>${cells.join("")}</tr>`);
1115
+ }
1116
+ const body = rows.join("\n");
1117
+ const bodyHtml = isNonEmptyArray(current.rows) ? `\n<tbody>\n${body}\n</tbody>` : "";
1118
+ value = `<table>\n<thead>\n<tr>${header.join("")}</tr>\n</thead>${bodyHtml}\n</table>`;
1119
+ break;
1009
1120
  }
1010
- case "text": return escapeHtml(current.value);
1011
- case "emphasis": return current.strong ? `<strong>${renderInline(current.children, depth + 1)}</strong>` : `<em>${renderInline(current.children, depth + 1)}</em>`;
1012
- case "codeSpan": return `<code>${escapeHtml(current.value)}</code>`;
1013
- case "link": return `<a href="${sanitizeUrl(current.href)}">${renderInline(current.children, depth + 1)}</a>`;
1014
- default: return "";
1015
- }
1016
- }
1017
- function renderInline(nodes, depth) {
1018
- return nodes.map((child) => render(child, depth + 1)).join("");
1019
- }
1020
- function renderCell(tag, cell, align, depth) {
1021
- return `<${tag}${align === "left" || align === "right" || align === "center" ? ` style="text-align:${align}"` : ""}>${renderInline(cell, depth + 1)}</${tag}>`;
1022
- }
1023
- function renderItem(children, depth) {
1024
- if (children.length === 1) {
1025
- const only = children[0];
1026
- if (only !== void 0 && only.element === "paragraph") return renderInline(only.children, depth);
1121
+ case "text":
1122
+ value = escapeHtml(current.value);
1123
+ break;
1124
+ case "emphasis":
1125
+ value = current.strong ? `<strong>${children.join("")}</strong>` : `<em>${children.join("")}</em>`;
1126
+ break;
1127
+ case "codeSpan":
1128
+ value = `<code>${escapeHtml(current.value)}</code>`;
1129
+ break;
1130
+ case "link":
1131
+ value = `<a href="${sanitizeUrl(current.href)}">${children.join("")}</a>`;
1132
+ break;
1133
+ default:
1134
+ value = "";
1135
+ break;
1027
1136
  }
1028
- return children.map((child) => render(child, depth + 1)).join("\n");
1137
+ if (stack.length === 0) return value;
1138
+ values.push(value);
1029
1139
  }
1030
- return render(node, 0);
1140
+ return "";
1031
1141
  }
1032
1142
  /**
1033
1143
  * Render a {@link MarkdownNode} to its CANONICAL markdown source - the inverse
@@ -1057,125 +1167,220 @@ function renderHTML(node) {
1057
1167
  * ```
1058
1168
  */
1059
1169
  function renderMarkdown(node) {
1060
- function escapeText(value) {
1061
- let out = "";
1062
- for (let index = 0; index < value.length; index += 1) {
1063
- const character = value[index] ?? "";
1064
- const atLineStart = index === 0 || value[index - 1] === "\n";
1065
- if (character === "\\" || character === "*" || character === "_" || character === "`" || character === "[" || character === "]") {
1066
- out += `\\${character}`;
1067
- continue;
1068
- }
1069
- if (atLineStart) {
1070
- if (character === "#" || character === ">") {
1071
- out += `\\${character}`;
1170
+ const stack = [{
1171
+ node,
1172
+ depth: 0,
1173
+ expanded: false,
1174
+ count: 0,
1175
+ escaped: ""
1176
+ }];
1177
+ const values = [];
1178
+ while (stack.length > 0) {
1179
+ const frame = stack.pop();
1180
+ if (frame === void 0) continue;
1181
+ const current = frame.node;
1182
+ if (!frame.expanded) {
1183
+ let escaped = "";
1184
+ if ((frame.depth >= 64 || current.element === "text") && "value" in current && typeof current.value === "string") for (let index = 0; index < current.value.length; index += 1) {
1185
+ const character = current.value[index] ?? "";
1186
+ const atLineStart = index === 0 || current.value[index - 1] === "\n";
1187
+ if (character === "\\" || character === "*" || character === "_" || character === "`" || character === "[" || character === "]") {
1188
+ escaped += `\\${character}`;
1072
1189
  continue;
1073
1190
  }
1074
- if ((character === "-" || character === "+") && (value[index + 1] ?? " ") === " ") {
1075
- out += `\\${character}`;
1076
- continue;
1077
- }
1078
- if (/[0-9]/.test(character)) {
1079
- let end = index;
1080
- while (end < value.length && /[0-9]/.test(value[end] ?? "")) end += 1;
1081
- const marker = value[end];
1082
- if ((marker === "." || marker === ")") && value[end + 1] === " ") {
1083
- out += `${value.slice(index, end)}\\${marker}`;
1084
- index = end;
1191
+ if (atLineStart) {
1192
+ if (character === "#" || character === ">") {
1193
+ escaped += `\\${character}`;
1194
+ continue;
1195
+ }
1196
+ if ((character === "-" || character === "+") && (current.value[index + 1] ?? " ") === " ") {
1197
+ escaped += `\\${character}`;
1085
1198
  continue;
1086
1199
  }
1200
+ if (/[0-9]/.test(character)) {
1201
+ let end = index;
1202
+ while (end < current.value.length && /[0-9]/.test(current.value[end] ?? "")) end += 1;
1203
+ const marker = current.value[end];
1204
+ if ((marker === "." || marker === ")") && current.value[end + 1] === " ") {
1205
+ escaped += `${current.value.slice(index, end)}\\${marker}`;
1206
+ index = end;
1207
+ continue;
1208
+ }
1209
+ }
1087
1210
  }
1211
+ escaped += character;
1212
+ }
1213
+ if (frame.depth >= 64) {
1214
+ values.push(escaped);
1215
+ continue;
1216
+ }
1217
+ const children = [];
1218
+ let depth = frame.depth + 1;
1219
+ switch (current.element) {
1220
+ case "document":
1221
+ case "heading":
1222
+ case "paragraph":
1223
+ case "blockquote":
1224
+ case "listItem":
1225
+ case "emphasis":
1226
+ case "link":
1227
+ for (const child of current.children) if (child !== void 0) children.push(child);
1228
+ break;
1229
+ case "list":
1230
+ for (const child of current.items) if (child !== void 0) children.push(child);
1231
+ break;
1232
+ case "table":
1233
+ for (const cell of current.header) if (cell !== void 0) {
1234
+ for (const child of cell) if (child !== void 0) children.push(child);
1235
+ }
1236
+ for (const row of current.rows) {
1237
+ if (row === void 0) continue;
1238
+ for (let column = 0; column < current.header.length; column += 1) {
1239
+ const cell = row[column];
1240
+ if (cell !== void 0) {
1241
+ for (const child of cell) if (child !== void 0) children.push(child);
1242
+ }
1243
+ }
1244
+ }
1245
+ depth += 1;
1246
+ break;
1247
+ }
1248
+ stack.push({
1249
+ ...frame,
1250
+ expanded: true,
1251
+ count: children.length,
1252
+ escaped
1253
+ });
1254
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1255
+ const child = children[index];
1256
+ if (child !== void 0) stack.push({
1257
+ node: child,
1258
+ depth,
1259
+ expanded: false,
1260
+ count: 0,
1261
+ escaped: ""
1262
+ });
1088
1263
  }
1089
- out += character;
1264
+ continue;
1090
1265
  }
1091
- return out;
1092
- }
1093
- function fenceFor(body, minimum) {
1094
- let longest = 0;
1095
- let run = 0;
1096
- for (const character of body) if (character === "`") {
1097
- run += 1;
1098
- longest = Math.max(longest, run);
1099
- } else run = 0;
1100
- return "`".repeat(Math.max(minimum, longest + 1));
1101
- }
1102
- function renderInline(nodes, depth) {
1103
- return nodes.map((child) => render(child, depth + 1)).join("");
1104
- }
1105
- function renderBlocks(blocks, depth) {
1106
- return blocks.map((block) => render(block, depth + 1)).join("\n\n");
1107
- }
1108
- function renderItem(item, marker, depth) {
1109
- const body = renderBlocks(item.children, depth + 1);
1110
- const pad = " ".repeat(marker.length);
1111
- return body.split("\n").map((line, index) => index === 0 ? marker + line : line === "" ? "" : pad + line).join("\n");
1112
- }
1113
- function renderCell(cell, depth) {
1114
- return renderInline(cell, depth + 1).replace(/\|/g, "\\|");
1115
- }
1116
- function renderTable(current, depth) {
1117
- const columns = current.header.length;
1118
- return [
1119
- `| ${current.header.map((cell) => renderCell(cell, depth)).join(" | ")} |`,
1120
- `| ${current.align.map((align) => {
1121
- if (align === "left") return ":--";
1122
- if (align === "right") return "--:";
1123
- if (align === "center") return ":-:";
1124
- return "---";
1125
- }).join(" | ")} |`,
1126
- ...current.rows.map((row) => {
1127
- const cells = [];
1128
- for (let column = 0; column < columns; column += 1) {
1129
- const cell = row[column];
1130
- cells.push(cell === void 0 ? "" : renderCell(cell, depth));
1131
- }
1132
- return `| ${cells.join(" | ")} |`;
1133
- })
1134
- ].join("\n");
1135
- }
1136
- function render(current, depth) {
1137
- if (depth >= 64) return "value" in current && typeof current.value === "string" ? escapeText(current.value) : "";
1266
+ const children = frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count);
1267
+ let value = "";
1138
1268
  switch (current.element) {
1139
- case "document": return renderBlocks(current.children, depth);
1269
+ case "codeBlock":
1270
+ case "codeSpan": {
1271
+ const body = current.element === "codeBlock" ? current.code : current.value;
1272
+ let longest = 0;
1273
+ let run = 0;
1274
+ for (const character of body) if (character === "`") {
1275
+ run += 1;
1276
+ longest = Math.max(longest, run);
1277
+ } else run = 0;
1278
+ const fence = "`".repeat(Math.max(current.element === "codeBlock" ? 3 : 1, longest + 1));
1279
+ if (current.element === "codeBlock") {
1280
+ value = `${fence}${current.lang === void 0 ? "" : current.lang}\n${current.code}\n${fence}`;
1281
+ break;
1282
+ }
1283
+ const pad = current.value.startsWith("`") || current.value.endsWith("`") ? " " : "";
1284
+ value = `${fence}${pad}${current.value}${pad}${fence}`;
1285
+ break;
1286
+ }
1287
+ case "document":
1288
+ value = children.join("\n\n");
1289
+ break;
1140
1290
  case "heading": {
1141
- const escaped = renderInline(current.children, depth).replace(/(^|[^\\])(#+)$/, (_match, pre, hashes) => {
1142
- return `${pre}\\${hashes[0] ?? ""}${hashes.slice(1)}`;
1291
+ const escaped = children.join("").replace(/(^|[^\\])(#+)$/, (_match, before, hashes) => {
1292
+ return `${before}\\${hashes[0] ?? ""}${hashes.slice(1)}`;
1143
1293
  });
1144
- return `${"#".repeat(current.level)} ${escaped}`;
1145
- }
1146
- case "paragraph": return renderInline(current.children, depth);
1147
- case "thematicBreak": return "---";
1148
- case "blockquote": return renderBlocks(current.children, depth).split("\n").map((line) => line === "" ? ">" : `> ${line}`).join("\n");
1149
- case "codeBlock": {
1150
- const fence = fenceFor(current.code, 3);
1151
- return `${fence}${current.lang === void 0 ? "" : current.lang}\n${current.code}\n${fence}`;
1294
+ value = `${"#".repeat(current.level)} ${escaped}`;
1295
+ break;
1152
1296
  }
1297
+ case "paragraph":
1298
+ value = children.join("");
1299
+ break;
1300
+ case "thematicBreak":
1301
+ value = "---";
1302
+ break;
1303
+ case "blockquote":
1304
+ value = children.join("\n\n").split("\n").map((line) => line === "" ? ">" : `> ${line}`).join("\n");
1305
+ break;
1153
1306
  case "list": {
1307
+ const items = [];
1154
1308
  let ordinal = current.start;
1155
- return current.items.map((item) => {
1156
- return renderItem(item, current.ordered ? `${ordinal++}. ` : "- ", depth);
1157
- }).join("\n");
1309
+ for (const body of children) {
1310
+ const marker = current.ordered ? `${ordinal}. ` : "- ";
1311
+ ordinal += 1;
1312
+ const pad = " ".repeat(marker.length);
1313
+ items.push(body.split("\n").map((line, index) => index === 0 ? marker + line : line === "" ? "" : pad + line).join("\n"));
1314
+ }
1315
+ value = items.join("\n");
1316
+ break;
1317
+ }
1318
+ case "listItem":
1319
+ value = children.join("\n\n");
1320
+ break;
1321
+ case "table": {
1322
+ let offset = 0;
1323
+ const header = [];
1324
+ for (const cell of current.header) {
1325
+ if (cell === void 0) {
1326
+ header.push("");
1327
+ continue;
1328
+ }
1329
+ let count = 0;
1330
+ for (const child of cell) if (child !== void 0) count += 1;
1331
+ header.push(children.slice(offset, offset + count).join("").replace(/\|/g, "\\|"));
1332
+ offset += count;
1333
+ }
1334
+ const delimiter = current.align.map((align) => {
1335
+ if (align === "left") return ":--";
1336
+ if (align === "right") return "--:";
1337
+ if (align === "center") return ":-:";
1338
+ return "---";
1339
+ });
1340
+ const rows = [];
1341
+ for (const row of current.rows) {
1342
+ const cells = [];
1343
+ for (let column = 0; column < current.header.length; column += 1) {
1344
+ const cell = row[column];
1345
+ if (cell === void 0) {
1346
+ cells.push("");
1347
+ continue;
1348
+ }
1349
+ let count = 0;
1350
+ for (const child of cell) if (child !== void 0) count += 1;
1351
+ cells.push(children.slice(offset, offset + count).join("").replace(/\|/g, "\\|"));
1352
+ offset += count;
1353
+ }
1354
+ rows.push(`| ${cells.join(" | ")} |`);
1355
+ }
1356
+ value = [
1357
+ `| ${header.join(" | ")} |`,
1358
+ `| ${delimiter.join(" | ")} |`,
1359
+ ...rows
1360
+ ].join("\n");
1361
+ break;
1158
1362
  }
1159
- case "listItem": return renderBlocks(current.children, depth);
1160
- case "table": return renderTable(current, depth);
1161
- case "text": return escapeText(current.value);
1363
+ case "text":
1364
+ value = frame.escaped;
1365
+ break;
1162
1366
  case "emphasis": {
1163
1367
  const marker = current.strong ? "**" : "*";
1164
- return `${marker}${renderInline(current.children, depth)}${marker}`;
1165
- }
1166
- case "codeSpan": {
1167
- const fence = fenceFor(current.value, 1);
1168
- const pad = current.value.startsWith("`") || current.value.endsWith("`") ? " " : "";
1169
- return `${fence}${pad}${current.value}${pad}${fence}`;
1368
+ value = `${marker}${children.join("")}${marker}`;
1369
+ break;
1170
1370
  }
1171
1371
  case "link": {
1172
1372
  const href = current.href.replace(/[\\()]/g, (character) => `\\${character}`);
1173
- return `[${renderInline(current.children, depth)}](${href})`;
1373
+ value = `[${children.join("")}](${href})`;
1374
+ break;
1174
1375
  }
1175
- default: return "";
1376
+ default:
1377
+ value = "";
1378
+ break;
1176
1379
  }
1380
+ if (stack.length === 0) return value;
1381
+ values.push(value);
1177
1382
  }
1178
- return render(node, 0);
1383
+ return "";
1179
1384
  }
1180
1385
  /**
1181
1386
  * Depth-first, pre-order, root-inclusive traversal of a {@link MarkdownNode} - yields
@@ -1197,10 +1402,17 @@ function renderMarkdown(node) {
1197
1402
  * ```
1198
1403
  */
1199
1404
  function* walkNodes(node) {
1200
- function* walk(current, depth) {
1201
- yield current;
1202
- if (depth >= 64) return;
1203
- switch (current.element) {
1405
+ const stack = [{
1406
+ node,
1407
+ depth: 0
1408
+ }];
1409
+ while (stack.length > 0) {
1410
+ const frame = stack.pop();
1411
+ if (frame === void 0) continue;
1412
+ yield frame.node;
1413
+ if (frame.depth >= 64) continue;
1414
+ const children = [];
1415
+ switch (frame.node.element) {
1204
1416
  case "document":
1205
1417
  case "heading":
1206
1418
  case "paragraph":
@@ -1208,19 +1420,30 @@ function* walkNodes(node) {
1208
1420
  case "listItem":
1209
1421
  case "emphasis":
1210
1422
  case "link":
1211
- for (const child of current.children) yield* walk(child, depth + 1);
1212
- return;
1423
+ for (const child of frame.node.children) if (child !== void 0) children.push(child);
1424
+ break;
1213
1425
  case "list":
1214
- for (const item of current.items) yield* walk(item, depth + 1);
1215
- return;
1426
+ for (const child of frame.node.items) if (child !== void 0) children.push(child);
1427
+ break;
1216
1428
  case "table":
1217
- for (const cell of current.header) for (const inline of cell) yield* walk(inline, depth + 1);
1218
- for (const row of current.rows) for (const cell of row) for (const inline of cell) yield* walk(inline, depth + 1);
1219
- return;
1220
- default: return;
1429
+ for (const cell of frame.node.header) if (cell !== void 0) {
1430
+ for (const child of cell) if (child !== void 0) children.push(child);
1431
+ }
1432
+ for (const row of frame.node.rows) if (row !== void 0) {
1433
+ for (const cell of row) if (cell !== void 0) {
1434
+ for (const child of cell) if (child !== void 0) children.push(child);
1435
+ }
1436
+ }
1437
+ break;
1438
+ }
1439
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1440
+ const child = children[index];
1441
+ if (child !== void 0) stack.push({
1442
+ node: child,
1443
+ depth: frame.depth + 1
1444
+ });
1221
1445
  }
1222
1446
  }
1223
- yield* walk(node, 0);
1224
1447
  }
1225
1448
  /**
1226
1449
  * Fold a {@link MarkdownNode} into a `T` via a total catamorphism - children are
@@ -1254,46 +1477,119 @@ function* walkNodes(node) {
1254
1477
  * ```
1255
1478
  */
1256
1479
  function foldNode(node, handlers, depth) {
1257
- function dispatch(current, children) {
1258
- switch (current.element) {
1259
- case "document": return handlers.document(current, children);
1260
- case "heading": return handlers.heading(current, children);
1261
- case "paragraph": return handlers.paragraph(current, children);
1262
- case "thematicBreak": return handlers.thematicBreak(current, children);
1263
- case "blockquote": return handlers.blockquote(current, children);
1264
- case "codeBlock": return handlers.codeBlock(current, children);
1265
- case "list": return handlers.list(current, children);
1266
- case "listItem": return handlers.listItem(current, children);
1267
- case "table": return handlers.table(current, children);
1268
- case "text": return handlers.text(current, children);
1269
- case "emphasis": return handlers.emphasis(current, children);
1270
- case "codeSpan": return handlers.codeSpan(current, children);
1271
- case "link": return handlers.link(current, children);
1480
+ const stack = [{
1481
+ node,
1482
+ depth,
1483
+ expanded: false,
1484
+ count: 0
1485
+ }];
1486
+ const values = [];
1487
+ while (stack.length > 0) {
1488
+ const frame = stack.pop();
1489
+ if (frame === void 0) continue;
1490
+ if (!frame.expanded) {
1491
+ const children = [];
1492
+ if (frame.depth < 64) switch (frame.node.element) {
1493
+ case "document":
1494
+ case "heading":
1495
+ case "paragraph":
1496
+ case "blockquote":
1497
+ case "listItem":
1498
+ case "emphasis":
1499
+ case "link":
1500
+ for (const child of frame.node.children) if (child !== void 0) children.push(child);
1501
+ break;
1502
+ case "list":
1503
+ for (const child of frame.node.items) if (child !== void 0) children.push(child);
1504
+ break;
1505
+ case "table":
1506
+ for (const cell of frame.node.header) if (cell !== void 0) {
1507
+ for (const child of cell) if (child !== void 0) children.push(child);
1508
+ }
1509
+ for (const row of frame.node.rows) if (row !== void 0) {
1510
+ for (const cell of row) if (cell !== void 0) {
1511
+ for (const child of cell) if (child !== void 0) children.push(child);
1512
+ }
1513
+ }
1514
+ break;
1515
+ }
1516
+ stack.push({
1517
+ ...frame,
1518
+ expanded: true,
1519
+ count: children.length
1520
+ });
1521
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1522
+ const child = children[index];
1523
+ if (child !== void 0) stack.push({
1524
+ node: child,
1525
+ depth: frame.depth + 1,
1526
+ expanded: false,
1527
+ count: 0
1528
+ });
1529
+ }
1530
+ continue;
1272
1531
  }
1273
- }
1274
- function childNodes(current) {
1275
- switch (current.element) {
1532
+ const children = frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count);
1533
+ let value;
1534
+ switch (frame.node.element) {
1276
1535
  case "document":
1536
+ value = handlers.document(frame.node, children);
1537
+ break;
1277
1538
  case "heading":
1539
+ value = handlers.heading(frame.node, children);
1540
+ break;
1278
1541
  case "paragraph":
1542
+ value = handlers.paragraph(frame.node, children);
1543
+ break;
1544
+ case "thematicBreak":
1545
+ value = handlers.thematicBreak(frame.node, children);
1546
+ break;
1279
1547
  case "blockquote":
1548
+ value = handlers.blockquote(frame.node, children);
1549
+ break;
1550
+ case "codeBlock":
1551
+ value = handlers.codeBlock(frame.node, children);
1552
+ break;
1553
+ case "list":
1554
+ value = handlers.list(frame.node, children);
1555
+ break;
1280
1556
  case "listItem":
1557
+ value = handlers.listItem(frame.node, children);
1558
+ break;
1559
+ case "table":
1560
+ value = handlers.table(frame.node, children);
1561
+ break;
1562
+ case "text":
1563
+ value = handlers.text(frame.node, children);
1564
+ break;
1281
1565
  case "emphasis":
1282
- case "link": return current.children;
1283
- case "list": return current.items;
1284
- case "table": {
1285
- const header = current.header.flatMap((cell) => cell);
1286
- const rows = current.rows.flatMap((row) => row.flatMap((cell) => cell));
1287
- return [...header, ...rows];
1288
- }
1289
- default: return [];
1566
+ value = handlers.emphasis(frame.node, children);
1567
+ break;
1568
+ case "codeSpan":
1569
+ value = handlers.codeSpan(frame.node, children);
1570
+ break;
1571
+ case "link":
1572
+ value = handlers.link(frame.node, children);
1573
+ break;
1290
1574
  }
1575
+ if (stack.length === 0) return value;
1576
+ values.push(value);
1291
1577
  }
1292
- function fold(current, level) {
1293
- if (level >= 64) return dispatch(current, []);
1294
- return dispatch(current, childNodes(current).map((child) => fold(child, level + 1)));
1578
+ switch (node.element) {
1579
+ case "document": return handlers.document(node, []);
1580
+ case "heading": return handlers.heading(node, []);
1581
+ case "paragraph": return handlers.paragraph(node, []);
1582
+ case "thematicBreak": return handlers.thematicBreak(node, []);
1583
+ case "blockquote": return handlers.blockquote(node, []);
1584
+ case "codeBlock": return handlers.codeBlock(node, []);
1585
+ case "list": return handlers.list(node, []);
1586
+ case "listItem": return handlers.listItem(node, []);
1587
+ case "table": return handlers.table(node, []);
1588
+ case "text": return handlers.text(node, []);
1589
+ case "emphasis": return handlers.emphasis(node, []);
1590
+ case "codeSpan": return handlers.codeSpan(node, []);
1591
+ case "link": return handlers.link(node, []);
1295
1592
  }
1296
- return fold(node, depth);
1297
1593
  }
1298
1594
  /**
1299
1595
  * Rewrite a {@link MarkdownDocument} bottom-up (copy-on-write) - each node's children
@@ -1328,71 +1624,227 @@ function foldNode(node, handlers, depth) {
1328
1624
  * ```
1329
1625
  */
1330
1626
  function rewriteDocument(document, rewrite) {
1331
- function rewriteInline(node, depth) {
1332
- if (depth >= 64) return node;
1333
- const rebuilt = rebuildInline(node, depth);
1334
- const result = rewrite(rebuilt);
1335
- return isInlineNode(result) ? result : rebuilt;
1336
- }
1337
- function rewriteBlock(node, depth) {
1338
- if (depth >= 64) return node;
1339
- const rebuilt = rebuildBlock(node, depth);
1340
- const result = rewrite(rebuilt);
1341
- return isBlockNode(result) ? result : rebuilt;
1342
- }
1343
- function rewriteItem(item, depth) {
1344
- if (depth >= 64) return item;
1345
- const rebuilt = {
1346
- element: "listItem",
1347
- children: item.children.map((child) => rewriteBlock(child, depth + 1))
1348
- };
1627
+ const stack = [{
1628
+ node: document,
1629
+ depth: -1,
1630
+ expanded: false,
1631
+ count: 0
1632
+ }];
1633
+ const values = [];
1634
+ while (stack.length > 0) {
1635
+ const frame = stack.pop();
1636
+ if (frame === void 0) continue;
1637
+ const current = frame.node;
1638
+ if (!frame.expanded) {
1639
+ if (current.element !== "document" && frame.depth >= 64) {
1640
+ values.push(current);
1641
+ continue;
1642
+ }
1643
+ const children = [];
1644
+ switch (current.element) {
1645
+ case "document":
1646
+ case "heading":
1647
+ case "paragraph":
1648
+ case "blockquote":
1649
+ case "listItem":
1650
+ case "emphasis":
1651
+ case "link":
1652
+ for (const child of current.children) if (child !== void 0) children.push(child);
1653
+ break;
1654
+ case "list":
1655
+ for (const child of current.items) if (child !== void 0) children.push(child);
1656
+ break;
1657
+ case "table":
1658
+ for (const cell of current.header) if (cell !== void 0) {
1659
+ for (const child of cell) if (child !== void 0) children.push(child);
1660
+ }
1661
+ for (const row of current.rows) if (row !== void 0) {
1662
+ for (const cell of row) if (cell !== void 0) {
1663
+ for (const child of cell) if (child !== void 0) children.push(child);
1664
+ }
1665
+ }
1666
+ break;
1667
+ }
1668
+ stack.push({
1669
+ ...frame,
1670
+ expanded: true,
1671
+ count: children.length
1672
+ });
1673
+ const depth = current.element === "document" ? 0 : frame.depth + 1;
1674
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1675
+ const child = children[index];
1676
+ if (child !== void 0) stack.push({
1677
+ node: child,
1678
+ depth,
1679
+ expanded: false,
1680
+ count: 0
1681
+ });
1682
+ }
1683
+ continue;
1684
+ }
1685
+ const children = frame.count === 0 ? [] : values.splice(values.length - frame.count, frame.count);
1686
+ let rebuilt = current;
1687
+ switch (current.element) {
1688
+ case "document": {
1689
+ const blocks = [];
1690
+ let offset = 0;
1691
+ for (const block of current.children) {
1692
+ if (block === void 0) continue;
1693
+ const child = children[offset];
1694
+ blocks.push(child !== void 0 && isBlockNode(child) ? child : block);
1695
+ offset += 1;
1696
+ }
1697
+ const result = {
1698
+ element: "document",
1699
+ children: blocks
1700
+ };
1701
+ if (stack.length === 0) return result;
1702
+ values.push(result);
1703
+ continue;
1704
+ }
1705
+ case "heading":
1706
+ case "paragraph": {
1707
+ const inlines = [];
1708
+ let offset = 0;
1709
+ for (const inline of current.children) {
1710
+ if (inline === void 0) continue;
1711
+ const child = children[offset];
1712
+ inlines.push(child !== void 0 && isInlineNode(child) ? child : inline);
1713
+ offset += 1;
1714
+ }
1715
+ rebuilt = {
1716
+ ...current,
1717
+ children: inlines
1718
+ };
1719
+ break;
1720
+ }
1721
+ case "blockquote": {
1722
+ const blocks = [];
1723
+ let offset = 0;
1724
+ for (const block of current.children) {
1725
+ if (block === void 0) continue;
1726
+ const child = children[offset];
1727
+ blocks.push(child !== void 0 && isBlockNode(child) ? child : block);
1728
+ offset += 1;
1729
+ }
1730
+ rebuilt = {
1731
+ ...current,
1732
+ children: blocks
1733
+ };
1734
+ break;
1735
+ }
1736
+ case "listItem": {
1737
+ const blocks = [];
1738
+ let offset = 0;
1739
+ for (const block of current.children) {
1740
+ if (block === void 0) continue;
1741
+ const child = children[offset];
1742
+ blocks.push(child !== void 0 && isBlockNode(child) ? child : block);
1743
+ offset += 1;
1744
+ }
1745
+ rebuilt = {
1746
+ element: "listItem",
1747
+ children: blocks
1748
+ };
1749
+ break;
1750
+ }
1751
+ case "emphasis":
1752
+ case "link": {
1753
+ const inlines = [];
1754
+ let offset = 0;
1755
+ for (const inline of current.children) {
1756
+ if (inline === void 0) continue;
1757
+ const child = children[offset];
1758
+ inlines.push(child !== void 0 && isInlineNode(child) ? child : inline);
1759
+ offset += 1;
1760
+ }
1761
+ rebuilt = {
1762
+ ...current,
1763
+ children: inlines
1764
+ };
1765
+ break;
1766
+ }
1767
+ case "list": {
1768
+ const items = [];
1769
+ let offset = 0;
1770
+ for (const item of current.items) {
1771
+ if (item === void 0) continue;
1772
+ const child = children[offset];
1773
+ items.push(child?.element === "listItem" ? child : item);
1774
+ offset += 1;
1775
+ }
1776
+ rebuilt = {
1777
+ ...current,
1778
+ items
1779
+ };
1780
+ break;
1781
+ }
1782
+ case "table": {
1783
+ let offset = 0;
1784
+ const header = [];
1785
+ for (const cell of current.header) {
1786
+ if (cell === void 0) continue;
1787
+ const inlines = [];
1788
+ for (const inline of cell) {
1789
+ if (inline === void 0) continue;
1790
+ const child = children[offset];
1791
+ inlines.push(child !== void 0 && isInlineNode(child) ? child : inline);
1792
+ offset += 1;
1793
+ }
1794
+ header.push(inlines);
1795
+ }
1796
+ const rows = [];
1797
+ for (const row of current.rows) {
1798
+ if (row === void 0) continue;
1799
+ const cells = [];
1800
+ for (const cell of row) {
1801
+ if (cell === void 0) continue;
1802
+ const inlines = [];
1803
+ for (const inline of cell) {
1804
+ if (inline === void 0) continue;
1805
+ const child = children[offset];
1806
+ inlines.push(child !== void 0 && isInlineNode(child) ? child : inline);
1807
+ offset += 1;
1808
+ }
1809
+ cells.push(inlines);
1810
+ }
1811
+ rows.push(cells);
1812
+ }
1813
+ rebuilt = {
1814
+ ...current,
1815
+ header,
1816
+ rows
1817
+ };
1818
+ break;
1819
+ }
1820
+ }
1349
1821
  const result = rewrite(rebuilt);
1350
- return result.element === "listItem" ? result : rebuilt;
1351
- }
1352
- function rebuildInline(node, depth) {
1353
- switch (node.element) {
1354
- case "emphasis": return {
1355
- ...node,
1356
- children: node.children.map((child) => rewriteInline(child, depth + 1))
1357
- };
1358
- case "link": return {
1359
- ...node,
1360
- children: node.children.map((child) => rewriteInline(child, depth + 1))
1361
- };
1822
+ let accepted = rebuilt;
1823
+ switch (current.element) {
1362
1824
  case "text":
1363
- case "codeSpan": return node;
1364
- }
1365
- }
1366
- function rebuildBlock(node, depth) {
1367
- switch (node.element) {
1368
- case "heading": return {
1369
- ...node,
1370
- children: node.children.map((child) => rewriteInline(child, depth + 1))
1371
- };
1372
- case "paragraph": return {
1373
- ...node,
1374
- children: node.children.map((child) => rewriteInline(child, depth + 1))
1375
- };
1376
- case "blockquote": return {
1377
- ...node,
1378
- children: node.children.map((child) => rewriteBlock(child, depth + 1))
1379
- };
1380
- case "list": return {
1381
- ...node,
1382
- items: node.items.map((item) => rewriteItem(item, depth + 1))
1383
- };
1384
- case "table": return {
1385
- ...node,
1386
- header: node.header.map((cell) => cell.map((inline) => rewriteInline(inline, depth + 1))),
1387
- rows: node.rows.map((row) => row.map((cell) => cell.map((inline) => rewriteInline(inline, depth + 1))))
1388
- };
1825
+ case "emphasis":
1826
+ case "codeSpan":
1827
+ case "link":
1828
+ if (isInlineNode(result)) accepted = result;
1829
+ break;
1830
+ case "heading":
1831
+ case "paragraph":
1832
+ case "list":
1833
+ case "table":
1389
1834
  case "codeBlock":
1390
- case "thematicBreak": return node;
1835
+ case "blockquote":
1836
+ case "thematicBreak":
1837
+ if (isBlockNode(result)) accepted = result;
1838
+ break;
1839
+ case "listItem":
1840
+ if (result.element === "listItem") accepted = result;
1841
+ break;
1391
1842
  }
1843
+ values.push(accepted);
1392
1844
  }
1393
1845
  return {
1394
1846
  element: "document",
1395
- children: document.children.map((child) => rewriteBlock(child, 0))
1847
+ children: [...document.children]
1396
1848
  };
1397
1849
  }
1398
1850
  /**
@@ -1417,26 +1869,55 @@ function rewriteDocument(document, rewrite) {
1417
1869
  * ```
1418
1870
  */
1419
1871
  function flattenText(node) {
1420
- function flatten(current, depth) {
1421
- if (depth >= 64) return "";
1422
- switch (current.element) {
1423
- case "text": return current.value;
1424
- case "codeSpan": return current.value;
1425
- case "codeBlock": return current.code;
1872
+ const stack = [{
1873
+ node,
1874
+ depth: 0
1875
+ }];
1876
+ let value = "";
1877
+ while (stack.length > 0) {
1878
+ const frame = stack.pop();
1879
+ if (frame === void 0 || frame.depth >= 64) continue;
1880
+ const children = [];
1881
+ switch (frame.node.element) {
1882
+ case "text":
1883
+ case "codeSpan":
1884
+ value += frame.node.value;
1885
+ break;
1886
+ case "codeBlock":
1887
+ value += frame.node.code;
1888
+ break;
1426
1889
  case "document":
1427
1890
  case "heading":
1428
1891
  case "paragraph":
1429
1892
  case "blockquote":
1430
1893
  case "listItem":
1431
1894
  case "emphasis":
1432
- case "link": return current.children.map((child) => flatten(child, depth + 1)).join("");
1433
- case "list": return current.items.map((item) => flatten(item, depth + 1)).join("");
1434
- case "table": return current.header.map((cell) => cell.map((inline) => flatten(inline, depth + 1)).join("")).join("") + current.rows.map((row) => row.map((cell) => cell.map((inline) => flatten(inline, depth + 1)).join("")).join("")).join("");
1435
- case "thematicBreak": return "";
1436
- default: return "";
1895
+ case "link":
1896
+ for (const child of frame.node.children) if (child !== void 0) children.push(child);
1897
+ break;
1898
+ case "list":
1899
+ for (const child of frame.node.items) if (child !== void 0) children.push(child);
1900
+ break;
1901
+ case "table":
1902
+ for (const cell of frame.node.header) if (cell !== void 0) {
1903
+ for (const child of cell) if (child !== void 0) children.push(child);
1904
+ }
1905
+ for (const row of frame.node.rows) if (row !== void 0) {
1906
+ for (const cell of row) if (cell !== void 0) {
1907
+ for (const child of cell) if (child !== void 0) children.push(child);
1908
+ }
1909
+ }
1910
+ break;
1911
+ }
1912
+ for (let index = children.length - 1; index >= 0; index -= 1) {
1913
+ const child = children[index];
1914
+ if (child !== void 0) stack.push({
1915
+ node: child,
1916
+ depth: frame.depth + 1
1917
+ });
1437
1918
  }
1438
1919
  }
1439
- return flatten(node, 0);
1920
+ return value;
1440
1921
  }
1441
1922
  //#endregion
1442
1923
  //#region src/core/parsers.ts
@@ -1595,6 +2076,51 @@ function collectList(lines, start, depth) {
1595
2076
  const startOrdinal = first?.start ?? 1;
1596
2077
  const topIndent = first?.indent ?? 0;
1597
2078
  const items = [];
2079
+ const chain = [];
2080
+ let nested = true;
2081
+ for (let cursor = start; cursor < lines.length; cursor += 1) {
2082
+ const parsed = extractListItem(lines[cursor] ?? "");
2083
+ const previous = chain[chain.length - 1];
2084
+ if (parsed === void 0 || previous !== void 0 && (previous.content.length > 0 || parsed.indent !== previous.marker)) {
2085
+ nested = false;
2086
+ break;
2087
+ }
2088
+ chain.push(parsed);
2089
+ }
2090
+ const remaining = 64 - depth;
2091
+ if (nested && remaining > 0 && chain.length > remaining) {
2092
+ const terminal = chain[remaining - 1];
2093
+ if (terminal !== void 0) {
2094
+ const source = [terminal.content];
2095
+ for (let cursor = start + remaining; cursor < lines.length; cursor += 1) source.push((lines[cursor] ?? "").slice(terminal.marker));
2096
+ let children = [{
2097
+ element: "paragraph",
2098
+ children: [{
2099
+ element: "text",
2100
+ value: source.join("\n")
2101
+ }]
2102
+ }];
2103
+ let node;
2104
+ for (let cursor = remaining - 1; cursor >= 0; cursor -= 1) {
2105
+ const parsed = chain[cursor];
2106
+ if (parsed === void 0) continue;
2107
+ node = {
2108
+ element: "list",
2109
+ ordered: parsed.ordered,
2110
+ start: parsed.start,
2111
+ items: [{
2112
+ element: "listItem",
2113
+ children
2114
+ }]
2115
+ };
2116
+ children = [node];
2117
+ }
2118
+ if (node !== void 0) return {
2119
+ node,
2120
+ next: lines.length
2121
+ };
2122
+ }
2123
+ }
1598
2124
  let index = start;
1599
2125
  while (index < lines.length) {
1600
2126
  const parsed = extractListItem(lines[index] ?? "");
@@ -1877,7 +2403,12 @@ var Markdown = class Markdown {
1877
2403
  let index = 0;
1878
2404
  return new ReadableStream({ pull(controller) {
1879
2405
  if (index < blocks.length) {
1880
- controller.enqueue(blocks[index]);
2406
+ const block = blocks[index];
2407
+ if (block === void 0) {
2408
+ controller.close();
2409
+ return;
2410
+ }
2411
+ controller.enqueue(block);
1881
2412
  index += 1;
1882
2413
  } else controller.close();
1883
2414
  } });