stream-markdown-parser 0.0.20 → 0.0.22

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
@@ -98,17 +98,68 @@ function applyFixHtmlInlineTokens(md) {
98
98
  const toks = state.tokens ?? [];
99
99
  for (let i = 0; i < toks.length; i++) {
100
100
  const t = toks[i];
101
- if (!t || t.type !== "inline") continue;
102
- if (/\s*<$/.test(t.content)) {
103
- t.children.length = 0;
101
+ if (t.type === "html_block" && /^<[^>\s/]+>$/.test(t.content)) {
102
+ const tag = t.content?.match(/<([^\s>/]+)/)?.[1] ?? "";
103
+ if ([
104
+ "br",
105
+ "hr",
106
+ "img",
107
+ "input",
108
+ "link",
109
+ "meta"
110
+ ].includes(tag)) continue;
111
+ t.type = "inline";
112
+ t.children = [{
113
+ type: "html_block",
114
+ content: t.content,
115
+ tag: t.content?.match(/<([^\s>/]+)/)?.[1] ?? "",
116
+ loading: true
117
+ }];
104
118
  continue;
105
119
  }
120
+ if (!t || t.type !== "inline") continue;
106
121
  if (t.children.length === 2 && t.children[0].type === "html_inline") {
122
+ const tag = t.children[0].content?.match(/<([^\s>/]+)/)?.[1] ?? "";
123
+ if ([
124
+ "a",
125
+ "span",
126
+ "strong",
127
+ "em",
128
+ "b",
129
+ "i",
130
+ "u"
131
+ ].includes(tag)) {
132
+ t.children[0].loading = true;
133
+ t.children[0].tag = tag;
134
+ t.children.push({
135
+ type: "html_inline",
136
+ tag,
137
+ loading: true,
138
+ content: `</${tag}>`
139
+ });
140
+ } else t.children = [{
141
+ type: "html_block",
142
+ loading: true,
143
+ tag,
144
+ content: t.children[0].content + t.children[1].content
145
+ }];
146
+ continue;
147
+ } else if (t.children.length === 3 && t.children[0].type === "html_inline" && t.children[2].type === "html_inline") {
148
+ const tag = t.children[0].content?.match(/<([^\s>/]+)/)?.[1] ?? "";
149
+ if ([
150
+ "a",
151
+ "span",
152
+ "strong",
153
+ "em",
154
+ "b",
155
+ "i",
156
+ "u"
157
+ ].includes(tag)) continue;
107
158
  t.children = [{
108
159
  type: "html_block",
109
- content: t.children[0].content + t.children[1].content,
110
- tag: t.children[0].content.match(/<([^\s>/]+)/)?.[1] ?? "",
111
- loading: true
160
+ loading: false,
161
+ tag,
162
+ content: t.children.map((ct) => ct.content).join("")
112
163
  }];
113
164
  continue;
114
165
  }
@@ -176,145 +227,303 @@ function applyFixLinkTokens(md) {
176
227
  }
177
228
  });
178
229
  }
