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

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
@@ -55,10 +55,35 @@ const pipeline = new MarkdownPipeline({
55
55
  gfm: true,
56
56
  breaks: false,
57
57
  pedantic: false
58
+ },
59
+ styleOptions: {
60
+ classPrefix: 'md-',
61
+ customCSS: 'body { font-family: system-ui; }',
62
+ addHeadingIds: true
58
63
  }
59
64
  });
60
65
  ```
61
66
 
67
+ ### Style Configuration Options
68
+
69
+ | Option | Type | Default | Description |
70
+ |--------|------|---------|-------------|
71
+ | `classPrefix` | string | `''` | Prefix for CSS classes on elements (e.g., `'md-'` produces `md-heading`, `md-paragraph`) |
72
+ | `customCSS` | string | `''` | Custom CSS string to inject (use `pipeline.getCustomCSS()` to retrieve) |
73
+ | `addHeadingIds` | boolean | `false` | Add ID attributes to headings based on their content for anchor links |
74
+
75
+ When `classPrefix` or `addHeadingIds` is set, CSS classes will be added to elements:
76
+ - `heading`, `paragraph`, `list`, `list-item`, `image`, `code`, `container`, `blockquote`
77
+
78
+ Example output with `classPrefix: 'md-'` and `addHeadingIds: true`:
79
+ ```html
80
+ <h1 id="hello-world" class="md-heading">Hello World</h1>
81
+ <p class="md-paragraph">This is a paragraph.</p>
82
+ <ul class="md-list">
83
+ <li class="md-list-item">Item 1</li>
84
+ </ul>
85
+ ```
86
+
62
87
  ### API
63
88
 
64
89
  | Method | Description |
@@ -66,7 +91,9 @@ const pipeline = new MarkdownPipeline({
66
91
  | `parse(markdown)` | Parse markdown string to AST |
67
92
  | `render(nodes)` | Render AST to HTML string |
68
93
  | `renderMarkdown(markdown)` | Parse and render in one call |
69
- | `renderPage(title, nodes)` | Render AST to full HTML page |
94
+ | `renderPage(title, nodes, options?)` | Render AST to full HTML page |
95
+ | `getCustomCSS()` | Get custom CSS string from style config |
96
+ | `getConfig()` | Get current pipeline configuration |
70
97
 
71
98
  ## License
72
99
 
@@ -0,0 +1,7 @@
1
+ export * from './types.js';
2
+ export * from './parser.js';
3
+ export * from './renderer.js';
4
+ export * from './lit-renderer.js';
5
+ export * from './pipeline.js';
6
+ export { LitRenderer as HTMLRenderer } from './lit-renderer.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAE9B,OAAO,EAAE,WAAW,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './types.js';
2
+ export * from './parser.js';
3
+ export * from './renderer.js';
4
+ export * from './lit-renderer.js';
5
+ export * from './pipeline.js';
6
+ export { LitRenderer as HTMLRenderer } from './lit-renderer.js';
@@ -0,0 +1,10 @@
1
+ import { TemplateResult } from 'lit';
2
+ import { ContentNode } from './types';
3
+ export declare class LitRenderer {
4
+ private renderTextNode;
5
+ renderNode(node: ContentNode): TemplateResult;
6
+ renderNodes(nodes: ContentNode[]): TemplateResult;
7
+ renderToHTMLString(nodes: ContentNode[]): string;
8
+ private nodeToHTMLString;
9
+ }
10
+ //# sourceMappingURL=lit-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lit-renderer.d.ts","sourceRoot":"","sources":["../src/lit-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAQ,MAAM,KAAK,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC,qBAAa,WAAW;IACtB,OAAO,CAAC,cAAc;IAOtB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,cAAc;IAyC7C,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,cAAc;IAOjD,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM;IAOhD,OAAO,CAAC,gBAAgB;CAuBzB"}
@@ -0,0 +1,80 @@
1
+ import { html } from 'lit';
2
+ import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3
+ export class LitRenderer {
4
+ renderTextNode(node) {
5
+ if (node.type === 'image') {
6
+ return html `<img src="${node.src}" alt="${node.alt || ''}" class="inline-image" style="max-width:100%;height:auto;">`;
7
+ }
8
+ return html `${unsafeHTML(node.content || '')}`;
9
+ }
10
+ renderNode(node) {
11
+ switch (node.type) {
12
+ case 'heading': {
13
+ const level = node.attributes?.level || '2';
14
+ if (level === '1')
15
+ return html `<h1>${unsafeHTML(node.content)}</h1>`;
16
+ if (level === '2')
17
+ return html `<h2>${unsafeHTML(node.content)}</h2>`;
18
+ if (level === '3')
19
+ return html `<h3>${unsafeHTML(node.content)}</h3>`;
20
+ return html `<h2>${unsafeHTML(node.content)}</h2>`;
21
+ }
22
+ case 'paragraph':
23
+ if (node.children) {
24
+ return html `<p>${node.children.map(child => this.renderTextNode(child))}</p>`;
25
+ }
26
+ return html `<p>${unsafeHTML(node.content)}</p>`;
27
+ case 'list':
28
+ return html `<ul>${node.children?.map(child => this.renderNode(child))}</ul>`;
29
+ case 'list-item':
30
+ return html `<li>${unsafeHTML(node.content)}</li>`;
31
+ case 'image':
32
+ return html `<img src="${node.src || node.attributes?.src}" alt="${node.alt || node.attributes?.alt || ''}" class="${node.className || ''}" style="max-width:100%;height:auto;">`;
33
+ case 'container':
34
+ return html `<div class="${node.className || ''}" style="${node.attributes?.style || ''}">
35
+ ${node.children?.map(child => this.renderNode(child))}
36
+ </div>`;
37
+ case 'code':
38
+ return html `<pre><code class="language-${node.attributes?.lang || ''}">${node.content || ''}</code></pre>`;
39
+ case 'text':
40
+ return html `${node.content}`;
41
+ default:
42
+ return html ``;
43
+ }
44
+ }
45
+ renderNodes(nodes) {
46
+ if (!nodes || nodes.length === 0) {
47
+ return html ``;
48
+ }
49
+ return html `${nodes.map(node => this.renderNode(node))}`;
50
+ }
51
+ renderToHTMLString(nodes) {
52
+ if (!nodes || nodes.length === 0) {
53
+ return '';
54
+ }
55
+ return nodes.map(node => this.nodeToHTMLString(node)).join('\n');
56
+ }
57
+ nodeToHTMLString(node) {
58
+ switch (node.type) {
59
+ case 'heading':
60
+ const level = node.attributes?.level || '2';
61
+ return `<h${level}>${node.content}</h${level}>`;
62
+ case 'paragraph':
63
+ return `<p>${node.content}</p>`;
64
+ case 'list':
65
+ const items = node.children?.map(child => this.nodeToHTMLString(child)).join('') || '';
66
+ return `<ul>${items}</ul>`;
67
+ case 'list-item':
68
+ return `<li>${node.content}</li>`;
69
+ case 'image':
70
+ return `<img src="${node.attributes?.src}" alt="${node.attributes?.alt}" class="${node.className || ''}">`;
71
+ case 'container':
72
+ const childrenHTML = node.children?.map(child => this.nodeToHTMLString(child)).join('') || '';
73
+ return `<div class="${node.className || ''}">${childrenHTML}</div>`;
74
+ case 'text':
75
+ return node.content || '';
76
+ default:
77
+ return '';
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,16 @@
1
+ import { ContentNode, MarkdownContent, ParseOptions } from './types';
2
+ export declare class MarkdownParser {
3
+ private imagePathPrefix;
4
+ private imageBaseUrl;
5
+ constructor(options?: {
6
+ imagePathPrefix?: string;
7
+ imageBaseUrl?: string;
8
+ });
9
+ private processImagePath;
10
+ private processInlineFormatting;
11
+ private parseTokens;
12
+ private parseToken;
13
+ parse(markdown: string, options?: ParseOptions): MarkdownContent;
14
+ parseToNodes(markdown: string, options?: ParseOptions): ContentNode[];
15
+ }
16
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAErE,qBAAa,cAAc;IACzB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;IAKzE,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,uBAAuB;IAM/B,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,UAAU;IAgFlB,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,eAAe;IAgBhE,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,WAAW,EAAE;CAGtE"}
package/dist/parser.js ADDED
@@ -0,0 +1,117 @@
1
+ import { marked } from 'marked';
2
+ export 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
19
+ .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
20
+ .replace(/\*(.+?)\*/g, '<em>$1</em>');
21
+ }
22
+ parseTokens(tokens) {
23
+ const nodes = [];
24
+ for (const token of tokens) {
25
+ const node = this.parseToken(token);
26
+ if (node) {
27
+ nodes.push(node);
28
+ }
29
+ }
30
+ return nodes;
31
+ }
32
+ parseToken(token) {
33
+ switch (token.type) {
34
+ case 'heading':
35
+ return {
36
+ type: 'heading',
37
+ content: token.text,
38
+ attributes: { level: String(token.depth) }
39
+ };
40
+ case 'paragraph':
41
+ const tokens = token.tokens || [];
42
+ const hasInlineImage = tokens.some(t => t.type === 'image');
43
+ if (hasInlineImage) {
44
+ const children = tokens.map(t => {
45
+ if (t.type === 'image') {
46
+ return {
47
+ type: 'image',
48
+ src: this.processImagePath(t.href),
49
+ alt: t.text || ''
50
+ };
51
+ }
52
+ return {
53
+ type: 'text',
54
+ content: this.processInlineFormatting(t.text || '')
55
+ };
56
+ });
57
+ return {
58
+ type: 'paragraph',
59
+ children
60
+ };
61
+ }
62
+ return {
63
+ type: 'paragraph',
64
+ content: this.processInlineFormatting(token.text)
65
+ };
66
+ case 'list':
67
+ return {
68
+ type: 'list',
69
+ ordered: token.ordered,
70
+ children: token.items.map((item) => ({
71
+ type: 'list-item',
72
+ content: this.processInlineFormatting(item.text)
73
+ }))
74
+ };
75
+ case 'image':
76
+ return {
77
+ type: 'image',
78
+ src: this.processImagePath(token.href),
79
+ alt: token.title || ''
80
+ };
81
+ case 'code':
82
+ return {
83
+ type: 'code',
84
+ content: token.text,
85
+ attributes: { lang: token.lang || '' }
86
+ };
87
+ case 'hr':
88
+ return { type: 'container', attributes: { tag: 'hr' } };
89
+ case 'blockquote':
90
+ return {
91
+ type: 'container',
92
+ attributes: { tag: 'blockquote' },
93
+ children: this.parseTokens(token.tokens || [])
94
+ };
95
+ case 'html':
96
+ return { type: 'container', content: token.raw };
97
+ default:
98
+ return null;
99
+ }
100
+ }
101
+ parse(markdown, options) {
102
+ const parseOptions = {
103
+ gfm: options?.gfm ?? true,
104
+ breaks: options?.breaks ?? false,
105
+ pedantic: options?.pedantic ?? false
106
+ };
107
+ const tokens = marked.lexer(markdown, parseOptions);
108
+ const content = this.parseTokens(tokens);
109
+ return {
110
+ title: '',
111
+ content
112
+ };
113
+ }
114
+ parseToNodes(markdown, options) {
115
+ return this.parse(markdown, options).content;
116
+ }
117
+ }
@@ -0,0 +1,18 @@
1
+ import { ContentNode, MarkdownContent, PipelineConfig } from './types.js';
2
+ export declare class MarkdownPipeline {
3
+ private parser;
4
+ private renderer;
5
+ private config;
6
+ constructor(config?: PipelineConfig);
7
+ parse(markdown: string): ContentNode[];
8
+ parseWithMetadata(markdown: string): MarkdownContent;
9
+ render(nodes: ContentNode[]): string;
10
+ renderMarkdown(markdown: string): string;
11
+ renderPage(title: string, nodes: ContentNode[], options?: {
12
+ lang?: string;
13
+ charset?: string;
14
+ }): string;
15
+ getConfig(): Readonly<Required<PipelineConfig>>;
16
+ getCustomCSS(): string;
17
+ }
18
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAe,MAAM,YAAY,CAAC;AAEvF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,MAAM,CAA2B;gBAE7B,MAAM,GAAE,cAAmB;IAuBvC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE;IAItC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAIpD,MAAM,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM;IAIpC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKxC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,OAAO,CAAC,EAAE;QACxD,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,MAAM;IAeV,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAI/C,YAAY,IAAI,MAAM;CAGvB"}
@@ -0,0 +1,58 @@
1
+ import { MarkdownParser } from './parser.js';
2
+ import { HTMLRenderer } from './renderer.js';
3
+ export class MarkdownPipeline {
4
+ constructor(config = {}) {
5
+ this.config = {
6
+ imagePathPrefix: config.imagePathPrefix || '',
7
+ imageBaseUrl: config.imageBaseUrl || '',
8
+ parseOptions: {
9
+ gfm: config.parseOptions?.gfm ?? true,
10
+ breaks: config.parseOptions?.breaks ?? false,
11
+ pedantic: config.parseOptions?.pedantic ?? false
12
+ },
13
+ styleOptions: {
14
+ classPrefix: config.styleOptions?.classPrefix || '',
15
+ customCSS: config.styleOptions?.customCSS || '',
16
+ addHeadingIds: config.styleOptions?.addHeadingIds ?? false
17
+ }
18
+ };
19
+ this.parser = new MarkdownParser({
20
+ imagePathPrefix: this.config.imagePathPrefix,
21
+ imageBaseUrl: this.config.imageBaseUrl
22
+ });
23
+ this.renderer = new HTMLRenderer(this.config.styleOptions);
24
+ }
25
+ parse(markdown) {
26
+ return this.parser.parseToNodes(markdown, this.config.parseOptions);
27
+ }
28
+ parseWithMetadata(markdown) {
29
+ return this.parser.parse(markdown, this.config.parseOptions);
30
+ }
31
+ render(nodes) {
32
+ return this.renderer.renderNodes(nodes);
33
+ }
34
+ renderMarkdown(markdown) {
35
+ const nodes = this.parse(markdown);
36
+ return this.render(nodes);
37
+ }
38
+ renderPage(title, nodes, options) {
39
+ const html = this.render(nodes);
40
+ return `<!DOCTYPE html>
41
+ <html lang="${options?.lang || 'en'}">
42
+ <head>
43
+ <meta charset="${options?.charset || 'UTF-8'}">
44
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
45
+ <title>${title}</title>
46
+ </head>
47
+ <body>
48
+ ${html}
49
+ </body>
50
+ </html>`;
51
+ }
52
+ getConfig() {
53
+ return { ...this.config };
54
+ }
55
+ getCustomCSS() {
56
+ return this.renderer.getCustomCSS();
57
+ }
58
+ }
@@ -0,0 +1,15 @@
1
+ import { ContentNode, StyleConfig } from './types.js';
2
+ export declare class HTMLRenderer {
3
+ private config;
4
+ constructor(config?: StyleConfig);
5
+ private hasClassConfig;
6
+ private getClass;
7
+ private generateHeadingId;
8
+ private renderWithClass;
9
+ renderNode(node: ContentNode): string;
10
+ renderNodes(nodes: ContentNode[]): string;
11
+ renderToHTMLString(nodes: ContentNode[]): string;
12
+ render(markdown: string): string;
13
+ getCustomCSS(): string;
14
+ }
15
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAwB;gBAE1B,MAAM,GAAE,WAAgB;IAQpC,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,eAAe;IAOvB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM;IA4DrC,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM;IAOzC,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,MAAM;IAIhD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAIhC,YAAY,IAAI,MAAM;CAGvB"}
@@ -0,0 +1,102 @@
1
+ export class HTMLRenderer {
2
+ constructor(config = {}) {
3
+ this.config = {
4
+ classPrefix: config.classPrefix || '',
5
+ customCSS: config.customCSS || '',
6
+ addHeadingIds: config.addHeadingIds ?? false
7
+ };
8
+ }
9
+ hasClassConfig() {
10
+ return this.config.classPrefix !== '' || this.config.addHeadingIds;
11
+ }
12
+ getClass(baseClass, nodeClass) {
13
+ if (!this.hasClassConfig()) {
14
+ return nodeClass || '';
15
+ }
16
+ const prefix = this.config.classPrefix;
17
+ const classes = [prefix ? `${prefix}${baseClass}` : baseClass];
18
+ if (nodeClass)
19
+ classes.push(nodeClass);
20
+ return classes.join(' ');
21
+ }
22
+ generateHeadingId(content) {
23
+ if (!content)
24
+ return '';
25
+ return content
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9]+/g, '-')
28
+ .replace(/(^-|-$)/g, '');
29
+ }
30
+ renderWithClass(tag, content, baseClass, nodeClass, extraAttrs) {
31
+ const classAttr = this.hasClassConfig() && baseClass
32
+ ? ` class="${this.getClass(baseClass, nodeClass)}"`
33
+ : '';
34
+ return `<${tag}${classAttr}${extraAttrs || ''}>${content}</${tag}>`;
35
+ }
36
+ renderNode(node) {
37
+ switch (node.type) {
38
+ case 'heading':
39
+ const level = node.attributes?.level || '2';
40
+ const headingId = this.config.addHeadingIds
41
+ ? ` id="${this.generateHeadingId(node.content)}"`
42
+ : '';
43
+ if (!this.hasClassConfig()) {
44
+ return `<h${level}${headingId}>${node.content || ''}</h${level}>`;
45
+ }
46
+ return `<h${level}${headingId} class="${this.getClass('heading')}">${node.content || ''}</h${level}>`;
47
+ case 'paragraph':
48
+ if (node.children) {
49
+ const childrenHtml = node.children.map(child => this.renderNode(child)).join('');
50
+ return this.renderWithClass('p', childrenHtml, 'paragraph');
51
+ }
52
+ return this.renderWithClass('p', node.content || '', 'paragraph');
53
+ case 'list':
54
+ const tag = node.ordered ? 'ol' : 'ul';
55
+ const items = node.children?.map(child => this.renderNode(child)).join('') || '';
56
+ return this.renderWithClass(tag, items, 'list');
57
+ case 'list-item':
58
+ return this.renderWithClass('li', node.content || '', 'list-item');
59
+ case 'image':
60
+ const src = node.src || node.attributes?.src || '';
61
+ const alt = node.alt || node.attributes?.alt || '';
62
+ const classStr = this.getClass('image', node.className || undefined);
63
+ return `<img src="${src}" alt="${alt}"${classStr ? ` class="${classStr}"` : ''}>`;
64
+ case 'code':
65
+ const codeClass = this.hasClassConfig()
66
+ ? ` class="${this.getClass('code')} language-${node.attributes?.lang || ''}"`
67
+ : ` class="language-${node.attributes?.lang || ''}"`;
68
+ return `<pre><code${codeClass}>${node.content || ''}</code></pre>`;
69
+ case 'container':
70
+ if (node.attributes?.tag === 'hr')
71
+ return '<hr>';
72
+ if (node.attributes?.tag === 'blockquote') {
73
+ const children = node.children?.map(child => this.renderNode(child)).join('') || '';
74
+ return this.renderWithClass('blockquote', children, 'blockquote');
75
+ }
76
+ const containerChildren = node.children?.map(child => this.renderNode(child)).join('') || '';
77
+ return this.renderWithClass('div', containerChildren, 'container', node.className || undefined);
78
+ case 'strong':
79
+ return `<strong>${node.content || ''}</strong>`;
80
+ case 'emphasis':
81
+ return `<em>${node.content || ''}</em>`;
82
+ case 'text':
83
+ default:
84
+ return node.content || '';
85
+ }
86
+ }
87
+ renderNodes(nodes) {
88
+ if (!nodes || nodes.length === 0) {
89
+ return '';
90
+ }
91
+ return nodes.map(node => this.renderNode(node)).join('\n');
92
+ }
93
+ renderToHTMLString(nodes) {
94
+ return this.renderNodes(nodes);
95
+ }
96
+ render(markdown) {
97
+ return markdown;
98
+ }
99
+ getCustomCSS() {
100
+ return this.config.customCSS;
101
+ }
102
+ }
@@ -0,0 +1,33 @@
1
+ export type ContentNodeType = 'text' | 'heading' | 'paragraph' | 'list' | 'list-item' | 'image' | 'code' | 'container' | 'strong' | 'emphasis';
2
+ export interface ContentNode {
3
+ type: ContentNodeType;
4
+ content?: string;
5
+ children?: ContentNode[];
6
+ attributes?: Record<string, unknown>;
7
+ className?: string;
8
+ src?: string;
9
+ alt?: string;
10
+ ordered?: boolean;
11
+ }
12
+ export interface MarkdownContent {
13
+ title: string;
14
+ metadata?: Record<string, unknown>;
15
+ content: ContentNode[];
16
+ }
17
+ export interface ParseOptions {
18
+ gfm?: boolean;
19
+ breaks?: boolean;
20
+ pedantic?: boolean;
21
+ }
22
+ export interface StyleConfig {
23
+ classPrefix?: string;
24
+ customCSS?: string;
25
+ addHeadingIds?: boolean;
26
+ }
27
+ export interface PipelineConfig {
28
+ imagePathPrefix?: string;
29
+ imageBaseUrl?: string;
30
+ parseOptions?: ParseOptions;
31
+ styleOptions?: StyleConfig;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB,MAAM,GACN,SAAS,GACT,WAAW,GACX,MAAM,GACN,WAAW,GACX,OAAO,GACP,MAAM,GACN,WAAW,GACX,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,WAAW,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,WAAW,CAAC;CAC5B"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadertechie/md2html",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Markdown to HTML pipeline - parse markdown to AST, render to HTML or Lit templates",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,6 +36,11 @@
36
36
  "ssr"
37
37
  ],
38
38
  "license": "MIT",
39
+ "files": [
40
+ "dist",
41
+ "README.md",
42
+ "LICENSE"
43
+ ],
39
44
  "repository": {
40
45
  "type": "git",
41
46
  "url": "git+https://github.com/leadertechie/md2html.git"
@@ -1,76 +0,0 @@
1
- name: CI / Publish
2
-
3
- on:
4
- push:
5
- branches:
6
- - '**'
7
- pull_request:
8
- workflow_dispatch:
9
-
10
- jobs:
11
- build-and-test:
12
- runs-on: ubuntu-latest
13
- permissions:
14
- contents: read
15
-
16
- steps:
17
- - uses: actions/checkout@v4
18
-
19
- - uses: actions/setup-node@v4
20
- with:
21
- node-version: '20'
22
-
23
- - name: Install dependencies
24
- run: npm install
25
-
26
- - name: Run tests
27
- run: npm test
28
-
29
- - name: Build package
30
- run: npm run build
31
-
32
- publish:
33
- needs: build-and-test
34
- runs-on: ubuntu-latest
35
- if: github.ref == 'refs/heads/main' && github.event_name == 'push'
36
- permissions:
37
- contents: read
38
-
39
- steps:
40
- - uses: actions/checkout@v4
41
- with:
42
- fetch-depth: 0
43
-
44
- - name: Setup GitVersion
45
- uses: gittools/actions/gitversion/setup@v0.10.2
46
- with:
47
- versionSpec: '5.x'
48
-
49
- - name: Execute GitVersion
50
- id: gitversion
51
- uses: gittools/actions/gitversion/execute@v0.10.2
52
- with:
53
- useConfigFile: true
54
-
55
- - name: Display GitVersion outputs
56
- run: |
57
- echo "FullSemVer: ${{ steps.gitversion.outputs.fullSemVer }}"
58
-
59
- - uses: actions/setup-node@v4
60
- with:
61
- node-version: '20'
62
- registry-url: 'https://registry.npmjs.org/'
63
-
64
- - name: Install dependencies
65
- run: npm install
66
-
67
- - name: Build package
68
- run: npm run build
69
-
70
- - name: Update package version
71
- run: npm version ${{ steps.gitversion.outputs.fullSemVer }} --no-git-tag-version
72
-
73
- - name: Publish to npm
74
- run: npm publish --access public
75
- env:
76
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/GitVersion.yml DELETED
@@ -1,7 +0,0 @@
1
- next-version: 0.0.1
2
- mode: Mainline
3
- branches:
4
- main:
5
- regex: ^main$
6
- increment: Patch
7
- tag: alpha
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["index.test.ts"],"names":[],"mappings":""}