@next-md-blog/core 1.0.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.
Files changed (134) hide show
  1. package/README.md +242 -0
  2. package/dist/components/BlogPostSEO.d.ts +28 -0
  3. package/dist/components/BlogPostSEO.d.ts.map +1 -0
  4. package/dist/components/BlogPostSEO.js +22 -0
  5. package/dist/components/MarkdownContent.d.ts +48 -0
  6. package/dist/components/MarkdownContent.d.ts.map +1 -0
  7. package/dist/components/MarkdownContent.js +44 -0
  8. package/dist/components/OgImage.d.ts +35 -0
  9. package/dist/components/OgImage.d.ts.map +1 -0
  10. package/dist/components/OgImage.js +52 -0
  11. package/dist/components/markdown/a.d.ts +6 -0
  12. package/dist/components/markdown/a.d.ts.map +1 -0
  13. package/dist/components/markdown/a.js +7 -0
  14. package/dist/components/markdown/blockquote.d.ts +6 -0
  15. package/dist/components/markdown/blockquote.d.ts.map +1 -0
  16. package/dist/components/markdown/blockquote.js +7 -0
  17. package/dist/components/markdown/code.d.ts +6 -0
  18. package/dist/components/markdown/code.d.ts.map +1 -0
  19. package/dist/components/markdown/code.js +7 -0
  20. package/dist/components/markdown/defaults.d.ts +12 -0
  21. package/dist/components/markdown/defaults.d.ts.map +1 -0
  22. package/dist/components/markdown/defaults.js +59 -0
  23. package/dist/components/markdown/em.d.ts +6 -0
  24. package/dist/components/markdown/em.d.ts.map +1 -0
  25. package/dist/components/markdown/em.js +7 -0
  26. package/dist/components/markdown/h1.d.ts +6 -0
  27. package/dist/components/markdown/h1.d.ts.map +1 -0
  28. package/dist/components/markdown/h1.js +7 -0
  29. package/dist/components/markdown/h2.d.ts +6 -0
  30. package/dist/components/markdown/h2.d.ts.map +1 -0
  31. package/dist/components/markdown/h2.js +7 -0
  32. package/dist/components/markdown/h3.d.ts +6 -0
  33. package/dist/components/markdown/h3.d.ts.map +1 -0
  34. package/dist/components/markdown/h3.js +7 -0
  35. package/dist/components/markdown/h4.d.ts +6 -0
  36. package/dist/components/markdown/h4.d.ts.map +1 -0
  37. package/dist/components/markdown/h4.js +7 -0
  38. package/dist/components/markdown/h5.d.ts +6 -0
  39. package/dist/components/markdown/h5.d.ts.map +1 -0
  40. package/dist/components/markdown/h5.js +7 -0
  41. package/dist/components/markdown/h6.d.ts +6 -0
  42. package/dist/components/markdown/h6.d.ts.map +1 -0
  43. package/dist/components/markdown/h6.js +7 -0
  44. package/dist/components/markdown/hr.d.ts +6 -0
  45. package/dist/components/markdown/hr.d.ts.map +1 -0
  46. package/dist/components/markdown/hr.js +7 -0
  47. package/dist/components/markdown/img.d.ts +6 -0
  48. package/dist/components/markdown/img.d.ts.map +1 -0
  49. package/dist/components/markdown/img.js +9 -0
  50. package/dist/components/markdown/index.d.ts +29 -0
  51. package/dist/components/markdown/index.d.ts.map +1 -0
  52. package/dist/components/markdown/index.js +28 -0
  53. package/dist/components/markdown/li.d.ts +6 -0
  54. package/dist/components/markdown/li.d.ts.map +1 -0
  55. package/dist/components/markdown/li.js +7 -0
  56. package/dist/components/markdown/ol.d.ts +6 -0
  57. package/dist/components/markdown/ol.d.ts.map +1 -0
  58. package/dist/components/markdown/ol.js +7 -0
  59. package/dist/components/markdown/p.d.ts +6 -0
  60. package/dist/components/markdown/p.d.ts.map +1 -0
  61. package/dist/components/markdown/p.js +7 -0
  62. package/dist/components/markdown/pre.d.ts +6 -0
  63. package/dist/components/markdown/pre.d.ts.map +1 -0
  64. package/dist/components/markdown/pre.js +7 -0
  65. package/dist/components/markdown/strong.d.ts +6 -0
  66. package/dist/components/markdown/strong.d.ts.map +1 -0
  67. package/dist/components/markdown/strong.js +7 -0
  68. package/dist/components/markdown/table.d.ts +6 -0
  69. package/dist/components/markdown/table.d.ts.map +1 -0
  70. package/dist/components/markdown/table.js +7 -0
  71. package/dist/components/markdown/tbody.d.ts +6 -0
  72. package/dist/components/markdown/tbody.d.ts.map +1 -0
  73. package/dist/components/markdown/tbody.js +7 -0
  74. package/dist/components/markdown/td.d.ts +6 -0
  75. package/dist/components/markdown/td.d.ts.map +1 -0
  76. package/dist/components/markdown/td.js +7 -0
  77. package/dist/components/markdown/th.d.ts +6 -0
  78. package/dist/components/markdown/th.d.ts.map +1 -0
  79. package/dist/components/markdown/th.js +7 -0
  80. package/dist/components/markdown/thead.d.ts +6 -0
  81. package/dist/components/markdown/thead.d.ts.map +1 -0
  82. package/dist/components/markdown/thead.js +7 -0
  83. package/dist/components/markdown/tr.d.ts +6 -0
  84. package/dist/components/markdown/tr.d.ts.map +1 -0
  85. package/dist/components/markdown/tr.js +7 -0
  86. package/dist/components/markdown/ul.d.ts +6 -0
  87. package/dist/components/markdown/ul.d.ts.map +1 -0
  88. package/dist/components/markdown/ul.js +7 -0
  89. package/dist/components/markdown/utils.d.ts +6 -0
  90. package/dist/components/markdown/utils.d.ts.map +1 -0
  91. package/dist/components/markdown/utils.js +7 -0
  92. package/dist/core/config.d.ts +36 -0
  93. package/dist/core/config.d.ts.map +1 -0
  94. package/dist/core/config.js +63 -0
  95. package/dist/core/constants.d.ts +36 -0
  96. package/dist/core/constants.d.ts.map +1 -0
  97. package/dist/core/constants.js +44 -0
  98. package/dist/core/errors.d.ts +48 -0
  99. package/dist/core/errors.d.ts.map +1 -0
  100. package/dist/core/errors.js +57 -0
  101. package/dist/core/file-utils.d.ts +22 -0
  102. package/dist/core/file-utils.d.ts.map +1 -0
  103. package/dist/core/file-utils.js +180 -0
  104. package/dist/core/seo-feeds.d.ts +16 -0
  105. package/dist/core/seo-feeds.d.ts.map +1 -0
  106. package/dist/core/seo-feeds.js +73 -0
  107. package/dist/core/seo-metadata.d.ts +17 -0
  108. package/dist/core/seo-metadata.d.ts.map +1 -0
  109. package/dist/core/seo-metadata.js +197 -0
  110. package/dist/core/seo-schema.d.ts +20 -0
  111. package/dist/core/seo-schema.d.ts.map +1 -0
  112. package/dist/core/seo-schema.js +131 -0
  113. package/dist/core/seo-utils.d.ts +66 -0
  114. package/dist/core/seo-utils.d.ts.map +1 -0
  115. package/dist/core/seo-utils.js +135 -0
  116. package/dist/core/seo.d.ts +11 -0
  117. package/dist/core/seo.d.ts.map +1 -0
  118. package/dist/core/seo.js +12 -0
  119. package/dist/core/type-guards.d.ts +58 -0
  120. package/dist/core/type-guards.d.ts.map +1 -0
  121. package/dist/core/type-guards.js +83 -0
  122. package/dist/core/types.d.ts +116 -0
  123. package/dist/core/types.d.ts.map +1 -0
  124. package/dist/core/types.js +1 -0
  125. package/dist/core/utils.d.ts +49 -0
  126. package/dist/core/utils.d.ts.map +1 -0
  127. package/dist/core/utils.js +175 -0
  128. package/dist/core/validation.d.ts +22 -0
  129. package/dist/core/validation.d.ts.map +1 -0
  130. package/dist/core/validation.js +50 -0
  131. package/dist/index.d.ts +15 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +17 -0
  134. package/package.json +80 -0
