@san-siva/blogkit-md 0.1.2 → 0.2.1
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 +7 -7
- package/hooks/readMarkdownFile.ts +8 -11
- package/index.ts +2 -0
- package/package.json +1 -5
- package/website/package-lock.json +1 -1
- package/app/layout.tsx +0 -44
- package/app/page.tsx +0 -32
- package/next.config.ts +0 -5
- package/scripts/dev.ts +0 -50
package/README.md
CHANGED
|
@@ -123,7 +123,7 @@ description: A short description shown below the title
|
|
|
123
123
|
| Image | `` |
|
|
124
124
|
| Ordered list | `1. item` |
|
|
125
125
|
| Unordered list | `- item` |
|
|
126
|
-
| Table |
|
|
126
|
+
| Table | `\| col \| col \|` — headers and rows only |
|
|
127
127
|
| Code block | ` ```lang ` |
|
|
128
128
|
| Mermaid diagram | ` ```mermaid ` |
|
|
129
129
|
| Thematic break | `---` |
|
|
@@ -259,13 +259,13 @@ Result content.
|
|
|
259
259
|
|
|
260
260
|
### Callouts
|
|
261
261
|
|
|
262
|
-
Blockquotes are rendered as styled callout banners. The callout type is controlled by a prefix on the first word of the quote
|
|
262
|
+
Blockquotes are rendered as styled callout banners. The callout type is controlled by a prefix on the first word of the quote.
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
264
|
+
> This is an info callout. Use `> text` for general information.
|
|
265
|
+
|
|
266
|
+
> ~This is a warning callout. Use `> ~text` to flag caution.
|
|
267
|
+
|
|
268
|
+
> !This is an error callout. Use `> !text` for errors or destructive actions.
|
|
269
269
|
|
|
270
270
|
The prefix character is stripped from the rendered output — only the callout style changes.
|
|
271
271
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
-
import '
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import { renderMarkdownAst } from '@/utils/renderMarkdown';
|
|
4
|
+
import type { Frontmatter } from '../utils/parseMarkdown';
|
|
5
|
+
import { parseMarkdown } from '../utils/parseMarkdown';
|
|
6
|
+
import type { RenderedMarkdown } from '../utils/renderMarkdown';
|
|
7
|
+
import { renderMarkdownAst } from '../utils/renderMarkdown';
|
|
9
8
|
|
|
10
9
|
type MarkdownFileResult =
|
|
11
10
|
| { success: true; rendered: RenderedMarkdown; frontmatter: Frontmatter }
|
|
@@ -15,14 +14,12 @@ export const readMarkdownFile = async (
|
|
|
15
14
|
filePath: string | undefined
|
|
16
15
|
): Promise<MarkdownFileResult> => {
|
|
17
16
|
if (!filePath) {
|
|
18
|
-
return {
|
|
19
|
-
success: false,
|
|
20
|
-
error:
|
|
21
|
-
'MARKDOWN_FILE env variable is required. Usage: MARKDOWN_FILE=data/test.md npm run dev',
|
|
22
|
-
};
|
|
17
|
+
return { success: false, error: 'No file path provided.' };
|
|
23
18
|
}
|
|
24
19
|
|
|
25
|
-
const absolutePath = path.
|
|
20
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
21
|
+
? filePath
|
|
22
|
+
: path.join(process.cwd(), filePath);
|
|
26
23
|
|
|
27
24
|
let content: string;
|
|
28
25
|
try {
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@san-siva/blogkit-md",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Converts markdown files into JSX blog posts for Blogkit",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./index.ts"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"dev": "tsx scripts/dev.ts",
|
|
11
|
-
"build": "MARKDOWN_FILE=data/test.md next build",
|
|
12
|
-
"start": "next start",
|
|
13
10
|
"test": "vitest run",
|
|
14
11
|
"lint": "eslint . --config eslint.config.ts",
|
|
15
12
|
"fix": "eslint . --config eslint.config.ts --fix"
|
|
@@ -50,7 +47,6 @@
|
|
|
50
47
|
"eslint-plugin-unicorn": "^62.0.0",
|
|
51
48
|
"jiti": "^2.0.0",
|
|
52
49
|
"prettier": "^3.0.0",
|
|
53
|
-
"tsx": "^4.0.0",
|
|
54
50
|
"typescript": "^5.0.0",
|
|
55
51
|
"typescript-eslint": "^8.0.0",
|
|
56
52
|
"vitest": "^4.1.0"
|
package/app/layout.tsx
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { Metadata } from 'next';
|
|
2
|
-
import { JetBrains_Mono, Montserrat, Rubik } from 'next/font/google';
|
|
3
|
-
|
|
4
|
-
import '@san-siva/stylekit/styles/globals.scss';
|
|
5
|
-
import '@san-siva/blogkit/styles.css';
|
|
6
|
-
import styles from '@san-siva/stylekit/styles/index.module.scss';
|
|
7
|
-
|
|
8
|
-
const montserrat = Montserrat({
|
|
9
|
-
subsets: ['latin'] as const,
|
|
10
|
-
weight: ['400', '500', '600', '700', '800'] as const,
|
|
11
|
-
style: ['normal', 'italic'] as const,
|
|
12
|
-
variable: '--font-montserrat',
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const rubik = Rubik({
|
|
16
|
-
subsets: ['latin'] as const,
|
|
17
|
-
weight: ['300', '400', '500', '600', '700', '800', '900'] as const,
|
|
18
|
-
style: ['normal', 'italic'] as const,
|
|
19
|
-
variable: '--font-rubik',
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const jetbrainsMono = JetBrains_Mono({
|
|
23
|
-
subsets: ['latin'] as const,
|
|
24
|
-
weight: ['400', '500', '600', '700'] as const,
|
|
25
|
-
style: ['normal', 'italic'] as const,
|
|
26
|
-
variable: '--font-jetbrains-mono',
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
export const metadata: Metadata = {
|
|
30
|
-
title: 'Blogkit MD',
|
|
31
|
-
description: 'Markdown to JSX blog post renderer',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const RootLayout = ({ children }: { children: React.ReactNode }) => (
|
|
35
|
-
<html lang="en">
|
|
36
|
-
<body
|
|
37
|
-
className={`${montserrat.variable} ${rubik.variable} ${jetbrainsMono.variable}`}
|
|
38
|
-
>
|
|
39
|
-
<div className={`${styles.page}`}>{children}</div>
|
|
40
|
-
</body>
|
|
41
|
-
</html>
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
export default RootLayout;
|
package/app/page.tsx
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Blog, BlogHeader, Callout } from '@san-siva/blogkit';
|
|
2
|
-
|
|
3
|
-
import { readMarkdownFile } from '@/hooks/readMarkdownFile';
|
|
4
|
-
import { MarkdownSections } from '@/utils/renderMarkdown';
|
|
5
|
-
|
|
6
|
-
const Page = async () => {
|
|
7
|
-
const result = await readMarkdownFile(process.env.MARKDOWN_FILE);
|
|
8
|
-
|
|
9
|
-
if (!result.success) {
|
|
10
|
-
return (
|
|
11
|
-
<Blog>
|
|
12
|
-
<Callout type="warning">{result.error}</Callout>
|
|
13
|
-
</Blog>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const { rendered, frontmatter } = result;
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<Blog>
|
|
21
|
-
{frontmatter.title && (
|
|
22
|
-
<BlogHeader
|
|
23
|
-
title={[frontmatter.title]}
|
|
24
|
-
desc={frontmatter.description ? [frontmatter.description] : []}
|
|
25
|
-
/>
|
|
26
|
-
)}
|
|
27
|
-
<MarkdownSections rendered={rendered} />
|
|
28
|
-
</Blog>
|
|
29
|
-
);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default Page;
|
package/next.config.ts
DELETED
package/scripts/dev.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { exec, spawn } from 'node:child_process';
|
|
2
|
-
import { stat, writeFileSync } from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
const fileArgument = process.argv.find(argument => argument.startsWith('--file='));
|
|
6
|
-
const markdownFile = fileArgument ? fileArgument.replace('--file=', '') : 'data/test.md';
|
|
7
|
-
|
|
8
|
-
const markdownPath = path.join(process.cwd(), markdownFile);
|
|
9
|
-
const triggerPath = path.join(process.cwd(), 'utils/devReloadTrigger.ts');
|
|
10
|
-
|
|
11
|
-
// Write on startup so fresh clones have the file before Next.js compiles.
|
|
12
|
-
writeFileSync(triggerPath, `export const reloadTrigger = '${Date.now()}';\n`);
|
|
13
|
-
|
|
14
|
-
let lastMtime: number | null = null;
|
|
15
|
-
|
|
16
|
-
const checkMarkdownFile = () => {
|
|
17
|
-
stat(markdownPath, (error, stats) => {
|
|
18
|
-
if (error) return;
|
|
19
|
-
const { mtimeMs } = stats;
|
|
20
|
-
if (lastMtime !== null && mtimeMs !== lastMtime) {
|
|
21
|
-
writeFileSync(triggerPath, `export const reloadTrigger = '${mtimeMs}';\n`);
|
|
22
|
-
}
|
|
23
|
-
lastMtime = mtimeMs;
|
|
24
|
-
});
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
setInterval(checkMarkdownFile, 500);
|
|
28
|
-
checkMarkdownFile();
|
|
29
|
-
|
|
30
|
-
const child = spawn('next', ['dev'], {
|
|
31
|
-
env: { ...process.env, MARKDOWN_FILE: markdownFile },
|
|
32
|
-
stdio: ['inherit', 'pipe', 'inherit'],
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
let browserOpened = false;
|
|
36
|
-
|
|
37
|
-
child.stdout?.on('data', (chunk: Buffer) => {
|
|
38
|
-
process.stdout.write(chunk);
|
|
39
|
-
if (!browserOpened) {
|
|
40
|
-
const match = chunk.toString().match(/http:\/\/localhost:\d+/);
|
|
41
|
-
if (match) {
|
|
42
|
-
browserOpened = true;
|
|
43
|
-
exec(`open ${match[0]}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
child.on('exit', code => {
|
|
49
|
-
process.exit(code ?? 0);
|
|
50
|
-
});
|