stream-markdown-parser 0.0.42 → 0.0.44

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
@@ -601,7 +601,7 @@ var require_markdown_it_task_checkbox = /* @__PURE__ */ __commonJS({ "../../node
601
601
  }) });
602
602
 
603
603
  //#endregion
604
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.2/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
604
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.3/node_modules/markdown-it-ts/dist/chunk-Bp6m_JJh.js
605
605
  var import_markdown_it_task_checkbox = /* @__PURE__ */ __toESM(require_markdown_it_task_checkbox(), 1);
606
606
  var __defProp = Object.defineProperty;
607
607
  var __export = (all) => {
@@ -2187,7 +2187,7 @@ var require_punycode = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/pu
2187
2187
  }) });
2188
2188
 
2189
2189
  //#endregion
2190
- //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.2/node_modules/markdown-it-ts/dist/index.js
2190
+ //#region ../../node_modules/.pnpm/markdown-it-ts@0.0.3/node_modules/markdown-it-ts/dist/index.js
2191
2191
  var import_punycode = /* @__PURE__ */ __toESM(require_punycode(), 1);
2192
2192
  var utils_exports = /* @__PURE__ */ __export({
2193
2193
  arrayReplaceAt: () => arrayReplaceAt,
@@ -5302,35 +5302,20 @@ var StateInline = class {
5302
5302
  const { src, posMax } = this;
5303
5303
  const marker = src.charCodeAt(start);
5304
5304
  let pos = start;
5305
- let count = 0;
5306
- while (pos < posMax && src.charCodeAt(pos) === marker) {
5307
- count++;
5308
- pos++;
5309
- }
5305
+ while (pos < posMax && src.charCodeAt(pos) === marker) pos++;
5306
+ const count = pos - start;
5310
5307
  if (count < 1) return null;
5311
5308
  const lastChar = start > 0 ? src.charCodeAt(start - 1) : 32;
5312
5309
  const nextChar = pos < posMax ? src.charCodeAt(pos) : 32;
5313
- const isLastPunctChar = lastChar === 32 || lastChar === 10;
5314
- const isNextPunctChar = nextChar === 32 || nextChar === 10;
5315
- const isLastWhiteSpace = lastChar === 32;
5316
- const isNextWhiteSpace = nextChar === 32;
5317
- let can_open = true;
5318
- let can_close = true;
5319
- if (isNextWhiteSpace) can_open = false;
5320
- else if (isNextPunctChar) {
5321
- if (!(isLastWhiteSpace || isLastPunctChar)) can_open = false;
5322
- }
5323
- if (isLastWhiteSpace) can_close = false;
5324
- else if (isLastPunctChar) {
5325
- if (!(isNextWhiteSpace || isNextPunctChar)) can_close = false;
5326
- }
5327
- if (!canSplitWord) {
5328
- can_open = can_open && (!isLastPunctChar || isLastWhiteSpace);
5329
- can_close = can_close && (!isNextPunctChar || isNextWhiteSpace);
5330
- }
5310
+ const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
5311
+ const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
5312
+ const isLastWhiteSpace = isWhiteSpace(lastChar);
5313
+ const isNextWhiteSpace = isWhiteSpace(nextChar);
5314
+ const left_flanking = !isNextWhiteSpace && (!isNextPunctChar || isLastWhiteSpace || isLastPunctChar);
5315
+ const right_flanking = !isLastWhiteSpace && (!isLastPunctChar || isNextWhiteSpace || isNextPunctChar);
5331
5316
  return {
5332
- can_open,
5333
- can_close,
5317
+ can_open: left_flanking && (canSplitWord || !right_flanking || isLastPunctChar),
5318
+ can_close: right_flanking && (canSplitWord || !left_flanking || isNextPunctChar),
5334
5319
  length: count
5335
5320
  };
5336
5321
  }
@@ -6834,6 +6819,7 @@ const BASE_COMMON_HTML_TAGS = new Set([
6834
6819
  "del",
6835
6820
  "dfn",
6836
6821
  "em",
6822
+ "font",
6837
6823
  "i",
6838
6824
  "img",
6839
6825
  "input",
@@ -6889,7 +6875,7 @@ const BASE_COMMON_HTML_TAGS = new Set([
6889
6875
  const OPEN_TAG_RE = /<([A-Z][\w-]*)(?=[\s/>]|$)/gi;
6890
6876
  const CLOSE_TAG_RE = /<\/\s*([A-Z][\w-]*)(?=[\s/>]|$)/gi;
6891
6877
  const TAG_NAME_AT_START_RE = /^<\s*(?:\/\s*)?([A-Z][\w-]*)/i;
6892
- function findTagCloseIndexOutsideQuotes(html) {
6878
+ function findTagCloseIndexOutsideQuotes$1(html) {
6893
6879
  let inSingle = false;
6894
6880
  let inDouble = false;
6895
6881
  for (let i = 0; i < html.length; i++) {
@@ -6932,8 +6918,8 @@ function findFirstIncompleteTag(content, tagSet) {
6932
6918
  const idx = m.index ?? -1;
6933
6919
  if (idx < 0) continue;
6934
6920
  const tag = (m[1] ?? "").toLowerCase();
6935
- if (!tagSet.has(tag)) continue;
6936
- if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
6921
+ if (!isCommonHtmlTagOrPrefix(tag, tagSet)) continue;
6922
+ if (findTagCloseIndexOutsideQuotes$1(content.slice(idx)) !== -1) continue;
6937
6923
  if (!first || idx < first.index) first = {
6938
6924
  index: idx,
6939
6925
  tag,
@@ -6945,7 +6931,7 @@ function findFirstIncompleteTag(content, tagSet) {
6945
6931
  if (idx < 0) continue;
6946
6932
  const tag = (m[1] ?? "").toLowerCase();
6947
6933
  if (!isCommonHtmlTagOrPrefix(tag, tagSet)) continue;
6948
- if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
6934
+ if (findTagCloseIndexOutsideQuotes$1(content.slice(idx)) !== -1) continue;
6949
6935
  if (!first || idx < first.index) first = {
6950
6936
  index: idx,
6951
6937
  tag,
@@ -7011,7 +6997,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
7011
6997
  cursor = lt + 1;
7012
6998
  continue;
7013
6999
  }
7014
- const closeIdx = findTagCloseIndexOutsideQuotes(sub);
7000
+ const closeIdx = findTagCloseIndexOutsideQuotes$1(sub);
7015
7001
  if (closeIdx === -1) {
7016
7002
  pushTextPart("<", baseToken);
7017
7003
  cursor = lt + 1;
@@ -7049,7 +7035,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
7049
7035
  if (pending) {
7050
7036
  pending.buffer += tokenToRaw$1(child);
7051
7037
  pendingAtEnd = pending.buffer;
7052
- const closeIdx = findTagCloseIndexOutsideQuotes(pending.buffer);
7038
+ const closeIdx = findTagCloseIndexOutsideQuotes$1(pending.buffer);
7053
7039
  if (closeIdx === -1) continue;
7054
7040
  const tagChunk = pending.buffer.slice(0, closeIdx + 1);
7055
7041
  const afterChunk = pending.buffer.slice(closeIdx + 1);
@@ -7067,7 +7053,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
7067
7053
  if (child.type === "html_inline") {
7068
7054
  const content = tokenToRaw$1(child);
7069
7055
  const tagName = (content.match(TAG_NAME_AT_START_RE)?.[1] ?? "").toLowerCase();
7070
- if (tagName && tagSet.has(tagName) && findTagCloseIndexOutsideQuotes(content) === -1) {
7056
+ if (tagName && tagSet.has(tagName) && findTagCloseIndexOutsideQuotes$1(content) === -1) {
7071
7057
  pending = {
7072
7058
  tag: tagName,
7073
7059
  buffer: content,
@@ -7147,10 +7133,69 @@ function applyFixHtmlInlineTokens(md, options = {}) {
7147
7133
  const tagStack = [];
7148
7134
  for (let i = 0; i < toks.length; i++) {
7149
7135
  const t = toks[i];
7136
+ if (tagStack.length > 0) {
7137
+ const [openTag, openIndex] = tagStack[tagStack.length - 1];
7138
+ if (i !== openIndex) {
7139
+ if (t.type === "paragraph_open" || t.type === "paragraph_close") {
7140
+ toks.splice(i, 1);
7141
+ i--;
7142
+ continue;
7143
+ }
7144
+ const chunk = String(t.content ?? t.raw ?? "");
7145
+ const closeRe = new RegExp(`<\\s*\\/\\s*${openTag}\\s*>`, "i");
7146
+ const closeMatch = chunk ? closeRe.exec(chunk) : null;
7147
+ const isClosingTag$1 = !!closeMatch;
7148
+ if (chunk) {
7149
+ const openToken = toks[openIndex];
7150
+ if (closeMatch && typeof closeMatch.index === "number") {
7151
+ const end = closeMatch.index + String(closeMatch[0] ?? "").length;
7152
+ const before = chunk.slice(0, end);
7153
+ const after = chunk.slice(end);
7154
+ openToken.content = `${String(openToken.content || "")}\n${before}`;
7155
+ openToken.loading = false;
7156
+ const afterTrimmed = after.replace(/^\s+/, "");
7157
+ toks.splice(i, 1);
7158
+ tagStack.pop();
7159
+ if (afterTrimmed) toks.splice(i, 0, afterTrimmed.startsWith("<") ? {
7160
+ type: "html_block",
7161
+ content: afterTrimmed
7162
+ } : {
7163
+ type: "inline",
7164
+ content: afterTrimmed,
7165
+ children: [{
7166
+ type: "text",
7167
+ content: afterTrimmed,
7168
+ raw: afterTrimmed
7169
+ }]
7170
+ });
7171
+ i--;
7172
+ continue;
7173
+ }
7174
+ openToken.content = `${String(openToken.content || "")}\n${chunk}`;
7175
+ if (openToken.loading !== false) openToken.loading = !isClosingTag$1;
7176
+ }
7177
+ toks.splice(i, 1);
7178
+ i--;
7179
+ if (isClosingTag$1) tagStack.pop();
7180
+ continue;
7181
+ }
7182
+ }
7150
7183
  if (t.type === "html_block") {
7151
- const tag = (t.content?.match(/<([^\s>/]+)/)?.[1] ?? "").toLowerCase();
7152
- if (!/<\s*\/\s*[^\s>]+\s*>/.test(t.content || "")) tagStack.push([tag, i]);
7153
- else if (tagStack.length > 0 && tagStack[tagStack.length - 1][0] === tag) tagStack.pop();
7184
+ const rawContent = String(t.content || "");
7185
+ const tag = (rawContent.match(/<\s*(?:\/\s*)?([^\s>/]+)/)?.[1] ?? "").toLowerCase();
7186
+ if (!/^\s*<\s*\//.test(rawContent)) {
7187
+ if (tag) {
7188
+ if (!new RegExp(`<\\s*\\/\\s*${tag}\\s*>`, "i").test(rawContent)) tagStack.push([tag, i]);
7189
+ }
7190
+ } else if (tagStack.length > 0 && tag && tagStack[tagStack.length - 1][0] === tag) {
7191
+ const [, openIndex] = tagStack[tagStack.length - 1];
7192
+ const openToken = toks[openIndex];
7193
+ openToken.content = `${String(openToken.content || "")}\n${rawContent}`;
7194
+ openToken.loading = false;
7195
+ tagStack.pop();
7196
+ toks.splice(i, 1);
7197
+ i--;
7198
+ }
7154
7199
  continue;
7155
7200
  } else if (tagStack.length > 0) {
7156
7201
  if (t.type === "paragraph_open" || t.type === "paragraph_close") {
@@ -7171,10 +7216,153 @@ function applyFixHtmlInlineTokens(md, options = {}) {
7171
7216
  i--;
7172
7217
  } else continue;
7173
7218
  }
7219
+ if (customTagSet.size > 0) {
7220
+ const openReCache = /* @__PURE__ */ new Map();
7221
+ const closeReCache = /* @__PURE__ */ new Map();
7222
+ const getOpenRe = (tag) => {
7223
+ let r = openReCache.get(tag);
7224
+ if (!r) {
7225
+ r = new RegExp(`<\\s*${tag}\\b`, "i");
7226
+ openReCache.set(tag, r);
7227
+ }
7228
+ return r;
7229
+ };
7230
+ const getCloseRe = (tag) => {
7231
+ let r = closeReCache.get(tag);
7232
+ if (!r) {
7233
+ r = new RegExp(`<\\s*\\/\\s*${tag}\\s*>`, "i");
7234
+ closeReCache.set(tag, r);
7235
+ }
7236
+ return r;
7237
+ };
7238
+ const stack = [];
7239
+ for (let i = 0; i < toks.length; i++) {
7240
+ const tok = toks[i];
7241
+ const content = String(tok.content ?? "");
7242
+ if (stack.length > 0) {
7243
+ const top = stack[stack.length - 1];
7244
+ const openTok = toks[top.index];
7245
+ if (tok.type === "html_block" && getCloseRe(top.tag).test(content)) {
7246
+ openTok.content = `${String(openTok.content ?? "")}\n${content}`;
7247
+ if (Array.isArray(openTok.children)) openTok.children.push({
7248
+ type: "html_inline",
7249
+ content: `</${top.tag}>`,
7250
+ raw: `</${top.tag}>`
7251
+ });
7252
+ toks.splice(i, 1);
7253
+ i--;
7254
+ stack.pop();
7255
+ continue;
7256
+ }
7257
+ if (tok.type !== "inline") continue;
7258
+ const children = Array.isArray(tok.children) ? tok.children : [];
7259
+ const closeChildIndex = children.findIndex((c) => {
7260
+ if (!c || c.type !== "html_inline") return false;
7261
+ const cContent = String(c.content ?? "");
7262
+ return /^\s*<\s*\//.test(cContent) && cContent.toLowerCase().includes(top.tag);
7263
+ });
7264
+ if (closeChildIndex !== -1) {
7265
+ const beforeChildren = children.slice(0, closeChildIndex + 1);
7266
+ const afterChildren = children.slice(closeChildIndex + 1);
7267
+ const beforeText = beforeChildren.map((c) => String(c?.content ?? c?.raw ?? "")).join("");
7268
+ openTok.content = `${String(openTok.content ?? "")}\n${beforeText}`;
7269
+ if (Array.isArray(openTok.children)) openTok.children.push(...beforeChildren);
7270
+ if (afterChildren.length) {
7271
+ const afterText = afterChildren.map((c) => String(c.content ?? c.raw ?? "")).join("");
7272
+ if (afterText.trim()) {
7273
+ const trimmed = afterText.replace(/^\s+/, "");
7274
+ if (trimmed.startsWith("<")) toks.splice(i, 1, {
7275
+ type: "html_block",
7276
+ content: trimmed
7277
+ });
7278
+ else toks.splice(i, 1, {
7279
+ type: "paragraph_open",
7280
+ tag: "p",
7281
+ nesting: 1
7282
+ }, {
7283
+ type: "inline",
7284
+ tag: "",
7285
+ nesting: 0,
7286
+ content: afterText,
7287
+ children: [{
7288
+ type: "text",
7289
+ content: afterText,
7290
+ raw: afterText
7291
+ }]
7292
+ }, {
7293
+ type: "paragraph_close",
7294
+ tag: "p",
7295
+ nesting: -1
7296
+ });
7297
+ } else {
7298
+ toks.splice(i, 1);
7299
+ i--;
7300
+ }
7301
+ } else {
7302
+ toks.splice(i, 1);
7303
+ i--;
7304
+ }
7305
+ stack.pop();
7306
+ continue;
7307
+ }
7308
+ openTok.content = `${String(openTok.content ?? "")}\n${content}`;
7309
+ if (Array.isArray(openTok.children)) openTok.children.push(...children);
7310
+ toks.splice(i, 1);
7311
+ i--;
7312
+ continue;
7313
+ }
7314
+ if (tok.type !== "inline") continue;
7315
+ for (const tag of customTagSet) if (getOpenRe(tag).test(content) && !getCloseRe(tag).test(content)) {
7316
+ stack.push({
7317
+ tag,
7318
+ index: i
7319
+ });
7320
+ break;
7321
+ }
7322
+ }
7323
+ }
7324
+ {
7325
+ let depth = 0;
7326
+ for (let i = 0; i < toks.length; i++) {
7327
+ const t = toks[i];
7328
+ if (t.type === "paragraph_open") {
7329
+ depth++;
7330
+ continue;
7331
+ }
7332
+ if (t.type === "paragraph_close") if (depth > 0) depth--;
7333
+ else {
7334
+ toks.splice(i, 1);
7335
+ i--;
7336
+ }
7337
+ }
7338
+ }
7174
7339
  for (let i = 0; i < toks.length; i++) {
7175
7340
  const t = toks[i];
7176
7341
  if (t.type === "html_block") {
7177
7342
  const tag = (t.content?.match(/<([^\s>/]+)/)?.[1] ?? "").toLowerCase();
7343
+ if (customTagSet.has(tag)) {
7344
+ const raw$1 = String(t.content ?? "");
7345
+ const closeRe = new RegExp(`<\\/\\s*${tag}\\s*>`, "i");
7346
+ t.loading = closeRe.test(raw$1) ? false : t.loading !== void 0 ? t.loading : true;
7347
+ const closeMatch = closeRe.exec(raw$1);
7348
+ const endTagIndex = closeMatch ? closeMatch.index : -1;
7349
+ const closeLen = closeMatch ? closeMatch[0].length : 0;
7350
+ if (endTagIndex !== -1) {
7351
+ const rawForNode = raw$1.slice(0, endTagIndex + closeLen);
7352
+ t.content = rawForNode;
7353
+ t.raw = rawForNode;
7354
+ const afterTrimmed = (raw$1.slice(endTagIndex + closeLen) || "").replace(/^\s+/, "");
7355
+ if (afterTrimmed) toks.splice(i + 1, 0, afterTrimmed.startsWith("<") ? {
7356
+ type: "html_block",
7357
+ content: afterTrimmed
7358
+ } : {
7359
+ type: "text",
7360
+ content: afterTrimmed,
7361
+ raw: afterTrimmed
7362
+ });
7363
+ }
7364
+ continue;
7365
+ }
7178
7366
  if (tag.startsWith("!") || tag.startsWith("?")) {
7179
7367
  t.loading = false;
7180
7368
  continue;
@@ -7192,7 +7380,7 @@ function applyFixHtmlInlineTokens(md, options = {}) {
7192
7380
  "li"
7193
7381
  ].includes(tag)) continue;
7194
7382
  t.type = "inline";
7195
- const loading = t.content?.toLowerCase().includes(`</${tag}>`) ? false : t.loading !== void 0 ? t.loading : true;
7383
+ const loading = new RegExp(`<\\/\\s*${tag}\\s*>`, "i").test(String(t.content ?? "")) ? false : t.loading !== void 0 ? t.loading : true;
7196
7384
  const attrs = [];
7197
7385
  const attrRegex = /\s([\w:-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
7198
7386
  let match;
@@ -7202,24 +7390,36 @@ function applyFixHtmlInlineTokens(md, options = {}) {
7202
7390
  attrs.push([attrName, attrValue]);
7203
7391
  }
7204
7392
  if (customTagSet.has(tag)) {
7205
- const contentMatch = t.content?.match(new RegExp(`<\\s*${tag}[^>]*>([\\s\\S]*)`, "i"));
7206
- const raw$1 = t.content;
7207
- const endTagRegex = new RegExp(`</\\s*${tag}\\s*>`, "i");
7208
- const endTagIndex = t.content?.toLowerCase().indexOf(`</${tag}>`) ?? -1;
7393
+ const raw$1 = String(t.content ?? "");
7394
+ const closeMatch = new RegExp(`<\\/\\s*${tag}\\s*>`, "i").exec(raw$1);
7395
+ const endTagIndex = closeMatch ? closeMatch.index : -1;
7396
+ const closeLen = closeMatch ? closeMatch[0].length : 0;
7397
+ const rawForNode = endTagIndex !== -1 ? raw$1.slice(0, endTagIndex + closeLen) : raw$1;
7398
+ let inner = "";
7399
+ const openEnd = findTagCloseIndexOutsideQuotes$1(raw$1);
7400
+ if (openEnd !== -1) {
7401
+ if (endTagIndex !== -1 && openEnd < endTagIndex) inner = raw$1.slice(openEnd + 1, endTagIndex);
7402
+ else if (endTagIndex === -1) inner = raw$1.slice(openEnd + 1).replace(/<.*$/, "");
7403
+ }
7209
7404
  t.children = [{
7210
7405
  type: tag,
7211
- content: endTagIndex !== -1 ? contentMatch[1].split(endTagRegex)[0] ? contentMatch ? contentMatch[1] : "" : "" : contentMatch ? contentMatch[1].replace(/<.*$/, "") : "",
7212
- raw: raw$1,
7406
+ content: inner,
7407
+ raw: rawForNode,
7213
7408
  attrs,
7214
7409
  tag,
7215
7410
  loading
7216
7411
  }];
7217
7412
  if (endTagIndex !== -1) {
7218
- const afterContent = t.content?.slice(endTagIndex + tag.length + 3) || "";
7219
- if (afterContent.trim()) toks.splice(i + 1, 0, {
7413
+ t.content = rawForNode;
7414
+ t.raw = rawForNode;
7415
+ const afterTrimmed = (raw$1.slice(endTagIndex + closeLen) || "").replace(/^\s+/, "");
7416
+ if (afterTrimmed) toks.splice(i + 1, 0, afterTrimmed.startsWith("<") ? {
7417
+ type: "html_block",
7418
+ content: afterTrimmed
7419
+ } : {
7220
7420
  type: "text",
7221
- content: afterContent,
7222
- raw: afterContent
7421
+ content: afterTrimmed,
7422
+ raw: afterTrimmed
7223
7423
  });
7224
7424
  }
7225
7425
  } else t.children = [{
@@ -7976,7 +8176,7 @@ function fixTableTokens(tokens) {
7976
8176
  if (token.type === "inline") {
7977
8177
  const tcontent = String(token.content ?? "");
7978
8178
  const childContent = String(token.children?.[0]?.content ?? "");
7979
- if (/^\|(?:[^|\n]+\|?)+/.test(tcontent)) {
8179
+ if (!tcontent.includes("\n") && /^\|(?:[^|\n]+\|?)+/.test(tcontent)) {
7980
8180
  const body = childContent.slice(1).split("|").map((i$1) => i$1.trim()).filter(Boolean).flatMap((i$1) => createTh(i$1));
7981
8181
  const insert = [
7982
8182
  ...createStart(),
@@ -8923,6 +9123,7 @@ function parseHtmlInlineCodeToken(token, tokens, i, parseInlineTokens$1, raw, pP
8923
9123
  tag,
8924
9124
  attrs,
8925
9125
  content: fragment.innerTokens.length ? stringifyTokens(fragment.innerTokens) : "",
9126
+ children: fragment.innerTokens.length ? parseInlineTokens$1(fragment.innerTokens, raw, pPreToken, options) : [],
8926
9127
  raw: content,
8927
9128
  loading: token.loading || loading,
8928
9129
  autoClosed
@@ -9270,7 +9471,7 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
9270
9471
  i++;
9271
9472
  return true;
9272
9473
  }
9273
- if (/\*\*/.test(content) && !content.endsWith("**")) {
9474
+ if (/\*\*/.test(content)) {
9274
9475
  const openIdx = content.indexOf("**");
9275
9476
  const beforeText = openIdx > -1 ? content.slice(0, openIdx) : "";
9276
9477
  if (beforeText) pushText(beforeText, beforeText);
@@ -10184,7 +10385,7 @@ function parseHtmlBlock(token) {
10184
10385
  const isVoid = VOID_TAGS.has(tag);
10185
10386
  let closeRe = CLOSE_TAG_RE_CACHE.get(tag);
10186
10387
  if (!closeRe) {
10187
- closeRe = new RegExp(`<\\/\\s*${tag}\\b`, "i");
10388
+ closeRe = new RegExp(`<\\s*\\/\\s*${tag}\\s*>`, "i");
10188
10389
  CLOSE_TAG_RE_CACHE.set(tag, closeRe);
10189
10390
  }
10190
10391
  const hasClosing = closeRe.test(raw);
@@ -10293,6 +10494,98 @@ function parseThematicBreak() {
10293
10494
 
10294
10495
  //#endregion
10295
10496
  //#region src/parser/node-parsers/block-token-parser.ts
10497
+ function findTagCloseIndexOutsideQuotes(input) {
10498
+ let inSingle = false;
10499
+ let inDouble = false;
10500
+ for (let i = 0; i < input.length; i++) {
10501
+ const ch = input[i];
10502
+ if (ch === "\\") {
10503
+ i++;
10504
+ continue;
10505
+ }
10506
+ if (!inDouble && ch === "'") {
10507
+ inSingle = !inSingle;
10508
+ continue;
10509
+ }
10510
+ if (!inSingle && ch === "\"") {
10511
+ inDouble = !inDouble;
10512
+ continue;
10513
+ }
10514
+ if (!inSingle && !inDouble && ch === ">") return i;
10515
+ }
10516
+ return -1;
10517
+ }
10518
+ function stripWrapperNewlines(s) {
10519
+ return s.replace(/^\r?\n/, "").replace(/\r?\n$/, "");
10520
+ }
10521
+ function stripTrailingPartialClosingTag(inner, tag) {
10522
+ if (!inner || !tag) return inner;
10523
+ const re = new RegExp(String.raw`[\t ]*<\s*\/\s*${tag}[^>]*$`, "i");
10524
+ return inner.replace(re, "");
10525
+ }
10526
+ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
10527
+ if (!source || !tag) return null;
10528
+ const lowerTag = tag.toLowerCase();
10529
+ const openRe = new RegExp(String.raw`<\s*${lowerTag}(?=\s|>|/)`, "gi");
10530
+ openRe.lastIndex = Math.max(0, startIndex || 0);
10531
+ const openMatch = openRe.exec(source);
10532
+ if (!openMatch || openMatch.index == null) return null;
10533
+ const openStart = openMatch.index;
10534
+ const openSlice = source.slice(openStart);
10535
+ const openEndRel = findTagCloseIndexOutsideQuotes(openSlice);
10536
+ if (openEndRel === -1) return null;
10537
+ const openEnd = openStart + openEndRel;
10538
+ if (/\/\s*>\s*$/.test(openSlice.slice(0, openEndRel + 1))) {
10539
+ const end = openEnd + 1;
10540
+ return {
10541
+ raw: source.slice(openStart, end),
10542
+ end
10543
+ };
10544
+ }
10545
+ let depth = 1;
10546
+ let i = openEnd + 1;
10547
+ const isOpenAt = (pos) => {
10548
+ const s = source.slice(pos);
10549
+ return new RegExp(String.raw`^<\s*${lowerTag}(?=\s|>|/)`, "i").test(s);
10550
+ };
10551
+ const isCloseAt = (pos) => {
10552
+ const s = source.slice(pos);
10553
+ return new RegExp(String.raw`^<\s*\/\s*${lowerTag}(?=\s|>)`, "i").test(s);
10554
+ };
10555
+ while (i < source.length) {
10556
+ const lt = source.indexOf("<", i);
10557
+ if (lt === -1) return {
10558
+ raw: source.slice(openStart),
10559
+ end: source.length
10560
+ };
10561
+ if (isCloseAt(lt)) {
10562
+ const gt = source.indexOf(">", lt);
10563
+ if (gt === -1) return null;
10564
+ depth--;
10565
+ if (depth === 0) {
10566
+ const end = gt + 1;
10567
+ return {
10568
+ raw: source.slice(openStart, end),
10569
+ end
10570
+ };
10571
+ }
10572
+ i = gt + 1;
10573
+ continue;
10574
+ }
10575
+ if (isOpenAt(lt)) {
10576
+ const rel = findTagCloseIndexOutsideQuotes(source.slice(lt));
10577
+ if (rel === -1) return null;
10578
+ depth++;
10579
+ i = lt + rel + 1;
10580
+ continue;
10581
+ }
10582
+ i = lt + 1;
10583
+ }
10584
+ return {
10585
+ raw: source.slice(openStart),
10586
+ end: source.length
10587
+ };
10588
+ }
10296
10589
  function parseBasicBlockToken(tokens, index, options) {
10297
10590
  const token = tokens[index];
10298
10591
  switch (token.type) {
@@ -10306,10 +10599,37 @@ function parseBasicBlockToken(tokens, index, options) {
10306
10599
  if (new Set(options.customHtmlTags.map((t) => {
10307
10600
  const m = String(t ?? "").trim().match(/^[<\s/]*([A-Z][\w-]*)/i);
10308
10601
  return m ? m[1].toLowerCase() : "";
10309
- }).filter(Boolean)).has(htmlBlockNode.tag)) return [{
10310
- ...htmlBlockNode,
10311
- type: htmlBlockNode.tag
10312
- }, index + 1];
10602
+ }).filter(Boolean)).has(htmlBlockNode.tag)) {
10603
+ const tag = htmlBlockNode.tag;
10604
+ const fromSource = findNextCustomHtmlBlockFromSource(String(options?.__sourceMarkdown ?? ""), tag, Number(options?.__customHtmlBlockCursor ?? 0));
10605
+ if (fromSource) options.__customHtmlBlockCursor = fromSource.end;
10606
+ const rawHtml = String(fromSource?.raw ?? htmlBlockNode.content ?? "");
10607
+ const openEnd = findTagCloseIndexOutsideQuotes(rawHtml);
10608
+ const closeMatch = new RegExp(`<\\s*\\/\\s*${tag}\\s*>`, "i").exec(rawHtml);
10609
+ const closeIndex = closeMatch ? closeMatch.index : -1;
10610
+ let inner = "";
10611
+ if (openEnd !== -1) if (closeIndex !== -1 && openEnd < closeIndex) inner = rawHtml.slice(openEnd + 1, closeIndex);
10612
+ else inner = rawHtml.slice(openEnd + 1);
10613
+ if (closeIndex === -1) inner = stripTrailingPartialClosingTag(inner, tag);
10614
+ const attrs = [];
10615
+ const openTag = openEnd !== -1 ? rawHtml.slice(0, openEnd + 1) : rawHtml;
10616
+ const attrRegex = /\s([\w:-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
10617
+ let m;
10618
+ while ((m = attrRegex.exec(openTag)) !== null) {
10619
+ const name = m[1];
10620
+ if (!name || name.toLowerCase() === tag) continue;
10621
+ const value = m[2] || m[3] || m[4] || "";
10622
+ attrs.push([name, value]);
10623
+ }
10624
+ return [{
10625
+ type: tag,
10626
+ tag,
10627
+ content: stripWrapperNewlines(inner),
10628
+ raw: String(fromSource?.raw ?? htmlBlockNode.raw ?? rawHtml),
10629
+ loading: htmlBlockNode.loading,
10630
+ attrs: attrs.length ? attrs : void 0
10631
+ }, index + 1];
10632
+ }
10313
10633
  }
10314
10634
  return [htmlBlockNode, index + 1];
10315
10635
  }
@@ -10623,18 +10943,51 @@ function parseParagraph(tokens, index, options) {
10623
10943
 
10624
10944
  //#endregion
10625
10945
  //#region src/parser/index.ts
10946
+ function stripDanglingHtmlLikeTail(markdown) {
10947
+ const s = String(markdown ?? "");
10948
+ const lastLt = s.lastIndexOf("<");
10949
+ if (lastLt === -1) return s;
10950
+ const tail = s.slice(lastLt);
10951
+ if (tail.includes(">")) return s;
10952
+ if (!/^<\s*(?:\/\s*)?[A-Z!][\s\S]*$/i.test(tail)) return s;
10953
+ return s.slice(0, lastLt);
10954
+ }
10626
10955
  function parseMarkdownToStructure(markdown, md, options = {}) {
10627
10956
  let safeMarkdown = (markdown ?? "").toString().replace(/([^\\])\r(ight|ho)/g, "$1\\r$2").replace(/([^\\])\n(abla|eq|ot|exists)/g, "$1\\n$2");
10628
10957
  if (safeMarkdown.endsWith("- *")) safeMarkdown = safeMarkdown.replace(/- \*$/, "- \\*");
10629
- if (/\n\s*-\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/\n\s*-\s*$/, "\n");
10958
+ if (/(?:^|\n)\s*-\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(?:^|\n)\s*-\s*$/, (m) => {
10959
+ return m.startsWith("\n") ? "\n" : "";
10960
+ });
10961
+ else if (/(?:^|\n)\s*--\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(?:^|\n)\s*--\s*$/, (m) => {
10962
+ return m.startsWith("\n") ? "\n" : "";
10963
+ });
10964
+ else if (/(?:^|\n)\s*>\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(?:^|\n)\s*>\s*$/, (m) => {
10965
+ return m.startsWith("\n") ? "\n" : "";
10966
+ });
10967
+ else if (/\n\s*[*+]\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/\n\s*[*+]\s*$/, "\n");
10630
10968
  else if (/\n[[(]\n*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(\n\[|\n\()+\n*$/g, "\n");
10969
+ if (options.customHtmlTags?.length) {
10970
+ const tags = options.customHtmlTags.map((t) => String(t ?? "").trim()).filter(Boolean).map((t) => {
10971
+ return (t.match(/^[<\s/]*([A-Z][\w-]*)/i)?.[1] ?? "").toLowerCase();
10972
+ }).filter(Boolean);
10973
+ if (tags.length) if (!safeMarkdown.includes("</")) {} else for (const tag of tags) {
10974
+ const re = new RegExp(String.raw`(^[\t ]*<\s*\/\s*${tag}\s*>[\t ]*)(\r?\n)(?![\t ]*\r?\n|$)`, "gim");
10975
+ safeMarkdown = safeMarkdown.replace(re, "$1$2$2");
10976
+ }
10977
+ }
10978
+ safeMarkdown = stripDanglingHtmlLikeTail(safeMarkdown);
10631
10979
  const tokens = md.parse(safeMarkdown, {});
10632
10980
  if (!tokens || !Array.isArray(tokens)) return [];
10633
10981
  const pre = options.preTransformTokens;
10634
10982
  const post = options.postTransformTokens;
10635
10983
  let transformedTokens = tokens;
10636
10984
  if (pre && typeof pre === "function") transformedTokens = pre(transformedTokens) || transformedTokens;
10637
- let result = processTokens(transformedTokens, options);
10985
+ const internalOptions = {
10986
+ ...options,
10987
+ __sourceMarkdown: safeMarkdown,
10988
+ __customHtmlBlockCursor: 0
10989
+ };
10990
+ let result = processTokens(transformedTokens, internalOptions);
10638
10991
  if (post && typeof post === "function") {
10639
10992
  const postResult = post(transformedTokens);
10640
10993
  if (Array.isArray(postResult)) {
@@ -10692,6 +11045,20 @@ function processTokens(tokens, options) {
10692
11045
  result.push(parseHardBreak());
10693
11046
  i++;
10694
11047
  break;
11048
+ case "text": {
11049
+ const content = String(token.content ?? "");
11050
+ result.push({
11051
+ type: "paragraph",
11052
+ raw: content,
11053
+ children: content ? [{
11054
+ type: "text",
11055
+ content,
11056
+ raw: content
11057
+ }] : []
11058
+ });
11059
+ i++;
11060
+ break;
11061
+ }
10695
11062
  case "inline":
10696
11063
  result.push(...parseInlineTokens(token.children || [], String(token.content ?? ""), void 0, {
10697
11064
  requireClosingStrong: options?.requireClosingStrong,