179
- function isTextToken(t) {
180
- return !!t && t.type === "text" && typeof t.content === "string";
181
- }
182
230
  function fixLinkToken(tokens) {
183
- const tokensAny = tokens;
184
- tokens = fixLinkToken4(fixLinkToken3(tokens));
185
- if (tokens.length < 5) return tokens;
186
- const first = tokens[tokens.length - 5];
187
- const firstAny = first;
188
- const firstContent = String(firstAny.content ?? "");
189
- if (first.type !== "text" || !firstContent.endsWith("[")) return fixLinkTokens2(tokens);
190
- if (tokens[tokens.length - 4].tag !== "em") return fixLinkTokens2(tokens);
191
- const last = tokens[tokens.length - 1];
192
- const lastAny = last;
193
- const lastContent = String(lastAny.content ?? "");
194
- if (last?.type === "text" && !lastContent.startsWith("]")) return fixLinkTokens2(tokens);
195
- const thirdAny = tokens[tokens.length - 3];
196
- const thirdContent = String(thirdAny.content ?? "");
197
- const href = lastContent.replace(/^\]\(*/, "");
198
- const loading = !lastContent.includes(")");
199
- tokensAny[tokens.length - 5].content = firstContent.replace(/\[$/, "");
200
- tokens.splice(tokens.length - 3, 1, {
201
- type: "link",
202
- href,
203
- text: thirdContent,
204
- children: [{
205
- type: "text",
206
- content: thirdContent,
207
- raw: thirdContent
208
- }],
209
- loading
210
- });
211
- tokens.splice(tokens.length - 1, 1);
212
- return tokens;
213
- }
214
- function fixLinkTokens2(tokens) {
215
- const tokensAny = tokens;
216
- if (tokens.length < 8) return tokens;
217
- let length = tokens.length;
218
- let last = tokens[length - 1];
219
- if (!last) return tokens;
220
- if (last.type !== "link_close") {
221
- length--;
222
- last = tokens[length - 1];
223
- if (last.type !== "link_close") return tokens;
224
- }
225
- if (tokens[length - 7].type !== "em_open") return tokens;
226
- const third = tokens[length - 6];
227
- const first = tokens[length - 8];
228
- if (first.type !== "text") return tokens;
229
- let href = String(tokensAny[length - 2]?.content ?? "");
230
- let count = 4;
231
- if (length !== tokens.length) {
232
- href += String(last.content ?? "");
233
- count++;
234
- }
235
- tokens.splice(length - 4, count);
236
- const thirdAny = third;
237
- const content = String(thirdAny.content ?? "");
238
- length -= 4;
239
- const firstAny = first;
240
- tokensAny[length - 8].content = String(firstAny.content ?? "").replace(/\[$/, "");
241
- tokens.splice(length - 2, 1, {
242
- type: "link",
243
- href,
244
- text: content,
245
- children: [{
246
- type: "text",
247
- content,
248
- raw: content
249
- }],
250
- loading: true
251
- });
252
- return tokens;
253
- }
254
- function fixLinkToken3(tokens) {
255
- const tokensAny = tokens;
256
- const last = tokens[tokens.length - 1];
257
- const preLast = tokens[tokens.length - 2];
258
- const fixedTokens = [...tokens];
259
- if (!last) return tokens;
260
- if (last.type !== "text" || !last.content?.startsWith(")")) return tokens;
261
- if (preLast.type !== "link_close") return tokens;
262
- if (isTextToken(tokens[tokens.length - 5]) && String(tokens[tokens.length - 5].content ?? "").endsWith("(")) {
263
- const a = tokensAny[tokens.length - 5];
264
- const b = tokensAny[tokens.length - 3];
265
- const content = String(a.content ?? "") + String(b.content ?? "") + String(last.content ?? "");
266
- fixedTokens.splice(tokens.length - 5, 5, {
267
- type: "text",
268
- content,
269
- raw: content
270
- });
271
- } else {
272
- const lc = (last.content ?? "").slice(1);
273
- fixedTokens[fixedTokens.length - 1] = {
274
- ...last,
275
- content: lc
276
- };
277
- }
278
- return fixedTokens;
279
- }
280
- function fixLinkToken4(tokens) {
281
- const tokensAny = tokens;
282
- const fixedTokens = [...tokens];
283
- for (let i = tokens.length - 1; i >= 3; i--) {
284
- const token = tokens[i];
285
- if (token && token.type === "link_close") {
286
- if (tokens[i - 3]?.content?.endsWith("(")) {
287
- const nextToken = tokens[i + 1];
288
- if (nextToken && nextToken?.type === "text") {
289
- if (tokens[i - 1].type === "text" && tokens[i - 3]?.type === "text") {
290
- const nextTokenContent = String(nextToken.content ?? "");
291
- const a = tokensAny[i - 3];
292
- const b = tokensAny[i - 1];
293
- const content = String(a.content ?? "") + String(b.content ?? "") + nextTokenContent;
294
- fixedTokens.splice(i - 3, 5, {
231
+ if (tokens.length < 4) return tokens;
232
+ for (let i = 0; i <= tokens.length - 1; i++) {
233
+ if (!tokens[i]) break;
234
+ if (tokens[i]?.type === "text" && tokens[i].content?.endsWith("(") && tokens[i + 1]?.type === "link_open") {
235
+ const match = tokens[i].content.match(/\[([^\]]+)\]/);
236
+ if (match) {
237
+ let beforeText = tokens[i].content.slice(0, match.index);
238
+ const emphasisMatch = beforeText.match(/(\*+)$/);
239
+ const replacerTokens = [];
240
+ if (emphasisMatch) {
241
+ beforeText = beforeText.slice(0, emphasisMatch.index);
242
+ if (beforeText) replacerTokens.push({
243
+ type: "text",
244
+ content: beforeText,
245
+ raw: beforeText
246
+ });
247
+ const text = match[1];
248
+ const type = emphasisMatch[1].length;
249
+ if (type === 1) replacerTokens.push({
250
+ type: "em_open",
251
+ tag: "em",
252
+ nesting: 1
253
+ });
254
+ else if (type === 2) replacerTokens.push({
255
+ type: "strong_open",
256
+ tag: "strong",
257
+ nesting: 1
258
+ });
259
+ else if (type === 3) {
260
+ replacerTokens.push({
261
+ type: "strong_open",
262
+ tag: "strong",
263
+ nesting: 1
264
+ });
265
+ replacerTokens.push({
266
+ type: "em_open",
267
+ tag: "em",
268
+ nesting: 1
269
+ });
270
+ }
271
+ let href = tokens[i + 2]?.content || "";
272
+ if (tokens[i + 4]?.type === "text" && !tokens[i + 4].content?.startsWith(")")) {
273
+ href += tokens[i + 4]?.content || "";
274
+ tokens[i + 4].content = "";
275
+ }
276
+ replacerTokens.push({
277
+ type: "link",
278
+ loading: !tokens[i + 4]?.content?.startsWith(")"),
279
+ href,
280
+ title: "",
281
+ text,
282
+ children: [{
295
283
  type: "text",
296
- content,
297
- raw: content
284
+ content: text,
285
+ raw: text
286
+ }],
287
+ raw: String(`[${text}](${href})`)
288
+ });
289
+ if (type === 1) replacerTokens.push({
290
+ type: "em_close",
291
+ tag: "em",
292
+ nesting: -1
293
+ });
294
+ else if (type === 2) replacerTokens.push({
295
+ type: "strong_close",
296
+ tag: "strong",
297
+ nesting: -1
298
+ });
299
+ else if (type === 3) {
300
+ replacerTokens.push({
301
+ type: "em_close",
302
+ tag: "em",
303
+ nesting: -1
304
+ });
305
+ replacerTokens.push({
306
+ type: "strong_close",
307
+ tag: "strong",
308
+ nesting: -1
298
309
  });
299
- i -= 3;
300
310
  }
311
+ if (tokens[i + 4]?.type === "text") {
312
+ const afterText = tokens[i + 4].content?.replace(/^\)\**/, "");
313
+ if (afterText) replacerTokens.push({
314
+ type: "text",
315
+ content: afterText,
316
+ raw: afterText
317
+ });
318
+ tokens.splice(i, 5, ...replacerTokens);
319
+ } else tokens.splice(i, 4, ...replacerTokens);
301
320
  } else {
302
- if (tokens[i - 1].type === "text" && tokens[i - 3]?.type === "text") {
303
- const a = tokensAny[i - 3];
304
- const b = tokensAny[i - 1];
305
- const content = String(a.content ?? "") + String(b.content ?? "");
306
- fixedTokens.splice(i - 3, 4, {
321
+ if (beforeText) replacerTokens.push({
322
+ type: "text",
323
+ content: beforeText,
324
+ raw: beforeText
325
+ });
326
+ const text = match[1];
327
+ let href = tokens[i + 2]?.content || "";
328
+ if (tokens[i + 4]?.type === "text" && !tokens[i + 4].content?.startsWith(")")) {
329
+ href += tokens[i + 4]?.content || "";
330
+ tokens[i + 4].content = "";
331
+ }
332
+ replacerTokens.push(...[{
333
+ type: "link",
334
+ loading: !tokens[i + 4]?.content?.startsWith(")"),
335
+ href,
336
+ title: "",
337
+ text,
338
+ children: [{
339
+ type: "text",
340
+ content: text,
341
+ raw: text
342
+ }],
343
+ raw: String(`[${text}](${href})`)
344
+ }]);
345
+ if (tokens[i + 4]?.type === "text") {
346
+ const afterText = tokens[i + 4].content?.replace(/^\)/, "");
347
+ if (afterText) replacerTokens.push({
307
348
  type: "text",
308
- content,
309
- raw: content
349
+ content: afterText,
350
+ raw: afterText
310
351
  });
352
+ tokens.splice(i, 5, ...replacerTokens);
353
+ } else tokens.splice(i, 4, ...replacerTokens);
354
+ }
355
+ i -= replacerTokens.length - 1;
356
+ continue;
357
+ }
358
+ } else if (tokens[i].type === "link_open" && tokens[i].markup === "linkify" && tokens[i - 1]?.type === "text" && tokens[i - 1].content?.endsWith("(")) {
359
+ if (tokens[i - 2]?.type === "link_close") {
360
+ const replacerTokens = [];
361
+ const text = tokens[i - 3].content || "";
362
+ let href = tokens[i].attrs?.find((attr) => attr[0] === "href")?.[1] || "";
363
+ if (tokens[i + 3]?.type === "text") {
364
+ const m = (tokens[i + 3]?.content ?? "").indexOf(")");
365
+ const loading = m === -1;
366
+ if (m === -1) {
367
+ href += tokens[i + 3]?.content?.slice(0, m) || "";
368
+ tokens[i + 3].content = "";
311
369
  }
312
- i -= 3;
370
+ replacerTokens.push({
371
+ type: "link",
372
+ loading,
373
+ href,
374
+ title: "",
375
+ text,
376
+ children: [{
377
+ type: "text",
378
+ content: text,
379
+ raw: text
380
+ }],
381
+ raw: String(`[${text}](${href})`)
382
+ });
383
+ const afterText = tokens[i + 3].content?.replace(/^\)\**/, "");
384
+ if (afterText) replacerTokens.push({
385
+ type: "text",
386
+ content: afterText,
387
+ raw: afterText
388
+ });
389
+ tokens.splice(i - 4, 8, ...replacerTokens);
390
+ } else {
391
+ replacerTokens.push({
392
+ type: "link",
393
+ loading: true,
394
+ href,
395
+ title: "",
396
+ text,
397
+ children: [{
398
+ type: "text",
399
+ content: href,
400
+ raw: href
401
+ }],
402
+ raw: String(`[${text}](${href})`)
403
+ });
404
+ tokens.splice(i - 4, 7, ...replacerTokens);
405
+ }
406
+ continue;
407
+ }
408
+ }
409
+ if (tokens[i].type === "link_close" && tokens[i].nesting === -1 && tokens[i + 1]?.type === "text" && tokens[i - 1]?.type === "text" && tokens[i + 2]?.type !== "link_open") {
410
+ tokens[i - 2].loading = true;
411
+ const text = tokens[i - 1].content || "";
412
+ let href = tokens[i - 2].attrs?.[0]?.[1] || "";
413
+ let count = 3;
414
+ if (tokens[i].markup === "linkify" && tokens[i + 1]?.type === "text") {
415
+ const m = (tokens[i + 1]?.content ?? "").indexOf(")");
416
+ if (m === -1) {
417
+ href += tokens[i + 1]?.content?.slice(0, m) || "";
418
+ tokens[i + 1].content = "";
419
+ }
420
+ count += 1;
421
+ }
422
+ tokens.splice(i - 2, count, {
423
+ type: "link",
424
+ loading: false,
425
+ href,
426
+ title: "",
427
+ text,
428
+ children: [{
429
+ type: "text",
430
+ content: text,
431
+ raw: text
432
+ }],
433
+ raw: String(`[${text}](${href})`)
434
+ });
435
+ } else if (tokens[i].content?.startsWith("](") && tokens[i - 1].markup?.includes("*") && tokens[i - 4].type === "text" && tokens[i - 4].content?.endsWith("[")) {
436
+ const type = tokens[i - 1].markup.length;
437
+ const replacerTokens = [];
438
+ const beforeText = tokens[i - 4].content.slice(0, tokens[i - 4].content.length - 1 - type);
439
+ if (beforeText) replacerTokens.push({
440
+ type: "text",
441
+ content: beforeText,
442
+ raw: beforeText
443
+ });
444
+ if (type === 1) replacerTokens.push({
445
+ type: "em_open",
446
+ tag: "em",
447
+ nesting: 1
448
+ });
449
+ else if (type === 2) replacerTokens.push({
450
+ type: "strong_open",
451
+ tag: "strong",
452
+ nesting: 1
453
+ });
454
+ else if (type === 3) {
455
+ replacerTokens.push({
456
+ type: "strong_open",
457
+ tag: "strong",
458
+ nesting: 1
459
+ });
460
+ replacerTokens.push({
461
+ type: "em_open",
462
+ tag: "em",
463
+ nesting: 1
464
+ });
465
+ }
466
+ const text = tokens[i - 2].content || "";
467
+ let href = tokens[i].content.slice(2);
468
+ let loading = true;
469
+ if (tokens[i + 1]?.type === "text") {
470
+ const m = (tokens[i + 1]?.content ?? "").indexOf(")");
471
+ loading = m === -1;
472
+ if (m === -1) {
473
+ href += tokens[i + 1]?.content?.slice(0, m) || "";
474
+ tokens[i + 1].content = "";
313
475
  }
314
476
  }
477
+ replacerTokens.push({
478
+ type: "link",
479
+ loading,
480
+ href,
481
+ title: "",
482
+ text,
483
+ children: [{
484
+ type: "text",
485
+ content: text,
486
+ raw: text
487
+ }],
488
+ raw: String(`[${text}](${href})`)
489
+ });
490
+ if (type === 1) replacerTokens.push({
491
+ type: "em_close",
492
+ tag: "em",
493
+ nesting: -1
494
+ });
495
+ else if (type === 2) replacerTokens.push({
496
+ type: "strong_close",
497
+ tag: "strong",
498
+ nesting: -1
499
+ });
500
+ else if (type === 3) {
501
+ replacerTokens.push({
502
+ type: "em_close",
503
+ tag: "em",
504
+ nesting: -1
505
+ });
506
+ replacerTokens.push({
507
+ type: "strong_close",
508
+ tag: "strong",
509
+ nesting: -1
510
+ });
511
+ }
512
+ if (tokens[i + 1]?.type === "text") {
513
+ const afterText = tokens[i + 1].content?.replace(/^\)\**/, "");
514
+ if (afterText) replacerTokens.push({
515
+ type: "text",
516
+ content: afterText,
517
+ raw: afterText
518
+ });
519
+ tokens.splice(i - 4, 8, ...replacerTokens);
520
+ } else if (tokens[i + 1]?.type === "link_open") tokens.splice(i - 4, 10, ...replacerTokens);
521
+ else tokens.splice(i - 4, 7, ...replacerTokens);
522
+ i -= replacerTokens.length - 1;
523
+ continue;
315
524
  }
316
525
  }
317
- return fixedTokens;
526
+ return tokens;
318
527
  }
319
528
 
320
529
  //#endregion
@@ -359,9 +568,10 @@ function fixStrongTokens(tokens) {
359
568
  if (tokens.length < 4) return fixedTokens;
360
569
  const i = tokens.length - 4;
361
570
  const token = tokens[i];
571
+ if (!token) return fixedTokens;
362
572
  const nextToken = tokens[i + 1];
363
573
  const tokenContent = String(token.content ?? "");
364
- if (token.type === "link_open" && tokens[i - 1].type === "em_open" && tokens[i - 2].type === "text" && tokens[i - 2].content?.endsWith("*")) {
574
+ if (token.type === "link_open" && tokens[i - 1]?.type === "em_open" && tokens[i - 2]?.type === "text" && tokens[i - 2].content?.endsWith("*")) {
365
575
  const textContent = String(tokens[i - 2].content ?? "").slice(0, -1);
366
576
  const replaceTokens = [
367
577
  {
@@ -1979,7 +2189,8 @@ function parseInlineTokens(tokens, raw, pPreToken) {
1979
2189
  }
1980
2190
  if (!nextToken && /[^\]]\s*\(\s*$/.test(content)) content = content.replace(/\(\s*$/, "");
1981
2191
  if (handleCheckboxLike(content)) return;
1982
- if (content === "[") {
2192
+ const preToken = tokens[i - 1];
2193
+ if (content === "[" && !nextToken?.markup?.includes("*") || content === "]" && !preToken.markup?.includes("*")) {
1983
2194
  i++;
1984
2195
  return;
1985
2196
  }
@@ -1991,7 +2202,6 @@ function parseInlineTokens(tokens, raw, pPreToken) {
1991
2202
  content
1992
2203
  });
1993
2204
  if (handleInlineLinkContent(content, token)) return;
1994
- const preToken = tokens[i - 1];
1995
2205
  if (currentTextNode) {
1996
2206
  currentTextNode.content += textNode.content.replace(/(\*+|\(|\\)$/, "");
1997
2207
  currentTextNode.raw += textNode.raw;