@leadertechie/md2html 0.1.0-alpha.18 → 0.1.0-alpha.19

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
@@ -1,41 +1,6 @@
1
+ import { d as defaultAllowedHTMLTags, H as HTMLRenderer } from "./lit-renderer-Bp1Q6wYL.js";
2
+ import { L, a, b, c, e, f, g, h, i, j, k, l, m, n, R, o } from "./lit-renderer-Bp1Q6wYL.js";
1
3
  import { marked } from "marked";
2
- const defaultAllowedHTMLTags = [
3
- "img",
4
- "style",
5
- "div",
6
- "span",
7
- "section",
8
- "article",
9
- "aside",
10
- "header",
11
- "footer",
12
- "nav",
13
- "main",
14
- "figure",
15
- "figcaption",
16
- "details",
17
- "summary",
18
- "mark",
19
- "time",
20
- "video",
21
- "audio",
22
- "source",
23
- "iframe",
24
- "embed"
25
- ];
26
- const nodeTypeToScope = {
27
- "text": "root",
28
- "heading": "heading",
29
- "paragraph": "paragraph",
30
- "list": "list",
31
- "list-item": "list-item",
32
- "image": "image",
33
- "code": "code",
34
- "container": "container",
35
- "strong": "strong",
36
- "emphasis": "emphasis",
37
- "link": "link"
38
- };
39
4
  class HeadingHandler {
40
5
  constructor() {
41
6
  this.type = "heading";
@@ -268,7 +233,7 @@ class ContainerBlockHandler {
268
233
  const classMatches = [...specifier.matchAll(/\.([\w-]+)/g)];
269
234
  const tag = tagMatch?.[1] || "div";
270
235
  const id = idMatch?.[1] || "";
271
- const classes = classMatches.map((m) => m[1]);
236
+ const classes = classMatches.map((m2) => m2[1]);
272
237
  const children = ctx.parseTokens(childTokens, 0);
273
238
  return {
274
239
  type: "container",
@@ -329,6 +294,392 @@ class TokenHandlerRegistry {
329
294
  return this.catchAll;
330
295
  }
331
296
  }
297
+ var h2 = /* @__PURE__ */ ((t) => (t[t.DEBUG = 1] = "DEBUG", t[t.INFO = 9] = "INFO", t[t.WARN = 13] = "WARN", t[t.ERROR = 17] = "ERROR", t))(h2 || {});
298
+ const S = {
299
+ 1: "DEBUG",
300
+ 9: "INFO",
301
+ 13: "WARN",
302
+ 17: "ERROR"
303
+ }, E = [
304
+ "/telemetry/src/logger.ts",
305
+ "/telemetry/src/caller.ts",
306
+ "/telemetry/src/index.ts"
307
+ ];
308
+ function N() {
309
+ const e2 = new Error().stack;
310
+ if (!e2) return;
311
+ const s = e2.split(`
312
+ `);
313
+ for (let r = 1; r < s.length; r++) {
314
+ const o2 = s[r].trim(), n2 = $(o2);
315
+ if (!n2) continue;
316
+ const { file: l2 } = n2;
317
+ if (!(l2 && E.some((m2) => l2.includes(m2))) && !(l2 && l2.includes("/node_modules/")) && l2)
318
+ return n2;
319
+ }
320
+ }
321
+ function $(t) {
322
+ const e2 = t.match(
323
+ /at\s+(?:(?:async\s+)?(?:(.+?)\s+\()?)?(?:(.+?):(\d+):(\d+)\)?)$/
324
+ );
325
+ if (!e2) return null;
326
+ const s = e2[1] || "<anonymous>", r = e2[2], o2 = parseInt(e2[3], 10), n2 = parseInt(e2[4], 10);
327
+ return r ? { file: r, line: o2, column: n2, functionName: s } : null;
328
+ }
329
+ class g2 {
330
+ constructor(e2 = [], s, r = {}) {
331
+ this.processors = [...e2], this.resource = s ?? { serviceName: "unknown" }, this.baseAttributes = { ...r };
332
+ }
333
+ // ── Public API ───────────────────────────────────────────────────────────
334
+ debug(e2, s) {
335
+ this.emit(h2.DEBUG, e2, void 0, s);
336
+ }
337
+ info(e2, s) {
338
+ this.emit(h2.INFO, e2, void 0, s);
339
+ }
340
+ warn(e2, s) {
341
+ this.emit(h2.WARN, e2, void 0, s);
342
+ }
343
+ error(e2, s, r) {
344
+ this.emit(h2.ERROR, e2, s, r);
345
+ }
346
+ /**
347
+ * Create a child logger with merged base attributes.
348
+ * Returns a NEW Logger — original is immutable.
349
+ */
350
+ withContext(e2) {
351
+ return new g2(this.processors, this.resource, {
352
+ ...this.baseAttributes,
353
+ ...e2
354
+ });
355
+ }
356
+ /**
357
+ * Append a processor to the pipeline.
358
+ * Returns a NEW Logger — original is immutable.
359
+ */
360
+ withProcessor(e2) {
361
+ return new g2(
362
+ [...this.processors, e2],
363
+ this.resource,
364
+ this.baseAttributes
365
+ );
366
+ }
367
+ /**
368
+ * Force-flush all pending records in all processors.
369
+ * Await before the end of a CF Worker request (inside ctx.waitUntil).
370
+ */
371
+ async flush() {
372
+ await Promise.all(this.processors.map((e2) => e2.forceFlush()));
373
+ }
374
+ /**
375
+ * Shutdown: flush + release resources. No logging after shutdown.
376
+ */
377
+ async shutdown() {
378
+ await this.flush(), await Promise.all(this.processors.map((e2) => e2.shutdown()));
379
+ }
380
+ // ── Internal ────────────────────────────────────────────────────────────
381
+ emit(e2, s, r, o2) {
382
+ if (this.processors.length === 0) return;
383
+ const n2 = (/* @__PURE__ */ new Date()).toISOString(), l2 = {
384
+ severityNumber: e2,
385
+ severityText: S[e2],
386
+ body: s,
387
+ timestamp: n2,
388
+ observedTimestamp: n2,
389
+ attributes: { ...this.baseAttributes, ...o2 },
390
+ caller: N(),
391
+ ...r ? { error: r } : {},
392
+ resource: { ...this.resource }
393
+ };
394
+ for (const m2 of this.processors)
395
+ try {
396
+ m2.onEmit(l2);
397
+ } catch (f2) {
398
+ console.error(
399
+ "[telemetry] Processor threw in onEmit:",
400
+ f2
401
+ );
402
+ }
403
+ }
404
+ }
405
+ class T {
406
+ constructor(e2) {
407
+ this.shutdownFlag = false, this.pendingExport = Promise.resolve(), this.adapter = e2;
408
+ }
409
+ onEmit(e2) {
410
+ this.shutdownFlag || (this.pendingExport = this.pendingExport.then(() => this.adapter.export([e2])).catch((s) => {
411
+ console.error(
412
+ `[telemetry] SimpleLogProcessor: adapter "${this.adapter.name}" export failed:`,
413
+ s
414
+ );
415
+ }));
416
+ }
417
+ async forceFlush() {
418
+ await this.pendingExport;
419
+ }
420
+ async shutdown() {
421
+ this.shutdownFlag = true, await this.forceFlush(), await this.adapter.shutdown?.();
422
+ }
423
+ }
424
+ class M {
425
+ constructor(e2) {
426
+ this.processors = [], this.resource = {
427
+ serviceName: e2?.serviceName ?? "unknown",
428
+ environment: e2?.environment,
429
+ version: e2?.version,
430
+ processName: e2?.processName
431
+ };
432
+ }
433
+ /**
434
+ * Register an adapter via a SimpleLogProcessor (immediate export).
435
+ * For batching, use addProcessor(new BatchLogProcessor(adapter, opts)).
436
+ */
437
+ addAdapter(e2) {
438
+ return this.addProcessor(new T(e2));
439
+ }
440
+ /**
441
+ * Register a custom processor (SimpleLogProcessor, BatchLogProcessor,
442
+ * or your own implementation).
443
+ */
444
+ addProcessor(e2) {
445
+ return this.processors.push(e2), this;
446
+ }
447
+ /**
448
+ * Get a named Logger instance.
449
+ * The logger inherits the provider's resource and processors.
450
+ * Optionally provide initial context attributes.
451
+ */
452
+ getLogger(e2, s) {
453
+ const r = {
454
+ ...this.resource,
455
+ ...e2 ? { processName: e2 } : {}
456
+ };
457
+ return new g2(this.processors, r, s);
458
+ }
459
+ /**
460
+ * Force-flush all registered processors.
461
+ */
462
+ async flush() {
463
+ await Promise.all(this.processors.map((e2) => e2.forceFlush()));
464
+ }
465
+ /**
466
+ * Shutdown: flush + release all processor resources.
467
+ */
468
+ async shutdown() {
469
+ await this.flush(), await Promise.all(this.processors.map((e2) => e2.shutdown()));
470
+ }
471
+ }
472
+ function U(t) {
473
+ const e2 = t?.level ?? h2.DEBUG, s = t?.json ?? false;
474
+ return {
475
+ name: "console",
476
+ async export(r) {
477
+ for (const o2 of r)
478
+ o2.severityNumber < e2 || (s ? I(o2) : R2(o2));
479
+ }
480
+ };
481
+ }
482
+ function R2(t) {
483
+ const e2 = F(t.severityNumber), s = t.timestamp, r = t.severityText, o2 = t.caller ? ` (${t.caller.file}:${t.caller.line})` : "", n2 = Object.keys(t.attributes).length > 0;
484
+ !!t.error ? e2(
485
+ `[${s}] [${r}]${o2} ${t.body}`,
486
+ t.attributes,
487
+ t.error
488
+ ) : n2 ? e2(`[${s}] [${r}]${o2} ${t.body}`, t.attributes) : e2(`[${s}] [${r}]${o2} ${t.body}`);
489
+ }
490
+ function I(t) {
491
+ const e2 = {
492
+ timestamp: t.timestamp,
493
+ level: t.severityText,
494
+ message: t.body,
495
+ service: t.resource.serviceName,
496
+ environment: t.resource.environment,
497
+ attributes: t.attributes,
498
+ caller: t.caller
499
+ };
500
+ t.error && (e2.error = {
501
+ name: t.error.name,
502
+ message: t.error.message,
503
+ stack: t.error.stack
504
+ }), console.log(JSON.stringify(e2));
505
+ }
506
+ function F(t) {
507
+ switch (t) {
508
+ case h2.ERROR:
509
+ return console.error.bind(console);
510
+ case h2.WARN:
511
+ return console.warn.bind(console);
512
+ case h2.INFO:
513
+ return console.log.bind(console);
514
+ case h2.DEBUG:
515
+ default:
516
+ return console.debug.bind(console);
517
+ }
518
+ }
519
+ const defaultLoggers = /* @__PURE__ */ new Map();
520
+ function getDefaultLogger(serviceName) {
521
+ let log = defaultLoggers.get(serviceName);
522
+ if (!log) {
523
+ const provider = new M({ serviceName });
524
+ provider.addAdapter(U({ level: h2.WARN }));
525
+ log = provider.getLogger();
526
+ defaultLoggers.set(serviceName, log);
527
+ }
528
+ return log;
529
+ }
530
+ function createParseContext(services) {
531
+ const metadata = {};
532
+ return {
533
+ get preserveRawHTML() {
534
+ return services.preserveRawHTML;
535
+ },
536
+ get errorRecovery() {
537
+ return services.errorRecovery;
538
+ },
539
+ get maxRecursionDepth() {
540
+ return services.maxRecursionDepth;
541
+ },
542
+ processImagePath: (src) => services.processImagePath(src),
543
+ processInlineFormatting: (text) => services.processInlineFormatting(text),
544
+ processSlots: (text) => services.processSlots(text),
545
+ processRawHTML: (html) => services.processRawHTML(html),
546
+ parseTokens: (tokens, depth) => services.parseTokens(tokens, depth),
547
+ reportUnhandled: (type, token) => {
548
+ services.onUnhandledToken?.(type, token);
549
+ },
550
+ metadata
551
+ };
552
+ }
553
+ class ContainerBlockPreprocessor {
554
+ constructor() {
555
+ this.name = "container-blocks";
556
+ }
557
+ process(markdown) {
558
+ return markdown.replace(/^:::(?:(\w+(?:[.#][\w-]+)*)\s*)?$/gm, (_match, specifier) => {
559
+ if (!specifier) {
560
+ return "<!-- /md-container -->";
561
+ }
562
+ const normalized = specifier.match(/^\w/) ? specifier : `div${specifier}`;
563
+ return `<!-- md-container:${normalized} -->`;
564
+ });
565
+ }
566
+ }
567
+ class CompositePreprocessor {
568
+ constructor(processors) {
569
+ this.name = "composite";
570
+ this.processors = [];
571
+ if (processors) {
572
+ this.processors = [...processors];
573
+ }
574
+ }
575
+ /** Add a preprocessor to the chain. Returns `this` for fluent API. */
576
+ add(processor) {
577
+ this.processors.push(processor);
578
+ return this;
579
+ }
580
+ /** Remove a preprocessor by name. */
581
+ remove(name) {
582
+ this.processors = this.processors.filter((p) => p.name !== name);
583
+ }
584
+ /** Get the list of registered preprocessors. */
585
+ getProcessors() {
586
+ return [...this.processors];
587
+ }
588
+ process(markdown) {
589
+ let result = markdown;
590
+ for (const processor of this.processors) {
591
+ result = processor.process(result);
592
+ }
593
+ return result;
594
+ }
595
+ }
596
+ function createDefaultPreprocessor() {
597
+ return new CompositePreprocessor([
598
+ new ContainerBlockPreprocessor()
599
+ ]);
600
+ }
601
+ class ContainerBlockPostprocessor {
602
+ constructor() {
603
+ this.name = "container-blocks";
604
+ }
605
+ process(tokens) {
606
+ const result = [];
607
+ const stack = [];
608
+ for (const token of tokens) {
609
+ const t = token;
610
+ if (t.type === "html") {
611
+ const raw = t.raw.trim();
612
+ const openMatch = raw.match(/^<!--\s*md-container:\s*(\S+)\s*-->$/);
613
+ const closeMatch = raw.match(/^<!--\s*\/md-container\s*-->$/);
614
+ if (openMatch) {
615
+ stack.push({
616
+ specifier: openMatch[1],
617
+ tokens: []
618
+ });
619
+ continue;
620
+ }
621
+ if (closeMatch) {
622
+ if (stack.length === 0) {
623
+ continue;
624
+ }
625
+ const container = stack.pop();
626
+ const processedInner = this.process(container.tokens);
627
+ const containerToken = {
628
+ type: "containerBlock",
629
+ specifier: container.specifier,
630
+ tokens: processedInner
631
+ };
632
+ if (stack.length > 0) {
633
+ stack[stack.length - 1].tokens.push(containerToken);
634
+ } else {
635
+ result.push(containerToken);
636
+ }
637
+ continue;
638
+ }
639
+ }
640
+ if (stack.length > 0) {
641
+ stack[stack.length - 1].tokens.push(token);
642
+ } else {
643
+ result.push(token);
644
+ }
645
+ }
646
+ return result;
647
+ }
648
+ }
649
+ class CompositeTokenPostprocessor {
650
+ constructor(processors) {
651
+ this.name = "composite";
652
+ this.processors = [];
653
+ if (processors) {
654
+ this.processors = [...processors];
655
+ }
656
+ }
657
+ /** Add a postprocessor to the chain. Returns `this` for fluent API. */
658
+ add(processor) {
659
+ this.processors.push(processor);
660
+ return this;
661
+ }
662
+ /** Remove a postprocessor by name. */
663
+ remove(name) {
664
+ this.processors = this.processors.filter((p) => p.name !== name);
665
+ }
666
+ /** Get the list of registered postprocessors. */
667
+ getProcessors() {
668
+ return [...this.processors];
669
+ }
670
+ process(tokens) {
671
+ let result = tokens;
672
+ for (const processor of this.processors) {
673
+ result = processor.process(result);
674
+ }
675
+ return result;
676
+ }
677
+ }
678
+ function createDefaultPostprocessor() {
679
+ return new CompositeTokenPostprocessor([
680
+ new ContainerBlockPostprocessor()
681
+ ]);
682
+ }
332
683
  const DEFAULT_SLOT_PATTERN = /\[\[(.*?)\]\]/g;
333
684
  class MarkdownParser {
334
685
  constructor(options) {
@@ -337,6 +688,7 @@ class MarkdownParser {
337
688
  this.preserveRawHTML = options?.preserveRawHTML ?? false;
338
689
  this.slotPattern = options?.slotPattern ?? DEFAULT_SLOT_PATTERN;
339
690
  this.onSlot = options?.onSlot;
691
+ this.log = options?.logger ?? getDefaultLogger("md2html");
340
692
  this.errorRecovery = options?.errorRecovery ?? "throw";
341
693
  this.maxRecursionDepth = options?.maxRecursionDepth ?? 100;
342
694
  this.allowedHTMLTags = /* @__PURE__ */ new Set([
@@ -346,11 +698,21 @@ class MarkdownParser {
346
698
  this.allowedAttributes = options?.allowedAttributes ?? {};
347
699
  this.handlerRegistry = new TokenHandlerRegistry();
348
700
  this.onUnhandledToken = options?.onUnhandledToken;
701
+ this.preprocessor = createDefaultPreprocessor();
702
+ this.postprocessor = createDefaultPostprocessor();
349
703
  }
350
704
  /** Access the handler registry for customization. */
351
705
  get handlers() {
352
706
  return this.handlerRegistry;
353
707
  }
708
+ /** Access the preprocessor chain for customization. */
709
+ get preprocessors() {
710
+ return this.preprocessor;
711
+ }
712
+ /** Access the token postprocessor chain for customization. */
713
+ get postprocessors() {
714
+ return this.postprocessor;
715
+ }
354
716
  processImagePath(src) {
355
717
  if (src.startsWith("http") || src.startsWith("/")) {
356
718
  return src;
@@ -445,31 +807,20 @@ class MarkdownParser {
445
807
  return html;
446
808
  }
447
809
  /**
448
- * Build the ParseContext that is passed to every token handler.
449
- * This is the bridge between the parser's private services and the handlers.
810
+ * Build a ParserServices object that bridges the parser's private methods
811
+ * to the ParseContext factory. This keeps context creation decoupled.
450
812
  */
451
- createContext() {
452
- const self = this;
453
- const metadata = {};
813
+ buildServices() {
454
814
  return {
455
- get preserveRawHTML() {
456
- return self.preserveRawHTML;
457
- },
458
- get errorRecovery() {
459
- return self.errorRecovery;
460
- },
461
- get maxRecursionDepth() {
462
- return self.maxRecursionDepth;
463
- },
464
- processImagePath: (src) => self.processImagePath(src),
465
- processInlineFormatting: (text) => self.processInlineFormatting(text),
466
- processSlots: (text) => self.processSlots(text),
467
- processRawHTML: (html) => self.processRawHTML(html),
468
- parseTokens: (tokens, depth) => self.parseTokens(tokens, depth),
469
- reportUnhandled: (type, token) => {
470
- self.onUnhandledToken?.(type, token);
471
- },
472
- metadata
815
+ preserveRawHTML: this.preserveRawHTML,
816
+ errorRecovery: this.errorRecovery,
817
+ maxRecursionDepth: this.maxRecursionDepth,
818
+ processImagePath: (src) => this.processImagePath(src),
819
+ processInlineFormatting: (text) => this.processInlineFormatting(text),
820
+ processSlots: (text) => this.processSlots(text),
821
+ processRawHTML: (html) => this.processRawHTML(html),
822
+ parseTokens: (tokens, depth) => this.parseTokens(tokens, depth),
823
+ onUnhandledToken: this.onUnhandledToken
473
824
  };
474
825
  }
475
826
  /**
@@ -481,12 +832,12 @@ class MarkdownParser {
481
832
  if (depth > this.maxRecursionDepth) {
482
833
  const msg = `[md2html] Max recursion depth (${this.maxRecursionDepth}) exceeded, truncating`;
483
834
  if (this.errorRecovery === "warn") {
484
- console.warn(msg);
835
+ this.log.warn(msg);
485
836
  }
486
837
  return [];
487
838
  }
488
839
  const nodes = [];
489
- const ctx = sharedCtx || this.createContext();
840
+ const ctx = sharedCtx || createParseContext(this.buildServices());
490
841
  for (const token of tokens) {
491
842
  const typedToken = token;
492
843
  const handler = this.handlerRegistry.get(typedToken.type);
@@ -497,82 +848,6 @@ class MarkdownParser {
497
848
  }
498
849
  return nodes;
499
850
  }
500
- /**
501
- * Pre-process markdown: convert `:::tag#id.class` container syntax
502
- * into HTML comment markers that marked will preserve as html tokens,
503
- * but won't affect markdown parsing of the inner content.
504
- *
505
- * Example:
506
- * :::section#header
507
- * # Heading inside container
508
- * Some text
509
- * :::
510
- *
511
- * Becomes:
512
- * <!-- md-container:section#header -->
513
- * # Heading inside container
514
- * Some text
515
- * <!-- /md-container -->
516
- */
517
- preprocessContainerBlocks(markdown) {
518
- return markdown.replace(/^:::(?:(\w+(?:[.#][\w-]+)*)\s*)?$/gm, (match, specifier) => {
519
- if (!specifier) {
520
- return "<!-- /md-container -->";
521
- }
522
- const normalized = specifier.match(/^\w/) ? specifier : `div${specifier}`;
523
- return `<!-- md-container:${normalized} -->`;
524
- });
525
- }
526
- /**
527
- * Post-process marked tokens to collapse container block markers
528
- * into structured containerBlock tokens with proper nesting.
529
- *
530
- * This handles nesting depth up to maxRecursionDepth.
531
- */
532
- postprocessTokens(tokens) {
533
- const result = [];
534
- const stack = [];
535
- for (const token of tokens) {
536
- const t = token;
537
- if (t.type === "html") {
538
- const raw = t.raw.trim();
539
- const openMatch = raw.match(/^<!--\s*md-container:\s*(\S+)\s*-->$/);
540
- const closeMatch = raw.match(/^<!--\s*\/md-container\s*-->$/);
541
- if (openMatch) {
542
- const newContainer = {
543
- specifier: openMatch[1],
544
- tokens: []
545
- };
546
- stack.push(newContainer);
547
- continue;
548
- }
549
- if (closeMatch) {
550
- if (stack.length === 0) {
551
- continue;
552
- }
553
- const container = stack.pop();
554
- const processedInner = this.postprocessTokens(container.tokens);
555
- const containerToken = {
556
- type: "containerBlock",
557
- specifier: container.specifier,
558
- tokens: processedInner
559
- };
560
- if (stack.length > 0) {
561
- stack[stack.length - 1].tokens.push(containerToken);
562
- } else {
563
- result.push(containerToken);
564
- }
565
- continue;
566
- }
567
- }
568
- if (stack.length > 0) {
569
- stack[stack.length - 1].tokens.push(token);
570
- } else {
571
- result.push(token);
572
- }
573
- }
574
- return result;
575
- }
576
851
  parse(markdown, options) {
577
852
  const parseOptions = {
578
853
  gfm: options?.gfm ?? true,
@@ -580,10 +855,10 @@ class MarkdownParser {
580
855
  pedantic: options?.pedantic ?? false
581
856
  };
582
857
  try {
583
- const processed = this.preprocessContainerBlocks(markdown);
858
+ const processed = this.preprocessor.process(markdown);
584
859
  const rawTokens = marked.lexer(processed, parseOptions);
585
- const tokens = this.postprocessTokens(rawTokens);
586
- const ctx = this.createContext();
860
+ const tokens = this.postprocessor.process(rawTokens);
861
+ const ctx = createParseContext(this.buildServices());
587
862
  const content = this.parseTokens(tokens, 0, ctx);
588
863
  return {
589
864
  title: "",
@@ -594,7 +869,7 @@ class MarkdownParser {
594
869
  if (this.errorRecovery === "throw") throw err;
595
870
  const msg = `[md2html] Parse error: ${err instanceof Error ? err.message : String(err)}`;
596
871
  if (this.errorRecovery === "warn") {
597
- console.warn(msg);
872
+ this.log.warn(msg);
598
873
  }
599
874
  return {
600
875
  title: "",
@@ -606,296 +881,9 @@ class MarkdownParser {
606
881
  return this.parse(markdown, options).content;
607
882
  }
608
883
  }
609
- class HeadingRendererStrategy {
610
- constructor() {
611
- this.type = "heading";
612
- }
613
- render(node, _renderChild, ctx) {
614
- const level = node.attributes?.level || "2";
615
- const headingId = ctx.addHeadingIds ? ` id="${ctx.generateHeadingId(node.content)}"` : "";
616
- const scopeAttr = ctx.getScopeAttr(node);
617
- if (!ctx.hasClassConfig()) {
618
- return `<h${level}${headingId}${scopeAttr}>${node.content || ""}</h${level}>`;
619
- }
620
- const prefix = ctx.classPrefix;
621
- const levelClass = level === "1" ? "h1" : level === "2" ? "h2" : level === "3" ? "h3" : level === "4" ? "h4" : level === "5" ? "h5" : "h6";
622
- const headingClass = prefix ? `${prefix}${levelClass}` : levelClass;
623
- return `<h${level}${headingId}${scopeAttr} class="${headingClass}">${node.content || ""}</h${level}>`;
624
- }
625
- }
626
- class ParagraphRendererStrategy {
627
- constructor() {
628
- this.type = "paragraph";
629
- }
630
- render(node, renderChild, ctx) {
631
- const scopeAttr = ctx.getScopeAttr(node);
632
- if (node.children) {
633
- const childrenHtml = node.children.map(renderChild).join("");
634
- return ctx.hasClassConfig() && ctx.classPrefix ? `<p class="${ctx.classPrefix}paragraph"${scopeAttr}>${childrenHtml}</p>` : `<p${scopeAttr}>${childrenHtml}</p>`;
635
- }
636
- return ctx.hasClassConfig() && ctx.classPrefix ? `<p class="${ctx.classPrefix}paragraph"${scopeAttr}>${node.content || ""}</p>` : `<p${scopeAttr}>${node.content || ""}</p>`;
637
- }
638
- }
639
- class ListRendererStrategy {
640
- constructor() {
641
- this.type = "list";
642
- }
643
- render(node, renderChild, ctx) {
644
- const tag = node.ordered ? "ol" : "ul";
645
- const items = node.children?.map(renderChild).join("") || "";
646
- const scopeAttr = ctx.getScopeAttr(node);
647
- return ctx.hasClassConfig() && ctx.classPrefix ? `<${tag} class="${ctx.classPrefix}list"${scopeAttr}>${items}</${tag}>` : `<${tag}${scopeAttr}>${items}</${tag}>`;
648
- }
649
- }
650
- class ListItemRendererStrategy {
651
- constructor() {
652
- this.type = "list-item";
653
- }
654
- render(node, _renderChild, ctx) {
655
- const scopeAttr = ctx.getScopeAttr(node);
656
- return ctx.hasClassConfig() && ctx.classPrefix ? `<li class="${ctx.classPrefix}list-item"${scopeAttr}>${node.content || ""}</li>` : `<li${scopeAttr}>${node.content || ""}</li>`;
657
- }
658
- }
659
- class ImageRendererStrategy {
660
- constructor() {
661
- this.type = "image";
662
- }
663
- render(node, _renderChild, ctx) {
664
- const src = node.src || node.attributes?.src || "";
665
- const alt = node.alt || node.attributes?.alt || "";
666
- const scopeAttr = ctx.getScopeAttr(node);
667
- let classStr = "";
668
- if (ctx.hasClassConfig()) {
669
- const prefix = ctx.classPrefix;
670
- classStr = prefix ? `${prefix}image` : "image";
671
- if (node.className) classStr += ` ${node.className}`;
672
- return `<img src="${src}" alt="${alt}" class="${classStr}"${scopeAttr}>`;
673
- }
674
- if (node.className) {
675
- return `<img src="${src}" alt="${alt}" class="${node.className}"${scopeAttr}>`;
676
- }
677
- return `<img src="${src}" alt="${alt}"${scopeAttr}>`;
678
- }
679
- }
680
- class CodeRendererStrategy {
681
- constructor() {
682
- this.type = "code";
683
- }
684
- render(node, _renderChild, ctx) {
685
- const scopeAttr = ctx.getScopeAttr(node);
686
- const lang = node.attributes?.lang || "";
687
- if (ctx.hasClassConfig()) {
688
- const prefix = ctx.classPrefix;
689
- const codeClass = prefix ? `${prefix}code` : "code";
690
- return `<pre${scopeAttr}><code class="${codeClass} language-${lang}">${node.content || ""}</code></pre>`;
691
- }
692
- return `<pre${scopeAttr}><code class="language-${lang}">${node.content || ""}</code></pre>`;
693
- }
694
- }
695
- class ContainerRendererStrategy {
696
- constructor() {
697
- this.type = "container";
698
- }
699
- render(node, renderChild, ctx) {
700
- if (node.rawHTML) {
701
- return node.rawHTML;
702
- }
703
- const tag = node.attributes?.tag || "div";
704
- const children = node.children?.map(renderChild).join("") || "";
705
- const id = node.attributes?.id;
706
- const idAttr = id ? ` id="${id}"` : "";
707
- const scopeAttr = ctx.getScopeAttr(node);
708
- if (tag === "hr") return "<hr>";
709
- if (ctx.hasClassConfig()) {
710
- const containerClass = ctx.getContainerClass(tag);
711
- const prefix = ctx.classPrefix;
712
- if (prefix) {
713
- const classes2 = [prefix + (containerClass || "container")];
714
- if (node.className) classes2.push(node.className);
715
- return `<${tag} class="${classes2.join(" ")}"${idAttr}${scopeAttr}>${children}</${tag}>`;
716
- }
717
- const classes = [containerClass || "container"];
718
- if (node.className) classes.push(node.className);
719
- return `<${tag} class="${classes.join(" ")}"${idAttr}${scopeAttr}>${children}</${tag}>`;
720
- }
721
- if (node.className) {
722
- return `<${tag} class="${node.className}"${idAttr}${scopeAttr}>${children}</${tag}>`;
723
- }
724
- return `<${tag}${idAttr}${scopeAttr}>${children}</${tag}>`;
725
- }
726
- }
727
- class StrongRendererStrategy {
728
- constructor() {
729
- this.type = "strong";
730
- }
731
- render(node, _renderChild, ctx) {
732
- return `<strong${ctx.getScopeAttr(node)}>${node.content || ""}</strong>`;
733
- }
734
- }
735
- class EmphasisRendererStrategy {
736
- constructor() {
737
- this.type = "emphasis";
738
- }
739
- render(node, _renderChild, ctx) {
740
- return `<em${ctx.getScopeAttr(node)}>${node.content || ""}</em>`;
741
- }
742
- }
743
- class LinkRendererStrategy {
744
- constructor() {
745
- this.type = "link";
746
- }
747
- render(node, _renderChild, ctx) {
748
- const href = node.attributes?.href || "";
749
- return `<a href="${href}"${ctx.getScopeAttr(node)}>${node.content || ""}</a>`;
750
- }
751
- }
752
- class TextRendererStrategy {
753
- constructor() {
754
- this.type = "text";
755
- }
756
- render(node, _renderChild, _ctx) {
757
- return node.content || "";
758
- }
759
- }
760
- class RendererStrategyRegistry {
761
- constructor() {
762
- this.strategies = /* @__PURE__ */ new Map();
763
- this.register(new HeadingRendererStrategy());
764
- this.register(new ParagraphRendererStrategy());
765
- this.register(new ListRendererStrategy());
766
- this.register(new ListItemRendererStrategy());
767
- this.register(new ImageRendererStrategy());
768
- this.register(new CodeRendererStrategy());
769
- this.register(new ContainerRendererStrategy());
770
- this.register(new StrongRendererStrategy());
771
- this.register(new EmphasisRendererStrategy());
772
- this.register(new LinkRendererStrategy());
773
- this.register(new TextRendererStrategy());
774
- this.fallback = new TextRendererStrategy();
775
- }
776
- /** Register a strategy for a node type. Overrides any existing strategy. */
777
- register(strategy) {
778
- this.strategies.set(strategy.type, strategy);
779
- }
780
- /** Unregister a strategy by node type. */
781
- unregister(type) {
782
- this.strategies.delete(type);
783
- }
784
- /** Get a strategy for the given node type, falling back to catch-all. */
785
- get(type) {
786
- return this.strategies.get(type) ?? this.fallback;
787
- }
788
- /** Check if a dedicated strategy exists for the given node type. */
789
- has(type) {
790
- return this.strategies.has(type);
791
- }
792
- /** Get all registered dedicated strategy types. */
793
- get types() {
794
- return Array.from(this.strategies.keys());
795
- }
796
- /** Replace the fallback strategy. */
797
- setFallback(strategy) {
798
- this.fallback = strategy;
799
- }
800
- }
801
- class HTMLRenderer {
802
- constructor(config = {}) {
803
- this.config = {
804
- classPrefix: config.classPrefix || "",
805
- customCSS: config.customCSS || "",
806
- addHeadingIds: config.addHeadingIds ?? false,
807
- emitScopeAnchors: config.emitScopeAnchors ?? false
808
- };
809
- this.strategyRegistry = new RendererStrategyRegistry();
810
- }
811
- /** Access the strategy registry for customization. */
812
- get strategies() {
813
- return this.strategyRegistry;
814
- }
815
- hasClassConfig() {
816
- return this.config.classPrefix !== "" || this.config.addHeadingIds;
817
- }
818
- getClass(baseClass, nodeClass) {
819
- if (!this.hasClassConfig()) {
820
- return nodeClass || "";
821
- }
822
- const prefix = this.config.classPrefix;
823
- const classes = [prefix ? `${prefix}${baseClass}` : baseClass];
824
- if (nodeClass) classes.push(nodeClass);
825
- return classes.join(" ");
826
- }
827
- generateHeadingId(content) {
828
- if (!content) return "";
829
- return content.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
830
- }
831
- /**
832
- * Get the scope attribute string for a node type.
833
- * Returns empty string if emitScopeAnchors is disabled.
834
- */
835
- getScopeAttr(node) {
836
- if (!this.config.emitScopeAnchors) return "";
837
- const scopeValue = node.scope || nodeTypeToScope[node.type] || "container";
838
- return ` data-md-scope="${scopeValue}"`;
839
- }
840
- /**
841
- * Get the CSS class for a container's tag-based rendering.
842
- * Returns just the tag name since renderWithClass applies the prefix.
843
- */
844
- getContainerClass(tag) {
845
- if (!this.hasClassConfig()) return "";
846
- return tag;
847
- }
848
- buildRenderContext() {
849
- const self = this;
850
- return {
851
- get classPrefix() {
852
- return self.config.classPrefix;
853
- },
854
- get addHeadingIds() {
855
- return self.config.addHeadingIds;
856
- },
857
- get emitScopeAnchors() {
858
- return self.config.emitScopeAnchors;
859
- },
860
- get customCSS() {
861
- return self.config.customCSS;
862
- },
863
- hasClassConfig: () => self.hasClassConfig(),
864
- getClass: (baseClass, nodeClass) => self.getClass(baseClass, nodeClass),
865
- getScopeAttr: (node) => self.getScopeAttr(node),
866
- generateHeadingId: (content) => self.generateHeadingId(content),
867
- getContainerClass: (tag) => self.getContainerClass(tag)
868
- };
869
- }
870
- renderNode(node) {
871
- const ctx = this.buildRenderContext();
872
- const strategy = this.strategyRegistry.get(node.type);
873
- return strategy.render(node, (child) => this.renderNode(child), ctx);
874
- }
875
- renderNodes(nodes) {
876
- if (!nodes || nodes.length === 0) {
877
- return "";
878
- }
879
- if (this.config.emitScopeAnchors) {
880
- const inner = nodes.map((node) => this.renderNode(node)).join("\n");
881
- return `<div data-md-scope="root">
882
- ${inner}
883
- </div>`;
884
- }
885
- return nodes.map((node) => this.renderNode(node)).join("\n");
886
- }
887
- renderToHTMLString(nodes) {
888
- return this.renderNodes(nodes);
889
- }
890
- render(markdown) {
891
- return markdown;
892
- }
893
- getCustomCSS() {
894
- return this.config.customCSS;
895
- }
896
- }
897
884
  class MarkdownPipeline {
898
885
  constructor(config = {}) {
886
+ this.log = config.logger ?? getDefaultLogger("md2html");
899
887
  this.config = {
900
888
  imagePathPrefix: config.imagePathPrefix || "",
901
889
  imageBaseUrl: config.imageBaseUrl || "",
@@ -941,8 +929,16 @@ class MarkdownPipeline {
941
929
  return this.renderer.renderNodes(nodes);
942
930
  }
943
931
  renderMarkdown(markdown) {
944
- const nodes = this.parse(markdown);
945
- return this.render(nodes);
932
+ try {
933
+ const nodes = this.parse(markdown);
934
+ return this.render(nodes);
935
+ } catch (err) {
936
+ this.log.error("Markdown render failed", err, {
937
+ length: markdown.length,
938
+ recovery: this.config.errorRecovery
939
+ });
940
+ throw err;
941
+ }
946
942
  }
947
943
  renderPage(title, nodes, options) {
948
944
  const html = this.render(nodes);
@@ -1087,7 +1083,11 @@ export {
1087
1083
  BlockquoteHandler,
1088
1084
  CatchAllHandler,
1089
1085
  CodeHandler,
1086
+ CompositePreprocessor,
1087
+ CompositeTokenPostprocessor,
1090
1088
  ContainerBlockHandler,
1089
+ ContainerBlockPostprocessor,
1090
+ ContainerBlockPreprocessor,
1091
1091
  FrontmatterHandler,
1092
1092
  HTMLRenderer,
1093
1093
  HeadingHandler,
@@ -1096,18 +1096,35 @@ export {
1096
1096
  ImageHandler,
1097
1097
  LinkHandler,
1098
1098
  ListHandler,
1099
+ L as LitCodeStrategy,
1100
+ a as LitContainerStrategy,
1101
+ b as LitEmphasisStrategy,
1102
+ c as LitFallbackStrategy,
1103
+ e as LitHeadingStrategy,
1104
+ f as LitImageStrategy,
1105
+ g as LitLinkStrategy,
1106
+ h as LitListItemStrategy,
1107
+ i as LitListStrategy,
1108
+ j as LitParagraphStrategy,
1109
+ k as LitRenderer,
1110
+ l as LitStrategyRegistry,
1111
+ m as LitStrongStrategy,
1112
+ n as LitTextStrategy,
1099
1113
  MarkdownParser,
1100
1114
  MarkdownPipeline,
1101
1115
  NodeFactory,
1102
1116
  ParagraphHandler,
1103
- RendererStrategyRegistry,
1117
+ R as RendererStrategyRegistry,
1104
1118
  TokenHandlerRegistry,
1105
1119
  collectByType,
1106
1120
  collectFromTree,
1107
1121
  collectHeadings,
1108
1122
  collectImages,
1123
+ createDefaultPostprocessor,
1124
+ createDefaultPreprocessor,
1125
+ createParseContext,
1109
1126
  defaultAllowedHTMLTags,
1110
- nodeTypeToScope,
1127
+ o as nodeTypeToScope,
1111
1128
  walkTree
1112
1129
  };
1113
1130
  //# sourceMappingURL=index.js.map