@changerawr/markdown 1.1.11 → 1.2.0

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
@@ -255,23 +255,20 @@ The main component for rendering markdown:
255
255
 
256
256
  ## 🔧 Custom Extensions
257
257
 
258
- Create powerful custom extensions to extend markdown syntax:
258
+ Extensions are the building block of the entire library — every feature, including all the built-ins, is an extension. You can add new syntax, replace existing behaviour, or attach full React components for framework-specific rendering while keeping a plain-HTML fallback for everywhere else.
259
259
 
260
260
  ```typescript
261
- import { ChangerawrMarkdown } from '@changerawr/markdown';
262
-
263
- const engine = new ChangerawrMarkdown();
261
+ import { createEngine } from '@changerawr/markdown';
264
262
 
265
- // Add a highlight extension
266
- const highlightExtension = {
263
+ const HighlightExtension = {
267
264
  name: 'highlight',
268
265
  parseRules: [{
269
266
  name: 'highlight',
270
- pattern: /==(.+?)==/g,
267
+ pattern: /==(.+?)==/,
271
268
  render: (match) => ({
272
269
  type: 'highlight',
273
- content: match[1],
274
- raw: match[0]
270
+ content: match[1] ?? '',
271
+ raw: match[0] ?? '',
275
272
  })
276
273
  }],
277
274
  renderRules: [{
@@ -280,12 +277,17 @@ const highlightExtension = {
280
277
  }]
281
278
  };
282
279
 
283
- engine.registerExtension(highlightExtension);
280
+ const engine = createEngine();
281
+ engine.registerExtension(HighlightExtension);
284
282
 
285
- const html = engine.toHtml('This is ==highlighted text==');
286
- // <mark class="bg-yellow-200 px-1">highlighted text</mark>
283
+ engine.toHtml('This is ==highlighted text==');
284
+ // Works in React, Astro, vanilla JS, Node — everywhere.
287
285
  ```
288
286
 
287
+ **[Full extension documentation →](./EXTENSIONS.md)**
288
+
289
+ The guide covers: block extensions, inline extensions, passing attributes, component extensions with React state and lifecycle, Astro compatibility, priority ordering, TypeScript types, and a complete worked example.
290
+
289
291
  ## 🎯 Advanced Configuration
290
292
 
291
293
  ### Engine Configuration
@@ -632,6 +634,7 @@ npm run test:coverage # Run with coverage report
632
634
  ### React Hooks
633
635
 
634
636
  - `useMarkdown(content: string, options?)` - Main markdown processing hook
637
+ - `useMarkdownComponents(content: string, options?)` - Hook for component extensions (returns `componentMap` + `renderBatch`)
635
638
  - `useMarkdownEngine(options?)` - Engine management hook
636
639
  - `useMarkdownDebug(content: string)` - Debug information hook
637
640
 
@@ -645,7 +648,13 @@ npm run test:coverage # Run with coverage report
645
648
 
646
649
  - **Core Extensions**: `CoreExtensions` array or individual exports
647
650
  - **Feature Extensions**: `AlertExtension`, `ButtonExtension`, `EmbedExtension`
648
- - **Custom**: Create your own with `parseRules` and `renderRules`
651
+ - **Custom**: Create your own with `parseRules` and `renderRules` — [see the extension guide](./EXTENSIONS.md)
652
+ - **Component Extensions**: Attach React components to render rules via `ReactComponentExtension` (with universal string fallback for Astro, HTML, etc.)
653
+
654
+ ### Astro
655
+
656
+ - `renderMarkdownForAstro(content, options?)` — server-side render to HTML string
657
+ - `<MarkdownRenderer />` — zero-JS Astro component (`@changerawr/markdown/astro/MarkdownRenderer.astro`)
649
658
 
650
659
  ## 🤝 Contributing
651
660
 