package/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # @florianamette/next-md-blog
2
+
3
+ A powerful React library for parsing and displaying markdown and MDX blog posts in Next.js projects. Write your blog posts in markdown or MDX format and easily display them with dynamic routes, complete SEO optimization, and beautiful Open Graph images.
4
+
5
+ ## ✨ Features
6
+
7
+ - 📝 **Markdown & MDX Support** - Full markdown and MDX support with GitHub Flavored Markdown (GFM) support
8
+ - 🎨 **Server Components** - Built for Next.js App Router with server-side rendering
9
+ - 🔍 **SEO Optimized** - Automatic metadata generation with Open Graph and Twitter Cards
10
+ - 🖼️ **OG Images** - Built-in OG image generation component
11
+ - ⚡ **Type Safe** - Full TypeScript support with comprehensive types
12
+ - 🎯 **Flexible** - Works with both App Router and Pages Router
13
+ - 🛡️ **Robust** - Input validation, error handling, and clean code principles
14
+
15
+ ## 📦 Installation
16
+
17
+ ```bash
18
+ npm install @florianamette/next-md-blog
19
+ ```
20
+
21
+ ## 🚀 Quick Start
22
+
23
+ ### 1. Initialize with CLI (Recommended)
24
+
25
+ The easiest way to get started is using the CLI:
26
+
27
+ ```bash
28
+ npx @florianamette/next-md-blog-cli
29
+ ```
30
+
31
+ This will automatically:
32
+ - ✅ Create blog routes and pages
33
+ - ✅ Install required dependencies
34
+ - ✅ Configure Tailwind CSS typography
35
+ - ✅ Set up SEO configuration
36
+
37
+ ### 2. Manual Setup
38
+
39
+ If you prefer manual setup:
40
+
41
+ 1. **Install the package:**
42
+ ```bash
43
+ npm install @florianamette/next-md-blog @tailwindcss/typography
44
+ ```
45
+
46
+ 2. **Create a config file:**
47
+ ```tsx
48
+ // next-md-blog.config.ts
49
+ import { createConfig } from '@florianamette/next-md-blog';
50
+
51
+ export default createConfig({
52
+ siteName: 'My Blog',
53
+ siteUrl: process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com',
54
+ defaultAuthor: 'Your Name',
55
+ twitterHandle: '@yourhandle',
56
+ defaultLang: 'en',
57
+ });
58
+ ```
59
+
60
+ 3. **Create blog routes:**
61
+ ```tsx
62
+ // app/blog/[slug]/page.tsx
63
+ import { getBlogPost, getAllBlogPosts, generateBlogPostMetadata } from '@florianamette/next-md-blog';
64
+ import { MarkdownContent } from '@florianamette/next-md-blog';
65
+ import { notFound } from 'next/navigation';
66
+ import type { Metadata } from 'next';
67
+ import blogConfig from '@/next-md-blog.config';
68
+
69
+ export async function generateStaticParams() {
70
+ const posts = await getAllBlogPosts({ config: blogConfig });
71
+ return posts.map((post) => ({
72
+ slug: post.slug,
73
+ }));
74
+ }
75
+
76
+ export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
77
+ const { slug } = await params;
78
+ const post = await getBlogPost(slug, { config: blogConfig });
79
+
80
+ if (!post) {
81
+ return { title: 'Post Not Found' };
82
+ }
83
+
84
+ return generateBlogPostMetadata(post, blogConfig);
85
+ }
86
+
87
+ export default async function BlogPost({ params }: { params: Promise<{ slug: string }> }) {
88
+ const { slug } = await params;
89
+ const post = await getBlogPost(slug, { config: blogConfig });
90
+
91
+ if (!post) {
92
+ notFound();
93
+ }
94
+
95
+ return (
96
+ <div className="prose prose-lg dark:prose-invert max-w-none">
97
+ <MarkdownContent content={post.content} />
98
+ </div>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ## 📚 API Reference
104
+
105
+ ### Components
106
+
107
+ #### `MarkdownContent`
108
+
109
+ A React Server Component that renders markdown content as HTML.
110
+
111
+ **Props:**
112
+ - `content` (string, required): The markdown content to render
113
+ - `className` (string, optional): CSS class name for the container
114
+ - `components` (MarkdownComponents, optional): Custom components to override default markdown rendering
115
+ - `remarkPlugins` (any[], optional): Custom remark plugins to extend markdown parsing
116
+ - `rehypePlugins` (any[], optional): Custom rehype plugins to extend HTML processing
117
+
118
+ **Example:**
119
+ ```tsx
120
+ <MarkdownContent content="# Hello World" className="prose prose-lg" />
121
+ ```
122
+
123
+ #### `BlogPostSEO`
124
+
125
+ A component that generates JSON-LD structured data for SEO.
126
+
127
+ **Props:**
128
+ - `post` (BlogPost, required): The blog post object
129
+ - `config` (SEOConfig, required): SEO configuration
130
+
131
+ **Example:**
132
+ ```tsx
133
+ <BlogPostSEO post={post} config={blogConfig} />
134
+ ```
135
+
136
+ #### `OgImage`
137
+
138
+ A component for generating Open Graph images (used with `@vercel/og`).
139
+
140
+ **Props:**
141
+ - `title` (string, required): Title text
142
+ - `description` (string, optional): Description/subtitle text
143
+ - `siteName` (string, optional): Site name
144
+ - `backgroundColor` (string, optional): Background color (default: `#1a1a1a`)
145
+ - `textColor` (string, optional): Text color (default: `#ffffff`)
146
+
147
+ ### Functions
148
+
149
+ #### `getBlogPost(slug: string, options?: GetBlogPostOptions): Promise<BlogPost | null>`
150
+
151
+ Retrieves a single blog post by its slug.
152
+
153
+ **Parameters:**
154
+ - `slug` (string): The slug of the blog post (filename without .md or .mdx extension)
155
+ - `options` (optional): Configuration object
156
+ - `postsDir` (string, optional): Custom path to posts directory
157
+ - `config` (Config, optional): Blog configuration
158
+
159
+ **Returns:**
160
+ - `Promise<BlogPost | null>`: The blog post object or null if not found
161
+
162
+ #### `getAllBlogPosts(options?: GetBlogPostOptions): Promise<BlogPostMetadata[]>`
163
+
164
+ Retrieves all blog posts from the posts folder.
165
+
166
+ **Parameters:**
167
+ - `options` (optional): Configuration object
168
+ - `postsDir` (string, optional): Custom path to posts directory
169
+ - `config` (Config, optional): Blog configuration
170
+
171
+ **Returns:**
172
+ - `Promise<BlogPostMetadata[]>`: Array of blog post metadata, sorted by date (newest first)
173
+
174
+ #### `generateBlogPostMetadata(post: BlogPost, config?: SEOConfig): Metadata`
175
+
176
+ Generates comprehensive SEO metadata for a blog post.
177
+
178
+ **Parameters:**
179
+ - `post` (BlogPost): The blog post object
180
+ - `config` (SEOConfig, optional): SEO configuration
181
+
182
+ **Returns:**
183
+ - `Metadata`: Next.js metadata object
184
+
185
+ #### `generateBlogListMetadata(posts: BlogPostMetadata[], config?: SEOConfig): Metadata`
186
+
187
+ Generates SEO metadata for the blog listing page.
188
+
189
+ ### Types
190
+
191
+ #### `BlogPost`
192
+
193
+ ```typescript
194
+ interface BlogPost {
195
+ slug: string;
196
+ content: string;
197
+ frontmatter: BlogPostFrontmatter;
198
+ authors: Author[];
199
+ readingTime: number;
200
+ wordCount: number;
201
+ }
202
+ ```
203
+
204
+ #### `BlogPostFrontmatter`
205
+
206
+ ```typescript
207
+ interface BlogPostFrontmatter {
208
+ title?: string;
209
+ date?: string;
210
+ description?: string;
211
+ author?: string;
212
+ tags?: string[];
213
+ ogImage?: string;
214
+ image?: string;
215
+ [key: string]: unknown;
216
+ }
217
+ ```
218
+
219
+ ## 🎨 Styling
220
+
221
+ The library is designed to work with Tailwind CSS v4 and the `@tailwindcss/typography` plugin. Use the `prose` classes for beautiful markdown styling:
222
+
223
+ ```tsx
224
+ <div className="prose prose-lg dark:prose-invert max-w-none">
225
+ <MarkdownContent content={post.content} />
226
+ </div>
227
+ ```
228
+
229
+ ## 📖 Documentation
230
+
231
+ For complete documentation, examples, and advanced usage, visit the [main repository](https://github.com/florianamette/next-mdx-blog).
232
+
233
+ ## 📝 License
234
+
235
+ MIT
236
+
237
+ ## 🔗 Links
238
+
239
+ - [GitHub Repository](https://github.com/florianamette/next-mdx-blog)
240
+ - [CLI Package](https://www.npmjs.com/package/@florianamette/next-md-blog-cli)
241
+ - [Issues](https://github.com/florianamette/next-mdx-blog/issues)
242
+
@@ -0,0 +1,28 @@
1
+ import type { BlogPost, Config } from '../core/types.js';
2
+ /**
3
+ * Props for the BlogPostSEO component
4
+ */
5
+ export interface BlogPostSEOProps {
6
+ /** The blog post */
7
+ post: BlogPost;
8
+ /** Configuration (optional - will load from next-md-blog.config.ts if not provided) */
9
+ config?: Config;
10
+ /** Custom breadcrumb items (optional) */
11
+ breadcrumbs?: Array<{
12
+ name: string;
13
+ url: string;
14
+ }>;
15
+ /** Whether to include breadcrumbs schema (default: true) */
16
+ includeBreadcrumbs?: boolean;
17
+ }
18
+ /**
19
+ * Component that generates and injects JSON-LD structured data for a blog post
20
+ * Handles both article schema and breadcrumbs schema automatically
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * <BlogPostSEO post={post} />
25
+ * ```
26
+ */
27
+ export declare function BlogPostSEO({ post, config, breadcrumbs, includeBreadcrumbs, }: BlogPostSEOProps): import("react/jsx-runtime").JSX.Element;
28
+ //# sourceMappingURL=BlogPostSEO.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlogPostSEO.d.ts","sourceRoot":"","sources":["../../src/components/BlogPostSEO.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIzD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oBAAoB;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,4DAA4D;IAC5D,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,WAAW,EACX,kBAAyB,GAC1B,EAAE,gBAAgB,2CA2BlB"}
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { generateBlogPostSchema, generateBreadcrumbsSchema } from '../core/seo.js';
3
+ import { getConfig } from '../core/config.js';
4
+ /**
5
+ * Component that generates and injects JSON-LD structured data for a blog post
6
+ * Handles both article schema and breadcrumbs schema automatically
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <BlogPostSEO post={post} />
11
+ * ```
12
+ */
13
+ export function BlogPostSEO({ post, config, breadcrumbs, includeBreadcrumbs = true, }) {
14
+ const blogConfig = config || getConfig();
15
+ // Generate article schema
16
+ const articleSchema = generateBlogPostSchema(post, blogConfig);
17
+ // Generate breadcrumbs schema if enabled
18
+ const breadcrumbsSchema = includeBreadcrumbs
19
+ ? generateBreadcrumbsSchema(post, blogConfig, breadcrumbs)
20
+ : null;
21
+ return (_jsxs(_Fragment, { children: [_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(articleSchema) } }), breadcrumbsSchema && (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: JSON.stringify(breadcrumbsSchema) } }))] }));
22
+ }
@@ -0,0 +1,48 @@
1
+ import type { Components } from 'react-markdown';
2
+ /**
3
+ * Component types that can be overridden
4
+ */
5
+ export interface MarkdownComponents extends Partial<Components> {
6
+ }
7
+ /**
8
+ * Props for the MarkdownContent component
9
+ */
10
+ export interface MarkdownContentProps {
11
+ /** The markdown content to render */
12
+ content: string;
13
+ /** Optional CSS class name for the container */
14
+ className?: string;
15
+ /** Optional custom components to override default markdown rendering */
16
+ components?: MarkdownComponents;
17
+ /** Optional remark plugins */
18
+ remarkPlugins?: any[];
19
+ /** Optional rehype plugins */
20
+ rehypePlugins?: any[];
21
+ }
22
+ /**
23
+ * React Server Component that renders markdown content as React elements
24
+ * Uses react-markdown under the hood with support for custom components.
25
+ *
26
+ * If custom components are provided in the components/markdown folder, they will be used.
27
+ * Otherwise, Tailwind's prose classes will handle the styling.
28
+ *
29
+ * @example
30
+ * ```tsx
31
+ * // Using default components from components/markdown folder
32
+ * <MarkdownContent
33
+ * content="# Hello World"
34
+ * className="prose prose-lg"
35
+ * />
36
+ *
37
+ * // Overriding specific components
38
+ * <MarkdownContent
39
+ * content="# Hello World"
40
+ * className="prose prose-lg"
41
+ * components={{
42
+ * h1: ({ children, ...props }) => <h1 className="text-4xl font-bold" {...props}>{children}</h1>,
43
+ * }}
44
+ * />
45
+ * ```
46
+ */
47
+ export declare function MarkdownContent({ content, className, components, remarkPlugins, rehypePlugins, }: MarkdownContentProps): import("react/jsx-runtime").JSX.Element;
48
+ //# sourceMappingURL=MarkdownContent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownContent.d.ts","sourceRoot":"","sources":["../../src/components/MarkdownContent.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAGjD;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,OAAO,CAAC,UAAU,CAAC;CAAG;AAElE;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,8BAA8B;IAC9B,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;IACtB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,GAAG,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,EAC9B,OAAO,EACP,SAAc,EACd,UAAU,EACV,aAAkB,EAClB,aAAkB,GACnB,EAAE,oBAAoB,2CA0BtB"}
@@ -0,0 +1,44 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import ReactMarkdown from 'react-markdown';
3
+ import remarkGfm from 'remark-gfm';
4
+ import remarkEmoji from 'remark-emoji';
5
+ import { defaultMarkdownComponents } from './markdown/defaults.js';
6
+ /**
7
+ * React Server Component that renders markdown content as React elements
8
+ * Uses react-markdown under the hood with support for custom components.
9
+ *
10
+ * If custom components are provided in the components/markdown folder, they will be used.
11
+ * Otherwise, Tailwind's prose classes will handle the styling.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * // Using default components from components/markdown folder
16
+ * <MarkdownContent
17
+ * content="# Hello World"
18
+ * className="prose prose-lg"
19
+ * />
20
+ *
21
+ * // Overriding specific components
22
+ * <MarkdownContent
23
+ * content="# Hello World"
24
+ * className="prose prose-lg"
25
+ * components={{
26
+ * h1: ({ children, ...props }) => <h1 className="text-4xl font-bold" {...props}>{children}</h1>,
27
+ * }}
28
+ * />
29
+ * ```
30
+ */
31
+ export function MarkdownContent({ content, className = '', components, remarkPlugins = [], rehypePlugins = [], }) {
32
+ if (!content || typeof content !== 'string') {
33
+ throw new Error('Invalid content: must be a non-empty string');
34
+ }
35
+ // Merge default components with user-provided components
36
+ // User components override defaults
37
+ const mergedComponents = {
38
+ ...defaultMarkdownComponents,
39
+ ...components,
40
+ };
41
+ // Default remark plugins
42
+ const defaultRemarkPlugins = [remarkGfm, remarkEmoji, ...remarkPlugins];
43
+ return (_jsx("article", { className: className ? `prose prose-lg max-w-none dark:prose-invert ${className}` : 'prose prose-lg max-w-none dark:prose-invert', children: _jsx(ReactMarkdown, { remarkPlugins: defaultRemarkPlugins, rehypePlugins: rehypePlugins, components: mergedComponents, children: content }) }));
44
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ /**
3
+ * Props for the OgImage component
4
+ */
5
+ export interface OgImageProps {
6
+ /** Title text */
7
+ title: string;
8
+ /** Description/subtitle text */
9
+ description?: string;
10
+ /** Site name */
11
+ siteName?: string;
12
+ /** Background color */
13
+ backgroundColor?: string;
14
+ /** Text color */
15
+ textColor?: string;
16
+ /** Width of the image */
17
+ width?: number;
18
+ /** Height of the image */
19
+ height?: number;
20
+ }
21
+ /**
22
+ * Default OG Image component for generating Open Graph images
23
+ * This component can be used with @vercel/og or similar libraries
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <OgImage
28
+ * title="My Blog Post"
29
+ * description="A great blog post"
30
+ * siteName="My Blog"
31
+ * />
32
+ * ```
33
+ */
34
+ export declare function OgImage({ title, description, siteName, backgroundColor, textColor, width, height, }: OgImageProps): React.JSX.Element;
35
+ //# sourceMappingURL=OgImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OgImage.d.ts","sourceRoot":"","sources":["../../src/components/OgImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,WAAW,EACX,QAAoB,EACpB,eAA2B,EAC3B,SAAqB,EACrB,KAAY,EACZ,MAAY,GACb,EAAE,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAiElC"}
@@ -0,0 +1,52 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Default OG Image component for generating Open Graph images
4
+ * This component can be used with @vercel/og or similar libraries
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * <OgImage
9
+ * title="My Blog Post"
10
+ * description="A great blog post"
11
+ * siteName="My Blog"
12
+ * />
13
+ * ```
14
+ */
15
+ export function OgImage({ title, description, siteName = 'My Blog', backgroundColor = '#1a1a1a', textColor = '#ffffff', width = 1200, height = 630, }) {
16
+ return (_jsx("div", { style: {
17
+ width,
18
+ height,
19
+ backgroundColor,
20
+ color: textColor,
21
+ display: 'flex',
22
+ flexDirection: 'column',
23
+ justifyContent: 'center',
24
+ alignItems: 'center',
25
+ padding: '80px',
26
+ fontFamily: 'system-ui, -apple-system, sans-serif',
27
+ }, children: _jsxs("div", { style: {
28
+ display: 'flex',
29
+ flexDirection: 'column',
30
+ alignItems: 'center',
31
+ textAlign: 'center',
32
+ maxWidth: '1000px',
33
+ }, children: [siteName && (_jsx("div", { style: {
34
+ fontSize: '24px',
35
+ opacity: 0.8,
36
+ marginBottom: '20px',
37
+ fontWeight: 500,
38
+ }, children: siteName })), _jsx("h1", { style: {
39
+ fontSize: '64px',
40
+ fontWeight: 700,
41
+ margin: 0,
42
+ marginBottom: description ? '24px' : 0,
43
+ lineHeight: 1.2,
44
+ wordWrap: 'break-word',
45
+ }, children: title }), description && (_jsx("p", { style: {
46
+ fontSize: '32px',
47
+ opacity: 0.9,
48
+ margin: 0,
49
+ lineHeight: 1.4,
50
+ wordWrap: 'break-word',
51
+ }, children: description }))] }) }));
52
+ }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface AProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
3
+ }
4
+ declare const A: React.ForwardRefExoticComponent<AProps & React.RefAttributes<HTMLAnchorElement>>;
5
+ export default A;
6
+ //# sourceMappingURL=a.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/a.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,MAAO,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;CAAG;AAEhF,QAAA,MAAM,CAAC,kFAIN,CAAC;AAGF,eAAe,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ const A = React.forwardRef(({ className, ...props }, ref) => {
4
+ return _jsx("a", { ref: ref, className: className, ...props });
5
+ });
6
+ A.displayName = 'A';
7
+ export default A;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface BlockquoteProps extends React.HTMLAttributes<HTMLQuoteElement> {
3
+ }
4
+ declare const Blockquote: React.ForwardRefExoticComponent<BlockquoteProps & React.RefAttributes<HTMLQuoteElement>>;
5
+ export default Blockquote;
6
+ //# sourceMappingURL=blockquote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blockquote.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/blockquote.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,eAAgB,SAAQ,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;CAAG;AAElF,QAAA,MAAM,UAAU,0FAIf,CAAC;AAGF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ const Blockquote = React.forwardRef(({ className, ...props }, ref) => {
4
+ return _jsx("blockquote", { ref: ref, className: className, ...props });
5
+ });
6
+ Blockquote.displayName = 'Blockquote';
7
+ export default Blockquote;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface CodeProps extends React.HTMLAttributes<HTMLElement> {
3
+ }
4
+ declare const Code: React.ForwardRefExoticComponent<CodeProps & React.RefAttributes<HTMLElement>>;
5
+ export default Code;
6
+ //# sourceMappingURL=code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/code.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;CAAG;AAEvE,QAAA,MAAM,IAAI,+EAIT,CAAC;AAGF,eAAe,IAAI,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ const Code = React.forwardRef(({ className, ...props }, ref) => {
4
+ return _jsx("code", { ref: ref, className: className, ...props });
5
+ });
6
+ Code.displayName = 'Code';
7
+ export default Code;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Default markdown components
3
+ * These are used when no custom components are provided
4
+ */
5
+ import type { Components } from 'react-markdown';
6
+ /**
7
+ * Default markdown components that will be used if no custom components are provided
8
+ * These components from the components/markdown folder will be used.
9
+ * If a component is not provided here, Tailwind's prose classes will handle the styling.
10
+ */
11
+ export declare const defaultMarkdownComponents: Partial<Components>;
12
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/defaults.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AA0BjD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,OAAO,CAAC,UAAU,CAyBzD,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Default markdown components
3
+ * These are used when no custom components are provided
4
+ */
5
+ import H1 from './h1.js';
6
+ import H2 from './h2.js';
7
+ import H3 from './h3.js';
8
+ import H4 from './h4.js';
9
+ import H5 from './h5.js';
10
+ import H6 from './h6.js';
11
+ import P from './p.js';
12
+ import A from './a.js';
13
+ import Img from './img.js';
14
+ import Code from './code.js';
15
+ import Pre from './pre.js';
16
+ import Blockquote from './blockquote.js';
17
+ import Ul from './ul.js';
18
+ import Ol from './ol.js';
19
+ import Li from './li.js';
20
+ import Table from './table.js';
21
+ import Thead from './thead.js';
22
+ import Tbody from './tbody.js';
23
+ import Tr from './tr.js';
24
+ import Th from './th.js';
25
+ import Td from './td.js';
26
+ import Hr from './hr.js';
27
+ import Strong from './strong.js';
28
+ import Em from './em.js';
29
+ /**
30
+ * Default markdown components that will be used if no custom components are provided
31
+ * These components from the components/markdown folder will be used.
32
+ * If a component is not provided here, Tailwind's prose classes will handle the styling.
33
+ */
34
+ export const defaultMarkdownComponents = {
35
+ h1: H1,
36
+ h2: H2,
37
+ h3: H3,
38
+ h4: H4,
39
+ h5: H5,
40
+ h6: H6,
41
+ p: P,
42
+ a: A,
43
+ img: Img,
44
+ code: Code,
45
+ pre: Pre,
46
+ blockquote: Blockquote,
47
+ ul: Ul,
48
+ ol: Ol,
49
+ li: Li,
50
+ table: Table,
51
+ thead: Thead,
52
+ tbody: Tbody,
53
+ tr: Tr,
54
+ th: Th,
55
+ td: Td,
56
+ hr: Hr,
57
+ strong: Strong,
58
+ em: Em,
59
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface EmProps extends React.HTMLAttributes<HTMLElement> {
3
+ }
4
+ declare const Em: React.ForwardRefExoticComponent<EmProps & React.RefAttributes<HTMLElement>>;
5
+ export default Em;
6
+ //# sourceMappingURL=em.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"em.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/em.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,OAAQ,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;CAAG;AAErE,QAAA,MAAM,EAAE,6EAIP,CAAC;AAGF,eAAe,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ const Em = React.forwardRef(({ className, ...props }, ref) => {
4
+ return _jsx("em", { ref: ref, className: className, ...props });
5
+ });
6
+ Em.displayName = 'Em';
7
+ export default Em;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ export interface H1Props extends React.HTMLAttributes<HTMLHeadingElement> {
3
+ }
4
+ declare const H1: React.ForwardRefExoticComponent<H1Props & React.RefAttributes<HTMLHeadingElement>>;
5
+ export default H1;
6
+ //# sourceMappingURL=h1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"h1.d.ts","sourceRoot":"","sources":["../../../src/components/markdown/h1.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,OAAQ,SAAQ,KAAK,CAAC,cAAc,CAAC,kBAAkB,CAAC;CAAG;AAE5E,QAAA,MAAM,EAAE,oFAIP,CAAC;AAGF,eAAe,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ const H1 = React.forwardRef(({ className, ...props }, ref) => {
4
+ return _jsx("h1", { ref: ref, className: className, ...props });
5
+ });
6
+ H1.displayName = 'H1';
7
+ export default H1;