@leadertechie/md2html 0.1.0-alpha.0 → 0.1.0-alpha.10

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/README.md CHANGED
@@ -14,9 +14,11 @@ A configuration-driven markdown to HTML pipeline that parses markdown to an AST
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
- npm install @leadertechie/md2html
17
+ npm install @leadertechie/md2html lit
18
18
  ```
19
19
 
20
+ > Note: `lit` is a peer dependency and required for rendering Lit templates.
21
+
20
22
  ## Usage
21
23
 
22
24
  ### Basic Usage
@@ -55,10 +57,35 @@ const pipeline = new MarkdownPipeline({
55
57
  gfm: true,
56
58
  breaks: false,
57
59
  pedantic: false
60
+ },
61
+ styleOptions: {
62
+ classPrefix: 'md-',
63
+ customCSS: 'body { font-family: system-ui; }',
64
+ addHeadingIds: true
58
65
  }
59
66
  });
60
67
  ```
61
68
 
69
+ ### Style Configuration Options
70
+
71
+ | Option | Type | Default | Description |
72
+ |--------|------|---------|-------------|
73
+ | `classPrefix` | string | `''` | Prefix for CSS classes on elements (e.g., `'md-'` produces `md-heading`, `md-paragraph`) |
74
+ | `customCSS` | string | `''` | Custom CSS string to inject (use `pipeline.getCustomCSS()` to retrieve) |
75
+ | `addHeadingIds` | boolean | `false` | Add ID attributes to headings based on their content for anchor links |
76
+
77
+ When `classPrefix` or `addHeadingIds` is set, CSS classes will be added to elements:
78
+ - `heading`, `paragraph`, `list`, `list-item`, `image`, `code`, `container`, `blockquote`
79
+
80
+ Example output with `classPrefix: 'md-'` and `addHeadingIds: true`:
81
+ ```html
82
+ <h1 id="hello-world" class="md-heading">Hello World</h1>
83
+ <p class="md-paragraph">This is a paragraph.</p>
84
+ <ul class="md-list">
85
+ <li class="md-list-item">Item 1</li>
86
+ </ul>
87
+ ```
88
+
62
89
  ### API
63
90
 
64
91
  | Method | Description |