@@ -0,0 +1,31 @@
1
+ ---
2
+ /**
3
+ * @changerawr/markdown — Astro MarkdownRenderer component
4
+ *
5
+ * Usage:
6
+ * import MarkdownRenderer from '@changerawr/markdown/astro/MarkdownRenderer.astro';
7
+ *
8
+ * <MarkdownRenderer content={post.body} format="tailwind" class="prose" />
9
+ *
10
+ * The component renders markdown server-side at build / SSR time — zero client JS.
11
+ */
12
+ import { renderMarkdownForAstro } from '@changerawr/markdown/astro';
13
+ import type { AstroMarkdownRendererProps } from '@changerawr/markdown/astro';
14
+
15
+ type Props = AstroMarkdownRendererProps;
16
+
17
+ const {
18
+ content,
19
+ format = 'html',
20
+ class: className,
21
+ config,
22
+ extensions,
23
+ } = Astro.props;
24
+
25
+ const html = renderMarkdownForAstro(content, { format, config, extensions });
26
+ ---
27
+
28
+ <div
29
+ class:list={['changerawr-markdown', className]}
30
+ set:html={html}
31
+ />
@@ -0,0 +1,213 @@
1
+ interface MarkdownToken {
2
+ type: string;
3
+ content: string;
4
+ raw: string;
5
+ attributes?: Record<string, string | number | boolean | Function | any>;
6
+ children?: MarkdownToken[];
7
+ }
8
+ interface ParseRule {
9
+ name: string;
10
+ pattern: RegExp;
11
+ render: (match: RegExpMatchArray) => MarkdownToken;
12
+ }
13
+ interface RenderRule {
14
+ type: string;
15
+ render: (token: MarkdownToken) => string;
16
+ }
17
+ interface Extension {
18
+ name: string;
19
+ parseRules: ParseRule[];
20
+ renderRules: RenderRule[];
21
+ }
22
+ type OutputFormat = 'html' | 'tailwind' | 'json';
23
+ interface RendererConfig {
24
+ format: OutputFormat;
25
+ sanitize?: boolean;
26
+ allowUnsafeHtml?: boolean;
27
+ customClasses?: Record<string, string>;
28
+ debugMode?: boolean;
29
+ }
30
+ interface ParserConfig {
31
+ debugMode?: boolean;
32
+ maxIterations?: number;
33
+ validateMarkdown?: boolean;
34
+ }
35
+ interface EngineConfig {
36
+ parser?: ParserConfig;
37
+ renderer?: RendererConfig;
38
+ extensions?: Extension[];
39
+ }
40
+ interface DebugInfo {
41
+ warnings: string[];
42
+ parseTime: number;
43
+ renderTime: number;
44
+ tokenCount: number;
45
+ iterationCount: number;
46
+ }
47
+ interface PerformanceMetrics {
48
+ parseTime: number;
49
+ renderTime: number;
50
+ totalTime: number;
51
+ tokenCount: number;
52
+ memoryUsed?: number;
53
+ }
54
+ interface ExtensionRegistration {
55
+ success: boolean;
56
+ extensionName: string;
57
+ error?: string;
58
+ conflictingRules?: string[];
59
+ }
60
+
61
+ /**
62
+ * Props for the Astro MarkdownRenderer component.
63
+ */
64
+ interface AstroMarkdownRendererProps {
65
+ /** Markdown content to render */
66
+ content: string;
67
+ /** Output format — 'html' (default) or 'tailwind' */
68
+ format?: 'html' | 'tailwind';
69
+ /** CSS class(es) to add to the wrapper element */
70
+ class?: string;
71
+ /** Engine configuration */
72
+ config?: EngineConfig;
73
+ /** Additional extensions to register */
74
+ extensions?: Extension[];
75
+ }
76
+
77
+ /**
78
+ * Plain HTML output renderer - no CSS framework classes
79
+ */
80
+
81
+ /**
82
+ * Render markdown to plain HTML without CSS framework classes
83
+ */
84
+ declare function renderToHTML(markdown: string, config?: Omit<EngineConfig, 'renderer'>): string;
85
+ /**
86
+ * Parse markdown and render to HTML with custom configuration
87
+ */
88
+ declare function renderToHTMLWithConfig(markdown: string, rendererConfig: {
89
+ sanitize?: boolean;
90
+ allowUnsafeHtml?: boolean;
91
+ debugMode?: boolean;
92
+ }): string;
93
+
94
+ /**
95
+ * Tailwind CSS output renderer - HTML with Tailwind CSS classes
96
+ */
97
+
98
+ /**
99
+ * Render markdown to HTML with Tailwind CSS classes (default behavior)
100
+ */
101
+ declare function renderToTailwind(markdown: string, config?: Omit<EngineConfig, 'renderer'>): string;
102
+ /**
103
+ * Render markdown to Tailwind HTML with custom configuration
104
+ */
105
+ declare function renderToTailwindWithConfig(markdown: string, rendererConfig: {
106
+ sanitize?: boolean;
107
+ allowUnsafeHtml?: boolean;
108
+ customClasses?: Record<string, string>;
109
+ debugMode?: boolean;
110
+ }): string;
111
+
112
+ interface CacheStats {
113
+ size: number;
114
+ capacity: number;
115
+ hits: number;
116
+ misses: number;
117
+ hitRate: number;
118
+ evictions: number;
119
+ }
120
+
121
+ interface RenderMetrics {
122
+ inputSize: number;
123
+ parseTime: number;
124
+ renderTime: number;
125
+ totalTime: number;
126
+ tokenCount: number;
127
+ cacheHit: boolean;
128
+ }
129
+ declare class ChangerawrMarkdown {
130
+ private parser;
131
+ private renderer;
132
+ private extensions;
133
+ private parseCache;
134
+ private renderCache;
135
+ private parseTime;
136
+ private renderTime;
137
+ private lastTokenCount;
138
+ constructor(config?: EngineConfig);
139
+ private registerFeatureExtensions;
140
+ private registerCoreExtensions;
141
+ registerExtension(extension: Extension): ExtensionRegistration;
142
+ unregisterExtension(name: string): boolean;
143
+ parse(markdown: string): MarkdownToken[];
144
+ render(tokens: MarkdownToken[], cacheKey?: string): string;
145
+ toHtml(markdown: string): string;
146
+ /**
147
+ * Render markdown with performance metrics
148
+ */
149
+ toHtmlWithMetrics(markdown: string): {
150
+ html: string;
151
+ metrics: RenderMetrics;
152
+ };
153
+ /**
154
+ * Stream-render large documents in chunks for better performance
155
+ */
156
+ toHtmlStreamed(markdown: string, options?: {
157
+ chunkSize?: number;
158
+ onChunk?: (chunk: {
159
+ html: string;
160
+ progress: number;
161
+ }) => void;
162
+ }): Promise<string>;
163
+ getExtensions(): string[];
164
+ hasExtension(name: string): boolean;
165
+ getWarnings(): string[];
166
+ getDebugInfo(): DebugInfo | null;
167
+ getPerformanceMetrics(): PerformanceMetrics | null;
168
+ /**
169
+ * Get cache statistics
170
+ */
171
+ getCacheStats(): {
172
+ parse: CacheStats;
173
+ render: CacheStats;
174
+ };
175
+ /**
176
+ * Clear all caches
177
+ */
178
+ clearCaches(): void;
179
+ /**
180
+ * Update cache capacity
181
+ */
182
+ setCacheSize(size: number): void;
183
+ private rebuildParserAndRenderer;
184
+ }
185
+
186
+ /**
187
+ * @changerawr/markdown — Astro integration utilities
188
+ *
189
+ * Usage:
190
+ * import { renderMarkdownForAstro } from '@changerawr/markdown/astro';
191
+ *
192
+ * For the Astro component:
193
+ * import MarkdownRenderer from '@changerawr/markdown/astro/MarkdownRenderer.astro';
194
+ */
195
+
196
+ /**
197
+ * Render markdown to an HTML string suitable for Astro's `set:html` directive.
198
+ * Runs entirely server-side — no client JavaScript emitted.
199
+ *
200
+ * @example
201
+ * ---
202
+ * import { renderMarkdownForAstro } from '@changerawr/markdown/astro';
203
+ * const html = renderMarkdownForAstro(Astro.props.content);
204
+ * ---
205
+ * <div set:html={html} />
206
+ */
207
+ declare function renderMarkdownForAstro(content: string, options?: {
208
+ format?: 'html' | 'tailwind';
209
+ config?: EngineConfig;
210
+ extensions?: Extension[];
211
+ }): string;
212
+
213
+ export { type AstroMarkdownRendererProps, ChangerawrMarkdown, type EngineConfig, type Extension, type MarkdownToken, renderMarkdownForAstro, renderToHTML, renderToHTMLWithConfig, renderToTailwind, renderToTailwindWithConfig };
@@ -0,0 +1,213 @@
1
+ interface MarkdownToken {
2
+ type: string;
3
+ content: string;
4
+ raw: string;
5
+ attributes?: Record<string, string | number | boolean | Function | any>;
6
+ children?: MarkdownToken[];
7
+ }
8
+ interface ParseRule {
9
+ name: string;
10
+ pattern: RegExp;
11
+ render: (match: RegExpMatchArray) => MarkdownToken;
12
+ }
13
+ interface RenderRule {
14
+ type: string;
15
+ render: (token: MarkdownToken) => string;
16
+ }
17
+ interface Extension {
18
+ name: string;
19
+ parseRules: ParseRule[];
20
+ renderRules: RenderRule[];
21
+ }
22
+ type OutputFormat = 'html' | 'tailwind' | 'json';
23
+ interface RendererConfig {
24
+ format: OutputFormat;
25
+ sanitize?: boolean;
26
+ allowUnsafeHtml?: boolean;
27
+ customClasses?: Record<string, string>;
28
+ debugMode?: boolean;
29
+ }
30
+ interface ParserConfig {
31
+ debugMode?: boolean;
32
+ maxIterations?: number;
33
+ validateMarkdown?: boolean;
34
+ }
35
+ interface EngineConfig {
36
+ parser?: ParserConfig;
37
+ renderer?: RendererConfig;
38
+ extensions?: Extension[];
39
+ }
40
+ interface DebugInfo {
41
+ warnings: string[];
42
+ parseTime: number;
43
+ renderTime: number;
44
+ tokenCount: number;
45
+ iterationCount: number;
46
+ }
47
+ interface PerformanceMetrics {
48
+ parseTime: number;
49
+ renderTime: number;
50
+ totalTime: number;
51
+ tokenCount: number;
52
+ memoryUsed?: number;
53
+ }
54
+ interface ExtensionRegistration {
55
+ success: boolean;
56
+ extensionName: string;
57
+ error?: string;
58
+ conflictingRules?: string[];
59
+ }
60
+
61
+ /**
62
+ * Props for the Astro MarkdownRenderer component.
63
+ */
64
+ interface AstroMarkdownRendererProps {
65
+ /** Markdown content to render */
66
+ content: string;
67
+ /** Output format — 'html' (default) or 'tailwind' */
68
+ format?: 'html' | 'tailwind';
69
+ /** CSS class(es) to add to the wrapper element */
70
+ class?: string;
71
+ /** Engine configuration */
72
+ config?: EngineConfig;
73
+ /** Additional extensions to register */
74
+ extensions?: Extension[];
75
+ }
76
+
77
+ /**
78
+ * Plain HTML output renderer - no CSS framework classes
79
+ */
80
+
81
+ /**
82
+ * Render markdown to plain HTML without CSS framework classes
83
+ */
84
+ declare function renderToHTML(markdown: string, config?: Omit<EngineConfig, 'renderer'>): string;
85
+ /**
86
+ * Parse markdown and render to HTML with custom configuration
87
+ */
88
+ declare function renderToHTMLWithConfig(markdown: string, rendererConfig: {
89
+ sanitize?: boolean;
90
+ allowUnsafeHtml?: boolean;
91
+ debugMode?: boolean;
92
+ }): string;
93
+
94
+ /**
95
+ * Tailwind CSS output renderer - HTML with Tailwind CSS classes
96
+ */
97
+
98
+ /**
99
+ * Render markdown to HTML with Tailwind CSS classes (default behavior)
100
+ */
101
+ declare function renderToTailwind(markdown: string, config?: Omit<EngineConfig, 'renderer'>): string;
102
+ /**
103
+ * Render markdown to Tailwind HTML with custom configuration
104
+ */
105
+ declare function renderToTailwindWithConfig(markdown: string, rendererConfig: {
106
+ sanitize?: boolean;
107
+ allowUnsafeHtml?: boolean;
108
+ customClasses?: Record<string, string>;
109
+ debugMode?: boolean;
110
+ }): string;
111
+
112
+ interface CacheStats {
113
+ size: number;
114
+ capacity: number;
115
+ hits: number;
116
+ misses: number;
117
+ hitRate: number;
118
+ evictions: number;
119
+ }
120
+
121
+ interface RenderMetrics {
122
+ inputSize: number;
123
+ parseTime: number;
124
+ renderTime: number;
125
+ totalTime: number;
126
+ tokenCount: number;
127
+ cacheHit: boolean;
128
+ }
129
+ declare class ChangerawrMarkdown {
130
+ private parser;
131
+ private renderer;
132
+ private extensions;
133
+ private parseCache;
134
+ private renderCache;
135
+ private parseTime;
136
+ private renderTime;
137
+ private lastTokenCount;
138
+ constructor(config?: EngineConfig);
139
+ private registerFeatureExtensions;
140
+ private registerCoreExtensions;
141
+ registerExtension(extension: Extension): ExtensionRegistration;
142
+ unregisterExtension(name: string): boolean;
143
+ parse(markdown: string): MarkdownToken[];
144
+ render(tokens: MarkdownToken[], cacheKey?: string): string;
145
+ toHtml(markdown: string): string;
146
+ /**
147
+ * Render markdown with performance metrics
148
+ */
149
+ toHtmlWithMetrics(markdown: string): {
150
+ html: string;
151
+ metrics: RenderMetrics;
152
+ };
153
+ /**
154
+ * Stream-render large documents in chunks for better performance
155
+ */
156
+ toHtmlStreamed(markdown: string, options?: {
157
+ chunkSize?: number;
158
+ onChunk?: (chunk: {
159
+ html: string;
160
+ progress: number;
161
+ }) => void;
162
+ }): Promise<string>;
163
+ getExtensions(): string[];
164
+ hasExtension(name: string): boolean;
165
+ getWarnings(): string[];
166
+ getDebugInfo(): DebugInfo | null;
167
+ getPerformanceMetrics(): PerformanceMetrics | null;
168
+ /**
169
+ * Get cache statistics
170
+ */
171
+ getCacheStats(): {
172
+ parse: CacheStats;
173
+ render: CacheStats;
174
+ };
175
+ /**
176
+ * Clear all caches
177
+ */
178
+ clearCaches(): void;
179
+ /**
180
+ * Update cache capacity
181
+ */
182
+ setCacheSize(size: number): void;
183
+ private rebuildParserAndRenderer;
184
+ }
185
+
186
+ /**
187
+ * @changerawr/markdown — Astro integration utilities
188
+ *
189
+ * Usage:
190
+ * import { renderMarkdownForAstro } from '@changerawr/markdown/astro';
191
+ *
192
+ * For the Astro component:
193
+ * import MarkdownRenderer from '@changerawr/markdown/astro/MarkdownRenderer.astro';
194
+ */
195
+
196
+ /**
197
+ * Render markdown to an HTML string suitable for Astro's `set:html` directive.
198
+ * Runs entirely server-side — no client JavaScript emitted.
199
+ *
200
+ * @example
201
+ * ---
202
+ * import { renderMarkdownForAstro } from '@changerawr/markdown/astro';
203
+ * const html = renderMarkdownForAstro(Astro.props.content);
204
+ * ---
205
+ * <div set:html={html} />
206
+ */
207
+ declare function renderMarkdownForAstro(content: string, options?: {
208
+ format?: 'html' | 'tailwind';
209
+ config?: EngineConfig;
210
+ extensions?: Extension[];
211
+ }): string;
212
+
213
+ export { type AstroMarkdownRendererProps, ChangerawrMarkdown, type EngineConfig, type Extension, type MarkdownToken, renderMarkdownForAstro, renderToHTML, renderToHTMLWithConfig, renderToTailwind, renderToTailwindWithConfig };