@@ -66,7 +93,9 @@ const pipeline = new MarkdownPipeline({
66
93
  | `parse(markdown)` | Parse markdown string to AST |
67
94
  | `render(nodes)` | Render AST to HTML string |
68
95
  | `renderMarkdown(markdown)` | Parse and render in one call |
69
- | `renderPage(title, nodes)` | Render AST to full HTML page |
96
+ | `renderPage(title, nodes, options?)` | Render AST to full HTML page |
97
+ | `getCustomCSS()` | Get custom CSS string from style config |
98
+ | `getConfig()` | Get current pipeline configuration |
70
99
 
71
100
  ## License
72
101
 
@@ -0,0 +1,85 @@
1
+ export declare interface ContentNode {
2
+ type: ContentNodeType;
3
+ content?: string;
4
+ children?: ContentNode[];
5
+ attributes?: Record<string, unknown>;
6
+ className?: string;
7
+ src?: string;
8
+ alt?: string;
9
+ ordered?: boolean;
10
+ }
11
+
12
+ export declare type ContentNodeType = 'text' | 'heading' | 'paragraph' | 'list' | 'list-item' | 'image' | 'code' | 'container' | 'strong' | 'emphasis';
13
+
14
+ export declare class HTMLRenderer {
15
+ private config;
16
+ constructor(config?: StyleConfig);
17
+ private hasClassConfig;
18
+ private getClass;
19
+ private generateHeadingId;
20
+ private renderWithClass;
21
+ renderNode(node: ContentNode): string;
22
+ renderNodes(nodes: ContentNode[]): string;
23
+ renderToHTMLString(nodes: ContentNode[]): string;
24
+ render(markdown: string): string;
25
+ getCustomCSS(): string;
26
+ }
27
+
28
+ export declare interface MarkdownContent {
29
+ title: string;
30
+ metadata?: Record<string, unknown>;
31
+ content: ContentNode[];
32
+ }
33
+
34
+ export declare class MarkdownParser {
35
+ private imagePathPrefix;
36
+ private imageBaseUrl;
37
+ constructor(options?: {
38
+ imagePathPrefix?: string;
39
+ imageBaseUrl?: string;
40
+ });
41
+ private processImagePath;
42
+ private processInlineFormatting;
43
+ private parseTokens;
44
+ private parseToken;
45
+ parse(markdown: string, options?: ParseOptions): MarkdownContent;
46
+ parseToNodes(markdown: string, options?: ParseOptions): ContentNode[];
47
+ }
48
+
49
+ export declare class MarkdownPipeline {
50
+ private parser;
51
+ private renderer;
52
+ private config;
53
+ constructor(config?: PipelineConfig);
54
+ parse(markdown: string): ContentNode[];
55
+ parseWithMetadata(markdown: string): MarkdownContent;
56
+ render(nodes: ContentNode[]): string;
57
+ renderMarkdown(markdown: string): string;
58
+ renderPage(title: string, nodes: ContentNode[], options?: {
59
+ lang?: string;
60
+ charset?: string;
61
+ }): string;
62
+ getConfig(): Readonly<Required<PipelineConfig>>;
63
+ getCustomCSS(): string;
64
+ }
65
+
66
+ export declare interface ParseOptions {
67
+ gfm?: boolean;
68
+ breaks?: boolean;
69
+ pedantic?: boolean;
70
+ }
71
+
72
+ export declare interface PipelineConfig {
73
+ imagePathPrefix?: string;
74
+ imageBaseUrl?: string;
75
+ parseOptions?: ParseOptions;
76
+ styleOptions?: StyleConfig;
77
+ }
78
+
79
+ export declare interface StyleConfig {
80
+ classPrefix?: string;
81
+ customCSS?: string;
82
+ addHeadingIds?: boolean;
83
+ }
84
+
85
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,273 @@
1
+ import { marked } from "marked";
2
+ class MarkdownParser {
3
+ constructor(options) {
4
+ this.imagePathPrefix = options?.imagePathPrefix || "";
5
+ this.imageBaseUrl = options?.imageBaseUrl || "";
6
+ }
7
+ processImagePath(src) {
8
+ if (src.startsWith("http") || src.startsWith("/")) {
9
+ return src;
10
+ }
11
+ let path = this.imagePathPrefix ? `${this.imagePathPrefix}${src}` : src;
12
+ if (this.imageBaseUrl && !path.startsWith("http")) {
13
+ path = `${this.imageBaseUrl}${path}`;
14
+ }
15
+ return path;
16
+ }
17
+ processInlineFormatting(text) {
18
+ return text.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/\*(.+?)\*/g, "<em>$1</em>");
19
+ }
20
+ parseTokens(tokens) {
21
+ const nodes = [];
22
+ for (const token of tokens) {
23
+ const node = this.parseToken(token);
24
+ if (node) {
25
+ nodes.push(node);
26
+ }
27
+ }
28
+ return nodes;
29
+ }
30
+ parseToken(token) {
31
+ switch (token.type) {
32
+ case "heading":
33
+ return {
34
+ type: "heading",
35
+ content: token.text,
36
+ attributes: { level: String(token.depth) }
37
+ };
38
+ case "paragraph":
39
+ const tokens = token.tokens || [];
40
+ const hasInlineImage = tokens.some((t) => t.type === "image");
41
+ if (hasInlineImage) {
42
+ const children = tokens.map((t) => {
43
+ if (t.type === "image") {
44
+ return {
45
+ type: "image",
46
+ src: this.processImagePath(t.href),
47
+ alt: t.text || ""
48
+ };
49
+ }
50
+ return {
51
+ type: "text",
52
+ content: this.processInlineFormatting(t.text || "")
53
+ };
54
+ });
55
+ return {
56
+ type: "paragraph",
57
+ children
58
+ };
59
+ }
60
+ return {
61
+ type: "paragraph",
62
+ content: this.processInlineFormatting(token.text)
63
+ };
64
+ case "list":
65
+ return {
66
+ type: "list",
67
+ ordered: token.ordered,
68
+ children: token.items.map((item) => ({
69
+ type: "list-item",
70
+ content: this.processInlineFormatting(item.text)
71
+ }))
72
+ };
73
+ case "image":
74
+ return {
75
+ type: "image",
76
+ src: this.processImagePath(token.href),
77
+ alt: token.title || ""
78
+ };
79
+ case "code":
80
+ return {
81
+ type: "code",
82
+ content: token.text,
83
+ attributes: { lang: token.lang || "" }
84
+ };
85
+ case "hr":
86
+ return { type: "container", attributes: { tag: "hr" } };
87
+ case "blockquote":
88
+ return {
89
+ type: "container",
90
+ attributes: { tag: "blockquote" },
91
+ children: this.parseTokens(token.tokens || [])
92
+ };
93
+ case "html":
94
+ return { type: "container", content: token.raw };
95
+ default:
96
+ return null;
97
+ }
98
+ }
99
+ parse(markdown, options) {
100
+ const parseOptions = {
101
+ gfm: options?.gfm ?? true,
102
+ breaks: options?.breaks ?? false,
103
+ pedantic: options?.pedantic ?? false
104
+ };
105
+ const tokens = marked.lexer(markdown, parseOptions);
106
+ const content = this.parseTokens(tokens);
107
+ return {
108
+ title: "",
109
+ content
110
+ };
111
+ }
112
+ parseToNodes(markdown, options) {
113
+ return this.parse(markdown, options).content;
114
+ }
115
+ }
116
+ class HTMLRenderer {
117
+ constructor(config = {}) {
118
+ this.config = {
119
+ classPrefix: config.classPrefix || "",
120
+ customCSS: config.customCSS || "",
121
+ addHeadingIds: config.addHeadingIds ?? false
122
+ };
123
+ }
124
+ hasClassConfig() {
125
+ return this.config.classPrefix !== "" || this.config.addHeadingIds;
126
+ }
127
+ getClass(baseClass, nodeClass) {
128
+ if (!this.hasClassConfig()) {
129
+ return nodeClass || "";
130
+ }
131
+ const prefix = this.config.classPrefix;
132
+ const classes = [prefix ? `${prefix}${baseClass}` : baseClass];
133
+ if (nodeClass) classes.push(nodeClass);
134
+ return classes.join(" ");
135
+ }
136
+ generateHeadingId(content) {
137
+ if (!content) return "";
138
+ return content.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
139
+ }
140
+ renderWithClass(tag, content, baseClass, nodeClass, extraAttrs) {
141
+ const classAttr = this.hasClassConfig() && baseClass ? ` class="${this.getClass(baseClass, nodeClass)}"` : "";
142
+ return `<${tag}${classAttr}${extraAttrs || ""}>${content}</${tag}>`;
143
+ }
144
+ renderNode(node) {
145
+ switch (node.type) {
146
+ case "heading":
147
+ const level = node.attributes?.level || "2";
148
+ const headingId = this.config.addHeadingIds ? ` id="${this.generateHeadingId(node.content)}"` : "";
149
+ let headingClass = "";
150
+ if (this.hasClassConfig()) {
151
+ const prefix = this.config.classPrefix;
152
+ const levelClass = level === "1" ? "h1" : level === "2" ? "h2" : level === "3" ? "h3" : level === "4" ? "h4" : level === "5" ? "h5" : "h6";
153
+ headingClass = prefix ? `${prefix}${levelClass}` : levelClass;
154
+ }
155
+ if (!headingClass) {
156
+ return `<h${level}${headingId}>${node.content || ""}</h${level}>`;
157
+ }
158
+ return `<h${level}${headingId} class="${headingClass}">${node.content || ""}</h${level}>`;
159
+ case "paragraph":
160
+ if (node.children) {
161
+ const childrenHtml = node.children.map((child) => this.renderNode(child)).join("");
162
+ return this.renderWithClass("p", childrenHtml, "paragraph");
163
+ }
164
+ return this.renderWithClass("p", node.content || "", "paragraph");
165
+ case "list":
166
+ const tag = node.ordered ? "ol" : "ul";
167
+ const items = node.children?.map((child) => this.renderNode(child)).join("") || "";
168
+ return this.renderWithClass(tag, items, "list");
169
+ case "list-item":
170
+ return this.renderWithClass("li", node.content || "", "list-item");
171
+ case "image":
172
+ const src = node.src || node.attributes?.src || "";
173
+ const alt = node.alt || node.attributes?.alt || "";
174
+ const classStr = this.getClass("image", node.className || void 0);
175
+ return `<img src="${src}" alt="${alt}"${classStr ? ` class="${classStr}"` : ""}>`;
176
+ case "code":
177
+ const codeClass = this.hasClassConfig() ? ` class="${this.getClass("code")} language-${node.attributes?.lang || ""}"` : ` class="language-${node.attributes?.lang || ""}"`;
178
+ return `<pre><code${codeClass}>${node.content || ""}</code></pre>`;
179
+ case "container":
180
+ if (node.attributes?.tag === "hr") return "<hr>";
181
+ if (node.attributes?.tag === "blockquote") {
182
+ const children = node.children?.map((child) => this.renderNode(child)).join("") || "";
183
+ return this.renderWithClass("blockquote", children, "blockquote");
184
+ }
185
+ const containerChildren = node.children?.map((child) => this.renderNode(child)).join("") || "";
186
+ return this.renderWithClass("div", containerChildren, "container", node.className || void 0);
187
+ case "strong":
188
+ return `<strong>${node.content || ""}</strong>`;
189
+ case "emphasis":
190
+ return `<em>${node.content || ""}</em>`;
191
+ case "text":
192
+ default:
193
+ return node.content || "";
194
+ }
195
+ }
196
+ renderNodes(nodes) {
197
+ if (!nodes || nodes.length === 0) {
198
+ return "";
199
+ }
200
+ return nodes.map((node) => this.renderNode(node)).join("\n");
201
+ }
202
+ renderToHTMLString(nodes) {
203
+ return this.renderNodes(nodes);
204
+ }
205
+ render(markdown) {
206
+ return markdown;
207
+ }
208
+ getCustomCSS() {
209
+ return this.config.customCSS;
210
+ }
211
+ }
212
+ class MarkdownPipeline {
213
+ constructor(config = {}) {
214
+ this.config = {
215
+ imagePathPrefix: config.imagePathPrefix || "",
216
+ imageBaseUrl: config.imageBaseUrl || "",
217
+ parseOptions: {
218
+ gfm: config.parseOptions?.gfm ?? true,
219
+ breaks: config.parseOptions?.breaks ?? false,
220
+ pedantic: config.parseOptions?.pedantic ?? false
221
+ },
222
+ styleOptions: {
223
+ classPrefix: config.styleOptions?.classPrefix || "",
224
+ customCSS: config.styleOptions?.customCSS || "",
225
+ addHeadingIds: config.styleOptions?.addHeadingIds ?? false
226
+ }
227
+ };
228
+ this.parser = new MarkdownParser({
229
+ imagePathPrefix: this.config.imagePathPrefix,
230
+ imageBaseUrl: this.config.imageBaseUrl
231
+ });
232
+ this.renderer = new HTMLRenderer(this.config.styleOptions);
233
+ }
234
+ parse(markdown) {
235
+ return this.parser.parseToNodes(markdown, this.config.parseOptions);
236
+ }
237
+ parseWithMetadata(markdown) {
238
+ return this.parser.parse(markdown, this.config.parseOptions);
239
+ }
240
+ render(nodes) {
241
+ return this.renderer.renderNodes(nodes);
242
+ }
243
+ renderMarkdown(markdown) {
244
+ const nodes = this.parse(markdown);
245
+ return this.render(nodes);
246
+ }
247
+ renderPage(title, nodes, options) {
248
+ const html = this.render(nodes);
249
+ return `<!DOCTYPE html>
250
+ <html lang="${options?.lang || "en"}">
251
+ <head>
252
+ <meta charset="${options?.charset || "UTF-8"}">
253
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
254
+ <title>${title}</title>
255
+ </head>
256
+ <body>
257
+ ${html}
258
+ </body>
259
+ </html>`;
260
+ }
261
+ getConfig() {
262
+ return { ...this.config };
263
+ }
264
+ getCustomCSS() {
265
+ return this.renderer.getCustomCSS();
266
+ }
267
+ }
268
+ export {
269
+ HTMLRenderer,
270
+ MarkdownParser,
271
+ MarkdownPipeline
272
+ };
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/parser.ts","../src/renderer.ts","../src/pipeline.ts"],"sourcesContent":["import { marked } from 'marked';\nimport { ContentNode, MarkdownContent, ParseOptions } from './types';\n\nexport class MarkdownParser {\n private imagePathPrefix: string;\n private imageBaseUrl: string;\n\n constructor(options?: { imagePathPrefix?: string; imageBaseUrl?: string }) {\n this.imagePathPrefix = options?.imagePathPrefix || '';\n this.imageBaseUrl = options?.imageBaseUrl || '';\n }\n\n private processImagePath(src: string): string {\n if (src.startsWith('http') || src.startsWith('/')) {\n return src;\n }\n let path = this.imagePathPrefix ? `${this.imagePathPrefix}${src}` : src;\n if (this.imageBaseUrl && !path.startsWith('http')) {\n path = `${this.imageBaseUrl}${path}`;\n }\n return path;\n }\n\n private processInlineFormatting(text: string): string {\n return text\n .replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*(.+?)\\*/g, '<em>$1</em>');\n }\n\n private parseTokens(tokens: unknown[]): ContentNode[] {\n const nodes: ContentNode[] = [];\n \n for (const token of tokens) {\n const node = this.parseToken(token as Record<string, unknown>);\n if (node) {\n nodes.push(node);\n }\n }\n \n return nodes;\n }\n\n private parseToken(token: Record<string, unknown>): ContentNode | null {\n switch (token.type) {\n case 'heading':\n return {\n type: 'heading',\n content: token.text as string,\n attributes: { level: String(token.depth) }\n };\n \n case 'paragraph':\n const tokens = (token.tokens as Array<Record<string, unknown>>) || [];\n const hasInlineImage = tokens.some(t => t.type === 'image');\n \n if (hasInlineImage) {\n const children = tokens.map(t => {\n if (t.type === 'image') {\n return {\n type: 'image' as const,\n src: this.processImagePath(t.href as string),\n alt: t.text as string || ''\n };\n }\n return {\n type: 'text' as const,\n content: this.processInlineFormatting(t.text as string || '')\n };\n });\n return {\n type: 'paragraph',\n children\n };\n }\n \n return {\n type: 'paragraph',\n content: this.processInlineFormatting(token.text as string)\n };\n \n case 'list':\n return {\n type: 'list',\n ordered: token.ordered as boolean,\n children: (token.items as Array<Record<string, unknown>>).map((item) => ({\n type: 'list-item',\n content: this.processInlineFormatting(item.text as string)\n }))\n };\n \n case 'image':\n return {\n type: 'image',\n src: this.processImagePath(token.href as string),\n alt: token.title as string || ''\n };\n\n case 'code':\n return {\n type: 'code',\n content: token.text as string,\n attributes: { lang: token.lang as string || '' }\n };\n \n case 'hr':\n return { type: 'container', attributes: { tag: 'hr' } };\n \n case 'blockquote':\n return {\n type: 'container',\n attributes: { tag: 'blockquote' },\n children: this.parseTokens((token as Record<string, unknown>).tokens as unknown[] || [])\n };\n \n case 'html':\n return { type: 'container', content: token.raw as string };\n \n default:\n return null;\n }\n }\n\n parse(markdown: string, options?: ParseOptions): MarkdownContent {\n const parseOptions = {\n gfm: options?.gfm ?? true,\n breaks: options?.breaks ?? false,\n pedantic: options?.pedantic ?? false\n };\n \n const tokens = marked.lexer(markdown, parseOptions as Parameters<typeof marked.lexer>[1]);\n const content = this.parseTokens(tokens);\n \n return {\n title: '',\n content\n };\n }\n\n parseToNodes(markdown: string, options?: ParseOptions): ContentNode[] {\n return this.parse(markdown, options).content;\n }\n}\n","import { ContentNode, StyleConfig } from './types.js';\n\nexport class HTMLRenderer {\n private config: Required<StyleConfig>;\n\n constructor(config: StyleConfig = {}) {\n this.config = {\n classPrefix: config.classPrefix || '',\n customCSS: config.customCSS || '',\n addHeadingIds: config.addHeadingIds ?? false\n };\n }\n\n private hasClassConfig(): boolean {\n return this.config.classPrefix !== '' || this.config.addHeadingIds;\n }\n\n private getClass(baseClass: string, nodeClass?: string): string {\n if (!this.hasClassConfig()) {\n return nodeClass || '';\n }\n const prefix = this.config.classPrefix;\n const classes = [prefix ? `${prefix}${baseClass}` : baseClass];\n if (nodeClass) classes.push(nodeClass);\n return classes.join(' ');\n }\n\n private generateHeadingId(content?: string): string {\n if (!content) return '';\n return content\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '');\n }\n\n private renderWithClass(tag: string, content: string, baseClass?: string, nodeClass?: string, extraAttrs?: string): string {\n const classAttr = this.hasClassConfig() && baseClass \n ? ` class=\"${this.getClass(baseClass, nodeClass)}\"` \n : '';\n return `<${tag}${classAttr}${extraAttrs || ''}>${content}</${tag}>`;\n }\n\n renderNode(node: ContentNode): string {\n switch (node.type) {\n case 'heading':\n const level = node.attributes?.level || '2';\n const headingId = this.config.addHeadingIds \n ? ` id=\"${this.generateHeadingId(node.content)}\"` \n : '';\n let headingClass = '';\n if (this.hasClassConfig()) {\n const prefix = this.config.classPrefix;\n const levelClass = level === '1' ? 'h1' : level === '2' ? 'h2' : level === '3' ? 'h3' : level === '4' ? 'h4' : level === '5' ? 'h5' : 'h6';\n headingClass = prefix ? `${prefix}${levelClass}` : levelClass;\n }\n if (!headingClass) {\n return `<h${level}${headingId}>${node.content || ''}</h${level}>`;\n }\n return `<h${level}${headingId} class=\"${headingClass}\">${node.content || ''}</h${level}>`;\n \n case 'paragraph':\n if (node.children) {\n const childrenHtml = node.children.map(child => this.renderNode(child)).join('');\n return this.renderWithClass('p', childrenHtml, 'paragraph');\n }\n return this.renderWithClass('p', node.content || '', 'paragraph');\n \n case 'list':\n const tag = node.ordered ? 'ol' : 'ul';\n const items = node.children?.map(child => this.renderNode(child)).join('') || '';\n return this.renderWithClass(tag, items, 'list');\n \n case 'list-item':\n return this.renderWithClass('li', node.content || '', 'list-item');\n \n case 'image':\n const src = node.src || node.attributes?.src || '';\n const alt = node.alt || node.attributes?.alt || '';\n const classStr = this.getClass('image', node.className || undefined);\n return `<img src=\"${src}\" alt=\"${alt}\"${classStr ? ` class=\"${classStr}\"` : ''}>`;\n \n case 'code':\n const codeClass = this.hasClassConfig() \n ? ` class=\"${this.getClass('code')} language-${node.attributes?.lang || ''}\"` \n : ` class=\"language-${node.attributes?.lang || ''}\"`;\n return `<pre><code${codeClass}>${node.content || ''}</code></pre>`;\n \n case 'container':\n if (node.attributes?.tag === 'hr') return '<hr>';\n if (node.attributes?.tag === 'blockquote') {\n const children = node.children?.map(child => this.renderNode(child)).join('') || '';\n return this.renderWithClass('blockquote', children, 'blockquote');\n }\n const containerChildren = node.children?.map(child => this.renderNode(child)).join('') || '';\n return this.renderWithClass('div', containerChildren, 'container', node.className || undefined);\n \n case 'strong':\n return `<strong>${node.content || ''}</strong>`;\n \n case 'emphasis':\n return `<em>${node.content || ''}</em>`;\n \n case 'text':\n default:\n return node.content || '';\n }\n }\n\n renderNodes(nodes: ContentNode[]): string {\n if (!nodes || nodes.length === 0) {\n return '';\n }\n return nodes.map(node => this.renderNode(node)).join('\\n');\n }\n\n renderToHTMLString(nodes: ContentNode[]): string {\n return this.renderNodes(nodes);\n }\n\n render(markdown: string): string {\n return markdown;\n }\n\n getCustomCSS(): string {\n return this.config.customCSS;\n }\n}\n","import { MarkdownParser } from './parser.js';\nimport { HTMLRenderer } from './renderer.js';\nimport { ContentNode, MarkdownContent, PipelineConfig, StyleConfig } from './types.js';\n\nexport class MarkdownPipeline {\n private parser: MarkdownParser;\n private renderer: HTMLRenderer;\n private config: Required<PipelineConfig>;\n\n constructor(config: PipelineConfig = {}) {\n this.config = {\n imagePathPrefix: config.imagePathPrefix || '',\n imageBaseUrl: config.imageBaseUrl || '',\n parseOptions: {\n gfm: config.parseOptions?.gfm ?? true,\n breaks: config.parseOptions?.breaks ?? false,\n pedantic: config.parseOptions?.pedantic ?? false\n },\n styleOptions: {\n classPrefix: config.styleOptions?.classPrefix || '',\n customCSS: config.styleOptions?.customCSS || '',\n addHeadingIds: config.styleOptions?.addHeadingIds ?? false\n }\n };\n\n this.parser = new MarkdownParser({\n imagePathPrefix: this.config.imagePathPrefix,\n imageBaseUrl: this.config.imageBaseUrl\n });\n this.renderer = new HTMLRenderer(this.config.styleOptions);\n }\n\n parse(markdown: string): ContentNode[] {\n return this.parser.parseToNodes(markdown, this.config.parseOptions);\n }\n\n parseWithMetadata(markdown: string): MarkdownContent {\n return this.parser.parse(markdown, this.config.parseOptions);\n }\n\n render(nodes: ContentNode[]): string {\n return this.renderer.renderNodes(nodes);\n }\n\n renderMarkdown(markdown: string): string {\n const nodes = this.parse(markdown);\n return this.render(nodes);\n }\n\n renderPage(title: string, nodes: ContentNode[], options?: {\n lang?: string;\n charset?: string;\n }): string {\n const html = this.render(nodes);\n return `<!DOCTYPE html>\n<html lang=\"${options?.lang || 'en'}\">\n<head>\n <meta charset=\"${options?.charset || 'UTF-8'}\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${title}</title>\n</head>\n<body>\n ${html}\n</body>\n</html>`;\n }\n\n getConfig(): Readonly<Required<PipelineConfig>> {\n return { ...this.config };\n }\n\n getCustomCSS(): string {\n return this.renderer.getCustomCSS();\n }\n}\n"],"names":[],"mappings":"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"}